summaryrefslogtreecommitdiff
path: root/storage/ndb/test
diff options
context:
space:
mode:
Diffstat (limited to 'storage/ndb/test')
-rw-r--r--storage/ndb/test/Makefile.am8
-rw-r--r--storage/ndb/test/include/CpcClient.hpp104
-rw-r--r--storage/ndb/test/include/HugoAsynchTransactions.hpp75
-rw-r--r--storage/ndb/test/include/HugoCalculator.hpp52
-rw-r--r--storage/ndb/test/include/HugoOperations.hpp114
-rw-r--r--storage/ndb/test/include/HugoTransactions.hpp119
-rw-r--r--storage/ndb/test/include/NDBT.hpp39
-rw-r--r--storage/ndb/test/include/NDBT_DataSet.hpp140
-rw-r--r--storage/ndb/test/include/NDBT_DataSetTransaction.hpp162
-rw-r--r--storage/ndb/test/include/NDBT_Error.hpp101
-rw-r--r--storage/ndb/test/include/NDBT_Output.hpp30
-rw-r--r--storage/ndb/test/include/NDBT_ResultRow.hpp61
-rw-r--r--storage/ndb/test/include/NDBT_ReturnCodes.h42
-rw-r--r--storage/ndb/test/include/NDBT_Stats.hpp74
-rw-r--r--storage/ndb/test/include/NDBT_Table.hpp82
-rw-r--r--storage/ndb/test/include/NDBT_Tables.hpp50
-rw-r--r--storage/ndb/test/include/NDBT_Test.hpp439
-rw-r--r--storage/ndb/test/include/NdbBackup.hpp54
-rw-r--r--storage/ndb/test/include/NdbConfig.hpp39
-rw-r--r--storage/ndb/test/include/NdbGrep.hpp53
-rw-r--r--storage/ndb/test/include/NdbRestarter.hpp96
-rw-r--r--storage/ndb/test/include/NdbRestarts.hpp120
-rw-r--r--storage/ndb/test/include/NdbSchemaCon.hpp147
-rw-r--r--storage/ndb/test/include/NdbSchemaOp.hpp546
-rw-r--r--storage/ndb/test/include/NdbTest.hpp35
-rw-r--r--storage/ndb/test/include/NdbTimer.hpp109
-rw-r--r--storage/ndb/test/include/TestNdbEventOperation.hpp24
-rw-r--r--storage/ndb/test/include/UtilTransactions.hpp129
-rw-r--r--storage/ndb/test/include/getarg.h115
-rw-r--r--storage/ndb/test/ndbapi/InsertRecs.cpp571
-rw-r--r--storage/ndb/test/ndbapi/Makefile.am159
-rw-r--r--storage/ndb/test/ndbapi/ScanFilter.hpp131
-rw-r--r--storage/ndb/test/ndbapi/ScanFunctions.hpp352
-rw-r--r--storage/ndb/test/ndbapi/ScanInterpretTest.hpp515
-rw-r--r--storage/ndb/test/ndbapi/TraceNdbApi.cpp543
-rw-r--r--storage/ndb/test/ndbapi/VerifyNdbApi.cpp151
-rw-r--r--storage/ndb/test/ndbapi/acid.cpp559
-rw-r--r--storage/ndb/test/ndbapi/acid2.cpp693
-rw-r--r--storage/ndb/test/ndbapi/adoInsertRecs.cpp363
-rw-r--r--storage/ndb/test/ndbapi/asyncGenerator.cpp571
-rw-r--r--storage/ndb/test/ndbapi/bank/Bank.cpp2462
-rw-r--r--storage/ndb/test/ndbapi/bank/Bank.hpp145
-rw-r--r--storage/ndb/test/ndbapi/bank/BankLoad.cpp581
-rw-r--r--storage/ndb/test/ndbapi/bank/Makefile.am24
-rw-r--r--storage/ndb/test/ndbapi/bank/bankCreator.cpp61
-rw-r--r--storage/ndb/test/ndbapi/bank/bankMakeGL.cpp62
-rw-r--r--storage/ndb/test/ndbapi/bank/bankSumAccounts.cpp62
-rw-r--r--storage/ndb/test/ndbapi/bank/bankTimer.cpp65
-rw-r--r--storage/ndb/test/ndbapi/bank/bankTransactionMaker.cpp65
-rw-r--r--storage/ndb/test/ndbapi/bank/bankValidateAllGLs.cpp63
-rw-r--r--storage/ndb/test/ndbapi/bank/testBank.cpp151
-rw-r--r--storage/ndb/test/ndbapi/bench/asyncGenerator.cpp571
-rw-r--r--storage/ndb/test/ndbapi/bench/dbGenerator.h63
-rw-r--r--storage/ndb/test/ndbapi/bench/dbPopulate.cpp244
-rw-r--r--storage/ndb/test/ndbapi/bench/dbPopulate.h59
-rw-r--r--storage/ndb/test/ndbapi/bench/macros.h51
-rw-r--r--storage/ndb/test/ndbapi/bench/mainAsyncGenerator.cpp503
-rw-r--r--storage/ndb/test/ndbapi/bench/mainPopulate.cpp81
-rw-r--r--storage/ndb/test/ndbapi/bench/ndb_async1.cpp647
-rw-r--r--storage/ndb/test/ndbapi/bench/ndb_async2.cpp757
-rw-r--r--storage/ndb/test/ndbapi/bench/ndb_error.hpp81
-rw-r--r--storage/ndb/test/ndbapi/bench/ndb_schema.hpp78
-rw-r--r--storage/ndb/test/ndbapi/bench/ndb_user_transaction.cpp825
-rw-r--r--storage/ndb/test/ndbapi/bench/ndb_user_transaction2.cpp825
-rw-r--r--storage/ndb/test/ndbapi/bench/ndb_user_transaction3.cpp793
-rw-r--r--storage/ndb/test/ndbapi/bench/ndb_user_transaction4.cpp770
-rw-r--r--storage/ndb/test/ndbapi/bench/ndb_user_transaction5.cpp769
-rw-r--r--storage/ndb/test/ndbapi/bench/ndb_user_transaction6.cpp561
-rw-r--r--storage/ndb/test/ndbapi/bench/testData.h156
-rw-r--r--storage/ndb/test/ndbapi/bench/testDefinitions.h90
-rw-r--r--storage/ndb/test/ndbapi/bench/userInterface.cpp744
-rw-r--r--storage/ndb/test/ndbapi/bench/userInterface.h151
-rw-r--r--storage/ndb/test/ndbapi/benchronja.cpp1201
-rw-r--r--storage/ndb/test/ndbapi/bulk_copy.cpp276
-rw-r--r--storage/ndb/test/ndbapi/cdrserver.cpp1628
-rw-r--r--storage/ndb/test/ndbapi/celloDb.cpp1504
-rw-r--r--storage/ndb/test/ndbapi/create_all_tabs.cpp69
-rw-r--r--storage/ndb/test/ndbapi/create_tab.cpp113
-rw-r--r--storage/ndb/test/ndbapi/drop_all_tabs.cpp63
-rw-r--r--storage/ndb/test/ndbapi/flexAsynch.cpp995
-rw-r--r--storage/ndb/test/ndbapi/flexBench.cpp1166
-rw-r--r--storage/ndb/test/ndbapi/flexHammer.cpp897
-rw-r--r--storage/ndb/test/ndbapi/flexScan.cpp1667
-rw-r--r--storage/ndb/test/ndbapi/flexTT.cpp937
-rw-r--r--storage/ndb/test/ndbapi/flexTimedAsynch.cpp852
-rw-r--r--storage/ndb/test/ndbapi/flex_bench_mysql.cpp1751
-rw-r--r--storage/ndb/test/ndbapi/index.cpp998
-rw-r--r--storage/ndb/test/ndbapi/index2.cpp836
-rw-r--r--storage/ndb/test/ndbapi/initronja.cpp351
-rw-r--r--storage/ndb/test/ndbapi/interpreterInTup.cpp1518
-rw-r--r--storage/ndb/test/ndbapi/mainAsyncGenerator.cpp391
-rw-r--r--storage/ndb/test/ndbapi/msa.cpp1206
-rw-r--r--storage/ndb/test/ndbapi/ndb_async1.cpp647
-rw-r--r--storage/ndb/test/ndbapi/ndb_async2.cpp754
-rw-r--r--storage/ndb/test/ndbapi/ndb_user_populate.cpp165
-rw-r--r--storage/ndb/test/ndbapi/ndb_user_transaction.cpp825
-rw-r--r--storage/ndb/test/ndbapi/ndb_user_transaction2.cpp825
-rw-r--r--storage/ndb/test/ndbapi/ndb_user_transaction3.cpp793
-rw-r--r--storage/ndb/test/ndbapi/ndb_user_transaction4.cpp770
-rw-r--r--storage/ndb/test/ndbapi/ndb_user_transaction5.cpp769
-rw-r--r--storage/ndb/test/ndbapi/ndb_user_transaction6.cpp561
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/acid/Makefile10
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/acid2/Makefile10
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/acid2/TraceNdbApi.hpp132
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/acid2/VerifyNdbApi.hpp466
-rwxr-xr-xstorage/ndb/test/ndbapi/old_dirs/basicAsynch/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/bulk_copy/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/create_all_tabs/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/create_tab/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/drop_all_tabs/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/flexAsynch/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/flexBench/Makefile.am10
-rwxr-xr-xstorage/ndb/test/ndbapi/old_dirs/flexBench/ndbplot.pl305
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/flexHammer/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/flexHammer/README67
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/flexScan/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/flexScan/README66
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/flexTT/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/flexTimedAsynch/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/flex_bench_mysql/Makefile15
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/indexTest/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/indexTest2/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/interpreterInTup/Makefile10
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/Makefile6
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/Makefile8
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/generator/Makefile13
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/dbGenerator.h63
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/testData.h156
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/userInterface.h79
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/macros.h51
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/ndb_error.hpp63
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/bin/.empty0
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/include/ndb_schema.hpp78
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/include/testDefinitions.h90
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/lib/.empty0
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/Makefile5
-rwxr-xr-xstorage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-l-p10.sh14
-rwxr-xr-xstorage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-l.sh14
-rwxr-xr-xstorage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-p10.sh14
-rwxr-xr-xstorage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench.sh14
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/Makefile8
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/README8
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/Makefile17
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/dbGenerator.c543
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/dbGenerator.h61
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/mainGenerator.c323
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/include/testData.h103
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/include/userInterface.h128
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/makevars.linux6
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/makevars.sparc15
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/Makefile15
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/dbPopulate.c244
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/dbPopulate.h59
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/mainPopulate.c76
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/localDbPrepare.c648
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/macros.h51
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/ndb_error.hpp31
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/Makefile10
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userHandle.h190
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userInterface.c453
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userTransaction.c473
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userHandle.h51
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userInterface.cpp739
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userTransaction.c473
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/restarter/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/restarter2/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/restarts/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/ronja/Makefile6
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/ronja/benchronja/Makefile10
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/ronja/initronja/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/telco/Makefile10
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/telco/readme9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testBackup/Makefile8
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testBasic/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testBlobs/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testDataBuffers/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testDict/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testGrep/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testGrep/verify/Makefile10
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testIndex/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testInterpreter/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testMgm/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testNdbApi/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testNodeRestart/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testOIBasic/Makefile13
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testOIBasic/times.txt8
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testOperations/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testOrderedIndex/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testRestartGci/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testScan/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testScanInterpreter/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testSystemRestart/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testTimeout/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/testTransactions/Makefile10
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/test_event/Makefile9
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/vw_test/Makefile11
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/vw_test/bcd.h26
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/vw_test/script/client_start10
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/vw_test/utv.h161
-rw-r--r--storage/ndb/test/ndbapi/old_dirs/vw_test/vcdrfunc.h55
-rw-r--r--storage/ndb/test/ndbapi/restarter.cpp130
-rw-r--r--storage/ndb/test/ndbapi/restarter2.cpp117
-rw-r--r--storage/ndb/test/ndbapi/restarts.cpp116
-rw-r--r--storage/ndb/test/ndbapi/size.cpp28
-rw-r--r--storage/ndb/test/ndbapi/slow_select.cpp228
-rw-r--r--storage/ndb/test/ndbapi/testBackup.cpp491
-rw-r--r--storage/ndb/test/ndbapi/testBasic.cpp1298
-rw-r--r--storage/ndb/test/ndbapi/testBasicAsynch.cpp187
-rw-r--r--storage/ndb/test/ndbapi/testBitfield.cpp198
-rw-r--r--storage/ndb/test/ndbapi/testBlobs.cpp1868
-rw-r--r--storage/ndb/test/ndbapi/testDataBuffers.cpp630
-rw-r--r--storage/ndb/test/ndbapi/testDeadlock.cpp523
-rw-r--r--storage/ndb/test/ndbapi/testDict.cpp1680
-rw-r--r--storage/ndb/test/ndbapi/testGrep.cpp540
-rw-r--r--storage/ndb/test/ndbapi/testGrepVerify.cpp118
-rw-r--r--storage/ndb/test/ndbapi/testIndex.cpp1522
-rw-r--r--storage/ndb/test/ndbapi/testInterpreter.cpp232
-rw-r--r--storage/ndb/test/ndbapi/testLcp.cpp331
-rw-r--r--storage/ndb/test/ndbapi/testMgm.cpp185
-rw-r--r--storage/ndb/test/ndbapi/testNdbApi.cpp1012
-rw-r--r--storage/ndb/test/ndbapi/testNodeRestart.cpp688
-rw-r--r--storage/ndb/test/ndbapi/testOIBasic.cpp5027
-rw-r--r--storage/ndb/test/ndbapi/testOperations.cpp641
-rw-r--r--storage/ndb/test/ndbapi/testOrderedIndex.cpp225
-rw-r--r--storage/ndb/test/ndbapi/testPartitioning.cpp430
-rw-r--r--storage/ndb/test/ndbapi/testReadPerf.cpp409
-rw-r--r--storage/ndb/test/ndbapi/testRestartGci.cpp222
-rw-r--r--storage/ndb/test/ndbapi/testScan.cpp1594
-rw-r--r--storage/ndb/test/ndbapi/testScanInterpreter.cpp281
-rw-r--r--storage/ndb/test/ndbapi/testScanPerf.cpp372
-rw-r--r--storage/ndb/test/ndbapi/testSystemRestart.cpp1244
-rw-r--r--storage/ndb/test/ndbapi/testTimeout.cpp353
-rw-r--r--storage/ndb/test/ndbapi/testTransactions.cpp412
-rw-r--r--storage/ndb/test/ndbapi/test_event.cpp489
-rw-r--r--storage/ndb/test/ndbapi/test_event_multi_table.cpp487
-rw-r--r--storage/ndb/test/ndbapi/userInterface.cpp117
-rw-r--r--storage/ndb/test/ndbnet/test.run3
-rw-r--r--storage/ndb/test/ndbnet/testError.run266
-rw-r--r--storage/ndb/test/ndbnet/testMNF.run277
-rw-r--r--storage/ndb/test/ndbnet/testNR.run60
-rw-r--r--storage/ndb/test/ndbnet/testNR1.run61
-rw-r--r--storage/ndb/test/ndbnet/testNR4.run77
-rw-r--r--storage/ndb/test/ndbnet/testSRhang.run50
-rw-r--r--storage/ndb/test/ndbnet/testTR295.run75
-rw-r--r--storage/ndb/test/newtonapi/basic_test/Makefile25
-rw-r--r--storage/ndb/test/newtonapi/basic_test/basic/Makefile14
-rw-r--r--storage/ndb/test/newtonapi/basic_test/basic/basic.cpp321
-rw-r--r--storage/ndb/test/newtonapi/basic_test/bulk_read/Makefile14
-rw-r--r--storage/ndb/test/newtonapi/basic_test/bulk_read/br_test.cpp262
-rw-r--r--storage/ndb/test/newtonapi/basic_test/common.cpp133
-rw-r--r--storage/ndb/test/newtonapi/basic_test/common.hpp66
-rw-r--r--storage/ndb/test/newtonapi/basic_test/ptr_binding/Makefile14
-rw-r--r--storage/ndb/test/newtonapi/basic_test/ptr_binding/ptr_binding_test.cpp264
-rw-r--r--storage/ndb/test/newtonapi/basic_test/too_basic.cpp105
-rw-r--r--storage/ndb/test/newtonapi/perf_test/Makefile14
-rw-r--r--storage/ndb/test/newtonapi/perf_test/perf.cpp647
-rw-r--r--storage/ndb/test/odbc/SQL99_test/Makefile26
-rw-r--r--storage/ndb/test/odbc/SQL99_test/SQL99_test.cpp2138
-rw-r--r--storage/ndb/test/odbc/SQL99_test/SQL99_test.h261
-rw-r--r--storage/ndb/test/odbc/client/Makefile95
-rw-r--r--storage/ndb/test/odbc/client/NDBT_ALLOCHANDLE.cpp53
-rw-r--r--storage/ndb/test/odbc/client/NDBT_ALLOCHANDLE_HDBC.cpp59
-rw-r--r--storage/ndb/test/odbc/client/NDBT_SQLConnect.cpp82
-rw-r--r--storage/ndb/test/odbc/client/NDBT_SQLPrepare.cpp109
-rw-r--r--storage/ndb/test/odbc/client/SQLAllocEnvTest.cpp115
-rw-r--r--storage/ndb/test/odbc/client/SQLAllocHandleTest.cpp314
-rw-r--r--storage/ndb/test/odbc/client/SQLAllocHandleTest_bf.cpp259
-rw-r--r--storage/ndb/test/odbc/client/SQLBindColTest.cpp537
-rw-r--r--storage/ndb/test/odbc/client/SQLBindParameterTest.cpp219
-rw-r--r--storage/ndb/test/odbc/client/SQLCancelTest.cpp254
-rw-r--r--storage/ndb/test/odbc/client/SQLCloseCursorTest.cpp92
-rw-r--r--storage/ndb/test/odbc/client/SQLColAttributeTest.cpp328
-rw-r--r--storage/ndb/test/odbc/client/SQLColAttributeTest1.cpp143
-rw-r--r--storage/ndb/test/odbc/client/SQLColAttributeTest2.cpp277
-rw-r--r--storage/ndb/test/odbc/client/SQLColAttributeTest3.cpp275
-rw-r--r--storage/ndb/test/odbc/client/SQLConnectTest.cpp165
-rw-r--r--storage/ndb/test/odbc/client/SQLCopyDescTest.cpp140
-rw-r--r--storage/ndb/test/odbc/client/SQLDescribeColTest.cpp260
-rw-r--r--storage/ndb/test/odbc/client/SQLDisconnectTest.cpp155
-rw-r--r--storage/ndb/test/odbc/client/SQLDriverConnectTest.cpp96
-rw-r--r--storage/ndb/test/odbc/client/SQLEndTranTest.cpp108
-rw-r--r--storage/ndb/test/odbc/client/SQLErrorTest.cpp107
-rw-r--r--storage/ndb/test/odbc/client/SQLExecDirectTest.cpp353
-rw-r--r--storage/ndb/test/odbc/client/SQLExecuteTest.cpp122
-rw-r--r--storage/ndb/test/odbc/client/SQLFetchScrollTest.cpp82
-rw-r--r--storage/ndb/test/odbc/client/SQLFetchTest.cpp438
-rw-r--r--storage/ndb/test/odbc/client/SQLFreeHandleTest.cpp195
-rw-r--r--storage/ndb/test/odbc/client/SQLFreeStmtTest.cpp182
-rw-r--r--storage/ndb/test/odbc/client/SQLGetConnectAttrTest.cpp131
-rw-r--r--storage/ndb/test/odbc/client/SQLGetCursorNameTest.cpp221
-rw-r--r--storage/ndb/test/odbc/client/SQLGetDataTest.cpp358
-rw-r--r--storage/ndb/test/odbc/client/SQLGetDescFieldTest.cpp113
-rw-r--r--storage/ndb/test/odbc/client/SQLGetDescRecTest.cpp95
-rw-r--r--storage/ndb/test/odbc/client/SQLGetDiagFieldTest.cpp236
-rw-r--r--storage/ndb/test/odbc/client/SQLGetDiagRecSimpleTest.cpp167
-rw-r--r--storage/ndb/test/odbc/client/SQLGetDiagRecTest.cpp207
-rw-r--r--storage/ndb/test/odbc/client/SQLGetEnvAttrTest.cpp110
-rw-r--r--storage/ndb/test/odbc/client/SQLGetFunctionsTest.cpp284
-rw-r--r--storage/ndb/test/odbc/client/SQLGetInfoTest.cpp215
-rw-r--r--storage/ndb/test/odbc/client/SQLGetStmtAttrTest.cpp155
-rw-r--r--storage/ndb/test/odbc/client/SQLGetTypeInfoTest.cpp202
-rw-r--r--storage/ndb/test/odbc/client/SQLMoreResultsTest.cpp91
-rw-r--r--storage/ndb/test/odbc/client/SQLNumResultColsTest.cpp202
-rw-r--r--storage/ndb/test/odbc/client/SQLParamDataTest.cpp105
-rw-r--r--storage/ndb/test/odbc/client/SQLPrepareTest.cpp285
-rw-r--r--storage/ndb/test/odbc/client/SQLPutDataTest.cpp108
-rw-r--r--storage/ndb/test/odbc/client/SQLRowCountTest.cpp203
-rw-r--r--storage/ndb/test/odbc/client/SQLSetConnectAttrTest.cpp131
-rw-r--r--storage/ndb/test/odbc/client/SQLSetCursorNameTest.cpp215
-rw-r--r--storage/ndb/test/odbc/client/SQLSetDescFieldTest.cpp100
-rw-r--r--storage/ndb/test/odbc/client/SQLSetDescRecTest.cpp99
-rw-r--r--storage/ndb/test/odbc/client/SQLSetEnvAttrTest.cpp108
-rw-r--r--storage/ndb/test/odbc/client/SQLSetStmtAttrTest.cpp108
-rw-r--r--storage/ndb/test/odbc/client/SQLTablesTest.cpp227
-rw-r--r--storage/ndb/test/odbc/client/SQLTransactTest.cpp305
-rw-r--r--storage/ndb/test/odbc/client/common.hpp81
-rw-r--r--storage/ndb/test/odbc/client/main.cpp158
-rw-r--r--storage/ndb/test/odbc/dm-iodbc/Makefile38
-rw-r--r--storage/ndb/test/odbc/dm-unixodbc/Makefile39
-rw-r--r--storage/ndb/test/odbc/driver/Makefile30
-rw-r--r--storage/ndb/test/odbc/driver/testOdbcDriver.cpp4964
-rw-r--r--storage/ndb/test/odbc/test_compiler/Makefile21
-rw-r--r--storage/ndb/test/odbc/test_compiler/test_compiler.cpp233
-rw-r--r--storage/ndb/test/run-test/16node-tests.txt733
-rw-r--r--storage/ndb/test/run-test/Makefile.am29
-rw-r--r--storage/ndb/test/run-test/README43
-rw-r--r--storage/ndb/test/run-test/README.ATRT34
-rwxr-xr-xstorage/ndb/test/run-test/atrt-analyze-result.sh12
-rwxr-xr-xstorage/ndb/test/run-test/atrt-clear-result.sh4
-rw-r--r--storage/ndb/test/run-test/atrt-example.tgzbin0 -> 2196 bytes
-rwxr-xr-xstorage/ndb/test/run-test/atrt-gather-result.sh16
-rwxr-xr-xstorage/ndb/test/run-test/atrt-mysql-test-run18
-rwxr-xr-xstorage/ndb/test/run-test/atrt-setup.sh6
-rwxr-xr-xstorage/ndb/test/run-test/atrt-testBackup6
-rw-r--r--storage/ndb/test/run-test/basic.txt763
-rw-r--r--storage/ndb/test/run-test/daily-basic-tests.txt584
-rw-r--r--storage/ndb/test/run-test/daily-devel-tests.txt210
-rw-r--r--storage/ndb/test/run-test/example.conf10
-rw-r--r--storage/ndb/test/run-test/main.cpp1004
-rwxr-xr-xstorage/ndb/test/run-test/make-config.sh465
-rwxr-xr-xstorage/ndb/test/run-test/make-html-reports.sh192
-rwxr-xr-xstorage/ndb/test/run-test/make-index.sh242
-rwxr-xr-xstorage/ndb/test/run-test/ndb-autotest.sh228
-rw-r--r--storage/ndb/test/run-test/run-test.hpp95
-rw-r--r--storage/ndb/test/src/CpcClient.cpp557
-rw-r--r--storage/ndb/test/src/HugoAsynchTransactions.cpp498
-rw-r--r--storage/ndb/test/src/HugoCalculator.cpp267
-rw-r--r--storage/ndb/test/src/HugoOperations.cpp634
-rw-r--r--storage/ndb/test/src/HugoTransactions.cpp2050
-rw-r--r--storage/ndb/test/src/Makefile.am35
-rw-r--r--storage/ndb/test/src/NDBT_Error.cpp283
-rw-r--r--storage/ndb/test/src/NDBT_Output.cpp36
-rw-r--r--storage/ndb/test/src/NDBT_ResultRow.cpp142
-rw-r--r--storage/ndb/test/src/NDBT_ReturnCodes.cpp50
-rw-r--r--storage/ndb/test/src/NDBT_Table.cpp89
-rw-r--r--storage/ndb/test/src/NDBT_Tables.cpp945
-rw-r--r--storage/ndb/test/src/NDBT_Test.cpp1188
-rw-r--r--storage/ndb/test/src/NdbBackup.cpp440
-rw-r--r--storage/ndb/test/src/NdbConfig.cpp83
-rw-r--r--storage/ndb/test/src/NdbGrep.cpp333
-rw-r--r--storage/ndb/test/src/NdbRestarter.cpp651
-rw-r--r--storage/ndb/test/src/NdbRestarts.cpp875
-rw-r--r--storage/ndb/test/src/NdbSchemaCon.cpp169
-rw-r--r--storage/ndb/test/src/NdbSchemaOp.cpp219
-rw-r--r--storage/ndb/test/src/UtilTransactions.cpp1451
-rw-r--r--storage/ndb/test/src/getarg.c608
-rw-r--r--storage/ndb/test/tools/Makefile.am30
-rw-r--r--storage/ndb/test/tools/copy_tab.cpp103
-rw-r--r--storage/ndb/test/tools/cpcc.cpp352
-rw-r--r--storage/ndb/test/tools/create_index.cpp111
-rw-r--r--storage/ndb/test/tools/hugoCalculator.cpp71
-rw-r--r--storage/ndb/test/tools/hugoFill.cpp84
-rw-r--r--storage/ndb/test/tools/hugoLoad.cpp88
-rw-r--r--storage/ndb/test/tools/hugoLockRecords.cpp96
-rw-r--r--storage/ndb/test/tools/hugoPkDelete.cpp92
-rw-r--r--storage/ndb/test/tools/hugoPkRead.cpp97
-rw-r--r--storage/ndb/test/tools/hugoPkReadRecord.cpp199
-rw-r--r--storage/ndb/test/tools/hugoPkUpdate.cpp94
-rw-r--r--storage/ndb/test/tools/hugoScanRead.cpp131
-rw-r--r--storage/ndb/test/tools/hugoScanUpdate.cpp106
-rw-r--r--storage/ndb/test/tools/old_dirs/hugoCalculator/Makefile11
-rw-r--r--storage/ndb/test/tools/old_dirs/hugoFill/Makefile11
-rw-r--r--storage/ndb/test/tools/old_dirs/hugoLoad/Makefile11
-rw-r--r--storage/ndb/test/tools/old_dirs/hugoLockRecords/Makefile9
-rw-r--r--storage/ndb/test/tools/old_dirs/hugoPkDelete/Makefile9
-rw-r--r--storage/ndb/test/tools/old_dirs/hugoPkRead/Makefile9
-rw-r--r--storage/ndb/test/tools/old_dirs/hugoPkReadRecord/Makefile11
-rw-r--r--storage/ndb/test/tools/old_dirs/hugoPkUpdate/Makefile9
-rw-r--r--storage/ndb/test/tools/old_dirs/hugoScanRead/Makefile9
-rw-r--r--storage/ndb/test/tools/old_dirs/hugoScanUpdate/Makefile9
-rw-r--r--storage/ndb/test/tools/old_dirs/restart/Makefile11
-rw-r--r--storage/ndb/test/tools/old_dirs/transproxy/Makefile29
-rw-r--r--storage/ndb/test/tools/old_dirs/verify_index/Makefile9
-rw-r--r--storage/ndb/test/tools/old_dirs/waiter/waiter.cpp56
-rw-r--r--storage/ndb/test/tools/restart.cpp84
-rw-r--r--storage/ndb/test/tools/transproxy.cpp361
-rw-r--r--storage/ndb/test/tools/verify_index.cpp91
399 files changed, 116287 insertions, 0 deletions
diff --git a/storage/ndb/test/Makefile.am b/storage/ndb/test/Makefile.am
new file mode 100644
index 00000000000..b8753668c60
--- /dev/null
+++ b/storage/ndb/test/Makefile.am
@@ -0,0 +1,8 @@
+SUBDIRS = src tools ndbapi run-test
+
+EXTRA_DIST = include
+
+dist-hook:
+ -rm -rf `find $(distdir) -type d -name SCCS`
+
+windoze-dsp:
diff --git a/storage/ndb/test/include/CpcClient.hpp b/storage/ndb/test/include/CpcClient.hpp
new file mode 100644
index 00000000000..1655bc57b56
--- /dev/null
+++ b/storage/ndb/test/include/CpcClient.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 */
+
+#ifndef __CPCCLIENT_HPP_INCLUDED__
+#define __CPCCLIENT_HPP_INCLUDED__
+
+#include <Parser.hpp>
+#include <SocketServer.hpp>
+#include <util/InputStream.hpp>
+#include <util/OutputStream.hpp>
+
+/**
+ * Simple CPC client class. The whole management client should be replaced
+ * something smarter and more worked through.
+ */
+class SimpleCpcClient {
+public:
+ SimpleCpcClient(const char *host, int port);
+ ~SimpleCpcClient();
+
+ static void run(SimpleCpcClient &);
+
+ int getPort() const { return port;}
+ const char * getHost() const { return host;}
+
+ struct Process {
+ int m_id;
+ BaseString m_name;
+
+ BaseString m_owner;
+ BaseString m_group;
+ BaseString m_runas;
+
+ BaseString m_cwd;
+ BaseString m_env;
+ BaseString m_path;
+ BaseString m_args;
+
+ BaseString m_type;
+ BaseString m_status;
+
+ BaseString m_stdin;
+ BaseString m_stdout;
+ BaseString m_stderr;
+ BaseString m_ulimit;
+ };
+
+private:
+ class ParserDummy : SocketServer::Session {
+ public:
+ ParserDummy(NDB_SOCKET_TYPE sock);
+ };
+
+ typedef Parser<ParserDummy> Parser_t;
+ typedef ParserRow<ParserDummy> ParserRow_t;
+
+ char *host;
+ int port;
+ NDB_SOCKET_TYPE cpc_sock;
+ InputStream *cpc_in;
+ OutputStream *cpc_out;
+
+public:
+ int connect();
+
+ void cmd_list(char *arg);
+ void cmd_start(char *arg);
+ void cmd_stop(char *arg);
+ void cmd_help(char *arg);
+
+ int list_processes(Vector<Process>&, Properties &reply);
+ int start_process(Uint32 id, Properties& reply);
+ int stop_process(Uint32 id, Properties& reply);
+ int undefine_process(Uint32 id, Properties& reply);
+ int define_process(Process & p, Properties& reply);
+
+private:
+ int cpc_send(const char *cmd,
+ const Properties &args);
+
+
+ Parser_t::ParserStatus cpc_recv(const ParserRow_t *syntax,
+ const Properties **reply,
+ void **user_data = NULL);
+
+ const Properties *cpc_call(const char *cmd,
+ const Properties &args,
+ const ParserRow_t *reply_syntax);
+};
+
+#endif /* !__CPCCLIENT_HPP_INCLUDED__ */
diff --git a/storage/ndb/test/include/HugoAsynchTransactions.hpp b/storage/ndb/test/include/HugoAsynchTransactions.hpp
new file mode 100644
index 00000000000..d7e6e8fc187
--- /dev/null
+++ b/storage/ndb/test/include/HugoAsynchTransactions.hpp
@@ -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 */
+
+#ifndef HUGO_ASYNCH_TRANSACTIONS_HPP
+#define HUGO_ASYNCH_TRANSACTIONS_HPP
+
+
+#include <NDBT.hpp>
+#include <HugoCalculator.hpp>
+#include <HugoTransactions.hpp>
+
+class HugoAsynchTransactions : private HugoTransactions {
+public:
+ HugoAsynchTransactions(const NdbDictionary::Table&);
+ ~HugoAsynchTransactions();
+ int loadTableAsynch(Ndb*,
+ int records = 0,
+ int batch = 1,
+ int trans = 1,
+ int operations = 1);
+ int pkReadRecordsAsynch(Ndb*,
+ int records = 0,
+ int batch= 1,
+ int trans = 1,
+ int operations = 1);
+ int pkUpdateRecordsAsynch(Ndb*,
+ int records = 0,
+ int batch= 1,
+ int trans = 1,
+ int operations = 1);
+ int pkDelRecordsAsynch(Ndb*,
+ int records = 0,
+ int batch = 1,
+ int trans = 1,
+ int operations = 1);
+ void transactionCompleted();
+
+ long getTransactionsCompleted();
+
+private:
+ enum NDB_OPERATION {NO_INSERT, NO_UPDATE, NO_READ, NO_DELETE};
+
+ void allocTransactions(int trans);
+ void deallocTransactions();
+
+ int executeAsynchOperation(Ndb*,
+ int records,
+ int batch,
+ int trans,
+ int operations,
+ NDB_OPERATION theOperation,
+ ExecType theType = Commit);
+
+ long transactionsCompleted;
+ int numTransactions;
+ NdbConnection** transactions;
+};
+
+
+
+#endif
+
diff --git a/storage/ndb/test/include/HugoCalculator.hpp b/storage/ndb/test/include/HugoCalculator.hpp
new file mode 100644
index 00000000000..03de46cd7ea
--- /dev/null
+++ b/storage/ndb/test/include/HugoCalculator.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 NDBT_CALC_HPP
+#define NDBT_CALC_HPP
+
+#include <NDBT_ResultRow.hpp>
+
+/* *************************************************************
+ * HugoCalculator
+ *
+ * Comon class for the Hugo test suite, provides the functions
+ * that is used for calculating values to load in to table and
+ * also knows how to verify a row that's been read from db
+ *
+ * ************************************************************/
+class HugoCalculator {
+public:
+ HugoCalculator(const NdbDictionary::Table& tab);
+ Int32 calcValue(int record, int attrib, int updates) const;
+ const char* calcValue(int record, int attrib, int updates, char* buf, int len) const;
+
+ int verifyRowValues(NDBT_ResultRow* const pRow) const;
+ int getIdValue(NDBT_ResultRow* const pRow) const;
+ int getUpdatesValue(NDBT_ResultRow* const pRow) const;
+ int isIdCol(int colId) { return m_idCol == colId; };
+ int isUpdateCol(int colId){ return m_updatesCol == colId; };
+private:
+ const NdbDictionary::Table& m_tab;
+ int m_idCol;
+ int m_updatesCol;
+};
+
+
+#endif
+
+
+
+
diff --git a/storage/ndb/test/include/HugoOperations.hpp b/storage/ndb/test/include/HugoOperations.hpp
new file mode 100644
index 00000000000..05137710609
--- /dev/null
+++ b/storage/ndb/test/include/HugoOperations.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 HUGO_OPERATIONS_HPP
+#define HUGO_OPERATIONS_HPP
+
+#include <NDBT.hpp>
+#include <HugoCalculator.hpp>
+#include <UtilTransactions.hpp>
+#include <Vector.hpp>
+
+class HugoOperations : public UtilTransactions {
+public:
+ HugoOperations(const NdbDictionary::Table&,
+ const NdbDictionary::Index* idx = 0);
+
+ ~HugoOperations();
+ int startTransaction(Ndb*);
+ int setTransaction(NdbTransaction*);
+ int closeTransaction(Ndb*);
+ NdbTransaction* getTransaction();
+ void refresh();
+
+ void setTransactionId(Uint64);
+
+ int pkInsertRecord(Ndb*,
+ int recordNo,
+ int numRecords = 1,
+ int updatesValue = 0);
+
+ int pkReadRecord(Ndb*,
+ int recordNo,
+ int numRecords = 1,
+ NdbOperation::LockMode lm = NdbOperation::LM_Read);
+
+ int pkUpdateRecord(Ndb*,
+ int recordNo,
+ int numRecords = 1,
+ int updatesValue = 0);
+
+ int pkDeleteRecord(Ndb*,
+ int recordNo,
+ int numRecords = 1);
+
+ int execute_Commit(Ndb*,
+ AbortOption ao = AbortOnError);
+ int execute_NoCommit(Ndb*,
+ AbortOption ao = AbortOnError);
+ int execute_Rollback(Ndb*);
+
+ int saveCopyOfRecord(int numRecords = 1);
+ int compareRecordToCopy(int numRecords = 1);
+
+ BaseString getRecordStr(int recordNum);
+ int getRecordGci(int recordNum);
+
+ int setValueForAttr(NdbOperation*,
+ int attrId,
+ int rowId,
+ int updateId);
+
+ int equalForAttr(NdbOperation*,
+ int attrId,
+ int rowId);
+
+ int setValues(NdbOperation*, int rowId, int updateId);
+
+ int verifyUpdatesValue(int updatesValue, int _numRows = 0);
+
+ int indexReadRecords(Ndb*, const char * idxName, int recordNo,
+ bool exclusive = false,
+ int records = 1);
+
+ int indexUpdateRecord(Ndb*,
+ const char * idxName,
+ int recordNo,
+ int numRecords = 1,
+ int updatesValue = 0);
+
+ int scanReadRecords(Ndb*, NdbScanOperation::LockMode =
+ NdbScanOperation::LM_CommittedRead,
+ int numRecords = 1);
+
+ NdbIndexScanOperation* pIndexScanOp;
+
+ NDBT_ResultRow& get_row(Uint32 idx) { return *rows[idx];}
+protected:
+ void allocRows(int rows);
+ void deallocRows();
+
+ Vector<NDBT_ResultRow*> rows;
+ HugoCalculator calc;
+
+ Vector<BaseString> savedRecords;
+
+ struct RsPair { NdbScanOperation* m_result_set; int records; };
+ Vector<RsPair> m_result_sets;
+ Vector<RsPair> m_executed_result_sets;
+};
+
+#endif
diff --git a/storage/ndb/test/include/HugoTransactions.hpp b/storage/ndb/test/include/HugoTransactions.hpp
new file mode 100644
index 00000000000..83493bcc2a4
--- /dev/null
+++ b/storage/ndb/test/include/HugoTransactions.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 HUGO_TRANSACTIONS_HPP
+#define HUGO_TRANSACTIONS_HPP
+
+
+#include <NDBT.hpp>
+#include <HugoCalculator.hpp>
+#include <HugoOperations.hpp>
+
+
+class HugoTransactions : public HugoOperations {
+public:
+ HugoTransactions(const NdbDictionary::Table&,
+ const NdbDictionary::Index* idx = 0);
+ ~HugoTransactions();
+ int createEvent(Ndb*);
+ int eventOperation(Ndb*, void* stats,
+ int records);
+ int loadTable(Ndb*,
+ int records,
+ int batch = 512,
+ bool allowConstraintViolation = true,
+ int doSleep = 0,
+ bool oneTrans = false);
+
+ int scanReadRecords(Ndb*,
+ int records,
+ int abort = 0,
+ int parallelism = 0,
+ NdbOperation::LockMode = NdbOperation::LM_Read);
+
+ int scanReadRecords(Ndb*,
+ const NdbDictionary::Index*,
+ int records,
+ int abort = 0,
+ int parallelism = 0,
+ NdbOperation::LockMode = NdbOperation::LM_Read,
+ bool sorted = false);
+
+ int pkReadRecords(Ndb*,
+ int records,
+ int batchsize = 1,
+ NdbOperation::LockMode = NdbOperation::LM_Read);
+
+ int scanUpdateRecords(Ndb*,
+ int records,
+ int abort = 0,
+ int parallelism = 0);
+
+ int scanUpdateRecords1(Ndb*,
+ int records,
+ int abort = 0,
+ int parallelism = 0);
+ int scanUpdateRecords2(Ndb*,
+ int records,
+ int abort = 0,
+ int parallelism = 0);
+ int scanUpdateRecords3(Ndb*,
+ int records,
+ int abort = 0,
+ int parallelism = 0);
+
+ int pkUpdateRecords(Ndb*,
+ int records,
+ int batchsize = 1,
+ int doSleep = 0);
+ int pkInterpretedUpdateRecords(Ndb*,
+ int records,
+ int batchsize = 1);
+ int pkDelRecords(Ndb*,
+ int records = 0,
+ int batch = 1,
+ bool allowConstraintViolation = true,
+ int doSleep = 0);
+ int lockRecords(Ndb*,
+ int records,
+ int percentToLock = 1,
+ int lockTime = 1000);
+ int fillTable(Ndb*,
+ int batch=512);
+
+ /**
+ * Reading using UniqHashIndex with key = pk
+ */
+ int indexReadRecords(Ndb*,
+ const char * idxName,
+ int records,
+ int batchsize = 1);
+
+ int indexUpdateRecords(Ndb*,
+ const char * idxName,
+ int records,
+ int batchsize = 1);
+
+protected:
+ NDBT_ResultRow row;
+ int m_defaultScanUpdateMethod;
+};
+
+
+
+
+#endif
+
diff --git a/storage/ndb/test/include/NDBT.hpp b/storage/ndb/test/include/NDBT.hpp
new file mode 100644
index 00000000000..657a9cb03b6
--- /dev/null
+++ b/storage/ndb/test/include/NDBT.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 NDB_TEST_HPP
+#define NDB_TEST_HPP
+
+/**
+ * NdbTest.hpp
+ * This is the main include file to include in test programs
+ * It will include all the other include files in the NDBT-toolkit
+ *
+ */
+
+#include "NDBT_ReturnCodes.h"
+
+#ifdef __cplusplus
+#include "NDBT_Table.hpp"
+#include "NDBT_Tables.hpp"
+#include "NDBT_Error.hpp"
+#include "NDBT_ResultRow.hpp"
+#include "NDBT_Output.hpp"
+
+#endif
+
+
+#endif
diff --git a/storage/ndb/test/include/NDBT_DataSet.hpp b/storage/ndb/test/include/NDBT_DataSet.hpp
new file mode 100644
index 00000000000..1a0122f617c
--- /dev/null
+++ b/storage/ndb/test/include/NDBT_DataSet.hpp
@@ -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 */
+
+#ifndef NDBT_DATA_SET_HPP
+#define NDBT_DATA_SET_HPP
+
+#include "NDBT_Table.hpp"
+#include <NdbApi.hpp>
+
+class NDBT_DataSet;
+
+class NDBT_DataSetFactory {
+public:
+ NDBT_DataSet * createEmpty(const NDBT_Table & table,
+ const char * columns[]);
+
+ NDBT_DataSet * createRandom(const NDBT_DataSet & table,
+ const char * columns[],
+ int rows);
+
+ NDBT_DataSet * createXXX(int noOfDS, const NDBT_DataSet ** datasets);
+};
+
+class NDBT_DataSet {
+ friend class NDBT_DataSetFactory;
+public:
+ /**
+ * Rows in the data set
+ */
+ void setRows(int rows);
+ void addRows(int rows);
+
+ int getNoOfRows() const;
+
+ /**
+ * Columns for a row in the data set
+ */
+ int getNoOfColumns() const;
+ int getNoOfPKs() const;
+
+ const NDBT_Attribute * getColumn(int index);
+ const NDBT_Attribute * getColumn(const char * name);
+
+ /**
+ * Data status in dataset
+ */
+ bool hasPK(int row);
+ bool hasData(int row);
+
+ /**
+ * Do all rows in the dataset have a PK
+ */
+ bool hasPK();
+
+ /**
+ * Do all rows in the dataset has data
+ */
+ bool hasData();
+
+ /**
+ * Getters for data
+ */
+ Uint32 getUInt(int row, int index) const;
+ Uint32 getUInt(int row, const char * attribute) const;
+
+ Int32 getInt(int row, int index) const;
+ Int32 getInt(int row, const char * attribute) const;
+
+ const char * getString(int row, int index) const;
+ const char * getString(int row, const char * attribute) const;
+
+ bool getIsNull(int row, int index) const;
+ bool getIsNull(int row, const char * attribute) const;
+
+ /**
+ * Setters for data
+ */
+ void set(int row, int index, Int32 value);
+ void set(int row, const char * attr, Int32 value);
+
+ void set(int row, int index, Uint32 value);
+ void set(int row, const char * attr, Uint32 value);
+
+ void set(int row, int index, const char * value);
+ void set(int row, const char * attr, const char * value);
+
+ /**
+ * Comparators
+ */
+
+ /**
+ * Is this dataset identical to other dataset
+ *
+ * If either of the datasets have "undefined" rows the answer is false
+ */
+ bool equal(const NDBT_DataSet & other) const;
+
+ /**
+ * Do these dataset have identical PK's
+ *
+ * I.e noOfRows equal
+ *
+ * and for each row there is a corresponding row in the other ds
+ * with the same pk
+ */
+ bool equalPK(const NDBT_DataSet & other) const;
+
+private:
+ NDBT_Attribute * columns;
+
+ Uint32 noOfRows;
+ Uint32 noOfPKs;
+
+ Uint32 * pks;
+ Uint32 * columnSizes;
+
+ char * pkData;
+ char * data;
+
+ char * pk(int row, int pkIndex);
+ char * column(int row, int columnIndex);
+
+ Uint32 * hasPK;
+ Uint32 * hasData;
+};
+
+#endif
diff --git a/storage/ndb/test/include/NDBT_DataSetTransaction.hpp b/storage/ndb/test/include/NDBT_DataSetTransaction.hpp
new file mode 100644
index 00000000000..9f250c566dd
--- /dev/null
+++ b/storage/ndb/test/include/NDBT_DataSetTransaction.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 NDBT_DATA_SET_TRANSACTION_HPP
+#define NDBT_DATA_SET_TRANSACTION_HPP
+
+#include "NDBT_Table.hpp"
+#include <NdbApi.hpp>
+
+class NDBT_DataSet;
+
+/**
+ * This class contains a bunch a methods
+ * that operates on NDB together with a NDBT_DataSet
+ * using <b>one</b> transaction
+ */
+class NDBT_DataSetTransaction {
+public:
+ /**
+ * Store the data into ndb
+ */
+ static void insert(Ndb * theNdbObject,
+ const NDBT_DataSet *,
+ bool rollback = false);
+
+ /**
+ * Read data (using pk) from ndb
+ */
+ static void readByPk(Ndb * theNdbObject,
+ NDBT_DataSet *,
+ bool rollback = false);
+
+ /**
+ * Update data using pk
+ *
+ */
+ static void updateByPk(Ndb * theNdbObject,
+ const NDBT_DataSet *,
+ bool rollback = false);
+
+ /**
+ * Delete
+ */
+ static void deleteByPk(Ndb * theNdbObject,
+ const NDBT_DataSet *,
+ bool rollback = false);
+};
+
+class NDBT_DataSetAsyncTransaction {
+public:
+ enum OperationType {
+ OT_Insert,
+ OT_ReadByPk,
+ OT_UpdateByPk,
+ OT_DeleteByPk
+ };
+
+ /**
+ * A callback for the NDBT_DataSetAsyncTransaction
+ * interface.
+ *
+ * The callback method returns:
+ * - the operation performed
+ * - the data set
+ * - if the transaction was commited or aborted
+ */
+ typedef (* NDBT_DataSetAsyncTransactionCallback)(OperationType,
+ const NDBT_DataSet *,
+ bool commit,
+ void * anyObject);
+ /**
+ * Store the data into ndb
+ */
+ static void insert(Ndb * theNdbObject,
+ const NDBT_DataSet *,
+ bool rollback = false,
+ NDBT_DataSetAsyncTransactionCallback fun,
+ void * anyObject);
+
+ /**
+ * Read data (using pk) from ndb
+ */
+ static void readByPk(Ndb * theNdbObject,
+ NDBT_DataSet *,
+ bool rollback = false,
+ NDBT_DataSetAsyncTransactionCallback fun,
+ void * anyObject);
+
+
+ /**
+ * Update data using pk
+ *
+ */
+ static void updateByPk(Ndb * theNdbObject,
+ const NDBT_DataSet *,
+ bool rollback = false,
+ NDBT_DataSetAsyncTransactionCallback fun,
+ void * anyObject);
+
+
+ /**
+ * Delete
+ */
+ static void deleteByPk(Ndb * theNdbObject,
+ const NDBT_DataSet *,
+ bool rollback = false,
+ NDBT_DataSetAsyncTransactionCallback fun,
+ void * anyObject);
+
+};
+
+class NDBT_DataSetBulkOperation {
+public:
+ /**
+ * Store the data into ndb
+ */
+ static void insert(Ndb * theNdbObject,
+ const NDBT_DataSet *,
+ int parallellTransactions = 1,
+ int operationsPerTransaction = 10);
+
+ /**
+ * Read data (using pk) from ndb
+ */
+ static void readByPk(Ndb * theNdbObject,
+ NDBT_DataSet *,
+ int parallellTransactions = 1,
+ int operationsPerTransaction = 10);
+
+ /**
+ * Update data using pk
+ *
+ */
+ static void updateByPk(Ndb * theNdbObject,
+ const NDBT_DataSet *,
+ int parallellTransactions = 1,
+ int operationsPerTransaction = 10);
+
+ /**
+ * Delete
+ */
+ static void deleteByPk(Ndb * theNdbObject,
+ const NDBT_DataSet *,
+ int parallellTransactions = 1,
+ int operationsPerTransaction = 10);
+};
+
+
+#endif
diff --git a/storage/ndb/test/include/NDBT_Error.hpp b/storage/ndb/test/include/NDBT_Error.hpp
new file mode 100644
index 00000000000..6775a107196
--- /dev/null
+++ b/storage/ndb/test/include/NDBT_Error.hpp
@@ -0,0 +1,101 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 NDBT_Error_HPP
+#define NDBT_Error_HPP
+
+#include <NdbOut.hpp>
+#include <NdbError.hpp>
+
+/**
+ * NDBT_Error.hpp
+ * This is the main include file about error handling in NDBT test programs
+ *
+ */
+class ErrorData {
+
+public:
+ ErrorData();
+ ~ErrorData();
+
+ /**
+ * Parse cmd line arg
+ *
+ * Return true if successeful
+ */
+ bool parseCmdLineArg(const char ** argv, int & i);
+
+ /**
+ * Print cmd line arguments
+ */
+ void printCmdLineArgs(NdbOut & out = ndbout);
+
+ /**
+ * Print settings
+ */
+ void printSettings(NdbOut & out = ndbout);
+
+ /**
+ * Print error count
+ */
+ void printErrorCounters(NdbOut & out = ndbout) const;
+
+ /**
+ * Reset error counters
+ */
+ void resetErrorCounters();
+
+ /**
+ *
+ */
+ int handleErrorCommon(const NdbError & error);
+
+private:
+ bool key_error;
+ bool temporary_resource_error;
+ bool insufficient_space_error;
+ bool node_recovery_error;
+ bool overload_error;
+ bool timeout_error;
+ bool internal_error;
+ bool user_error;
+ bool application_error;
+
+ Uint32 * errorCountArray;
+};
+
+//
+// ERR prints an NdbError object togheter with a description of where
+// the error occured
+//
+#define ERR_OUT(where, error) \
+ { where << "ERROR: " << error.code << " " \
+ << error.message << endl \
+ << " " << "Status: " << error.status \
+ << ", Classification: " << error.classification << endl\
+ << " " << "File: " << __FILE__ \
+ << " (Line: " << __LINE__ << ")" << endl \
+ ; \
+ }
+
+#define ERR(error) \
+{ \
+ const NdbError &_error= (error); \
+ ERR_OUT(g_err, _error); \
+}
+#define ERR_INFO(error) ERR_OUT(g_info, error)
+
+#endif
diff --git a/storage/ndb/test/include/NDBT_Output.hpp b/storage/ndb/test/include/NDBT_Output.hpp
new file mode 100644
index 00000000000..aaa619ac479
--- /dev/null
+++ b/storage/ndb/test/include/NDBT_Output.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 NDBT_Output_HPP
+#define NDBT_Output_HPP
+
+#include <OutputStream.hpp>
+#include <NdbOut.hpp>
+
+void setOutputLevel(int i);
+
+extern FilteredNdbOut g_err;
+extern FilteredNdbOut g_warning;
+extern FilteredNdbOut g_info;
+extern FilteredNdbOut g_debug;
+
+#endif
diff --git a/storage/ndb/test/include/NDBT_ResultRow.hpp b/storage/ndb/test/include/NDBT_ResultRow.hpp
new file mode 100644
index 00000000000..cbb5d7f6c6a
--- /dev/null
+++ b/storage/ndb/test/include/NDBT_ResultRow.hpp
@@ -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 */
+
+#ifndef NDBT_RESULTROW_HPP
+#define NDBT_RESULTROW_HPP
+
+#include <NdbApi.hpp>
+
+class NDBT_ResultRow {
+public:
+ NDBT_ResultRow(const NdbDictionary::Table &tab, char attrib_delimiter='\t');
+ ~NDBT_ResultRow();
+ NdbRecAttr * & attributeStore(int i);
+ const NdbRecAttr * attributeStore(int i) const ;
+ const NdbRecAttr * attributeStore(const char* name) const ;
+
+ BaseString c_str() const ;
+
+ NdbOut & header (NdbOut &) const;
+ friend NdbOut & operator << (NdbOut&, const NDBT_ResultRow &);
+
+ /**
+ * Make copy of NDBT_ResultRow
+ */
+ NDBT_ResultRow * clone() const;
+
+ bool operator==(const NDBT_ResultRow&) const ;
+ bool operator!=(const NDBT_ResultRow& other) const {
+ return ! (*this == other);
+ }
+
+private:
+ int cols;
+ char **names;
+ NdbRecAttr **data;
+ char ad[2];
+
+ bool m_ownData;
+ const NdbDictionary::Table & m_table;
+
+ NDBT_ResultRow(const NDBT_ResultRow &);
+ NDBT_ResultRow& operator=(const NDBT_ResultRow &);
+};
+
+
+
+
+#endif
diff --git a/storage/ndb/test/include/NDBT_ReturnCodes.h b/storage/ndb/test/include/NDBT_ReturnCodes.h
new file mode 100644
index 00000000000..0bc71ad8ceb
--- /dev/null
+++ b/storage/ndb/test/include/NDBT_ReturnCodes.h
@@ -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 */
+
+#ifndef NDBT_RETURNCODES_H
+#define NDBT_RETURNCODES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NDBT_OK 0
+#define NDBT_FAILED 1
+#define NDBT_WRONGARGS 2
+#define NDBT_TEMPORARY 3
+/**
+ * NDBT_ProgramExit
+ * This function will print the returncode togheter with a prefix on
+ * the screen and then exit the test program.
+ * Call this function when exiting the main function in your test programs
+ * Returns the return code
+ */
+int NDBT_ProgramExit(int rcode);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/storage/ndb/test/include/NDBT_Stats.hpp b/storage/ndb/test/include/NDBT_Stats.hpp
new file mode 100644
index 00000000000..28212bdba17
--- /dev/null
+++ b/storage/ndb/test/include/NDBT_Stats.hpp
@@ -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 */
+
+#ifndef NDBT_STATS_HPP
+#define NDBT_STATS_HPP
+
+#include <ndb_global.h>
+
+class NDBT_Stats {
+public:
+ NDBT_Stats() { reset(); }
+
+ void reset() { sum = sum2 = 0.0; max = DBL_MIN; ; min = DBL_MAX; n = 0;}
+
+ void addObservation(double t) {
+ sum+= t;
+ sum2 += (t*t);
+ n++;
+ if(min > t) min = t;
+ if(max < t) max = t;
+ }
+
+ double getMean() const { return sum/n;}
+ double getStddev() const { return sqrt(getVariance()); }
+ double getVariance() const { return (n*sum2 - (sum*sum))/(n*n);}
+ double getMin() const { return min;}
+ double getMax() const { return max;}
+ int getCount() const { return n;}
+
+ NDBT_Stats & operator+=(const NDBT_Stats & c){
+ sum += c.sum;
+ sum2 += c.sum2;
+ n += c.n;
+ if(min > c.min) min = c.min;
+ if(max < c.max) max = c.max;
+ return * this;
+ }
+private:
+ double sum;
+ double sum2;
+ int n;
+ double min, max;
+};
+
+inline
+double
+NDB_SQRT(double x){
+ assert(x >= 0);
+
+ double y = 0;
+ double s = 1;
+ double r = 0;
+ while(y <= x){
+ y += s;
+ s += 2;
+ r += 1;
+ }
+ return r - 1;
+}
+
+#endif
diff --git a/storage/ndb/test/include/NDBT_Table.hpp b/storage/ndb/test/include/NDBT_Table.hpp
new file mode 100644
index 00000000000..d2f99b85187
--- /dev/null
+++ b/storage/ndb/test/include/NDBT_Table.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 NDBT_TABLE_HPP
+#define NDBT_TABLE_HPP
+
+#include <ndb_global.h>
+
+#include <NdbApi.hpp>
+#include <NdbOut.hpp>
+
+class NDBT_Attribute : public NdbDictionary::Column {
+public:
+ NDBT_Attribute(const char* _name,
+ NdbDictionary::Column::Type _type,
+ int _length = 1,
+ bool _pk = false,
+ bool _nullable = false):
+ NdbDictionary::Column(_name)
+ {
+ assert(_name != 0);
+
+ setType(_type);
+ setLength(_length);
+ setNullable(_nullable);
+ setPrimaryKey(_pk);
+ }
+};
+
+class NDBT_Table : public NdbDictionary::Table {
+ /**
+ * Print meta information about table
+ * (information on how it is strored, what the attributes look like etc.)
+ */
+ friend class NdbOut& operator <<(class NdbOut&, const NDBT_Table &);
+public:
+
+ NDBT_Table(const char* name,
+ int noOfAttributes,
+ const NdbDictionary::Column attributes[])
+ : NdbDictionary::Table(name)
+ {
+ assert(name != 0);
+
+ //setStoredTable(stored);
+ for(int i = 0; i<noOfAttributes; i++)
+ addColumn(attributes[i]);
+ }
+
+ static const NdbDictionary::Table * discoverTableFromDb(Ndb* ndb,
+ const char * name);
+};
+
+inline
+const NdbDictionary::Table *
+NDBT_Table::discoverTableFromDb(Ndb* ndb, const char * name){
+ return ndb->getDictionary()->getTable(name);
+}
+
+
+/**
+ * Print meta information about index
+ * (information on how it is strored, what the attributes look like etc.)
+ */
+class NdbOut& operator <<(class NdbOut&, const NdbDictionary::Index &);
+
+
+
+#endif
diff --git a/storage/ndb/test/include/NDBT_Tables.hpp b/storage/ndb/test/include/NDBT_Tables.hpp
new file mode 100644
index 00000000000..fb0df8aa35b
--- /dev/null
+++ b/storage/ndb/test/include/NDBT_Tables.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 NDBT_TABLES_HPP
+#define NDBT_TABLES_HPP
+
+
+#include <NDBT.hpp>
+#include <Ndb.hpp>
+#include <NdbDictionary.hpp>
+#include <NDBT_Table.hpp>
+
+typedef int (* NDBT_CreateTableHook)(Ndb*, NdbDictionary::Table&, int when);
+
+class NDBT_Tables {
+public:
+
+ static int createTable(Ndb* pNdb, const char* _name, bool _temp = false,
+ bool existsOK = false, NDBT_CreateTableHook = 0);
+ static int createAllTables(Ndb* pNdb, bool _temp, bool existsOK = false);
+ static int createAllTables(Ndb* pNdb);
+
+ static int dropAllTables(Ndb* pNdb);
+
+ static int print(const char * name);
+ static int printAll();
+
+ static const NdbDictionary::Table* getTable(const char* _nam);
+ static const NdbDictionary::Table* getTable(int _num);
+ static int getNumTables();
+
+private:
+ static const NdbDictionary::Table* tableWithPkSize(const char* _nam, Uint32 pkSize);
+};
+#endif
+
+
diff --git a/storage/ndb/test/include/NDBT_Test.hpp b/storage/ndb/test/include/NDBT_Test.hpp
new file mode 100644
index 00000000000..1b9c2751f64
--- /dev/null
+++ b/storage/ndb/test/include/NDBT_Test.hpp
@@ -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 */
+
+#ifndef NDBT_TEST_HPP
+#define NDBT_TEST_HPP
+
+
+#include "NDBT_ReturnCodes.h"
+#include <Properties.hpp>
+#include <NdbThread.h>
+#include <NdbSleep.h>
+#include <NdbCondition.h>
+#include <NdbTimer.hpp>
+#include <Vector.hpp>
+#include <NdbApi.hpp>
+#include <NdbDictionary.hpp>
+
+class NDBT_Step;
+class NDBT_TestCase;
+class NDBT_TestSuite;
+class NDBT_TestCaseImpl1;
+
+class NDBT_Context {
+public:
+ Ndb_cluster_connection& m_cluster_connection;
+
+ NDBT_Context(Ndb_cluster_connection&);
+ ~NDBT_Context();
+ const NdbDictionary::Table* getTab();
+ NDBT_TestSuite* getSuite();
+ NDBT_TestCase* getCase();
+
+ // Get arguments
+ int getNumRecords() const;
+ int getNumLoops() const;
+ char * getRemoteMgm() const;
+ // Common place to store state between
+ // steps, for example information from one step to the
+ // verifier about how many records have been inserted
+ Uint32 getProperty(const char*, Uint32 = 0 );
+ const char* getProperty(const char*, const char* );
+ void setProperty(const char*, Uint32);
+ void setProperty(const char*, const char*);
+
+ // Signal that a property value that another
+ // thread might be waiting for has changed
+ void broadcast();
+ // Wait for the signal that a property has changed
+ void wait();
+ void wait_timeout(int msec);
+
+ // Wait until the property has been set to a certain value
+ bool getPropertyWait(const char*, Uint32);
+ const char* getPropertyWait(const char*, const char* );
+
+ void decProperty(const char *);
+
+ // Communicate with other tests
+ void stopTest();
+ bool isTestStopped();
+
+ // Communicate with tests in other API nodes
+ // This is done using a "system" table in the database
+ Uint32 getDbProperty(const char*);
+ bool setDbProperty(const char*, Uint32);
+
+ void setTab(const NdbDictionary::Table*);
+ void setRemoteMgm(char * mgm);
+
+ /**
+ * Get no of steps running/completed
+ */
+ int getNoOfRunningSteps() const ;
+ int getNoOfCompletedSteps() const ;
+
+ /**
+ * Thread sync
+ */
+ void sync_down(const char * key);
+ void sync_up_and_wait(const char * key, Uint32 count = 0);
+private:
+ friend class NDBT_Step;
+ friend class NDBT_TestSuite;
+ friend class NDBT_TestCase;
+ friend class NDBT_TestCaseImpl1;
+
+ void setSuite(NDBT_TestSuite*);
+ void setCase(NDBT_TestCase*);
+ void setNumRecords(int);
+ void setNumLoops(int);
+ const NdbDictionary::Table* tab;
+ NDBT_TestSuite* suite;
+ NDBT_TestCase* testcase;
+ Ndb* ndb;
+ int records;
+ int loops;
+ bool stopped;
+ char * remote_mgm;
+ Properties props;
+ NdbMutex* propertyMutexPtr;
+ NdbCondition* propertyCondPtr;
+};
+
+typedef int (NDBT_TESTFUNC)(NDBT_Context*, NDBT_Step*);
+
+class NDBT_Step {
+public:
+ NDBT_Step(NDBT_TestCase* ptest,
+ const char* pname,
+ NDBT_TESTFUNC* pfunc);
+ virtual ~NDBT_Step() {}
+ int execute(NDBT_Context*);
+ virtual int setUp(Ndb_cluster_connection&) = 0;
+ virtual void tearDown() = 0;
+ void setContext(NDBT_Context*);
+ NDBT_Context* getContext();
+ void print();
+ const char* getName() { return name; }
+ int getStepNo() { return step_no; }
+ void setStepNo(int n) { step_no = n; }
+protected:
+ NDBT_Context* m_ctx;
+ const char* name;
+ NDBT_TESTFUNC* func;
+ NDBT_TestCase* testcase;
+ int step_no;
+};
+
+class NDBT_NdbApiStep : public NDBT_Step {
+public:
+ NDBT_NdbApiStep(NDBT_TestCase* ptest,
+ const char* pname,
+ NDBT_TESTFUNC* pfunc);
+ virtual ~NDBT_NdbApiStep() {}
+ virtual int setUp(Ndb_cluster_connection&);
+ virtual void tearDown();
+
+ Ndb* getNdb();
+protected:
+ Ndb* ndb;
+};
+
+class NDBT_ParallelStep : public NDBT_NdbApiStep {
+public:
+ NDBT_ParallelStep(NDBT_TestCase* ptest,
+ const char* pname,
+ NDBT_TESTFUNC* pfunc);
+ virtual ~NDBT_ParallelStep() {}
+};
+
+class NDBT_Verifier : public NDBT_NdbApiStep {
+public:
+ NDBT_Verifier(NDBT_TestCase* ptest,
+ const char* name,
+ NDBT_TESTFUNC* func);
+ virtual ~NDBT_Verifier() {}
+};
+
+class NDBT_Initializer : public NDBT_NdbApiStep {
+public:
+ NDBT_Initializer(NDBT_TestCase* ptest,
+ const char* name,
+ NDBT_TESTFUNC* func);
+ virtual ~NDBT_Initializer() {}
+};
+
+class NDBT_Finalizer : public NDBT_NdbApiStep {
+public:
+ NDBT_Finalizer(NDBT_TestCase* ptest,
+ const char* name,
+ NDBT_TESTFUNC* func);
+ virtual ~NDBT_Finalizer() {}
+};
+
+
+class NDBT_TestCase {
+public:
+ NDBT_TestCase(NDBT_TestSuite* psuite,
+ const char* name,
+ const char* comment);
+ virtual ~NDBT_TestCase() {}
+
+ // This is the default executor of a test case
+ // When a test case is executed it will need to be suplied with a number of
+ // different parameters and settings, these are passed to the test in the
+ // NDBT_Context object
+ virtual int execute(NDBT_Context*);
+ void setProperty(const char*, Uint32);
+ void setProperty(const char*, const char*);
+ virtual void print() = 0;
+ virtual void printHTML() = 0;
+
+ const char* getName(){return name;};
+ virtual bool tableExists(NdbDictionary::Table* aTable) = 0;
+ virtual bool isVerify(const NdbDictionary::Table* aTable) = 0;
+
+ virtual void saveTestResult(const NdbDictionary::Table* ptab, int result) = 0;
+ virtual void printTestResult() = 0;
+ void initBeforeTest(){ timer.doReset();};
+
+ /**
+ * Get no of steps running/completed
+ */
+ virtual int getNoOfRunningSteps() const = 0;
+ virtual int getNoOfCompletedSteps() const = 0;
+
+protected:
+ virtual int runInit(NDBT_Context* ctx) = 0;
+ virtual int runSteps(NDBT_Context* ctx) = 0;
+ virtual int runVerifier(NDBT_Context* ctx) = 0;
+ virtual int runFinal(NDBT_Context* ctx) = 0;
+ virtual void addTable(const char* aTableName, bool isVerify=true) = 0;
+
+ void startTimer(NDBT_Context*);
+ void stopTimer(NDBT_Context*);
+ void printTimer(NDBT_Context*);
+
+ BaseString _name;
+ BaseString _comment;
+ const char* name;
+ const char* comment;
+ NDBT_TestSuite* suite;
+ Properties props;
+ NdbTimer timer;
+ bool isVerifyTables;
+};
+
+static const int FAILED_TO_CREATE = 1000;
+static const int FAILED_TO_DISCOVER = 1001;
+
+
+class NDBT_TestCaseResult{
+public:
+ NDBT_TestCaseResult(const char* name, int _result, NDB_TICKS _ticks):
+ m_result(_result){
+ m_name.assign(name);
+ m_ticks = _ticks;
+
+ };
+ const char* getName(){return m_name.c_str(); };
+ int getResult(){return m_result; };
+ const char* getTimeStr(){
+ // Convert to Uint32 in order to be able to print it to screen
+ Uint32 lapTime = (Uint32)m_ticks;
+ Uint32 secTime = lapTime/1000;
+ BaseString::snprintf(buf, 255, "%d secs (%d ms)", secTime, lapTime);
+ return buf;
+ }
+private:
+ char buf[255];
+ int m_result;
+ BaseString m_name;
+ NDB_TICKS m_ticks;
+};
+
+class NDBT_TestCaseImpl1 : public NDBT_TestCase {
+public:
+ NDBT_TestCaseImpl1(NDBT_TestSuite* psuite,
+ const char* name,
+ const char* comment);
+ virtual ~NDBT_TestCaseImpl1();
+ int addStep(NDBT_Step*);
+ int addVerifier(NDBT_Verifier*);
+ int addInitializer(NDBT_Initializer*);
+ int addFinalizer(NDBT_Finalizer*);
+ void addTable(const char*, bool);
+ bool tableExists(NdbDictionary::Table*);
+ bool isVerify(const NdbDictionary::Table*);
+ void reportStepResult(const NDBT_Step*, int result);
+ // int execute(NDBT_Context* ctx);
+ int runInit(NDBT_Context* ctx);
+ int runSteps(NDBT_Context* ctx);
+ int runVerifier(NDBT_Context* ctx);
+ int runFinal(NDBT_Context* ctx);
+ void print();
+ void printHTML();
+
+ virtual int getNoOfRunningSteps() const;
+ virtual int getNoOfCompletedSteps() const;
+private:
+ static const int NORESULT = 999;
+
+ void saveTestResult(const NdbDictionary::Table* ptab, int result);
+ void printTestResult();
+
+ void startStepInThread(int stepNo, NDBT_Context* ctx);
+ void waitSteps();
+ Vector<NDBT_Step*> steps;
+ Vector<NdbThread*> threads;
+ Vector<int> results;
+ Vector<NDBT_Verifier*> verifiers;
+ Vector<NDBT_Initializer*> initializers;
+ Vector<NDBT_Finalizer*> finalizers;
+ Vector<const NdbDictionary::Table*> testTables;
+ Vector<NDBT_TestCaseResult*> testResults;
+ unsigned numStepsFail;
+ unsigned numStepsOk;
+ unsigned numStepsCompleted;
+ NdbMutex* waitThreadsMutexPtr;
+ NdbCondition* waitThreadsCondPtr;
+};
+
+
+// A NDBT_TestSuite is a collection of TestCases
+// the test suite will know how to execute the test cases
+class NDBT_TestSuite {
+public:
+ NDBT_TestSuite(const char* name);
+ ~NDBT_TestSuite();
+
+ // Default executor of a test suite
+ // supply argc and argv as parameters
+ int execute(int, const char**);
+
+
+ // These function can be used from main in the test program
+ // to control the behaviour of the testsuite
+ void setCreateTable(bool); // Create table before test func is called
+ void setCreateAllTables(bool); // Create all tables before testsuite is executed
+
+ // Prints the testsuite, testcases and teststeps
+ void printExecutionTree();
+ void printExecutionTreeHTML();
+
+ // Prints list of testcases
+ void printCases();
+
+ // Print summary of executed tests
+ void printTestCaseSummary(const char* tcname = NULL);
+
+ /**
+ * Returns current date and time in the format of 2002-12-04 10:00:01
+ */
+ const char* getDate();
+
+ // Returns true if timing info should be printed
+ bool timerIsOn();
+
+
+ int addTest(NDBT_TestCase* pTest);
+private:
+ int executeOne(Ndb_cluster_connection&,
+ const char* _tabname, const char* testname = NULL);
+ int executeAll(Ndb_cluster_connection&,
+ const char* testname = NULL);
+ void execute(Ndb_cluster_connection&,
+ Ndb*, const NdbDictionary::Table*, const char* testname = NULL);
+
+ int report(const char* _tcname = NULL);
+ int reportAllTables(const char* );
+ const char* name;
+ char* remote_mgm;
+ int numTestsOk;
+ int numTestsFail;
+ int numTestsExecuted;
+ Vector<NDBT_TestCase*> tests;
+ NDBT_Context* ctx;
+ int records;
+ int loops;
+ int timer;
+ NdbTimer testSuiteTimer;
+ bool createTable;
+};
+
+
+
+#define NDBT_TESTSUITE(suitname) \
+class C##suitname : public NDBT_TestSuite { \
+public: \
+C##suitname():NDBT_TestSuite(#suitname){ \
+ NDBT_TestCaseImpl1* pt; pt = NULL; \
+ NDBT_Step* pts; pts = NULL; \
+ NDBT_Verifier* ptv; ptv = NULL; \
+ NDBT_Initializer* pti; pti = NULL; \
+ NDBT_Finalizer* ptf; ptf = NULL;
+
+#define TESTCASE(testname, comment) \
+ pt = new NDBT_TestCaseImpl1(this, testname, comment); \
+ addTest(pt);
+
+#define TC_PROPERTY(propname, propval) \
+ pt->setProperty(propname, propval);
+
+#define STEP(stepfunc) \
+ pts = new NDBT_ParallelStep(pt, #stepfunc, stepfunc); \
+ pt->addStep(pts);
+
+// Add a number of equal steps to the testcase
+#define STEPS(stepfunc, num) \
+ { int i; for (i = 0; i < num; i++){ \
+ pts = new NDBT_ParallelStep(pt, #stepfunc, stepfunc); \
+ pt->addStep(pts);\
+ } }
+
+#define VERIFIER(stepfunc) \
+ ptv = new NDBT_Verifier(pt, #stepfunc, stepfunc); \
+ pt->addVerifier(ptv);
+
+#define INITIALIZER(stepfunc) \
+ pti = new NDBT_Initializer(pt, #stepfunc, stepfunc); \
+ pt->addInitializer(pti);
+
+#define FINALIZER(stepfunc) \
+ ptf = new NDBT_Finalizer(pt, #stepfunc, stepfunc); \
+ pt->addFinalizer(ptf);
+
+// Test case can be run only on this table(s), can be multiple tables
+// Ex TABLE("T1")
+// TABLE("T3")
+// Means test will only be run on T1 and T3
+#define TABLE(tableName) \
+ pt->addTable(tableName, true);
+
+// Test case can be run on all tables except
+// Ex NOT_TABLE("T10")
+// Means test will be run on all tables execept T10
+#define NOT_TABLE(tableName) \
+ pt->addTable(tableName, false);
+
+#define NDBT_TESTSUITE_END(suitname) \
+ } } ; C##suitname suitname
+
+// Helper functions for retrieving variables from NDBT_Step
+#define GETNDB(ps) ((NDBT_NdbApiStep*)ps)->getNdb()
+
+#endif
diff --git a/storage/ndb/test/include/NdbBackup.hpp b/storage/ndb/test/include/NdbBackup.hpp
new file mode 100644
index 00000000000..e2e672b8a72
--- /dev/null
+++ b/storage/ndb/test/include/NdbBackup.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 NDBT_BACKUP_HPP
+#define NDBT_BACKUP_HPP
+
+#include <mgmapi.h>
+#include <Vector.hpp>
+#include "NdbConfig.hpp"
+#include <NdbRestarter.hpp>
+
+class NdbBackup : public NdbConfig {
+public:
+ NdbBackup(int _own_id, const char* _addr = 0)
+ : NdbConfig(_own_id, _addr) {};
+
+ int start(unsigned & _backup_id);
+ int restore(unsigned _backup_id);
+
+ int NFMaster(NdbRestarter& _restarter);
+ int NFMasterAsSlave(NdbRestarter& _restarter);
+ int NFSlave(NdbRestarter& _restarter);
+ int NF(NdbRestarter& _restarter, int *NFDuringBackup_codes, const int sz, bool onMaster);
+
+ int FailMaster(NdbRestarter& _restarter);
+ int FailMasterAsSlave(NdbRestarter& _restarter);
+ int FailSlave(NdbRestarter& _restarter);
+ int Fail(NdbRestarter& _restarter, int *Fail_codes, const int sz, bool onMaster);
+
+private:
+
+ int execRestore(bool _restore_data,
+ bool _restore_meta,
+ int _node_id,
+ unsigned _backup_id);
+
+ const char * getBackupDataDirForNode(int _node_id);
+
+};
+
+#endif
diff --git a/storage/ndb/test/include/NdbConfig.hpp b/storage/ndb/test/include/NdbConfig.hpp
new file mode 100644
index 00000000000..19439fafbb2
--- /dev/null
+++ b/storage/ndb/test/include/NdbConfig.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 NDBT_CONFIG_HPP
+#define NDBT_CONFIG_HPP
+
+#include <ndb_types.h>
+#include <mgmapi.h>
+#include <Vector.hpp>
+#include <NdbRestarter.hpp>
+#include <mgmapi_config_parameters.h>
+
+class NdbConfig : public NdbRestarter {
+public:
+ NdbConfig(int own_id, const char* addr = 0)
+ : NdbRestarter(addr),
+ ownNodeId(own_id) {};
+
+ bool getProperty(unsigned nodeid, unsigned type, unsigned key, Uint32 * val);
+
+ bool getHostName(unsigned int node_id, const char ** hostname);
+ //protected:
+ int ownNodeId;
+};
+
+#endif
diff --git a/storage/ndb/test/include/NdbGrep.hpp b/storage/ndb/test/include/NdbGrep.hpp
new file mode 100644
index 00000000000..31c49d1e4da
--- /dev/null
+++ b/storage/ndb/test/include/NdbGrep.hpp
@@ -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 */
+
+#ifndef NDBT_GREP_HPP
+#define NDBT_GREP_HPP
+
+#include <mgmapi.h>
+#include <Vector.hpp>
+#include "NdbConfig.hpp"
+#include <NdbRestarter.hpp>
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+class NdbGrep : public NdbConfig {
+public:
+ NdbGrep(int _own_id, const char* _addr = 0)
+ : NdbConfig(_own_id, _addr) {};
+
+ int start();
+ int stop();
+ int query();
+
+
+ int verify(NDBT_Context* ctx);
+
+
+ int NFMaster(NdbRestarter& _restarter);
+ int NFMasterAsSlave(NdbRestarter& _restarter);
+ int NFSlave(NdbRestarter& _restarter);
+ int NF(NdbRestarter& _restarter, int *NFDuringGrep_codes, const int sz, bool onMaster);
+
+ int FailMaster(NdbRestarter& _restarter);
+ int FailMasterAsSlave(NdbRestarter& _restarter);
+ int FailSlave(NdbRestarter& _restarter);
+ int Fail(NdbRestarter& _restarter, int *Fail_codes, const int sz, bool onMaster);
+
+private:
+
+};
+
+#endif
diff --git a/storage/ndb/test/include/NdbRestarter.hpp b/storage/ndb/test/include/NdbRestarter.hpp
new file mode 100644
index 00000000000..19a88b4f8ad
--- /dev/null
+++ b/storage/ndb/test/include/NdbRestarter.hpp
@@ -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 */
+
+#ifndef NDBT_RESTARTER_HPP
+#define NDBT_RESTARTER_HPP
+
+#include <mgmapi.h>
+#include <Vector.hpp>
+#include <BaseString.hpp>
+
+class NdbRestarter {
+public:
+ NdbRestarter(const char* _addr = 0);
+ ~NdbRestarter();
+
+ int getDbNodeId(int _i);
+
+ int restartOneDbNode(int _nodeId,
+ bool initial = false,
+ bool nostart = false,
+ bool abort = false);
+
+ int restartAll(bool initial = false,
+ bool nostart = false,
+ bool abort = false);
+
+ int startAll();
+ int startNodes(int * _nodes, int _num_nodes);
+ int waitClusterStarted(unsigned int _timeout = 120);
+ int waitClusterSingleUser(unsigned int _timeout = 120);
+ int waitClusterStartPhase(int _startphase, unsigned int _timeout = 120);
+ int waitClusterNoStart(unsigned int _timeout = 120);
+ int waitNodesStarted(int * _nodes, int _num_nodes,
+ unsigned int _timeout = 120);
+ int waitNodesStartPhase(int * _nodes, int _num_nodes,
+ int _startphase, unsigned int _timeout = 120);
+ int waitNodesNoStart(int * _nodes, int _num_nodes,
+ unsigned int _timeout = 120);
+
+
+ int getNumDbNodes();
+ int insertErrorInNode(int _nodeId, int error);
+ int insertErrorInAllNodes(int error);
+
+ int enterSingleUserMode(int _nodeId);
+ int exitSingleUserMode();
+
+ int dumpStateOneNode(int _nodeId, int * _args, int _num_args);
+ int dumpStateAllNodes(int * _args, int _num_args);
+
+ int getMasterNodeId();
+ int getRandomNodeOtherNodeGroup(int nodeId, int randomNumber);
+ int getRandomNotMasterNodeId(int randomNumber);
+
+protected:
+
+ int waitClusterState(ndb_mgm_node_status _status,
+ unsigned int _timeout,
+ int _startphase = -1);
+
+ int waitNodesState(int * _nodes, int _num_nodes,
+ ndb_mgm_node_status _status,
+ unsigned int _timeout,
+ int _startphase = -1);
+
+ bool isConnected();
+ int connect();
+ void disconnect();
+ int getStatus();
+
+ Vector<ndb_mgm_node_state> ndbNodes;
+ Vector<ndb_mgm_node_state> mgmNodes;
+ Vector<ndb_mgm_node_state> apiNodes;
+
+ bool connected;
+ BaseString addr;
+ NdbMgmHandle handle;
+ ndb_mgm_configuration * m_config;
+protected:
+ ndb_mgm_configuration * getConfig();
+};
+
+#endif
diff --git a/storage/ndb/test/include/NdbRestarts.hpp b/storage/ndb/test/include/NdbRestarts.hpp
new file mode 100644
index 00000000000..aabcd7b9975
--- /dev/null
+++ b/storage/ndb/test/include/NdbRestarts.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 NDBT_RESTARTS_HPP
+#define NDBT_RESTARTS_HPP
+
+#include <NdbRestarter.hpp>
+#include <NdbTick.h>
+#include <random.h>
+
+/**
+ * This class is used to test Ndb's ability to handle
+ * node- and system-restarts.
+ * For example:
+ * Node restart: Restart one node in the cluster.
+ * System restart: Restart all nodes in the cluster.
+ * Node crash: Crash one node in the middle of execution and bring it up again.
+ * Multiple node crash: Crash multiple nodes with a few seconds or milliseconds delay between.
+ * Initial node restart: Restart one node in the cluster without a filesystem on disk.
+ *
+ * Each restart type is represented by a NdbRestart class and a collection of these are stored
+ * in the NdbRestarts class.
+ *
+ * This class may be used from other programs to execute a particular restart.
+ *
+ */
+
+
+class NdbRestarts {
+public:
+ NdbRestarts(const char* _addr = 0):
+ m_restarter(_addr)
+ {
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ }
+
+ enum NdbRestartType{
+ NODE_RESTART,
+ MULTIPLE_NODE_RESTART,
+ SYSTEM_RESTART
+ };
+
+ struct NdbRestart {
+ typedef int (restartFunc)(NdbRestarter&, const NdbRestart*);
+
+ NdbRestart(const char* _name,
+ NdbRestartType _type,
+ restartFunc* _func,
+ int _requiredNodes,
+ int _arg1 = -1);
+
+ const char * m_name;
+ NdbRestartType m_type;
+ restartFunc* m_restartFunc;
+ int m_numRequiredNodes;
+ int m_arg1;
+
+ };
+
+ int getNumRestarts();
+
+ int executeRestart(int _num, unsigned int _timeout = 120);
+ int executeRestart(const char* _name, unsigned int _timeout = 120);
+
+ void listRestarts();
+ void listRestarts(NdbRestartType _type);
+private:
+ int executeRestart(const NdbRestart*, unsigned int _timeout);
+
+ struct NdbErrorInsert {
+ NdbErrorInsert(const char* _name,
+ int _errorNo);
+
+ const char * m_name;
+ int m_errorNo;
+
+ public:
+ const char* getName();
+ };
+
+ int getNumErrorInserts();
+ const NdbErrorInsert* getError(int _num);
+ const NdbErrorInsert* getRandomError();
+
+ static const NdbErrorInsert m_errors[];
+ static const int m_NoOfErrors;
+
+ const NdbRestart* getRestart(int _num);
+ const NdbRestart* getRestart(const char* _name);
+
+ static const NdbRestart m_restarts[];
+ static const int m_NoOfRestarts;
+
+ NdbRestarter m_restarter;
+};
+
+
+
+
+
+
+
+
+
+
+
+#endif
diff --git a/storage/ndb/test/include/NdbSchemaCon.hpp b/storage/ndb/test/include/NdbSchemaCon.hpp
new file mode 100644
index 00000000000..313daf0094b
--- /dev/null
+++ b/storage/ndb/test/include/NdbSchemaCon.hpp
@@ -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 */
+
+#ifndef NdbSchemaCon_H
+#define NdbSchemaCon_H
+
+#ifndef DOXYGEN_SHOULD_SKIP_DEPRECATED
+
+#include <ndb_types.h>
+#include "NdbError.hpp"
+#include <NdbSchemaOp.hpp>
+
+class NdbSchemaOp;
+class Ndb;
+class NdbApiSignal;
+
+/**
+ * @class NdbSchemaCon
+ * @brief Represents a schema transaction.
+ *
+ * When creating a new table,
+ * the first step is to get a NdbSchemaCon object to represent
+ * the schema transaction.
+ * This is done by calling Ndb::startSchemaTransaction.
+ *
+ * The next step is to get a NdbSchemaOp object by calling
+ * NdbSchemaCon::getNdbSchemaOp.
+ * The NdbSchemaOp object then has methods to define the table and
+ * its attributes.
+ *
+ * Finally, the NdbSchemaCon::execute method inserts the table
+ * into the database.
+ *
+ * @note Currently only one table can be added per transaction.
+ * @note Depricated, use NdbDictionary
+ */
+class NdbSchemaCon
+{
+friend class Ndb;
+friend class NdbSchemaOp;
+
+public:
+
+ static
+ NdbSchemaCon* startSchemaTrans(Ndb* pNdb){
+ return new NdbSchemaCon(pNdb);
+ }
+
+ static
+ void closeSchemaTrans(NdbSchemaCon* pSchCon){
+ delete pSchCon;
+ }
+
+
+ /**
+ * Execute a schema transaction.
+ *
+ * @return 0 if successful otherwise -1.
+ */
+ int execute();
+
+ /**
+ * Get a schemaoperation.
+ *
+ * @note Currently, only one operation per transaction is allowed.
+ *
+ * @return Pointer to a NdbSchemaOp or NULL if unsuccessful.
+ */
+ NdbSchemaOp* getNdbSchemaOp();
+
+ /**
+ * Get the latest error
+ *
+ * @return Error object.
+ */
+ const NdbError & getNdbError() const;
+
+private:
+
+/******************************************************************************
+ * These are the create and delete methods of this class.
+ *****************************************************************************/
+
+ NdbSchemaCon(Ndb* aNdb);
+ ~NdbSchemaCon();
+
+/******************************************************************************
+ * These are the private methods of this class.
+ *****************************************************************************/
+
+ void release(); // Release all schemaop in schemaCon
+
+ /***************************************************************************
+ * These methods are service methods to other classes in the NDBAPI.
+ ***************************************************************************/
+
+ int checkMagicNumber(); // Verify correct object
+ int receiveDICTTABCONF(NdbApiSignal* aSignal);
+ int receiveDICTTABREF(NdbApiSignal* aSignal);
+
+
+ int receiveCREATE_INDX_CONF(NdbApiSignal*);
+ int receiveCREATE_INDX_REF(NdbApiSignal*);
+ int receiveDROP_INDX_CONF(NdbApiSignal*);
+ int receiveDROP_INDX_REF(NdbApiSignal*);
+
+
+/*****************************************************************************
+ * These are the private variables of this class.
+ *****************************************************************************/
+
+
+ NdbError theError; // Errorcode
+ Ndb* theNdb; // Pointer to Ndb object
+
+ NdbSchemaOp* theFirstSchemaOpInList; // First operation in operation list.
+ int theMagicNumber; // Magic number
+};
+
+inline
+int
+NdbSchemaCon::checkMagicNumber()
+{
+ if (theMagicNumber != 0x75318642)
+ return -1;
+ return 0;
+}//NdbSchemaCon::checkMagicNumber()
+
+
+
+#endif
+#endif
+
+
diff --git a/storage/ndb/test/include/NdbSchemaOp.hpp b/storage/ndb/test/include/NdbSchemaOp.hpp
new file mode 100644
index 00000000000..1edbc155643
--- /dev/null
+++ b/storage/ndb/test/include/NdbSchemaOp.hpp
@@ -0,0 +1,546 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 NdbSchemaOp_H
+#define NdbSchemaOp_H
+
+#include <NdbDictionary.hpp>
+
+#ifndef DOXYGEN_SHOULD_SKIP_DEPRECATED
+
+ /**
+ * Type of attribute
+ *
+ * NOTE! AttrType is deprecated, use NdbDictionary::Column::Type instead!
+ */
+ enum AttrType {
+ Signed, ///< Attributes of this type can be read with:
+ ///< NdbRecAttr::int64_value,
+ ///< NdbRecAttr::int32_value,
+ ///< NdbRecAttr::short_value,
+ ///< NdbRecAttr::char_value
+ UnSigned, ///< Attributes of this type can be read with:
+ ///< NdbRecAttr::u_64_value,
+ ///< NdbRecAttr::u_32_value,
+ ///< NdbRecAttr::u_short_value,
+ ///< NdbRecAttr::u_char_value
+ Float, ///< Attributes of this type can be read with:
+ ///< NdbRecAttr::float_value and
+ ///< NdbRecAttr::double_value
+ String, ///< Attributes of this type can be read with:
+ ///< NdbRecAttr::aRef,
+ ///< NdbRecAttr::getAttributeObject
+ NoAttrTypeDef ///< Used for debugging only
+ };
+
+
+ /**
+ * @deprecated
+ */
+ enum NullAttributeType {
+ NoNullTypeDefined = -1,
+ NotNullAttribute,
+ NullAttribute,
+ AttributeDefined
+ };
+ /**
+ * Indicates whether the attribute is part of a primary key or not
+ */
+ enum KeyType {
+ Undefined = -1, ///< Used for debugging only
+ NoKey, ///< Attribute is not part of primary key
+ ///< or tuple identity
+ TupleKey, ///< Attribute is part of primary key
+ TupleId ///< Attribute is part of tuple identity
+ ///< (This type of attribute is created
+ ///< internally, and should not be
+ ///< manually created.)
+ };
+ /**
+ * Indicate whether the attribute should be stored on disk or not
+ */
+ enum StorageMode {
+ MMBased = 0, ///< Main memory
+ DiskBased = 1, ///< Disk (Not yet supported.)
+ NoStorageTypeDef ///< Used for debugging only
+ };
+
+ /**
+ * Type of fragmentation used for a table
+ */
+ enum FragmentType {
+ Default = 0, ///< (All is default!)
+ Single = 1, ///< Only one fragment
+ All = 2, ///< Default value. One fragment per node group
+ DistributionGroup = 3, ///< Distribution Group used for fragmentation.
+ ///< One fragment per node group
+ DistributionKey = 4, ///< Distribution Key used for fragmentation.
+ ///< One fragment per node group.
+ AllLarge = 5, ///< Sixten fragments per node group.
+ DGroupLarge = 6, ///< Distribution Group used for fragmentation.
+ ///< Sixten fragments per node group
+ DKeyLarge = 7 ///< Distribution Key used for fragmentation.
+ ///< Sixten fragments per node group
+ };
+
+ /**
+ * Type of table or index.
+ */
+ enum TableType {
+ UndefTableType = 0,
+ SystemTable = 1, ///< Internal.Table cannot be updated by user
+ UserTable = 2, ///< Normal application table
+ UniqueHashIndex = 3, ///< Unique un-ordered hash index
+ HashIndex = 4, ///< Non-unique un-ordered hash index
+ UniqueOrderedIndex = 5, ///< Unique ordered index
+ OrderedIndex = 6 ///< Non-unique ordered index
+ };
+
+
+class NdbSchemaCon;
+class Ndb;
+
+
+/**
+ * @class NdbSchemaOp
+ * @brief Represents various operations for use in schema transactions
+ *
+ * This class is used for schema operations, e.g. creating tables and
+ * attributes.
+ *
+ * The NdbSchemaOp object is created using NdbSchemaCon::getNdbSchemaOp.
+ *
+ * @note This class is depricated and is now replaced with the class
+ * NdbDictionary.
+ */
+class NdbSchemaOp
+{
+ friend class Ndb;
+ friend class NdbSchemaCon;
+
+public:
+
+
+ /**
+ * Create a new table in the database.
+ *
+ * @note The NdbSchemaCon should be closed with
+ * Ndb::closeSchemaTransaction, even if this method fails.
+ *
+ * @param aTableName Table name. Should not be NULL.
+ * @param aTableSize (Performance parameter.)
+ * Initial size of the data part of the table
+ * expressed in kByte.
+ * The database handles
+ * bad parameter setting but at a certain
+ * loss in performance.
+ * The size given here is
+ * the initial size allocated for the table
+ * storage (the data part).
+ * When calculating the data storage one should
+ * add the size of all attributes (each attribute
+ * consumes at least 4 bytes) and also an overhead
+ * of 12 byte.
+ * Variable size attributes (not supported yet)
+ * will have a size of 12 bytes plus the actual
+ * data storage parts where there is an
+ * additional overhead based on the size of the
+ * variable part.
+ * <br>
+ * An example table with 5 attributes:
+ * one 64 bit attribute, one 32 bit attribute,
+ * two 16 bit attributes and one array of 64 8 bits.
+ * This table will consume
+ * 12 (overhead) + 8 + 4 + 2*4 (4 is minimum) + 64 =
+ * 96 bytes per record.
+ * Additionally an overhead of about 2 % as page
+ * headers and waste should be allocated.
+ * Thus, 1 million records should consume 96 MBytes
+ * plus the overhead 2 MByte and rounded up to
+ * 100 000 kBytes.
+ * <br><em>
+ * This parameter is currently not used.
+ * </em>
+ * @param aTupleKey Indicates if the table has a primary key or not.
+ * <br>
+ * <b>TupleKey</b> means that a <em>primary key</em>
+ * consisting of one to four attributes
+ * (at most one of variable size)
+ * uniquely identifies each record in the created
+ * table.
+ * <br>
+ * <b>TupleId</b> means that a <em>tuple identity</em>
+ * is used. The tuple identity is
+ * a unique key indentifying each record of the
+ * created table.
+ * The tuple identity is a (non-stored)
+ * 64 bit attribute named <b>NDB$TID</b>.
+ * <br>
+ * When inserting a record (tuple), the method
+ * NdbOperation::setTupleId
+ * will generate a unique tuple identity
+ * and return it to the user.
+ * <br>
+ * When reading, updating or deleting a record
+ * in a table with <b>TupleId</b>,
+ * NdbOperation::equal("NDB$TID", value_Uint64)
+ * can be used to identify the record.
+ * <br>
+ * Legal values: TupleKey or TupleId.
+ * @param aNrOfPages (Performance parameter.)
+ * Specifies the initial size of the index storage.
+ * When calculating the index storage,
+ * each key has approximately 14 byte of
+ * overhead plus the size of the key.
+ * Each key attribute takes up at least 4 bytes
+ * of storage.
+ * Thus a mixed key consisting of a
+ * 64 bit attribute, a 32 bit attribute
+ * and a 16 bit attribute will
+ * consume approx. 30 bytes per key.
+ * Thus, the if initial size is to be 1 million rows,
+ * then aNrOfPages should be set to
+ * 30 M / 8k = 2670 pages.
+ * <br><em>
+ * This parameter is currently not used.
+ * </em>
+ * @param aFragmentType Type of fragmentation.<br>
+ * <b>All</b> (default) means that the
+ * table fragments are automatically
+ * distributed on all nodes in the system.<br>
+ * <b>DistributionGroup</b> and
+ * <b>DistributionKey</b> are
+ * also supported. For further details about
+ * these types see the documentation of
+ * Ndb::startTransaction.
+ * @param aKValue (Hash parameter.)
+ * Only allowed value is 6.
+ * Later implementations might add flexibility
+ * in this parameter.
+ * @param aMinLoadFactor (Hash parameter.)
+ * This value specifies the load factor when
+ * starting to shrink the hash table.
+ * It must be smaller than aMaxLoadFactor.
+ * Both these factors are given in percentage.
+ * @param aMaxLoadFactor (Hash parameter.)
+ * This value specifies the load factor when
+ * starting to split the containers in the local
+ * hash tables. 100 is the maximum which will
+ * optimize memory usage (this is the figure
+ * used for the above calculations).
+ * A lower figure will store less information in
+ * each container and thus
+ * find the key faster but consume more memory.
+ * @param aMemoryType Currently only 1 is allowed which specifies
+ * storage of table in main memory.
+ * Later 2 will be added where the table is stored
+ * completely on disk
+ * and 3 where the index is in main memory but
+ * data is on disk.
+ * If 1 is chosen an individual attribute can
+ * still be specified as a disk attribute.
+ * @param aStoredTable If set to false it indicates that the table is
+ * a temporary table and should not be logged
+ * to disk.
+ * In case of a system restart the table will still
+ * be defined and exist but will be empty.
+ * Thus no checkpointing and
+ * no logging is performed on the table.
+ * The default value is true and indicates a
+ * normal table with full checkpointing and
+ * logging activated.
+ * @return Returns 0 when successful and returns -1 otherwise.
+ */
+ int createTable( const char* aTableName,
+ Uint32 aTableSize = 8,
+ KeyType aTupleKey = TupleKey,
+ int aNrOfPages = 2,
+ FragmentType aFragmentType = All,
+ int aKValue = 6,
+ int aMinLoadFactor = 78,
+ int aMaxLoadFactor = 80,
+ int aMemoryType = 1,
+ bool aStoredTable = true);
+
+ /**
+ * This is the old function declaration, don't use.
+ *
+ * @deprecated do not use!
+ */
+#ifndef NDB_WIN32
+ inline int createTable( const char* aTableName,
+ Uint32 aTableSize,
+ KeyType aTupleKey,
+ int aNrOfPages,
+ FragmentType aFragmentType,
+ int aKValue,
+ int aMinLoadFactor,
+ int aMaxLoadFactor,
+ int aMemoryType,
+ int aStoredTable){
+ return createTable(aTableName,
+ aTableSize,
+ aTupleKey,
+ aNrOfPages,
+ aFragmentType,
+ aKValue,
+ aMinLoadFactor,
+ aMaxLoadFactor,
+ aMemoryType,
+ (aStoredTable == 1 ? true : false));
+ }
+#endif
+
+ /**
+ * Add a new attribute to a database table.
+ *
+ * Attributes can only be added to a table in the same transaction
+ * as the transaction creating the table.
+ *
+ * @note The NdbSchemaCon transaction should be closed with
+ * Ndb::closeSchemaTransaction, even if this method fails.
+ *
+ * Example creating an unsigned int attribute belonging to the primary key
+ * of the table it is created in:
+ * @code
+ * MySchemaOp->createAttribute("Attr1", // Attribute name
+ * TupleKey, // Belongs to primary key
+ * 32, // 32 bits
+ * 1, // Not an array attribute
+ * UnSigned, // Unsigned type
+ * );
+ * @endcode
+ *
+ * Example creating a string attribute belonging to the primary key
+ * of the table it is created in:
+ * @code
+ * MySchemaOp->createAttribute("Attr1", // Attribute name
+ * TupleKey, // Belongs to primary key
+ * 8, // Each character is 8 bits
+ * 12, // Max 12 chars in string
+ * String, // Attribute if of type string
+ * );
+ * @endcode
+ *
+ * A <em>distribution key</em> is a set of attributes which are used
+ * to distribute the tuples onto the NDB nodes.
+ * A <em>distribution group</em> is a part (currently 16 bits)
+ * of an attribute used to distribute the tuples onto the NDB nodes.
+ * The distribution key uses the NDB Cluster hashing function,
+ * while the distribution group uses a simpler function.
+ *
+ * @param aAttrName Attribute name. Should not be NULL.
+ * @param aTupleKey This parameter specifies whether the
+ * attribute is part of the primary key or not.
+ * Floats are not allowed in the primary key.
+ * <br>
+ * Legal values: NoKey, TupleKey
+ * @param aAttrSize Specifies the size of the elements of the
+ * attribute. (An attribute can consist
+ * of an array of elements.)
+ * <br>
+ * Legal values: 8, 16, 32, 64 and 128 bits.
+ * @param aArraySize Size of array.
+ * <br>
+ * Legal values:
+ * 0 = variable-sized array,
+ * 1 = no array, and
+ * 2- = fixed size array.
+ * <br>
+ * <em>
+ * Variable-sized array attributes are
+ * not yet supported.
+ * </em>
+ * <br>
+ * There is no upper limit of the array size
+ * for a single attribute.
+ * @param aAttrType The attribute type.
+ * This is only of interest if calculations are
+ * made within NDB.
+ * <br>
+ * Legal values: UnSigned, Signed, Float, String
+ * @param aStorageMode Main memory based or disk based attribute.<br>
+ * Legal values: MMBased, DiskBased
+ * <br>
+ * <em>
+ * Disk-based attributes are not yet supported.
+ * </em>
+ * @param nullable Set to true if NULL is a correct value for
+ * the attribute.
+ * <br>
+ * Legal values: true, false
+ * @param aStType Obsolete since wl-2066
+ * @param aDistributionKey Sometimes it is preferable to use a subset
+ * of the primary key as the distribution key.
+ * An example is TPC-C where it might be
+ * good to use the warehouse id and district id
+ * as the distribution key.
+ * <br>
+ * Locally in the fragments the full primary key
+ * will still be used with the hashing algorithm.
+ * Set to 1 if this attribute is part of the
+ * distribution key.
+ * All distribution key attributes must be
+ * defined before
+ * any other attributes are defined.
+ * @param aDistributionGroup In other applications it is desirable to use
+ * only a part of an attribute to create the
+ * distribution key.
+ * This is applicable for some telecom
+ * applications.
+ * <br>
+ * In these situations one must provide how many
+ * bits of the attribute that is to
+ * be used as the distribution hash value.
+ * <br>
+ * This provides some control to the
+ * application of the distribution.
+ * It still needs to be part of a primary key
+ * the attribute and must be defined as the
+ * first attribute.
+ * @param aDistributionGroupNoOfBits
+ * Number of bits to use of the
+ * distribution group attribute in the
+ * distribution hash value.
+ * <br>
+ * Currently, only 16 bits is supported. It will
+ * always be the last 16 bits in the attribute
+ * which is used for the distribution group.
+ * @param aAutoIncrement Set to autoincrement attribute.
+ * @param aDefaultValue Set a default value of attribute.
+ *
+ * @return Returns 0 when successful and returns -1 otherwise.
+ ****************************************************************************/
+ int createAttribute(const char* aAttrName,
+ KeyType aTupleKey = NoKey,
+ int aAttrSize = 32,
+ int aArraySize = 1,
+ AttrType aAttrType = UnSigned,
+ StorageMode aStorageMode = MMBased,
+ bool nullable = false,
+ int aStType= 0, // obsolete
+ int aDistributionKey = 0,
+ int aDistributionGroup = 0,
+ int aDistributionGroupNoOfBits = 16,
+ bool aAutoIncrement = false,
+ const char* aDefaultValue = 0);
+
+ /**
+ * @deprecated do not use!
+ */
+ int createAttribute(const char* aAttrName,
+ KeyType aTupleKey,
+ int aAttrSize,
+ int aArraySize,
+ AttrType aAttrType,
+ StorageMode aStorageMode,
+ NullAttributeType aNullAttr,
+ int aStType, // obsolete
+ int aDistributionKey = 0,
+ int aDistributionGroup = 0,
+ int aDistributionGroupNoOfBits = 16){
+ return createAttribute(aAttrName,
+ aTupleKey,
+ aAttrSize,
+ aArraySize,
+ aAttrType,
+ aStorageMode,
+ aNullAttr == NullAttribute,
+ aStType,
+ aDistributionKey,
+ aDistributionGroup,
+ aDistributionGroupNoOfBits);
+ }
+
+ const NdbError & getNdbError() const;
+
+protected:
+
+/*****************************************************************************
+ * These are the methods used to create and delete the NdbOperation objects.
+ ****************************************************************************/
+ NdbSchemaOp(Ndb* aNdb);
+
+ ~NdbSchemaOp();
+
+/******************************************************************************
+ * These methods are service routines used by the other NDBAPI classes.
+ *****************************************************************************/
+
+ void release(); // Release all memory connected
+ // to the operations object.
+
+/****************************************************************************
+ * The methods below is the execution part of the NdbSchemaOp class.
+ *****************************************************************************/
+
+ int sendRec();
+ int sendSignals(Uint32 aNodeId, bool HaveMutex);
+
+ int init(NdbSchemaCon* aSchemaCon);
+
+ /**************************************************************************
+ * These are the private variables that are defined in the operation
+ * objects.
+ **************************************************************************/
+ Ndb* theNdb; // Point back to the Ndb object.
+ NdbSchemaCon* theSchemaCon; // Point back to the connection object.
+
+
+ class NdbDictionary::Table * m_currentTable;
+};
+
+
+/**
+ * Get old attribute type from new type
+ *
+ * NOTE! attrType is deprecated, use getType instead!
+ *
+ * @return Type of attribute: { Signed, UnSigned, Float,a String }
+ */
+inline
+AttrType
+convertColumnTypeToAttrType(NdbDictionary::Column::Type _type)
+{
+
+ switch(_type){
+ case NdbDictionary::Column::Bigint:
+ case NdbDictionary::Column::Int:
+ return Signed;
+ case NdbDictionary::Column::Bigunsigned:
+ case NdbDictionary::Column::Unsigned:
+ return UnSigned;
+ case NdbDictionary::Column::Float:
+ case NdbDictionary::Column::Olddecimal:
+ case NdbDictionary::Column::Olddecimalunsigned:
+ case NdbDictionary::Column::Decimal:
+ case NdbDictionary::Column::Decimalunsigned:
+ case NdbDictionary::Column::Double:
+ return Float;
+ case NdbDictionary::Column::Char:
+ case NdbDictionary::Column::Varchar:
+ case NdbDictionary::Column::Binary:
+ case NdbDictionary::Column::Varbinary:
+ return String;
+ default:
+ return NoAttrTypeDef;
+ }
+}
+#endif
+
+#endif
+
+
diff --git a/storage/ndb/test/include/NdbTest.hpp b/storage/ndb/test/include/NdbTest.hpp
new file mode 100644
index 00000000000..a2e612b7ffa
--- /dev/null
+++ b/storage/ndb/test/include/NdbTest.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 NDB_TEST_HPP
+#define NDB_TEST_HPP
+
+/**
+ * NdbTest.hpp
+ * This is the main include file to include in test programs
+ * It will include all the other include files in the NDBT-toolkit
+ *
+ */
+
+#include "NDBT_ReturnCodes.h"
+
+#ifdef __cplusplus
+#include "NDBT_Table.hpp"
+#include "NDBT_Error.hpp"
+#endif
+
+
+#endif
diff --git a/storage/ndb/test/include/NdbTimer.hpp b/storage/ndb/test/include/NdbTimer.hpp
new file mode 100644
index 00000000000..b0d500b5c2c
--- /dev/null
+++ b/storage/ndb/test/include/NdbTimer.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 NDBTIMER_H
+#define NDBTIMER_H
+
+#include <NdbTick.h>
+#include <NdbOut.hpp>
+
+//
+// Class used for measuring time and priting the results
+//
+// Currently measures time in milliseconds
+//
+
+class NdbTimer
+{
+public:
+
+ NdbTimer();
+ ~NdbTimer() {};
+
+ void doStart();
+ void doStop();
+ void doReset();
+ NDB_TICKS elapsedTime();
+ void printTransactionStatistics(const char* text,
+ int numTransactions,
+ int numOperations);
+ void printTestTimer(int numLoops,
+ int numRecords);
+ void printTotalTime(void);
+private:
+ NDB_TICKS startTime;
+ NDB_TICKS stopTime;
+};
+
+inline NdbTimer::NdbTimer(){
+ doReset();
+}
+
+inline void NdbTimer::doReset(void){
+ startTime = 0;
+ stopTime = 0;
+}
+
+inline void NdbTimer::doStart(void){
+ startTime = NdbTick_CurrentMillisecond();
+}
+
+inline void NdbTimer::doStop(void){
+ stopTime = NdbTick_CurrentMillisecond();
+}
+
+inline NDB_TICKS NdbTimer::elapsedTime(void){
+ return (stopTime - startTime);
+}
+
+inline void NdbTimer::printTransactionStatistics(const char* text,
+ int numTransactions,
+ int numOperations){
+
+ // Convert to Uint32 in order to be able to print it to screen
+ Uint32 lapTime = (Uint32)elapsedTime();
+ ndbout_c("%i transactions, %i %s total time = %d ms\nAverage %f ms/transaction, %f ms/%s.\n%f transactions/second, %f %ss/second.\n",
+ numTransactions, numTransactions*numOperations, text, lapTime,
+ ((double)lapTime/numTransactions), ((double)lapTime/(numTransactions*numOperations)), text,
+ 1000.0/((double)lapTime/numOperations), 1000.0/((double)lapTime/(numTransactions*numOperations)), text);
+}
+
+
+
+inline void NdbTimer::printTestTimer(int numLoops,
+ int numRecords){
+ // Convert to Uint32 in order to be able to print it to screen
+ Uint32 lapTime = (Uint32)elapsedTime();
+ ndbout_c("%i loop * %i records, total time = %d ms\nAverage %f ms/loop, %f ms/record.\n%f looop/second, %f records/second.\n",
+ numLoops, numRecords, lapTime,
+ ((double)lapTime/numLoops), ((double)lapTime/(numLoops*numRecords)),
+ 1000.0/((double)lapTime/numLoops), 1000.0/((double)lapTime/(numLoops*numRecords)));
+}
+
+
+inline void NdbTimer::printTotalTime(void){
+ // Convert to Uint32 in order to be able to print it to screen
+ Uint32 lapTime = (Uint32)elapsedTime();
+ Uint32 secTime = lapTime/1000;
+ ndbout_c("Total time : %d seconds (%d ms)\n", secTime, lapTime);
+}
+
+
+
+
+
+
+#endif
diff --git a/storage/ndb/test/include/TestNdbEventOperation.hpp b/storage/ndb/test/include/TestNdbEventOperation.hpp
new file mode 100644
index 00000000000..307b0e0089b
--- /dev/null
+++ b/storage/ndb/test/include/TestNdbEventOperation.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 */
+
+struct EventOperationStats {
+ int n_inserts;
+ int n_deletes;
+ int n_updates;
+ int n_duplicates;
+ int n_consecutive;
+ int n_inconsistent_gcis;
+};
diff --git a/storage/ndb/test/include/UtilTransactions.hpp b/storage/ndb/test/include/UtilTransactions.hpp
new file mode 100644
index 00000000000..afdbc5c3445
--- /dev/null
+++ b/storage/ndb/test/include/UtilTransactions.hpp
@@ -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 */
+
+#ifndef UTIL_TRANSACTIONS_HPP
+#define UTIL_TRANSACTIONS_HPP
+
+#include <NDBT.hpp>
+
+typedef int (ReadCallBackFn)(NDBT_ResultRow*);
+
+class UtilTransactions {
+public:
+ UtilTransactions(const NdbDictionary::Table&,
+ const NdbDictionary::Index* idx = 0);
+ UtilTransactions(Ndb* ndb,
+ const char * tableName, const char * indexName = 0);
+
+ int clearTable(Ndb*,
+ int records = 0,
+ int parallelism = 0);
+
+ // Delete all records from the table using a scan
+ int clearTable1(Ndb*,
+ int records = 0,
+ int parallelism = 0);
+ // Delete all records from the table using a scan
+ // Using batching
+ int clearTable2(Ndb*,
+ int records = 0,
+ int parallelism = 0);
+
+ int clearTable3(Ndb*,
+ int records = 0,
+ int parallelism = 0);
+
+ int selectCount(Ndb*,
+ int parallelism = 0,
+ int* count_rows = NULL,
+ NdbOperation::LockMode lm = NdbOperation::LM_CommittedRead,
+ NdbConnection* pTrans = NULL);
+ int scanReadRecords(Ndb*,
+ int parallelism,
+ NdbOperation::LockMode lm,
+ int records,
+ int noAttribs,
+ int* attrib_list,
+ ReadCallBackFn* fn = NULL);
+ int verifyIndex(Ndb*,
+ const char* indexName,
+ int parallelism = 0,
+ bool transactional = false);
+
+ int copyTableData(Ndb*,
+ const char* destName);
+
+ /**
+ * Compare this table with other_table
+ *
+ * return 0 - on equality
+ * -1 - on error
+ * >0 - otherwise
+ */
+ int compare(Ndb*, const char * other_table, int flags);
+
+private:
+ static int takeOverAndDeleteRecord(Ndb*,
+ NdbOperation*);
+
+ int addRowToDelete(Ndb* pNdb,
+ NdbConnection* pDelTrans,
+ NdbOperation* pOrgOp);
+
+
+ int addRowToInsert(Ndb* pNdb,
+ NdbConnection* pInsTrans,
+ NDBT_ResultRow & row,
+ const char* insertTabName);
+
+
+ int verifyUniqueIndex(Ndb*,
+ const NdbDictionary::Index *,
+ int parallelism = 0,
+ bool transactional = false);
+
+ int scanAndCompareUniqueIndex(Ndb* pNdb,
+ const NdbDictionary::Index *,
+ int parallelism,
+ bool transactional);
+
+ int readRowFromTableAndIndex(Ndb* pNdb,
+ NdbConnection* pTrans,
+ const NdbDictionary::Index *,
+ NDBT_ResultRow& row );
+
+ int verifyOrderedIndex(Ndb*,
+ const NdbDictionary::Index *,
+ int parallelism = 0,
+ bool transactional = false);
+
+
+ int get_values(NdbOperation* op, NDBT_ResultRow& dst);
+ int equal(const NdbDictionary::Table*, NdbOperation*, const NDBT_ResultRow&);
+ int equal(const NdbDictionary::Index*, NdbOperation*, const NDBT_ResultRow&);
+
+protected:
+ int m_defaultClearMethod;
+ const NdbDictionary::Table& tab;
+ const NdbDictionary::Index* idx;
+ NdbConnection* pTrans;
+
+ NdbOperation* getOperation(NdbConnection*,
+ NdbOperation::OperationType);
+ NdbScanOperation* getScanOperation(NdbConnection*);
+};
+
+#endif
diff --git a/storage/ndb/test/include/getarg.h b/storage/ndb/test/include/getarg.h
new file mode 100644
index 00000000000..03ed25f6828
--- /dev/null
+++ b/storage/ndb/test/include/getarg.h
@@ -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 */
+
+/*
+ * Copyright (c) 1997, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $KTH: getarg.h,v 1.9 2000/09/01 21:25:55 lha Exp $ */
+
+#ifndef __GETARG_H__
+#define __GETARG_H__
+
+#include <ndb_global.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ arg_integer,
+ arg_string,
+ arg_flag,
+ arg_negative_flag,
+ arg_strings,
+ arg_double,
+ arg_collect,
+ arg_counter
+} arg_type;
+
+struct getargs{
+ const char *long_name;
+ char short_name;
+ arg_type type;
+ void *value;
+ const char *help;
+ const char *arg_help;
+};
+
+enum {
+ ARG_ERR_NO_MATCH = 1,
+ ARG_ERR_BAD_ARG,
+ ARG_ERR_NO_ARG
+};
+
+typedef struct getarg_strings {
+ int num_strings;
+ char **strings;
+} getarg_strings;
+
+typedef int (*getarg_collect_func)(int short_opt,
+ int argc,
+ const char **argv,
+ int *optind,
+ int *optarg,
+ void *data);
+
+typedef struct getarg_collect_info {
+ getarg_collect_func func;
+ void *data;
+} getarg_collect_info;
+
+int getarg(struct getargs *args, size_t num_args,
+ int argc, const char **argv, int *optind);
+
+void arg_printusage (struct getargs *args,
+ size_t num_args,
+ const char *progname,
+ const char *extra_string);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GETARG_H__ */
diff --git a/storage/ndb/test/ndbapi/InsertRecs.cpp b/storage/ndb/test/ndbapi/InsertRecs.cpp
new file mode 100644
index 00000000000..f42786d666d
--- /dev/null
+++ b/storage/ndb/test/ndbapi/InsertRecs.cpp
@@ -0,0 +1,571 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+// InsertRecs.cpp : Defines the entry point for the console application.
+//
+
+
+#include <NdbApi.hpp>
+#include <windows.h>
+#include <tchar.h>
+
+
+// data for CALL_CONTEXT and GROUP_RESOURCE
+static TCHAR STATUS_DATA[]=_T("000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F")
+ _T("101112131415161718191A1B1C1D1E1F000102030405060708090A0B0C0D0E0F")
+ _T("202122232425262728292A2B2C2D2E2F000102030405060708090A0B0C0D0E0F")
+ _T("303132333435363738393A3B3C3D3E3F000102030405060708090A0B0C0D0E0F")
+ _T("404142434445464748494A4B4C4D4E4F000102030405060708090A0B0C0D0E0F")
+ _T("505152535455565758595A5B5C5D5E5F000102030405060708090A0B0C0D0E0F")
+ _T("606162636465666768696A6B6C6D6E6F000102030405060708090A0B0C0D0E0F")
+ _T("707172737475767778797A7B7C7D7E7F000102030405060708090A0B0C0D0E0F")
+ _T("808182838485868788898A8B8C8D8E8F000102030405060708090A0B0C0D0E0F")
+ _T("909192939495969798999A9B9C9D9E9F000102030405060708090A0B0C0D0E0F")
+ _T("10010110210310410510610710810910A000102030405060708090A0B0C0D0EF")
+ _T("10B10C10D10E10F110111112113114115000102030405060708090A0B0C0D0EF")
+ _T("11611711811911A11B11C11D11E11F120000102030405060708090A0B0C0D0EF")
+ _T("12112212312412512612712812912A12B000102030405060708090A0B0C0D0EF")
+ _T("12C12D12E12F130131132134135136137000102030405060708090A0B0C0D0EF")
+ _T("13813913A13B13C13D13E13F140141142000102030405060708090A0B0C0D0EF")
+ _T("14314414514614714814914A14B14C14D000102030405060708090A0B0C0D0EF")
+ _T("14E14F150151152153154155156157158000102030405060708090A0B0C0D0EF")
+ _T("15915A15B15C15D15E15F160161162163000102030405060708090A0B0C0D0EF")
+ _T("16416516616716816916A16B16C16D16E000102030405060708090A0B0C0D0EF")
+ _T("16F170171172173174175176177178179000102030405060708090A0B0C0D0EF")
+ _T("17A17B17C17D17E17F180181182183184000102030405060708090A0B0C0D0EF")
+ _T("18518618718818918A18B18C18D18E18F000102030405060708090A0B0C0D0EF")
+ _T("19019119219319419519619719819919A000102030405060708090A0B0C0D0EF")
+ _T("19B19C19D19E19F200201202203204205000102030405060708090A0B0C0D0EF")
+ _T("20620720820920A20B20C20D20F210211000102030405060708090A0B0C0D0EF")
+ _T("21221321421521621721821921A21B21C000102030405060708090A0B0C0D0EF")
+ _T("21D21E21F220221222223224225226227000102030405060708090A0B0C0D0EF")
+ _T("22822922A22B22C22D22E22F230231232000102030405060708090A0B0C0D0EF")
+ _T("23323423523623723823923A23B23C23D000102030405060708090A0B0C0D0EF")
+ _T("23E23F240241242243244245246247248000102030405060708090A0B0C0D0EF")
+ _T("24924A24B24C24D24E24F250251252253000102030405060708090A0B0C0D0EF")
+ _T("101112131415161718191A1B1C1D1E1F000102030405060708090A0B0C0D0E0F")
+ _T("202122232425262728292A2B2C2D2E2F000102030405060708090A0B0C0D0E0F")
+ _T("303132333435363738393A3B3C3D3E3F000102030405060708090A0B0C0D0E0F")
+ _T("404142434445464748494A4B4C4D4E4F000102030405060708090A0B0C0D0E0F")
+ _T("505152535455565758595A5B5C5D5E5F000102030405060708090A0B0C0D0E0F")
+ _T("606162636465666768696A6B6C6D6E6F000102030405060708090A0B0C0D0E0F")
+ _T("707172737475767778797A7B7C7D7E7F000102030405060708090A0B0C0D0E0F")
+ _T("808182838485868788898A8B8C8D8E8F000102030405060708090A0B0C0D0E0F")
+ _T("909192939495969798999A9B9C9D9E9F000102030405060708090A0B0C0D0E0F")
+ _T("10010110210310410510610710810910A000102030405060708090A0B0C0D0EF")
+ _T("10B10C10D10E10F110111112113114115000102030405060708090A0B0C0D0EF")
+ _T("11611711811911A11B11C11D11E11F120000102030405060708090A0B0C0D0EF")
+ _T("12112212312412512612712812912A12B000102030405060708090A0B0C0D0EF")
+ _T("12C12D12E12F130131132134135136137000102030405060708090A0B0C0D0EF")
+ _T("13813913A13B13C13D13E13F140141142000102030405060708090A0B0C0D0EF")
+ _T("14314414514614714814914A14B14C14D000102030405060708090A0B0C0D0EF")
+ _T("14E14F150151152153154155156157158000102030405060708090A0B0C0D0EF")
+ _T("15915A15B15C15D15E15F160161162163000102030405060708090A0B0C0D0EF")
+ _T("16416516616716816916A16B16C16D16E000102030405060708090A0B0C0D0EF")
+ _T("16F170171172173174175176177178179000102030405060708090A0B0C0D0EF")
+ _T("17A17B17C17D17E17F180181182183184000102030405060708090A0B0C0D0EF")
+ _T("18518618718818918A18B18C18D18E18F000102030405060708090A0B0C0D0EF")
+ _T("19019119219319419519619719819919A000102030405060708090A0B0C0D0EF")
+ _T("19B19C19D19E19F200201202203204205000102030405060708090A0B0C0D0EF")
+ _T("20620720820920A20B20C20D20F210211000102030405060708090A0B0C0D0EF")
+ _T("21221321421521621721821921A21B21C000102030405060708090A0B0C0D0EF")
+ _T("21D21E21F220221222223224225226227000102030405060708090A0B0C0D0EF")
+ _T("22822922A22B22C22D22E22F230231232000102030405060708090A0B0C0D0EF")
+ _T("23323423523623723823923A23B23C23D000102030405060708090A0B0C0D0EF")
+ _T("2366890FE1438751097E7F6325DC0E6326F")
+ _T("25425525625725825925A25B25C25D25E25F000102030405060708090A0B0C0F");
+// Thread function for Call Context Inserts
+
+struct _ParamStruct
+{
+ HANDLE hShutdownEvent;
+ int nStartingRecordNum;
+ long* pnNumCallsProcessed;
+};
+
+HANDLE hShutdownEvent = 0;
+
+BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
+{
+ if(CTRL_C_EVENT == dwCtrlType)
+ {
+ SetEvent(hShutdownEvent);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+
+
+DWORD WINAPI RuntimeCallContext(LPVOID lpParam)
+{
+ long nNumCallsProcessed = 0;
+
+ struct _ParamStruct* pData = (struct _ParamStruct*)lpParam;
+ int nStartingRecordID = pData->nStartingRecordNum;
+
+ Ndb* pNdb;
+ NdbConnection* pNdbConnection;
+ NdbOperation* pNdbOperation;
+ NdbRecAttr* pNdbRecAttrContextData;
+
+ char pchContextData[4008];
+
+ LARGE_INTEGER freq;
+ LARGE_INTEGER liStartTime, liEndTime;
+
+ pNdb = new Ndb("TEST_DB");
+ if(!pNdb)
+ {
+ printf("new Ndb failed\n");
+ return 0;
+ }
+
+ try
+ {
+ if(pNdb->init(1)
+ || pNdb->waitUntilReady())
+ {
+ throw pNdb;
+ }
+
+ while(WaitForSingleObject(pData->hShutdownEvent,0) != WAIT_OBJECT_0)
+ {
+ nStartingRecordID++;
+
+ bool bTimeLatency = (nStartingRecordID == 100) ? TRUE : FALSE;
+
+ if (bTimeLatency)
+ {
+ BOOL bSuccess = QueryPerformanceFrequency(&freq);
+ if (!bSuccess)
+ printf("Error retrieving frequency: %d\n", GetLastError());
+
+ }
+
+ for (int i=0; i < 20; i++)
+ {
+ switch(i)
+ {
+ case 3:
+ case 6:
+ case 9:
+ case 11:
+ case 12:
+ case 15:
+ case 18: // Query Record
+ if (bTimeLatency)
+ QueryPerformanceCounter(&liStartTime);
+
+ pNdbConnection = pNdb->startTransaction((Uint32)0, (const char*)&nStartingRecordID, (Uint32)4);
+ if(!pNdbConnection)
+ {
+ throw pNdb;
+ }
+ pNdbOperation = pNdbConnection->getNdbOperation(_T("CallContext"));
+ if(!pNdbOperation)
+ {
+ throw pNdbConnection;
+ }
+ if(pNdbOperation->readTuple()
+ || pNdbOperation->equal(_T("ContextId"), nStartingRecordID))
+ {
+ throw pNdbOperation;
+ }
+ pNdbRecAttrContextData = pNdbOperation->getValue(_T("ContextData"), pchContextData);
+ if(!pNdbRecAttrContextData)
+ {
+ throw pNdbOperation;
+ }
+ if(pNdbConnection->execute(Commit))
+ {
+ throw pNdbConnection;
+ }
+ pNdb->closeTransaction(pNdbConnection);
+
+ if (bTimeLatency)
+ {
+ QueryPerformanceCounter(&liEndTime);
+ printf("Read = %d msec.\n", (liEndTime.QuadPart - liStartTime.QuadPart) / (freq.QuadPart/1000));
+ }
+ break;
+
+ case 19: // Delete Record
+ if (bTimeLatency)
+ QueryPerformanceCounter(&liStartTime);
+
+ pNdbConnection = pNdb->startTransaction((Uint32)0, (const char*)&nStartingRecordID, (Uint32)4);
+ if(!pNdbConnection)
+ {
+ throw pNdb;
+ }
+ pNdbOperation = pNdbConnection->getNdbOperation(_T("CallContext"));
+ if(!pNdbOperation)
+ {
+ throw pNdbConnection;
+ }
+ if(pNdbOperation->deleteTuple()
+ || pNdbOperation->equal(_T("ContextId"), nStartingRecordID))
+ {
+ throw pNdbOperation;
+ }
+ if(pNdbConnection->execute(Commit))
+ {
+ throw pNdbConnection;
+ }
+ pNdb->closeTransaction(pNdbConnection);
+
+ if (bTimeLatency)
+ {
+ QueryPerformanceCounter(&liEndTime);
+ printf("Delete = %d msec.\n", (liEndTime.QuadPart - liStartTime.QuadPart) / (freq.QuadPart/1000));
+ }
+ break;
+
+ case 0: // Insert Record
+ if (bTimeLatency)
+ QueryPerformanceCounter(&liStartTime);
+
+ pNdbConnection = pNdb->startTransaction((Uint32)0, (const char*)&nStartingRecordID, (Uint32)4);
+ if(!pNdbConnection)
+ {
+ throw pNdb;
+ }
+ pNdbOperation = pNdbConnection->getNdbOperation(_T("CallContext"));
+ if(!pNdbOperation)
+ {
+ throw pNdbConnection;
+ }
+ if(pNdbOperation->insertTuple()
+ || pNdbOperation->equal(_T("ContextId"), nStartingRecordID)
+ || pNdbOperation->setValue(_T("Version"), Int32(1))
+ || pNdbOperation->setValue(_T("LockFlag"), Int32(1))
+ || pNdbOperation->setValue(_T("LockTime"), Int32(1))
+ || pNdbOperation->setValue(_T("LockTimeUSec"), Int32(1))
+ || pNdbOperation->setValue(_T("ContextData"), STATUS_DATA, sizeof(STATUS_DATA)))
+ {
+ throw pNdbOperation;
+ }
+ if(pNdbConnection->execute(Commit))
+ {
+ throw pNdbConnection;
+ }
+ pNdb->closeTransaction(pNdbConnection);
+
+ if (bTimeLatency)
+ {
+ QueryPerformanceCounter(&liEndTime);
+ printf("Insert = %d msec.\n", (liEndTime.QuadPart - liStartTime.QuadPart) / (freq.QuadPart/1000));
+ }
+ break;
+
+ default: // Update Record
+ if (bTimeLatency)
+ QueryPerformanceCounter(&liStartTime);
+
+ pNdbConnection = pNdb->startTransaction((Uint32)0, (const char*)&nStartingRecordID, (Uint32)4);
+ if(!pNdbConnection)
+ {
+ throw pNdb;
+ }
+ pNdbOperation = pNdbConnection->getNdbOperation(_T("CallContext"));
+ if(!pNdbOperation)
+ {
+ throw pNdbConnection;
+ }
+ if(pNdbOperation->updateTuple())
+ {
+ throw pNdbOperation;
+ }
+ if(pNdbOperation->equal(_T("ContextId"), nStartingRecordID)
+ || pNdbOperation->setValue(_T("ContextData"), STATUS_DATA, sizeof(STATUS_DATA)))
+ {
+ throw pNdbOperation;
+ }
+ if(pNdbConnection->execute(Commit))
+ {
+ throw pNdbConnection;
+ }
+ pNdb->closeTransaction(pNdbConnection);
+
+ if (bTimeLatency)
+ {
+ QueryPerformanceCounter(&liEndTime);
+ printf("Update = %d msec.\n", (liEndTime.QuadPart - liStartTime.QuadPart) / (freq.QuadPart/1000));
+ }
+
+ break;
+ }
+ }
+
+ nNumCallsProcessed++;
+
+ InterlockedIncrement(pData->pnNumCallsProcessed);
+ }
+
+ delete pNdb;
+ }
+ catch(Ndb* pNdb)
+ {
+ printf("%d: \n\t%s\n\t%s\n",
+ pNdb->getNdbError(),
+ pNdb->getNdbErrorString(),
+ "Ndb");
+ delete pNdb;
+ }
+ catch(NdbConnection* pNdbConnection)
+ {
+ printf("%d: \n\t%s\n\t%s\n",
+ pNdbConnection->getNdbError(),
+ pNdbConnection->getNdbErrorString(),
+ "NdbConnection");
+ pNdb->closeTransaction(pNdbConnection);
+ delete pNdb;
+ }
+ catch(NdbOperation* pNdbOperation)
+ {
+ printf("%d: \n\t%s\n\t%s\n",
+ pNdbOperation->getNdbError(),
+ pNdbOperation->getNdbErrorString(),
+ "NdbOperation");
+ pNdb->closeTransaction(pNdbConnection);
+ delete pNdb;
+ }
+
+ return 0;
+}
+
+
+void Initialize(Ndb* pNdb, long nInsert, bool bStoredTable)
+{
+ NdbSchemaCon* pNdbSchemaCon;
+ NdbSchemaOp* pNdbSchemaOp;
+ NdbConnection* pNdbConnection;
+ NdbOperation* pNdbOperation;
+
+ try
+ {
+ _tprintf(_T("Create CallContext table\n"));
+
+ pNdbSchemaCon = pNdb->startSchemaTransaction();
+ if(!pNdbSchemaCon)
+ {
+ throw pNdb;
+ }
+ pNdbSchemaOp = pNdbSchemaCon->getNdbSchemaOp();
+ if(!pNdbSchemaOp)
+ {
+ throw pNdbSchemaCon;
+ }
+ if(pNdbSchemaOp->createTable(_T("CallContext"), 8, TupleKey, 2, All, 6, 78, 80, 1, bStoredTable)
+ || pNdbSchemaOp->createAttribute(_T("ContextId"), TupleKey, 32, 1, Signed)
+ || pNdbSchemaOp->createAttribute(_T("Version"), NoKey, 32, 1, Signed)
+ || pNdbSchemaOp->createAttribute(_T("LockFlag"), NoKey, 32, 1, Signed)
+ || pNdbSchemaOp->createAttribute(_T("LockTime"), NoKey, 32, 1, Signed)
+ || pNdbSchemaOp->createAttribute(_T("LockTimeUSec"), NoKey, 32, 1, Signed)
+ || pNdbSchemaOp->createAttribute(_T("ContextData"), NoKey, 8, 4004, String))
+ {
+ throw pNdbSchemaOp;
+ }
+ if(pNdbSchemaCon->execute())
+ {
+ throw pNdbSchemaCon;
+ }
+ pNdb->closeSchemaTransaction(pNdbSchemaCon);
+
+ _tprintf(_T("Insert %d tuples in the CallContext table\n"), nInsert);
+ for(long i=0; i<nInsert; ++i)
+ {
+ long iContextId = -i;
+ pNdbConnection = pNdb->startTransaction((Uint32)0, (const char*)&iContextId, (Uint32)4);
+ if(!pNdbConnection)
+ {
+ throw pNdb;
+ }
+ pNdbOperation = pNdbConnection->getNdbOperation(_T("CallContext"));
+ if(!pNdbOperation)
+ {
+ throw pNdbConnection;
+ }
+ if(pNdbOperation->insertTuple()
+ || pNdbOperation->equal(_T("ContextId"), iContextId)
+ || pNdbOperation->setValue(_T("Version"), Int32(1))
+ || pNdbOperation->setValue(_T("LockFlag"), Int32(1))
+ || pNdbOperation->setValue(_T("LockTime"), Int32(1))
+ || pNdbOperation->setValue(_T("LockTimeUSec"), Int32(1))
+ || pNdbOperation->setValue(_T("ContextData"), STATUS_DATA, sizeof(STATUS_DATA)))
+ {
+ throw pNdbOperation;
+ }
+ if(pNdbConnection->execute(Commit))
+ {
+ throw pNdbConnection;
+ }
+ pNdb->closeTransaction(pNdbConnection);
+ }
+ _tprintf(_T("initialisation done\n"));
+ }
+ catch(Ndb* pNdb)
+ {
+ printf("%d: \n\t%s\n\t%s\n",
+ pNdb->getNdbError(),
+ pNdb->getNdbErrorString(),
+ "Ndb");
+ delete pNdb;
+ }
+ catch(NdbConnection* pNdbConnection)
+ {
+ printf("%d: \n\t%s\n\t%s\n",
+ pNdbConnection->getNdbError(),
+ pNdbConnection->getNdbErrorString(),
+ "NdbConnection");
+ pNdb->closeTransaction(pNdbConnection);
+ delete pNdb;
+ }
+ catch(NdbOperation* pNdbOperation)
+ {
+ printf("%d: \n\t%s\n\t%s\n",
+ pNdbOperation->getNdbError(),
+ pNdbOperation->getNdbErrorString(),
+ "NdbOperation");
+ pNdb->closeTransaction(pNdbConnection);
+ delete pNdb;
+ }
+ catch(NdbSchemaCon* pNdbSchemaCon)
+ {
+ printf("%d: \n\t%s\n\t%s\n",
+ pNdbSchemaCon->getNdbError(),
+ pNdbSchemaCon->getNdbErrorString(),
+ "pNdbSchemaCon");
+ pNdb->closeSchemaTransaction(pNdbSchemaCon);
+ delete pNdb;
+ }
+ catch(NdbSchemaOp* pNdbSchemaOp)
+ {
+ printf("%d: \n\t%s\n\t%s\n",
+ pNdbSchemaOp->getNdbError(),
+ pNdbSchemaOp->getNdbErrorString(),
+ "pNdbSchemaOp");
+ pNdb->closeTransaction(pNdbConnection);
+ delete pNdb;
+ }
+}
+
+
+int _tmain(int argc, _TCHAR* argv[])
+{
+ long nNumThreads=4;
+ long nSeed = 0;
+ long nInsert = 0;
+ bool bStoredTable = true;
+ if(lstrcmp(argv[1],_T("/?")) == 0)
+ {
+ _tprintf(_T("InsertRecs [No.Of Threads] [Record Seed No.] [Init no. of rec.] [Stored?]\n"));
+ return 0;
+ }
+
+ if(argc > 1)
+ nNumThreads = _ttol(argv[1]);
+ else
+ nNumThreads = 4;
+ if (argc > 2)
+ nSeed = _ttol(argv[2]);
+ _tprintf(_T("Num of Threads = %d, Seed = %d"), nNumThreads, nSeed);
+
+ if(argc>3)
+ nInsert = _ttol(argv[3]);
+ if(argc>4)
+ bStoredTable = (_ttol(argv[4])!=0);
+
+ long nNumCallsProcessed = 0;
+
+ SetConsoleCtrlHandler(ConsoleCtrlHandler,true);
+ hShutdownEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+
+ // initiate windows sockets
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+ wVersionRequested = MAKEWORD( 2, 2 );
+ err = WSAStartup( wVersionRequested, &wsaData );
+ if ( err != 0 ) {
+ _tprintf(_T("could not find a usable WinSock DLL\n"));
+ return 0;
+ }
+ if ( LOBYTE( wsaData.wVersion ) != 2
+ || HIBYTE( wsaData.wVersion ) != 2 )
+ {
+ _tprintf(_T("could not find a usable WinSock DLL\n"));
+ WSACleanup();
+ return 0;
+ }
+
+ Ndb* pNdb = new Ndb("TEST_DB");
+ if(!pNdb)
+ {
+ _tprintf(_T("could not construct ndb\n"));
+ return 0;
+ }
+ if(pNdb->init(1)
+ || pNdb->waitUntilReady())
+ {
+ _tprintf(_T("could not initialize ndb\n"));
+ return 0;
+ }
+
+ if(nInsert>0)
+ {
+ Initialize(pNdb, nInsert, bStoredTable);
+ }
+
+ if(nNumThreads>0)
+ {
+ _tprintf(_T("creating %d threads\n"), nNumThreads);
+ DWORD dwStartTime = GetTickCount();
+
+ DWORD dwThreadID = 0;
+ HANDLE hThreads[50];
+
+ struct _ParamStruct params[50];
+
+ for(int ij=0;ij<nNumThreads;ij++) {
+ params[ij].hShutdownEvent = hShutdownEvent;
+ params[ij].nStartingRecordNum = (ij*5000) + nSeed;
+ params[ij].pnNumCallsProcessed = &nNumCallsProcessed;
+ }
+
+ for(ij=0;ij<nNumThreads;ij++) {
+ hThreads[ij] = CreateThread(NULL,NULL,RuntimeCallContext,&params[ij],0,&dwThreadID);
+ }
+
+ //Wait for the threads to finish
+ WaitForMultipleObjects(nNumThreads,hThreads,TRUE,INFINITE);
+ DWORD dwEndTime = GetTickCount();
+
+ //Print time taken
+ _tprintf(_T("Time Taken for %d Calls is %ld msec (= %ld calls/sec\n"),
+ nNumCallsProcessed,dwEndTime-dwStartTime, (1000*nNumCallsProcessed/(dwEndTime-dwStartTime)));
+ }
+
+ delete pNdb;
+ WSACleanup();
+ CloseHandle(hShutdownEvent);
+
+ return 0;
+}
+
+
diff --git a/storage/ndb/test/ndbapi/Makefile.am b/storage/ndb/test/ndbapi/Makefile.am
new file mode 100644
index 00000000000..1d2dfb3f948
--- /dev/null
+++ b/storage/ndb/test/ndbapi/Makefile.am
@@ -0,0 +1,159 @@
+
+SUBDIRS = bank
+
+ndbtest_PROGRAMS = \
+flexBench \
+drop_all_tabs \
+create_all_tabs \
+create_tab \
+flexAsynch \
+flexBench \
+flexHammer \
+flexTT \
+testBackup \
+testBasic \
+testBasicAsynch \
+testBlobs \
+testDataBuffers \
+testDict \
+testIndex \
+testMgm \
+testNdbApi \
+testNodeRestart \
+testOIBasic \
+testOperations \
+testRestartGci \
+testScan \
+testScanInterpreter \
+testScanPerf \
+testSystemRestart \
+testTimeout \
+testTransactions \
+testDeadlock \
+test_event ndbapi_slow_select testReadPerf testLcp \
+testPartitioning \
+testBitfield \
+DbCreate DbAsyncGenerator \
+test_event_multi_table
+
+#flexTimedAsynch
+#testBlobs
+#flex_bench_mysql
+
+create_all_tabs_SOURCES = create_all_tabs.cpp
+create_tab_SOURCES = create_tab.cpp
+drop_all_tabs_SOURCES = drop_all_tabs.cpp
+flexAsynch_SOURCES = flexAsynch.cpp
+flexBench_SOURCES = flexBench.cpp
+flexHammer_SOURCES = flexHammer.cpp
+flexTT_SOURCES = flexTT.cpp
+#flexTimedAsynch_SOURCES = flexTimedAsynch.cpp
+#flex_bench_mysql_SOURCES = flex_bench_mysql.cpp
+testBackup_SOURCES = testBackup.cpp
+testBasic_SOURCES = testBasic.cpp
+testBasicAsynch_SOURCES = testBasicAsynch.cpp
+testBlobs_SOURCES = testBlobs.cpp
+testDataBuffers_SOURCES = testDataBuffers.cpp
+testDict_SOURCES = testDict.cpp
+testIndex_SOURCES = testIndex.cpp
+testMgm_SOURCES = testMgm.cpp
+testNdbApi_SOURCES = testNdbApi.cpp
+testNodeRestart_SOURCES = testNodeRestart.cpp
+testOIBasic_SOURCES = testOIBasic.cpp
+testOperations_SOURCES = testOperations.cpp
+testRestartGci_SOURCES = testRestartGci.cpp
+testScan_SOURCES = testScan.cpp ScanFunctions.hpp
+testScanInterpreter_SOURCES = testScanInterpreter.cpp ScanFilter.hpp ScanInterpretTest.hpp
+testScanPerf_SOURCES = testScanPerf.cpp
+testSystemRestart_SOURCES = testSystemRestart.cpp
+testTimeout_SOURCES = testTimeout.cpp
+testTransactions_SOURCES = testTransactions.cpp
+testDeadlock_SOURCES = testDeadlock.cpp
+test_event_SOURCES = test_event.cpp
+ndbapi_slow_select_SOURCES = slow_select.cpp
+testReadPerf_SOURCES = testReadPerf.cpp
+testLcp_SOURCES = testLcp.cpp
+testPartitioning_SOURCES = testPartitioning.cpp
+testBitfield_SOURCES = testBitfield.cpp
+DbCreate_SOURCES = bench/mainPopulate.cpp bench/dbPopulate.cpp bench/userInterface.cpp bench/dbPopulate.h bench/userInterface.h bench/testData.h bench/testDefinitions.h bench/ndb_schema.hpp bench/ndb_error.hpp
+DbAsyncGenerator_SOURCES = bench/mainAsyncGenerator.cpp bench/asyncGenerator.cpp bench/ndb_async2.cpp bench/dbGenerator.h bench/macros.h bench/userInterface.h bench/testData.h bench/testDefinitions.h bench/ndb_schema.hpp bench/ndb_error.hpp
+test_event_multi_table_SOURCES = test_event_multi_table.cpp
+
+INCLUDES_LOC = -I$(top_srcdir)/ndb/include/kernel
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_ndbapitest.mk.am
+
+##testDict_INCLUDES = $(INCLUDES) -I$(top_srcdir)/ndb/include/kernel
+##testIndex_INCLUDES = $(INCLUDES) -I$(top_srcdir)/ndb/include/kernel
+##testSystemRestart_INCLUDES = $(INCLUDES) -I$(top_srcdir)/ndb/include/kernel
+##testTransactions_INCLUDES = $(INCLUDES) -I$(top_srcdir)/ndb/include/kernel
+testBackup_LDADD = $(LDADD) bank/libbank.a
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+
+
+windoze-dsp: flexBench.dsp testBasic.dsp testBlobs.dsp \
+ testScan.dsp
+
+flexBench.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 $@ flexBench
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(flexBench_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
+
+testBasic.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 $@ testBasic
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(testBasic_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
+
+testOIBasic.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 $@ testOIBasic
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(testOIBasic_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
+
+testBlobs.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 $@ testBlobs
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(testBlobs_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
+
+testScan.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 $@ testScan
+ @$(top_srcdir)/ndb/config/win-includes $@ $(INCLUDES)
+ @$(top_srcdir)/ndb/config/win-sources $@ $(testScan_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LINK $(LDADD)
diff --git a/storage/ndb/test/ndbapi/ScanFilter.hpp b/storage/ndb/test/ndbapi/ScanFilter.hpp
new file mode 100644
index 00000000000..09786756798
--- /dev/null
+++ b/storage/ndb/test/ndbapi/ScanFilter.hpp
@@ -0,0 +1,131 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 SCAN_FILTER_HPP
+#define SCAN_FILTER_HPP
+
+class ScanFilter {
+public:
+#if 0
+ /**
+ * Create a scan filter for table tab
+ * colNo - column to filter on
+ * val - val to use when selecting valu to filter on
+ *
+ */
+ ScanFilter(const NDBT_Table& tab,
+ int colNo,
+ int val);
+#endif
+ ScanFilter(int records = 1000){};
+ virtual int filterOp(NdbOperation*) = 0;
+ virtual int verifyRecord(NDBT_ResultRow&) = 0;
+private:
+
+ // const NDBT_Table& tab;
+};
+
+class LessThanFilter : public ScanFilter {
+public:
+ LessThanFilter(int records){ compare_value = records / 100; };
+private:
+ Uint32 compare_value;
+ int filterOp(NdbOperation* pOp);
+ int verifyRecord(NDBT_ResultRow&);
+};
+
+class EqualFilter : public ScanFilter {
+ static const Uint32 compare_value = 100;
+ int filterOp(NdbOperation* pOp);
+ int verifyRecord(NDBT_ResultRow&);
+};
+
+class NoFilter : public ScanFilter {
+ int filterOp(NdbOperation* pOp);
+ int verifyRecord(NDBT_ResultRow&);
+};
+
+
+int LessThanFilter::filterOp(NdbOperation* pOp){
+
+ if (pOp->load_const_u32(1, compare_value) != 0)
+ return NDBT_FAILED;
+
+ if (pOp->read_attr("KOL2", 2) != 0)
+ return NDBT_FAILED;
+
+ if (pOp->branch_lt(1, 2, 0) != 0)
+ return NDBT_FAILED;
+
+ if (pOp->interpret_exit_nok() != 0)
+ return NDBT_FAILED;
+
+ if (pOp->def_label(0) != 0)
+ return NDBT_FAILED;
+
+ if (pOp->interpret_exit_ok() != 0)
+ return NDBT_FAILED;
+
+ return NDBT_OK;
+}
+
+int LessThanFilter::verifyRecord(NDBT_ResultRow& row){
+ NdbRecAttr* rec = row.attributeStore(1);
+ if (rec->u_32_value() < compare_value)
+ return NDBT_OK;
+ return NDBT_FAILED;
+}
+
+int EqualFilter::filterOp(NdbOperation* pOp){
+
+ if (pOp->load_const_u32(1, compare_value) != 0)
+ return NDBT_FAILED;
+
+ if (pOp->read_attr("KOL2", 2) != 0)
+ return NDBT_FAILED;
+
+ if (pOp->branch_eq(1, 2, 0) != 0)
+ return NDBT_FAILED;
+
+ if (pOp->interpret_exit_nok() != 0)
+ return NDBT_FAILED;
+
+ if (pOp->def_label(0) != 0)
+ return NDBT_FAILED;
+
+ if (pOp->interpret_exit_ok() != 0)
+ return NDBT_FAILED;
+
+ return NDBT_OK;
+}
+
+int EqualFilter::verifyRecord(NDBT_ResultRow& row){
+ NdbRecAttr* rec = row.attributeStore(1);
+ if (rec->u_32_value() == compare_value)
+ return NDBT_OK;
+ return NDBT_FAILED;
+}
+
+int NoFilter::filterOp(NdbOperation* pOp){
+ return NDBT_OK;
+}
+
+int NoFilter::verifyRecord(NDBT_ResultRow& row){
+ // Check if this record should be in the result set or not
+ return NDBT_OK;
+}
+
+#endif
diff --git a/storage/ndb/test/ndbapi/ScanFunctions.hpp b/storage/ndb/test/ndbapi/ScanFunctions.hpp
new file mode 100644
index 00000000000..37389d9b7de
--- /dev/null
+++ b/storage/ndb/test/ndbapi/ScanFunctions.hpp
@@ -0,0 +1,352 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+
+
+
+struct Attrib {
+ int numAttribs;
+ int attribs[1024];
+};
+class AttribList {
+public:
+ AttribList(){};
+ ~AttribList(){
+ for(size_t i = 0; i < attriblist.size(); i++){
+ delete attriblist[i];
+ }
+ };
+ void buildAttribList(const NdbDictionary::Table* pTab);
+ Vector<Attrib*> attriblist;
+};
+
+
+// Functions that help out in testing that we may call
+// scan functions in wrong order etc
+// and receive a proper errormessage
+class ScanFunctions {
+public:
+ ScanFunctions(const NdbDictionary::Table& _tab) : tab(_tab){
+ }
+ enum ActionType {
+ CloseWithoutStop,
+ NextScanWhenNoMore,
+ ExecuteScanWithOutOpenScan,
+ OnlyOneScanPerTrans,
+ OnlyOneOpBeforeOpenScan,
+ OnlyOpenScanOnce,
+ OnlyOneOpInScanTrans,
+ CheckInactivityTimeOut,
+ CheckInactivityBeforeClose ,
+ NoCloseTransaction,
+ EqualAfterOpenScan
+ };
+
+
+ int scanReadFunctions(Ndb* pNdb,
+ int records,
+ int parallelism,
+ ActionType action,
+ bool exclusive);
+private:
+ const NdbDictionary::Table& tab;
+};
+
+
+inline
+int
+ScanFunctions::scanReadFunctions(Ndb* pNdb,
+ int records,
+ int parallelism,
+ ActionType action,
+ bool exclusive){
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int sleepTime = 10;
+ int check;
+ NdbConnection *pTrans = 0;
+ NdbScanOperation *pOp = 0;
+
+ while (true){
+ if (retryAttempt >= retryMax){
+ g_err << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ // Execute the scan without defining a scan operation
+ pOp = pTrans->getNdbScanOperation(tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples(exclusive ?
+ NdbScanOperation::LM_Exclusive :
+ NdbScanOperation::LM_Read) ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+
+ if (action == OnlyOpenScanOnce){
+ // Call openScan one more time when it's already defined
+ if( pOp->readTuples(NdbScanOperation::LM_Read) ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+
+ if (action==EqualAfterOpenScan){
+ check = pOp->equal(tab.getColumn(0)->getName(), 10);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ for(int a = 0; a<tab.getNoOfColumns(); a++){
+ if(pOp->getValue(tab.getColumn(a)->getName()) == NULL) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ int abortCount = records / 10;
+ bool abortTrans = (action==CloseWithoutStop);
+ int eof;
+ int rows = 0;
+ eof = pOp->nextResult();
+
+ while(eof == 0){
+ rows++;
+
+ if (abortCount == rows && abortTrans == true){
+ g_info << "Scan is aborted after "<<abortCount<<" rows" << endl;
+
+ if (action != CloseWithoutStop){
+ // Test that we can closeTrans without stopScan
+ pOp->close();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+
+
+ pNdb->closeTransaction(pTrans);
+ return NDBT_OK;
+ }
+
+ if(action == CheckInactivityTimeOut){
+ if ((rows % (records / 10)) == 0){
+ // Sleep for a long time before calling nextScanResult
+ if (sleepTime > 1)
+ sleepTime--;
+ g_info << "Sleeping "<<sleepTime<<" secs " << endl;
+ NdbSleep_SecSleep(sleepTime);
+ }
+ }
+
+ eof = pOp->nextResult();
+ }
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+
+ // Be cruel, call nextScanResult after error
+ for(int i=0; i<10; i++){
+ eof = pOp->nextResult();
+ if(eof == 0){
+ g_err << "nextScanResult returned eof = " << eof << endl
+ << " That is an error when there are no more records" << endl;
+ return NDBT_FAILED;
+ }
+ }
+ // Be cruel end
+
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ g_info << "Starting over" << endl;
+
+ // If test is CheckInactivityTimeOut
+ // error 296 is expected
+ if ((action == CheckInactivityTimeOut) &&
+ (err.code == 296))
+ return NDBT_OK;
+
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if (action == NextScanWhenNoMore){
+ g_info << "Calling nextScanresult when there are no more records" << endl;
+ for(int i=0; i<10; i++){
+ eof = pOp->nextResult();
+ if(eof == 0){
+ g_err << "nextScanResult returned eof = " << eof << endl
+ << " That is an error when there are no more records" << endl;
+ return NDBT_FAILED;
+ }
+ }
+
+ }
+ if(action == CheckInactivityBeforeClose){
+ // Sleep for a long time before calling close
+ g_info << "NdbSleep_SecSleep(5) before close transaction" << endl;
+ NdbSleep_SecSleep(5);
+ }
+ if(action == NoCloseTransaction)
+ g_info << "Forgetting to close transaction" << endl;
+ else
+ pNdb->closeTransaction(pTrans);
+
+ g_info << rows << " rows have been read" << endl;
+ if (records != 0 && rows != records){
+ g_err << "Check expected number of records failed" << endl
+ << " expected=" << records <<", " << endl
+ << " read=" << rows << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+
+
+}
+
+void AttribList::buildAttribList(const NdbDictionary::Table* pTab){
+ attriblist.clear();
+
+ Attrib* attr;
+ // Build attrib definitions that describes which attributes to read
+ // Try to build strange combinations, not just "all" or all PK's
+
+ // Scan without reading any attributes
+ attr = new Attrib;
+ attr->numAttribs = 0;
+ attriblist.push_back(attr);
+ int i;
+ for(i = 1; i < pTab->getNoOfColumns(); i++){
+ attr = new Attrib;
+ attr->numAttribs = i;
+ for(int a = 0; a<i; a++)
+ attr->attribs[a] = a;
+ attriblist.push_back(attr);
+ }
+ for(i = pTab->getNoOfColumns()-1; i > 0; i--){
+ attr = new Attrib;
+ attr->numAttribs = i;
+ for(int a = 0; a<i; a++)
+ attr->attribs[a] = a;
+ attriblist.push_back(attr);
+ }
+ for(i = pTab->getNoOfColumns(); i > 0; i--){
+ attr = new Attrib;
+ attr->numAttribs = pTab->getNoOfColumns() - i;
+ for(int a = 0; a<pTab->getNoOfColumns() - i; a++)
+ attr->attribs[a] = pTab->getNoOfColumns()-a-1;
+ attriblist.push_back(attr);
+ }
+ for(i = 1; i < pTab->getNoOfColumns(); i++){
+ attr = new Attrib;
+ attr->numAttribs = pTab->getNoOfColumns() - i;
+ for(int a = 0; a<pTab->getNoOfColumns() - i; a++)
+ attr->attribs[a] = pTab->getNoOfColumns()-a-1;
+ attriblist.push_back(attr);
+ }
+ for(i = 1; i < pTab->getNoOfColumns(); i++){
+ attr = new Attrib;
+ attr->numAttribs = 2;
+ for(int a = 0; a<2; a++){
+ attr->attribs[a] = i%pTab->getNoOfColumns();
+ }
+ attriblist.push_back(attr);
+ }
+
+ // Last
+ attr = new Attrib;
+ attr->numAttribs = 1;
+ attr->attribs[0] = pTab->getNoOfColumns()-1;
+ attriblist.push_back(attr);
+
+ // Last and first
+ attr = new Attrib;
+ attr->numAttribs = 2;
+ attr->attribs[0] = pTab->getNoOfColumns()-1;
+ attr->attribs[1] = 0;
+ attriblist.push_back(attr);
+
+ // First and last
+ attr = new Attrib;
+ attr->numAttribs = 2;
+ attr->attribs[0] = 0;
+ attr->attribs[1] = pTab->getNoOfColumns()-1;
+ attriblist.push_back(attr);
+
+#if 1
+ for(size_t j = 0; j < attriblist.size(); j++){
+
+ g_info << attriblist[j]->numAttribs << ": " ;
+ for(int a = 0; a < attriblist[j]->numAttribs; a++)
+ g_info << attriblist[j]->attribs[a] << ", ";
+ g_info << endl;
+ }
+#endif
+
+}
diff --git a/storage/ndb/test/ndbapi/ScanInterpretTest.hpp b/storage/ndb/test/ndbapi/ScanInterpretTest.hpp
new file mode 100644
index 00000000000..d4e9bbecc81
--- /dev/null
+++ b/storage/ndb/test/ndbapi/ScanInterpretTest.hpp
@@ -0,0 +1,515 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 SCAN_INTERPRET_TEST_HPP
+#define SCAN_INTERPRET_TEST_HPP
+
+#include "ScanFilter.hpp"
+
+class ScanInterpretTest {
+public:
+ ScanInterpretTest(const NdbDictionary::Table& _tab,
+ const NdbDictionary::Table& _restab) :
+ tab(_tab),
+ restab(_restab),
+ row(_tab){
+ }
+
+ int scanRead(Ndb*,
+ int records,
+ int parallelism,
+ ScanFilter& filter);
+ int scanReadVerify(Ndb*,
+ int records,
+ int parallelism,
+ ScanFilter& filter);
+
+ int addRowToInsert(Ndb* pNdb,
+ NdbConnection* pInsTrans);
+ int addRowToCheckTrans(Ndb* pNdb,
+ NdbConnection* pCheckTrans);
+private:
+ const NdbDictionary::Table& tab;
+ const NdbDictionary::Table& restab;
+ NDBT_ResultRow row;
+
+};
+
+
+inline
+int
+ScanInterpretTest::addRowToInsert(Ndb* pNdb,
+ NdbConnection* pInsTrans){
+
+ NdbOperation* pOp =
+ pInsTrans->getNdbOperation(restab.getName());
+ if (pOp == NULL) {
+ ERR(pInsTrans->getNdbError());
+ pNdb->closeTransaction(pInsTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->insertTuple() == -1 ) {
+ ERR(pInsTrans->getNdbError());
+ pNdb->closeTransaction(pInsTrans);
+ return NDBT_FAILED;
+ }
+
+ // Copy all attribute to the new operation
+ for (int a = 0; a<restab.getNoOfColumns(); a++){
+ const NdbDictionary::Column* attr = tab.getColumn(a);
+ NdbRecAttr* reca = row.attributeStore(a);
+ int check = -1;
+ switch (attr->getType()){
+ case NdbDictionary::Column::Char:
+ case NdbDictionary::Column::Varchar:
+ case NdbDictionary::Column::Binary:
+ case NdbDictionary::Column::Varbinary:{
+ check = pOp->setValue( attr->getName(),
+ reca->aRef());
+ break;
+ }
+ case NdbDictionary::Column::Int:{
+ check = pOp->setValue( attr->getName(),
+ reca->int32_value());
+ }
+ break;
+ case NdbDictionary::Column::Bigint:{
+ check = pOp->setValue( attr->getName(),
+ reca->int64_value());
+ }
+ break;
+ case NdbDictionary::Column::Unsigned:{
+ check = pOp->setValue( attr->getName(),
+ reca->u_32_value());
+ }
+ break;
+ case NdbDictionary::Column::Bigunsigned:{
+ check = pOp->setValue( attr->getName(),
+ reca->u_64_value());
+ }
+ break;
+ case NdbDictionary::Column::Float:
+ check = pOp->setValue( attr->getName(),
+ reca->float_value());
+
+ break;
+ default:
+ check = -1;
+ break;
+ }
+ if(check != 0){
+ ERR(pInsTrans->getNdbError());
+ pNdb->closeTransaction(pInsTrans);
+ return NDBT_FAILED;
+ }
+ }
+
+ return NDBT_OK;
+}
+
+inline
+int
+ScanInterpretTest::addRowToCheckTrans(Ndb* pNdb,
+ NdbConnection* pCheckTrans){
+
+ NdbOperation* pOp =
+ pCheckTrans->getNdbOperation(restab.getName());
+ if (pOp == NULL) {
+ ERR(pNdb->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ if(pOp->readTuple() != 0) {
+ ERR(pNdb->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Copy pk attribute's to the new operation
+ for (int a = 0; a<restab.getNoOfColumns(); a++){
+ const NdbDictionary::Column* attr = restab.getColumn(a);
+ if (attr->getPrimaryKey() == true){
+ NdbRecAttr* reca = row.attributeStore(a);
+ int check = -1;
+ switch (attr->getType()){
+ case NdbDictionary::Column::Char:
+ case NdbDictionary::Column::Varchar:
+ case NdbDictionary::Column::Binary:
+ case NdbDictionary::Column::Varbinary:{
+ check = pOp->equal( attr->getName(),
+ reca->aRef());
+ break;
+ }
+ case NdbDictionary::Column::Int:{
+ check = pOp->equal( attr->getName(),
+ reca->int32_value());
+ }
+ break;
+ case NdbDictionary::Column::Bigint:{
+ check = pOp->equal( attr->getName(),
+ reca->int64_value());
+ }
+ break;
+ case NdbDictionary::Column::Unsigned:{
+ check = pOp->equal( attr->getName(),
+ reca->u_32_value());
+ }
+ break;
+ case NdbDictionary::Column::Bigunsigned:{
+ check = pOp->equal( attr->getName(),
+ reca->u_64_value());
+ }
+ break;
+ default:
+ check = -1;
+ break;
+ }
+ if(check != 0){
+ ERR(pNdb->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ return NDBT_OK;
+}
+
+inline
+int
+ScanInterpretTest::scanRead(Ndb* pNdb,
+ int records,
+ int parallelism,
+ ScanFilter& filter){
+ int retryAttempt = 0;
+ int retryMax = 100;
+ int check;
+ NdbConnection *pTrans;
+ NdbScanOperation *pOp;
+
+ while (true){
+
+ if (retryAttempt >= retryMax){
+ ndbout << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ pOp = pTrans->getNdbScanOperation(tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples(NdbScanOperation::LM_Read, 0, parallelism) ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if (filter.filterOp(pOp) != 0){
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ // Read all attributes
+ for(int a = 0; a<tab.getNoOfColumns(); a++){
+ if((row.attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+ NdbConnection* pInsTrans;
+
+ while((eof = pOp->nextResult(true)) == 0){
+ do {
+ rows++;
+ if (addRowToInsert(pNdb, pTrans) != 0){
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ } while((eof = pOp->nextResult(false)) == 0);
+
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ pNdb->closeTransaction(pTrans);
+
+ g_info << rows << " rows have been scanned" << endl;
+
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+}
+
+inline
+int
+ScanInterpretTest::scanReadVerify(Ndb* pNdb,
+ int records,
+ int parallelism,
+ ScanFilter& filter){
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check;
+ NdbConnection *pTrans;
+ NdbScanOperation *pOp;
+
+ while (true){
+
+ if (retryAttempt >= retryMax){
+ ndbout << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+
+ pOp = pTrans->getNdbScanOperation(tab.getName());
+ if (pOp == NULL) { if (pOp->getValue("KOL2") == 0){
+ ERR(pNdb->getNdbError());
+ return NDBT_FAILED;
+ }
+
+
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples(NdbScanOperation::LM_Read, 0, parallelism) ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if (check == -1) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+
+ // Read all attributes
+ for(int a = 0; a<tab.getNoOfColumns(); a++){
+ if((row.attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+ int rowsNoExist = 0;
+ int rowsExist = 0;
+ int existingRecordsNotFound = 0;
+ int nonExistingRecordsFound = 0;
+
+
+ NdbConnection* pExistTrans;
+ NdbConnection* pNoExistTrans;
+
+ while((eof = pOp->nextResult(true)) == 0){
+ pExistTrans = pNdb->startTransaction();
+ if (pExistTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+ ERR(err);
+ return NDBT_FAILED;
+ }
+ pNoExistTrans = pNdb->startTransaction();
+ if (pNoExistTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+ ERR(err);
+ return NDBT_FAILED;
+ }
+ do {
+ rows++;
+ if (filter.verifyRecord(row) == NDBT_OK){
+ rowsExist++;
+ if (addRowToCheckTrans(pNdb, pExistTrans) != 0){
+ pNdb->closeTransaction(pTrans);
+ pNdb->closeTransaction(pExistTrans);
+ pNdb->closeTransaction(pNoExistTrans);
+ return NDBT_FAILED;
+ }
+ }else{
+ rowsNoExist++;
+ if (addRowToCheckTrans(pNdb, pNoExistTrans) != 0){
+ pNdb->closeTransaction(pTrans);
+ pNdb->closeTransaction(pExistTrans);
+ pNdb->closeTransaction(pNoExistTrans);
+ return NDBT_FAILED;
+ }
+ }
+ } while((eof = pOp->nextResult(false)) == 0);
+
+
+ // Execute the transaction containing reads of
+ // all the records that should be in the result table
+ check = pExistTrans->execute(Commit);
+ if( check == -1 ) {
+ const NdbError err = pExistTrans->getNdbError();
+ ERR(err);
+ if (err.code != 626){
+ pNdb->closeTransaction(pExistTrans);
+ pNdb->closeTransaction(pNoExistTrans);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }else{
+ // Some of the records expected to be found wasn't
+ // there
+ existingRecordsNotFound = 1;
+ }
+ }
+ pNdb->closeTransaction(pExistTrans);
+
+ // Execute the transaction containing reads of
+ // all the records that should NOT be in the result table
+ check = pNoExistTrans->execute(Commit, CommitAsMuchAsPossible);
+ if( check == -1 ) {
+ const NdbError err = pNoExistTrans->getNdbError();
+ // The transactions error code should be zero
+ if (err.code != 626){
+ ERR(err);
+ pNdb->closeTransaction(pNoExistTrans);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ // Loop through the no existing transaction and check that no
+ // operations where successful
+ const NdbOperation* pOp2 = NULL;
+ while ((pOp2 = pNoExistTrans->getNextCompletedOperation(pOp2)) != NULL){
+ const NdbError err = pOp2->getNdbError();
+ if (err.code != 626){
+ ndbout << "err.code = " << err.code<< endl;
+ nonExistingRecordsFound = 1;
+ }
+ }
+ }
+
+ pNdb->closeTransaction(pNoExistTrans);
+
+
+ }
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ int testResult = NDBT_OK;
+ int rowsResult = 0;
+ UtilTransactions utilTrans(restab);
+ if (utilTrans.selectCount(pNdb,
+ 240,
+ &rowsResult) != 0){
+ return NDBT_FAILED;
+ }
+ if (existingRecordsNotFound == 1){
+ ndbout << "!!! Expected records not found" << endl;
+ testResult = NDBT_FAILED;
+ }
+ if (nonExistingRecordsFound == 1){
+ ndbout << "!!! Unxpected records found" << endl;
+ testResult = NDBT_FAILED;
+ }
+ ndbout << rows << " rows scanned("
+ << rowsExist << " found, " << rowsResult<<" expected)" << endl;
+ if (rowsResult != rowsExist){
+ ndbout << "!!! Number of rows in result table different from expected" << endl;
+ testResult = NDBT_FAILED;
+ }
+
+ return testResult;
+ }
+ return NDBT_FAILED;
+}
+
+#endif
diff --git a/storage/ndb/test/ndbapi/TraceNdbApi.cpp b/storage/ndb/test/ndbapi/TraceNdbApi.cpp
new file mode 100644
index 00000000000..bd43b15f2e6
--- /dev/null
+++ b/storage/ndb/test/ndbapi/TraceNdbApi.cpp
@@ -0,0 +1,543 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <NdbOut.hpp>
+#include <NdbMutex.h>
+
+#include "TraceNdbApi.hpp"
+
+
+int g_nParamTrace;
+NdbMutex* g_pNdbMutexTrace = 0;
+
+
+void TraceBegin(void)
+{
+ if(!g_pNdbMutexTrace)
+ {
+ g_pNdbMutexTrace = NdbMutex_Create();
+ }
+ NdbMutex_Lock(g_pNdbMutexTrace);
+ g_nParamTrace = 0;
+}
+
+void TraceEnd(void)
+{
+ ndbout << endl;
+ g_nParamTrace = 0;
+ NdbMutex_Unlock(g_pNdbMutexTrace);
+}
+
+void TraceMethod(const char* szMethod)
+{
+ ndbout << "->" << szMethod << "(";
+ g_nParamTrace = 0;
+}
+
+void TraceParamComma(void)
+{
+ if(g_nParamTrace)
+ {
+ ndbout << ", ";
+ }
+ ++g_nParamTrace;
+}
+
+void TraceNdb(Ndb* pNdb)
+{
+ TraceParamComma();
+ ndbout << "((Ndb*)" << hex << (Uint32)pNdb << ")";
+}
+
+void TraceNdbSchemaCon(NdbSchemaCon* pNdbSchemaCon)
+{
+ TraceParamComma();
+ ndbout << "((NdbSchemaCon*)" << hex << (Uint32)pNdbSchemaCon << ")";
+}
+
+void TraceNdbSchemaOp(NdbSchemaOp* pNdbSchemaOp)
+{
+ TraceParamComma();
+ ndbout << "((NdbSchemaOp*)" << hex << (Uint32)pNdbSchemaOp << ")";
+}
+
+void TraceNdbConnection(const NdbConnection* pNdbConnection)
+{
+ TraceParamComma();
+ ndbout << "((NdbConnection*)" << hex << (Uint32)pNdbConnection << ")";
+}
+
+void TraceNdbOperation(NdbOperation* pNdbOperation)
+{
+ TraceParamComma();
+ ndbout << "((NdbOperation*)" << hex << (Uint32)pNdbOperation << ")";
+}
+
+void TraceNdbIndexOperation(NdbIndexOperation* pNdbIndexOperation)
+{
+ TraceParamComma();
+ ndbout << "((NdbIndexOperation*)" << hex << (Uint32)pNdbIndexOperation << ")";
+}
+
+void TraceNdbRecAttr(NdbRecAttr* pNdbRecAttr)
+{
+ TraceParamComma();
+ ndbout << "((NdbRecAttr*)" << hex << (Uint32)pNdbRecAttr << ")";
+}
+
+void TraceTable(Table* pTable)
+{
+ TraceParamComma();
+ ndbout << "((Table*)" << hex << (Uint32)pTable << ")";
+}
+
+void TraceString(const char* szParam)
+{
+ TraceParamComma();
+ ndbout << "\"" << szParam << "\"";
+}
+
+void TraceInt(const int i)
+{
+ TraceParamComma();
+ ndbout << "(int)" << dec << i;
+}
+
+void TraceUint32(const Uint32 n)
+{
+ TraceParamComma();
+ ndbout << "(Uint32)" << dec << n;
+}
+
+void TraceKeyType(const KeyType aKeyType)
+{
+ TraceParamComma();
+ switch(aKeyType)
+ {
+ case Undefined: ndbout << "Undefined"; break;
+ case NoKey: ndbout << "NoKey"; break;
+ case TupleKey: ndbout << "TupleKey"; break;
+ case TupleId: ndbout << "TupleId"; break;
+ default: ndbout << "(KeyType)" << aKeyType; break;
+ }
+}
+
+void TraceExecType(const ExecType aExecType)
+{
+ switch(aExecType)
+ {
+ case NoExecTypeDef: ndbout << "NoExecTypeDef"; break;
+ case Prepare: ndbout << "Prepare"; break;
+ case NoCommit: ndbout << "NoCommit"; break;
+ case Commit: ndbout << "Commit"; break;
+ case Rollback: ndbout << "Rollback"; break;
+ default: ndbout << "(ExecType)" << aExecType; break;
+ }
+}
+
+
+void TraceNdbError(const NdbError& err)
+{
+ TraceParamComma();
+ ndbout << "(NdbError)" << err;
+}
+
+
+
+void TraceVoid(void)
+{
+ ndbout << "void";
+}
+
+void TraceReturn(void)
+{
+ ndbout << "); // return ";
+ g_nParamTrace = 0;
+}
+
+
+// TraceNdbSchemaOp
+
+int CTraceNdbSchemaOp::createTable(const char* aTableName)
+{
+ int i = NdbSchemaOp::createTable(aTableName);
+ TraceBegin();
+ TraceNdbSchemaOp(this);
+ TraceMethod("createTable");
+ TraceString(aTableName);
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+int CTraceNdbSchemaOp::createAttribute(const char* aAttrName, KeyType aTupleyKey)
+{
+ int i = NdbSchemaOp::createAttribute(aAttrName, aTupleyKey);
+ TraceBegin();
+ TraceNdbSchemaOp(this);
+ TraceMethod("createAttribute");
+ TraceString(aAttrName);
+ TraceKeyType(aTupleyKey);
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+
+
+// TraceNdbSchemaCon
+
+CTraceNdbSchemaOp* CTraceNdbSchemaCon::getNdbSchemaOp()
+{
+ NdbSchemaOp* pNdbSchemaOp = NdbSchemaCon::getNdbSchemaOp();
+ TraceBegin();
+ TraceNdbSchemaCon(this);
+ TraceMethod("getNdbSchemaOp");
+ TraceReturn();
+ TraceNdbSchemaOp(pNdbSchemaOp);
+ TraceEnd();
+ return (CTraceNdbSchemaOp*)pNdbSchemaOp;
+}
+
+int CTraceNdbSchemaCon::execute()
+{
+ int i = NdbSchemaCon::execute();
+ TraceBegin();
+ TraceNdbSchemaCon(this);
+ TraceMethod("execute");
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+
+
+// TraceNdbRecAttr
+
+Uint32 CTraceNdbRecAttr::u_32_value()
+{
+ Uint32 n = NdbRecAttr::u_32_value();
+ TraceBegin();
+ TraceNdbRecAttr(this);
+ TraceMethod("u_32_value");
+ TraceReturn();
+ TraceUint32(n);
+ TraceEnd();
+ return n;
+}
+
+
+
+// TraceNdbOperation
+
+int CTraceNdbOperation::insertTuple()
+{
+ int i = NdbOperation::insertTuple();
+ TraceBegin();
+ TraceNdbOperation(this);
+ TraceMethod("insertTuple");
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+int CTraceNdbOperation::updateTuple()
+{
+ int i = NdbOperation::updateTuple();
+ TraceBegin();
+ TraceNdbOperation(this);
+ TraceMethod("updateTuple");
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+int CTraceNdbOperation::interpretedUpdateTuple()
+{
+ int i = NdbOperation::interpretedUpdateTuple();
+ TraceBegin();
+ TraceNdbOperation(this);
+ TraceMethod("interpretedUpdateTuple");
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+int CTraceNdbOperation::readTuple()
+{
+ int i = NdbOperation::readTuple();
+ TraceBegin();
+ TraceNdbOperation(this);
+ TraceMethod("readTuple");
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+
+int CTraceNdbOperation::readTupleExclusive()
+{
+ int i = NdbOperation::readTupleExclusive();
+ TraceBegin();
+ TraceNdbOperation(this);
+ TraceMethod("readTupleExclusive");
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+
+int CTraceNdbOperation::deleteTuple()
+{
+ int i = NdbOperation::deleteTuple();
+ TraceBegin();
+ TraceNdbOperation(this);
+ TraceMethod("deleteTuple");
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+int CTraceNdbOperation::equal(const char* anAttrName, Uint32 aValue)
+{
+ int i = NdbOperation::equal(anAttrName, aValue);
+ TraceBegin();
+ TraceNdbOperation(this);
+ TraceMethod("equal");
+ TraceString(anAttrName);
+ TraceUint32(aValue);
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+int CTraceNdbOperation::setValue(const char* anAttrName, Uint32 aValue)
+{
+ int i = NdbOperation::setValue(anAttrName, aValue);
+ TraceBegin();
+ TraceNdbOperation(this);
+ TraceMethod("setValue");
+ TraceString(anAttrName);
+ TraceUint32(aValue);
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+int CTraceNdbOperation::incValue(const char* anAttrName, Uint32 aValue)
+{
+ int i = NdbOperation::incValue(anAttrName, aValue);
+ TraceBegin();
+ TraceNdbOperation(this);
+ TraceMethod("incValue");
+ TraceString(anAttrName);
+ TraceUint32(aValue);
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+CTraceNdbRecAttr* CTraceNdbOperation::getValue(const char* anAttrName)
+{
+ NdbRecAttr* pNdbRecAttr = NdbOperation::getValue(anAttrName);
+ TraceBegin();
+ TraceNdbOperation(this);
+ TraceMethod("getValue");
+ TraceString(anAttrName);
+ TraceReturn();
+ TraceNdbRecAttr(pNdbRecAttr);
+ TraceEnd();
+ return (CTraceNdbRecAttr*)pNdbRecAttr;
+}
+
+
+// TraceNdbIndexOperation
+
+int CTraceNdbIndexOperation::equal(const char* anAttrName, Uint32 aValue)
+{
+ int i = NdbIndexOperation::equal(anAttrName, aValue);
+ TraceBegin();
+ TraceNdbIndexOperation(this);
+ TraceMethod("equal");
+ TraceString(anAttrName);
+ TraceUint32(aValue);
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+
+int CTraceNdbIndexOperation::incValue(const char* anAttrName, Uint32 aValue)
+{
+ int i = NdbIndexOperation::incValue(anAttrName, aValue);
+ TraceBegin();
+ TraceNdbIndexOperation(this);
+ TraceMethod("incValue");
+ TraceString(anAttrName);
+ TraceUint32(aValue);
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+
+CTraceNdbRecAttr* CTraceNdbIndexOperation::getValue(const char* anAttrName)
+{
+ NdbRecAttr* pNdbRecAttr = NdbIndexOperation::getValue(anAttrName);
+ TraceBegin();
+ TraceNdbIndexOperation(this);
+ TraceMethod("getValue");
+ TraceString(anAttrName);
+ TraceReturn();
+ TraceNdbRecAttr(pNdbRecAttr);
+ TraceEnd();
+ return (CTraceNdbRecAttr*)pNdbRecAttr;
+}
+
+
+// TraceNdbConnection
+
+CTraceNdbOperation* CTraceNdbConnection::getNdbOperation(const char* aTableName)
+{
+ NdbOperation* pNdbOperation = NdbConnection::getNdbOperation(aTableName);
+ TraceBegin();
+ TraceNdbConnection(this);
+ TraceMethod("getNdbOperation");
+ TraceString(aTableName);
+ TraceReturn();
+ TraceNdbOperation(pNdbOperation);
+ TraceEnd();
+ return (CTraceNdbOperation*)pNdbOperation;
+}
+
+CTraceNdbIndexOperation* CTraceNdbConnection::getNdbIndexOperation(const char* anIndexName, const char* aTableName)
+{
+ NdbIndexOperation* pNdbIndexOperation = NdbConnection::getNdbIndexOperation(anIndexName, aTableName);
+ TraceBegin();
+ TraceNdbConnection(this);
+ TraceMethod("getNdbIndexOperation");
+ TraceString(anIndexName);
+ TraceString(aTableName);
+ TraceReturn();
+ TraceNdbIndexOperation(pNdbIndexOperation);
+ TraceEnd();
+ return (CTraceNdbIndexOperation*)pNdbIndexOperation;
+}
+
+int CTraceNdbConnection::execute(ExecType aTypeOfExec)
+{
+ int i = NdbConnection::execute(aTypeOfExec);
+ TraceBegin();
+ TraceNdbConnection(this);
+ TraceMethod("execute");
+ TraceExecType(aTypeOfExec);
+ TraceReturn();
+ TraceInt(i);
+ TraceEnd();
+ return i;
+}
+
+const NdbError & CTraceNdbConnection::getNdbError(void) const
+{
+ const NdbError& err = NdbConnection::getNdbError();
+ TraceBegin();
+ TraceNdbConnection(this);
+ TraceMethod("getNdbError");
+ TraceReturn();
+ TraceNdbError(err);
+ TraceEnd();
+ return err;
+}
+
+
+
+// TraceNdb
+
+CTraceNdb::CTraceNdb(const char* aDataBase)
+: Ndb(aDataBase)
+{
+ TraceBegin();
+ TraceNdb(this);
+ TraceMethod("Ndb");
+ TraceString(aDataBase);
+ TraceReturn();
+ TraceVoid();
+ TraceEnd();
+}
+
+CTraceNdbSchemaCon* CTraceNdb::startSchemaTransaction()
+{
+ NdbSchemaCon* pNdbSchemaCon = Ndb::startSchemaTransaction();
+ TraceBegin();
+ TraceNdb(this);
+ TraceMethod("startSchemaTransaction");
+ TraceReturn();
+ TraceNdbSchemaCon(pNdbSchemaCon);
+ TraceEnd();
+ return (CTraceNdbSchemaCon*)pNdbSchemaCon;
+}
+
+void CTraceNdb::closeSchemaTransaction(CTraceNdbSchemaCon* aSchemaCon)
+{
+ Ndb::closeSchemaTransaction(aSchemaCon);
+ TraceBegin();
+ TraceNdb(this);
+ TraceMethod("closeSchemaTransaction");
+ TraceReturn();
+ TraceVoid();
+ TraceEnd();
+}
+
+CTraceNdbConnection* CTraceNdb::startTransaction()
+{
+ NdbConnection* pNdbConnection = Ndb::startTransaction();
+ TraceBegin();
+ TraceNdb(this);
+ TraceMethod("startTransaction");
+ TraceReturn();
+ TraceNdbConnection(pNdbConnection);
+ TraceEnd();
+ return (CTraceNdbConnection*)pNdbConnection;
+}
+
+void CTraceNdb::closeTransaction(CTraceNdbConnection* aConnection)
+{
+ Ndb::closeTransaction(aConnection);
+ TraceBegin();
+ TraceNdb(this);
+ TraceMethod("closeTransaction");
+ TraceNdbConnection(aConnection);
+ TraceReturn();
+ TraceVoid();
+ TraceEnd();
+}
+
+
diff --git a/storage/ndb/test/ndbapi/VerifyNdbApi.cpp b/storage/ndb/test/ndbapi/VerifyNdbApi.cpp
new file mode 100644
index 00000000000..79645827e2c
--- /dev/null
+++ b/storage/ndb/test/ndbapi/VerifyNdbApi.cpp
@@ -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 */
+
+
+#include <NdbApi.hpp>
+#include <NdbOut.hpp>
+#include <NdbMutex.h>
+
+#include "VerifyNdbApi.hpp"
+
+
+NdbMutex* g_pNdbMutexVerify = 0;
+
+
+void VerifyBegin(void)
+{
+ if(!g_pNdbMutexVerify)
+ {
+ g_pNdbMutexVerify = NdbMutex_Create();
+ }
+ NdbMutex_Lock(g_pNdbMutexVerify);
+}
+
+void VerifyEnd(void)
+{
+ NdbMutex_Unlock(g_pNdbMutexVerify);
+}
+
+
+
+void CVerifyNdbSchemaOp::VerifyIntError(const int i, const char* szMethod)
+{
+ VerifyBegin();
+ ndbout << "NdbSchemaOp::" << szMethod << " returned " << dec << i;
+ ndbout << " : " << dec << getNdbError().code << " : " << getNdbError().message << endl;
+ VerifyEnd();
+}
+
+
+void CVerifyNdbSchemaCon::VerifyIntError(const int i, const char* szMethod)
+{
+ VerifyBegin();
+ ndbout << "NdbSchemaCon::" << szMethod << " returned " << dec << i;
+ ndbout << " : " << dec << getNdbError().code << " : " << getNdbError().message << endl;
+ VerifyEnd();
+}
+
+
+void CVerifyNdbSchemaCon::VerifyPtrError(void* p, const char* szMethod)
+{
+ VerifyBegin();
+ ndbout << "NdbSchemaCon::" << szMethod << " returned " << hex << (Uint32)p;
+ ndbout << " : " << dec << getNdbError().code << " : " << getNdbError().message << endl;
+ VerifyEnd();
+}
+
+
+void CVerifyNdbRecAttr::VerifyValueError(const int iNull, const char* szMethod)
+{
+ VerifyBegin();
+ ndbout << "NdbRecAttr::" << szMethod << " : isNULL() returned " << dec << iNull;
+ ndbout << endl;
+ VerifyEnd();
+}
+
+
+void CVerifyNdbOperation::VerifyIntError(const int i, const char* szMethod)
+{
+ VerifyBegin();
+ ndbout << "NdbOperation::" << szMethod << " returned " << dec << i;
+ ndbout << " : " << dec << getNdbError().code << " : " << getNdbError().message << endl;
+ VerifyEnd();
+}
+
+
+void CVerifyNdbOperation::VerifyPtrError(void* p, const char* szMethod)
+{
+ VerifyBegin();
+ ndbout << "NdbOperation::" << szMethod << " returned " << hex << (Uint32)p;
+ ndbout << " : " << dec << getNdbError().code << " : " << getNdbError().message << endl;
+ VerifyEnd();
+}
+
+
+void CVerifyNdbIndexOperation::VerifyIntError(const int i, const char* szMethod)
+{
+ VerifyBegin();
+ ndbout << "NdbIndexOperation::" << szMethod << " returned " << dec << i;
+ ndbout << " : " << dec << getNdbError().code << " : " << getNdbError().message << endl;
+ VerifyEnd();
+}
+
+
+void CVerifyNdbIndexOperation::VerifyPtrError(void* p, const char* szMethod)
+{
+ VerifyBegin();
+ ndbout << "NdbIndexOperation::" << szMethod << " returned " << hex << (Uint32)p;
+ ndbout << " : " << dec << getNdbError().code << " : " << getNdbError().message << endl;
+ VerifyEnd();
+}
+
+
+void CVerifyNdbConnection::VerifyIntError(const int i, const char* szMethod)
+{
+ VerifyBegin();
+ ndbout << "NdbConnection::" << szMethod << " returned " << dec << i;
+ ndbout << " : " << dec << getNdbError().code << " : " << getNdbError().message << endl;
+ VerifyEnd();
+}
+
+
+void CVerifyNdbConnection::VerifyPtrError(void* p, const char* szMethod)
+{
+ VerifyBegin();
+ ndbout << "NdbConnection::" << szMethod << " returned " << hex << (Uint32)p;
+ ndbout << " : " << dec << getNdbError().code << " : " << getNdbError().message << endl;
+ VerifyEnd();
+}
+
+
+void CVerifyNdb::VerifyPtrError(void* p, const char* szMethod)
+{
+ VerifyBegin();
+ ndbout << "Ndb::" << szMethod << " returned " << hex << (Uint32)p;
+ ndbout << " : " << dec << getNdbError().code << " : " << getNdbError().message << endl;
+ VerifyEnd();
+}
+
+
+void CVerifyNdb::VerifyVoidError(const int iCode, const char* szMethod)
+{
+ VerifyBegin();
+ ndbout << "Ndb::" << szMethod << " : getNdbError().code returned " << dec << iCode;
+ ndbout << " : " << getNdbError().message << endl;
+ VerifyEnd();
+}
+
+
diff --git a/storage/ndb/test/ndbapi/acid.cpp b/storage/ndb/test/ndbapi/acid.cpp
new file mode 100644
index 00000000000..3eb1625be26
--- /dev/null
+++ b/storage/ndb/test/ndbapi/acid.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 <NdbApi.hpp>
+#include <NdbSchemaCon.hpp>
+#include <NdbMutex.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NdbThread.h>
+#include <NdbTick.h>
+#include <NdbMain.h>
+#include <NdbTest.hpp>
+#include <random.h>
+
+//#define TRACE
+#define DEBUG
+//#define RELEASE
+#define NODE_REC // epaulsa: introduces pointer checks to help 'acid' keep core
+// during node recovery
+
+#ifdef TRACE
+
+#define VerifyMethodInt(c, m) (ReportMethodInt(c->m, c, #c, #m, __FILE__, __LINE__))
+#define VerifyMethodPtr(v, c, m) (v=ReportMethodPtr(c->m, c, #v, #c, #m, __FILE__, __LINE__))
+#define VerifyMethodVoid(c, m) (c->m, ReportMethodVoid(c, #c, #m, __FILE__, __LINE__))
+
+int ReportMethodInt(int iRes, NdbConnection* pNdbConnection, const char* szClass, const char* szMethod, const char* szFile, const int iLine)
+{
+ ndbout << szFile << "(" << iLine << ") : ";
+ ndbout << szClass << "->" << szMethod << " return " << iRes << " : ";
+ ndbout << pNdbConnection->getNdbError();
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbErrorOperation();
+ if(pNdbOperation) {
+ ndbout << " : " << pNdbOperation->getNdbError();
+ }
+ ndbout << " : " << pNdbConnection->getNdbErrorLine();
+ ndbout << endl;
+ return iRes;
+}
+
+template <class C>
+int ReportMethodInt(int iRes, C* pC, const char* szClass, const char* szMethod, const char* szFile, const int iLine)
+{
+ ndbout << szFile << "(" << iLine << ") : ";
+ ndbout << szClass << "->" << szMethod << " return " << iRes << " : ";
+ ndbout << pC->getNdbError();
+ ndbout << endl;
+ return iRes;
+}
+
+template <class R, class C>
+R* ReportMethodPtr(R* pR, C* pC, const char* szVariable, const char* szClass, const char* szMethod, const char* szFile, const int iLine)
+{
+ ndbout << szFile << "(" << iLine << ") : ";
+ ndbout << szVariable << " = " << szClass << "->" << szMethod << " return " << (long)(void*)pR << " : ";
+ ndbout << pC->getNdbError();
+ ndbout << endl;
+ return pR;
+}
+
+template <class C>
+void ReportMethodVoid(C* pC, const char* szClass, const char* szMethod, const char* szFile, const int iLine)
+{
+ ndbout << szFile << "(" << iLine << ") : ";
+ ndbout << szClass << "->" << szMethod << " : ";
+ ndbout << pC->getNdbError();
+ ndbout << endl;
+}
+#endif /* TRACE */
+
+
+#ifdef DEBUG
+
+#define VerifyMethodInt(c, m) (ReportMethodInt(c->m, c, #c, #m, __FILE__, __LINE__))
+#define VerifyMethodPtr(v, c, m) (v=ReportMethodPtr(c->m, c, #v, #c, #m, __FILE__, __LINE__))
+#define VerifyMethodVoid(c, m) (c->m, ReportMethodVoid(c, #c, #m, __FILE__, __LINE__))
+
+int ReportMethodInt(int iRes, NdbConnection* pNdbConnection, const char* szClass, const char* szMethod, const char* szFile, const int iLine)
+{
+ if(iRes<0) {
+ ndbout << szFile << "(" << iLine << ") : ";
+ ndbout << szClass << "->" << szMethod << " return " << iRes << " : ";
+ ndbout << pNdbConnection->getNdbError();
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbErrorOperation();
+ if(pNdbOperation) {
+ ndbout << " : " << pNdbOperation->getNdbError();
+ }
+ ndbout << " : " << pNdbConnection->getNdbErrorLine();
+ ndbout << " : ";
+ ndbout << endl;
+ }
+ return iRes;
+}
+
+template <class C>
+int ReportMethodInt(int iRes, C* pC, const char* szClass, const char* szMethod, const char* szFile, const int iLine)
+{
+ if(iRes<0) {
+ ndbout << szFile << "(" << iLine << ") : ";
+ ndbout << szClass << "->" << szMethod << " return " << iRes << " : ";
+ ndbout << pC->getNdbError();
+ ndbout << endl;
+ }
+ return iRes;
+}
+
+template <class R, class C>
+R* ReportMethodPtr(R* pR, C* pC, const char* szVariable, const char* szClass, const char* szMethod, const char* szFile, const int iLine)
+{
+ if(!pR) {
+ ndbout << szFile << "(" << iLine << ") : ";
+ ndbout << szVariable << " = " << szClass << "->" << szMethod << " return " << " : ";
+ ndbout << pC->getNdbError();
+ ndbout << endl;
+ }
+ return pR;
+}
+
+template <class C>
+void ReportMethodVoid(C* pC, const char* szClass, const char* szMethod, const char* szFile, const int iLine)
+{
+ if(pC->getNdbError().code) {
+ ndbout << szFile << "(" << iLine << ") : ";
+ ndbout << szClass << "->" << szMethod << " : ";
+ ndbout << pC->getNdbError();
+ ndbout << endl;
+ }
+}
+
+
+#endif /* DEBUG */
+
+
+#ifdef RELEASE
+
+#define VerifyMethodInt(c, m) (c->m)
+#define VerifyMethodPtr(v, c, m) (v=(c->m))
+#define VerifyMethodVoid(c, m) (c->m)
+
+int ReportMethodInt(int iRes, NdbConnection* pNdbConnection, const char* szClass, const char* szMethod, const char* szFile, const int iLine)
+{
+ if(iRes<0) {
+ ndbout << szFile << "(" << iLine << ") : ";
+ ndbout << szClass << "->" << szMethod << " return " << iRes << " : ";
+ ndbout << pNdbConnection->getNdbError();
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbErrorOperation();
+ if(pNdbOperation) {
+ ndbout << " : " << pNdbOperation->getNdbError();
+ }
+ ndbout << " : " << pNdbConnection->getNdbErrorLine();
+ ndbout << endl;
+ }
+ return iRes;
+}
+
+#endif /* RELEASE */
+
+// epaulsa =>
+#ifndef NODE_REC
+#define CHK_TR(p)
+#else
+#define CHK_TR(p) if(!p){ \
+ ndbout <<"startTransaction failed, returning now." << endl ; \
+ delete pNdb ; \
+ pNdb = NULL ; \
+ return 0 ; \
+ }
+#endif // NODE_REC
+// <= epaulsa
+
+const char* c_szWarehouse = "WAREHOUSE";
+const char* c_szWarehouseNumber = "W_ID";
+const char* c_szWarehouseSum = "W_SUM";
+const char* c_szWarehouseCount = "W_CNT";
+const char* c_szDistrict = "DISTRICT";
+const char* c_szDistrictWarehouseNumber = "D_W_ID";
+const char* c_szDistrictNumber = "D_ID";
+const char* c_szDistrictSum = "D_SUM";
+const char* c_szDistrictCount = "D_CNT";
+
+Uint32 g_nWarehouseCount = 10;
+Uint32 g_nDistrictPerWarehouse = 10;
+Uint32 g_nThreadCount = 1;
+NdbMutex* g_pNdbMutex = 0;
+
+extern "C" void* NdbThreadFuncInsert(void* pArg)
+{
+ myRandom48Init((long int)NdbTick_CurrentMillisecond());
+ unsigned nSucc = 0;
+ unsigned nFail = 0;
+ Ndb* pNdb = NULL ;
+ pNdb = new Ndb("TEST_DB");
+ VerifyMethodInt(pNdb, init());
+ VerifyMethodInt(pNdb, waitUntilReady());
+
+ while(NdbMutex_Trylock(g_pNdbMutex)) {
+ Uint32 nWarehouse = myRandom48(g_nWarehouseCount);
+ NdbConnection* pNdbConnection = NULL ;
+ VerifyMethodPtr(pNdbConnection, pNdb, startTransaction());
+ CHK_TR(pNdbConnection);
+ NdbOperation* pNdbOperationW = NULL ;
+ VerifyMethodPtr(pNdbOperationW, pNdbConnection, getNdbOperation(c_szWarehouse));
+ VerifyMethodInt(pNdbOperationW, insertTuple());
+ VerifyMethodInt(pNdbOperationW, equal(c_szWarehouseNumber, nWarehouse));
+ VerifyMethodInt(pNdbOperationW, setValue(c_szWarehouseCount, Uint32(1)));
+ Uint32 nWarehouseSum = 0;
+ for(Uint32 nDistrict=0; nDistrict<g_nDistrictPerWarehouse; ++nDistrict) {
+ NdbOperation* pNdbOperationD = NULL ;
+ VerifyMethodPtr(pNdbOperationD, pNdbConnection, getNdbOperation(c_szDistrict));
+ VerifyMethodInt(pNdbOperationD, insertTuple());
+ VerifyMethodInt(pNdbOperationD, equal(c_szDistrictWarehouseNumber, nWarehouse));
+ VerifyMethodInt(pNdbOperationD, equal(c_szDistrictNumber, nDistrict));
+ VerifyMethodInt(pNdbOperationD, setValue(c_szDistrictCount, Uint32(1)));
+ Uint32 nDistrictSum = myRandom48(100);
+ nWarehouseSum += nDistrictSum;
+ VerifyMethodInt(pNdbOperationD, setValue(c_szDistrictSum, nDistrictSum));
+ }
+ VerifyMethodInt(pNdbOperationW, setValue(c_szWarehouseSum, nWarehouseSum));
+ int iExec = pNdbConnection->execute(Commit);
+ int iError = pNdbConnection->getNdbError().code;
+
+ if(iExec<0 && iError!=0 && iError!=266 && iError!=630) {
+ ReportMethodInt(iExec, pNdbConnection, "pNdbConnection", "execute(Commit)", __FILE__, __LINE__);
+ }
+ if(iExec==0) {
+ ++nSucc;
+ } else {
+ ++nFail;
+ }
+ VerifyMethodVoid(pNdb, closeTransaction(pNdbConnection));
+ }
+ ndbout << "insert: " << nSucc << " succeeded, " << nFail << " failed " << endl;
+ NdbMutex_Unlock(g_pNdbMutex);
+ delete pNdb;
+ pNdb = NULL ;
+ return NULL;
+}
+
+
+extern "C" void* NdbThreadFuncUpdate(void* pArg)
+{
+ myRandom48Init((long int)NdbTick_CurrentMillisecond());
+ unsigned nSucc = 0;
+ unsigned nFail = 0;
+ Ndb* pNdb = NULL ;
+ pNdb = new Ndb("TEST_DB");
+ VerifyMethodInt(pNdb, init());
+ VerifyMethodInt(pNdb, waitUntilReady());
+
+ while(NdbMutex_Trylock(g_pNdbMutex)) {
+ Uint32 nWarehouse = myRandom48(g_nWarehouseCount);
+ NdbConnection* pNdbConnection = NULL ;
+ VerifyMethodPtr(pNdbConnection, pNdb, startTransaction());
+ CHK_TR(pNdbConnection) ; // epaulsa
+ NdbOperation* pNdbOperationW = NULL ;
+ VerifyMethodPtr(pNdbOperationW, pNdbConnection, getNdbOperation(c_szWarehouse));
+ VerifyMethodInt(pNdbOperationW, interpretedUpdateTuple());
+ VerifyMethodInt(pNdbOperationW, equal(c_szWarehouseNumber, nWarehouse));
+ VerifyMethodInt(pNdbOperationW, incValue(c_szWarehouseCount, Uint32(1)));
+ Uint32 nWarehouseSum = 0;
+ for(Uint32 nDistrict=0; nDistrict<g_nDistrictPerWarehouse; ++nDistrict) {
+ NdbOperation* pNdbOperationD = NULL ;
+ VerifyMethodPtr(pNdbOperationD, pNdbConnection, getNdbOperation(c_szDistrict));
+ VerifyMethodInt(pNdbOperationD, interpretedUpdateTuple());
+ VerifyMethodInt(pNdbOperationD, equal(c_szDistrictWarehouseNumber, nWarehouse));
+ VerifyMethodInt(pNdbOperationD, equal(c_szDistrictNumber, nDistrict));
+ VerifyMethodInt(pNdbOperationD, incValue(c_szDistrictCount, Uint32(1)));
+ Uint32 nDistrictSum = myRandom48(100);
+ nWarehouseSum += nDistrictSum;
+ VerifyMethodInt(pNdbOperationD, setValue(c_szDistrictSum, nDistrictSum));
+ }
+ VerifyMethodInt(pNdbOperationW, setValue(c_szWarehouseSum, nWarehouseSum));
+ int iExec = pNdbConnection->execute(Commit);
+ int iError = pNdbConnection->getNdbError().code;
+
+ if(iExec<0 && iError!=0 && iError!=266 && iError!=626) {
+ ReportMethodInt(iExec, pNdbConnection, "pNdbConnection", "execute(Commit)", __FILE__, __LINE__);
+ }
+ if(iExec==0) {
+ ++nSucc;
+ } else {
+ ++nFail;
+ }
+ VerifyMethodVoid(pNdb, closeTransaction(pNdbConnection));
+ }
+ ndbout << "update: " << nSucc << " succeeded, " << nFail << " failed " << endl;
+ NdbMutex_Unlock(g_pNdbMutex);
+ delete pNdb;
+ pNdb = NULL ;
+ return NULL;
+}
+
+
+extern "C" void* NdbThreadFuncDelete(void* pArg)
+{
+ myRandom48Init((long int)NdbTick_CurrentMillisecond());
+ unsigned nSucc = 0;
+ unsigned nFail = 0;
+ Ndb* pNdb = NULL ;
+ pNdb = new Ndb("TEST_DB");
+ VerifyMethodInt(pNdb, init());
+ VerifyMethodInt(pNdb, waitUntilReady());
+
+ while(NdbMutex_Trylock(g_pNdbMutex)) {
+ Uint32 nWarehouse = myRandom48(g_nWarehouseCount);
+ NdbConnection* pNdbConnection = NULL ;
+ VerifyMethodPtr(pNdbConnection, pNdb, startTransaction());
+ CHK_TR(pNdbConnection) ; // epaulsa
+ NdbOperation* pNdbOperationW = NULL ;
+ VerifyMethodPtr(pNdbOperationW, pNdbConnection, getNdbOperation(c_szWarehouse));
+ VerifyMethodInt(pNdbOperationW, deleteTuple());
+ VerifyMethodInt(pNdbOperationW, equal(c_szWarehouseNumber, nWarehouse));
+ for(Uint32 nDistrict=0; nDistrict<g_nDistrictPerWarehouse; ++nDistrict) {
+ NdbOperation* pNdbOperationD = NULL ;
+ VerifyMethodPtr(pNdbOperationD, pNdbConnection, getNdbOperation(c_szDistrict));
+ VerifyMethodInt(pNdbOperationD, deleteTuple());
+ VerifyMethodInt(pNdbOperationD, equal(c_szDistrictWarehouseNumber, nWarehouse));
+ VerifyMethodInt(pNdbOperationD, equal(c_szDistrictNumber, nDistrict));
+ }
+ int iExec = pNdbConnection->execute(Commit);
+ int iError = pNdbConnection->getNdbError().code;
+
+ if(iExec<0 && iError!=0 && iError!=266 && iError!=626) {
+ ReportMethodInt(iExec, pNdbConnection, "pNdbConnection", "execute(Commit)", __FILE__, __LINE__);
+ }
+ if(iExec==0) {
+ ++nSucc;
+ } else {
+ ++nFail;
+ }
+ VerifyMethodVoid(pNdb, closeTransaction(pNdbConnection));
+ }
+ ndbout << "delete: " << nSucc << " succeeded, " << nFail << " failed " << endl;
+ NdbMutex_Unlock(g_pNdbMutex);
+ delete pNdb;
+ pNdb = NULL ;
+ return NULL;
+}
+
+
+extern "C" void* NdbThreadFuncRead(void* pArg)
+{
+ myRandom48Init((long int)NdbTick_CurrentMillisecond());
+ unsigned nSucc = 0;
+ unsigned nFail = 0;
+ NdbRecAttr** ppNdbRecAttrDSum = new NdbRecAttr*[g_nDistrictPerWarehouse];
+ NdbRecAttr** ppNdbRecAttrDCnt = new NdbRecAttr*[g_nDistrictPerWarehouse];
+ Ndb* pNdb = NULL ;
+ pNdb = new Ndb("TEST_DB");
+ VerifyMethodInt(pNdb, init());
+ VerifyMethodInt(pNdb, waitUntilReady());
+
+ while(NdbMutex_Trylock(g_pNdbMutex)) {
+ Uint32 nWarehouse = myRandom48(g_nWarehouseCount);
+ NdbConnection* pNdbConnection = NULL ;
+ VerifyMethodPtr(pNdbConnection, pNdb, startTransaction());
+ CHK_TR(pNdbConnection) ; // epaulsa
+ NdbOperation* pNdbOperationW = NULL ;
+ VerifyMethodPtr(pNdbOperationW, pNdbConnection, getNdbOperation(c_szWarehouse));
+ VerifyMethodInt(pNdbOperationW, readTuple());
+ VerifyMethodInt(pNdbOperationW, equal(c_szWarehouseNumber, nWarehouse));
+ NdbRecAttr* pNdbRecAttrWSum;
+ VerifyMethodPtr(pNdbRecAttrWSum, pNdbOperationW, getValue(c_szWarehouseSum, 0));
+ NdbRecAttr* pNdbRecAttrWCnt;
+ VerifyMethodPtr(pNdbRecAttrWCnt, pNdbOperationW, getValue(c_szWarehouseCount, 0));
+ for(Uint32 nDistrict=0; nDistrict<g_nDistrictPerWarehouse; ++nDistrict) {
+ NdbOperation* pNdbOperationD = NULL ;
+ VerifyMethodPtr(pNdbOperationD, pNdbConnection, getNdbOperation(c_szDistrict));
+ VerifyMethodInt(pNdbOperationD, readTuple());
+ VerifyMethodInt(pNdbOperationD, equal(c_szDistrictWarehouseNumber, nWarehouse));
+ VerifyMethodInt(pNdbOperationD, equal(c_szDistrictNumber, nDistrict));
+ VerifyMethodPtr(ppNdbRecAttrDSum[nDistrict], pNdbOperationD, getValue(c_szDistrictSum, 0));
+ VerifyMethodPtr(ppNdbRecAttrDCnt[nDistrict], pNdbOperationD, getValue(c_szDistrictCount, 0));
+ }
+ int iExec = pNdbConnection->execute(Commit);
+ int iError = pNdbConnection->getNdbError().code;
+
+ if(iExec<0 && iError!=0 && iError!=266 && iError!=626) {
+ ReportMethodInt(iExec, pNdbConnection, "pNdbConnection", "execute(Commit)", __FILE__, __LINE__);
+ }
+ if(iExec==0) {
+ Uint32 nSum = 0;
+ Uint32 nCnt = 0;
+ for(Uint32 nDistrict=0; nDistrict<g_nDistrictPerWarehouse; ++nDistrict) {
+ nSum += ppNdbRecAttrDSum[nDistrict]->u_32_value();
+ nCnt += ppNdbRecAttrDCnt[nDistrict]->u_32_value();
+ }
+ if(nSum!=pNdbRecAttrWSum->u_32_value()
+ || nCnt!=g_nDistrictPerWarehouse*pNdbRecAttrWCnt->u_32_value()) {
+ ndbout << "INCONSISTENT!" << endl;
+ ndbout << "iExec==" << iExec << endl;
+ ndbout << "iError==" << iError << endl;
+ ndbout << endl;
+ ndbout << c_szWarehouseSum << "==" << pNdbRecAttrWSum->u_32_value() << ", ";
+ ndbout << c_szWarehouseCount << "==" << pNdbRecAttrWCnt->u_32_value() << endl;
+ ndbout << "nSum==" << nSum << ", nCnt=" << nCnt << endl;
+ for(Uint32 nDistrict=0; nDistrict<g_nDistrictPerWarehouse; ++nDistrict) {
+ ndbout << c_szDistrictSum << "[" << nDistrict << "]==" << ppNdbRecAttrDSum[nDistrict]->u_32_value() << ", ";
+ ndbout << c_szDistrictCount << "[" << nDistrict << "]==" << ppNdbRecAttrDCnt[nDistrict]->u_32_value() << endl;
+ }
+ VerifyMethodVoid(pNdb, closeTransaction(pNdbConnection));
+ delete pNdb; pNdb = NULL ;
+ delete[] ppNdbRecAttrDSum; ppNdbRecAttrDSum = NULL ;
+ delete[] ppNdbRecAttrDCnt; ppNdbRecAttrDCnt = NULL ;
+ NDBT_ProgramExit(NDBT_FAILED);
+ }
+ ++nSucc;
+ } else {
+ ++nFail;
+ }
+ VerifyMethodVoid(pNdb, closeTransaction(pNdbConnection));
+ }
+ ndbout << "read: " << nSucc << " succeeded, " << nFail << " failed " << endl;
+ NdbMutex_Unlock(g_pNdbMutex);
+ delete pNdb; pNdb = NULL ;
+ delete[] ppNdbRecAttrDSum; ppNdbRecAttrDSum = NULL ;
+ delete[] ppNdbRecAttrDCnt; ppNdbRecAttrDCnt = NULL ;
+ return NULL;
+}
+
+
+NDB_COMMAND(acid, "acid", "acid", "acid", 65535)
+{
+ ndb_init();
+ long nSeconds = 60;
+ int rc = NDBT_OK;
+
+ for(int i=1; i<argc; ++i) {
+ if(argv[i][0]=='-' || argv[i][0]=='/') {
+ switch(argv[i][1]) {
+ case 'w': g_nWarehouseCount=atol(argv[i]+2); break;
+ case 'd': g_nDistrictPerWarehouse=atol(argv[i]+2); break;
+ case 's': nSeconds=atol(argv[i]+2); break;
+ case 't': g_nThreadCount=atol(argv[i]+2); break;
+ default: ndbout << "invalid option" << endl; return 1;
+ }
+ } else {
+ ndbout << "invalid operand" << endl;
+ return 1;
+ }
+ }
+ ndbout << argv[0];
+ ndbout << " -w" << g_nWarehouseCount;
+ ndbout << " -d" << g_nDistrictPerWarehouse;
+ ndbout << " -s" << (int)nSeconds;
+ ndbout << " -t" << g_nThreadCount;
+ ndbout << endl;
+
+ Ndb* pNdb = NULL ;
+ pNdb = new Ndb("TEST_DB");
+ VerifyMethodInt(pNdb, init());
+ VerifyMethodInt(pNdb, waitUntilReady());
+
+ NdbSchemaCon* pNdbSchemaCon= NdbSchemaCon::startSchemaTrans(pNdb);
+ if(!pNdbSchemaCon){
+ ndbout <<"startSchemaTransaction failed, exiting now" << endl ;
+ delete pNdb ;
+ NDBT_ProgramExit(NDBT_FAILED) ;
+ }
+ NdbSchemaOp* pNdbSchemaOp = NULL ;
+ VerifyMethodPtr(pNdbSchemaOp, pNdbSchemaCon, getNdbSchemaOp());
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ VerifyMethodInt(pNdbSchemaOp, createTable(
+ c_szWarehouse,
+ (4+4+4+12)*1.02*g_nWarehouseCount/1024+1,
+ TupleKey,
+ (4+14)*g_nWarehouseCount/8/1024+1,
+ All,
+ 6,
+ 78,
+ 80,
+ 1,
+ false));
+#else
+ VerifyMethodInt(pNdbSchemaOp, createTable(
+ c_szWarehouse,
+ (4+4+4+12)*1.02*g_nWarehouseCount/1024+1,
+ TupleKey,
+ (4+14)*g_nWarehouseCount/8/1024+1));
+#endif
+ VerifyMethodInt(pNdbSchemaOp, createAttribute(c_szWarehouseNumber, TupleKey, 32, 1, UnSigned, MMBased, false));
+ VerifyMethodInt(pNdbSchemaOp, createAttribute(c_szWarehouseSum, NoKey, 32, 1, UnSigned, MMBased, false));
+ VerifyMethodInt(pNdbSchemaOp, createAttribute(c_szWarehouseCount, NoKey, 32, 1, UnSigned, MMBased, false));
+ VerifyMethodInt(pNdbSchemaCon, execute());
+ NdbSchemaCon::closeSchemaTrans(pNdbSchemaCon);
+
+ pNdbSchemaCon= NdbSchemaCon::startSchemaTrans(pNdb);
+ VerifyMethodPtr(pNdbSchemaOp, pNdbSchemaCon, getNdbSchemaOp());
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ VerifyMethodInt(pNdbSchemaOp, createTable(
+ c_szDistrict,
+ (4+4+4+4+12)*1.02*g_nWarehouseCount*g_nDistrictPerWarehouse/1024+1,
+ TupleKey,
+ (4+4+14)*g_nWarehouseCount*g_nDistrictPerWarehouse/8/1024+1,
+ All,
+ 6,
+ 78,
+ 80,
+ 1,
+ false));
+#else
+ VerifyMethodInt(pNdbSchemaOp, createTable(
+ c_szDistrict,
+ (4+4+4+4+12)*1.02*g_nWarehouseCount*g_nDistrictPerWarehouse/1024+1,
+ TupleKey,
+ (4+4+14)*g_nWarehouseCount*g_nDistrictPerWarehouse/8/1024+1));
+
+#endif
+ VerifyMethodInt(pNdbSchemaOp, createAttribute(c_szDistrictWarehouseNumber, TupleKey, 32, 1, UnSigned, MMBased, false));
+ VerifyMethodInt(pNdbSchemaOp, createAttribute(c_szDistrictNumber, TupleKey, 32, 1, UnSigned, MMBased, false));
+ VerifyMethodInt(pNdbSchemaOp, createAttribute(c_szDistrictSum, NoKey, 32, 1, UnSigned, MMBased, false));
+ VerifyMethodInt(pNdbSchemaOp, createAttribute(c_szDistrictCount, NoKey, 32, 1, UnSigned, MMBased, false));
+ VerifyMethodInt(pNdbSchemaCon, execute());
+ NdbSchemaCon::closeSchemaTrans(pNdbSchemaCon);
+ g_pNdbMutex = NdbMutex_Create();
+ NdbMutex_Lock(g_pNdbMutex);
+
+ NdbThread** ppNdbThread = new NdbThread*[g_nThreadCount*4];
+ for(Uint32 nThread=0; nThread<g_nThreadCount; ++nThread) {
+ ppNdbThread[nThread*4+0] = NdbThread_Create(NdbThreadFuncInsert, 0, 65535, "insert",
+ NDB_THREAD_PRIO_LOW);
+ ppNdbThread[nThread*4+1] = NdbThread_Create(NdbThreadFuncUpdate, 0, 65535, "update",
+ NDB_THREAD_PRIO_LOW);
+ ppNdbThread[nThread*4+2] = NdbThread_Create(NdbThreadFuncDelete, 0, 65535, "delete",
+ NDB_THREAD_PRIO_LOW);
+ ppNdbThread[nThread*4+3] = NdbThread_Create(NdbThreadFuncRead, 0, 65535, "read",
+ NDB_THREAD_PRIO_LOW);
+ }
+
+ NdbSleep_SecSleep(nSeconds);
+ NdbMutex_Unlock(g_pNdbMutex);
+
+ void* pStatus;
+ for(Uint32 nThread=0; nThread<g_nThreadCount; ++nThread) {
+ NdbThread_WaitFor(ppNdbThread[nThread*4+0], &pStatus);
+ NdbThread_WaitFor(ppNdbThread[nThread*4+1], &pStatus);
+ NdbThread_WaitFor(ppNdbThread[nThread*4+2], &pStatus);
+ NdbThread_WaitFor(ppNdbThread[nThread*4+3], &pStatus);
+ }
+
+ NdbMutex_Destroy(g_pNdbMutex);
+ delete[] ppNdbThread;
+ delete pNdb;
+ return NDBT_ProgramExit(rc);
+}
+
diff --git a/storage/ndb/test/ndbapi/acid2.cpp b/storage/ndb/test/ndbapi/acid2.cpp
new file mode 100644
index 00000000000..7bd7ec00ac5
--- /dev/null
+++ b/storage/ndb/test/ndbapi/acid2.cpp
@@ -0,0 +1,693 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <NdbOut.hpp>
+#include <NdbThread.h>
+#include <NdbSleep.h>
+#include <NdbMutex.h>
+
+#include "TraceNdbApi.hpp"
+#include "VerifyNdbApi.hpp"
+
+
+#define Ndb CTraceNdb
+#define NdbSchemaCon CTraceNdbSchemaCon
+#define NdbSchemaOp CTraceNdbSchemaOp
+#define NdbConnection CTraceNdbConnection
+#define NdbOperation CTraceNdbOperation
+#define NdbIndexOperation CTraceNdbIndexOperation
+#define NdbRecAttr CTraceNdbRecAttr
+#define Table CTraceTable
+#define Index CTraceIndex
+#define Column CTraceColumn
+#define NdbDictionary CTraceNdbDictionary
+
+/*
+#define Ndb CVerifyNdb
+#define NdbSchemaCon CVerifyNdbSchemaCon
+#define NdbSchemaOp CVerifyNdbSchemaOp
+#define NdbConnection CVerifyNdbConnection
+#define NdbOperation CVerifyNdbOperation
+#define NdbIndexOperation CVerifyNdbIndexOperation
+#define NdbRecAttr CVerifyNdbRecAttr
+#define Table CVerifyTable
+#define Index CVerifyIndex
+#define Column CVerifyColumn
+#define NdbDictionary CVerifyNdbDictionary
+*/
+
+NdbMutex* g_pNdbMutexStop = 0;
+Uint32 g_nPart = 1;
+Uint32 g_nTable = 1;
+Uint32 g_nTuple = 1;
+Uint32 g_nAttribute = 1;
+char* g_szTable = 0;
+char* g_szIndex = 0;
+char* g_szAttribute = 0;
+bool g_bVerify = false;
+bool g_bUseIndex = false;
+
+
+
+#define N 624
+#define M 397
+#define MATRIX_A 0x9908b0df
+#define UPPER_MASK 0x80000000
+#define LOWER_MASK 0x7fffffff
+
+#define TEMPERING_MASK_B 0x9d2c5680
+#define TEMPERING_MASK_C 0xefc60000
+#define TEMPERING_SHIFT_U(y) (y >> 11)
+#define TEMPERING_SHIFT_S(y) (y << 7)
+#define TEMPERING_SHIFT_T(y) (y << 15)
+#define TEMPERING_SHIFT_L(y) (y >> 18)
+
+
+class MT19937
+{
+public:
+ MT19937(void);
+ void sgenrand(unsigned long seed);
+ unsigned long genrand(void);
+
+private:
+ unsigned long mt[N];
+ int mti;
+ unsigned long mag01[2];
+};
+
+
+MT19937::MT19937(void)
+{
+ mti = N+1;
+ mag01[0] = 0x0;
+ mag01[1] = MATRIX_A;
+ sgenrand(4357);
+}
+
+
+void MT19937::sgenrand(unsigned long seed)
+{
+ mt[0]= seed & 0xffffffff;
+ for (mti=1; mti<N; mti++)
+ mt[mti] = (69069 * mt[mti-1]) & 0xffffffff;
+}
+
+
+unsigned long MT19937::genrand(void)
+{
+ unsigned long y;
+ if (mti >= N) {
+ int kk;
+ if (mti == N+1)
+ {
+ sgenrand(4357);
+ }
+ for (kk=0;kk<N-M;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
+ }
+ for (;kk<N-1;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
+ }
+ y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
+ mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
+ mti = 0;
+ }
+ y = mt[mti++];
+ y ^= TEMPERING_SHIFT_U(y);
+ y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
+ y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
+ y ^= TEMPERING_SHIFT_L(y);
+ return y;
+}
+
+
+
+
+
+void CreateTables(Ndb* pNdb)
+{
+ for(Uint32 iTable=0; iTable<g_nTable; ++iTable)
+ {
+ NdbDictionary::Dictionary* pDictionary = pNdb->getDictionary();
+
+ NdbDictionary::Table table;
+ table.setName(g_szTable+iTable*4);
+
+ NdbDictionary::Index index;
+ index.setName(g_szIndex+iTable*4);
+ index.setTable(table.getName());
+ index.setType(NdbDictionary::Index::UniqueHashIndex);
+
+ NdbDictionary::Column columnPK;
+ columnPK.setName("PK");
+ columnPK.setTupleKey(true);
+ table.addColumn(columnPK);
+ index.addIndexColumn(columnPK.getName());
+
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ NdbDictionary::Column columnAttr;
+ columnAttr.setName(g_szAttribute+iAttr*4);
+ columnAttr.setTupleKey(false);
+ table.addColumn(columnAttr);
+ }
+
+ pDictionary->createTable(table);
+ pDictionary->createIndex(index);
+
+ /*
+ NdbSchemaCon* pNdbSchemaCon = pNdb->startSchemaTransaction();
+ NdbSchemaOp* pNdbSchemaOp = pNdbSchemaCon->getNdbSchemaOp();
+ pNdbSchemaOp->createTable(g_szTable+iTable*4);
+ pNdbSchemaOp->createAttribute("PK", TupleKey);
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ pNdbSchemaOp->createAttribute(g_szAttribute+iAttr*4, NoKey);
+ }
+
+ pNdbSchemaCon->execute();
+ pNdb->closeSchemaTransaction(pNdbSchemaCon);
+ */
+ }
+}
+
+
+int InsertTransaction(Ndb* pNdb, const Uint32 iPart, const bool bIndex)
+{
+ int iExec = -1;
+ int iCode = -1;
+ NdbConnection* pNdbConnection = pNdb->startTransaction();
+ if(pNdbConnection)
+ {
+ for(Uint32 iTable=0; iTable<g_nTable; ++iTable)
+ {
+ for(Uint32 iTuple=0; iTuple<g_nTuple; ++iTuple)
+ {
+ if(bIndex)
+ {
+ NdbIndexOperation* pNdbIndexOperation = pNdbConnection->getNdbIndexOperation(g_szIndex+iTable*4, g_szTable+iTable*4);
+ pNdbIndexOperation->insertTuple();
+ Uint32 nPK = iPart*g_nTuple + iTuple;
+ pNdbIndexOperation->equal("PK", nPK);
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ Uint32 nValue = ((iPart*g_nTable+iTable)*g_nTuple+iTuple)*g_nAttribute+iAttr;
+ pNdbIndexOperation->setValue(g_szAttribute+iAttr*4, nValue);
+ }
+ }
+ else
+ {
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbOperation(g_szTable+iTable*4);
+ pNdbOperation->insertTuple();
+ Uint32 nPK = iPart*g_nTuple + iTuple;
+ pNdbOperation->equal("PK", nPK);
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ Uint32 nValue = ((iPart*g_nTable+iTable)*g_nTuple+iTuple)*g_nAttribute+iAttr;
+ pNdbOperation->setValue(g_szAttribute+iAttr*4, nValue);
+ }
+ }
+ }
+ }
+ iExec = pNdbConnection->execute_ok(Commit);
+ if (iExec == -1)
+ {
+ ndbout << pNdbConnection->getNdbError() << endl;
+ }
+ pNdb->closeTransaction(pNdbConnection);
+ }
+ return 0;
+}
+
+
+int UpdateGetAndSetTransaction(Ndb* pNdb, const Uint32 iPart, const bool bIndex)
+{
+ int iExec = -1;
+ int iCode = -1;
+ NdbRecAttr** ppNdbRecAttr = new NdbRecAttr*[g_nTable*g_nTuple*g_nAttribute];
+ NdbConnection* pNdbConnection = pNdb->startTransaction();
+ if(pNdbConnection)
+ {
+ for(Uint32 iTable=0; iTable<g_nTable; ++iTable)
+ {
+ for(Uint32 iTuple=0; iTuple<g_nTuple; ++iTuple)
+ {
+ if(bIndex)
+ {
+ NdbIndexOperation* pNdbIndexOperation = pNdbConnection->getNdbIndexOperation(g_szIndex+iTable*4, g_szTable+iTable*4);
+ pNdbIndexOperation->readTupleExclusive();
+ Uint32 nPK = iPart*g_nTuple + iTuple;
+ pNdbIndexOperation->equal("PK", nPK);
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ ppNdbRecAttr[(iTable*g_nTuple+iTuple)*g_nAttribute+iAttr]
+ = pNdbIndexOperation->getValue(g_szAttribute+iAttr*4);
+ }
+ }
+ else
+ {
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbOperation(g_szTable+iTable*4);
+ pNdbOperation->readTupleExclusive();
+ Uint32 nPK = iPart*g_nTuple + iTuple;
+ pNdbOperation->equal("PK", nPK);
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ ppNdbRecAttr[(iTable*g_nTuple+iTuple)*g_nAttribute+iAttr]
+ = pNdbOperation->getValue(g_szAttribute+iAttr*4);
+ }
+ }
+ }
+ }
+ iExec = pNdbConnection->execute_ok(NoCommit);
+ if( iExec == -1)
+ {
+ ndbout << pNdbConnection->getNdbError() << endl;
+ }
+ }
+ iCode = pNdbConnection->getNdbError().code;
+ if(iExec==0)
+ {
+ for(Uint32 iTable=0; iTable<g_nTable; ++iTable)
+ {
+ for(Uint32 iTuple=0; iTuple<g_nTuple; ++iTuple)
+ {
+ if(bIndex)
+ {
+ NdbIndexOperation* pNdbIndexOperation = pNdbConnection->getNdbIndexOperation(g_szIndex+iTable*4, g_szTable+iTable*4);
+ pNdbIndexOperation->updateTuple();
+ Uint32 nPK = iPart*g_nTuple + iTuple;
+ pNdbIndexOperation->equal("PK", nPK);
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ NdbRecAttr* pNdbRecAttr
+ = ppNdbRecAttr[(iTable*g_nTuple+iTuple)*g_nAttribute+iAttr];
+ Uint32 nValue = pNdbRecAttr->u_32_value() + 1;
+ pNdbIndexOperation->setValue(g_szAttribute+iAttr*4, nValue);
+ }
+ }
+ else
+ {
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbOperation(g_szTable+iTable*4);
+ pNdbOperation->updateTuple();
+ Uint32 nPK = iPart*g_nTuple + iTuple;
+ pNdbOperation->equal("PK", nPK);
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ NdbRecAttr* pNdbRecAttr
+ = ppNdbRecAttr[(iTable*g_nTuple+iTuple)*g_nAttribute+iAttr];
+ Uint32 nValue = pNdbRecAttr->u_32_value() + 1;
+ pNdbOperation->setValue(g_szAttribute+iAttr*4, nValue);
+ }
+ }
+ }
+ }
+ iExec = pNdbConnection->execute(Commit);
+ if (iExec == -1)
+ {
+ ndbout << pNdbConnection->getNdbError() << endl;
+ }
+ pNdb->closeTransaction(pNdbConnection);
+ }
+ delete[] ppNdbRecAttr;
+ return 0;
+}
+
+
+int UpdateInterpretedTransaction(Ndb* pNdb, const Uint32 iPart, const bool bIndex)
+{
+ int iExec = -1;
+ int iCode = -1;
+ NdbConnection* pNdbConnection = pNdb->startTransaction();
+ if(pNdbConnection)
+ {
+ for(Uint32 iTable=0; iTable<g_nTable; ++iTable)
+ {
+ for(Uint32 iTuple=0; iTuple<g_nTuple; ++iTuple)
+ {
+ if(bIndex)
+ {
+ NdbIndexOperation* pNdbIndexOperation = pNdbConnection->getNdbIndexOperation(g_szIndex+iTable*4, g_szTable+iTable*4);
+ pNdbIndexOperation->interpretedUpdateTuple();
+ Uint32 nPK = iPart*g_nTuple + iTuple;
+ pNdbIndexOperation->equal("PK", nPK);
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ pNdbIndexOperation->incValue(g_szAttribute+iAttr*4, (Uint32)1);
+ }
+ }
+ else
+ {
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbOperation(g_szTable+iTable*4);
+ pNdbOperation->interpretedUpdateTuple();
+ Uint32 nPK = iPart*g_nTuple + iTuple;
+ pNdbOperation->equal("PK", nPK);
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ pNdbOperation->incValue(g_szAttribute+iAttr*4, (Uint32)1);
+ }
+ }
+ }
+ }
+ iExec = pNdbConnection->execute_ok(Commit);
+
+ if (iExec == -1)
+ {
+ ndbout << pNdbConnection->getNdbError() << endl;
+ }
+ pNdb->closeTransaction(pNdbConnection);
+ }
+ return 0;
+}
+
+
+void ReportInconsistency (const Uint32 iPart,
+ const Uint32 iTable,
+ const Uint32 iTuple,
+ const Uint32 iAttr,
+ const Uint32 nValue,
+ const Uint32 nExpected )
+{
+ ndbout << "INCONSISTENCY: ";
+ ndbout << "Part " << iPart;
+ ndbout << ", Table " << iTable;
+ ndbout << ", Tuple " << iTuple;
+ ndbout << ", Attr " << iAttr;
+ ndbout << ", Value " << nValue;
+ ndbout << ", Expected " << nExpected;
+ ndbout << endl;
+}
+
+
+int ReadTransaction(Ndb* pNdb, const Uint32 iPart, const bool bIndex)
+{
+ int iExec = -1;
+ int iCode = -1;
+ NdbRecAttr** ppNdbRecAttr = new NdbRecAttr*[g_nTable*g_nTuple*g_nAttribute];
+ NdbConnection* pNdbConnection = pNdb->startTransaction();
+ if(pNdbConnection)
+ {
+ for(Uint32 iTable=0; iTable<g_nTable; ++iTable)
+ {
+ for(Uint32 iTuple=0; iTuple<g_nTuple; ++iTuple)
+ {
+ if(bIndex)
+ {
+ NdbIndexOperation* pNdbIndexOperation = pNdbConnection->getNdbIndexOperation(g_szIndex+iTable*4, g_szTable+iTable*4);
+ pNdbIndexOperation->readTuple();
+ Uint32 nPK = iPart*g_nTuple + iTuple;
+ pNdbIndexOperation->equal("PK", nPK);
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ ppNdbRecAttr[(iTable*g_nTuple+iTuple)*g_nAttribute+iAttr]
+ = pNdbIndexOperation->getValue(g_szAttribute+iAttr*4);
+ }
+ }
+ else
+ {
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbOperation(g_szTable+iTable*4);
+ pNdbOperation->readTuple();
+ Uint32 nPK = iPart*g_nTuple + iTuple;
+ pNdbOperation->equal("PK", nPK);
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ ppNdbRecAttr[(iTable*g_nTuple+iTuple)*g_nAttribute+iAttr]
+ = pNdbOperation->getValue(g_szAttribute+iAttr*4);
+ }
+ }
+ }
+ }
+ iExec = pNdbConnection->execute_ok(Commit);
+ if (iExec == -1)
+ {
+ ndbout << pNdbConnection->getNdbError() << endl;
+ }
+ if(iExec==0)
+ {
+ Uint32 nValue0 = ppNdbRecAttr[0]->u_32_value();
+ for(Uint32 iTable=0; iTable<g_nTable; ++iTable)
+ {
+ for(Uint32 iTuple=0; iTuple<g_nTuple; ++iTuple)
+ {
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ Uint32 nValue = ppNdbRecAttr[(iTable*g_nTuple+iTuple)*g_nAttribute+iAttr]->u_32_value();
+ Uint32 nExpected = nValue0 + (iTable*g_nTuple+iTuple)*g_nAttribute+iAttr;
+ if(nValue!=nExpected)
+ {
+ ReportInconsistency(iPart, iTable, iTuple, iAttr, nValue, nExpected);
+ }
+ }
+ }
+ }
+ }
+ pNdb->closeTransaction(pNdbConnection);
+ }
+ return 0;
+}
+
+
+int DeleteTransaction(Ndb* pNdb, const Uint32 iPart, const bool bIndex)
+{
+ int iExec = -1;
+ int iCode = -1;
+ NdbConnection* pNdbConnection = pNdb->startTransaction();
+ if(pNdbConnection)
+ {
+ for(Uint32 iTable=0; iTable<g_nTable; ++iTable)
+ {
+ for(Uint32 iTuple=0; iTuple<g_nTuple; ++iTuple)
+ {
+ if(bIndex)
+ {
+ NdbIndexOperation* pNdbIndexOperation = pNdbConnection->getNdbIndexOperation(g_szIndex+iTable*4, g_szTable+iTable*4);
+ pNdbIndexOperation->deleteTuple();
+ Uint32 nPK = iPart*g_nTuple + iTuple;
+ pNdbIndexOperation->equal("PK", nPK);
+ }
+ else
+ {
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbOperation(g_szTable+iTable*4);
+ pNdbOperation->deleteTuple();
+ Uint32 nPK = iPart*g_nTuple + iTuple;
+ pNdbOperation->equal("PK", nPK);
+ }
+ }
+ }
+ iExec = pNdbConnection->execute_ok(Commit);
+
+ if (iExec == -1)
+ {
+ ndbout << pNdbConnection->getNdbError() << endl;
+ }
+ pNdb->closeTransaction(pNdbConnection);
+ }
+ return 0;
+}
+
+
+extern "C" void* ThreadFunc(void*)
+{
+ Ndb* pNdb = new Ndb("TEST_DB");
+ pNdb->init();
+ pNdb->waitUntilReady();
+
+ MT19937 rndgen;
+ rndgen.sgenrand((unsigned long)pNdb);
+
+ Uint32 nInsertError = 0;
+ Uint32 nInsertCommit = 0;
+ Uint32 nInsertRollback = 0;
+ Uint32 nUpdateGetAndSetError = 0;
+ Uint32 nUpdateGetAndSetCommit = 0;
+ Uint32 nUpdateGetAndSetRollback = 0;
+ Uint32 nReadError = 0;
+ Uint32 nReadCommit = 0;
+ Uint32 nReadRollback = 0;
+ Uint32 nUpdateInterpretedError = 0;
+ Uint32 nUpdateInterpretedCommit = 0;
+ Uint32 nUpdateInterpretedRollback = 0;
+ Uint32 nDeleteError = 0;
+ Uint32 nDeleteCommit = 0;
+ Uint32 nDeleteRollback = 0;
+
+ if (g_bVerify)
+ {
+ for (Uint32 iPart = 0; iPart < g_nPart; iPart++)
+ {
+ switch(ReadTransaction(pNdb, iPart, false))
+ {
+ case -1: ++nReadError; break;
+ case 0: ++nReadCommit; break;
+ case 1: ++nReadRollback; break;
+ }
+ }
+ }
+ else
+ while(NdbMutex_Trylock(g_pNdbMutexStop))
+ {
+ Uint32 iPart = rndgen.genrand() % g_nPart;
+ Uint32 iTrans = rndgen.genrand() % 5;
+ bool bIndex = ((rndgen.genrand() & 1) ? true : false);
+ switch(iTrans)
+ {
+ case 0:
+ switch(InsertTransaction(pNdb, iPart, bIndex))
+ {
+ case -1: ++nInsertError; break;
+ case 0: ++nInsertCommit; break;
+ case 1: ++nInsertRollback; break;
+ }
+ break;
+
+ case 1:
+ switch(UpdateGetAndSetTransaction(pNdb, iPart, bIndex))
+ {
+ case -1: ++nUpdateGetAndSetError; break;
+ case 0: ++nUpdateGetAndSetCommit; break;
+ case 1: ++nUpdateGetAndSetRollback; break;
+ }
+ break;
+
+ case 2:
+ switch(ReadTransaction(pNdb, iPart, bIndex))
+ {
+ case -1: ++nReadError; break;
+ case 0: ++nReadCommit; break;
+ case 1: ++nReadRollback; break;
+ }
+ break;
+
+ case 3:
+ switch(UpdateInterpretedTransaction(pNdb, iPart, bIndex))
+ {
+ case -1: ++nUpdateInterpretedError; break;
+ case 0: ++nUpdateInterpretedCommit; break;
+ case 1: ++nUpdateInterpretedRollback; break;
+ }
+ break;
+
+ case 4:
+ switch(DeleteTransaction(pNdb, iPart, bIndex))
+ {
+ case -1: ++nDeleteError; break;
+ case 0: ++nDeleteCommit; break;
+ case 1: ++nDeleteRollback; break;
+ }
+ break;
+ }
+ }
+
+ ndbout << "I:" << nInsertError << ":" << nInsertCommit << ":" << nInsertRollback;
+ ndbout << " UG:" << nUpdateGetAndSetError << ":" << nUpdateGetAndSetCommit << ":" << nUpdateGetAndSetRollback;
+ ndbout << " R:" << nReadError << ":" << nReadCommit << ":" << nReadRollback;
+ ndbout << " UI:" << nUpdateInterpretedError << ":" << nUpdateInterpretedCommit << ":" << nUpdateInterpretedRollback;
+ ndbout << " D:" << nDeleteError << ":" << nDeleteCommit << ":" << nDeleteRollback << endl;
+ ndbout << endl;
+
+ NdbMutex_Unlock(g_pNdbMutexStop);
+ delete pNdb;
+ return 0;
+}
+
+
+int main(int argc, char* argv[])
+{
+ ndb_init();
+ Uint32 nSeconds = 1;
+ Uint32 nThread = 1;
+
+ for(int iArg=1; iArg<argc; ++iArg)
+ {
+ if(argv[iArg][0]=='-')
+ {
+ switch(argv[iArg][1])
+ {
+ case 'p': g_nPart = atol(argv[iArg]+2); break;
+ case 'b': g_nTable = atol(argv[iArg]+2); break;
+ case 'u': g_nTuple = atol(argv[iArg]+2); break;
+ case 'a': g_nAttribute = atol(argv[iArg]+2); break;
+ case 'v': g_bVerify = true; break;
+ case 't': nThread = atol(argv[iArg]+2); break;
+ case 's': nSeconds = atol(argv[iArg]+2); break;
+ case 'i': g_bUseIndex = true; break;
+ }
+ }
+ }
+ ndbout << argv[0];
+ ndbout << " -p" << g_nPart;
+ ndbout << " -b" << g_nTable;
+ ndbout << " -u" << g_nTuple;
+ ndbout << " -a" << g_nAttribute;
+ if (g_bVerify)
+ ndbout << " -v";
+ ndbout << " -t" << nThread;
+ ndbout << " -s" << nSeconds;
+ ndbout << endl;
+
+ g_szTable = new char[g_nTable*4];
+ for(Uint32 iTable=0; iTable<g_nTable; ++iTable)
+ {
+ sprintf(g_szTable+iTable*4, "T%02d", iTable);
+ }
+
+ g_szIndex = new char[g_nTable*4];
+ for(Uint32 iIndex=0; iIndex<g_nTable; ++iIndex)
+ {
+ sprintf(g_szIndex+iIndex*4, "I%02d", iIndex);
+ }
+
+ g_szAttribute = new char[g_nAttribute*4];
+ for(Uint32 iAttr=0; iAttr<g_nAttribute; ++iAttr)
+ {
+ sprintf(g_szAttribute+iAttr*4, "A%02d", iAttr);
+ }
+
+
+ Ndb* pNdb = new Ndb("TEST_DB");
+ pNdb->init();
+ pNdb->waitUntilReady();
+
+ if (!g_bVerify) CreateTables(pNdb);
+ g_pNdbMutexStop = NdbMutex_Create();
+ NdbMutex_Lock(g_pNdbMutexStop);
+
+ NdbThread_SetConcurrencyLevel(nThread+1);
+ NdbThread** ppNdbThread = new NdbThread*[nThread];
+ for(Uint32 iThread=0; iThread<nThread; ++iThread)
+ {
+ ppNdbThread[iThread] = NdbThread_Create(ThreadFunc, 0, 0, "ThreadFunc", NDB_THREAD_PRIO_MEAN);
+ }
+
+ NdbSleep_SecSleep(nSeconds);
+ NdbMutex_Unlock(g_pNdbMutexStop);
+
+ for(Uint32 iThread=0; iThread<nThread; ++iThread)
+ {
+ void* status;
+ NdbThread_WaitFor(ppNdbThread[iThread], &status);
+ }
+
+ NdbMutex_Destroy(g_pNdbMutexStop);
+ g_pNdbMutexStop = 0;
+ delete pNdb;
+}
+
+
diff --git a/storage/ndb/test/ndbapi/adoInsertRecs.cpp b/storage/ndb/test/ndbapi/adoInsertRecs.cpp
new file mode 100644
index 00000000000..0bc67ef641b
--- /dev/null
+++ b/storage/ndb/test/ndbapi/adoInsertRecs.cpp
@@ -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 */
+
+// InsertRecs.cpp : Defines the entry point for the console application.
+//
+
+
+#include "stdafx.h"
+#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
+ no_namespace rename("EOF", "EndOfFile")
+
+
+// data for CALL_CONTEXT and GROUP_RESOURCE
+static TCHAR STATUS_DATA[]=_T("000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F")
+ _T("101112131415161718191A1B1C1D1E1F000102030405060708090A0B0C0D0E0F")
+ _T("202122232425262728292A2B2C2D2E2F000102030405060708090A0B0C0D0E0F")
+ _T("303132333435363738393A3B3C3D3E3F000102030405060708090A0B0C0D0E0F")
+ _T("404142434445464748494A4B4C4D4E4F000102030405060708090A0B0C0D0E0F")
+ _T("505152535455565758595A5B5C5D5E5F000102030405060708090A0B0C0D0E0F")
+ _T("606162636465666768696A6B6C6D6E6F000102030405060708090A0B0C0D0E0F")
+ _T("707172737475767778797A7B7C7D7E7F000102030405060708090A0B0C0D0E0F")
+ _T("808182838485868788898A8B8C8D8E8F000102030405060708090A0B0C0D0E0F")
+ _T("909192939495969798999A9B9C9D9E9F000102030405060708090A0B0C0D0E0F")
+ _T("10010110210310410510610710810910A000102030405060708090A0B0C0D0EF")
+ _T("10B10C10D10E10F110111112113114115000102030405060708090A0B0C0D0EF")
+ _T("11611711811911A11B11C11D11E11F120000102030405060708090A0B0C0D0EF")
+ _T("12112212312412512612712812912A12B000102030405060708090A0B0C0D0EF")
+ _T("12C12D12E12F130131132134135136137000102030405060708090A0B0C0D0EF")
+ _T("13813913A13B13C13D13E13F140141142000102030405060708090A0B0C0D0EF")
+ _T("14314414514614714814914A14B14C14D000102030405060708090A0B0C0D0EF")
+ _T("14E14F150151152153154155156157158000102030405060708090A0B0C0D0EF")
+ _T("15915A15B15C15D15E15F160161162163000102030405060708090A0B0C0D0EF")
+ _T("16416516616716816916A16B16C16D16E000102030405060708090A0B0C0D0EF")
+ _T("16F170171172173174175176177178179000102030405060708090A0B0C0D0EF")
+ _T("17A17B17C17D17E17F180181182183184000102030405060708090A0B0C0D0EF")
+ _T("18518618718818918A18B18C18D18E18F000102030405060708090A0B0C0D0EF")
+ _T("19019119219319419519619719819919A000102030405060708090A0B0C0D0EF")
+ _T("19B19C19D19E19F200201202203204205000102030405060708090A0B0C0D0EF")
+ _T("20620720820920A20B20C20D20F210211000102030405060708090A0B0C0D0EF")
+ _T("21221321421521621721821921A21B21C000102030405060708090A0B0C0D0EF")
+ _T("21D21E21F220221222223224225226227000102030405060708090A0B0C0D0EF")
+ _T("22822922A22B22C22D22E22F230231232000102030405060708090A0B0C0D0EF")
+ _T("23323423523623723823923A23B23C23D000102030405060708090A0B0C0D0EF")
+ _T("23E23F240241242243244245246247248000102030405060708090A0B0C0D0EF")
+ _T("24924A24B24C24D24E24F250251252253000102030405060708090A0B0C0D0EF")
+ _T("101112131415161718191A1B1C1D1E1F000102030405060708090A0B0C0D0E0F")
+ _T("202122232425262728292A2B2C2D2E2F000102030405060708090A0B0C0D0E0F")
+ _T("303132333435363738393A3B3C3D3E3F000102030405060708090A0B0C0D0E0F")
+ _T("404142434445464748494A4B4C4D4E4F000102030405060708090A0B0C0D0E0F")
+ _T("505152535455565758595A5B5C5D5E5F000102030405060708090A0B0C0D0E0F")
+ _T("606162636465666768696A6B6C6D6E6F000102030405060708090A0B0C0D0E0F")
+ _T("707172737475767778797A7B7C7D7E7F000102030405060708090A0B0C0D0E0F")
+ _T("808182838485868788898A8B8C8D8E8F000102030405060708090A0B0C0D0E0F")
+ _T("909192939495969798999A9B9C9D9E9F000102030405060708090A0B0C0D0E0F")
+ _T("10010110210310410510610710810910A000102030405060708090A0B0C0D0EF")
+ _T("10B10C10D10E10F110111112113114115000102030405060708090A0B0C0D0EF")
+ _T("11611711811911A11B11C11D11E11F120000102030405060708090A0B0C0D0EF")
+ _T("12112212312412512612712812912A12B000102030405060708090A0B0C0D0EF")
+ _T("12C12D12E12F130131132134135136137000102030405060708090A0B0C0D0EF")
+ _T("13813913A13B13C13D13E13F140141142000102030405060708090A0B0C0D0EF")
+ _T("14314414514614714814914A14B14C14D000102030405060708090A0B0C0D0EF")
+ _T("14E14F150151152153154155156157158000102030405060708090A0B0C0D0EF")
+ _T("15915A15B15C15D15E15F160161162163000102030405060708090A0B0C0D0EF")
+ _T("16416516616716816916A16B16C16D16E000102030405060708090A0B0C0D0EF")
+ _T("16F170171172173174175176177178179000102030405060708090A0B0C0D0EF")
+ _T("17A17B17C17D17E17F180181182183184000102030405060708090A0B0C0D0EF")
+ _T("18518618718818918A18B18C18D18E18F000102030405060708090A0B0C0D0EF")
+ _T("19019119219319419519619719819919A000102030405060708090A0B0C0D0EF")
+ _T("19B19C19D19E19F200201202203204205000102030405060708090A0B0C0D0EF")
+ _T("20620720820920A20B20C20D20F210211000102030405060708090A0B0C0D0EF")
+ _T("21221321421521621721821921A21B21C000102030405060708090A0B0C0D0EF")
+ _T("21D21E21F220221222223224225226227000102030405060708090A0B0C0D0EF")
+ _T("22822922A22B22C22D22E22F230231232000102030405060708090A0B0C0D0EF")
+ _T("23323423523623723823923A23B23C23D000102030405060708090A0B0C0D0EF")
+ _T("2366890FE1438751097E7F6325DC0E6326F")
+ _T("25425525625725825925A25B25C25D25E25F000102030405060708090A0B0C0F");
+// Thread function for Call Context Inserts
+
+struct _ParamStruct
+{
+ HANDLE hShutdownEvent;
+ int nStartingRecordNum;
+ long* pnNumCallsProcessed;
+};
+
+HANDLE hShutdownEvent = 0;
+
+BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
+{
+ if(CTRL_C_EVENT == dwCtrlType)
+ {
+ SetEvent(hShutdownEvent);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+DWORD WINAPI RuntimeCallContext(LPVOID lpParam)
+{
+ long nNumCallsProcessed = 0;
+
+ struct _ParamStruct* pData = (struct _ParamStruct*)lpParam;
+ int nStartingRecordID = pData->nStartingRecordNum;
+
+ HRESULT hr = CoInitialize(NULL);
+ if(FAILED(hr))
+ {
+ printf("Error Initializing COM Library\n");
+ return (int)hr;
+ }
+
+ _ConnectionPtr cn = NULL;
+ _CommandPtr cmdUpdate = NULL, cmdInsert = NULL, cmdDelete = NULL, cmdSelect = NULL;
+ _RecordsetPtr rs = NULL;
+ _ParameterPtr paramContextID = NULL;
+ _ParameterPtr paramVersion = NULL;
+ _ParameterPtr paramLockFlag = NULL;
+ _ParameterPtr ttparamLockFlag = NULL;
+ _ParameterPtr paramLockTime = NULL;
+ _ParameterPtr paramLockTimeUSec = NULL;
+ _ParameterPtr paramContextData = NULL;
+ _variant_t vtVersion;
+ _variant_t vtLockFlag;
+ _variant_t vtLockTime;
+ _variant_t vtLockTimeUSec;
+ _variant_t vtContextData;
+ // Initialize Values
+ vtVersion = CALL_CONTEXT_VERSION;
+ vtLockFlag = CALL_CONTEXT_LOCK_FLAG;
+ vtLockTime = CALL_CONTEXT_LOCK_TIME;
+ vtLockTimeUSec = CALL_CONTEXT_LOCK_TIME_USEC;
+ vtContextData = STATUS_DATA;
+
+ LARGE_INTEGER freq;
+
+ DWORD dwStartTime, dwEndTime;
+ LARGE_INTEGER liStartTime, liEndTime;
+
+ try
+ {
+ cn.CreateInstance(__uuidof(Connection));
+ cn->ConnectionString = _T("DSN=TTTelcoCS;");
+ cn->Open(_T(""),_T(""),_T(""),adConnectUnspecified);
+
+ cmdUpdate.CreateInstance(__uuidof(Command));
+ cmdInsert.CreateInstance(__uuidof(Command));
+ cmdDelete.CreateInstance(__uuidof(Command));
+ cmdSelect.CreateInstance(__uuidof(Command));
+
+ TCHAR tszInsert[10000], tszUpdate[10000];
+ memset(tszInsert, 0, sizeof(tszInsert));
+ memset(tszUpdate, 0, sizeof(tszUpdate));
+ strcpy(tszInsert, "INSERT INTO dbo.CallContext(ContextId,Version,LockFlag,LockTime,LockTimeUSec,ContextData) VALUES(?,?,?,?,?,'");
+ strcat(tszInsert, STATUS_DATA);
+ strcat(tszInsert, "')");
+
+ cmdInsert->CommandText= tszInsert;
+ cmdInsert->ActiveConnection = cn;
+ cmdInsert->Prepared = TRUE;
+
+
+ strcpy(tszUpdate, "UPDATE dbo.CallContext SET ContextData = '");
+ strcat(tszUpdate, STATUS_DATA);
+ strcat(tszUpdate, "' WHERE ContextId = ?");
+ cmdUpdate->CommandText= tszUpdate;
+ cmdUpdate->ActiveConnection = cn;
+ cmdUpdate->Prepared = TRUE;
+
+ cmdDelete->CommandText=_T("DELETE FROM dbo.CallContext WHERE ContextId = ?");
+ cmdDelete->ActiveConnection = cn;
+ cmdDelete->Prepared = TRUE;
+
+ cmdSelect->CommandText=_T("SELECT ContextData FROM dbo.CallContext WHERE ContextId = ?");
+ cmdSelect->ActiveConnection = cn;
+ cmdSelect->Prepared = TRUE;
+
+
+ //Create params
+ paramContextID = cmdInsert->CreateParameter(_T("ContextID"),adInteger,adParamInput,sizeof(int),nStartingRecordID);
+ paramVersion = cmdInsert->CreateParameter(_T("Version"),adInteger,adParamInput,sizeof(int),1);//vtVersion);
+ paramLockFlag = cmdInsert->CreateParameter(_T("LockFlag"),adInteger,adParamInput,sizeof(int),1);//vtLockFlag);
+ ttparamLockFlag = cmdUpdate->CreateParameter(_T("LockFlag"),adInteger,adParamInput,sizeof(int),1);//vtLockFlag);
+ paramLockTime = cmdInsert->CreateParameter(_T("LockTime"),adInteger,adParamInput,sizeof(int),1);//vtLockTime);
+ paramLockTimeUSec = cmdInsert->CreateParameter(_T("LockTimeUSec"),adInteger,adParamInput,sizeof(int),1);//vtLockTimeUSec);
+ paramContextData = cmdInsert->CreateParameter(_T("ContextData"), adBSTR, adParamInput, SysStringByteLen(vtContextData.bstrVal), vtContextData);
+ //paramContextData->put_Value(vtContextData);
+
+
+
+ //Append params
+ cmdInsert->Parameters->Append(paramContextID);
+ cmdInsert->Parameters->Append(paramVersion);
+ cmdInsert->Parameters->Append(paramLockFlag);
+ cmdInsert->Parameters->Append(paramLockTime);
+ cmdInsert->Parameters->Append(paramLockTimeUSec);
+ //cmdInsert->Parameters->Append(paramContextData);
+
+
+ cmdUpdate->Parameters->Append(paramContextID);
+ //cmdUpdate->Parameters->Append(paramContextID);
+
+ cmdSelect->Parameters->Append(paramContextID);
+
+ cmdDelete->Parameters->Append(paramContextID);
+
+ while(WaitForSingleObject(pData->hShutdownEvent,0) != WAIT_OBJECT_0)
+ {
+ paramContextID->Value = nStartingRecordID++;
+
+ bool bTimeLatency = (nStartingRecordID == 100) ? TRUE : FALSE;
+
+ if (bTimeLatency)
+ {
+ BOOL bSuccess = QueryPerformanceFrequency(&freq);
+ if (!bSuccess)
+ printf("Error retrieving frequency: %d\n", GetLastError());
+
+ }
+
+
+
+ for (int i=0; i < 20; i++)
+ {
+ switch(i)
+ {
+ case 3:
+ case 6:
+ case 9:
+ case 11:
+ case 12:
+ case 15:
+ case 18: // Query Record
+ if (bTimeLatency)
+ QueryPerformanceCounter(&liStartTime);
+
+ cmdSelect->Execute(NULL, NULL, -1);
+ if (bTimeLatency)
+ {
+ QueryPerformanceCounter(&liEndTime);
+ printf("Read = %d msec.\n", (liEndTime.QuadPart - liStartTime.QuadPart) / (freq.QuadPart/1000));
+ }
+ break;
+ case 19: // Delete Record
+ if (bTimeLatency)
+ QueryPerformanceCounter(&liStartTime);
+ cmdDelete->Execute(NULL,NULL,adExecuteNoRecords);
+ if (bTimeLatency)
+ {
+ QueryPerformanceCounter(&liEndTime);
+ printf("Delete = %d msec.\n", (liEndTime.QuadPart - liStartTime.QuadPart) / (freq.QuadPart/1000));
+ }
+ break;
+ case 0: // Insert Record
+ if (bTimeLatency)
+ QueryPerformanceCounter(&liStartTime);
+ cmdInsert->Execute(NULL,NULL,adExecuteNoRecords);
+ if (bTimeLatency)
+ {
+ QueryPerformanceCounter(&liEndTime);
+ printf("Insert = %d msec.\n", (liEndTime.QuadPart - liStartTime.QuadPart) / (freq.QuadPart/1000));
+ }
+ break;
+ default: // Update Record
+ if (bTimeLatency)
+ QueryPerformanceCounter(&liStartTime);
+ cmdUpdate->Execute(NULL,NULL,adExecuteNoRecords);
+ if (bTimeLatency)
+ {
+ QueryPerformanceCounter(&liEndTime);
+ printf("Update = %d msec.\n", (liEndTime.QuadPart - liStartTime.QuadPart) / (freq.QuadPart/1000));
+ }
+
+ break;
+ }
+ }
+
+ nNumCallsProcessed++;
+
+ InterlockedIncrement(pData->pnNumCallsProcessed);
+ }
+
+ cn->Close();
+ }
+ catch(_com_error &e)
+ {
+ printf("%d: \n\t%s\n\t%s\n",
+ e.Error(),
+ e.ErrorMessage(),
+ e.Source());
+
+ }
+
+ return 0;
+}
+
+
+int _tmain(int argc, _TCHAR* argv[])
+{
+ long nNumThreads=4;
+ long nSeed = 0;
+ if(lstrcmp(argv[1],_T("/?")) == 0)
+ {
+ _tprintf(_T("InsertRecs [No.Of Threads] [Record Seed No.]\n"));
+ return 0;
+ }
+
+ if(argc > 1)
+ nNumThreads = _ttol(argv[1]);
+ else
+ nNumThreads = 4;
+ if (argc > 2)
+ nSeed = _ttol(argv[2]);
+ _tprintf(_T("Num of Threads = %d, Seed = %d"), nNumThreads, nSeed);
+
+ long nNumCallsProcessed = 0;
+
+ SetConsoleCtrlHandler(ConsoleCtrlHandler,true);
+ hShutdownEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+
+ DWORD dwStartTime = GetTickCount();
+
+ DWORD dwThreadID = 0;
+ HANDLE hThreads[50];
+
+ struct _ParamStruct params[50];
+
+
+ for(int ij=0;ij<nNumThreads;ij++) {
+ params[ij].hShutdownEvent = hShutdownEvent;
+ params[ij].nStartingRecordNum = (ij*5000) + nSeed;
+ params[ij].pnNumCallsProcessed = &nNumCallsProcessed;
+ }
+
+
+ for(int ij=0;ij<nNumThreads;ij++) {
+ hThreads[ij] = CreateThread(NULL,NULL,RuntimeCallContext,&params[ij],0,&dwThreadID);
+ }
+
+ //Wait for the threads to finish
+ WaitForMultipleObjects(nNumThreads,hThreads,TRUE,INFINITE);
+ DWORD dwEndTime = GetTickCount();
+
+ CloseHandle(hShutdownEvent);
+
+ //Print time taken
+ _tprintf(_T("Time Taken for %d Calls is %ld msec (= %ld calls/sec\n"),
+ nNumCallsProcessed,dwEndTime-dwStartTime, (1000*nNumCallsProcessed/(dwEndTime-dwStartTime)));
+ return 0;
+
+}
diff --git a/storage/ndb/test/ndbapi/asyncGenerator.cpp b/storage/ndb/test/ndbapi/asyncGenerator.cpp
new file mode 100644
index 00000000000..d91e38dff1a
--- /dev/null
+++ b/storage/ndb/test/ndbapi/asyncGenerator.cpp
@@ -0,0 +1,571 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 "dbGenerator.h"
+#include <NdbApi.hpp>
+#include <NdbOut.hpp>
+#include <NdbSleep.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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+static void getRandomSubscriberNumber(SubscriberNumber number);
+static void getRandomServerId(ServerId *serverId);
+static void getRandomChangedBy(ChangedBy changedBy);
+static void getRandomChangedTime(ChangedTime changedTime);
+
+static void clearTransaction(TransactionDefinition *trans);
+static void initGeneratorStatistics(GeneratorStatistics *gen);
+
+static void doOneTransaction(ThreadData * td,
+ int parallellism,
+ int millisSendPoll,
+ int minEventSendPoll,
+ int forceSendPoll);
+static void doTransaction_T1(Ndb * pNDB, ThreadData * td, int async);
+static void doTransaction_T2(Ndb * pNDB, ThreadData * td, int async);
+static void doTransaction_T3(Ndb * pNDB, ThreadData * td, int async);
+static void doTransaction_T4(Ndb * pNDB, ThreadData * td, int async);
+static void doTransaction_T5(Ndb * pNDB, ThreadData * td, int async);
+
+/***************************************************************
+* L O C A L D A T A *
+***************************************************************/
+
+static SequenceValues transactionDefinition[] = {
+ {25, 1},
+ {25, 2},
+ {20, 3},
+ {15, 4},
+ {15, 5},
+ {0, 0}
+};
+
+static SequenceValues rollbackDefinition[] = {
+ {98, 0},
+ {2 , 1},
+ {0, 0}
+};
+
+static int maxsize = 0;
+
+/***************************************************************
+* 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 getRandomSubscriberNumber(SubscriberNumber number)
+{
+ uint32 tmp;
+ char sbuf[SUBSCRIBER_NUMBER_LENGTH + 1];
+ tmp = myRandom48(NO_OF_SUBSCRIBERS);
+ sprintf(sbuf, "%.*d", SUBSCRIBER_NUMBER_LENGTH, tmp);
+ memcpy(number, sbuf, SUBSCRIBER_NUMBER_LENGTH);
+}
+
+static void getRandomServerId(ServerId *serverId)
+{
+ *serverId = myRandom48(NO_OF_SERVERS);
+}
+
+static void getRandomChangedBy(ChangedBy changedBy)
+{
+ memset(changedBy, myRandom48(26)+'A', CHANGED_BY_LENGTH);
+ changedBy[CHANGED_BY_LENGTH] = 0;
+}
+
+static void getRandomChangedTime(ChangedTime changedTime)
+{
+ memset(changedTime, myRandom48(26)+'A', CHANGED_TIME_LENGTH);
+ changedTime[CHANGED_TIME_LENGTH] = 0;
+}
+
+static void clearTransaction(TransactionDefinition *trans)
+{
+ trans->count = 0;
+ trans->branchExecuted = 0;
+ trans->rollbackExecuted = 0;
+ trans->latencyCounter = myRandom48(127);
+ trans->latency.reset();
+}
+
+static int listFull(SessionList *list)
+{
+ return(list->numberInList == SESSION_LIST_LENGTH);
+}
+
+static int listEmpty(SessionList *list)
+{
+ return(list->numberInList == 0);
+}
+
+static void insertSession(SessionList *list,
+ SubscriberNumber number,
+ ServerId serverId)
+{
+ SessionElement *e;
+ if( listFull(list) ) return;
+
+ e = &list->list[list->writeIndex];
+
+ strcpy(e->subscriberNumber, number);
+ e->serverId = serverId;
+
+ list->writeIndex = (list->writeIndex + 1) % SESSION_LIST_LENGTH;
+ list->numberInList++;
+
+ if( list->numberInList > maxsize )
+ maxsize = list->numberInList;
+}
+
+static SessionElement *getNextSession(SessionList *list)
+{
+ if( listEmpty(list) ) return(0);
+
+ return(&list->list[list->readIndex]);
+}
+
+static void deleteSession(SessionList *list)
+{
+ if( listEmpty(list) ) return;
+
+ list->readIndex = (list->readIndex + 1) % SESSION_LIST_LENGTH;
+ list->numberInList--;
+}
+
+static void initGeneratorStatistics(GeneratorStatistics *gen)
+{
+ int i;
+
+ if( initSequence(&gen->transactionSequence,
+ transactionDefinition) != 0 ) {
+ ndbout_c("could not set the transaction types");
+ exit(0);
+ }
+
+ if( initSequence(&gen->rollbackSequenceT4,
+ rollbackDefinition) != 0 ) {
+ ndbout_c("could not set the rollback sequence");
+ exit(0);
+ }
+
+ if( initSequence(&gen->rollbackSequenceT5,
+ rollbackDefinition) != 0 ) {
+ ndbout_c("could not set the rollback sequence");
+ exit(0);
+ }
+
+ for(i = 0; i < NUM_TRANSACTION_TYPES; i++ )
+ clearTransaction(&gen->transactions[i]);
+
+ gen->totalTransactions = 0;
+
+ gen->activeSessions.numberInList = 0;
+ gen->activeSessions.readIndex = 0;
+ gen->activeSessions.writeIndex = 0;
+}
+
+
+static
+void
+doOneTransaction(ThreadData * td, int p, int millis, int minEvents, int force)
+{
+ int i;
+ unsigned int transactionType;
+ int async = 1;
+ if (p == 1) {
+ async = 0;
+ }//if
+ for(i = 0; i<p; i++){
+ if(td[i].runState == Runnable){
+ transactionType = getNextRandom(&td[i].generator.transactionSequence);
+
+ switch(transactionType) {
+ case 1:
+ doTransaction_T1(td[i].pNDB, &td[i], async);
+ break;
+ case 2:
+ doTransaction_T2(td[i].pNDB, &td[i], async);
+ break;
+ case 3:
+ doTransaction_T3(td[i].pNDB, &td[i], async);
+ break;
+ case 4:
+ doTransaction_T4(td[i].pNDB, &td[i], async);
+ break;
+ case 5:
+ doTransaction_T5(td[i].pNDB, &td[i], async);
+ break;
+ default:
+ ndbout_c("Unknown transaction type: %d", transactionType);
+ }
+ }
+ }
+ if (async == 1) {
+ td[0].pNDB->sendPollNdb(millis, minEvents, force);
+ }//if
+}
+
+static
+void
+doTransaction_T1(Ndb * pNDB, ThreadData * td, int async)
+{
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ getRandomSubscriberNumber(td->transactionData.number);
+ getRandomChangedBy(td->transactionData.changed_by);
+ BaseString::snprintf(td->transactionData.changed_time,
+ sizeof(td->transactionData.changed_time),
+ "%ld - %d", td->changedTime++, myRandom48(65536*1024));
+ //getRandomChangedTime(td->transactionData.changed_time);
+ td->transactionData.location = td->transactionData.changed_by[0];
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ td->runState = Running;
+ td->generator.transactions[0].startLatency();
+
+ start_T1(pNDB, td, async);
+}
+
+static
+void
+doTransaction_T2(Ndb * pNDB, ThreadData * td, int async)
+{
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ getRandomSubscriberNumber(td->transactionData.number);
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ td->runState = Running;
+ td->generator.transactions[1].startLatency();
+
+ start_T2(pNDB, td, async);
+}
+
+static
+void
+doTransaction_T3(Ndb * pNDB, ThreadData * td, int async)
+{
+ SessionElement *se;
+
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ se = getNextSession(&td->generator.activeSessions);
+ if( se ) {
+ strcpy(td->transactionData.number, se->subscriberNumber);
+ td->transactionData.server_id = se->serverId;
+ td->transactionData.sessionElement = 1;
+ } else {
+ getRandomSubscriberNumber(td->transactionData.number);
+ getRandomServerId(&td->transactionData.server_id);
+ td->transactionData.sessionElement = 0;
+ }
+
+ td->transactionData.server_bit = (1 << td->transactionData.server_id);
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ td->runState = Running;
+ td->generator.transactions[2].startLatency();
+ start_T3(pNDB, td, async);
+}
+
+static
+void
+doTransaction_T4(Ndb * pNDB, ThreadData * td, int async)
+{
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ getRandomSubscriberNumber(td->transactionData.number);
+ getRandomServerId(&td->transactionData.server_id);
+
+ td->transactionData.server_bit = (1 << td->transactionData.server_id);
+ td->transactionData.do_rollback =
+ getNextRandom(&td->generator.rollbackSequenceT4);
+
+#if 0
+ memset(td->transactionData.session_details,
+ myRandom48(26)+'A', SESSION_DETAILS_LENGTH);
+#endif
+ td->transactionData.session_details[SESSION_DETAILS_LENGTH] = 0;
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ td->runState = Running;
+ td->generator.transactions[3].startLatency();
+ start_T4(pNDB, td, async);
+}
+
+static
+void
+doTransaction_T5(Ndb * pNDB, ThreadData * td, int async)
+{
+ SessionElement * se;
+ se = getNextSession(&td->generator.activeSessions);
+ if( se ) {
+ strcpy(td->transactionData.number, se->subscriberNumber);
+ td->transactionData.server_id = se->serverId;
+ td->transactionData.sessionElement = 1;
+ }
+ else {
+ getRandomSubscriberNumber(td->transactionData.number);
+ getRandomServerId(&td->transactionData.server_id);
+ td->transactionData.sessionElement = 0;
+ }
+
+ td->transactionData.server_bit = (1 << td->transactionData.server_id);
+ td->transactionData.do_rollback
+ = getNextRandom(&td->generator.rollbackSequenceT5);
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ td->runState = Running;
+ td->generator.transactions[4].startLatency();
+ start_T5(pNDB, td, async);
+}
+
+void
+complete_T1(ThreadData * data){
+ data->generator.transactions[0].stopLatency();
+ data->generator.transactions[0].count++;
+
+ data->runState = Runnable;
+ data->generator.totalTransactions++;
+}
+
+void
+complete_T2(ThreadData * data){
+ data->generator.transactions[1].stopLatency();
+ data->generator.transactions[1].count++;
+
+ data->runState = Runnable;
+ data->generator.totalTransactions++;
+}
+
+void
+complete_T3(ThreadData * data){
+
+ data->generator.transactions[2].stopLatency();
+ data->generator.transactions[2].count++;
+
+ if(data->transactionData.branchExecuted)
+ data->generator.transactions[2].branchExecuted++;
+
+ data->runState = Runnable;
+ data->generator.totalTransactions++;
+}
+
+void
+complete_T4(ThreadData * data){
+
+ data->generator.transactions[3].stopLatency();
+ data->generator.transactions[3].count++;
+
+ if(data->transactionData.branchExecuted)
+ data->generator.transactions[3].branchExecuted++;
+ if(data->transactionData.do_rollback)
+ data->generator.transactions[3].rollbackExecuted++;
+
+ if(data->transactionData.branchExecuted &&
+ !data->transactionData.do_rollback){
+ insertSession(&data->generator.activeSessions,
+ data->transactionData.number,
+ data->transactionData.server_id);
+ }
+
+ data->runState = Runnable;
+ data->generator.totalTransactions++;
+
+}
+void
+complete_T5(ThreadData * data){
+
+ data->generator.transactions[4].stopLatency();
+ data->generator.transactions[4].count++;
+
+ if(data->transactionData.branchExecuted)
+ data->generator.transactions[4].branchExecuted++;
+ if(data->transactionData.do_rollback)
+ data->generator.transactions[4].rollbackExecuted++;
+
+ if(data->transactionData.sessionElement &&
+ !data->transactionData.do_rollback){
+ deleteSession(&data->generator.activeSessions);
+ }
+
+ data->runState = Runnable;
+ data->generator.totalTransactions++;
+}
+
+/***************************************************************
+****************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+void
+asyncGenerator(ThreadData *data,
+ int parallellism,
+ int millisSendPoll,
+ int minEventSendPoll,
+ int forceSendPoll)
+{
+ ThreadData * startUp;
+
+ GeneratorStatistics *st;
+ double periodStop;
+ double benchTimeStart;
+ double benchTimeEnd;
+ int i, j, done;
+
+ myRandom48Init(data->randomSeed);
+
+ for(i = 0; i<parallellism; i++){
+ initGeneratorStatistics(&data[i].generator);
+ }
+
+ startUp = (ThreadData*)malloc(parallellism * sizeof(ThreadData));
+ memcpy(startUp, data, (parallellism * sizeof(ThreadData)));
+
+ /*----------------*/
+ /* warm up period */
+ /*----------------*/
+ periodStop = userGetTime() + (double)data[0].warmUpSeconds;
+
+ while(userGetTime() < periodStop){
+ doOneTransaction(startUp, parallellism,
+ millisSendPoll, minEventSendPoll, forceSendPoll);
+ }
+
+ ndbout_c("Waiting for startup to finish");
+
+ /**
+ * Wait for all transactions
+ */
+ done = 0;
+ while(!done){
+ done = 1;
+ for(i = 0; i<parallellism; i++){
+ if(startUp[i].runState != Runnable){
+ done = 0;
+ break;
+ }
+ }
+ if(!done){
+ startUp[0].pNDB->sendPollNdb();
+ }
+ }
+ ndbout_c("Benchmark period starts");
+
+ /*-------------------------*/
+ /* normal benchmark period */
+ /*-------------------------*/
+ benchTimeStart = userGetTime();
+
+ periodStop = benchTimeStart + (double)data[0].testSeconds;
+ while(userGetTime() < periodStop)
+ doOneTransaction(data, parallellism,
+ millisSendPoll, minEventSendPoll, forceSendPoll);
+
+ benchTimeEnd = userGetTime();
+
+ ndbout_c("Benchmark period done");
+
+ /**
+ * Wait for all transactions
+ */
+ done = 0;
+ while(!done){
+ done = 1;
+ for(i = 0; i<parallellism; i++){
+ if(data[i].runState != Runnable){
+ done = 0;
+ break;
+ }
+ }
+ if(!done){
+ data[0].pNDB->sendPollNdb();
+ }
+ }
+
+ /*------------------*/
+ /* cool down period */
+ /*------------------*/
+ periodStop = userGetTime() + (double)data[0].coolDownSeconds;
+ while(userGetTime() < periodStop){
+ doOneTransaction(startUp, parallellism,
+ millisSendPoll, minEventSendPoll, forceSendPoll);
+ }
+
+ done = 0;
+ while(!done){
+ done = 1;
+ for(i = 0; i<parallellism; i++){
+ if(startUp[i].runState != Runnable){
+ done = 0;
+ break;
+ }
+ }
+ if(!done){
+ startUp[0].pNDB->sendPollNdb();
+ }
+ }
+
+
+ /*---------------------------------------------------------*/
+ /* add the times for all transaction for inner loop timing */
+ /*---------------------------------------------------------*/
+ for(j = 0; j<parallellism; j++){
+ st = &data[j].generator;
+
+ st->outerLoopTime = benchTimeEnd - benchTimeStart;
+ st->outerTps = getTps(st->totalTransactions, st->outerLoopTime);
+ }
+ /* ndbout_c("maxsize = %d\n",maxsize); */
+
+ free(startUp);
+}
+
diff --git a/storage/ndb/test/ndbapi/bank/Bank.cpp b/storage/ndb/test/ndbapi/bank/Bank.cpp
new file mode 100644
index 00000000000..40819ecc849
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bank/Bank.cpp
@@ -0,0 +1,2462 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "Bank.hpp"
+#include <time.h>
+#include <NdbSleep.h>
+#include <UtilTransactions.hpp>
+
+Bank::Bank(Ndb_cluster_connection& con):
+ m_ndb(&con, "BANK"),
+ m_maxAccount(-1),
+ m_initialized(false)
+{
+
+}
+
+int Bank::init(){
+ if (m_initialized == true)
+ return NDBT_OK;
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+
+ m_ndb.init();
+ while (m_ndb.waitUntilReady(10) != 0)
+ ndbout << "Waiting for ndb to be ready" << endl;
+
+ if (getNumAccounts() != NDBT_OK)
+ return NDBT_FAILED;
+ return NDBT_OK;
+}
+
+int Bank::performTransactions(int maxSleepBetweenTrans, int yield){
+
+ if (init() != NDBT_OK)
+ return NDBT_FAILED;
+ int transactions = 0;
+
+ while(1){
+
+ while(m_ndb.waitUntilReady(10) != 0)
+ ndbout << "Waiting for ndb to be ready" << endl;
+
+ while(performTransaction() != NDBT_FAILED){
+ transactions++;
+
+ if (maxSleepBetweenTrans > 0){
+ int val = myRandom48(maxSleepBetweenTrans);
+ NdbSleep_MilliSleep(val);
+ }
+
+ if((transactions % 100) == 0)
+ g_info << transactions << endl;
+
+ if (yield != 0 && transactions >= yield)
+ return NDBT_OK;
+ }
+ }
+ return NDBT_FAILED;
+
+}
+
+int Bank::performTransaction(){
+ int result = NDBT_OK;
+
+ if (m_maxAccount <= 0){
+ g_err << "No accounts in bank" << endl;
+ return NDBT_FAILED;
+ }
+
+ int fromAccount = myRandom48(m_maxAccount);
+ int toAccount = myRandom48(m_maxAccount);
+
+ if (fromAccount == toAccount){
+ // Increase toAccount with 1
+ toAccount = (toAccount+1)%m_maxAccount;
+ }
+
+ int maxAmount = getMaxAmount();
+
+ int amount = myRandom48(maxAmount);
+
+ retry_transaction:
+ int res = performTransaction(fromAccount, toAccount, amount);
+ if (res != 0){
+ switch (res){
+ case NDBT_FAILED:
+ g_err << "performTransaction returned NDBT_FAILED" << endl
+ << " fromAccount = " << fromAccount << endl
+ << " toAccount = " << toAccount << endl
+ << " amount = " << amount << endl;
+ result = NDBT_FAILED;
+ break;
+ case NOT_ENOUGH_FUNDS:
+ // ndbout << "performTransaction returned NOT_ENOUGH_FUNDS" << endl;
+ break;
+ case NDBT_TEMPORARY:
+ g_err << "TEMPORARY_ERRROR retrying" << endl;
+ goto retry_transaction;
+ break;
+ default:
+ g_info << "performTransaction returned "<<res << endl;
+ break;
+ }
+ }
+ return result;
+}
+
+/**
+ * Perform a transaction in the bank.
+ * Ie. transfer money from one account to another.
+ *
+ * @param
+ * @return 0 if successful or an error code
+ */
+int Bank::performTransaction(int fromAccountId,
+ int toAccountId,
+ int amount ){
+ /**
+ * 1. Start transaction
+ * 2. Check balance on from account, if there is
+ * not enough funds abort transaction
+ * 3. Update ACCOUNT set balance = balance - amount on
+ * from account
+ * 4. Insert withdrawal in TRANSACTION
+ * 5. Insert deposit in transaction
+ * 6. Update ACCOUNT set balance = balance + amount on
+ * to account
+ * 7. Commit transaction
+ */
+ // g_info << "performTransaction " << fromAccountId
+ // << ", "<<toAccountId<<", "<<amount << endl;
+
+ // Call the first implementation of this trans
+ // In the future we can have several different versions of this trans
+ // and call them randomly
+ return performTransactionImpl1(fromAccountId, toAccountId, amount);
+}
+
+
+int Bank::performTransactionImpl1(int fromAccountId,
+ int toAccountId,
+ int amount ){
+
+ int check;
+
+ // Ok, all clear to do the transaction
+ Uint64 transId;
+ if (getNextTransactionId(transId) != NDBT_OK){
+ return NDBT_FAILED;
+ }
+
+ NdbConnection* pTrans = m_ndb.startTransaction();
+
+ if( pTrans == NULL ) {
+ const NdbError err = m_ndb.getNdbError();
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ return NDBT_TEMPORARY;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ Uint64 currTime;
+ if (prepareGetCurrTimeOp(pTrans, currTime) != NDBT_OK){
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ /**
+ * Check balance on from account
+ */
+ NdbOperation* pOp = pTrans->getNdbOperation("ACCOUNT");
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->readTupleExclusive();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("ACCOUNT_ID", fromAccountId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* balanceFromRec = pOp->getValue("BALANCE");
+ if( balanceFromRec ==NULL ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* fromAccountTypeRec = pOp->getValue("ACCOUNT_TYPE");
+ if( fromAccountTypeRec == NULL ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ /**
+ * Read balance on to account
+ */
+ NdbOperation* pOp6 = pTrans->getNdbOperation("ACCOUNT");
+ if (pOp6 == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp6->readTupleExclusive();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp6->equal("ACCOUNT_ID", toAccountId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* balanceToRec = pOp6->getValue("BALANCE");
+ if( balanceToRec == NULL ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* toAccountTypeRec = pOp6->getValue("ACCOUNT_TYPE");
+ if( toAccountTypeRec == NULL ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ m_ndb.closeTransaction(pTrans);
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ return NDBT_TEMPORARY;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+
+ Uint32 balanceFrom = balanceFromRec->u_32_value();
+ // ndbout << "balanceFrom: " << balanceFrom << endl;
+
+ if (((Int64)balanceFrom - amount) < 0){
+ m_ndb.closeTransaction(pTrans);
+ //ndbout << "Not enough funds" << endl;
+ return NOT_ENOUGH_FUNDS;
+ }
+
+ Uint32 fromAccountType = fromAccountTypeRec->u_32_value();
+
+ Uint32 balanceTo = balanceToRec->u_32_value();
+ // ndbout << "balanceTo: " << balanceTo << endl;
+ Uint32 toAccountType = toAccountTypeRec->u_32_value();
+
+ /**
+ * Update balance on from account
+ */
+ NdbOperation* pOp2 = pTrans->getNdbOperation("ACCOUNT");
+ if (pOp2 == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp2->updateTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp2->equal("ACCOUNT_ID", fromAccountId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp2->setValue("BALANCE", balanceFrom - amount);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ /**
+ * Update balance on to account
+ */
+ NdbOperation* pOp3 = pTrans->getNdbOperation("ACCOUNT");
+ if (pOp3 == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp3->updateTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp3->equal("ACCOUNT_ID", toAccountId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp3->setValue("BALANCE", balanceTo + amount);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ /**
+ * Insert withdrawal transaction
+ */
+ NdbOperation* pOp4 = pTrans->getNdbOperation("TRANSACTION");
+ if (pOp4 == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp4->insertTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp4->equal("TRANSACTION_ID", transId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp4->equal("ACCOUNT", fromAccountId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp4->setValue("ACCOUNT_TYPE", fromAccountType);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp4->setValue("OTHER_ACCOUNT", toAccountId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp4->setValue("TRANSACTION_TYPE", WithDrawal);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp4->setValue("TIME", currTime);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp4->setValue("AMOUNT", amount);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ /**
+ * Insert deposit transaction
+ */
+ NdbOperation* pOp5 = pTrans->getNdbOperation("TRANSACTION");
+ if (pOp5 == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp5->insertTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp5->equal("TRANSACTION_ID", transId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp5->equal("ACCOUNT", toAccountId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp5->setValue("ACCOUNT_TYPE", toAccountType);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp5->setValue("OTHER_ACCOUNT", fromAccountId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp5->setValue("TRANSACTION_TYPE", Deposit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp5->setValue("TIME", currTime);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp5->setValue("AMOUNT", amount);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ m_ndb.closeTransaction(pTrans);
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ return NDBT_TEMPORARY;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_OK;
+}
+
+
+
+
+int Bank::performMakeGLs(int yield){
+ int result;
+ if (init() != NDBT_OK)
+ return NDBT_FAILED;
+
+ int counter, maxCounter;
+ int yieldCounter = 0;
+
+ while (1){
+ // Counters to keep tracck of how many
+ // GLs should be made before performing a validation
+ counter = 0;
+ maxCounter = 50 + myRandom48(100);
+
+ while(m_ndb.waitUntilReady(10) != 0)
+ ndbout << "Waiting for ndb to be ready" << endl;
+
+ /**
+ * Validate GLs and Transactions for previous days
+ *
+ */
+ result = performValidateGLs();
+ if (result != NDBT_OK){
+ if (result == VERIFICATION_FAILED){
+ g_err << "performValidateGLs verification failed" << endl;
+ return NDBT_FAILED;
+ }
+ g_info << "performValidateGLs failed" << endl;
+ continue;
+ }
+
+ result = performValidatePurged();
+ if (result != NDBT_OK){
+ if (result == VERIFICATION_FAILED){
+ g_err << "performValidatePurged verification failed" << endl;
+ return NDBT_FAILED;
+ }
+ g_info << "performValidatePurged failed" << endl;
+ continue;
+ }
+
+ while (1){
+
+ yieldCounter++;
+ if (yield != 0 && yieldCounter >= yield)
+ return NDBT_OK;
+
+ /**
+ * Find last GL time.
+ * ( GL record with highest time value)
+ */
+ Uint64 lastGLTime;
+ if (findLastGL(lastGLTime) != NDBT_OK){
+ g_info << "findLastGL failed" << endl;
+ // Break out of inner while loop
+ break;
+ }
+
+ lastGLTime++;
+
+ /**
+ * If last GL time + 1 is smaller than current time
+ * perform a GL for that time
+ */
+ Uint64 currTime;
+ if (getCurrTime(currTime) != NDBT_OK){
+ g_info << "getCurrTime failed" << endl;
+ // Break out of inner while loop
+ break;
+ }
+ if (lastGLTime < currTime){
+ counter++;
+ if (performMakeGL(lastGLTime) != NDBT_OK){
+ g_info << "performMakeGL failed" << endl;
+ // Break out of inner while loop
+ break;
+ }
+
+ if (counter > maxCounter){
+ // Break out of inner while loop and
+ // validatePreviousGLs
+ g_info << "counter("<<counter<<") > maxCounter("<<maxCounter<<")" << endl;
+ break;
+ }
+
+ } else {
+ ;//ndbout << "It's not time to make GL yet" << endl;
+
+ // ndbout << "Sleeping 1 second" << endl;
+ NdbSleep_SecSleep(1);
+
+ }
+
+ Uint32 age = 3;
+ if (purgeOldGLTransactions(currTime, age) != NDBT_OK){
+ g_info << "purgeOldGLTransactions failed" << endl;
+ // Break out of inner while loop
+ break;
+ }
+
+ }
+ }
+
+ return NDBT_FAILED;
+
+}
+
+int Bank::performValidateAllGLs(){
+ int result;
+ if (init() != NDBT_OK)
+ return NDBT_FAILED;
+
+ while (1){
+
+ while(m_ndb.waitUntilReady(10) != 0)
+ ndbout << "Waiting for ndb to be ready" << endl;
+
+ /**
+ * Validate GLs and Transactions for previous days
+ * Set age so that ALL GL's are validated
+ */
+ int age = 100000;
+ result = performValidateGLs(age);
+ if (result != NDBT_OK){
+ if (result == VERIFICATION_FAILED){
+ g_err << "performValidateGLs verification failed" << endl;
+ return NDBT_FAILED;
+ }
+ g_err << "performValidateGLs failed" << endl;
+ return NDBT_FAILED;
+ }
+
+ /**
+ *
+ *
+ */
+ result = performValidatePurged();
+ if (result != NDBT_OK){
+ if (result == VERIFICATION_FAILED){
+ g_err << "performValidatePurged verification failed" << endl;
+ return NDBT_FAILED;
+ }
+ g_err << "performValidatePurged failed" << endl;
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+ }
+
+ return NDBT_FAILED;
+
+}
+
+int Bank::findLastGL(Uint64 &lastTime){
+
+ int check;
+ /**
+ * SELECT MAX(time) FROM GL
+ */
+ NdbConnection* pScanTrans = m_ndb.startTransaction();
+ if (pScanTrans == NULL) {
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("GL");
+ if (pOp == NULL) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples() ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* timeRec = pOp->getValue("TIME");
+ if( timeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pScanTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+ eof = pOp->nextResult();
+ lastTime = 0;
+
+ while(eof == 0){
+ rows++;
+ Uint64 t = timeRec->u_32_value();
+
+ if (t > lastTime)
+ lastTime = t;
+
+ eof = pOp->nextResult();
+ }
+ if (eof == -1) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pScanTrans);
+
+ return NDBT_OK;
+}
+
+
+int Bank::performMakeGL(int time){
+ g_info << "performMakeGL: " << time << endl;
+ /**
+ * Create one GL record for each account type.
+ * All in the same transaction
+ */
+ // Start transaction
+ NdbConnection* pTrans = m_ndb.startTransaction();
+ if (pTrans == NULL){
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+ for (int i = 0; i < getNumAccountTypes(); i++){
+
+ if (performMakeGLForAccountType(pTrans, time, i) != NDBT_OK){
+ g_err << "performMakeGLForAccountType returned NDBT_FAILED"<<endl;
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+ // Execute transaction
+ if( pTrans->execute(Commit) == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ m_ndb.closeTransaction(pTrans);
+
+ return NDBT_OK;
+}
+
+int Bank::performMakeGLForAccountType(NdbConnection* pTrans,
+ Uint64 glTime,
+ Uint32 accountTypeId){
+ int check;
+
+ Uint32 balance = 0;
+ Uint32 withdrawalCount = 0;
+ Uint32 withdrawalSum = 0;
+ Uint32 depositSum = 0;
+ Uint32 depositCount = 0;
+ Uint32 countTransactions = 0;
+ Uint32 purged = 0;
+
+ // Insert record in GL so that we know
+ // that no one else is performing the same task
+ // Set purged = 0 to indicate that TRANSACTION
+ // records still exist
+ NdbOperation* pOp = pTrans->getNdbOperation("GL");
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->insertTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("TIME", glTime);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("ACCOUNT_TYPE", accountTypeId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->setValue("BALANCE", balance);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->setValue("DEPOSIT_COUNT", depositCount);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->setValue("DEPOSIT_SUM", depositSum);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->setValue("WITHDRAWAL_COUNT", withdrawalCount);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->setValue("WITHDRAWAL_SUM", withdrawalSum);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->setValue("PURGED", purged);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pOp->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Read previous GL record to get old balance
+ NdbOperation* pOp2 = pTrans->getNdbOperation("GL");
+ if (pOp2 == NULL) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp2->readTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp2->equal("TIME", glTime-1);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp2->equal("ACCOUNT_TYPE", accountTypeId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* oldBalanceRec = pOp2->getValue("BALANCE");
+ if( oldBalanceRec == NULL ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pOp2->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ Uint32 oldBalance = oldBalanceRec->u_32_value();
+ // ndbout << "oldBalance = "<<oldBalance<<endl;
+ balance = oldBalance;
+ // Start a scan transaction to search
+ // for TRANSACTION records with TIME = time
+ // and ACCOUNT_TYPE = accountTypeId
+ // Build sum of all found transactions
+
+ if (sumTransactionsForGL(glTime,
+ accountTypeId,
+ balance,
+ withdrawalCount,
+ withdrawalSum,
+ depositSum,
+ depositCount,
+ countTransactions,
+ pTrans) != NDBT_OK){
+ return NDBT_FAILED;
+ }
+ // ndbout << "sumTransactionsForGL completed" << endl;
+ // ndbout << "balance="<<balance<<endl
+ // << "withdrawalCount="<<withdrawalCount<<endl
+ // << "withdrawalSum="<<withdrawalSum<<endl
+ // << "depositCount="<<depositCount<<endl
+ // << "depositSum="<<depositSum<<endl;
+
+
+
+ NdbOperation* pOp3 = pTrans->getNdbOperation("GL");
+ if (pOp3 == NULL) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp3->updateTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp3->equal("TIME", glTime);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp3->equal("ACCOUNT_TYPE", accountTypeId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp3->setValue("BALANCE", balance);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp3->setValue("DEPOSIT_COUNT", depositCount);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp3->setValue("DEPOSIT_SUM", depositSum);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp3->setValue("WITHDRAWAL_COUNT", withdrawalCount);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp3->setValue("WITHDRAWAL_SUM", withdrawalSum);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp3->setValue("PURGED", purged);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Execute transaction
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+
+
+
+int Bank::sumTransactionsForGL(const Uint64 glTime,
+ const Uint32 accountType,
+ Uint32& balance,
+ Uint32& withdrawalCount,
+ Uint32& withdrawalSum,
+ Uint32& depositSum,
+ Uint32& depositCount,
+ Uint32& transactionsCount,
+ NdbConnection* pTrans){
+ int check;
+
+ // g_info << "sumTransactionsForGL: " << glTime << ", " << accountType << endl;
+
+ NdbConnection* pScanTrans = m_ndb.startTransaction();
+ if (pScanTrans == NULL) {
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("TRANSACTION");
+ if (pOp == NULL) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuplesExclusive()) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
+ if( accountTypeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* timeRec = pOp->getValue("TIME");
+ if( timeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* transTypeRec = pOp->getValue("TRANSACTION_TYPE");
+ if( transTypeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* amountRec = pOp->getValue("AMOUNT");
+ if( amountRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pScanTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+ int rowsFound = 0;
+ eof = pOp->nextResult();
+
+ while(eof == 0){
+ rows++;
+ Uint32 a = accountTypeRec->u_32_value();
+ Uint64 t = timeRec->u_64_value();
+
+ if (a == accountType && t == glTime){
+ rowsFound++;
+ // One record found
+ int transType = transTypeRec->u_32_value();
+ int amount = amountRec->u_32_value();
+ if (transType == WithDrawal){
+ withdrawalCount++;
+ withdrawalSum += amount;
+ balance -= amount;
+ } else {
+ assert(transType == Deposit);
+ depositCount++;
+ depositSum += amount;
+ balance += amount;
+ }
+ }
+
+ eof = pOp->nextResult();
+
+ if ((rows % 100) == 0){
+ // "refresh" ownner transaction every 100th row
+ if (pTrans->refresh() == -1) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+
+ }
+ if (eof == -1) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pScanTrans);
+ // ndbout << rows << " TRANSACTIONS have been read" << endl;
+ transactionsCount = rowsFound;
+
+ return NDBT_OK;
+
+}
+
+ int Bank::performValidateGLs(Uint64 age){
+
+ Uint64 currTime;
+ if (getCurrTime(currTime) != NDBT_OK){
+ return NDBT_FAILED;
+ }
+ Uint64 glTime = currTime - 1;
+ while((glTime > 0) && ((glTime + age) >= currTime)){
+
+ int result = performValidateGL(glTime);
+ if (result != NDBT_OK){
+ g_err << "performValidateGL failed" << endl;
+ return result;
+ }
+
+ glTime--;
+ }
+
+ return NDBT_OK;
+ }
+
+int Bank::performValidateGL(Uint64 glTime){
+
+ ndbout << "performValidateGL: " << glTime << endl;
+ /**
+ * Rules:
+ * - There should be zero or NoAccountTypes GL records for each glTime
+ * - If purged == 0, then the TRANSACTION table should be checked
+ * to see that there are:
+ * + DEPOSIT_COUNT deposit transactions with account_type == ACCOUNT_TYPE
+ * and TIME == glTime. The sum of these transactions should be
+ * DEPOSIT_SUM
+ * + WITHDRAWAL_COUNT withdrawal transactions with account_type ==
+ * ACCOUNT_TYPE and TIME == glTime. The sum of these transactions
+ * should be WITHDRAWAL_SUM
+ * + BALANCE should be equal to the sum of all transactions plus
+ * the balance of the previous GL record
+ * - If purged == 1 then there should be NO transactions with TIME == glTime
+ * and ACCOUNT_TYPE == account_type
+ *
+ */
+
+ int check;
+ /**
+ * SELECT * FROM GL WHERE account_type = @accountType and time = @time
+ */
+ NdbConnection* pScanTrans = m_ndb.startTransaction();
+ if (pScanTrans == NULL) {
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("GL");
+ if (pOp == NULL) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples() ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
+ if( accountTypeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* timeRec = pOp->getValue("TIME");
+ if( timeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* purgedRec = pOp->getValue("PURGED");
+ if( purgedRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* balanceRec = pOp->getValue("BALANCE");
+ if( balanceRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* depositSumRec = pOp->getValue("DEPOSIT_SUM");
+ if( depositSumRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* depositCountRec = pOp->getValue("DEPOSIT_COUNT");
+ if( depositCountRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* withdrawalSumRec = pOp->getValue("WITHDRAWAL_SUM");
+ if( withdrawalSumRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+ NdbRecAttr* withdrawalCountRec = pOp->getValue("WITHDRAWAL_COUNT");
+ if( withdrawalCountRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pScanTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+ int countGlRecords = 0;
+ int result = NDBT_OK;
+ eof = pOp->nextResult();
+
+ while(eof == 0){
+ rows++;
+ Uint64 t = timeRec->u_64_value();
+
+ if (t == glTime){
+ countGlRecords++;
+ Uint32 a = accountTypeRec->u_32_value();
+ Uint32 purged = purgedRec->u_32_value();
+ Uint32 wsum = withdrawalSumRec->u_32_value();
+ Uint32 wcount = withdrawalCountRec->u_32_value();
+ Uint32 dsum = depositSumRec->u_32_value();
+ Uint32 dcount = depositCountRec->u_32_value();
+ Uint32 b = balanceRec->u_32_value();
+
+ Uint32 balance = 0;
+ Uint32 withdrawalSum = 0;
+ Uint32 withdrawalCount = 0;
+ Uint32 depositSum = 0;
+ Uint32 depositCount = 0;
+ Uint32 countTransactions = 0;
+ if (purged == 0){
+ // If purged == 0, then the TRANSACTION table should be checked
+ // to see that there are:
+ // + DEPOSIT_COUNT deposit transactions with account_type == ACCOUNT_TYPE
+ // and TIME == glTime. The sum of these transactions should be
+ // DEPOSIT_SUM
+ // + WITHDRAWAL_COUNT withdrawal transactions with account_type ==
+ // ACCOUNT_TYPE and TIME == glTime. The sum of these transactions
+ // should be WITHDRAWAL_SUM
+ // + BALANCE should be equal to the sum of all transactions plus
+ // the balance of the previous GL record
+ if (sumTransactionsForGL(t,
+ a,
+ balance,
+ withdrawalCount,
+ withdrawalSum,
+ depositSum,
+ depositCount,
+ countTransactions,
+ pScanTrans) != NDBT_OK){
+ result = NDBT_FAILED;
+ } else {
+ Uint32 prevBalance = 0;
+ if (getBalanceForGL(t-1, a, prevBalance) != NDBT_OK){
+ result = NDBT_FAILED;
+ } else
+ if (((prevBalance + balance) != b) ||
+ (wsum != withdrawalSum) ||
+ (wcount != withdrawalCount) ||
+ (dsum != depositSum) ||
+ (dcount != depositCount)){
+ g_err << "performValidateGL, sums and counts failed" << endl
+ << "balance : " << balance+prevBalance << "!="<<b<<endl
+ << "with sum : " << withdrawalSum << "!="<<wsum<<endl
+ << "with count: " << withdrawalCount << "!="<<wcount<<endl
+ << "dep sum : " << depositSum << "!="<<dsum<<endl
+ << "dep count : " << depositCount << "!="<<dcount<<endl;
+ result = VERIFICATION_FAILED;
+ }
+ }
+
+ } else {
+ assert(purged == 1);
+ // If purged == 1 then there should be NO transactions with
+ // TIME == glTime and ACCOUNT_TYPE == account_type
+
+ if (sumTransactionsForGL(t,
+ a,
+ balance,
+ withdrawalCount,
+ withdrawalSum,
+ depositSum,
+ depositCount,
+ countTransactions,
+ pScanTrans) != NDBT_OK){
+ result = NDBT_FAILED;
+ } else {
+ if (countTransactions != 0){
+ g_err << "performValidateGL, countTransactions("<<countTransactions<<") != 0" << endl;
+ result = VERIFICATION_FAILED;
+ }
+ }
+ }
+
+ }
+ eof = pOp->nextResult();
+ }
+ if (eof == -1) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pScanTrans);
+
+ // - There should be zero or NoAccountTypes GL records for each glTime
+ if ((countGlRecords != 0) && (countGlRecords != getNumAccountTypes())){
+ g_err << "performValidateGL: " << endl
+ << "countGlRecords = " << countGlRecords << endl;
+ result = VERIFICATION_FAILED;
+ }
+
+ return result;
+
+
+ }
+
+int Bank::getBalanceForGL(const Uint64 glTime,
+ const Uint32 accountTypeId,
+ Uint32 &balance){
+ int check;
+
+ NdbConnection* pTrans = m_ndb.startTransaction();
+ if (pTrans == NULL) {
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbOperation* pOp = pTrans->getNdbOperation("GL");
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->readTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("TIME", glTime);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("ACCOUNT_TYPE", accountTypeId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* balanceRec = pOp->getValue("BALANCE");
+ if( balanceRec == NULL ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pTrans);
+
+ balance = balanceRec->u_32_value();
+
+ return NDBT_OK;
+}
+
+
+
+int Bank::getOldestPurgedGL(const Uint32 accountType,
+ Uint64 &oldest){
+ int check;
+ /**
+ * SELECT MAX(time) FROM GL WHERE account_type = @accountType and purged=1
+ */
+ NdbConnection* pScanTrans = m_ndb.startTransaction();
+ if (pScanTrans == NULL) {
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("GL");
+ if (pOp == NULL) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples() ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
+ if( accountTypeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* timeRec = pOp->getValue("TIME");
+ if( timeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* purgedRec = pOp->getValue("PURGED");
+ if( purgedRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pScanTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+ eof = pOp->nextResult();
+ oldest = 0;
+
+ while(eof == 0){
+ rows++;
+ Uint32 a = accountTypeRec->u_32_value();
+ Uint32 p = purgedRec->u_32_value();
+
+ if (a == accountType && p == 1){
+ // One record found
+ Uint64 t = timeRec->u_64_value();
+ if (t > oldest)
+ oldest = t;
+ }
+ eof = pOp->nextResult();
+ }
+ if (eof == -1) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pScanTrans);
+
+ return NDBT_OK;
+}
+
+int Bank::getOldestNotPurgedGL(Uint64 &oldest,
+ Uint32 &accountTypeId,
+ bool &found){
+ int check;
+ /**
+ * SELECT time, accountTypeId FROM GL
+ * WHERE purged=0 order by time asc
+ */
+ NdbConnection* pScanTrans = m_ndb.startTransaction();
+ if (pScanTrans == NULL) {
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("GL");
+ if (pOp == NULL) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples() ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
+ if( accountTypeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* timeRec = pOp->getValue("TIME");
+ if( timeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* purgedRec = pOp->getValue("PURGED");
+ if( purgedRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pScanTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+ eof = pOp->nextResult();
+ oldest = (Uint64)-1;
+ found = false;
+
+ while(eof == 0){
+ rows++;
+ Uint32 p = purgedRec->u_32_value();
+ if (p == 0){
+ found = true;
+ // One record found
+ Uint32 a = accountTypeRec->u_32_value();
+ Uint64 t = timeRec->u_64_value();
+ if (t < oldest){
+ oldest = t;
+ accountTypeId = a;
+ }
+ }
+ eof = pOp->nextResult();
+ }
+ if (eof == -1) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pScanTrans);
+
+ return NDBT_OK;
+}
+
+
+int Bank::checkNoTransactionsOlderThan(const Uint32 accountType,
+ const Uint64 oldest){
+ /**
+ * SELECT COUNT(transaction_id) FROM TRANSACTION
+ * WHERE account_type = @accountType and time <= @oldest
+ *
+ */
+
+ int check;
+ NdbConnection* pScanTrans = m_ndb.startTransaction();
+ if (pScanTrans == NULL) {
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("TRANSACTION");
+ if (pOp == NULL) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples() ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
+ if( accountTypeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* timeRec = pOp->getValue("TIME");
+ if( timeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* transactionIdRec = pOp->getValue("TRANSACTION_ID");
+ if( transactionIdRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pScanTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+ int found = 0;
+ eof = pOp->nextResult();
+
+ while(eof == 0){
+ rows++;
+ Uint32 a = accountTypeRec->u_32_value();
+ Uint32 t = timeRec->u_32_value();
+
+ if (a == accountType && t <= oldest){
+ // One record found
+ Uint64 ti = transactionIdRec->u_64_value();
+ g_err << "checkNoTransactionsOlderThan found one record" << endl
+ << " t = " << t << endl
+ << " a = " << a << endl
+ << " ti = " << ti << endl;
+ found++;
+ }
+ eof = pOp->nextResult();
+ }
+ if (eof == -1) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pScanTrans);
+
+ if (found == 0)
+ return NDBT_OK;
+ else
+ return VERIFICATION_FAILED;
+}
+
+
+ int Bank::performValidatePurged(){
+ /**
+ * Make sure there are no TRANSACTIONS older than the oldest
+ * purged GL record
+ *
+ */
+
+ for (int i = 0; i < getNumAccountTypes(); i++){
+ ndbout << "performValidatePurged: " << i << endl;
+ Uint64 oldestGlTime;
+ if (getOldestPurgedGL(i, oldestGlTime) != NDBT_OK){
+ g_err << "getOldestPurgedGL failed" << endl;
+ return NDBT_FAILED;
+ }
+ int result = checkNoTransactionsOlderThan(i, oldestGlTime);
+ if (result != NDBT_OK){
+ g_err << "checkNoTransactionsOlderThan failed" << endl;
+ return result;
+ }
+
+ }
+
+ return NDBT_OK;
+ }
+
+ int Bank::purgeOldGLTransactions(Uint64 currTime, Uint32 age){
+ /**
+ * For each GL record that are older than age and have purged == 0
+ * - delete all TRANSACTIONS belonging to the GL and set purged = 1
+ *
+ *
+ */
+ bool found;
+ int count = 0;
+
+ while(1){
+ count++;
+ if (count > 100)
+ return NDBT_OK;
+
+ // Search for the oldest GL record with purged == 0
+ Uint64 oldestGlTime;
+ Uint32 accountTypeId;
+ if (getOldestNotPurgedGL(oldestGlTime, accountTypeId, found) != NDBT_OK){
+ g_err << "getOldestNotPurgedGL failed" << endl;
+ return NDBT_FAILED;
+ }
+
+
+ if (found == false){
+ // ndbout << "not found" << endl;
+ return NDBT_OK;
+ }
+
+
+// ndbout << "purgeOldGLTransactions" << endl
+// << " oldestGlTime = " << oldestGlTime << endl
+// << " currTime = " << currTime << endl
+// << " age = " << age << endl;
+ // Check if this GL is old enough to be purged
+ if ((currTime < age) || (oldestGlTime > (currTime-age))){
+ // ndbout << "is not old enough" << endl;
+ return NDBT_OK;
+ }
+
+ if (purgeTransactions(oldestGlTime, accountTypeId) != NDBT_OK){
+ g_err << "purgeTransactions failed" << endl;
+ return NDBT_FAILED;
+ }
+ }
+ g_err << "abnormal return" << endl;
+ return NDBT_FAILED;
+ }
+
+
+int Bank::purgeTransactions(const Uint64 glTime,
+ const Uint32 accountTypeId)
+{
+ int check;
+ g_info << "purgeTransactions: " << glTime << ", "<<accountTypeId<<endl;
+ NdbConnection* pTrans = m_ndb.startTransaction();
+ if (pTrans == NULL){
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Start by updating the GL record with purged = 1, use NoCommit
+ NdbOperation* pOp = pTrans->getNdbOperation("GL");
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->updateTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("TIME", glTime);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("ACCOUNT_TYPE", accountTypeId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ Uint32 purged = 1;
+ check = pOp->setValue("PURGED", purged);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Execute transaction
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Find all transactions and take over them for delete
+
+ if(findTransactionsToPurge(glTime,
+ accountTypeId,
+ pTrans) != NDBT_OK){
+ g_err << "findTransactionToPurge failed" << endl;
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+
+
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_OK;
+}
+
+
+int Bank::findTransactionsToPurge(const Uint64 glTime,
+ const Uint32 accountType,
+ NdbConnection* pTrans){
+ int check;
+
+ NdbConnection* pScanTrans = m_ndb.startTransaction();
+ if (pScanTrans == NULL) {
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("TRANSACTION");
+ if (pOp == NULL) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuplesExclusive() ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* timeRec = pOp->getValue("TIME");
+ if( timeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
+ if( accountTypeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pScanTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+ int rowsFound = 0;
+ eof = pOp->nextResult();
+
+ while(eof == 0){
+ rows++;
+ Uint64 t = timeRec->u_64_value();
+ Uint32 a = accountTypeRec->u_32_value();
+
+ if (a == accountType && t == glTime){
+ rowsFound++;
+ // One record found
+ check = pOp->deleteCurrentTuple(pTrans);
+ if (check == -1){
+ ERR(m_ndb.getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ // Execute transaction
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+ }
+ eof = pOp->nextResult();
+ }
+ if (eof == -1) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pScanTrans);
+ // ndbout << rowsFound << " TRANSACTIONS have been deleted" << endl;
+
+ return NDBT_OK;
+
+}
+
+
+ int Bank::performIncreaseTime(int maxSleepBetweenDays, int yield){
+ if (init() != NDBT_OK)
+ return NDBT_FAILED;
+
+ int yieldCounter = 0;
+
+ while(1){
+
+ while(m_ndb.waitUntilReady(10) != 0)
+ ndbout << "Waiting for ndb to be ready" << endl;
+
+ while(1){
+
+ Uint64 currTime;
+ if (incCurrTime(currTime) != NDBT_OK)
+ break;
+
+ g_info << "Current time is " << currTime << endl;
+ if (maxSleepBetweenDays > 0){
+ int val = myRandom48(maxSleepBetweenDays);
+ NdbSleep_SecSleep(val);
+ }
+
+ yieldCounter++;
+ if (yield != 0 && yieldCounter >= yield)
+ return NDBT_OK;
+
+ }
+ }
+ return NDBT_FAILED;
+ }
+
+
+
+int Bank::readSystemValue(SystemValueId sysValId, Uint64 & value){
+
+ int check;
+
+ NdbConnection* pTrans = m_ndb.startTransaction();
+ if (pTrans == NULL){
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ if (prepareReadSystemValueOp(pTrans, sysValId, value) != NDBT_OK) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_OK;
+
+}
+
+int Bank::prepareReadSystemValueOp(NdbConnection* pTrans, SystemValueId sysValId, Uint64 & value){
+
+ int check;
+
+ NdbOperation* pOp = pTrans->getNdbOperation("SYSTEM_VALUES");
+ if (pOp == NULL) {
+ return NDBT_FAILED;
+ }
+
+ check = pOp->readTuple();
+ if( check == -1 ) {
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("SYSTEM_VALUES_ID", sysValId);
+ if( check == -1 ) {
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* valueRec = pOp->getValue("VALUE", (char *)&value);
+ if( valueRec == NULL ) {
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int Bank::writeSystemValue(SystemValueId sysValId, Uint64 value){
+
+ int check;
+
+ NdbConnection* pTrans = m_ndb.startTransaction();
+ if (pTrans == NULL){
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbOperation* pOp = pTrans->getNdbOperation("SYSTEM_VALUES");
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->insertTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("SYSTEM_VALUES_ID", sysValId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->setValue("VALUE", value);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_OK;
+
+}
+
+int Bank::getNextTransactionId(Uint64 &value){
+ return increaseSystemValue2(LastTransactionId, value);
+}
+
+int Bank::incCurrTime(Uint64 &value){
+ return increaseSystemValue(CurrentTime, value);
+}
+
+
+int Bank::increaseSystemValue(SystemValueId sysValId, Uint64 &value){
+ /**
+ * Increase value with one and return
+ * updated value
+ *
+ */
+
+ DBUG_ENTER("Bank::increaseSystemValue");
+
+ int check;
+
+ NdbConnection* pTrans = m_ndb.startTransaction();
+ if (pTrans == NULL){
+ ERR(m_ndb.getNdbError());
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ NdbOperation* pOp = pTrans->getNdbOperation("SYSTEM_VALUES");
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ check = pOp->readTupleExclusive();
+ // check = pOp->readTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ check = pOp->equal("SYSTEM_VALUES_ID", sysValId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ NdbRecAttr* valueRec = pOp->getValue("VALUE");
+ if( valueRec ==NULL ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ value = valueRec->u_64_value();
+ value++;
+
+ NdbOperation* pOp2 = pTrans->getNdbOperation("SYSTEM_VALUES");
+ if (pOp2 == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ check = pOp2->updateTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ check = pOp2->equal("SYSTEM_VALUES_ID", sysValId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ check = pOp2->setValue("VALUE", value);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ NdbOperation* pOp3 = pTrans->getNdbOperation("SYSTEM_VALUES");
+ if (pOp3 == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ check = pOp3->readTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ check = pOp3->equal("SYSTEM_VALUES_ID", sysValId);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ // Read new value
+ NdbRecAttr* valueNewRec = pOp3->getValue("VALUE");
+ if( valueNewRec ==NULL ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ // Check that value updated equals the value we read after the update
+ if (valueNewRec->u_64_value() != value){
+
+ printf("value actual=%lld\n", valueNewRec->u_64_value());
+ printf("value expected=%lld actual=%lld\n", value, valueNewRec->u_64_value());
+
+ DBUG_PRINT("info", ("value expected=%ld actual=%ld", value, valueNewRec->u_64_value()));
+ g_err << "getNextTransactionId: value was not updated" << endl;
+ m_ndb.closeTransaction(pTrans);
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ m_ndb.closeTransaction(pTrans);
+
+ DBUG_RETURN(0);
+}
+
+int Bank::increaseSystemValue2(SystemValueId sysValId, Uint64 &value){
+ /**
+ * Increase value with one and return
+ * updated value
+ * A more optimized version using interpreted update!
+ *
+ */
+
+ int check;
+
+ NdbConnection* pTrans = m_ndb.startTransaction();
+ if (pTrans == NULL){
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbOperation* pOp = pTrans->getNdbOperation("SYSTEM_VALUES");
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpretedUpdateTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("SYSTEM_VALUES_ID", sysValId );
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ Uint32 valToIncWith = 1;
+ check = pOp->incValue("VALUE", valToIncWith);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* valueRec = pOp->getValue("VALUE");
+ if( valueRec == NULL ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ value = valueRec->u_64_value();
+
+ m_ndb.closeTransaction(pTrans);
+
+ return 0;
+
+}
+
+
+
+int Bank::getCurrTime(Uint64 &time){
+ return readSystemValue(CurrentTime, time);
+}
+
+int Bank::prepareGetCurrTimeOp(NdbConnection *pTrans, Uint64 &time){
+ return prepareReadSystemValueOp(pTrans, CurrentTime, time);
+}
+
+
+int Bank::performSumAccounts(int maxSleepBetweenSums, int yield){
+ if (init() != NDBT_OK)
+ return NDBT_FAILED;
+
+ int yieldCounter = 0;
+
+ while (1){
+
+ while (m_ndb.waitUntilReady(10) != 0)
+ ndbout << "Waiting for ndb to be ready" << endl;
+
+ Uint32 sumAccounts = 0;
+ Uint32 numAccounts = 0;
+ if (getSumAccounts(sumAccounts, numAccounts) != NDBT_OK){
+ g_err << "getSumAccounts FAILED" << endl;
+ } else {
+
+ g_info << "num="<<numAccounts<<", sum=" << sumAccounts << endl;
+
+ if (sumAccounts != (10000000 + (10000*(numAccounts-1)))){
+ g_err << "performSumAccounts FAILED" << endl
+ << " sumAccounts="<<sumAccounts<<endl
+ << " expected ="<<(10000000 + (10000*(numAccounts-1)))<<endl
+ << " numAccounts="<<numAccounts<<endl;
+ return NDBT_FAILED;
+ }
+
+ if (maxSleepBetweenSums > 0){
+ int val = myRandom48(maxSleepBetweenSums);
+ NdbSleep_MilliSleep(val);
+ }
+ }
+
+ yieldCounter++;
+ if (yield != 0 && yieldCounter >= yield)
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+}
+
+
+int Bank::getSumAccounts(Uint32 &sumAccounts,
+ Uint32 &numAccounts){
+
+ // SELECT SUM(balance) FROM ACCOUNT
+
+ int check;
+ NdbConnection* pScanTrans = m_ndb.startTransaction();
+ if (pScanTrans == NULL) {
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("ACCOUNT");
+ if (pOp == NULL) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuplesExclusive() ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* balanceRec = pOp->getValue("BALANCE");
+ if( balanceRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pScanTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbConnection* pTrans = m_ndb.startTransaction();
+ if (pTrans == NULL) {
+ ERR(m_ndb.getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ eof = pOp->nextResult();
+
+ while(eof == 0){
+ Uint32 b = balanceRec->u_32_value();
+
+ sumAccounts += b;
+ numAccounts++;
+
+ // ndbout << numAccounts << ": balance =" << b
+ // << ", sum="<< sumAccounts << endl;
+
+ // Take over the operation so that the lock is kept in db
+ NdbOperation* pLockOp = pOp->updateCurrentTuple(pTrans);
+ if (pLockOp == NULL){
+ ERR(m_ndb.getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ Uint32 illegalBalance = 99;
+ check = pLockOp->setValue("BALANCE", illegalBalance);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ // Execute transaction
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ eof = pOp->nextResult();
+ }
+ if (eof == -1) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ // TODO Forget about rolling back, just close pTrans!!
+
+ // Rollback transaction
+ check = pTrans->execute(Rollback);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pScanTrans);
+ m_ndb.closeTransaction(pTrans);
+
+
+ return NDBT_OK;
+
+}
diff --git a/storage/ndb/test/ndbapi/bank/Bank.hpp b/storage/ndb/test/ndbapi/bank/Bank.hpp
new file mode 100644
index 00000000000..d9dd7b25944
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bank/Bank.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 BANK_HPP
+#define BANK_HPP
+
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NDBT.hpp>
+#include <NdbTick.h>
+#include <random.h>
+
+
+class Bank {
+public:
+
+ Bank(Ndb_cluster_connection&);
+
+ int createAndLoadBank(bool overWrite, int num_accounts=10);
+ int dropBank();
+
+ int performTransactions(int maxSleepBetweenTrans = 20, int yield=0);
+ int performMakeGLs(int yield=0);
+ int performValidateAllGLs();
+ int performSumAccounts(int maxSleepBetweenSums = 2000, int yield=0);
+ int performIncreaseTime(int maxSleepBetweenDays = 30, int yield=0);
+private:
+
+ int init();
+
+ enum TransactionTypes{
+ WithDrawal = 2000,
+ Deposit = 3000
+ };
+
+ static const int NOT_ENOUGH_FUNDS = 1000;
+ static const int VERIFICATION_FAILED = 1001;
+
+ int performTransaction();
+ int performTransaction(int fromAccountId,
+ int toAccountId,
+ int amount );
+ int performTransactionImpl1(int fromAccountId,
+ int toAccountId,
+ int amount );
+
+ int performValidateGLs(Uint64 age = 20);
+ int performValidateGL(Uint64 GLTime);
+ int performValidatePurged();
+
+ int performMakeGL(int time);
+ int performMakeGLForAccountType(NdbConnection* pTrans,
+ Uint64 time,
+ Uint32 accountTypeId);
+ int sumTransactionsForGL(const Uint64 time,
+ const Uint32 accountType,
+ Uint32& balance,
+ Uint32& withdrawalCount,
+ Uint32& withdrawalSum,
+ Uint32& depositSum,
+ Uint32& depositCount,
+ Uint32& transactionsCount,
+ NdbConnection* pTrans);
+ int getBalanceForAccountType(const Uint32 accountType,
+ Uint32& balance);
+ int getBalanceForGL(const Uint64 glTime,
+ const Uint32 accountType,
+ Uint32 &balance);
+
+ int checkNoTransactionsOlderThan(const Uint32 accountType,
+ const Uint64 oldest);
+ int getOldestPurgedGL(const Uint32 accountType,
+ Uint64 &oldest);
+ int getOldestNotPurgedGL(Uint64 &oldest,
+ Uint32 &accountTypeId,
+ bool &found);
+ int findLastGL(Uint64 &lastTime);
+ int purgeOldGLTransactions(Uint64 currTime, Uint32 age);
+
+ int purgeTransactions(const Uint64 glTime,
+ const Uint32 accountTypeId);
+ int findTransactionsToPurge(const Uint64 glTime,
+ const Uint32 accountType,
+ NdbConnection* pTrans);
+
+
+ int getSumAccounts(Uint32 &sumAccounts,
+ Uint32 &numAccounts);
+ int getNumAccounts();
+ int getNumAccountTypes();
+ int getMaxAmount();
+
+
+ enum SystemValueId {
+ LastTransactionId = 0,
+ CurrentTime = 1
+ };
+
+
+ int readSystemValue(SystemValueId sysValId, Uint64 & value);
+ int increaseSystemValue(SystemValueId sysValId, Uint64 &value);
+ int increaseSystemValue2(SystemValueId sysValId, Uint64 &value);
+ int writeSystemValue(SystemValueId sysValId, Uint64 value);
+ int getNextTransactionId(Uint64 &value);
+ int incCurrTime(Uint64 &value);
+ int getCurrTime(Uint64 &time);
+
+ int prepareReadSystemValueOp(NdbConnection*, SystemValueId sysValId, Uint64 &time);
+ int prepareGetCurrTimeOp(NdbConnection*, Uint64 &time);
+
+ int createTables();
+ int createTable(const char* tabName);
+
+ int dropTables();
+ int dropTable(const char* tabName);
+
+ int clearTables();
+ int clearTable(const char* tabName);
+
+ int loadGl();
+ int loadAccountType();
+ int loadAccount (int numAccounts);
+ int loadSystemValues();
+
+private:
+
+ Ndb m_ndb;
+ int m_maxAccount;
+ bool m_initialized;
+};
+
+#endif
diff --git a/storage/ndb/test/ndbapi/bank/BankLoad.cpp b/storage/ndb/test/ndbapi/bank/BankLoad.cpp
new file mode 100644
index 00000000000..34947019a51
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bank/BankLoad.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 */
+
+#include "Bank.hpp"
+#include <UtilTransactions.hpp>
+
+/**
+ * Default account types
+ *
+ */
+struct AccountTypesStruct {
+ int id;
+ const char* descr;
+};
+const AccountTypesStruct accountTypes[] = {
+ { 0, "KASSA"},
+ { 1, "BANKOMAT"},
+ { 2, "POSTGIRO"},
+ { 3, "LÖNEKONTO"},
+ { 4, "SPARKONTO"}
+};
+
+const int
+accountTypesSize = sizeof(accountTypes)/sizeof(AccountTypesStruct);
+
+
+const char* tableNames[] = {
+ "GL",
+ "ACCOUNT",
+ "SYSTEM_VALUES",
+ "TRANSACTION",
+ "ACCOUNT_TYPE"
+};
+
+const int
+tableNamesSize = sizeof(tableNames)/sizeof(const char*);
+
+
+int Bank::getNumAccountTypes(){
+ return accountTypesSize;
+}
+
+int Bank::createAndLoadBank(bool ovrWrt, int num_accounts){
+
+ m_ndb.init();
+ if (m_ndb.waitUntilReady() != 0)
+ return NDBT_FAILED;
+
+ const NdbDictionary::Table* pSysValTab =
+ m_ndb.getDictionary()->getTable("SYSTEM_VALUES");
+ if (pSysValTab != NULL){
+ // The table exists
+ if (ovrWrt == false){
+ ndbout << "Bank already exist and overwrite == false" << endl;
+ return NDBT_FAILED;
+ }
+ }
+
+ if (createTables() != NDBT_OK)
+ return NDBT_FAILED;
+
+ if (clearTables() != NDBT_OK)
+ return NDBT_FAILED;
+
+ if (loadAccountType() != NDBT_OK)
+ return NDBT_FAILED;
+
+ if (loadAccount(num_accounts) != NDBT_OK)
+ return NDBT_FAILED;
+
+ if (loadSystemValues() != NDBT_OK)
+ return NDBT_FAILED;
+
+ if (loadGl() != NDBT_OK)
+ return NDBT_FAILED;
+
+ return NDBT_OK;
+
+}
+
+int Bank::dropBank(){
+
+ m_ndb.init();
+ if (m_ndb.waitUntilReady() != 0)
+ return NDBT_FAILED;
+
+ if (dropTables() != NDBT_OK)
+ return NDBT_FAILED;
+
+ return NDBT_OK;
+
+}
+
+int Bank::createTables(){
+ for (int i = 0; i < tableNamesSize; i++){
+ if (createTable(tableNames[i]) != NDBT_OK)
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+
+int Bank::dropTables(){
+ for (int i = 0; i < tableNamesSize; i++){
+ if (dropTable(tableNames[i]) != NDBT_OK)
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int Bank::clearTables(){
+ for (int i = 0; i < tableNamesSize; i++){
+ if (clearTable(tableNames[i]) != NDBT_OK)
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int Bank::clearTable(const char* tabName){
+ UtilTransactions util(&m_ndb, tabName);
+ if(util.clearTable(&m_ndb, 64) != 0)
+ return NDBT_FAILED;
+ return NDBT_OK;
+}
+
+int Bank::createTable(const char* tabName){
+ ndbout << "createTable " << tabName << endl;
+
+ const NdbDictionary::Table* pTab = NDBT_Tables::getTable(tabName);
+ if (pTab == NULL)
+ return NDBT_FAILED;
+
+ const NdbDictionary::Table* org =
+ m_ndb.getDictionary()->getTable(tabName);
+
+ if (org != 0 && pTab->equal(* org)){
+ return NDBT_OK;
+ }
+
+ if (org != 0){
+ ndbout << "Different table with same name exists" << endl;
+ return NDBT_FAILED;
+ }
+
+ if(m_ndb.getDictionary()->createTable(* pTab) == -1){
+ ndbout << "Failed to create table: " <<
+ m_ndb.getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int Bank::dropTable(const char* tabName){
+ const NdbDictionary::Table* org =
+ m_ndb.getDictionary()->getTable(tabName);
+
+ if (org == NULL)
+ return NDBT_OK;
+
+ ndbout << "dropTable " <<tabName<<endl;
+ if (m_ndb.getDictionary()->dropTable(tabName) != 0){
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+
+
+
+
+
+
+
+/**
+ * Load SYSTEM_VALUES table
+ * This table keeps track of system wide settings
+ * For example:
+ * - next transaction id
+ *
+ */
+int Bank::loadSystemValues (){
+int result;
+
+/**
+ * Insert start value for next transaction id
+ *
+ */
+result = writeSystemValue(LastTransactionId, 0);
+
+/**
+ * Insert start value for current time
+ *
+ */
+result = writeSystemValue(CurrentTime, 1);
+
+return result;
+
+}
+
+
+/**
+ * Load GL table
+ *
+ * Insert GL records for time = 0 with balance 0
+ */
+int Bank::loadGl(){
+ g_info << "loadGl" << endl;
+ int check;
+
+ NdbConnection* pTrans = m_ndb.startTransaction();
+ if (pTrans == NULL){
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ for (int i = 0; i < getNumAccountTypes(); i++){
+
+ NdbOperation* pOp = pTrans->getNdbOperation("GL");
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->insertTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ Uint64 time = 0;
+ check = pOp->equal("TIME", time);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("ACCOUNT_TYPE", i);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ Uint32 balance = 0;
+ if (getBalanceForAccountType(i, balance) != NDBT_OK){
+ return NDBT_FAILED;
+ }
+
+ check = pOp->setValue("BALANCE", balance);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ Uint32 depositCount = 0;
+ check = pOp->setValue("DEPOSIT_COUNT", depositCount);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ Uint32 depositSum = 0;
+ check = pOp->setValue("DEPOSIT_SUM", depositSum);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ Uint32 withdrawalCount = 0;
+ check = pOp->setValue("WITHDRAWAL_COUNT", withdrawalCount);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ Uint32 withdrawalSum = 0;
+ check = pOp->setValue("WITHDRAWAL_SUM", withdrawalSum);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ Uint32 purged = 1;
+ check = pOp->setValue("PURGED", purged);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ }
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_OK;
+}
+
+
+int Bank::getBalanceForAccountType(const Uint32 accountType,
+ Uint32& balance){
+ int check;
+ g_info << "getBalanceForAccountType: accountType="<<accountType<<endl;
+
+ NdbConnection* pScanTrans = m_ndb.startTransaction();
+ if (pScanTrans == NULL) {
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("ACCOUNT");
+ if (pOp == NULL) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples() ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
+ if( accountTypeRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* balanceRec = pOp->getValue("BALANCE");
+ if( balanceRec ==NULL ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pScanTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+ eof = pOp->nextResult();
+
+ while(eof == 0){
+ rows++;
+ Uint32 a = accountTypeRec->u_32_value();
+ Uint32 b = balanceRec->u_32_value();
+
+ if (a == accountType){
+ // One record found
+ balance += b;
+ }
+
+ eof = pOp->nextResult();
+ }
+ if (eof == -1) {
+ ERR(pScanTrans->getNdbError());
+ m_ndb.closeTransaction(pScanTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pScanTrans);
+ // ndbout << rows << " rows have been read" << endl;
+
+ return NDBT_OK;
+
+}
+
+/**
+ * Load ACCOUNT_TYPE table
+ *
+ *
+ */
+int Bank::loadAccountType(){
+ g_info << "loadAccountType" << endl;
+ int check;
+
+ NdbConnection* pTrans = m_ndb.startTransaction();
+ if (pTrans == NULL){
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ for (int i = 0; i < getNumAccountTypes(); i++){
+
+ NdbOperation* pOp = pTrans->getNdbOperation("ACCOUNT_TYPE");
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->insertTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("ACCOUNT_TYPE_ID", accountTypes[i].id);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->setValue("DESCRIPTION", accountTypes[i].descr);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_OK;
+}
+
+/**
+ * Load ACCOUNT table
+ *
+ *
+ *
+ */
+int Bank::loadAccount (int numAccounts){
+ g_info << "loadAccount" << endl;
+ int check;
+
+ NdbConnection* pTrans = m_ndb.startTransaction();
+ if (pTrans == NULL){
+ ERR(m_ndb.getNdbError());
+ return NDBT_FAILED;
+ }
+
+ for (int i = 0; i < numAccounts; i++){
+
+ NdbOperation* pOp = pTrans->getNdbOperation("ACCOUNT");
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->insertTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->equal("ACCOUNT_ID", i);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ int owner;
+ if (i == 0)
+ owner = 0;
+ else
+ owner = i + 3000;
+ check = pOp->setValue("OWNER", owner);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ // Load balance so that the bank's account = 0 has 10 millions
+ // and all other accounts have 10000
+ // This set the total balance for the entire bank to
+ // 10000000 + (10000 * numAccounts-1)
+ // Since no money should dissapear from to the bank nor
+ // any money should be added this is a rule that can be checked when
+ // validating the db
+ int balance;
+ if (i == 0){
+ balance = 10000000;
+ } else {
+ balance = 10000;
+ }
+ check = pOp->setValue("BALANCE", balance);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ // TODO - This is how to set a value in a 16, 1 attribute, not so nice?
+ // NOTE - its not even possible to set the value 0 in this column
+ // since that is equal to NULL when casting to char*
+ // check = pOp->setValue("ACCOUNT_TYPE", (const char*)(Uint16)(i/accountTypesSize), 2);
+ // NOTE attribute now changed to be a 32 bit
+
+
+ int accountType;
+ if (i == 0)
+ accountType = 0; // KASSA
+ else
+ accountType = ((i%accountTypesSize) == 0 ? 1 : (i%getNumAccountTypes()));
+ check = pOp->setValue("ACCOUNT_TYPE", accountType);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ m_ndb.closeTransaction(pTrans);
+ return NDBT_OK;
+}
+
+
+int Bank::getNumAccounts(){
+ const NdbDictionary::Table* accountTab =
+ m_ndb.getDictionary()->getTable("ACCOUNT");
+ if (accountTab == NULL){
+ g_err << "Table ACCOUNT does not exist" << endl;
+ return NDBT_FAILED;
+ }
+ UtilTransactions util(*accountTab);
+ if(util.selectCount(&m_ndb, 64, &m_maxAccount) != 0)
+ return NDBT_FAILED;
+ return NDBT_OK;
+}
+
+int Bank::getMaxAmount(){
+ return 10000;
+}
diff --git a/storage/ndb/test/ndbapi/bank/Makefile.am b/storage/ndb/test/ndbapi/bank/Makefile.am
new file mode 100644
index 00000000000..d4f82a7f9c4
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bank/Makefile.am
@@ -0,0 +1,24 @@
+
+ndbtest_PROGRAMS = testBank bankSumAccounts bankValidateAllGLs bankMakeGL bankTransactionMaker bankCreator bankTimer
+
+noinst_LIBRARIES = libbank.a
+
+libbank_a_SOURCES = Bank.cpp BankLoad.cpp Bank.hpp
+
+testBank_SOURCES = testBank.cpp
+bankSumAccounts_SOURCES = bankSumAccounts.cpp
+bankValidateAllGLs_SOURCES = bankValidateAllGLs.cpp
+bankMakeGL_SOURCES = bankMakeGL.cpp
+bankTransactionMaker_SOURCES = bankTransactionMaker.cpp
+bankCreator_SOURCES = bankCreator.cpp
+bankTimer_SOURCES = bankTimer.cpp
+
+LDADD_LOC = $(noinst_LIBRARIES)
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_ndbapitest.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp:
diff --git a/storage/ndb/test/ndbapi/bank/bankCreator.cpp b/storage/ndb/test/ndbapi/bank/bankCreator.cpp
new file mode 100644
index 00000000000..257255babc8
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bank/bankCreator.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 <NdbOut.hpp>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+#include "Bank.hpp"
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "This program will create and load the tables for bank\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Bank bank(con);
+ int overWriteExisting = true;
+ if (bank.createAndLoadBank(overWriteExisting) != NDBT_OK)
+ return NDBT_ProgramExit(NDBT_FAILED);
+ return NDBT_ProgramExit(NDBT_OK);
+
+}
+
+
+
diff --git a/storage/ndb/test/ndbapi/bank/bankMakeGL.cpp b/storage/ndb/test/ndbapi/bank/bankMakeGL.cpp
new file mode 100644
index 00000000000..cf373481e3e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bank/bankMakeGL.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 <ndb_global.h>
+#include <NdbOut.hpp>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+#include "Bank.hpp"
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "This program will make GL records in the bank\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Bank bank(con);
+
+ if (bank.performMakeGLs() != 0)
+ return NDBT_ProgramExit(NDBT_FAILED);
+
+ return NDBT_ProgramExit(NDBT_OK);
+
+}
+
+
+
diff --git a/storage/ndb/test/ndbapi/bank/bankSumAccounts.cpp b/storage/ndb/test/ndbapi/bank/bankSumAccounts.cpp
new file mode 100644
index 00000000000..034f70f8f95
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bank/bankSumAccounts.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 <ndb_global.h>
+#include <NdbOut.hpp>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+#include "Bank.hpp"
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "This program will check the sum of all ACCOUNTS in the bank\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Bank bank(con);
+
+ if (bank.performSumAccounts() != 0)
+ return NDBT_ProgramExit(NDBT_FAILED);
+
+ return NDBT_ProgramExit(NDBT_OK);
+
+}
+
+
+
diff --git a/storage/ndb/test/ndbapi/bank/bankTimer.cpp b/storage/ndb/test/ndbapi/bank/bankTimer.cpp
new file mode 100644
index 00000000000..298f85e1e43
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bank/bankTimer.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 <ndb_global.h>
+
+#include <NdbOut.hpp>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+#include "Bank.hpp"
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+ int _help = 0;
+ int _wait = 30;
+
+ struct getargs args[] = {
+ { "wait", 'w', arg_integer, &_wait, "Max time to wait between days", "secs" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "This program will increase time in the bank\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Bank bank(con);
+
+ if (bank.performIncreaseTime(_wait) != 0)
+ return NDBT_ProgramExit(NDBT_FAILED);
+
+ return NDBT_ProgramExit(NDBT_OK);
+
+}
+
+
+
diff --git a/storage/ndb/test/ndbapi/bank/bankTransactionMaker.cpp b/storage/ndb/test/ndbapi/bank/bankTransactionMaker.cpp
new file mode 100644
index 00000000000..f8e646b6553
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bank/bankTransactionMaker.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 <ndb_global.h>
+
+#include <NdbOut.hpp>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+#include "Bank.hpp"
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+ int _help = 0;
+ int _wait = 20;
+
+ struct getargs args[] = {
+ { "wait", 'w', arg_integer, &_wait, "Time to wait between transactions", "ms" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "This program will perform transactions in the bank\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Bank bank(con);
+
+ if (bank.performTransactions(_wait) != 0)
+ return NDBT_ProgramExit(NDBT_FAILED);
+
+ return NDBT_ProgramExit(NDBT_OK);
+
+}
+
+
+
diff --git a/storage/ndb/test/ndbapi/bank/bankValidateAllGLs.cpp b/storage/ndb/test/ndbapi/bank/bankValidateAllGLs.cpp
new file mode 100644
index 00000000000..0c268121d8a
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bank/bankValidateAllGLs.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 <ndb_global.h>
+
+#include <NdbOut.hpp>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+#include "Bank.hpp"
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "This program will validate all GLs in the bank\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Bank bank(con);
+
+ if (bank.performValidateAllGLs() != 0)
+ return NDBT_ProgramExit(NDBT_FAILED);
+
+ return NDBT_ProgramExit(NDBT_OK);
+
+}
+
+
+
diff --git a/storage/ndb/test/ndbapi/bank/testBank.cpp b/storage/ndb/test/ndbapi/bank/testBank.cpp
new file mode 100644
index 00000000000..6be66d528b1
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bank/testBank.cpp
@@ -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 */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <NdbBackup.hpp>
+
+
+#define CHECK(b) if (!(b)) { \
+ g_err << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ continue; }
+
+
+#include "Bank.hpp"
+
+int runCreateBank(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank(ctx->m_cluster_connection);
+ int overWriteExisting = true;
+ if (bank.createAndLoadBank(overWriteExisting) != NDBT_OK)
+ return NDBT_FAILED;
+ return NDBT_OK;
+}
+
+int runBankTimer(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank(ctx->m_cluster_connection);
+ int wait = 30; // Max seconds between each "day"
+ int yield = 1; // Loops before bank returns
+
+ while (ctx->isTestStopped() == false) {
+ bank.performIncreaseTime(wait, yield);
+ }
+ return NDBT_OK;
+}
+
+int runBankTransactions(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank(ctx->m_cluster_connection);
+ int wait = 10; // Max ms between each transaction
+ int yield = 100; // Loops before bank returns
+
+ while (ctx->isTestStopped() == false) {
+ bank.performTransactions(wait, yield);
+ }
+ return NDBT_OK;
+}
+
+int runBankGL(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank(ctx->m_cluster_connection);
+ int yield = 20; // Loops before bank returns
+ int result = NDBT_OK;
+
+ while (ctx->isTestStopped() == false) {
+ if (bank.performMakeGLs(yield) != NDBT_OK){
+ ndbout << "bank.performMakeGLs FAILED" << endl;
+ result = NDBT_FAILED;
+ }
+ }
+ return NDBT_OK;
+}
+
+int runBankSum(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank(ctx->m_cluster_connection);
+ int wait = 2000; // Max ms between each sum of accounts
+ int yield = 1; // Loops before bank returns
+ int result = NDBT_OK;
+
+ while (ctx->isTestStopped() == false) {
+ if (bank.performSumAccounts(wait, yield) != NDBT_OK){
+ ndbout << "bank.performSumAccounts FAILED" << endl;
+ result = NDBT_FAILED;
+ }
+ }
+ return result ;
+}
+
+int runDropBank(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank(ctx->m_cluster_connection);
+ if (bank.dropBank() != NDBT_OK)
+ return NDBT_FAILED;
+ return NDBT_OK;
+}
+
+int runBankController(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int l = 0;
+ int result = NDBT_OK;
+
+ while (l < loops && result != NDBT_FAILED){
+
+ if (pNdb->waitUntilReady() != 0){
+ result = NDBT_FAILED;
+ continue;
+ }
+
+ // Sleep for a while
+ NdbSleep_SecSleep(records);
+
+ l++;
+ }
+
+ if (pNdb->waitUntilReady() != 0){
+ result = NDBT_FAILED;
+ }
+
+ ctx->stopTest();
+
+ return result;
+}
+
+NDBT_TESTSUITE(testBank);
+TESTCASE("Bank",
+ "Run the bank\n"){
+ INITIALIZER(runCreateBank);
+ STEP(runBankTimer);
+ STEP(runBankTransactions);
+ STEP(runBankGL);
+ // TODO STEP(runBankSum);
+ STEP(runBankController);
+ FINALIZER(runDropBank);
+
+}
+NDBT_TESTSUITE_END(testBank);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ // Tables should not be auto created
+ testBank.setCreateTable(false);
+
+ return testBank.execute(argc, argv);
+}
+
+
diff --git a/storage/ndb/test/ndbapi/bench/asyncGenerator.cpp b/storage/ndb/test/ndbapi/bench/asyncGenerator.cpp
new file mode 100644
index 00000000000..d91e38dff1a
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/asyncGenerator.cpp
@@ -0,0 +1,571 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 "dbGenerator.h"
+#include <NdbApi.hpp>
+#include <NdbOut.hpp>
+#include <NdbSleep.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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+static void getRandomSubscriberNumber(SubscriberNumber number);
+static void getRandomServerId(ServerId *serverId);
+static void getRandomChangedBy(ChangedBy changedBy);
+static void getRandomChangedTime(ChangedTime changedTime);
+
+static void clearTransaction(TransactionDefinition *trans);
+static void initGeneratorStatistics(GeneratorStatistics *gen);
+
+static void doOneTransaction(ThreadData * td,
+ int parallellism,
+ int millisSendPoll,
+ int minEventSendPoll,
+ int forceSendPoll);
+static void doTransaction_T1(Ndb * pNDB, ThreadData * td, int async);
+static void doTransaction_T2(Ndb * pNDB, ThreadData * td, int async);
+static void doTransaction_T3(Ndb * pNDB, ThreadData * td, int async);
+static void doTransaction_T4(Ndb * pNDB, ThreadData * td, int async);
+static void doTransaction_T5(Ndb * pNDB, ThreadData * td, int async);
+
+/***************************************************************
+* L O C A L D A T A *
+***************************************************************/
+
+static SequenceValues transactionDefinition[] = {
+ {25, 1},
+ {25, 2},
+ {20, 3},
+ {15, 4},
+ {15, 5},
+ {0, 0}
+};
+
+static SequenceValues rollbackDefinition[] = {
+ {98, 0},
+ {2 , 1},
+ {0, 0}
+};
+
+static int maxsize = 0;
+
+/***************************************************************
+* 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 getRandomSubscriberNumber(SubscriberNumber number)
+{
+ uint32 tmp;
+ char sbuf[SUBSCRIBER_NUMBER_LENGTH + 1];
+ tmp = myRandom48(NO_OF_SUBSCRIBERS);
+ sprintf(sbuf, "%.*d", SUBSCRIBER_NUMBER_LENGTH, tmp);
+ memcpy(number, sbuf, SUBSCRIBER_NUMBER_LENGTH);
+}
+
+static void getRandomServerId(ServerId *serverId)
+{
+ *serverId = myRandom48(NO_OF_SERVERS);
+}
+
+static void getRandomChangedBy(ChangedBy changedBy)
+{
+ memset(changedBy, myRandom48(26)+'A', CHANGED_BY_LENGTH);
+ changedBy[CHANGED_BY_LENGTH] = 0;
+}
+
+static void getRandomChangedTime(ChangedTime changedTime)
+{
+ memset(changedTime, myRandom48(26)+'A', CHANGED_TIME_LENGTH);
+ changedTime[CHANGED_TIME_LENGTH] = 0;
+}
+
+static void clearTransaction(TransactionDefinition *trans)
+{
+ trans->count = 0;
+ trans->branchExecuted = 0;
+ trans->rollbackExecuted = 0;
+ trans->latencyCounter = myRandom48(127);
+ trans->latency.reset();
+}
+
+static int listFull(SessionList *list)
+{
+ return(list->numberInList == SESSION_LIST_LENGTH);
+}
+
+static int listEmpty(SessionList *list)
+{
+ return(list->numberInList == 0);
+}
+
+static void insertSession(SessionList *list,
+ SubscriberNumber number,
+ ServerId serverId)
+{
+ SessionElement *e;
+ if( listFull(list) ) return;
+
+ e = &list->list[list->writeIndex];
+
+ strcpy(e->subscriberNumber, number);
+ e->serverId = serverId;
+
+ list->writeIndex = (list->writeIndex + 1) % SESSION_LIST_LENGTH;
+ list->numberInList++;
+
+ if( list->numberInList > maxsize )
+ maxsize = list->numberInList;
+}
+
+static SessionElement *getNextSession(SessionList *list)
+{
+ if( listEmpty(list) ) return(0);
+
+ return(&list->list[list->readIndex]);
+}
+
+static void deleteSession(SessionList *list)
+{
+ if( listEmpty(list) ) return;
+
+ list->readIndex = (list->readIndex + 1) % SESSION_LIST_LENGTH;
+ list->numberInList--;
+}
+
+static void initGeneratorStatistics(GeneratorStatistics *gen)
+{
+ int i;
+
+ if( initSequence(&gen->transactionSequence,
+ transactionDefinition) != 0 ) {
+ ndbout_c("could not set the transaction types");
+ exit(0);
+ }
+
+ if( initSequence(&gen->rollbackSequenceT4,
+ rollbackDefinition) != 0 ) {
+ ndbout_c("could not set the rollback sequence");
+ exit(0);
+ }
+
+ if( initSequence(&gen->rollbackSequenceT5,
+ rollbackDefinition) != 0 ) {
+ ndbout_c("could not set the rollback sequence");
+ exit(0);
+ }
+
+ for(i = 0; i < NUM_TRANSACTION_TYPES; i++ )
+ clearTransaction(&gen->transactions[i]);
+
+ gen->totalTransactions = 0;
+
+ gen->activeSessions.numberInList = 0;
+ gen->activeSessions.readIndex = 0;
+ gen->activeSessions.writeIndex = 0;
+}
+
+
+static
+void
+doOneTransaction(ThreadData * td, int p, int millis, int minEvents, int force)
+{
+ int i;
+ unsigned int transactionType;
+ int async = 1;
+ if (p == 1) {
+ async = 0;
+ }//if
+ for(i = 0; i<p; i++){
+ if(td[i].runState == Runnable){
+ transactionType = getNextRandom(&td[i].generator.transactionSequence);
+
+ switch(transactionType) {
+ case 1:
+ doTransaction_T1(td[i].pNDB, &td[i], async);
+ break;
+ case 2:
+ doTransaction_T2(td[i].pNDB, &td[i], async);
+ break;
+ case 3:
+ doTransaction_T3(td[i].pNDB, &td[i], async);
+ break;
+ case 4:
+ doTransaction_T4(td[i].pNDB, &td[i], async);
+ break;
+ case 5:
+ doTransaction_T5(td[i].pNDB, &td[i], async);
+ break;
+ default:
+ ndbout_c("Unknown transaction type: %d", transactionType);
+ }
+ }
+ }
+ if (async == 1) {
+ td[0].pNDB->sendPollNdb(millis, minEvents, force);
+ }//if
+}
+
+static
+void
+doTransaction_T1(Ndb * pNDB, ThreadData * td, int async)
+{
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ getRandomSubscriberNumber(td->transactionData.number);
+ getRandomChangedBy(td->transactionData.changed_by);
+ BaseString::snprintf(td->transactionData.changed_time,
+ sizeof(td->transactionData.changed_time),
+ "%ld - %d", td->changedTime++, myRandom48(65536*1024));
+ //getRandomChangedTime(td->transactionData.changed_time);
+ td->transactionData.location = td->transactionData.changed_by[0];
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ td->runState = Running;
+ td->generator.transactions[0].startLatency();
+
+ start_T1(pNDB, td, async);
+}
+
+static
+void
+doTransaction_T2(Ndb * pNDB, ThreadData * td, int async)
+{
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ getRandomSubscriberNumber(td->transactionData.number);
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ td->runState = Running;
+ td->generator.transactions[1].startLatency();
+
+ start_T2(pNDB, td, async);
+}
+
+static
+void
+doTransaction_T3(Ndb * pNDB, ThreadData * td, int async)
+{
+ SessionElement *se;
+
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ se = getNextSession(&td->generator.activeSessions);
+ if( se ) {
+ strcpy(td->transactionData.number, se->subscriberNumber);
+ td->transactionData.server_id = se->serverId;
+ td->transactionData.sessionElement = 1;
+ } else {
+ getRandomSubscriberNumber(td->transactionData.number);
+ getRandomServerId(&td->transactionData.server_id);
+ td->transactionData.sessionElement = 0;
+ }
+
+ td->transactionData.server_bit = (1 << td->transactionData.server_id);
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ td->runState = Running;
+ td->generator.transactions[2].startLatency();
+ start_T3(pNDB, td, async);
+}
+
+static
+void
+doTransaction_T4(Ndb * pNDB, ThreadData * td, int async)
+{
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ getRandomSubscriberNumber(td->transactionData.number);
+ getRandomServerId(&td->transactionData.server_id);
+
+ td->transactionData.server_bit = (1 << td->transactionData.server_id);
+ td->transactionData.do_rollback =
+ getNextRandom(&td->generator.rollbackSequenceT4);
+
+#if 0
+ memset(td->transactionData.session_details,
+ myRandom48(26)+'A', SESSION_DETAILS_LENGTH);
+#endif
+ td->transactionData.session_details[SESSION_DETAILS_LENGTH] = 0;
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ td->runState = Running;
+ td->generator.transactions[3].startLatency();
+ start_T4(pNDB, td, async);
+}
+
+static
+void
+doTransaction_T5(Ndb * pNDB, ThreadData * td, int async)
+{
+ SessionElement * se;
+ se = getNextSession(&td->generator.activeSessions);
+ if( se ) {
+ strcpy(td->transactionData.number, se->subscriberNumber);
+ td->transactionData.server_id = se->serverId;
+ td->transactionData.sessionElement = 1;
+ }
+ else {
+ getRandomSubscriberNumber(td->transactionData.number);
+ getRandomServerId(&td->transactionData.server_id);
+ td->transactionData.sessionElement = 0;
+ }
+
+ td->transactionData.server_bit = (1 << td->transactionData.server_id);
+ td->transactionData.do_rollback
+ = getNextRandom(&td->generator.rollbackSequenceT5);
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ td->runState = Running;
+ td->generator.transactions[4].startLatency();
+ start_T5(pNDB, td, async);
+}
+
+void
+complete_T1(ThreadData * data){
+ data->generator.transactions[0].stopLatency();
+ data->generator.transactions[0].count++;
+
+ data->runState = Runnable;
+ data->generator.totalTransactions++;
+}
+
+void
+complete_T2(ThreadData * data){
+ data->generator.transactions[1].stopLatency();
+ data->generator.transactions[1].count++;
+
+ data->runState = Runnable;
+ data->generator.totalTransactions++;
+}
+
+void
+complete_T3(ThreadData * data){
+
+ data->generator.transactions[2].stopLatency();
+ data->generator.transactions[2].count++;
+
+ if(data->transactionData.branchExecuted)
+ data->generator.transactions[2].branchExecuted++;
+
+ data->runState = Runnable;
+ data->generator.totalTransactions++;
+}
+
+void
+complete_T4(ThreadData * data){
+
+ data->generator.transactions[3].stopLatency();
+ data->generator.transactions[3].count++;
+
+ if(data->transactionData.branchExecuted)
+ data->generator.transactions[3].branchExecuted++;
+ if(data->transactionData.do_rollback)
+ data->generator.transactions[3].rollbackExecuted++;
+
+ if(data->transactionData.branchExecuted &&
+ !data->transactionData.do_rollback){
+ insertSession(&data->generator.activeSessions,
+ data->transactionData.number,
+ data->transactionData.server_id);
+ }
+
+ data->runState = Runnable;
+ data->generator.totalTransactions++;
+
+}
+void
+complete_T5(ThreadData * data){
+
+ data->generator.transactions[4].stopLatency();
+ data->generator.transactions[4].count++;
+
+ if(data->transactionData.branchExecuted)
+ data->generator.transactions[4].branchExecuted++;
+ if(data->transactionData.do_rollback)
+ data->generator.transactions[4].rollbackExecuted++;
+
+ if(data->transactionData.sessionElement &&
+ !data->transactionData.do_rollback){
+ deleteSession(&data->generator.activeSessions);
+ }
+
+ data->runState = Runnable;
+ data->generator.totalTransactions++;
+}
+
+/***************************************************************
+****************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+void
+asyncGenerator(ThreadData *data,
+ int parallellism,
+ int millisSendPoll,
+ int minEventSendPoll,
+ int forceSendPoll)
+{
+ ThreadData * startUp;
+
+ GeneratorStatistics *st;
+ double periodStop;
+ double benchTimeStart;
+ double benchTimeEnd;
+ int i, j, done;
+
+ myRandom48Init(data->randomSeed);
+
+ for(i = 0; i<parallellism; i++){
+ initGeneratorStatistics(&data[i].generator);
+ }
+
+ startUp = (ThreadData*)malloc(parallellism * sizeof(ThreadData));
+ memcpy(startUp, data, (parallellism * sizeof(ThreadData)));
+
+ /*----------------*/
+ /* warm up period */
+ /*----------------*/
+ periodStop = userGetTime() + (double)data[0].warmUpSeconds;
+
+ while(userGetTime() < periodStop){
+ doOneTransaction(startUp, parallellism,
+ millisSendPoll, minEventSendPoll, forceSendPoll);
+ }
+
+ ndbout_c("Waiting for startup to finish");
+
+ /**
+ * Wait for all transactions
+ */
+ done = 0;
+ while(!done){
+ done = 1;
+ for(i = 0; i<parallellism; i++){
+ if(startUp[i].runState != Runnable){
+ done = 0;
+ break;
+ }
+ }
+ if(!done){
+ startUp[0].pNDB->sendPollNdb();
+ }
+ }
+ ndbout_c("Benchmark period starts");
+
+ /*-------------------------*/
+ /* normal benchmark period */
+ /*-------------------------*/
+ benchTimeStart = userGetTime();
+
+ periodStop = benchTimeStart + (double)data[0].testSeconds;
+ while(userGetTime() < periodStop)
+ doOneTransaction(data, parallellism,
+ millisSendPoll, minEventSendPoll, forceSendPoll);
+
+ benchTimeEnd = userGetTime();
+
+ ndbout_c("Benchmark period done");
+
+ /**
+ * Wait for all transactions
+ */
+ done = 0;
+ while(!done){
+ done = 1;
+ for(i = 0; i<parallellism; i++){
+ if(data[i].runState != Runnable){
+ done = 0;
+ break;
+ }
+ }
+ if(!done){
+ data[0].pNDB->sendPollNdb();
+ }
+ }
+
+ /*------------------*/
+ /* cool down period */
+ /*------------------*/
+ periodStop = userGetTime() + (double)data[0].coolDownSeconds;
+ while(userGetTime() < periodStop){
+ doOneTransaction(startUp, parallellism,
+ millisSendPoll, minEventSendPoll, forceSendPoll);
+ }
+
+ done = 0;
+ while(!done){
+ done = 1;
+ for(i = 0; i<parallellism; i++){
+ if(startUp[i].runState != Runnable){
+ done = 0;
+ break;
+ }
+ }
+ if(!done){
+ startUp[0].pNDB->sendPollNdb();
+ }
+ }
+
+
+ /*---------------------------------------------------------*/
+ /* add the times for all transaction for inner loop timing */
+ /*---------------------------------------------------------*/
+ for(j = 0; j<parallellism; j++){
+ st = &data[j].generator;
+
+ st->outerLoopTime = benchTimeEnd - benchTimeStart;
+ st->outerTps = getTps(st->totalTransactions, st->outerLoopTime);
+ }
+ /* ndbout_c("maxsize = %d\n",maxsize); */
+
+ free(startUp);
+}
+
diff --git a/storage/ndb/test/ndbapi/bench/dbGenerator.h b/storage/ndb/test/ndbapi/bench/dbGenerator.h
new file mode 100644
index 00000000000..2256498e151
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/dbGenerator.h
@@ -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 DBGENERATOR_H
+#define DBGENERATOR_H
+
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+
+#include "testData.h"
+#include "userInterface.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void asyncGenerator(ThreadData *d, int parallellism,
+ int millisSendPoll,
+ int minEventSendPoll,
+ int forceSendPoll);
+
+#ifdef __cplusplus
+}
+#endif
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+
+
+#endif /* DBGENERATOR_H */
+
diff --git a/storage/ndb/test/ndbapi/bench/dbPopulate.cpp b/storage/ndb/test/ndbapi/bench/dbPopulate.cpp
new file mode 100644
index 00000000000..42fbb52f3b2
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/dbPopulate.cpp
@@ -0,0 +1,244 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 "userInterface.h"
+
+#include "dbPopulate.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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+static void getRandomSubscriberData(int subscriberNo,
+ SubscriberNumber number,
+ SubscriberName name);
+
+static void populate(char *title,
+ int count,
+ void (*func)(UserHandle*,int),
+ UserHandle *uh);
+
+static void populateServers(UserHandle *uh, int count);
+static void populateSubscribers(UserHandle *uh, int count);
+static void populateGroups(UserHandle *uh, int count);
+
+/***************************************************************
+* L O C A L D A T A *
+***************************************************************/
+
+static SequenceValues permissionsDefinition[] = {
+ {90, 1},
+ {10, 0},
+ {0, 0}
+};
+
+/***************************************************************
+* 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 getRandomSubscriberData(int subscriberNo,
+ SubscriberNumber number,
+ SubscriberName name)
+{
+ char sbuf[SUBSCRIBER_NUMBER_LENGTH + 1];
+ sprintf(sbuf, "%.*d", SUBSCRIBER_NUMBER_LENGTH, subscriberNo);
+ memcpy(number, sbuf, SUBSCRIBER_NUMBER_LENGTH);
+
+ memset(name, myRandom48(26)+'A', SUBSCRIBER_NAME_LENGTH);
+}
+
+static void populate(char *title,
+ int count,
+ void (*func)(UserHandle*, int),
+ UserHandle *uh)
+{
+ ndbout_c("Populating %d '%s' ... ",count, title);
+ /* fflush(stdout); */
+ func(uh,count);
+ ndbout_c("done");
+}
+
+static void populateServers(UserHandle *uh, int count)
+{
+ int i, j;
+ int len;
+ char tmp[80];
+ int suffix_length = 1;
+ ServerName serverName;
+ SubscriberSuffix suffix;
+
+ int commitCount = 0;
+
+ for(i = 0; i < SUBSCRIBER_NUMBER_SUFFIX_LENGTH; i++)
+ suffix_length *= 10;
+
+ for(i = 0; i < count; i++) {
+ sprintf(tmp, "-Server %d-", i);
+
+ len = strlen(tmp);
+ for(j = 0; j < SERVER_NAME_LENGTH; j++){
+ serverName[j] = tmp[j % len];
+ }
+ /* serverName[j] = 0; not null-terminated */
+
+ for(j = 0; j < suffix_length; j++){
+ char sbuf[SUBSCRIBER_NUMBER_SUFFIX_LENGTH + 1];
+ sprintf(sbuf, "%.*d", SUBSCRIBER_NUMBER_SUFFIX_LENGTH, j);
+ memcpy(suffix, sbuf, SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+ userDbInsertServer(uh, i, suffix, serverName);
+ commitCount ++;
+ if((commitCount % OP_PER_TRANS) == 0)
+ userDbCommit(uh);
+ }
+ }
+ if((commitCount % OP_PER_TRANS) != 0)
+ userDbCommit(uh);
+}
+
+static void populateSubscribers(UserHandle *uh, int count)
+{
+ SubscriberNumber number;
+ SubscriberName name;
+ int i, j, k;
+ int res;
+
+ SequenceValues values[NO_OF_GROUPS+1];
+ RandomSequence seq;
+
+ for(i = 0; i < NO_OF_GROUPS; i++) {
+ values[i].length = 1;
+ values[i].value = i;
+ }
+
+ values[i].length = 0;
+ values[i].value = 0;
+
+ if( initSequence(&seq, values) != 0 ) {
+ ndbout_c("could not set the sequence of random groups");
+ exit(0);
+ }
+
+#define RETRIES 25
+
+ for(i = 0; i < count; i+= OP_PER_TRANS) {
+ for(j = 0; j<RETRIES; j++){
+ for(k = 0; k<OP_PER_TRANS && i+k < count; k++){
+ getRandomSubscriberData(i+k, number, name);
+ userDbInsertSubscriber(uh, number, getNextRandom(&seq), name);
+ }
+ res = userDbCommit(uh);
+ if(res == 0)
+ break;
+ if(res != 1){
+ ndbout_c("Terminating");
+ exit(0);
+ }
+ }
+ if(j == RETRIES){
+ ndbout_c("Terminating");
+ exit(0);
+ }
+ }
+}
+
+static void populateGroups(UserHandle *uh, int count)
+{
+ int i;
+ int j;
+ int len;
+ RandomSequence seq;
+ Permission allow[NO_OF_GROUPS];
+ ServerBit serverBit;
+ GroupName groupName;
+ char tmp[80];
+ int commitCount = 0;
+
+ if( initSequence(&seq, permissionsDefinition) != 0 ) {
+ ndbout_c("could not set the sequence of random permissions");
+ exit(0);
+ }
+
+ for(i = 0; i < NO_OF_GROUPS; i++)
+ allow[i] = 0;
+
+ for(i = 0; i < NO_OF_SERVERS; i++) {
+ serverBit = 1 << i;
+
+ for(j = 0; j < NO_OF_GROUPS; j++ ) {
+ if( getNextRandom(&seq) )
+ allow[j] |= serverBit;
+ }
+ }
+
+ for(i = 0; i < NO_OF_GROUPS; i++) {
+ sprintf(tmp, "-Group %d-", i);
+
+ len = strlen(tmp);
+
+ for(j = 0; j < GROUP_NAME_LENGTH; j++) {
+ groupName[j] = tmp[j % len];
+ }
+ /* groupName[j] = 0; not null-terminated */
+
+ userDbInsertGroup(uh,
+ i,
+ groupName,
+ allow[i],
+ allow[i],
+ allow[i]);
+ commitCount ++;
+ if((commitCount % OP_PER_TRANS) == 0)
+ userDbCommit(uh);
+ }
+ if((commitCount % OP_PER_TRANS) != 0)
+ userDbCommit(uh);
+}
+
+/***************************************************************
+****************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+void dbPopulate(UserHandle *uh)
+{
+ populate("servers", NO_OF_SERVERS, populateServers, uh);
+ populate("subscribers", NO_OF_SUBSCRIBERS, populateSubscribers, uh);
+ populate("groups", NO_OF_GROUPS, populateGroups, uh);
+}
diff --git a/storage/ndb/test/ndbapi/bench/dbPopulate.h b/storage/ndb/test/ndbapi/bench/dbPopulate.h
new file mode 100644
index 00000000000..1916720e141
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/dbPopulate.h
@@ -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 DBPOPULATE_H
+#define DBPOPULATE_H
+
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+
+#include "userInterface.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void dbPopulate(UserHandle *uh);
+
+#ifdef __cplusplus
+}
+#endif
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+
+
+#endif /* DBPOPULATE_H */
+
diff --git a/storage/ndb/test/ndbapi/bench/macros.h b/storage/ndb/test/ndbapi/bench/macros.h
new file mode 100644
index 00000000000..22b7f564490
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/macros.h
@@ -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 MACROS_H
+#define MACROS_H
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+
+#define ERROR(x) {ndbout_c((x));}
+#define ERROR1(x,y) {ndbout_c((x), (y));}
+#define ERROR2(x,y,z) {ndbout_c((x), (y), (z));}
+#define ERROR3(x,y,z,u) {ndbout_c((x), (y), (z), (u));}
+#define ERROR4(x,y,z,u,w) {ndbout_c((x), (y), (z), (u), (w));}
+
+#define INIT_RANDOM(x) srand48((x))
+#define UI_RANDOM(x) ((unsigned int)(lrand48()%(x)))
+
+#define ASSERT(cond, message) \
+ { if(!(cond)) { ERROR(message); exit(-1); }}
+
+#ifdef DEBUG_ON
+#define DEBUG(x) {ndbout_c((x));}
+#define DEBUG1(x,y) {ndbout_c((x), (y));}
+#define DEBUG2(x,y,z) {ndbout_c((x), (y), (z));}
+#define DEBUG3(x,y,z,u) {ndbout_c((x), (y), (z), (u));}
+#define DEBUG4(x,y,z,u,w) {ndbout_c((x), (y), (z), (u), (w));}
+#define DEBUG5(x,y,z,u,w, v) {ndbout_c((x), (y), (z), (u), (w), (v));}
+#else
+#define DEBUG(x)
+#define DEBUG1(x,y)
+#define DEBUG2(x,y,z)
+#define DEBUG3(x,y,z,u)
+#define DEBUG4(x,y,z,u,w)
+#define DEBUG5(x,y,z,u,w, v)
+#endif
+
+#endif
diff --git a/storage/ndb/test/ndbapi/bench/mainAsyncGenerator.cpp b/storage/ndb/test/ndbapi/bench/mainAsyncGenerator.cpp
new file mode 100644
index 00000000000..828b924582f
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/mainAsyncGenerator.cpp
@@ -0,0 +1,503 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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>
+#include <NdbSleep.h>
+#include <NdbThread.h>
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbEnv.h>
+#include <NdbTest.hpp>
+
+#include "userInterface.h"
+#include "dbGenerator.h"
+
+static int numProcesses;
+static int numSeconds;
+static int numWarmSeconds;
+static int parallellism;
+static int millisSendPoll;
+static int minEventSendPoll;
+static int forceSendPoll;
+
+static ThreadData *data;
+static Ndb_cluster_connection *g_cluster_connection= 0;
+
+
+static void usage(const char *prog)
+{
+ const char *progname;
+
+ /*--------------------------------------------*/
+ /* Get the name of the program (without path) */
+ /*--------------------------------------------*/
+ progname = strrchr(prog, '/');
+
+ if (progname == 0)
+ progname = prog;
+ else
+ ++progname;
+
+ ndbout_c(
+ "Usage: %s [-proc <num>] [-warm <num>] [-time <num>] [ -p <num>] "
+ "[-t <num> ] [ -e <num> ] [ -f <num>] \n"
+ " -proc <num> Specifies that <num> is the number of\n"
+ " threads. The default is 1.\n"
+ " -time <num> Specifies that the test will run for <num> sec.\n"
+ " The default is 10 sec\n"
+ " -warm <num> Specifies the warm-up/cooldown period of <num> "
+ "sec.\n"
+ " The default is 10 sec\n"
+ " -p <num> The no of parallell transactions started by "
+ "one thread\n"
+ " -e <num> Minimum no of events before wake up in call to "
+ "sendPoll\n"
+ " Default is 1\n"
+ " -f <num> force parameter to sendPoll\n"
+ " Default is 0\n",
+ progname);
+}
+
+static
+int
+parse_args(int argc, const char **argv)
+{
+ int i;
+
+ numProcesses = 1;
+ numSeconds = 10;
+ numWarmSeconds = 10;
+ parallellism = 1;
+ millisSendPoll = 10000;
+ minEventSendPoll = 1;
+ forceSendPoll = 0;
+
+
+ i = 1;
+ while (i < argc){
+ if (strcmp("-proc",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ return 1;
+ }
+ if (sscanf(argv[i+1], "%d", &numProcesses) == -1 ||
+ numProcesses <= 0 || numProcesses > 127) {
+ ndbout_c("-proc flag requires a positive integer argument [1..127]");
+ return 1;
+ }
+ i += 2;
+ } else if (strcmp("-p", argv[i]) == 0){
+ if(i + 1 >= argc){
+ usage(argv[0]);
+ return 1;
+ }
+ if (sscanf(argv[i+1], "%d", &parallellism) == -1 ||
+ parallellism <= 0){
+ ndbout_c("-p flag requires a positive integer argument");
+ return 1;
+ }
+ i += 2;
+ }
+ else if (strcmp("-time",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ return 1;
+ }
+ if (sscanf(argv[i+1], "%d", &numSeconds) == -1 ||
+ numSeconds < 0) {
+ ndbout_c("-time flag requires a positive integer argument");
+ return 1;
+ }
+ i += 2;
+ }
+ else if (strcmp("-warm",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ return 1;
+ }
+ if (sscanf(argv[i+1], "%d", &numWarmSeconds) == -1 ||
+ numWarmSeconds < 0) {
+ ndbout_c("-warm flag requires a positive integer argument");
+ return 1;
+ }
+ i += 2;
+ }
+ else if (strcmp("-e",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ return 1;
+ }
+ if (sscanf(argv[i+1], "%d", &minEventSendPoll) == -1 ||
+ minEventSendPoll < 0) {
+ ndbout_c("-e flag requires a positive integer argument");
+ return 1;
+ }
+ i += 2;
+ }
+ else if (strcmp("-f",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ usage(argv[0]);
+ return 1;
+ }
+ if (sscanf(argv[i+1], "%d", &forceSendPoll) == -1 ||
+ forceSendPoll < 0) {
+ ndbout_c("-f flag requires a positive integer argument");
+ return 1;
+ }
+ i += 2;
+ }
+ else {
+ return 1;
+ }
+ }
+
+ if(minEventSendPoll > parallellism){
+ ndbout_c("minEventSendPoll(%d) > parallellism(%d)",
+ minEventSendPoll, parallellism);
+ ndbout_c("not very good...");
+ ndbout_c("very bad...");
+ ndbout_c("exiting...");
+ return 1;
+ }
+ return 0;
+}
+
+static
+void
+print_transaction(const char *header,
+ unsigned long totalCount,
+ TransactionDefinition *trans,
+ unsigned int printBranch,
+ unsigned int printRollback)
+{
+ double f;
+
+ ndbout_c(" %s: %d (%.2f%%) "
+ "Latency(ms) avg: %d min: %d max: %d std: %d n: %d",
+ header,
+ trans->count,
+ (double)trans->count / (double)totalCount * 100.0,
+ (int)trans->latency.getMean(),
+ (int)trans->latency.getMin(),
+ (int)trans->latency.getMax(),
+ (int)trans->latency.getStddev(),
+ (int)trans->latency.getCount()
+ );
+
+ if( printBranch ){
+ if( trans->count == 0 )
+ f = 0.0;
+ else
+ f = (double)trans->branchExecuted / (double)trans->count * 100.0;
+ ndbout_c(" Branches Executed: %d (%.2f%%)", trans->branchExecuted, f);
+ }
+
+ if( printRollback ){
+ if( trans->count == 0 )
+ f = 0.0;
+ else
+ f = (double)trans->rollbackExecuted / (double)trans->count * 100.0;
+ ndbout_c(" Rollback Executed: %d (%.2f%%)",trans->rollbackExecuted,f);
+ }
+}
+
+void
+print_stats(const char *title,
+ unsigned int length,
+ unsigned int transactionFlag,
+ GeneratorStatistics *gen,
+ int numProc, int parallellism)
+{
+ int i;
+ char buf[10];
+ char name[MAXHOSTNAMELEN];
+
+ name[0] = 0;
+ NdbHost_GetHostName(name);
+
+ ndbout_c("\n------ %s ------",title);
+ ndbout_c("Length : %d %s",
+ length,
+ transactionFlag ? "Transactions" : "sec");
+ ndbout_c("Processor : %s", name);
+ ndbout_c("Number of Proc: %d",numProc);
+ ndbout_c("Parallellism : %d", parallellism);
+ ndbout_c("\n");
+
+ if( gen->totalTransactions == 0 ) {
+ ndbout_c(" No Transactions for this test");
+ }
+ else {
+ for(i = 0; i < 5; i++) {
+ sprintf(buf, "T%d",i+1);
+ print_transaction(buf,
+ gen->totalTransactions,
+ &gen->transactions[i],
+ i >= 2,
+ i >= 3 );
+ }
+
+ ndbout_c("\n");
+ ndbout_c(" Overall Statistics:");
+ ndbout_c(" Transactions: %d", gen->totalTransactions);
+ ndbout_c(" Outer : %.0f TPS",gen->outerTps);
+ ndbout_c("\n");
+ }
+}
+
+static
+void *
+threadRoutine(void *arg)
+{
+ int i;
+ ThreadData *data = (ThreadData *)arg;
+ Ndb * pNDB;
+
+ pNDB = asyncDbConnect(parallellism);
+ /* NdbSleep_MilliSleep(rand() % 10); */
+
+ for(i = 0; i<parallellism; i++){
+ data[i].pNDB = pNDB;
+ }
+ millisSendPoll = 30000;
+ asyncGenerator(data, parallellism,
+ millisSendPoll, minEventSendPoll, forceSendPoll);
+
+ asyncDbDisconnect(pNDB);
+
+ return NULL;
+}
+
+NDB_COMMAND(DbAsyncGenerator, "DbAsyncGenerator",
+ "DbAsyncGenerator", "DbAsyncGenerator", 65535)
+{
+ ndb_init();
+ int i;
+ int j;
+ int k;
+ struct NdbThread* pThread = NULL;
+ GeneratorStatistics stats;
+ GeneratorStatistics *p;
+ char threadName[32];
+ int rc = NDBT_OK;
+ void* tmp = NULL;
+ if(parse_args(argc,argv) != 0){
+ usage(argv[0]);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+
+ ndbout_c("\nStarting Test with %d process(es) for %d %s parallellism %d",
+ numProcesses,
+ numSeconds,
+ "sec",
+ parallellism);
+
+ ndbout_c(" WarmUp/coolDown = %d sec", numWarmSeconds);
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ ndbout << "Unable to connect to management server." << endl;
+ return 0;
+ }
+ if (con.wait_until_ready(30,0) < 0)
+ {
+ ndbout << "Cluster nodes not ready in 30 seconds." << endl;
+ return 0;
+ }
+
+ g_cluster_connection= &con;
+ data = (ThreadData*)malloc((numProcesses*parallellism)*sizeof(ThreadData));
+
+ for(i = 0; i < numProcesses; i++) {
+ for(j = 0; j<parallellism; j++){
+ data[i*parallellism+j].warmUpSeconds = numWarmSeconds;
+ data[i*parallellism+j].testSeconds = numSeconds;
+ data[i*parallellism+j].coolDownSeconds = numWarmSeconds;
+ data[i*parallellism+j].randomSeed =
+ NdbTick_CurrentMillisecond()+i+j;
+ data[i*parallellism+j].changedTime = 0;
+ data[i*parallellism+j].runState = Runnable;
+ }
+ sprintf(threadName, "AsyncThread[%d]", i);
+ pThread = NdbThread_Create(threadRoutine,
+ (void**)&data[i*parallellism],
+ 65535,
+ threadName,
+ NDB_THREAD_PRIO_LOW);
+ if(pThread != 0 && pThread != NULL){
+ (&data[i*parallellism])->pThread = pThread;
+ } else {
+ perror("Failed to create thread");
+ rc = NDBT_FAILED;
+ }
+ }
+
+ showTime();
+
+ /*--------------------------------*/
+ /* Wait for all processes to exit */
+ /*--------------------------------*/
+ for(i = 0; i < numProcesses; i++) {
+ NdbThread_WaitFor(data[i*parallellism].pThread, &tmp);
+ NdbThread_Destroy(&data[i*parallellism].pThread);
+ }
+
+ ndbout_c("All threads have finished");
+
+ /*-------------------------------------------*/
+ /* Clear all structures for total statistics */
+ /*-------------------------------------------*/
+ stats.totalTransactions = 0;
+ stats.outerTps = 0.0;
+
+ for(i = 0; i < NUM_TRANSACTION_TYPES; i++ ) {
+ stats.transactions[i].count = 0;
+ stats.transactions[i].branchExecuted = 0;
+ stats.transactions[i].rollbackExecuted = 0;
+ stats.transactions[i].latency.reset();
+ }
+
+ /*--------------------------------*/
+ /* Add the values for all Threads */
+ /*--------------------------------*/
+ for(i = 0; i < numProcesses; i++) {
+ for(k = 0; k<parallellism; k++){
+ p = &data[i*parallellism+k].generator;
+
+ stats.totalTransactions += p->totalTransactions;
+ stats.outerTps += p->outerTps;
+
+ for(j = 0; j < NUM_TRANSACTION_TYPES; j++ ) {
+ stats.transactions[j].count +=
+ p->transactions[j].count;
+ stats.transactions[j].branchExecuted +=
+ p->transactions[j].branchExecuted;
+ stats.transactions[j].rollbackExecuted +=
+ p->transactions[j].rollbackExecuted;
+ stats.transactions[j].latency +=
+ p->transactions[j].latency;
+ }
+ }
+ }
+
+ print_stats("Test Results",
+ numSeconds,
+ 0,
+ &stats,
+ numProcesses,
+ parallellism);
+
+ free(data);
+
+ NDBT_ProgramExit(rc);
+}
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+#include "userInterface.h"
+#include <NdbMutex.h>
+#include <NdbThread.h>
+#include <NdbTick.h>
+#include <NdbApi.hpp>
+#include <NdbOut.hpp>
+
+/***************************************************************
+* 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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+#ifndef NDB_WIN32
+#include <unistd.h>
+#endif
+
+Ndb*
+asyncDbConnect(int parallellism){
+ Ndb * pNDB = new Ndb(g_cluster_connection, "TEST_DB");
+
+ pNDB->init(parallellism + 1);
+
+ while(pNDB->waitUntilReady() != 0){
+ }
+
+ return pNDB;
+}
+
+void
+asyncDbDisconnect(Ndb* pNDB)
+{
+ delete pNDB;
+}
+
+double
+userGetTime(void)
+{
+ static bool initialized = false;
+ static NDB_TICKS initSecs = 0;
+ static Uint32 initMicros = 0;
+ double timeValue = 0;
+
+ if ( !initialized ) {
+ initialized = true;
+ NdbTick_CurrentMicrosecond(&initSecs, &initMicros);
+ timeValue = 0.0;
+ } else {
+ NDB_TICKS secs = 0;
+ Uint32 micros = 0;
+
+ NdbTick_CurrentMicrosecond(&secs, &micros);
+ double s = (double)secs - (double)initSecs;
+ double us = (double)micros - (double)initMicros;
+
+ timeValue = s + (us / 1000000.0);
+ }
+ return timeValue;
+}
+
+void showTime()
+{
+ char buf[128];
+ struct tm* tm_now;
+ time_t now;
+ now = ::time((time_t*)NULL);
+ tm_now = ::gmtime(&now);
+
+ ::snprintf(buf, 128,
+ "%d-%.2d-%.2d %.2d:%.2d:%.2d",
+ tm_now->tm_year + 1900,
+ tm_now->tm_mon,
+ tm_now->tm_mday,
+ tm_now->tm_hour,
+ tm_now->tm_min,
+ tm_now->tm_sec);
+
+ ndbout_c("Time: %s", buf);
+}
+
diff --git a/storage/ndb/test/ndbapi/bench/mainPopulate.cpp b/storage/ndb/test/ndbapi/bench/mainPopulate.cpp
new file mode 100644
index 00000000000..5ab1a5b015d
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/mainPopulate.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 <ndb_global.h>
+#include <ndb_opts.h>
+
+#include "userInterface.h"
+#include "dbPopulate.h"
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <random.h>
+#include <NDBT.hpp>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int useTableLogging;
+#ifdef __cplusplus
+}
+#endif
+
+
+static void usage()
+{
+}
+
+static
+void usage(const char *prog)
+{
+
+ ndbout_c(
+ "Usage: %s [-l]\n"
+ " -l Use logging and checkpointing on tables\n",
+ prog);
+
+ exit(1);
+}
+
+NDB_STD_OPTS_VARS;
+
+NDB_COMMAND(DbCreate, "DbCreate", "DbCreate", "DbCreate", 16384)
+{
+ ndb_init();
+ int i;
+ UserHandle *uh;
+
+ useTableLogging = 0;
+
+ for(i = 1; i<argc; i++){
+ if(strcmp(argv[i], "-l") == 0){
+ useTableLogging = 1;
+ } else {
+ usage(argv[0]);
+ return 0;
+ }
+ }
+
+ ndbout_c("Using %s tables",
+ useTableLogging ? "logging" : "temporary");
+
+ myRandom48Init(0x3e6f);
+
+ uh = userDbConnect(1, "TEST_DB");
+ dbPopulate(uh);
+ userDbDisconnect(uh);
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/test/ndbapi/bench/ndb_async1.cpp b/storage/ndb/test/ndbapi/bench/ndb_async1.cpp
new file mode 100644
index 00000000000..2a84f6b2aca
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/ndb_async1.cpp
@@ -0,0 +1,647 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+#include "userInterface.h"
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <NdbApi.hpp>
+
+inline
+NdbConnection *
+startTransaction(Ndb * pNDB,
+ ServerId inServerId,
+ const SubscriberNumber inNumber){
+
+ const int keyDataLenBytes = sizeof(ServerId)+SUBSCRIBER_NUMBER_LENGTH;
+ const int keyDataLen_64Words = keyDataLenBytes >> 3;
+
+ Uint64 keyDataBuf[keyDataLen_64Words+1]; // The "+1" is for rounding...
+
+ char * keyDataBuf_charP = (char *)&keyDataBuf[0];
+ Uint32 * keyDataBuf_wo32P = (Uint32 *)&keyDataBuf[0];
+
+ // Server Id comes first
+ keyDataBuf_wo32P[0] = inServerId;
+ // Then subscriber number
+ memcpy(&keyDataBuf_charP[sizeof(ServerId)], inNumber,
+ SUBSCRIBER_NUMBER_LENGTH);
+
+ return pNDB->startTransaction(0, keyDataBuf_charP, keyDataLenBytes);
+}
+
+void T1_Callback(int result, NdbConnection * pCon, void * threadData);
+void T2_Callback(int result, NdbConnection * pCon, void * threadData);
+void T3_Callback_1(int result, NdbConnection * pCon, void * threadData);
+void T3_Callback_2(int result, NdbConnection * pCon, void * threadData);
+void T3_Callback_3(int result, NdbConnection * pCon, void * threadData);
+void T4_Callback_1(int result, NdbConnection * pCon, void * threadData);
+void T4_Callback_2(int result, NdbConnection * pCon, void * threadData);
+void T4_Callback_3(int result, NdbConnection * pCon, void * threadData);
+void T5_Callback_1(int result, NdbConnection * pCon, void * threadData);
+void T5_Callback_2(int result, NdbConnection * pCon, void * threadData);
+void T5_Callback_3(int result, NdbConnection * pCon, void * threadData);
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+void
+start_T1(Ndb * pNDB, ThreadData * td){
+
+ DEBUG2("T1(%.*s): - Starting\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number);
+
+ int check;
+ NdbConnection * pCON = pNDB->startTransaction();
+ if (pCON != NULL) {
+ NdbOperation *MyOp = pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ if (MyOp != NULL) {
+ MyOp->updateTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ pCON->executeAsynchPrepare( Commit , T1_Callback, td);
+ } else {
+ CHECK_NULL(MyOp, "T1: getNdbOperation", pCON);
+ }//if
+ } else {
+ error_handler("T1-1: startTranscation",
+ pNDB->getNdbErrorString(),
+ pNDB->getNdbError());
+ }//if
+}
+
+void
+T1_Callback(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+
+ DEBUG2("T1(%.*s): - Completing\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number);
+
+ CHECK_MINUS_ONE(result, "T1: Commit",
+ pCON);
+ td->pNDB->closeTransaction(pCON);
+ complete_T1(td);
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+void
+start_T2(Ndb * pNDB, ThreadData * td){
+
+ DEBUG3("T2(%.*s, %p): - Starting\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.location);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * pCON = pNDB->startTransaction();
+ if (pCON == NULL)
+ error_handler("T2-1: startTransaction",
+ pNDB->getNdbErrorString(),
+ pNDB->getNdbError());
+
+ NdbOperation *MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T2: getNdbOperation",
+ pCON);
+
+ MyOp->readTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_NAME,
+ td->transactionData.name);
+ pCON->executeAsynchPrepare( Commit, T2_Callback, td );
+}
+
+void
+T2_Callback(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ DEBUG3("T2(%.*s, %p): - Completing\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.location);
+
+ CHECK_MINUS_ONE(result, "T2: Commit", pCON);
+ td->pNDB->closeTransaction(pCON);
+ complete_T2(td);
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+void
+start_T3(Ndb * pNDB, ThreadData * td){
+
+ DEBUG3("T3(%.*s, %.2d): - Starting\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * pCON = startTransaction(pNDB,
+ td->transactionData.server_id,
+ td->transactionData.number);
+ if (pCON == NULL)
+ error_handler("T3-1: startTranscation",
+ pNDB->getNdbErrorString(),
+ pNDB->getNdbError());
+
+ NdbOperation *MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T3-1: getNdbOperation",
+ pCON);
+
+ MyOp->readTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&td->transactionData.group_id);
+ MyOp->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&td->transactionData.sessions);
+ pCON->executeAsynchPrepare( NoCommit , T3_Callback_1, td);
+}
+
+void
+T3_Callback_1(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ DEBUG3("T3(%.*s, %.2d): - Callback 1\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ CHECK_MINUS_ONE(result, "T3-1: NoCommit", pCON);
+
+ NdbOperation * MyOp = pCON->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOp, "T3-2: getNdbOperation",
+ pCON);
+
+ MyOp->readTuple();
+ MyOp->equal(IND_GROUP_ID,
+ (char*)&td->transactionData.group_id);
+ MyOp->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&td->transactionData.permission);
+ pCON->executeAsynchPrepare( NoCommit, T3_Callback_2, td );
+}
+
+void
+T3_Callback_2(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+
+ CHECK_MINUS_ONE(result, "T3-2: NoCommit", pCON);
+
+ Uint32 permission = td->transactionData.permission;
+ Uint32 sessions = td->transactionData.sessions;
+ Uint32 server_bit = td->transactionData.server_bit;
+
+ if(((permission & server_bit) == server_bit) &&
+ ((sessions & server_bit) == server_bit)){
+
+ memcpy(td->transactionData.suffix,
+ &td->transactionData.number
+ [SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH],
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+ DEBUG5("T3(%.*s, %.2d): - Callback 2 - reading(%.*s)\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ td->transactionData.suffix);
+
+ /* Operation 3 */
+ NdbOperation * MyOp = pCON->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOp, "T3-3: getNdbOperation",
+ pCON);
+
+ MyOp->simpleRead();
+ MyOp->equal(IND_SESSION_SUBSCRIBER,
+ (char*)td->transactionData.number);
+ MyOp->equal(IND_SESSION_SERVER,
+ (char*)&td->transactionData.server_id);
+ MyOp->getValue(IND_SESSION_DATA,
+ (char *)td->transactionData.session_details);
+
+ /* Operation 4 */
+ MyOp = pCON->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOp, "T3-4: getNdbOperation",
+ pCON);
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SERVER_ID,
+ (char*)&td->transactionData.server_id);
+ MyOp->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)td->transactionData.suffix);
+ MyOp->incValue(IND_SERVER_READS, (uint32)1);
+ td->transactionData.branchExecuted = 1;
+ } else {
+ DEBUG3("T3(%.*s, %.2d): - Callback 2 - no read\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+ td->transactionData.branchExecuted = 0;
+ }
+ pCON->executeAsynchPrepare( Commit, T3_Callback_3, td );
+}
+
+void
+T3_Callback_3(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ DEBUG3("T3(%.*s, %.2d): - Completing\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ CHECK_MINUS_ONE(result, "T3-3: Commit", pCON);
+
+ td->pNDB->closeTransaction(pCON);
+ complete_T3(td);
+}
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+void
+start_T4(Ndb * pNDB, ThreadData * td){
+
+ DEBUG3("T4(%.*s, %.2d): - Starting\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * pCON = startTransaction(pNDB,
+ td->transactionData.server_id,
+ td->transactionData.number);
+ if (pCON == NULL)
+ error_handler("T4-1: startTranscation",
+ pNDB->getNdbErrorString(),
+ pNDB->getNdbError());
+
+ NdbOperation *MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T4-1: getNdbOperation",
+ pCON);
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&td->transactionData.group_id);
+ MyOp->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&td->transactionData.sessions);
+ MyOp->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)td->transactionData.server_bit);
+ pCON->executeAsynchPrepare( NoCommit , T4_Callback_1, td);
+}
+
+void
+T4_Callback_1(int result, NdbConnection * pCON, void * threadData){
+ CHECK_MINUS_ONE(result, "T4-1: NoCommit", pCON);
+ ThreadData * td = (ThreadData *)threadData;
+
+ DEBUG3("T4(%.*s, %.2d): - Callback 1\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+
+ NdbOperation * MyOp = pCON->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOp, "T4-2: getNdbOperation",
+ pCON);
+
+ MyOp->readTuple();
+ MyOp->equal(IND_GROUP_ID,
+ (char*)&td->transactionData.group_id);
+ MyOp->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&td->transactionData.permission);
+ pCON->executeAsynchPrepare( NoCommit , T4_Callback_2, td);
+}
+
+void
+T4_Callback_2(int result, NdbConnection * pCON, void * threadData){
+ CHECK_MINUS_ONE(result, "T4-2: NoCommit", pCON);
+ ThreadData * td = (ThreadData *)threadData;
+
+ Uint32 permission = td->transactionData.permission;
+ Uint32 sessions = td->transactionData.sessions;
+ Uint32 server_bit = td->transactionData.server_bit;
+
+ if(((permission & server_bit) == server_bit) &&
+ ((sessions & server_bit) == 0)){
+
+ memcpy(td->transactionData.suffix,
+ &td->transactionData.number
+ [SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH],
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+
+ DEBUG5("T4(%.*s, %.2d): - Callback 2 - inserting(%.*s)\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ td->transactionData.suffix);
+
+ /* Operation 3 */
+
+ NdbOperation * MyOp = pCON->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOp, "T4-3: getNdbOperation",
+ pCON);
+
+ MyOp->insertTuple();
+ MyOp->equal(IND_SESSION_SUBSCRIBER,
+ (char*)td->transactionData.number);
+ MyOp->equal(IND_SESSION_SERVER,
+ (char*)&td->transactionData.server_id);
+ MyOp->setValue(SESSION_DATA,
+ (char *)td->transactionData.session_details);
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOp = pCON->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOp, "T4-5: getNdbOperation",
+ pCON);
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SERVER_ID,
+ (char*)&td->transactionData.server_id);
+ MyOp->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)td->transactionData.suffix);
+ MyOp->incValue(IND_SERVER_INSERTS, (uint32)1);
+ td->transactionData.branchExecuted = 1;
+ } else {
+ td->transactionData.branchExecuted = 0;
+ DEBUG5("T4(%.*s, %.2d): - Callback 2 - %s %s\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ ((permission & server_bit) ?
+ "permission - " : "no permission - "),
+ ((sessions & server_bit) ?
+ "in session - " : "no in session - "));
+ }
+
+ if(!td->transactionData.do_rollback && td->transactionData.branchExecuted){
+ pCON->executeAsynchPrepare(Commit, T4_Callback_3, td);
+ } else {
+ pCON->executeAsynchPrepare(Rollback, T4_Callback_3, td);
+ }
+}
+
+void
+T4_Callback_3(int result, NdbConnection * pCON, void * threadData){
+ CHECK_MINUS_ONE(result, "T4-3: Commit", pCON);
+ ThreadData * td = (ThreadData *)threadData;
+
+ DEBUG3("T4(%.*s, %.2d): - Completing\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ td->pNDB->closeTransaction(pCON);
+ complete_T4(td);
+}
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+void
+start_T5(Ndb * pNDB, ThreadData * td){
+
+ DEBUG3("T5(%.*s, %.2d): - Starting\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * pCON = pNDB->startTransaction();
+ if (pCON == NULL)
+ error_handler("T5-1: startTranscation",
+ pNDB->getNdbErrorString(),
+ pNDB->getNdbError());
+
+ NdbOperation * MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T5-1: getNdbOperation",
+ pCON);
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&td->transactionData.group_id);
+ MyOp->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&td->transactionData.sessions);
+ MyOp->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)td->transactionData.server_bit);
+ pCON->executeAsynchPrepare( NoCommit, T5_Callback_1, td );
+}
+
+void
+T5_Callback_1(int result, NdbConnection * pCON, void * threadData){
+ CHECK_MINUS_ONE(result, "T5-1: NoCommit", pCON);
+ ThreadData * td = (ThreadData *)threadData;
+
+ DEBUG3("T5(%.*s, %.2d): - Callback 1\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ NdbOperation * MyOp = pCON->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOp, "T5-2: getNdbOperation",
+ pCON);
+
+ MyOp->readTuple();
+ MyOp->equal(IND_GROUP_ID,
+ (char*)&td->transactionData.group_id);
+ MyOp->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&td->transactionData.permission);
+ pCON->executeAsynchPrepare( NoCommit, T5_Callback_2, td );
+}
+
+void
+T5_Callback_2(int result, NdbConnection * pCON, void * threadData){
+ CHECK_MINUS_ONE(result, "T5-2: NoCommit", pCON);
+ ThreadData * td = (ThreadData *)threadData;
+
+ Uint32 permission = td->transactionData.permission;
+ Uint32 sessions = td->transactionData.sessions;
+ Uint32 server_bit = td->transactionData.server_bit;
+
+ if(((permission & server_bit) == server_bit) &&
+ ((sessions & server_bit) == server_bit)){
+
+ memcpy(td->transactionData.suffix,
+ &td->transactionData.number
+ [SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH],
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+
+ DEBUG5("T5(%.*s, %.2d): - Callback 2 - deleting(%.*s)\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ td->transactionData.suffix);
+
+ /* Operation 3 */
+ NdbOperation * MyOp = pCON->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOp, "T5-3: getNdbOperation",
+ pCON);
+
+ MyOp->deleteTuple();
+ MyOp->equal(IND_SESSION_SUBSCRIBER,
+ (char*)td->transactionData.number);
+ MyOp->equal(IND_SESSION_SERVER,
+ (char*)&td->transactionData.server_id);
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOp = pCON->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOp, "T5-5: getNdbOperation",
+ pCON);
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SERVER_ID,
+ (char*)&td->transactionData.server_id);
+ MyOp->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)td->transactionData.suffix);
+ MyOp->incValue(IND_SERVER_DELETES, (uint32)1);
+ td->transactionData.branchExecuted = 1;
+ } else {
+ td->transactionData.branchExecuted = 0;
+
+ DEBUG5("T5(%.*s, %.2d): - Callback 2 - no delete - %s %s\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ ((permission & server_bit) ?
+ "permission - " : "no permission - "),
+ ((sessions & server_bit) ?
+ "in session - " : "no in session - "));
+ }
+
+ if(!td->transactionData.do_rollback && td->transactionData.branchExecuted){
+ pCON->executeAsynchPrepare(Commit, T5_Callback_3, td);
+ } else {
+ pCON->executeAsynchPrepare(Rollback, T5_Callback_3, td);
+ }
+}
+
+void
+T5_Callback_3(int result, NdbConnection * pCON, void * threadData){
+ CHECK_MINUS_ONE(result, "T5-3: Commit", pCON);
+ ThreadData * td = (ThreadData *)threadData;
+
+ DEBUG3("T5(%.*s, %.2d): - Completing\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ td->pNDB->closeTransaction(pCON);
+ complete_T5(td);
+}
diff --git a/storage/ndb/test/ndbapi/bench/ndb_async2.cpp b/storage/ndb/test/ndbapi/bench/ndb_async2.cpp
new file mode 100644
index 00000000000..31cf1d8310a
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/ndb_async2.cpp
@@ -0,0 +1,757 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+#include <string.h>
+#include "userInterface.h"
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+#include <NdbSleep.h>
+
+#include <NdbApi.hpp>
+
+void T1_Callback(int result, NdbConnection * pCon, void * threadData);
+void T2_Callback(int result, NdbConnection * pCon, void * threadData);
+void T3_Callback_1(int result, NdbConnection * pCon, void * threadData);
+void T3_Callback_2(int result, NdbConnection * pCon, void * threadData);
+void T3_Callback_3(int result, NdbConnection * pCon, void * threadData);
+void T4_Callback_1(int result, NdbConnection * pCon, void * threadData);
+void T4_Callback_2(int result, NdbConnection * pCon, void * threadData);
+void T4_Callback_3(int result, NdbConnection * pCon, void * threadData);
+void T5_Callback_1(int result, NdbConnection * pCon, void * threadData);
+void T5_Callback_2(int result, NdbConnection * pCon, void * threadData);
+void T5_Callback_3(int result, NdbConnection * pCon, void * threadData);
+
+static int stat_async = 0;
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+
+#define SFX_START (SUBSCRIBER_NUMBER_LENGTH - SUBSCRIBER_NUMBER_SUFFIX_LENGTH)
+
+inline
+NdbConnection *
+startTransaction(Ndb * pNDB, ThreadData * td){
+ return pNDB->startTransaction();
+#ifdef OLD_CODE
+ return pNDB->startTransactionDGroup (0,
+ &td->transactionData.number[SFX_START],
+ 1);
+#endif
+}
+
+void
+start_T1(Ndb * pNDB, ThreadData * td, int async){
+
+ DEBUG2("T1(%.*s): - Starting", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number);
+
+ NdbConnection * pCON = 0;
+ while((pCON = startTransaction(pNDB, td)) == 0){
+ CHECK_ALLOWED_ERROR("T1: startTransaction", td, pNDB->getNdbError());
+ NdbSleep_MilliSleep(10);
+ }
+
+ NdbOperation *MyOp = pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ if (MyOp != NULL) {
+ MyOp->updateTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ if (async == 1) {
+ pCON->executeAsynchPrepare( Commit , T1_Callback, td);
+ } else {
+ int result = pCON->execute(Commit);
+ T1_Callback(result, pCON, (void*)td);
+ return;
+ }//if
+ } else {
+ CHECK_NULL(MyOp, "T1: getNdbOperation", td, pCON->getNdbError());
+ }//if
+}
+
+void
+T1_Callback(int result, NdbConnection * pCON, void * threadData) {
+ ThreadData * td = (ThreadData *)threadData;
+
+ DEBUG2("T1(%.*s): - Completing", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number);
+
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T1: Commit", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T1(td->pNDB, td, stat_async);
+ return;
+ }//if
+ td->pNDB->closeTransaction(pCON);
+ complete_T1(td);
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+void
+start_T2(Ndb * pNDB, ThreadData * td, int async){
+
+ DEBUG3("T2(%.*s, %d): - Starting", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.location);
+
+ NdbConnection * pCON = 0;
+
+ while((pCON = startTransaction(pNDB, td)) == 0){
+ CHECK_ALLOWED_ERROR("T2-1: startTransaction", td, pNDB->getNdbError());
+ NdbSleep_MilliSleep(10);
+ }
+
+ NdbOperation *MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T2: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->readTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_NAME,
+ td->transactionData.name);
+ if (async == 1) {
+ pCON->executeAsynchPrepare( Commit , T2_Callback, td);
+ } else {
+ int result = pCON->execute(Commit);
+ T2_Callback(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T2_Callback(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ DEBUG3("T2(%.*s, %d): - Completing", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.location);
+
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T2: Commit", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T2(td->pNDB, td, stat_async);
+ return;
+ }//if
+ td->pNDB->closeTransaction(pCON);
+ complete_T2(td);
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+void
+start_T3(Ndb * pNDB, ThreadData * td, int async){
+
+ DEBUG3("T3(%.*s, %.2d): - Starting", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ NdbConnection * pCON = 0;
+
+ while((pCON = startTransaction(pNDB, td)) == 0){
+ CHECK_ALLOWED_ERROR("T3-1: startTransaction", td, pNDB->getNdbError());
+ NdbSleep_MilliSleep(10);
+ }
+
+ NdbOperation *MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T3-1: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->readTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&td->transactionData.group_id);
+ MyOp->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&td->transactionData.sessions);
+ stat_async = async;
+ if (async == 1) {
+ pCON->executeAsynchPrepare( NoCommit , T3_Callback_1, td);
+ } else {
+ int result = pCON->execute( NoCommit );
+ T3_Callback_1(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T3_Callback_1(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ DEBUG3("T3(%.*s, %.2d): - Callback 1", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T3-1: execute", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T3(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ NdbOperation * MyOp = pCON->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOp, "T3-2: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->readTuple();
+ MyOp->equal(IND_GROUP_ID,
+ (char*)&td->transactionData.group_id);
+ MyOp->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&td->transactionData.permission);
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( NoCommit , T3_Callback_2, td);
+ } else {
+ int result = pCON->execute( NoCommit );
+ T3_Callback_2(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T3_Callback_2(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T3-2: execute", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T3(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ Uint32 permission = td->transactionData.permission;
+ Uint32 sessions = td->transactionData.sessions;
+ Uint32 server_bit = td->transactionData.server_bit;
+
+ if(((permission & server_bit) == server_bit) &&
+ ((sessions & server_bit) == server_bit)){
+
+ memcpy(td->transactionData.suffix,
+ &td->transactionData.number[SFX_START],
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+ DEBUG5("T3(%.*s, %.2d): - Callback 2 - reading(%.*s)",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ td->transactionData.suffix);
+
+ /* Operation 3 */
+ NdbOperation * MyOp = pCON->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOp, "T3-3: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->simpleRead();
+ MyOp->equal(IND_SESSION_SUBSCRIBER,
+ (char*)td->transactionData.number);
+ MyOp->equal(IND_SESSION_SERVER,
+ (char*)&td->transactionData.server_id);
+ MyOp->getValue(IND_SESSION_DATA,
+ (char *)td->transactionData.session_details);
+
+ /* Operation 4 */
+ MyOp = pCON->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOp, "T3-4: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SERVER_ID,
+ (char*)&td->transactionData.server_id);
+ MyOp->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)td->transactionData.suffix);
+ MyOp->incValue(IND_SERVER_READS, (uint32)1);
+ td->transactionData.branchExecuted = 1;
+ } else {
+ DEBUG3("T3(%.*s, %.2d): - Callback 2 - no read",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+ td->transactionData.branchExecuted = 0;
+ }
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( Commit , T3_Callback_3, td);
+ } else {
+ int result = pCON->execute( Commit );
+ T3_Callback_3(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T3_Callback_3(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ DEBUG3("T3(%.*s, %.2d): - Completing", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T3-3: Commit", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T3(td->pNDB, td, stat_async);
+ return;
+ }//if
+ td->pNDB->closeTransaction(pCON);
+ complete_T3(td);
+}
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+void
+start_T4(Ndb * pNDB, ThreadData * td, int async){
+
+ DEBUG3("T4(%.*s, %.2d): - Starting", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ NdbConnection * pCON = 0;
+ while((pCON = startTransaction(pNDB, td)) == 0){
+ CHECK_ALLOWED_ERROR("T4-1: startTransaction", td, pNDB->getNdbError());
+ NdbSleep_MilliSleep(10);
+ }
+
+ NdbOperation *MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T4-1: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&td->transactionData.group_id);
+ MyOp->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&td->transactionData.sessions);
+ MyOp->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)td->transactionData.server_bit);
+ stat_async = async;
+ if (async == 1) {
+ pCON->executeAsynchPrepare( NoCommit , T4_Callback_1, td);
+ } else {
+ int result = pCON->execute( NoCommit );
+ T4_Callback_1(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T4_Callback_1(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T4-1: execute", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T4(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ DEBUG3("T4(%.*s, %.2d): - Callback 1",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+
+ NdbOperation * MyOp = pCON->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOp, "T4-2: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->readTuple();
+ MyOp->equal(IND_GROUP_ID,
+ (char*)&td->transactionData.group_id);
+ MyOp->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&td->transactionData.permission);
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( NoCommit , T4_Callback_2, td);
+ } else {
+ int result = pCON->execute( NoCommit );
+ T4_Callback_2(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T4_Callback_2(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T4-2: execute", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T4(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ Uint32 permission = td->transactionData.permission;
+ Uint32 sessions = td->transactionData.sessions;
+ Uint32 server_bit = td->transactionData.server_bit;
+
+ if(((permission & server_bit) == server_bit) &&
+ ((sessions & server_bit) == 0)){
+
+ memcpy(td->transactionData.suffix,
+ &td->transactionData.number[SFX_START],
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+
+ DEBUG5("T4(%.*s, %.2d): - Callback 2 - inserting(%.*s)",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ td->transactionData.suffix);
+
+ /* Operation 3 */
+
+ NdbOperation * MyOp = pCON->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOp, "T4-3: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->insertTuple();
+ MyOp->equal(IND_SESSION_SUBSCRIBER,
+ (char*)td->transactionData.number);
+ MyOp->equal(IND_SESSION_SERVER,
+ (char*)&td->transactionData.server_id);
+ MyOp->setValue(SESSION_DATA,
+ (char *)td->transactionData.session_details);
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOp = pCON->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOp, "T4-5: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SERVER_ID,
+ (char*)&td->transactionData.server_id);
+ MyOp->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)td->transactionData.suffix);
+ MyOp->incValue(IND_SERVER_INSERTS, (uint32)1);
+ td->transactionData.branchExecuted = 1;
+ } else {
+ td->transactionData.branchExecuted = 0;
+ DEBUG5("T4(%.*s, %.2d): - Callback 2 - %s %s",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ ((permission & server_bit) ?
+ "permission - " : "no permission - "),
+ ((sessions & server_bit) ?
+ "in session - " : "no in session - "));
+ }
+
+ if(!td->transactionData.do_rollback && td->transactionData.branchExecuted){
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( Commit , T4_Callback_3, td);
+ } else {
+ int result = pCON->execute( Commit );
+ T4_Callback_3(result, pCON, (void*)td);
+ return;
+ }//if
+ } else {
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( Rollback , T4_Callback_3, td);
+ } else {
+ int result = pCON->execute( Rollback );
+ T4_Callback_3(result, pCON, (void*)td);
+ return;
+ }//if
+ }
+}
+
+void
+T4_Callback_3(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T4-3: Commit", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T4(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ DEBUG3("T4(%.*s, %.2d): - Completing",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ td->pNDB->closeTransaction(pCON);
+ complete_T4(td);
+}
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+void
+start_T5(Ndb * pNDB, ThreadData * td, int async){
+
+ DEBUG3("T5(%.*s, %.2d): - Starting", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ NdbConnection * pCON = 0;
+ while((pCON = startTransaction(pNDB, td)) == 0){
+ CHECK_ALLOWED_ERROR("T5-1: startTransaction", td, pNDB->getNdbError());
+ NdbSleep_MilliSleep(10);
+ }
+
+ NdbOperation * MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T5-1: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&td->transactionData.group_id);
+ MyOp->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&td->transactionData.sessions);
+ MyOp->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)td->transactionData.server_bit);
+ stat_async = async;
+ if (async == 1) {
+ pCON->executeAsynchPrepare( NoCommit , T5_Callback_1, td);
+ } else {
+ int result = pCON->execute( NoCommit );
+ T5_Callback_1(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T5_Callback_1(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T5-1: execute", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T5(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ DEBUG3("T5(%.*s, %.2d): - Callback 1",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ NdbOperation * MyOp = pCON->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOp, "T5-2: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->readTuple();
+ MyOp->equal(IND_GROUP_ID,
+ (char*)&td->transactionData.group_id);
+ MyOp->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&td->transactionData.permission);
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( NoCommit , T5_Callback_2, td);
+ } else {
+ int result = pCON->execute( NoCommit );
+ T5_Callback_2(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T5_Callback_2(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T5-2: execute", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T5(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ Uint32 permission = td->transactionData.permission;
+ Uint32 sessions = td->transactionData.sessions;
+ Uint32 server_bit = td->transactionData.server_bit;
+
+ if(((permission & server_bit) == server_bit) &&
+ ((sessions & server_bit) == server_bit)){
+
+ memcpy(td->transactionData.suffix,
+ &td->transactionData.number[SFX_START],
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+
+ DEBUG5("T5(%.*s, %.2d): - Callback 2 - deleting(%.*s)",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ td->transactionData.suffix);
+
+ /* Operation 3 */
+ NdbOperation * MyOp = pCON->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOp, "T5-3: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->deleteTuple();
+ MyOp->equal(IND_SESSION_SUBSCRIBER,
+ (char*)td->transactionData.number);
+ MyOp->equal(IND_SESSION_SERVER,
+ (char*)&td->transactionData.server_id);
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOp = pCON->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOp, "T5-5: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SERVER_ID,
+ (char*)&td->transactionData.server_id);
+ MyOp->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)td->transactionData.suffix);
+ MyOp->incValue(IND_SERVER_DELETES, (uint32)1);
+ td->transactionData.branchExecuted = 1;
+ } else {
+ td->transactionData.branchExecuted = 0;
+
+ DEBUG5("T5(%.*s, %.2d): - Callback 2 - no delete - %s %s",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ ((permission & server_bit) ?
+ "permission - " : "no permission - "),
+ ((sessions & server_bit) ?
+ "in session - " : "no in session - "));
+ }
+
+ if(!td->transactionData.do_rollback && td->transactionData.branchExecuted){
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( Commit , T5_Callback_3, td);
+ } else {
+ int result = pCON->execute( Commit );
+ T5_Callback_3(result, pCON, (void*)td);
+ return;
+ }//if
+ } else {
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( Rollback , T5_Callback_3, td);
+ } else {
+ int result = pCON->execute( Rollback );
+ T5_Callback_3(result, pCON, (void*)td);
+ return;
+ }//if
+ }
+}
+
+void
+T5_Callback_3(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T5-3: Commit", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T5(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ DEBUG3("T5(%.*s, %.2d): - Completing",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ td->pNDB->closeTransaction(pCON);
+ complete_T5(td);
+}
diff --git a/storage/ndb/test/ndbapi/bench/ndb_error.hpp b/storage/ndb/test/ndbapi/bench/ndb_error.hpp
new file mode 100644
index 00000000000..d90f5506813
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/ndb_error.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 NDB_ERROR_H
+#define NDB_ERROR_H
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+#include "userInterface.h"
+#include <NdbError.hpp>
+#include <NdbApi.hpp>
+
+#define error_handler(x,y, z) { \
+ ndbout << x << " " << y << endl; \
+ exit(-1); }
+
+#define CHECK_MINUS_ONE(x, y, z) if(x == -1) \
+ error_handler(y,(z->getNdbError()), 0)
+
+inline
+void
+CHECK_ALLOWED_ERROR(const char * str,
+ const ThreadData * td,
+ const struct NdbError & error){
+
+ char buf[100];
+ snprintf(buf, sizeof(buf), "subscriber = %.*s ",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number);
+ ndbout << str << " " << error << endl
+ << buf;
+ showTime();
+
+ switch(error.classification) {
+ case NdbError::TimeoutExpired:
+ case NdbError::OverloadError:
+ case NdbError::TemporaryResourceError:
+ case NdbError::NodeRecoveryError:
+ break;
+ default:
+ if(error.status != NdbError::TemporaryError)
+ exit(-1);
+ }
+}
+
+inline
+void
+CHECK_NULL(void * null,
+ const char * str,
+ const ThreadData * td,
+ const struct NdbError & err){
+ if(null == 0){
+ CHECK_ALLOWED_ERROR(str, td, err);
+ exit(-1);
+ }
+}
+
+inline
+void
+CHECK_NULL(void * null, const char* msg, NdbConnection* obj)
+{
+ if(null == 0)
+ {
+ error_handler(msg, obj->getNdbError(), 0);
+ }
+}
+
+#endif
diff --git a/storage/ndb/test/ndbapi/bench/ndb_schema.hpp b/storage/ndb/test/ndbapi/bench/ndb_schema.hpp
new file mode 100644
index 00000000000..af08bc2eecd
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/ndb_schema.hpp
@@ -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 */
+
+#ifndef NDB_SCHEMA_H
+#define NDB_SCHEMA_H
+
+#include "testDefinitions.h"
+
+#define SUBSCRIBER_TABLE "SUBSCRIBER"
+#define SUBSCRIBER_NUMBER "NUMBER"
+#define SUBSCRIBER_LOCATION "LOCATION"
+#define SUBSCRIBER_NAME "NAME"
+#define SUBSCRIBER_GROUP "GROUP_ID"
+#define SUBSCRIBER_SESSIONS "SESSIONS"
+#define SUBSCRIBER_CHANGED_BY "CHANGED_BY"
+#define SUBSCRIBER_CHANGED_TIME "CHANGED_TIME"
+
+#define SERVER_TABLE "SERVER"
+#define SERVER_ID "SERVER_ID"
+#define SERVER_SUBSCRIBER_SUFFIX "SUFFIX"
+#define SERVER_NAME "NAME"
+#define SERVER_READS "NO_OF_READ"
+#define SERVER_INSERTS "NO_OF_INSERT"
+#define SERVER_DELETES "NO_OF_DELETE"
+
+#define GROUP_TABLE "GROUP"
+#define GROUP_ID "GROUP_ID"
+#define GROUP_NAME "GROUP_NAME"
+#define GROUP_ALLOW_READ "ALLOW_READ"
+#define GROUP_ALLOW_INSERT "ALLOW_INSERT"
+#define GROUP_ALLOW_DELETE "ALLOW_DELETE"
+
+#define SESSION_TABLE "SESSION"
+#define SESSION_SERVER "SERVER_ID"
+#define SESSION_SUBSCRIBER "NUMBER"
+#define SESSION_DATA "DATA"
+
+/** Numbers */
+
+#define IND_SUBSCRIBER_NUMBER (unsigned)0
+#define IND_SUBSCRIBER_NAME (unsigned)1
+#define IND_SUBSCRIBER_GROUP (unsigned)2
+#define IND_SUBSCRIBER_LOCATION (unsigned)3
+#define IND_SUBSCRIBER_SESSIONS (unsigned)4
+#define IND_SUBSCRIBER_CHANGED_BY (unsigned)5
+#define IND_SUBSCRIBER_CHANGED_TIME (unsigned)6
+
+#define IND_SERVER_SUBSCRIBER_SUFFIX (unsigned)0
+#define IND_SERVER_ID (unsigned)1
+#define IND_SERVER_NAME (unsigned)2
+#define IND_SERVER_READS (unsigned)3
+#define IND_SERVER_INSERTS (unsigned)4
+#define IND_SERVER_DELETES (unsigned)5
+
+#define IND_GROUP_ID (unsigned)0
+#define IND_GROUP_NAME (unsigned)1
+#define IND_GROUP_ALLOW_READ (unsigned)2
+#define IND_GROUP_ALLOW_INSERT (unsigned)3
+#define IND_GROUP_ALLOW_DELETE (unsigned)4
+
+#define IND_SESSION_SUBSCRIBER (unsigned)0
+#define IND_SESSION_SERVER (unsigned)1
+#define IND_SESSION_DATA (unsigned)2
+
+#endif
diff --git a/storage/ndb/test/ndbapi/bench/ndb_user_transaction.cpp b/storage/ndb/test/ndbapi/bench/ndb_user_transaction.cpp
new file mode 100644
index 00000000000..182f1f99586
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/ndb_user_transaction.cpp
@@ -0,0 +1,825 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+extern "C" {
+#include "user_transaction.h"
+};
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <time.h>
+#include <NdbApi.hpp>
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+int
+T1(void * obj,
+ const SubscriberNumber number,
+ const Location new_location,
+ const ChangedBy changed_by,
+ const ChangedTime changed_time,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T1: getNdbOperation", MyTransaction);
+
+ check = MyOperation->updateTuple();
+ CHECK_MINUS_ONE(check, "T1: updateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T1: equal subscriber",
+ MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_LOCATION,
+ (char *)&new_location);
+ CHECK_MINUS_ONE(check, "T1: setValue location",
+ MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_by",
+ MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_time",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T1: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+int
+T2(void * obj,
+ const SubscriberNumber number,
+ Location * readLocation,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T2: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T2: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_LOCATION,
+ (char *)readLocation);
+ CHECK_NULL(check2, "T2: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_NULL(check2, "T2: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_NULL(check2, "T2: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_NAME,
+ subscriberName);
+ CHECK_NULL(check2, "T2: getValue name",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T2: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+int
+T3(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ SessionDetails outSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T3-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T3-1: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T3-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T3-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T3-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T3-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T3-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T3-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T3-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T3-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(GROUP_ALLOW_READ,
+ (char *)&permission);
+ CHECK_NULL(check2, "T3-2: getValue allow_read",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T3(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("reading - ");
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T3-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-3: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T3-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-3: equal server id",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SESSION_DATA,
+ (char *)outSessionDetails);
+ CHECK_NULL(check2, "T3-3: getValue session details",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-3: NoCommit",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T3-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T3-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-4: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T3-4: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(SERVER_READS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T3-4: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-4: NoCommit",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ }
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T3: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T4(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ const SessionDetails inSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T4-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ DEBUG3("T4(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ NdbOperation * MyOperation = 0;
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTupleExclusive();
+ CHECK_MINUS_ONE(check, "T4-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T4-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T4-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T4-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T4-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T4-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T4-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T4-2: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T4-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T4-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(GROUP_ALLOW_INSERT,
+ (char *)&permission);
+ CHECK_NULL(check2, "T4-2: getValue allow_insert",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == 0)){
+
+ DEBUG("inserting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T4-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "T4-3: insertTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-3: equal server id",
+ MyTransaction);
+
+ check = MyOperation->setValue(SESSION_DATA,
+ (char *)inSessionDetails);
+ CHECK_MINUS_ONE(check, "T4-3: setValue session details",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-3: NoCommit",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-4: equal number",
+ MyTransaction);
+
+ check = MyOperation->incValue(SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T4-4: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-4: NoCommit",
+ MyTransaction);
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T4-5: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T4-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(SERVER_INSERTS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T4-5: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-5: NoCommit",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T4: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T4:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T5(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+ NdbConnection * MyTransaction = 0;
+ NdbOperation * MyOperation = 0;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T5-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-1: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTupleExclusive();
+ CHECK_MINUS_ONE(check, "T5-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T5-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T5-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T5-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T5-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T5-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T5-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T5-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T5-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T5-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(GROUP_ALLOW_DELETE,
+ (char *)&permission);
+ CHECK_NULL(check2, "T5-2: getValue allow_delete",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T5(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("deleting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T5-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->deleteTuple();
+ CHECK_MINUS_ONE(check, "T5-3: deleteTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-3: equal server id",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-3: NoCommit",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-4: equal number",
+ MyTransaction);
+
+ check = MyOperation->subValue(SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T5-4: dec value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-4: NoCommit",
+ MyTransaction);
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T5-5: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T5-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(SERVER_DELETES, (uint32)1);
+ CHECK_MINUS_ONE(check, "T5-5: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-5: NoCommit",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T5: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T5:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/bench/ndb_user_transaction2.cpp b/storage/ndb/test/ndbapi/bench/ndb_user_transaction2.cpp
new file mode 100644
index 00000000000..df3c7a7989e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/ndb_user_transaction2.cpp
@@ -0,0 +1,825 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+extern "C" {
+#include "user_transaction.h"
+};
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <time.h>
+#include <NdbApi.hpp>
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+int
+T1(void * obj,
+ const SubscriberNumber number,
+ const Location new_location,
+ const ChangedBy changed_by,
+ const ChangedTime changed_time,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T1: getNdbOperation", MyTransaction);
+
+ check = MyOperation->updateTuple();
+ CHECK_MINUS_ONE(check, "T1: updateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T1: equal subscriber",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&new_location);
+ CHECK_MINUS_ONE(check, "T1: setValue location",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_by",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_time",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T1: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+int
+T2(void * obj,
+ const SubscriberNumber number,
+ Location * readLocation,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T2: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T2: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)readLocation);
+ CHECK_NULL(check2, "T2: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_NULL(check2, "T2: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_NULL(check2, "T2: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_NAME,
+ subscriberName);
+ CHECK_NULL(check2, "T2: getValue name",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T2: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+int
+T3(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ SessionDetails outSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T3-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T3-1: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T3-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T3-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T3-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T3-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T3-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T3-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T3-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T3-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&permission);
+ CHECK_NULL(check2, "T3-2: getValue allow_read",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T3(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("reading - ");
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T3-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-3: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T3-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-3: equal server id",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SESSION_DATA,
+ (char *)outSessionDetails);
+ CHECK_NULL(check2, "T3-3: getValue session details",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-3: NoCommit",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T3-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T3-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-4: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T3-4: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_READS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T3-4: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-4: NoCommit",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ }
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T3: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T4(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ const SessionDetails inSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T4-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ DEBUG3("T4(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ NdbOperation * MyOperation = 0;
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTupleExclusive();
+ CHECK_MINUS_ONE(check, "T4-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T4-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T4-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T4-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T4-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T4-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T4-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T4-2: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T4-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T4-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&permission);
+ CHECK_NULL(check2, "T4-2: getValue allow_insert",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == 0)){
+
+ DEBUG("inserting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T4-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "T4-3: insertTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-3: equal server id",
+ MyTransaction);
+
+ check = MyOperation->setValue(SESSION_DATA,
+ (char *)inSessionDetails);
+ CHECK_MINUS_ONE(check, "T4-3: setValue session details",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-3: NoCommit",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-4: equal number",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T4-4: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-4: NoCommit",
+ MyTransaction);
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T4-5: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T4-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_INSERTS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T4-5: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-5: NoCommit",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T4: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T4:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T5(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+ NdbConnection * MyTransaction = 0;
+ NdbOperation * MyOperation = 0;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T5-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-1: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTupleExclusive();
+ CHECK_MINUS_ONE(check, "T5-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T5-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T5-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T5-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T5-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T5-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T5-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T5-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T5-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T5-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&permission);
+ CHECK_NULL(check2, "T5-2: getValue allow_delete",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T5(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("deleting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T5-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->deleteTuple();
+ CHECK_MINUS_ONE(check, "T5-3: deleteTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-3: equal server id",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-3: NoCommit",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-4: equal number",
+ MyTransaction);
+
+ check = MyOperation->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T5-4: dec value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-4: NoCommit",
+ MyTransaction);
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T5-5: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T5-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_DELETES, (uint32)1);
+ CHECK_MINUS_ONE(check, "T5-5: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-5: NoCommit",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T5: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T5:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/bench/ndb_user_transaction3.cpp b/storage/ndb/test/ndbapi/bench/ndb_user_transaction3.cpp
new file mode 100644
index 00000000000..d2c92ecd424
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/ndb_user_transaction3.cpp
@@ -0,0 +1,793 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+extern "C" {
+#include "user_transaction.h"
+};
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <time.h>
+#include <NdbApi.hpp>
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+int
+T1(void * obj,
+ const SubscriberNumber number,
+ const Location new_location,
+ const ChangedBy changed_by,
+ const ChangedTime changed_time,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T1: getNdbOperation", MyTransaction);
+
+ check = MyOperation->updateTuple();
+ CHECK_MINUS_ONE(check, "T1: updateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T1: equal subscriber",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&new_location);
+ CHECK_MINUS_ONE(check, "T1: setValue location",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_by",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_time",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T1: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+int
+T2(void * obj,
+ const SubscriberNumber number,
+ Location * readLocation,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T2: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T2: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)readLocation);
+ CHECK_NULL(check2, "T2: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_NULL(check2, "T2: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_NULL(check2, "T2: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_NAME,
+ subscriberName);
+ CHECK_NULL(check2, "T2: getValue name",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T2: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+int
+T3(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ SessionDetails outSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T3-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T3-1: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T3-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T3-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T3-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T3-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T3-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T3-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T3-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T3-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&permission);
+ CHECK_NULL(check2, "T3-2: getValue allow_read",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T3(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("reading - ");
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T3-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-3: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T3-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-3: equal server id",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SESSION_DATA,
+ (char *)outSessionDetails);
+ CHECK_NULL(check2, "T3-3: getValue session details",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T3-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T3-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-4: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T3-4: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_READS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T3-4: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ }
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T3: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T4(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ const SessionDetails inSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T4-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ DEBUG3("T4(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ NdbOperation * MyOperation = 0;
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTupleExclusive();
+ CHECK_MINUS_ONE(check, "T4-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T4-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T4-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T4-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T4-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T4-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T4-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T4-2: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T4-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T4-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&permission);
+ CHECK_NULL(check2, "T4-2: getValue allow_insert",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == 0)){
+
+ DEBUG("inserting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T4-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "T4-3: insertTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-3: equal server id",
+ MyTransaction);
+
+ check = MyOperation->setValue(SESSION_DATA,
+ (char *)inSessionDetails);
+ CHECK_MINUS_ONE(check, "T4-3: setValue session details",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-4: equal number",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T4-4: inc value",
+ MyTransaction);
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T4-5: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T4-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_INSERTS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T4-5: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T4: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T4:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T5(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+ NdbConnection * MyTransaction = 0;
+ NdbOperation * MyOperation = 0;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T5-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-1: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTupleExclusive();
+ CHECK_MINUS_ONE(check, "T5-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T5-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T5-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T5-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T5-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T5-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T5-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T5-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T5-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T5-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&permission);
+ CHECK_NULL(check2, "T5-2: getValue allow_delete",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T5(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("deleting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T5-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->deleteTuple();
+ CHECK_MINUS_ONE(check, "T5-3: deleteTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-3: equal server id",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-4: equal number",
+ MyTransaction);
+
+ check = MyOperation->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T5-4: dec value",
+ MyTransaction);
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T5-5: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T5-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_DELETES, (uint32)1);
+ CHECK_MINUS_ONE(check, "T5-5: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T5: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T5:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/bench/ndb_user_transaction4.cpp b/storage/ndb/test/ndbapi/bench/ndb_user_transaction4.cpp
new file mode 100644
index 00000000000..e652c7bfed8
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/ndb_user_transaction4.cpp
@@ -0,0 +1,770 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+extern "C" {
+#include "user_transaction.h"
+};
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <time.h>
+#include <NdbApi.hpp>
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+int
+T1(void * obj,
+ const SubscriberNumber number,
+ const Location new_location,
+ const ChangedBy changed_by,
+ const ChangedTime changed_time,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ DEBUG2("T1(%.*s):\n", SUBSCRIBER_NUMBER_LENGTH, number);
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T1-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T1: getNdbOperation", MyTransaction);
+
+ check = MyOperation->updateTuple();
+ CHECK_MINUS_ONE(check, "T1: updateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T1: equal subscriber",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&new_location);
+ CHECK_MINUS_ONE(check, "T1: setValue location",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_by",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_time",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T1: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+int
+T2(void * obj,
+ const SubscriberNumber number,
+ Location * readLocation,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ DEBUG2("T2(%.*s):\n", SUBSCRIBER_NUMBER_LENGTH, number);
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T2-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T2: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)readLocation);
+ CHECK_NULL(check2, "T2: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_NULL(check2, "T2: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_NULL(check2, "T2: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_NAME,
+ subscriberName);
+ CHECK_NULL(check2, "T2: getValue name",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T2: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+int
+T3(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ SessionDetails outSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ DEBUG3("T3(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T3-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T3-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T3-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T3-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T3-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T3-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T3-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T3-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T3-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T3-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&permission);
+ CHECK_NULL(check2, "T3-2: getValue allow_read",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("reading - ");
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T3-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-3: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T3-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-3: equal server id",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SESSION_DATA,
+ (char *)outSessionDetails);
+ CHECK_NULL(check2, "T3-3: getValue session details",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T3-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T3-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-4: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T3-4: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_READS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T3-4: inc value",
+ MyTransaction);
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ }
+ DEBUG("commit...");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T3: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ DEBUG("done\n");
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T4(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ const SessionDetails inSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ DEBUG3("T4(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T4-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T4-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T4-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T4-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T4-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T4-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T4-1: getValue sessions",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T4-4: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T4-2: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T4-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T4-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&permission);
+ CHECK_NULL(check2, "T4-2: getValue allow_insert",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == 0)){
+
+ DEBUG("inserting - ");
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T4-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "T4-3: insertTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-3: equal server id",
+ MyTransaction);
+
+ check = MyOperation->setValue(SESSION_DATA,
+ (char *)inSessionDetails);
+ CHECK_MINUS_ONE(check, "T4-3: setValue session details",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T4-5: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T4-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_INSERTS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T4-5: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback && (* outBranchExecuted)){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T4: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T4:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T5(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ DEBUG3("T5(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ Ndb * pNDB = (Ndb *) obj;
+ NdbConnection * MyTransaction = 0;
+ NdbOperation * MyOperation = 0;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T5-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T5-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T5-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T5-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T5-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T5-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T5-1: getValue sessions",
+ MyTransaction);
+
+ check = MyOperation->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T5-4: dec value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T5-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T5-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T5-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&permission);
+ CHECK_NULL(check2, "T5-2: getValue allow_delete",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("deleting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T5-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->deleteTuple();
+ CHECK_MINUS_ONE(check, "T5-3: deleteTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-3: equal server id",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T5-5: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T5-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_DELETES, (uint32)1);
+ CHECK_MINUS_ONE(check, "T5-5: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback && (* outBranchExecuted)){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T5: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T5:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/bench/ndb_user_transaction5.cpp b/storage/ndb/test/ndbapi/bench/ndb_user_transaction5.cpp
new file mode 100644
index 00000000000..86580008d10
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/ndb_user_transaction5.cpp
@@ -0,0 +1,769 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+extern "C" {
+#include "user_transaction.h"
+};
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <time.h>
+#include <NdbApi.hpp>
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+int
+T1(void * obj,
+ const SubscriberNumber number,
+ const Location new_location,
+ const ChangedBy changed_by,
+ const ChangedTime changed_time,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ DEBUG2("T1(%.*s):\n", SUBSCRIBER_NUMBER_LENGTH, number);
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T1-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T1: getNdbOperation", MyTransaction);
+
+ check = MyOperation->updateTuple();
+ CHECK_MINUS_ONE(check, "T1: updateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T1: equal subscriber",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&new_location);
+ CHECK_MINUS_ONE(check, "T1: setValue location",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_by",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_time",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T1: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+int
+T2(void * obj,
+ const SubscriberNumber number,
+ Location * readLocation,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ DEBUG2("T2(%.*s):\n", SUBSCRIBER_NUMBER_LENGTH, number);
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T2-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T2: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)readLocation);
+ CHECK_NULL(check2, "T2: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_NULL(check2, "T2: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_NULL(check2, "T2: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_NAME,
+ subscriberName);
+ CHECK_NULL(check2, "T2: getValue name",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T2: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+int
+T3(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ SessionDetails outSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T3-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T3-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T3-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T3-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T3-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T3-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T3-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T3-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T3-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T3-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&permission);
+ CHECK_NULL(check2, "T3-2: getValue allow_read",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T3(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("reading - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T3-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->simpleRead();
+ CHECK_MINUS_ONE(check, "T3-3: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T3-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-3: equal server id",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SESSION_DATA,
+ (char *)outSessionDetails);
+ CHECK_NULL(check2, "T3-3: getValue session details",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T3-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T3-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-4: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T3-4: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_READS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T3-4: inc value",
+ MyTransaction);
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ }
+ DEBUG("commit...");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T3: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ DEBUG("done\n");
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T4(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ const SessionDetails inSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T4-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T4-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T4-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T4-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T4-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T4-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T4-1: getValue sessions",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T4-4: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T4-2: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T4-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T4-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&permission);
+ CHECK_NULL(check2, "T4-2: getValue allow_insert",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T4(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == 0)){
+
+ DEBUG("inserting - ");
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T4-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "T4-3: insertTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-3: equal server id",
+ MyTransaction);
+
+ check = MyOperation->setValue(SESSION_DATA,
+ (char *)inSessionDetails);
+ CHECK_MINUS_ONE(check, "T4-3: setValue session details",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T4-5: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T4-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_INSERTS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T4-5: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback && (* outBranchExecuted)){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T4: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T4:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T5(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+ NdbConnection * MyTransaction = 0;
+ NdbOperation * MyOperation = 0;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T5-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T5-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T5-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T5-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T5-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T5-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T5-1: getValue sessions",
+ MyTransaction);
+
+ check = MyOperation->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T5-4: dec value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T5-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T5-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T5-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&permission);
+ CHECK_NULL(check2, "T5-2: getValue allow_delete",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T5(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("deleting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T5-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->deleteTuple();
+ CHECK_MINUS_ONE(check, "T5-3: deleteTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-3: equal server id",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T5-5: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T5-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_DELETES, (uint32)1);
+ CHECK_MINUS_ONE(check, "T5-5: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback && (* outBranchExecuted)){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T5: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T5:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/bench/ndb_user_transaction6.cpp b/storage/ndb/test/ndbapi/bench/ndb_user_transaction6.cpp
new file mode 100644
index 00000000000..262f38e9ffb
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/ndb_user_transaction6.cpp
@@ -0,0 +1,561 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+#include <string.h>
+#include "userHandle.h"
+#include "userInterface.h"
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <NdbApi.hpp>
+
+
+void
+userCheckpoint(UserHandle *uh){
+}
+
+inline
+NdbConnection *
+startTransaction(Ndb * pNDB, ServerId inServerId, const SubscriberNumber inNumber){
+
+ const int keyDataLenBytes = sizeof(ServerId)+SUBSCRIBER_NUMBER_LENGTH;
+ const int keyDataLen_64Words = keyDataLenBytes >> 3;
+
+ Uint64 keyDataBuf[keyDataLen_64Words+1]; // The "+1" is for rounding...
+
+ char * keyDataBuf_charP = (char *)&keyDataBuf[0];
+ Uint32 * keyDataBuf_wo32P = (Uint32 *)&keyDataBuf[0];
+
+ // Server Id comes first
+ keyDataBuf_wo32P[0] = inServerId;
+ // Then subscriber number
+ memcpy(&keyDataBuf_charP[sizeof(ServerId)], inNumber, SUBSCRIBER_NUMBER_LENGTH);
+
+ return pNDB->startTransaction(0, keyDataBuf_charP, keyDataLenBytes);
+}
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+void
+userTransaction_T1(UserHandle * uh,
+ SubscriberNumber number,
+ Location new_location,
+ ChangedBy changed_by,
+ ChangedTime changed_time){
+ Ndb * pNDB = uh->pNDB;
+
+ DEBUG2("T1(%.*s):\n", SUBSCRIBER_NUMBER_LENGTH, number);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction != NULL) {
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ if (MyOperation != NULL) {
+ MyOperation->updateTuple();
+ MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ MyOperation->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&new_location);
+ MyOperation->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ MyOperation->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ check = MyTransaction->execute( Commit );
+ if (check != -1) {
+ pNDB->closeTransaction(MyTransaction);
+ return;
+ } else {
+ CHECK_MINUS_ONE(check, "T1: Commit",
+ MyTransaction);
+ }//if
+ } else {
+ CHECK_NULL(MyOperation, "T1: getNdbOperation", MyTransaction);
+ }//if
+ } else {
+ error_handler("T1-1: startTranscation", pNDB->getNdbErrorString(), pNDB->getNdbError());
+ }//if
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+void
+userTransaction_T2(UserHandle * uh,
+ SubscriberNumber number,
+ Location * readLocation,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName){
+ Ndb * pNDB = uh->pNDB;
+
+ DEBUG2("T2(%.*s):\n", SUBSCRIBER_NUMBER_LENGTH, number);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T2-1: startTransaction", pNDB->getNdbErrorString(), pNDB->getNdbError());
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T2: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->readTuple();
+ MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)readLocation);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ MyOperation->getValue(IND_SUBSCRIBER_NAME,
+ subscriberName);
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T2: Commit",
+ MyTransaction);
+ pNDB->closeTransaction(MyTransaction);
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+void
+userTransaction_T3(UserHandle * uh,
+ SubscriberNumber inNumber,
+ ServerId inServerId,
+ ServerBit inServerBit,
+ SessionDetails outSessionDetails,
+ BranchExecuted * outBranchExecuted){
+ Ndb * pNDB = uh->pNDB;
+
+ char outChangedBy [sizeof(ChangedBy) +(4-(sizeof(ChangedBy) & 3))];
+ char outChangedTime [sizeof(ChangedTime)+(4-(sizeof(ChangedTime) & 3))];
+ Location outLocation;
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+ SubscriberSuffix inSuffix;
+
+ DEBUG3("T3(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = startTransaction(pNDB, inServerId, inNumber);
+ if (MyTransaction == NULL)
+ error_handler("T3-1: startTranscation", pNDB->getNdbErrorString(), pNDB->getNdbError());
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T3-1: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->readTuple();
+ MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&outLocation);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T3-2: getNdbOperation",
+ MyTransaction);
+
+
+ MyOperation->readTuple();
+ MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ MyOperation->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&permission);
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ memcpy(inSuffix,
+ &inNumber[SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH], SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+ DEBUG2("reading(%.*s) - ", SUBSCRIBER_NUMBER_SUFFIX_LENGTH, inSuffix);
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T3-3: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->simpleRead();
+
+ MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ MyOperation->getValue(IND_SESSION_DATA,
+ (char *)outSessionDetails);
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T3-4: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->interpretedUpdateTuple();
+ MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ MyOperation->incValue(IND_SERVER_READS, (uint32)1);
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ }
+ DEBUG("commit...");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T3: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ DEBUG("done\n");
+}
+
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+void
+userTransaction_T4(UserHandle * uh,
+ SubscriberNumber inNumber,
+ ServerId inServerId,
+ ServerBit inServerBit,
+ SessionDetails inSessionDetails,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted){
+
+ Ndb * pNDB = uh->pNDB;
+
+ char outChangedBy [sizeof(ChangedBy) +(4-(sizeof(ChangedBy) & 3))];
+ char outChangedTime [sizeof(ChangedTime)+(4-(sizeof(ChangedTime) & 3))];
+ Location outLocation;
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+ SubscriberSuffix inSuffix;
+
+ DEBUG3("T4(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = startTransaction(pNDB, inServerId, inNumber);
+ if (MyTransaction == NULL)
+ error_handler("T4-1: startTranscation", pNDB->getNdbErrorString(), pNDB->getNdbError());
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-1: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->interpretedUpdateTuple();
+ MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&outLocation);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ MyOperation->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ check = MyTransaction->execute( NoCommit );
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T4-2: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->readTuple();
+ MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ MyOperation->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&permission);
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == 0)){
+
+ memcpy(inSuffix,
+ &inNumber[SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH], SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+
+ DEBUG2("inserting(%.*s) - ", SUBSCRIBER_NUMBER_SUFFIX_LENGTH, inSuffix);
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T4-3: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->insertTuple();
+ MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ MyOperation->setValue(SESSION_DATA,
+ (char *)inSessionDetails);
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T4-5: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->interpretedUpdateTuple();
+ MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ MyOperation->incValue(IND_SERVER_INSERTS, (uint32)1);
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ }
+
+ if(!inDoRollback && (* outBranchExecuted)){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T4: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T4:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+}
+
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+void
+userTransaction_T5(UserHandle * uh,
+ SubscriberNumber inNumber,
+ ServerId inServerId,
+ ServerBit inServerBit,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted){
+ Ndb * pNDB = uh->pNDB;
+
+ DEBUG3("T5(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ NdbConnection * MyTransaction = 0;
+ NdbOperation * MyOperation = 0;
+
+ char outChangedBy [sizeof(ChangedBy) +(4-(sizeof(ChangedBy) & 3))];
+ char outChangedTime [sizeof(ChangedTime)+(4-(sizeof(ChangedTime) & 3))];
+ Location outLocation;
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+ SubscriberSuffix inSuffix;
+
+ int check;
+ NdbRecAttr * check2;
+
+ MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T5-1: startTranscation", pNDB->getNdbErrorString(), pNDB->getNdbError());
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-1: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->interpretedUpdateTuple();
+ MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&outLocation);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ &outChangedBy[0]);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ &outChangedTime[0]);
+ MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ MyOperation->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ MyTransaction->execute( NoCommit );
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T5-2: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->readTuple();
+ MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ MyOperation->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&permission);
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ memcpy(inSuffix,
+ &inNumber[SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH], SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+
+ DEBUG2("deleting(%.*s) - ", SUBSCRIBER_NUMBER_SUFFIX_LENGTH, inSuffix);
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T5-3: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->deleteTuple();
+ MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T5-5: getNdbOperation",
+ MyTransaction);
+
+
+ MyOperation->interpretedUpdateTuple();
+ MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ MyOperation->incValue(IND_SERVER_DELETES, (uint32)1);
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ }
+
+ if(!inDoRollback && (* outBranchExecuted)){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T5: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T5:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+}
+
diff --git a/storage/ndb/test/ndbapi/bench/testData.h b/storage/ndb/test/ndbapi/bench/testData.h
new file mode 100644
index 00000000000..3db85e7342e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/testData.h
@@ -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 TESTDATA_H
+#define TESTDATA_H
+
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+#include <NdbTick.h>
+#include <NdbThread.h>
+#include <NDBT_Stats.hpp>
+#include <random.h>
+#include "testDefinitions.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+#define NUM_TRANSACTION_TYPES 5
+#define SESSION_LIST_LENGTH 1000
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+typedef struct {
+ SubscriberNumber subscriberNumber;
+ ServerId serverId;
+} SessionElement;
+
+typedef struct {
+ SessionElement list[SESSION_LIST_LENGTH];
+ unsigned int readIndex;
+ unsigned int writeIndex;
+ unsigned int numberInList;
+} SessionList;
+
+typedef struct {
+ unsigned int count;
+ unsigned int branchExecuted;
+ unsigned int rollbackExecuted;
+
+ /**
+ * Latency measures
+ */
+ NDB_TICKS startTime;
+ NDBT_Stats latency;
+ unsigned int latencyCounter;
+
+ inline void startLatency(){
+ if((latencyCounter & 127) == 127)
+ startTime = NdbTick_CurrentMillisecond();
+ }
+
+ inline void stopLatency(){
+ if((latencyCounter & 127) == 127){
+ const NDB_TICKS tmp = NdbTick_CurrentMillisecond() - startTime;
+ latency.addObservation(tmp);
+ }
+ latencyCounter++;
+ }
+} TransactionDefinition;
+
+typedef struct {
+ RandomSequence transactionSequence;
+ RandomSequence rollbackSequenceT4;
+ RandomSequence rollbackSequenceT5;
+
+ TransactionDefinition transactions[NUM_TRANSACTION_TYPES];
+
+ unsigned int totalTransactions;
+
+ double outerLoopTime;
+ double outerTps;
+
+ SessionList activeSessions;
+
+} GeneratorStatistics;
+
+typedef enum{
+ Runnable,
+ Running
+} RunState ;
+
+typedef struct {
+ SubscriberNumber number;
+ SubscriberSuffix suffix;
+ SubscriberName name;
+ Location location;
+ ChangedBy changed_by;
+ ChangedTime changed_time;
+ ServerId server_id;
+ ServerBit server_bit;
+ SessionDetails session_details;
+
+ GroupId group_id;
+ ActiveSessions sessions;
+ Permission permission;
+
+ unsigned int do_rollback;
+
+ unsigned int branchExecuted;
+ unsigned int sessionElement;
+} TransactionData ;
+
+typedef struct {
+ struct NdbThread* pThread;
+
+ unsigned long randomSeed;
+ unsigned long changedTime;
+
+ unsigned int warmUpSeconds;
+ unsigned int testSeconds;
+ unsigned int coolDownSeconds;
+
+ GeneratorStatistics generator;
+
+ /**
+ * For async execution
+ */
+ RunState runState;
+ double startTime;
+ TransactionData transactionData;
+ struct Ndb * pNDB;
+} ThreadData;
+
+/***************************************************************
+ * P U B L I C F U N C T I O N S *
+ ***************************************************************/
+
+/***************************************************************
+ * E X T E R N A L D A T A *
+ ***************************************************************/
+
+
+
+#endif /* TESTDATA_H */
+
diff --git a/storage/ndb/test/ndbapi/bench/testDefinitions.h b/storage/ndb/test/ndbapi/bench/testDefinitions.h
new file mode 100644
index 00000000000..2f4aeb30975
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/testDefinitions.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 */
+
+#ifndef TESTDEFINITIONS_H
+#define TESTDEFINITIONS_H
+
+/***************************************************************/
+/* I N C L U D E D F I L E S */
+/***************************************************************/
+
+#include <ndb_types.h>
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+#define OP_PER_TRANS 200
+#define NO_OF_SUBSCRIBERS 500000
+#define NO_OF_GROUPS 100
+#define NO_OF_SERVERS 20
+
+#define SUBSCRIBER_NUMBER_LENGTH 12
+#define SUBSCRIBER_NUMBER_SUFFIX_LENGTH 2
+
+#define SUBSCRIBER_NAME_LENGTH 32
+#define CHANGED_BY_LENGTH 32
+#define CHANGED_TIME_LENGTH 32
+#define SESSION_DETAILS_LENGTH 2000
+#define SERVER_NAME_LENGTH 32
+#define GROUP_NAME_LENGTH 32
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+#define PADDING 4
+
+typedef char SubscriberNumber[SUBSCRIBER_NUMBER_LENGTH];
+typedef char SubscriberSuffix[SUBSCRIBER_NUMBER_SUFFIX_LENGTH + 2];
+typedef char SubscriberName[SUBSCRIBER_NAME_LENGTH];
+typedef char ServerName[SERVER_NAME_LENGTH];
+typedef char GroupName[GROUP_NAME_LENGTH];
+typedef char ChangedBy[CHANGED_BY_LENGTH];
+typedef char ChangedTime[CHANGED_TIME_LENGTH];
+typedef char SessionDetails[SESSION_DETAILS_LENGTH];
+typedef Uint32 ServerId;
+typedef Uint32 ServerBit;
+typedef Uint32 GroupId;
+typedef Uint32 Location;
+typedef Uint32 Permission;
+
+typedef Uint32 Counter;
+typedef Uint32 ActiveSessions;
+typedef unsigned int BranchExecuted;
+typedef unsigned int DoRollback;
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+
+
+#endif /* TESTDEFINITIONS_H */
+
diff --git a/storage/ndb/test/ndbapi/bench/userInterface.cpp b/storage/ndb/test/ndbapi/bench/userInterface.cpp
new file mode 100644
index 00000000000..35e88183230
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/userInterface.cpp
@@ -0,0 +1,744 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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>
+#ifndef NDB_WIN32
+#include <sys/time.h>
+#endif
+
+#include "ndb_error.hpp"
+#include "userInterface.h"
+#include <NdbThread.h>
+#include <NdbTick.h>
+#include <NdbMutex.h>
+#include <NdbSleep.h>
+#include "ndb_schema.hpp"
+#include <NDBT.hpp>
+#include <NdbSchemaCon.hpp>
+
+/***************************************************************
+* 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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+extern int localDbPrepare(UserHandle *uh);
+
+static int dbCreate(UserHandle *uh);
+
+/***************************************************************
+* L O C A L D A T A *
+***************************************************************/
+
+/***************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+/***************************************************************
+****************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+/*-----------------------------------*/
+/* Time related Functions */
+/* */
+/* Returns a double value in seconds */
+/*-----------------------------------*/
+double userGetTimeSync(void)
+{
+ static int initialized = 0;
+ static NDB_TICKS initSecs = 0;
+ static Uint32 initMicros = 0;
+ double timeValue = 0;
+
+ if ( !initialized ) {
+ initialized = 1;
+ NdbTick_CurrentMicrosecond(&initSecs, &initMicros);
+ timeValue = 0.0;
+ } else {
+ NDB_TICKS secs = 0;
+ Uint32 micros = 0;
+
+ NdbTick_CurrentMicrosecond(&secs, &micros);
+
+ double s = (double)secs - (double)initSecs;
+ double us = (double)secs - (double)initMicros;
+
+ timeValue = s + (us / 1000000.0);
+ }
+
+ return timeValue;
+}
+
+// 0 - OK
+// 1 - Retry transaction
+// 2 - Permanent
+int
+userDbCommit(UserHandle *uh){
+ if(uh->pCurrTrans != 0){
+ int check = uh->pCurrTrans->execute( Commit );
+ NdbError err = uh->pCurrTrans->getNdbError();
+ uh->pNDB->closeTransaction(uh->pCurrTrans);
+ uh->pCurrTrans = 0;
+
+ if(err.status != NdbError::Success)
+ ndbout << err << endl;
+
+ if(err.status == NdbError::TemporaryError &&
+ err.classification == NdbError::OverloadError){
+ NdbSleep_SecSleep(3);
+ }
+
+ return err.status;
+ }
+ return 2;
+}
+
+/**
+ * TRUE - Normal table
+ * FALSE - Table w.o. checkpoing and logging
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int useTableLogging;
+#ifdef __cplusplus
+}
+#endif
+
+
+int
+create_table_server(Ndb * pNdb){
+ int check;
+ NdbSchemaCon * MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pNdb);
+ if( MySchemaTransaction == NULL )
+ error_handler("startSchemaTransaction", pNdb->getNdbError(), 0);
+
+ NdbSchemaOp * MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL )
+ error_handler("getNdbSchemaOp", MySchemaTransaction->getNdbError(), 0);
+
+ // Create table
+ check = MySchemaOp->createTable( SERVER_TABLE,
+ 8, // Table size
+ TupleKey, // Key Type
+ 1 // Nr of Pages
+ ,DistributionGroup,
+ 6,
+ 78,
+ 80,
+ 1,
+ useTableLogging
+ );
+ if( check == -1 )
+ error_handler("createTable", MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute
+ ( SERVER_SUBSCRIBER_SUFFIX,
+ TupleKey,
+ sizeof(char) << 3,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute,
+ 0,
+ 0,
+ 1,
+ 16);
+ if( check == -1 )
+ error_handler("createAttribute (subscriber suffix)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute( SERVER_ID,
+ TupleKey,
+ sizeof(ServerId) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (serverid)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( SERVER_NAME,
+ NoKey,
+ sizeof(char) << 3,
+ SERVER_NAME_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (server name)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( SERVER_READS,
+ NoKey,
+ sizeof(Counter) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (server reads)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SERVER_INSERTS,
+ NoKey,
+ sizeof(Counter) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (server inserts)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SERVER_DELETES,
+ NoKey,
+ sizeof(Counter) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (server deletes)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ if( MySchemaTransaction->execute() == -1 ) {
+ error_handler("schemaTransaction->execute()",
+ MySchemaTransaction->getNdbError(), 0);
+ }
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return 0;
+}
+
+int
+create_table_group(Ndb * pNdb){
+ int check;
+
+ NdbSchemaCon * MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pNdb);
+ if( MySchemaTransaction == NULL )
+ error_handler("startSchemaTransaction", pNdb->getNdbError(), 0);
+
+ NdbSchemaOp * MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL )
+ error_handler("getNdbSchemaOp", MySchemaTransaction->getNdbError(), 0);
+
+ // Create table
+ check = MySchemaOp->createTable( GROUP_TABLE,
+ 8, // Table size
+ TupleKey, // Key Type
+ 1 // Nr of Pages
+ ,All,
+ 6,
+ 78,
+ 80,
+ 1,
+ useTableLogging
+ );
+
+ if( check == -1 )
+ error_handler("createTable", MySchemaTransaction->getNdbError(), 0);
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute( GROUP_ID,
+ TupleKey,
+ sizeof(GroupId) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (group id)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( GROUP_NAME,
+ NoKey,
+ sizeof(char) << 3,
+ GROUP_NAME_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (group name)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( GROUP_ALLOW_READ,
+ NoKey,
+ sizeof(Permission) << 3,
+ 1,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (group read)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( GROUP_ALLOW_INSERT,
+ NoKey,
+ sizeof(Permission) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (group insert)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( GROUP_ALLOW_DELETE,
+ NoKey,
+ sizeof(Permission) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (group delete)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ if( MySchemaTransaction->execute() == -1 ) {
+ error_handler("schemaTransaction->execute()",
+ MySchemaTransaction->getNdbError(), 0);
+ }
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return 0;
+}
+
+int
+create_table_subscriber(Ndb * pNdb){
+ int check;
+ NdbSchemaCon * MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pNdb);
+ if( MySchemaTransaction == NULL )
+ error_handler("startSchemaTransaction", pNdb->getNdbError(), 0);
+
+ NdbSchemaOp * MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL )
+ error_handler("getNdbSchemaOp", MySchemaTransaction->getNdbError(), 0);
+
+ // Create table
+ check = MySchemaOp->createTable( SUBSCRIBER_TABLE,
+ 8, // Table size
+ TupleKey, // Key Type
+ 1 // Nr of Pages
+ ,DistributionGroup,
+ 6,
+ 78,
+ 80,
+ 1,
+ useTableLogging
+ );
+ if( check == -1 )
+ error_handler("createTable", MySchemaTransaction->getNdbError(), 0);
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute
+ ( SUBSCRIBER_NUMBER,
+ TupleKey,
+ sizeof(char) << 3,
+ SUBSCRIBER_NUMBER_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute,
+ 0,
+ 0,
+ 1,
+ 16);
+ if( check == -1 )
+ error_handler("createAttribute (subscriber number)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SUBSCRIBER_NAME,
+ NoKey,
+ sizeof(char) << 3,
+ SUBSCRIBER_NAME_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (subscriber name)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( SUBSCRIBER_GROUP,
+ NoKey,
+ sizeof(GroupId) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (subscriber_group)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( SUBSCRIBER_LOCATION,
+ NoKey,
+ sizeof(Location) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (server reads)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SUBSCRIBER_SESSIONS,
+ NoKey,
+ sizeof(ActiveSessions) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (subscriber_sessions)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SUBSCRIBER_CHANGED_BY,
+ NoKey,
+ sizeof(char) << 3,
+ CHANGED_BY_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (subscriber_changed_by)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SUBSCRIBER_CHANGED_TIME,
+ NoKey,
+ sizeof(char) << 3,
+ CHANGED_TIME_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (subscriber_changed_time)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ if( MySchemaTransaction->execute() == -1 ) {
+ error_handler("schemaTransaction->execute()",
+ MySchemaTransaction->getNdbError(), 0);
+ }
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return 0;
+}
+
+int
+create_table_session(Ndb * pNdb){
+ int check;
+ NdbSchemaCon * MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pNdb);
+ if( MySchemaTransaction == NULL )
+ error_handler("startSchemaTransaction", pNdb->getNdbError(), 0);
+
+ NdbSchemaOp * MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL )
+ error_handler("getNdbSchemaOp",
+ MySchemaTransaction->getNdbError(), 0);
+
+ // Create table
+ check = MySchemaOp->createTable( SESSION_TABLE,
+ 8, // Table size
+ TupleKey, // Key Type
+ 1 // Nr of Pages
+ ,DistributionGroup,
+ 6,
+ 78,
+ 80,
+ 1,
+ useTableLogging
+ );
+ if( check == -1 )
+ error_handler("createTable", MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SESSION_SUBSCRIBER,
+ TupleKey,
+ sizeof(char) << 3,
+ SUBSCRIBER_NUMBER_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute,
+ 0,
+ 0,
+ 1,
+ 16);
+ if( check == -1 )
+ error_handler("createAttribute (session_subscriber)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute( SESSION_SERVER,
+ TupleKey,
+ sizeof(ServerId) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (session_server)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( SESSION_DATA,
+ NoKey,
+ sizeof(char) << 3,
+ SESSION_DETAILS_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (session_data)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ if( MySchemaTransaction->execute() == -1 ) {
+ error_handler("schemaTransaction->execute()",
+ MySchemaTransaction->getNdbError(), 0);
+ }
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return 0;
+}
+
+void
+create_table(const char * name, int (* function)(Ndb * pNdb), Ndb* pNdb){
+ printf("creating table %s...", name);
+ if(pNdb->getDictionary()->getTable(name) != 0){
+ printf(" it already exists\n");
+ return;
+ } else {
+ printf("\n");
+ }
+ function(pNdb);
+ printf("creating table %s... done\n", name);
+}
+
+static int dbCreate(Ndb * pNdb)
+{
+ create_table(SUBSCRIBER_TABLE, create_table_subscriber, pNdb);
+ create_table(GROUP_TABLE , create_table_group, pNdb);
+ create_table(SESSION_TABLE , create_table_session, pNdb);
+ create_table(SERVER_TABLE , create_table_server, pNdb);
+ return 0;
+}
+
+#ifndef NDB_WIN32
+#include <unistd.h>
+#endif
+
+UserHandle*
+userDbConnect(uint32 createDb, char *dbName)
+{
+ Ndb_cluster_connection *con= new Ndb_cluster_connection();
+ if(con->connect(12, 5, 1) != 0)
+ {
+ ndbout << "Unable to connect to management server." << endl;
+ return 0;
+ }
+ if (con->wait_until_ready(30,0) < 0)
+ {
+ ndbout << "Cluster nodes not ready in 30 seconds." << endl;
+ return 0;
+ }
+
+ Ndb * pNdb = new Ndb(con, dbName);
+
+ //printf("Initializing...\n");
+ pNdb->init();
+
+ //printf("Waiting...");
+ while(pNdb->waitUntilReady() != 0){
+ //printf("...");
+ }
+ // printf("done\n");
+
+ if( createDb )
+ dbCreate(pNdb);
+
+
+ UserHandle * uh = new UserHandle;
+ uh->pNCC = con;
+ uh->pNDB = pNdb;
+ uh->pCurrTrans = 0;
+
+ return uh;
+}
+
+void userDbDisconnect(UserHandle *uh)
+{
+ delete uh;
+}
+
+int userDbInsertServer(UserHandle *uh,
+ ServerId serverId,
+ SubscriberSuffix suffix,
+ ServerName name)
+{
+ int check;
+
+ uint32 noOfRead = 0;
+ uint32 noOfInsert = 0;
+ uint32 noOfDelete = 0;
+
+ NdbConnection * MyTransaction = 0;
+ if(uh->pCurrTrans != 0){
+ MyTransaction = uh->pCurrTrans;
+ } else {
+ uh->pCurrTrans = MyTransaction = uh->pNDB->startTransaction();
+ }
+ if (MyTransaction == NULL)
+ error_handler("startTranscation", uh->pNDB->getNdbError(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "getNdbOperation", MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "insert tuple", MyTransaction);
+
+ check = MyOperation->equal(SERVER_ID, (char*)&serverId);
+ CHECK_MINUS_ONE(check, "setValue id", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_SUBSCRIBER_SUFFIX, suffix);
+ CHECK_MINUS_ONE(check, "setValue suffix", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_NAME, name);
+ CHECK_MINUS_ONE(check, "setValue name", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_READS, (char*)&noOfRead);
+ CHECK_MINUS_ONE(check, "setValue reads", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_INSERTS, (char*)&noOfInsert);
+ CHECK_MINUS_ONE(check, "setValue inserts", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_DELETES, (char*)&noOfDelete);
+ CHECK_MINUS_ONE(check, "setValue deletes", MyTransaction);
+
+ return 0;
+}
+
+int userDbInsertSubscriber(UserHandle *uh,
+ SubscriberNumber number,
+ uint32 groupId,
+ SubscriberName name)
+{
+ int check;
+ uint32 activeSessions = 0;
+ Location l = 0;
+ ChangedBy changedBy; snprintf(changedBy, sizeof(changedBy), "ChangedBy");
+ ChangedTime changedTime; snprintf(changedTime, sizeof(changedTime), "ChangedTime");
+
+ NdbConnection * MyTransaction = 0;
+ if(uh->pCurrTrans != 0){
+ MyTransaction = uh->pCurrTrans;
+ } else {
+ uh->pCurrTrans = MyTransaction = uh->pNDB->startTransaction();
+ }
+ if (MyTransaction == NULL)
+ error_handler("startTranscation", uh->pNDB->getNdbError(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "getNdbOperation", MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "insertTuple", MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER, number);
+ CHECK_MINUS_ONE(check, "equal", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_NAME, name);
+ CHECK_MINUS_ONE(check, "setValue name", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_GROUP, (char*)&groupId);
+ CHECK_MINUS_ONE(check, "setValue group", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_LOCATION, (char*)&l);
+ CHECK_MINUS_ONE(check, "setValue location", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_SESSIONS, (char*)&activeSessions);
+ CHECK_MINUS_ONE(check, "setValue sessions", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_CHANGED_BY, changedBy);
+ CHECK_MINUS_ONE(check, "setValue changedBy", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_CHANGED_TIME, changedTime);
+ CHECK_MINUS_ONE(check, "setValue changedTime", MyTransaction);
+
+ return 0;
+}
+
+int userDbInsertGroup(UserHandle *uh,
+ GroupId groupId,
+ GroupName name,
+ Permission allowRead,
+ Permission allowInsert,
+ Permission allowDelete)
+{
+ int check;
+
+ NdbConnection * MyTransaction = 0;
+ if(uh->pCurrTrans != 0){
+ MyTransaction = uh->pCurrTrans;
+ } else {
+ uh->pCurrTrans = MyTransaction = uh->pNDB->startTransaction();
+ }
+ if (MyTransaction == NULL)
+ error_handler("startTranscation", uh->pNDB->getNdbError(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "getNdbOperation", MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "insertTuple", MyTransaction);
+
+ check = MyOperation->equal(GROUP_ID, (char*)&groupId);
+ CHECK_MINUS_ONE(check, "equal", MyTransaction);
+
+ check = MyOperation->setValue(GROUP_NAME, name);
+ CHECK_MINUS_ONE(check, "setValue name", MyTransaction);
+
+ check = MyOperation->setValue(GROUP_ALLOW_READ, (char*)&allowRead);
+ CHECK_MINUS_ONE(check, "setValue allowRead", MyTransaction);
+
+ check = MyOperation->setValue(GROUP_ALLOW_INSERT, (char*)&allowInsert);
+ CHECK_MINUS_ONE(check, "setValue allowInsert", MyTransaction);
+
+ check = MyOperation->setValue(GROUP_ALLOW_DELETE, (char*)&allowDelete);
+ CHECK_MINUS_ONE(check, "setValue allowDelete", MyTransaction);
+
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/bench/userInterface.h b/storage/ndb/test/ndbapi/bench/userInterface.h
new file mode 100644
index 00000000000..bad61fcf171
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bench/userInterface.h
@@ -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 DBINTERFACE_H
+#define DBINTERFACE_H
+
+/***************************************************************/
+/* I N C L U D E D F I L E S */
+/***************************************************************/
+
+#include "testDefinitions.h"
+#include "testData.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+/*-----------------------*/
+/* Default Database Name */
+/*-----------------------*/
+#define DEFAULTDB "TestDbClient"
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+typedef struct Ndb Ndb;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern void showTime();
+ extern double userGetTime(void);
+ extern Ndb *asyncDbConnect(int parallellism);
+ extern void asyncDbDisconnect(Ndb* pNDB);
+
+ extern void start_T1(Ndb * uh, ThreadData * data, int async);
+ extern void start_T2(Ndb * uh, ThreadData * data, int async);
+ extern void start_T3(Ndb * uh, ThreadData * data, int async);
+ extern void start_T4(Ndb * uh, ThreadData * data, int async);
+ extern void start_T5(Ndb * uh, ThreadData * data, int async);
+
+ extern void complete_T1(ThreadData * data);
+ extern void complete_T2(ThreadData * data);
+ extern void complete_T3(ThreadData * data);
+ extern void complete_T4(ThreadData * data);
+ extern void complete_T5(ThreadData * data);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+/***************************************************************/
+/* I N C L U D E D F I L E S */
+/***************************************************************/
+
+#include "testDefinitions.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+/*-----------------------*/
+/* Default Database Name */
+/*-----------------------*/
+#define DEFAULTDB "TestDbClient"
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+typedef struct {
+ struct Ndb_cluster_connection* pNCC;
+ struct Ndb * pNDB;
+ struct NdbTransaction * pCurrTrans;
+} UserHandle;
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern double userGetTimeSync(void);
+
+extern void userCheckpoint(UserHandle *uh);
+
+extern UserHandle *userDbConnect(uint32 createDb, char *dbName);
+extern void userDbDisconnect(UserHandle *uh);
+
+extern int userDbInsertServer(UserHandle *uh,
+ ServerId serverId,
+ SubscriberSuffix suffix,
+ ServerName name);
+
+extern int userDbInsertSubscriber(UserHandle *uh,
+ SubscriberNumber number,
+ uint32 groupId,
+ SubscriberName name);
+
+extern int userDbInsertGroup(UserHandle *uh,
+ GroupId groupId,
+ GroupName name,
+ Permission allowRead,
+ Permission allowInsert,
+ Permission allowDelete);
+
+ extern int userDbCommit(UserHandle *uh);
+ extern int userDbRollback(UserHandle *uh);
+
+#ifdef __cplusplus
+}
+#endif
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+#endif /* DBINTERFACE_H */
+
diff --git a/storage/ndb/test/ndbapi/benchronja.cpp b/storage/ndb/test/ndbapi/benchronja.cpp
new file mode 100644
index 00000000000..a7523e8e416
--- /dev/null
+++ b/storage/ndb/test/ndbapi/benchronja.cpp
@@ -0,0 +1,1201 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* ***************************************************
+ NODEREC
+ Perform benchmark of insert, update and delete transactions
+
+ Arguments:
+ -t Number of threads to start, default 1
+ -o Number of loops per thread, default 100000
+
+
+ * *************************************************** */
+
+#include <ndb_global.h>
+
+#include <NdbApi.hpp>
+#include <NdbTest.hpp>
+#include <NdbOut.hpp>
+#include <NdbThread.h>
+#include <NdbSleep.h>
+#include <NdbMain.h>
+#include <NdbTimer.hpp>
+#include <NdbTick.h>
+#include <random.h>
+
+#define MAX_TIMERS 4
+#define MAXSTRLEN 16
+#define MAXATTR 64
+#define MAXTABLES 64
+#define MAXTHREADS 256
+#define MAXATTRSIZE 8000
+#define START_TIMER NdbTimer timer; timer.doStart();
+#define STOP_TIMER timer.doStop();
+#define START_TIMER_TOP NdbTimer timer_top; timer_top.doStart();
+#define STOP_TIMER_TOP timer_top.doStop();
+
+void* ThreadExec(void*);
+struct ThreadNdb
+{
+ int NoOfOps;
+ int ThreadNo;
+ Ndb* NdbRef;
+};
+
+static NdbThread* threadLife[MAXTHREADS];
+static unsigned int tNoOfThreads;
+static unsigned int tNoOfOpsPerExecute;
+static unsigned int tNoOfRecords;
+static unsigned int tNoOfOperations;
+static int ThreadReady[MAXTHREADS];
+static int ThreadStart[MAXTHREADS];
+
+NDB_COMMAND(benchronja, "benchronja", "benchronja", "benchronja", 65535){
+ ndb_init();
+
+ ThreadNdb tabThread[MAXTHREADS];
+ int i = 0 ;
+ int cont = 0 ;
+ Ndb* pMyNdb = NULL ; //( "TEST_DB" );
+ int tmp = 0 ;
+ int nTest = 0 ;
+ char inp[100] ;
+
+ tNoOfThreads = 1; // Default Value
+ tNoOfOpsPerExecute = 1; // Default Value
+ tNoOfOperations = 100000; // Default Value
+ tNoOfRecords = 500 ; // Default Value <epaulsa: changed from original 500,000 to match 'initronja's' default
+ i = 1;
+ while (argc > 1)
+ {
+ if (strcmp(argv[i], "-t") == 0){
+ tNoOfThreads = atoi(argv[i+1]);
+ if ((tNoOfThreads < 1) || (tNoOfThreads > MAXTHREADS)) goto error_input;
+ }else if (strcmp(argv[i], "-o") == 0){
+ tNoOfOperations = atoi(argv[i+1]);
+ if (tNoOfOperations < 1) goto error_input;
+ }else if (strcmp(argv[i], "-r") == 0){
+ tNoOfRecords = atoi(argv[i+1]);
+ if ((tNoOfRecords < 1) || (tNoOfRecords > 1000000000)) goto error_input;
+ }else if (strcmp(argv[i], "-p") == 0){
+ nTest = atoi(argv[i+1]) ;
+ if (0 > nTest || 18 < nTest) goto error_input ;
+ }else if (strcmp(argv[i], "-c") == 0){
+ tNoOfOpsPerExecute = atoi(argv[i+1]);
+ if ((tNoOfOpsPerExecute < 1) || (tNoOfOpsPerExecute > 1024)) goto error_input;
+ }else{
+ goto error_input;
+ }
+ argc -= 2;
+ i = i + 2;
+ }
+
+ ndbout << "Initialisation started. " << endl;
+ pMyNdb = new Ndb("TEST_DB") ;
+ pMyNdb->init();
+ ndbout << "Initialisation completed. " << endl;
+
+ ndbout << endl << "Execute Ronja Benchmark" << endl;
+ ndbout << " NdbAPI node with id = " << pMyNdb->getNodeId() << endl;
+ ndbout << " " << tNoOfThreads << " thread(s) " << endl;
+ ndbout << " " << tNoOfOperations << " transaction(s) per thread and round " << endl;
+
+ if (pMyNdb->waitUntilReady(120) != 0) {
+ ndbout << "Benchmark failed - NDB is not ready" << endl;
+ delete pMyNdb ;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }//if
+
+ NdbThread_SetConcurrencyLevel(2 + tNoOfThreads);
+
+ for (i = 0; i < tNoOfThreads ; i++) {
+ ThreadReady[i] = 0;
+ ThreadStart[i] = 0;
+ }//for
+
+ for (i = 0; i < tNoOfThreads ; i++) {
+ tabThread[i].ThreadNo = i;
+ tabThread[i].NdbRef = NULL;
+ tabThread[i].NoOfOps = tNoOfOperations;
+ threadLife[i] = NdbThread_Create(ThreadExec,
+ (void**)&tabThread[i],
+ 32768,
+ "RonjaThread",
+ NDB_THREAD_PRIO_LOW);
+ }//for
+
+ cont = 1;
+ while (cont) {
+ NdbSleep_MilliSleep(10);
+ cont = 0;
+ for (i = 0; i < tNoOfThreads ; i++)
+ if (!ThreadReady[i]) cont = 1;
+ }//while
+
+ ndbout << "All threads started" << endl;
+
+ if(!nTest){
+
+ for (;;){
+
+ inp[0] = 0;
+ ndbout << endl << "What to do next:" << endl;
+ ndbout << "1 \t=> Perform lookups in short table" << endl;
+ ndbout << "2 \t=> Perform lookups in long table" << endl;
+ ndbout << "3 \t=> Perform updates in short table" << endl;
+ ndbout << "4 \t=> Perform updates in long table" << endl;
+ ndbout << "5 \t=> Perform 50% lookups/50% updates in short table" << endl;
+ ndbout << "6 \t=> Perform 50% lookups/50% updates in long table" << endl;
+ ndbout << "7 \t=> Perform 80% lookups/20% updates in short table" << endl;
+ ndbout << "8 \t=> Perform 80% lookups/20% updates in long table" << endl;
+ ndbout << "9 \t=> Perform 25% lookups short/25% lookups long/25% updates short/25% updates long" << endl;
+ ndbout << "10\t=> Test bug with replicated interpreted updates, short table" << endl;
+ ndbout << "11\t=> Test interpreter functions, short table" << endl;
+ ndbout << "12\t=> Test bug with replicated interpreted updates, long table" << endl;
+ ndbout << "13\t=> Test interpreter functions, long table" << endl;
+ ndbout << "14\t=> Perform lookups in short table, no guess of TC" << endl;
+ ndbout << "15\t=> Perform lookups in long table, no guess of TC" << endl;
+ ndbout << "16\t=> Perform updates in short table, no guess of TC" << endl;
+ ndbout << "17\t=> Perform updates in long table, no guess of TC" << endl;
+ ndbout << "18\t=> Multi record updates of transactions" << endl;
+ ndbout << "All other responses will exit" << endl;
+ ndbout << "_____________________________" << endl << endl ;
+
+ int inp_i = 0;
+ do {
+ inp[inp_i] = (char) fgetc(stdin);
+ if (inp[inp_i] == '\n' || inp[inp_i] == EOF) {
+ inp[inp_i] ='\0';
+ break;
+ }
+ inp_i++;
+
+ } while (inp[inp_i - 1] != '\n' && inp[inp_i - 1] != EOF);
+
+ tmp = atoi(inp);
+
+ if ((tmp > 18) || (tmp <= 0)) break;
+
+ ndbout << "Starting test " << tmp << "..." << endl;
+
+ for (i = 0; i < tNoOfThreads ; i++){ ThreadStart[i] = tmp; }
+
+ cont = 1;
+ while (cont) {
+ NdbSleep_MilliSleep(10);
+ cont = 0;
+ for (i = 0; i < tNoOfThreads ; i++){
+ if (!ThreadReady[i]) cont = 1;
+ }
+ }//while
+ }//for(;;)
+
+ }else{
+
+ if(19 == nTest){
+ ndbout << "Executing all 18 available tests..." << endl << endl;
+ for (int count = 1; count < nTest; count++){
+ ndbout << "Test " << count << endl ;
+ ndbout << "------" << endl << endl ;
+ for (i = 0; i < tNoOfThreads ; i++) { ThreadStart[i] = count ; }
+ cont = 1;
+ while (cont) {
+ NdbSleep_MilliSleep(10);
+ cont = 0;
+ for (i = 0; i < tNoOfThreads ; i++){
+ if (!ThreadReady[i]) cont = 1;
+ }
+ }
+ }//for
+ }else{
+ ndbout << endl << "Executing test " << nTest << endl << endl;
+ for (i = 0; i < tNoOfThreads ; i++) { ThreadStart[i] = nTest ; }
+ cont = 1;
+ while (cont) {
+ NdbSleep_MilliSleep(10);
+ cont = 0;
+ for (i = 0; i < tNoOfThreads ; i++){
+ if (!ThreadReady[i]) cont = 1;
+ }
+ }
+ }//if(18 == nTest)
+ } //if(!nTest)
+
+ ndbout << "--------------------------------------------------" << endl;
+
+ for (i = 0; i < tNoOfThreads ; i++) ThreadReady[i] = 0;
+ // Signaling threads to stop
+ for (i = 0; i < tNoOfThreads ; i++) ThreadStart[i] = 999;
+
+ // Wait for threads to stop
+ cont = 1;
+ do {
+ NdbSleep_MilliSleep(1);
+ cont = 0;
+ for (i = 0; i < tNoOfThreads ; i++){
+ if (ThreadReady[i] == 0) cont = 1;
+ }
+ } while (cont == 1);
+
+ delete pMyNdb ;
+ ndbout << endl << "Ronja Benchmark completed" << endl;
+ return NDBT_ProgramExit(NDBT_OK) ;
+
+error_input:
+ ndbout << endl << " Ivalid parameter(s)" << endl;
+ ndbout << " Usage: benchronja [-t threads][-r rec] [-o ops] [-c ops_per_exec] [-p test], where:" << endl;
+ ndbout << " threads - the number of threads to start; default: 1" << endl;
+ ndbout << " rec - the number of records in the tables; default: 500" << endl;
+ ndbout << " ops - the number of operations per transaction; default: 100000" << endl;
+ ndbout << " ops_per_exec - the number of operations per execution; default: 1" << endl ;
+ ndbout << " test - the number of test to execute; 19 executes all available tests; default: 0"<< endl ;
+ ndbout << " which enters a loop expecting manual input of test number to execute." << endl << endl ;
+ delete pMyNdb ;
+ return NDBT_ProgramExit(NDBT_WRONGARGS) ;
+
+ }
+////////////////////////////////////////
+
+void commitTrans(Ndb* aNdb, NdbConnection* aCon)
+{
+ int ret = aCon->execute(Commit);
+ assert (ret != -1);
+ aNdb->closeTransaction(aCon);
+}
+
+void rollbackTrans(Ndb* aNdb, NdbConnection* aCon)
+{
+ int ret = aCon->execute(Rollback);
+ assert (ret != -1);
+ aNdb->closeTransaction(aCon);
+}
+
+void updateNoCommit(NdbConnection* aCon, Uint32* flip, unsigned int key)
+{
+ NdbOperation* theOperation;
+
+ *flip = *flip + 1;
+ theOperation = aCon->getNdbOperation("SHORT_REC");
+ theOperation->updateTuple();
+ theOperation->equal((Uint32)0, key);
+ theOperation->setValue((Uint32)1, (char*)flip);
+ int ret = aCon->execute(NoCommit);
+ assert (ret != -1);
+}
+
+void updateNoCommitFail(NdbConnection* aCon, unsigned int key)
+{
+ NdbOperation* theOperation;
+
+ Uint32 flip = 0;
+ theOperation = aCon->getNdbOperation("SHORT_REC");
+ theOperation->updateTuple();
+ theOperation->equal((Uint32)0, key);
+ theOperation->setValue((Uint32)1, (char*)flip);
+ int ret = aCon->execute(NoCommit);
+ assert (ret == -1);
+}
+
+void deleteNoCommit(NdbConnection* aCon, Uint32* flip, unsigned int key)
+{
+ NdbOperation* theOperation;
+
+ *flip = 0;
+ theOperation = aCon->getNdbOperation("SHORT_REC");
+ theOperation->deleteTuple();
+ theOperation->equal((Uint32)0, key);
+ int ret = aCon->execute(NoCommit);
+ assert (ret != -1);
+}
+
+void insertNoCommit(NdbConnection* aCon, Uint32* flip, unsigned int key)
+{
+ NdbOperation* theOperation;
+ Uint32 placeholder[100];
+
+ *flip = *flip + 1;
+ theOperation = aCon->getNdbOperation("SHORT_REC");
+ theOperation->insertTuple();
+ theOperation->equal((Uint32)0, key);
+ theOperation->setValue((Uint32)1, (char*)flip);
+ theOperation->setValue((Uint32)2, (char*)&placeholder[0]);
+ theOperation->setValue((Uint32)3, (char*)&placeholder[0]);
+ int ret = aCon->execute(NoCommit);
+ assert (ret != -1);
+}
+
+void writeNoCommit(NdbConnection* aCon, Uint32* flip, unsigned int key)
+{
+ NdbOperation* theOperation;
+ Uint32 placeholder[100];
+
+ *flip = *flip + 1;
+ theOperation = aCon->getNdbOperation("SHORT_REC");
+ theOperation->writeTuple();
+ theOperation->equal((Uint32)0, key);
+ theOperation->setValue((Uint32)1, (char*)flip);
+ theOperation->setValue((Uint32)2, (char*)&placeholder[0]);
+ theOperation->setValue((Uint32)3, (char*)&placeholder[0]);
+ int ret = aCon->execute(NoCommit);
+ assert (ret != -1);
+}
+
+void readNoCommit(NdbConnection* aCon, Uint32* flip, Uint32 key, int expected_ret)
+{
+ NdbOperation* theOperation;
+ Uint32 readFlip;
+
+ theOperation = aCon->getNdbOperation("SHORT_REC");
+ theOperation->readTuple();
+ theOperation->equal((Uint32)0, key);
+ theOperation->getValue((Uint32)1, (char*)&readFlip);
+ int ret = aCon->execute(NoCommit);
+ assert (ret == expected_ret);
+ if (ret == 0)
+ assert (*flip == readFlip);
+}
+
+void readDirtyNoCommit(NdbConnection* aCon, Uint32* flip, Uint32 key, int expected_ret)
+{
+ NdbOperation* theOperation;
+ Uint32 readFlip;
+
+ theOperation = aCon->getNdbOperation("SHORT_REC");
+ theOperation->committedRead();
+ theOperation->equal((Uint32)0, key);
+ theOperation->getValue((Uint32)1, (char*)&readFlip);
+ int ret = aCon->execute(NoCommit);
+ assert (ret == expected_ret);
+ if (ret == 0)
+ assert (*flip == readFlip);
+}
+
+void readVerify(Ndb* aNdb, Uint32* flip, Uint32 key, int expected_ret)
+{
+ NdbConnection* theTransaction;
+ theTransaction = aNdb->startTransaction();
+ readNoCommit(theTransaction, flip, key, expected_ret);
+ commitTrans(aNdb, theTransaction);
+}
+
+void readDirty(Ndb* aNdb, Uint32* flip, Uint32 key, int expected_ret)
+{
+ NdbOperation* theOperation;
+ NdbConnection* theTransaction;
+ Uint32 readFlip;
+
+ theTransaction = aNdb->startTransaction();
+ theOperation = theTransaction->getNdbOperation("SHORT_REC");
+ theOperation->committedRead();
+ theOperation->equal((Uint32)0, key);
+ theOperation->getValue((Uint32)1, (char*)&readFlip);
+ int ret = theTransaction->execute(Commit);
+ assert (ret == expected_ret);
+ if (ret == 0)
+ assert (*flip == readFlip);
+ aNdb->closeTransaction(theTransaction);
+}
+
+int multiRecordTest(Ndb* aNdb, unsigned int key)
+{
+ NdbConnection* theTransaction;
+ Uint32 flip = 0;
+ Uint32 save_flip;
+ ndbout << "0" << endl;
+
+ theTransaction = aNdb->startTransaction();
+
+ updateNoCommit(theTransaction, &flip, key);
+
+ readNoCommit(theTransaction, &flip, key, 0);
+
+ updateNoCommit(theTransaction, &flip, key);
+
+ readNoCommit(theTransaction, &flip, key, 0);
+
+ commitTrans(aNdb, theTransaction);
+
+ ndbout << "1 " << endl;
+
+ readVerify(aNdb, &flip, key, 0);
+ readDirty(aNdb, &flip, key, 0);
+ save_flip = flip;
+ ndbout << "1.1 " << endl;
+
+ theTransaction = aNdb->startTransaction();
+
+ deleteNoCommit(theTransaction, &flip, key);
+
+ readNoCommit(theTransaction, &flip, key, -1);
+ readDirty(aNdb, &save_flip, key, 0); // COMMITTED READ!!!
+ readDirtyNoCommit(theTransaction, &flip, key, -1);
+ ndbout << "1.2 " << endl;
+
+ insertNoCommit(theTransaction, &flip, key);
+
+ readNoCommit(theTransaction, &flip, key, 0);
+ readDirtyNoCommit(theTransaction, &flip, key, 0);
+ readDirty(aNdb, &save_flip, key, 0); // COMMITTED READ!!!
+ ndbout << "1.3 " << endl;
+
+ updateNoCommit(theTransaction, &flip, key);
+
+ readNoCommit(theTransaction, &flip, key, 0);
+ readDirtyNoCommit(theTransaction, &flip, key, 0);
+ readDirty(aNdb, &save_flip, key, 0); // COMMITTED READ!!!
+ ndbout << "1.4 " << endl;
+
+ commitTrans(aNdb, theTransaction);
+
+ ndbout << "2 " << endl;
+
+ readDirty(aNdb, &flip, key, 0); // COMMITTED READ!!!
+ readVerify(aNdb, &flip, key, 0);
+
+ save_flip = flip;
+ theTransaction = aNdb->startTransaction();
+
+ deleteNoCommit(theTransaction, &flip, key);
+
+ readDirty(aNdb, &save_flip, key, 0); // COMMITTED READ!!!
+ readDirtyNoCommit(theTransaction, &flip, key, -1); // COMMITTED READ!!!
+ readNoCommit(theTransaction, &flip, key, -1);
+
+ insertNoCommit(theTransaction, &flip, key);
+
+ readNoCommit(theTransaction, &flip, key, 0);
+
+ updateNoCommit(theTransaction, &flip, key);
+
+ readNoCommit(theTransaction, &flip, key, 0);
+ readDirty(aNdb, &save_flip, key, 0); // COMMITTED READ!!!
+ readDirtyNoCommit(theTransaction, &flip, key, 0); // COMMITTED READ!!!
+
+ deleteNoCommit(theTransaction, &flip, key);
+
+ readNoCommit(theTransaction, &flip, key, -1);
+ readDirty(aNdb, &save_flip, key, 0); // COMMITTED READ!!!
+ readDirtyNoCommit(theTransaction, &flip, key, -1);
+
+ rollbackTrans(aNdb, theTransaction);
+
+ ndbout << "3 " << endl;
+
+ flip = save_flip;
+ readDirty(aNdb, &save_flip, key, 0); // COMMITTED READ!!!
+ readVerify(aNdb, &flip, key, 0);
+
+ theTransaction = aNdb->startTransaction();
+
+ updateNoCommit(theTransaction, &flip, key);
+
+ readDirty(aNdb, &save_flip, key, 0); // COMMITTED READ!!!
+ readDirtyNoCommit(theTransaction, &flip, key, 0);
+ readNoCommit(theTransaction, &flip, key, 0);
+
+ deleteNoCommit(theTransaction, &flip, key);
+
+ readNoCommit(theTransaction, &flip, key, -1);
+ readDirtyNoCommit(theTransaction, &flip, key, -1);
+ readDirty(aNdb, &save_flip, key, 0); // COMMITTED READ!!!
+
+ insertNoCommit(theTransaction, &flip, key);
+
+ readNoCommit(theTransaction, &flip, key, 0);
+ readDirtyNoCommit(theTransaction, &flip, key, 0);
+ readDirty(aNdb, &save_flip, key, 0); // COMMITTED READ!!!
+
+ updateNoCommit(theTransaction, &flip, key);
+
+ readNoCommit(theTransaction, &flip, key, 0);
+ readDirtyNoCommit(theTransaction, &flip, key, 0);
+ readDirty(aNdb, &save_flip, key, 0); // COMMITTED READ!!!
+
+ deleteNoCommit(theTransaction, &flip, key);
+
+ readDirty(aNdb, &save_flip, key, 0); // COMMITTED READ!!!
+ readNoCommit(theTransaction, &flip, key, -1);
+ readDirtyNoCommit(theTransaction, &flip, key, -1);
+
+ commitTrans(aNdb, theTransaction);
+
+ ndbout << "4 " << endl;
+
+ readVerify(aNdb, &flip, key, -1);
+
+ theTransaction = aNdb->startTransaction();
+
+ insertNoCommit(theTransaction, &flip, key);
+
+ readDirty(aNdb, &save_flip, key, -1); // COMMITTED READ!!!
+ readNoCommit(theTransaction, &flip, key, 0);
+ readDirtyNoCommit(theTransaction, &flip, key, 0);
+
+ deleteNoCommit(theTransaction, &flip, key);
+
+ readDirty(aNdb, &save_flip, key, -1); // COMMITTED READ!!!
+ readNoCommit(theTransaction, &flip, key, -1);
+ readDirtyNoCommit(theTransaction, &flip, key, -1);
+
+ insertNoCommit(theTransaction, &flip, key);
+
+ readDirty(aNdb, &save_flip, key, -1); // COMMITTED READ!!!
+ readNoCommit(theTransaction, &flip, key, 0);
+ readDirtyNoCommit(theTransaction, &flip, key, 0);
+
+ updateNoCommit(theTransaction, &flip, key);
+
+ readDirty(aNdb, &save_flip, key, -1); // COMMITTED READ!!!
+ readNoCommit(theTransaction, &flip, key, 0);
+ readDirtyNoCommit(theTransaction, &flip, key, 0);
+
+ deleteNoCommit(theTransaction, &flip, key);
+
+ readDirty(aNdb, &save_flip, key, -1); // COMMITTED READ!!!
+ readNoCommit(theTransaction, &flip, key, -1);
+ readDirtyNoCommit(theTransaction, &flip, key, -1);
+
+ commitTrans(aNdb, theTransaction);
+
+ ndbout << "5 " << endl;
+
+ readDirty(aNdb, &flip, key, -1); // COMMITTED READ!!!
+ readVerify(aNdb, &flip, key, -1);
+
+ theTransaction = aNdb->startTransaction();
+
+ insertNoCommit(theTransaction, &flip, key);
+
+ readDirty(aNdb, &flip, key, -1); // COMMITTED READ!!!
+ readDirtyNoCommit(theTransaction, &flip, key, 0); // COMMITTED READ!!!
+
+ commitTrans(aNdb, theTransaction);
+ readDirty(aNdb, &flip, key, 0); // COMMITTED READ!!!
+
+ ndbout << "6 " << endl;
+
+ theTransaction = aNdb->startTransaction();
+
+ deleteNoCommit(theTransaction, &flip, key);
+ updateNoCommitFail(theTransaction, key);
+ rollbackTrans(aNdb, theTransaction);
+ return 0;
+}
+
+int lookup(Ndb* aNdb, unsigned int key, unsigned int long_short, int guess){
+
+ int placeholder[500];
+ unsigned int flip, count;
+ int ret_value, i;
+ NdbConnection* theTransaction;
+ NdbOperation* theOperation;
+ if ( !aNdb ) return -1 ;
+
+ if (guess != 0)
+ theTransaction = aNdb->startTransaction((Uint32)0, (const char*)&key, (Uint32)4);
+ else
+ theTransaction = aNdb->startTransaction();
+
+ for (i = 0; i < tNoOfOpsPerExecute; i++) {
+ if (long_short == 0)
+ theOperation = theTransaction->getNdbOperation("SHORT_REC");
+ else
+ theOperation = theTransaction->getNdbOperation("LONG_REC");
+ if (theOperation == NULL) {
+ ndbout << "Table missing" << endl;
+ aNdb->closeTransaction(theTransaction) ;
+ return -1;
+ }//if
+ theOperation->simpleRead();
+ theOperation->equal((Uint32)0, key);
+ theOperation->getValue((Uint32)1, (char*)&flip);
+ theOperation->getValue((Uint32)2, (char*)&count);
+ if (theOperation->getValue((Uint32)3, (char*)&placeholder[0]) == NULL) {
+ ndbout << "Error in definition phase = " << theTransaction->getNdbError() << endl;
+ aNdb->closeTransaction(theTransaction);
+ return -1;
+ }//if
+ }//for
+ ret_value = theTransaction->execute(Commit);
+ if (ret_value == -1)
+ ndbout << "Error in lookup:" << theTransaction->getNdbError() << endl;
+ aNdb->closeTransaction(theTransaction);
+ return ret_value;
+}//lookup()
+
+int update(Ndb* aNdb, unsigned int key, unsigned int long_short, int guess)
+{
+ int placeholder[500];
+ int ret_value, i;
+ unsigned int flip, count;
+ NdbConnection* theTransaction;
+ NdbOperation* theOperation;
+
+ if ( !aNdb ) return -1 ;
+
+ if (guess != 0)
+ theTransaction = aNdb->startTransaction((Uint32)0, (const char*)&key, (Uint32)4);
+ else
+ theTransaction = aNdb->startTransaction();
+
+ for (i = 0; i < tNoOfOpsPerExecute; i++) {
+ if (long_short == 0)
+ theOperation = theTransaction->getNdbOperation("SHORT_REC"); // Use table SHORT_REC
+ else
+ theOperation = theTransaction->getNdbOperation("LONG_REC"); // Use table LONG_REC
+ if (theOperation == NULL) {
+ ndbout << "Table missing" << endl;
+ aNdb->closeTransaction(theTransaction) ;
+ delete aNdb ;
+ return -1;
+ }//if
+ theOperation->interpretedUpdateTuple(); // Send interpreted program to NDB kernel
+ theOperation->equal((Uint32)0, key); // Search key
+ theOperation->getValue((Uint32)1, (char*)&flip); // Read value of flip
+ theOperation->getValue((Uint32)2, (char*)&count); // Read value of count
+ theOperation->getValue((Uint32)3, (char*)&placeholder[0]); // Read value of placeholder
+ theOperation->load_const_u32((Uint32)1, (Uint32)0); // Load register 1 with 0
+ theOperation->read_attr((Uint32)1, (Uint32)2); // Read Flip value into register 2
+ theOperation->branch_eq((Uint32)1, (Uint32)2, (Uint32)0); // If Flip (register 2) == 0 (register 1) goto label 0
+ theOperation->branch_label((Uint32)1); // Goto label 1
+ theOperation->def_label((Uint32)0); // Define label 0
+ theOperation->load_const_u32((Uint32)1, (Uint32)1); // Load register 1 with 1
+ theOperation->def_label((Uint32)1); // Define label 0
+ theOperation->write_attr((Uint32)1, (Uint32)1); // Write 1 (register 1) into Flip
+ ret_value = theOperation->incValue((Uint32)2, (Uint32)1); // Increment Count by 1
+ if (ret_value == -1) {
+ ndbout << "Error in definition phase " << endl;
+ aNdb->closeTransaction(theTransaction);
+ return ret_value;
+ }//if
+ }//for
+ ret_value = theTransaction->execute(Commit); // Perform the actual read and update
+ if (ret_value == -1) {
+ ndbout << "Error in update:" << theTransaction->getNdbError() << endl;
+ aNdb->closeTransaction(theTransaction); // < epaulsa
+ return ret_value ;
+ }//if
+ aNdb->closeTransaction(theTransaction);
+ return ret_value;
+}//update()
+
+int update_bug(Ndb* aNdb, unsigned int key, unsigned int long_short)
+{
+ int placeholder[500];
+ int ret_value, i;
+ unsigned int flip, count;
+ NdbConnection* theTransaction;
+ NdbOperation* theOperation;
+
+ if ( !aNdb ) return -1 ;
+
+ theTransaction = aNdb->startTransaction();
+ for (i = 0; i < tNoOfOpsPerExecute; i++) {
+ if (long_short == 0)
+ theOperation = theTransaction->getNdbOperation("SHORT_REC"); // Use table SHORT_REC
+ else
+ theOperation = theTransaction->getNdbOperation("LONG_REC"); // Use table LONG_REC
+ if (theOperation == NULL) {
+ ndbout << "Table missing" << endl;
+ aNdb->closeTransaction(theTransaction) ;
+ return -1;
+ }//if
+ theOperation->interpretedUpdateTuple(); // Send interpreted program to NDB kernel
+ theOperation->equal((Uint32)0, key); // Search key
+ theOperation->getValue((Uint32)1, (char*)&flip); // Read value of flip
+ theOperation->getValue((Uint32)2, (char*)&count); // Read value of count
+ theOperation->getValue((Uint32)3, (char*)&placeholder[0]); // Read value of placeholder
+ theOperation->load_const_u32((Uint32)1, (Uint32)0); // Load register 1 with 0
+ theOperation->read_attr((Uint32)1, (Uint32)2); // Read Flip value into register 2
+ theOperation->branch_eq((Uint32)1, (Uint32)2, (Uint32)0); // If Flip (register 2) == 0 (register 1) goto label 0
+ theOperation->branch_label((Uint32)1); // Goto label 1
+ theOperation->def_label((Uint32)0); // Define label 0
+ theOperation->load_const_u32((Uint32)1, (Uint32)1); // Load register 1 with 1
+ theOperation->def_label((Uint32)1); // Define label 0
+ theOperation->write_attr((Uint32)1, (Uint32)1); // Write 1 (register 1) into Flip
+ ret_value = theOperation->incValue((Uint32)2, (Uint32)1); // Increment Count by 1
+ if (ret_value == -1) {
+ ndbout << "Error in definition phase " << endl;
+ aNdb->closeTransaction(theTransaction);
+ return ret_value;
+ }//if
+ }//for
+ ret_value = theTransaction->execute(NoCommit); // Perform the actual read and update
+ if (ret_value == -1) {
+ ndbout << "Error in update:" << theTransaction->getNdbError() << endl;
+ aNdb->closeTransaction(theTransaction);
+ return ret_value ;
+ }//if
+ aNdb->closeTransaction(theTransaction);
+ return ret_value;
+}//update_bug()
+
+int update_interpreter_test(Ndb* aNdb, unsigned int key, unsigned int long_short)
+{
+ int placeholder[500];
+ int ret_value, i;
+ unsigned int flip, count;
+ NdbConnection* theTransaction;
+ NdbOperation* theOperation;
+ Uint32 Tlabel = 0;
+
+ if ( !aNdb ) return -1 ;
+
+//------------------------------------------------------------------------------
+// Start the transaction and get a unique transaction id
+//------------------------------------------------------------------------------
+ theTransaction = aNdb->startTransaction();
+ for (i = 0; i < tNoOfOpsPerExecute; i++) {
+//------------------------------------------------------------------------------
+// Get the proper table object and load schema information if not already
+// present.
+//------------------------------------------------------------------------------
+ if (long_short == 0)
+ theOperation = theTransaction->getNdbOperation("SHORT_REC"); // Use table SHORT_REC
+ else
+ theOperation = theTransaction->getNdbOperation("LONG_REC"); // Use table LONG_REC
+ if (theOperation == NULL) {
+ ndbout << "Table missing" << endl;
+ aNdb->closeTransaction(theTransaction) ;
+ return -1;
+ }//if
+//------------------------------------------------------------------------------
+// Define the operation type and the tuple key (primary key in this case).
+//------------------------------------------------------------------------------
+ theOperation->interpretedUpdateTuple(); // Send interpreted program to NDB kernel
+ theOperation->equal((Uint32)0, key); // Search key
+
+//------------------------------------------------------------------------------
+// Perform initial read of attributes before updating them
+//------------------------------------------------------------------------------
+ theOperation->getValue((Uint32)1, (char*)&flip); // Read value of flip
+ theOperation->getValue((Uint32)2, (char*)&count); // Read value of count
+ theOperation->getValue((Uint32)3, (char*)&placeholder[0]); // Read value of placeholder
+
+//------------------------------------------------------------------------------
+// Test that the various branch operations can handle things correctly.
+// Test first 2 + 3 = 5 with 32 bit registers
+// Next test the same with 32 bit + 64 bit = 64
+//------------------------------------------------------------------------------
+ theOperation->load_const_u32((Uint32)4, (Uint32)0); // Load register 4 with 0
+
+ theOperation->load_const_u32((Uint32)0, (Uint32)0);
+ theOperation->load_const_u32((Uint32)1, (Uint32)3);
+ theOperation->load_const_u32((Uint32)2, (Uint32)5);
+ theOperation->load_const_u32((Uint32)3, (Uint32)1);
+ theOperation->def_label(Tlabel++);
+ theOperation->def_label(Tlabel++);
+ theOperation->sub_reg((Uint32)2, (Uint32)3, (Uint32)2);
+ theOperation->branch_ne((Uint32)2, (Uint32)0, (Uint32)0);
+ theOperation->load_const_u32((Uint32)2, (Uint32)5);
+ theOperation->sub_reg((Uint32)1, (Uint32)3, (Uint32)1);
+ theOperation->branch_ne((Uint32)1, (Uint32)0, (Uint32)1);
+
+ theOperation->load_const_u32((Uint32)1, (Uint32)2); // Load register 1 with 2
+ theOperation->load_const_u32((Uint32)2, (Uint32)3); // Load register 2 with 3
+ theOperation->add_reg((Uint32)1, (Uint32)2, (Uint32)1); // 2+3 = 5 into reg 1
+ theOperation->load_const_u32((Uint32)2, (Uint32)5); // Load register 2 with 5
+
+ theOperation->def_label(Tlabel++);
+
+ theOperation->branch_eq((Uint32)1, (Uint32)2, Tlabel);
+ theOperation->interpret_exit_nok((Uint32)6001);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->branch_ne((Uint32)1, (Uint32)2, Tlabel);
+ theOperation->branch_label(Tlabel + 1);
+ theOperation->def_label(Tlabel++);
+ theOperation->interpret_exit_nok((Uint32)6002);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->branch_lt((Uint32)1, (Uint32)2, Tlabel);
+ theOperation->branch_label(Tlabel + 1);
+ theOperation->def_label(Tlabel++);
+ theOperation->interpret_exit_nok((Uint32)6003);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->branch_gt((Uint32)1, (Uint32)2, Tlabel);
+ theOperation->branch_label(Tlabel + 1);
+ theOperation->def_label(Tlabel++);
+ theOperation->interpret_exit_nok((Uint32)6005);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->branch_eq_null((Uint32)1, Tlabel);
+ theOperation->branch_label(Tlabel + 1);
+ theOperation->def_label(Tlabel++);
+ theOperation->interpret_exit_nok((Uint32)6006);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->branch_ne_null((Uint32)1,Tlabel);
+ theOperation->interpret_exit_nok((Uint32)6007);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->branch_ge((Uint32)1, (Uint32)2, Tlabel);
+ theOperation->interpret_exit_nok((Uint32)6008);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->branch_eq_null((Uint32)6,Tlabel);
+ theOperation->interpret_exit_nok((Uint32)6009);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->branch_ne_null((Uint32)6, Tlabel);
+ theOperation->branch_label(Tlabel + 1);
+ theOperation->def_label(Tlabel++);
+ theOperation->interpret_exit_nok((Uint32)6010);
+
+ theOperation->def_label(Tlabel++);
+
+ theOperation->load_const_u32((Uint32)5, (Uint32)1);
+ theOperation->add_reg((Uint32)4, (Uint32)5, (Uint32)4);
+
+ theOperation->load_const_u32((Uint32)5, (Uint32)1);
+ theOperation->branch_eq((Uint32)4, (Uint32)5, Tlabel);
+
+
+ theOperation->load_const_u32((Uint32)5, (Uint32)2);
+ theOperation->branch_eq((Uint32)4, (Uint32)5, (Tlabel + 1));
+
+ theOperation->load_const_u32((Uint32)5, (Uint32)3);
+ theOperation->branch_eq((Uint32)4, (Uint32)5, (Tlabel + 2));
+
+ theOperation->load_const_u32((Uint32)5, (Uint32)4);
+ theOperation->branch_eq((Uint32)4, (Uint32)5, (Tlabel + 3));
+
+ theOperation->branch_label(Tlabel + 4);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->load_const_u32((Uint32)1, (Uint32)200000);
+ theOperation->load_const_u32((Uint32)2, (Uint32)300000);
+ theOperation->add_reg((Uint32)1, (Uint32)2, (Uint32)1);
+ theOperation->load_const_u32((Uint32)2, (Uint32)500000);
+ theOperation->branch_label((Uint32)2);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->load_const_u32((Uint32)1, (Uint32)200000);
+ theOperation->load_const_u32((Uint32)2, (Uint32)300000);
+ theOperation->add_reg((Uint32)1, (Uint32)2, (Uint32)1);
+ theOperation->load_const_u32((Uint32)2, (Uint32)500000);
+ theOperation->branch_label((Uint32)2);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->load_const_u32((Uint32)1, (Uint32)2);
+ Uint64 x = 0;
+ theOperation->load_const_u64((Uint32)2, (Uint64)(x - 1));
+ theOperation->add_reg((Uint32)1, (Uint32)2, (Uint32)1);
+ theOperation->load_const_u32((Uint32)2, (Uint32)1);
+ theOperation->branch_label((Uint32)2);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->load_const_u32((Uint32)1, (Uint32)2);
+ theOperation->load_const_u64((Uint32)2, (Uint64)(x - 1));
+ theOperation->add_reg((Uint32)1, (Uint32)2, (Uint32)1);
+ theOperation->load_const_u64((Uint32)2, (Uint64)1);
+ theOperation->branch_label((Uint32)2);
+
+ theOperation->def_label(Tlabel++);
+ theOperation->read_attr((Uint32)1, (Uint32)2);
+ theOperation->branch_eq((Uint32)1, (Uint32)2, Tlabel);
+ theOperation->load_const_u32((Uint32)1, (Uint32)0);
+ theOperation->branch_label(Tlabel + 1);
+ theOperation->def_label(Tlabel++);
+ theOperation->load_const_u32((Uint32)1, (Uint32)1);
+ theOperation->def_label(Tlabel++);
+ theOperation->write_attr((Uint32)1, (Uint32)1);
+ ret_value = theOperation->incValue((Uint32)2, (Uint32)1);
+ if (ret_value == -1) {
+ ndbout << "Error in definition phase " << endl;
+ ndbout << "Error = " << theOperation->getNdbError() << " on line = " << theOperation->getNdbErrorLine() << endl;
+ aNdb->closeTransaction(theTransaction);
+ return ret_value;
+ }//if
+ }//for
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+ ret_value = theTransaction->execute(Commit); // Perform the actual read and update
+ if (ret_value == -1) {
+ ndbout << "Error in update:" << theTransaction->getNdbError() << endl;
+ aNdb->closeTransaction(theTransaction); // < epaulsa
+ return ret_value ;
+ }//if
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+ aNdb->closeTransaction(theTransaction);
+ return ret_value;
+}//update_interpreter_test()
+
+void* ThreadExec(void* ThreadData){
+
+ ThreadNdb* tabThread = (ThreadNdb*)ThreadData;
+ Ndb* pMyNdb = NULL ;
+ myRandom48Init(NdbTick_CurrentMillisecond());
+
+ int Tsuccess = 0 ;
+ int check = 0 ;
+ int loop_count_ops = 0;
+ int count, i, Ti;
+ int tType = 0 ;
+ int remType = 0 ;
+ unsigned int thread_no = 0 ;
+ unsigned long total_milliseconds;
+ unsigned int key = 0 ;
+ unsigned int prob = 0 ;
+ unsigned long transaction_time = 0 ;
+ unsigned long transaction_max_time = 0 ;
+ unsigned long min_time, max_time[MAX_TIMERS];
+ double mean_time, mean_square_time, std_time;
+
+ thread_no = tabThread->ThreadNo;
+ pMyNdb = tabThread->NdbRef;
+ if (!pMyNdb) {
+ pMyNdb = new Ndb( "TEST_DB" );
+ pMyNdb->init();
+ }//if
+
+ for (;;){
+
+ min_time = 0xFFFFFFFF;
+ //for (Ti = 0; Ti < MAX_TIMERS ; Ti++) max_time[Ti] = 0;
+ memset(&max_time, 0, sizeof max_time) ;
+ mean_time = 0;
+ mean_square_time = 0;
+ ThreadReady[thread_no] = 1;
+
+ while (!ThreadStart[thread_no]){
+ NdbSleep_MilliSleep(1);
+ }
+
+ // Check if signal to exit is received
+ if (ThreadStart[thread_no] == 999){
+ delete pMyNdb;
+ pMyNdb = NULL ;
+ ThreadReady[thread_no] = 1;
+ return 0 ;
+ }//if
+
+ tType = ThreadStart[thread_no];
+ remType = tType;
+ ThreadStart[thread_no] = 0;
+ ThreadReady[thread_no] = 0 ;
+
+ // Start transaction, type of transaction
+ // is received in the array ThreadStart
+ loop_count_ops = tNoOfOperations;
+
+ START_TIMER_TOP
+ for (count=0 ; count < loop_count_ops ; count++) {
+
+ Tsuccess = 0;
+//----------------------------------------------------
+// Generate a random key between 0 and tNoOfRecords - 1
+//----------------------------------------------------
+ key = myRandom48(tNoOfRecords);
+//----------------------------------------------------
+// Start time measurement of transaction.
+//----------------------------------------------------
+ START_TIMER
+ //do {
+ switch (remType){
+ case 1:
+//----------------------------------------------------
+// Only lookups in short record table
+//----------------------------------------------------
+ Tsuccess = lookup(pMyNdb, key, 0, 1);
+ break;
+
+ case 2:
+//----------------------------------------------------
+// Only lookups in long record table
+//----------------------------------------------------
+ Tsuccess = lookup(pMyNdb, key, 1, 1);
+ break;
+ case 3:
+//----------------------------------------------------
+// Only updates in short record table
+//----------------------------------------------------
+ Tsuccess = update(pMyNdb, key, 0, 1);
+ break;
+ case 4:
+//----------------------------------------------------
+// Only updates in long record table
+//----------------------------------------------------
+ Tsuccess = update(pMyNdb, key, 1, 1);
+ break;
+ case 5:
+//----------------------------------------------------
+// 50% read/50 % update in short record table
+//----------------------------------------------------
+ prob = myRandom48(100);
+ if (prob < 50)
+ Tsuccess = update(pMyNdb, key, 0, 1);
+ else
+ Tsuccess = lookup(pMyNdb, key, 0, 1);
+ break;
+ case 6:
+//----------------------------------------------------
+// 50% read/50 % update in long record table
+//----------------------------------------------------
+ prob = myRandom48(100);
+ if (prob < 50)
+ Tsuccess = update(pMyNdb, key, 1, 1);
+ else
+ Tsuccess = lookup(pMyNdb, key, 1, 1);
+ break;
+ case 7:
+//----------------------------------------------------
+// 80 read/20 % update in short record table
+//----------------------------------------------------
+ prob = myRandom48(100);
+ if (prob < 20)
+ Tsuccess = update(pMyNdb, key, 0, 1);
+ else
+ Tsuccess = lookup(pMyNdb, key, 0, 1);
+ break;
+ case 8:
+//----------------------------------------------------
+// 80 read/20 % update in long record table
+//----------------------------------------------------
+ prob = myRandom48(100);
+ if (prob < 20)
+ Tsuccess = update(pMyNdb, key, 1, 1);
+ else
+ Tsuccess = lookup(pMyNdb, key, 1, 1);
+ break;
+ case 9:
+//----------------------------------------------------
+// 25 read short/25 % read long/25 % update short/25 % update long
+//----------------------------------------------------
+ prob = myRandom48(100);
+ if (prob < 25)
+ Tsuccess = update(pMyNdb, key, 0, 1);
+ else if (prob < 50)
+ Tsuccess = update(pMyNdb, key, 1, 1);
+ else if (prob < 75)
+ Tsuccess = lookup(pMyNdb, key, 0, 1);
+ else
+ Tsuccess = lookup(pMyNdb, key, 1, 1);
+ break;
+ case 10:
+//----------------------------------------------------
+// Test bug with replicated interpreted update, short table
+//----------------------------------------------------
+ Tsuccess = update_bug(pMyNdb, key, 0);
+ break;
+ case 11:
+//----------------------------------------------------
+// Test interpreter functions, short table
+//----------------------------------------------------
+ Tsuccess = update_interpreter_test(pMyNdb, key, 0);
+ break;
+ case 12:
+//----------------------------------------------------
+// Test bug with replicated interpreted update, long table
+//----------------------------------------------------
+ Tsuccess = update_bug(pMyNdb, key, 1);
+ break;
+ case 13:
+//----------------------------------------------------
+// Test interpreter functions, long table
+//----------------------------------------------------
+ Tsuccess = update_interpreter_test(pMyNdb, key, 1);
+ break;
+ case 14:
+//----------------------------------------------------
+// Only lookups in short record table
+//----------------------------------------------------
+ Tsuccess = lookup(pMyNdb, key, 0, 0);
+ break;
+ case 15:
+//----------------------------------------------------
+// Only lookups in long record table
+//----------------------------------------------------
+ Tsuccess = lookup(pMyNdb, key, 1, 0);
+ break;
+ case 16:
+//----------------------------------------------------
+// Only updates in short record table
+//----------------------------------------------------
+ Tsuccess = update(pMyNdb, key, 0, 0);
+ break;
+ case 17:
+//----------------------------------------------------
+// Only updates in long record table
+//----------------------------------------------------
+ Tsuccess = update(pMyNdb, key, 1, 0);
+ break;
+ case 18:
+ Tsuccess = multiRecordTest(pMyNdb, key);
+ break;
+ default:
+ break;
+ }//switch
+ //} while (0);//
+ if(-1 == Tsuccess) {
+ NDBT_ProgramExit(NDBT_FAILED);
+ exit(-1);
+ } // for
+//----------------------------------------------------
+// Stop time measurement of transaction.
+//----------------------------------------------------
+ STOP_TIMER
+ transaction_time = (unsigned long)timer.elapsedTime() ;//stopTimer(&theStartTime);
+//----------------------------------------------------
+// Perform calculations of time measurements.
+//----------------------------------------------------
+ transaction_max_time = transaction_time;
+ for (Ti = 0; Ti < MAX_TIMERS; Ti++) {
+ if (transaction_max_time > max_time[Ti]) {
+ Uint32 tmp = max_time[Ti];
+ max_time[Ti] = transaction_max_time;
+ transaction_max_time = tmp;
+ }//if
+ }//if
+ if (transaction_time < min_time) min_time = transaction_time;
+ mean_time = (double)transaction_time + mean_time;
+ mean_square_time = (double)(transaction_time * transaction_time) + mean_square_time;
+ }//for
+//----------------------------------------------------
+// Calculate mean and standard deviation
+//----------------------------------------------------
+ STOP_TIMER_TOP
+ total_milliseconds = (unsigned long)timer_top.elapsedTime() ;//stopTimer(&total_time);
+ mean_time = mean_time / loop_count_ops;
+ mean_square_time = mean_square_time / loop_count_ops;
+ std_time = sqrt(mean_square_time - (mean_time * mean_time));
+//----------------------------------------------------
+// Report statistics
+//----------------------------------------------------
+ ndbout << "Thread = " << thread_no << " reporting:" << endl ;
+ ndbout << "------------------------------" << endl ;
+ ndbout << "Total time is " << (unsigned int)(total_milliseconds /1000);
+ ndbout << " seconds and " << (unsigned int)(total_milliseconds % 1000);
+ ndbout << " milliseconds" << endl;
+ ndbout << "Minimum time = " << (unsigned int)min_time << " milliseconds" << endl;
+ for (Ti = 0; Ti < MAX_TIMERS; Ti++) {
+ ndbout << "Maximum timer " << Ti << " = " << (unsigned int)max_time[Ti] << " milliseconds" << endl;
+ ndbout << "Mean time = " << (unsigned int)mean_time << " milliseconds" << endl;
+ ndbout << "Standard deviation on time = " << (unsigned int)std_time;
+ ndbout << " milliseconds" << endl << endl ;
+ }//for
+ ndbout << endl ;
+
+ } // for(;;)
+
+ delete pMyNdb ;
+ return 0 ;
+}
+
diff --git a/storage/ndb/test/ndbapi/bulk_copy.cpp b/storage/ndb/test/ndbapi/bulk_copy.cpp
new file mode 100644
index 00000000000..b53654ce0fb
--- /dev/null
+++ b/storage/ndb/test/ndbapi/bulk_copy.cpp
@@ -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 */
+
+
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NdbSleep.h>
+#include <NDBT.hpp>
+
+#include <getarg.h>
+
+
+int setValuesFromLine(NdbOperation* pOp,
+ const NdbDictionary::Table* pTab,
+ char* line){
+
+ int check = 0;
+ char* p = line;
+ char* pn;
+ char buf[8000];
+ // Loop through each attribute in this table
+ for (int a = 0; a<pTab->getNoOfColumns(); a++){
+
+ pn = p;
+ while (*pn != ';')
+ pn++;
+
+ memset(buf, 0, sizeof(buf));
+ strncpy(buf, p, pn-p);
+ // ndbout << a << ": " << buf << endl;
+ const NdbDictionary::Column* attr = pTab->getColumn(a);
+ switch (attr->getType()){
+ case NdbDictionary::Column::Unsigned:
+ Int32 sval;
+ if (sscanf(buf, "%d", &sval) == 0)
+ return -2;
+ check = pOp->setValue(a, sval);
+ break;
+
+ case NdbDictionary::Column::Int:
+ Uint32 uval;
+ if (sscanf(buf, "%u", &uval) == 0)
+ return -2;
+ check = pOp->setValue(a, uval);
+ break;
+
+ case NdbDictionary::Column::Char:
+ char buf2[8000];
+ char* p2;
+ memset(buf2, 0, sizeof(buf));
+ p2 = &buf2[0];
+ while(*p != ';'){
+ *p2 = *p;
+ p++;p2++;
+ };
+ *p2 = 0;
+ check = pOp->setValue(a, buf2);
+ break;
+
+ default:
+ check = -2;
+ break;
+ }
+
+ // Move pointer to after next ";"
+ while (*p != ';')
+ p++;
+ p++;
+
+ }
+
+ return check;
+}
+
+
+int insertLine(Ndb* pNdb,
+ const NdbDictionary::Table* pTab,
+ char* line){
+ int check;
+ int retryAttempt = 0;
+ int retryMax = 5;
+ NdbConnection *pTrans;
+ NdbOperation *pOp;
+
+ while (retryAttempt < retryMax){
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ ERR(pNdb->getNdbError());
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+
+ pOp = pTrans->getNdbOperation(pTab->getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return -1;
+ }
+
+ check = pOp->insertTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return -1;
+ }
+
+ check = setValuesFromLine(pOp,
+ pTab,
+ line);
+ if (check == -2){
+ pNdb->closeTransaction(pTrans);
+ return -2;
+ }
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return -1;
+ }
+
+
+ // Execute the transaction and insert the record
+ check = pTrans->execute( Commit );
+ if(check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ pNdb->closeTransaction(pTrans);
+
+ switch(err.status){
+ case NdbError::Success:
+ ERR(err);
+ ndbout << "ERROR: NdbError reports success when transcaction failed" << endl;
+ return -1;
+ break;
+
+ case NdbError::TemporaryError:
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ break;
+
+ case NdbError::UnknownResult:
+ ERR(err);
+ return -1;
+ break;
+
+ case NdbError::PermanentError:
+ switch (err.classification){
+ case NdbError::ConstraintViolation:
+ // Tuple already existed, OK in this application, but should be reported
+ ndbout << err.code << " " << err.message << endl;
+ break;
+ default:
+ ERR(err);
+ return -1;
+ break;
+ }
+ break;
+ }
+ }
+ else{
+
+ pNdb->closeTransaction(pTrans);
+ }
+ return 0;
+ }
+ return check;
+}
+
+int insertFile(Ndb* pNdb,
+ const NdbDictionary::Table* pTab,
+ const char* fileName){
+
+ const int MAX_LINE_LEN = 8000;
+ char line[MAX_LINE_LEN];
+ int lineNo = 0;
+
+ FILE* instr = fopen(fileName, "r");
+ if (instr == NULL){
+ ndbout << "Coul'd not open " << fileName << endl;
+ return -1;
+ }
+
+ while(fgets(line, MAX_LINE_LEN, instr)){
+ lineNo++;
+
+ if (line[strlen(line)-1] == '\n') {
+ line[strlen(line)-1] = '\0';
+ }
+
+ int check = insertLine(pNdb, pTab, line);
+ if (check == -2){
+ ndbout << "Wrong format in input data file, line: " << lineNo << endl;
+ fclose(instr);
+ return -1;
+ }
+ if (check == -1){
+ fclose(instr);
+ return -1;
+
+ }
+ }
+
+ fclose(instr);
+ return 0;
+}
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ const char* _tabname = NULL;
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "tabname\n"\
+ "This program will bulk copy data from a file to a table in Ndb.\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) ||
+ argv[optind] == NULL || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _tabname = argv[optind];
+ ndbout << "Tablename: " << _tabname << endl;
+
+ // Connect to Ndb
+ Ndb MyNdb( "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ // Connect to Ndb and wait for it to become ready
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ // Check if table exists in db
+ const NdbDictionary::Table* pTab = MyNdb.getDictionary()->getTable(_tabname);
+ if(pTab == NULL){
+ ndbout << " Table " << _tabname << " does not exist!" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ char buf[255];
+ BaseString::snprintf(buf, sizeof(buf), "%s.data", (const char*)_tabname);
+ if (insertFile(&MyNdb, pTab, buf) != 0){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+
+}
+
+
+
diff --git a/storage/ndb/test/ndbapi/cdrserver.cpp b/storage/ndb/test/ndbapi/cdrserver.cpp
new file mode 100644
index 00000000000..976319034bf
--- /dev/null
+++ b/storage/ndb/test/ndbapi/cdrserver.cpp
@@ -0,0 +1,1628 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* **************************************************************** */
+/* */
+/* S E R V . T C P */
+/* * This is an example program that demonstrates the use of */
+/* stream sockets as an IPC mechanism. This contains the server, */
+/* and is intended to operate in conjunction with the client */
+/* program found in client.tcp. Together, these two programs */
+/* demonstrate many of the features of sockets, as well as good */
+/* conventions for using these features. */
+/* * This program provides a service called "example". In order for*/
+/* it to function, an entry for it needs to exist in the */
+/* ./etc/services file. The port address for this service can be */
+/* any port number that is likely to be unused, such as 22375, */
+/* for example. The host on which the client will be running */
+/* must also have the same entry (same port number) in its */
+/* ./etc/services file. */
+/* **************************************************************** */
+
+#include <ndb_global.h>
+
+/******** NDB INCLUDE ******/
+#include <NdbApi.hpp>
+/***************************/
+/*#include <sys/shm.h>*/
+#include <pthread.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <netdb.h>
+#include <time.h>
+#include <synch.h>
+#include <sched.h>
+
+extern "C" {
+#include "utv.h"
+#include "vcdrfunc.h"
+#include "bcd.h"
+}
+
+#ifndef TESTLEV
+#define TESTLEV
+#endif
+//#define DEBUG
+//#define MYDEBUG
+//#define SETDBG
+
+//#define ops_before_exe 64
+#define MAXOPSEXEC 1024
+
+/* Used in nanosleep */
+/**** NDB ********/
+static int bTestPassed;
+void create_table(Ndb* pMyNdb);
+void error_handler(const char* errorText);
+/*****************/
+static struct timespec tmspec1;
+static int server(long int);
+
+/* Function for initiating the cdr-area and make it clean for ongoing calls */
+
+static int s; /* connected socket descriptor */
+static int ls; /* listen socket descriptor */
+
+static struct hostent *hp; /* pointer to host info for remote host */
+static struct servent *sp; /* pointer to service information */
+
+struct linger linger; /* allow a lingering, graceful close; */
+ /* used when setting SO_LINGER */
+
+static struct sockaddr_in myaddr_in; /* for local socket address */
+static struct sockaddr_in peeraddr_in; /* for peer socket address */
+
+static FILE *fi; /* Log output */
+static char temp[600]="";
+
+static int ops_before_exe = 1; /* Number of operations per execute, default is 1,
+ but it can be changed with the -o parameter. */
+
+/*----------------------------------------------------------------------
+
+ M A I N
+ * This routine starts the server. It forks, leaving the child
+ to do all the work, so it does not have to be run in the
+ background. It sets up the listen socket, and for each incoming
+ connection, it forks a child process to process the data. It
+ will loop forever, until killed by a signal.
+
+ ----------------------------------------------------------------------*/
+
+/****** NDB *******/
+static char *tableName = "VWTABLE";
+/******************/
+
+#include <iostream>
+using namespace std;
+
+int main(int argc, const char** argv)
+{
+ ndb_init();
+ /******** NDB ***********/
+ /*
+ Ndb MyNdb( "TEST_DB" );
+ int tTableId;
+ */
+ /************************/
+ char tmpbuf[400];
+ /* Loop and status variables */
+ int i,j,found;
+
+ /* Used by the server */
+ int addrlen;
+
+ /* return code used with functions */
+ int rc;
+
+ i = 1;
+ while (argc > 1)
+ {
+ if (strcmp(argv[i], "-o") == 0)
+ {
+ ops_before_exe = atoi(argv[i+1]);
+ if ((ops_before_exe < 1) || (ops_before_exe > MAXOPSEXEC))
+ {
+ cout << "Number of operations per execute must be at least 1, and at most " << MAXOPSEXEC << endl;
+ exit(1);
+ }
+
+ }
+ else
+ {
+ cout << "Invalid parameter!" << endl << "Look in cdrserver.C for more info." << endl;
+ exit(1);
+ }
+
+ argc -= 2;
+ i = i + 2;
+ }
+
+
+ /* Setup log handling */
+ logname(temp,"Cdrserver","Mother","");
+ puts(temp);
+ fi=fopen(temp,"w");
+ if (fi == NULL)
+ {
+ perror(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ m2log(fi,"Initiation of program");
+
+ /***** NDB ******/
+ /*
+ MyNdb.init();
+ if (MyNdb.waitUntilReady(30) != 0)
+ {
+ puts("Not ready");
+ exit(-1);
+ }
+ tTableId = MyNdb.getTable()->openTable(tableName);
+ if (tTableId == -1)
+ {
+ printf("%d: Creating table",getpid());
+ create_table(&MyNdb);
+ }
+ else printf("%d: Table already create",getpid());
+ */
+
+ /****************/
+
+ /* clear out address structures */
+ memset ((char *)&myaddr_in, 0, sizeof(struct sockaddr_in));
+ memset ((char *)&peeraddr_in, 0, sizeof(struct sockaddr_in));
+
+ m2log(fi,"Socket setup starting");
+
+ /* Set up address structure for the listen socket. */
+ myaddr_in.sin_family = AF_INET;
+
+ /* The server should listen on the wildcard address, */
+ /* rather than its own internet address. This is */
+ /* generally good practice for servers, because on */
+ /* systems which are connected to more than one */
+ /* network at once will be able to have one server */
+ /* listening on all networks at once. Even when the */
+ /* host is connected to only one network, this is good */
+ /* practice, because it makes the server program more */
+ /* portable. */
+
+ myaddr_in.sin_addr.s_addr = INADDR_ANY;
+ /* Find the information for the "cdrserver" server */
+ /* in order to get the needed port number. */
+
+ sp = getservbyname ("cdrserver", "tcp");
+ if (sp == NULL) {
+ m2log(fi,"Service cdrserver not found in /etc/services");
+ m2log(fi,"Terminating.");
+ exit(EXIT_FAILURE);
+ }
+
+ myaddr_in.sin_port = sp->s_port;
+
+ /* Create the listen socket.i */
+
+ ls = socket (AF_INET, SOCK_STREAM, 0);
+ if (ls == -1) {
+ m2log(fi,"Unable to create socket");
+ m2log(fi,"Terminating.");
+ exit(EXIT_FAILURE);
+ }
+ printf("Socket created\n");
+ printf("Wait..........\n");
+ /* Bind the listen address to the socket. */
+ if (bind(ls,(struct sockaddr*)&myaddr_in, sizeof(struct sockaddr_in)) == -1) {
+ m2log(fi,"Unable to bind address");
+ m2log(fi,"Terminating.");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Initiate the listen on the socket so remote users */
+ /* can connect. The listen backlog is set to 5, which */
+ /* is the largest currently supported. */
+
+ if (listen(ls, 5) == -1) {
+ m2log(fi,"Unable to listen on socket");
+ m2log(fi,"Terminating.");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Now, all the initialization of the server is */
+ /* complete, and any user errors will have already */
+ /* been detected. Now we can fork the daemon and */
+ /* return to the user. We need to do a setpgrp */
+ /* so that the daemon will no longer be associated */
+ /* with the user's control terminal. This is done */
+ /* before the fork, so that the child will not be */
+ /* a process group leader. Otherwise, if the child */
+ /* were to open a terminal, it would become associated */
+ /* with that terminal as its control terminal. It is */
+ /* always best for the parent to do the setpgrp. */
+
+ m2log(fi,"Socket setup completed");
+ m2log(fi,"Start server");
+
+ setpgrp();
+
+ /* Initiate the tmspec struct for use with nanosleep() */
+ tmspec1.tv_sec = 0;
+ tmspec1.tv_nsec = 1;
+
+ printf("Waiting for client to connect.........\n");
+ printf("Done\n");
+ switch (fork()) {
+ case -1: /* Unable to fork, for some reason. */
+ m2log(fi,"Failed to start server");
+ m2log(fi,"Terminating.");
+ fclose(fi);
+ perror(argv[0]);
+ fprintf(stderr, "%s: unable to fork daemon\n", argv[0]);
+ exit(EXIT_FAILURE);
+
+ break;
+ case 0: /* The child process (daemon) comes here. */
+ m2log(fi,"Server started");
+
+ /* Close stdin and stderr so that they will not */
+ /* be kept open. Stdout is assumed to have been */
+ /* redirected to some logging file, or /dev/null. */
+ /* From now on, the daemon will not report any */
+ /* error messages. This daemon will loop forever, */
+ /* waiting for connections and forking a child */
+ /* server to handle each one. */
+
+ close((int)stdin);
+ close((int)stderr);
+ /* Set SIGCLD to SIG_IGN, in order to prevent */
+ /* the accumulation of zombies as each child */
+ /* terminates. This means the daemon does not */
+ /* have to make wait calls to clean them up. */
+
+ signal(SIGCLD, SIG_IGN);
+ for(EVER) {
+ if ((checkchangelog(fi,temp))==0)
+ m2log(fi,"Waiting for connection");
+ /* Note that addrlen is passed as a pointer */
+ /* so that the accept call can return the */
+ /* size of the returned address. */
+
+ addrlen = sizeof(struct sockaddr_in);
+
+ /* This call will block until a new */
+ /* connection arrives. Then, it will */
+ /* return the address of the connecting */
+ /* peer, and a new socket descriptor, s, */
+ /* for that connection. */
+
+ s = accept(ls,(struct sockaddr*) &peeraddr_in, &addrlen);
+ #ifdef MYDEBUG
+ puts("accepted");
+ #endif
+ if ((checkchangelog(fi,temp))==0)
+ m2log(fi,"Connection attempt from a client");
+ if ((checkchangelog(fi,temp))==0)
+ m2log(fi,"Start communication server");
+
+ if ( s == -1) exit(EXIT_FAILURE);
+ switch (fork()) {
+ case -1: /* Can't fork, just exit. */
+ if ((checkchangelog(fi,temp))==0)
+ m2log(fi,"Start communication server failed.");
+ exit(EXIT_FAILURE);
+ break;
+ case 0: /* Child process comes here. */
+
+ /* Get clients adress and save it in the info area */
+ /* Keep track of how many times the client connects to the server */
+ printf("Connect attempt from client %u\n",peeraddr_in.sin_addr.s_addr);
+ server(peeraddr_in.sin_addr.s_addr);
+ exit(EXIT_FAILURE);
+ break;
+ default: /* Daemon process comes here. */
+ /* The daemon needs to remember */
+ /* to close the new accept socket */
+ /* after forking the child. This */
+ /* prevents the daemon from running */
+ /* out of file descriptor space. It */
+ /* also means that when the server */
+ /* closes the socket, that it will */
+ /* allow the socket to be destroyed */
+ /* since it will be the last close. */
+ close(s);
+ break;
+ }
+ }
+ default: /* Parent process comes here. */
+ exit(EXIT_FAILURE);
+ }
+ return EXIT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+
+ S E R V E R
+ * This is the actual server routine that the daemon forks to
+ handle each individual connection. Its purpose is to receive
+ the request packets from the remote client, process them,
+ and return the results to the client. It will also write some
+ logging information to stdout.
+
+ ----------------------------------------------------------------------*/
+
+server(long int servernum)
+{
+ /******** NDB ***********/
+ Ndb MyNdb( "TEST_DB" );
+ int tTableId;
+ NdbConnection *MyTransaction;
+ NdbOperation *MyOperation;
+ int check;
+ int c1 = 0;
+ int c2 = 0;
+ int c3 = 0;
+ int c4 = 0;
+ int act_index = 0;
+ /************************/
+ register unsigned int reqcnt; /* keeps count of number of requests */
+ register unsigned int i; /* Loop counters */
+ register int x;
+ register short done; /* Loop variable */
+ short int found;
+
+ /* The server index number */
+ int thisServer;
+
+ /* Variables used to keep track of some statistics */
+ time_t ourtime;
+ time_t tmptime;
+ int tmpvalue;
+ long int tmptransfer;
+ long int transfer;
+ int ops = 0;
+
+ /* Variables used by the server */
+ char buf[400]; /* This example uses 10 byte messages. */
+ char *inet_ntoa();
+ char *hostname; /* points to the remote host's name string */
+ int len;
+ int rcvbuf_size;
+
+ long ctid;
+
+ unsigned char uc;
+
+ /* Variables used by the logging facilitiy */
+ char msg[600];
+ char crap[600];
+ char lognamn[600];
+
+ FILE *log;
+
+ /* scheduling parameter for pthread */
+ struct sched_param param1,param2,param3;
+
+ /* Header information */
+ /* cdrtype not used */
+ /*short cdrtype; */ /* 1 CDR Typ */
+ short cdrlen; /* 2 CDR recored length in bytes excluding CDR type */
+ short cdrsubtype; /* 1 CDR subtype */
+ unsigned int cdrid; /* 8 CDR unique number of each call */
+ unsigned int cdrtime; /* 4 CDR Time in seconds */
+ short cdrmillisec; /* 2 CDR Milliseconds */
+ short cdrstatus; /* 1 CDR For future use */
+ short cdrequipeid; /* 1 CDR Equipment id */
+ int cdrreserved1; /* 4 CDR For future use */
+
+ /* Defined or calculated for each record */
+ int cdrrestlen; /* Unprocessed data left in record in bytes */
+
+ /* Gemensamma datatyper */
+ unsigned short parmtype_prev; /* 1 Parameter type */
+ unsigned short parmtype; /* 1 Parameter type */
+ unsigned short parmlen; /* 1 Parameter type */
+
+ int rc; /* return code for functions */
+
+ /* Attribute object used with threads */
+ pthread_attr_t attr1;
+ pthread_attr_t attr2;
+ pthread_attr_t attr3;
+ struct cdr_record *tmpcdrptr,*ftest;
+ void *dat;
+
+ int error_from_client = 0;
+
+ /* Konstanter */
+ const int headerlen = 24; /* Length of header record */
+
+ parmtype_prev = 99;
+ reqcnt = 0;
+
+ /* Close the listen socket inherited from the daemon. */
+ close(ls);
+
+ printf("Use the readinfo program to get information about server status\n\n");
+
+ if((checkchangelog(fi,temp))==0)
+ c2log(fi,"Communication server started");
+
+ /* Look up the host information for the remote host */
+ /* that we have connected with. Its internet address */
+ /* was returned by the accept call, in the main */
+ /* daemon loop above. */
+
+ hp=gethostbyaddr((char *) &peeraddr_in.sin_addr,sizeof(struct in_addr),peeraddr_in.sin_family);
+
+ if (hp == NULL) {
+ /* The information is unavailable for the remote */
+ /* host. Just format its internet address to be */
+ /* printed out in the logging information. The */
+ /* address will be shown in "internet dot format". */
+
+ /*
+ hostname = inet_ntoa(peeraddr_in.sin_addr);
+ */
+ sprintf(hostname,"Test");
+ logname(lognamn,"Cdrserver","Child",hostname);
+ }
+ else {
+ hostname = hp->h_name; /* point to host's name */
+ logname(lognamn,"Cdrserver","Child",hostname);
+ }
+
+ log=fopen(lognamn,"w");
+ if (log == NULL)
+ {
+ perror(hostname);
+ exit(EXIT_FAILURE);
+ }
+ n2log(log,"Setup in progress");
+ /* Log a startup message. */
+
+ /* The port number must be converted first to host byte */
+ /* order before printing. On most hosts, this is not */
+ /* necessary, but the ntohs() call is included here so */
+ /* that this program could easily be ported to a host */
+ /* that does require it. */
+
+ BaseString::snprintf(msg,sizeof(msg),"Startup from %s port %u",hostname,ntohs(peeraddr_in.sin_port));
+ if ((checkchangelog(fi,temp))==0)
+ c2log(fi,msg);
+ n2log(log,msg);
+ BaseString::snprintf(msg,sizeof(msg),"For further information, see log(%s)",lognamn);
+ if ((checkchangelog(fi,temp))==0)
+ c2log(fi,msg);
+
+ /* Set the socket for a lingering, graceful close. */
+ /* This will cause a final close of this socket to wait until */
+ /* all * data sent on it has been received by the remote host. */
+
+ linger.l_onoff =1;
+ linger.l_linger =0;
+ if (setsockopt(s, SOL_SOCKET, SO_LINGER,(const char*)&linger,sizeof(linger)) == -1) {
+ BaseString::snprintf(msg,sizeof(msg),"Setting SO_LINGER, l_onoff=%d, l_linger=%d",linger.l_onoff,linger.l_linger);
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ goto errout;
+ }
+
+ /* Set the socket for a lingering, graceful close. */
+ /* This will cause a final close of this socket to wait until all * data sent */
+ /* on it has been received by the remote host. */
+
+ rcvbuf_size=64*1024;
+
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,(const char*) &rcvbuf_size,sizeof(rcvbuf_size)) == -1) {
+ BaseString::snprintf(msg,sizeof(msg),"Setting SO_RCVBUF = %d",rcvbuf_size);
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ goto errout;
+ }
+
+ /* Set nodelay on socket */
+ n2log(log,"Port setup complete");
+
+ /* Go into a loop, receiving requests from the remote */
+ /* client. After the client has sent the last request, */
+ /* it will do a shutdown for sending, which will cause */
+ /* an end-of-file condition to appear on this end of the */
+ /* connection. After all of the client's requests have */
+ /* been received, the next recv call will return zero */
+ /* bytes, signalling an end-of-file condition. This is */
+ /* how the server will know that no more requests will */
+ /* follow, and the loop will be exited. */
+
+ n2log(log,"Setup completed");
+
+ /* Fetch the process id for the server */
+
+ /* Inititate the variables used for counting transfer rates and rec/sec */
+ tmpvalue = 0;
+ tmptime = 0;
+ tmptransfer = 0;
+ transfer = 0;
+
+ printf("Client %s connected\nStarting to process the data\n\n",hostname);
+
+ tmpcdrptr = (struct cdr_record*)malloc(sizeof(struct cdr_record));
+
+ /***** NDB ******/
+ MyNdb.init();
+ if (MyNdb.waitUntilReady(30) != 0)
+ {
+ puts("Not ready");
+ exit(-1);
+ }
+ tTableId = MyNdb.getTable()->openTable(tableName);
+ if (tTableId == -1)
+ {
+ printf("%d: Creating table",getpid());
+ create_table(&MyNdb);
+ }
+ else printf("%d: Table already created",getpid());
+
+ /****************/
+
+ while (len = recv(s,buf,headerlen,MSG_WAITALL)) {
+ if (len == -1) {
+ snprintf(msg,sizeof(msg),"Error from recv");
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ goto errout; /* error from recv */
+ }
+
+ /* The reason this while loop exists is that there */
+ /* is a remote possibility of the above recv returning */
+ /* less than 10 bytes. This is because a recv returns */
+ /* as soon as there is some data, and will not wait for */
+ /* all of the requested data to arrive. Since 10 bytes */
+ /* is relatively small compared to the allowed TCP */
+ /* packet sizes, a partial receive is unlikely. If */
+ /* this example had used 2048 bytes requests instead, */
+ /* a partial receive would be far more likely. */
+ /* This loop will keep receiving until all 10 bytes */
+ /* have been received, thus guaranteeing that the */
+ /* next recv at the top of the loop will start at */
+ /* the begining of the next request. */
+
+ for (;len < headerlen;) {
+ x = recv(s,buf,(headerlen-len),0);
+ if (x == -1) {
+ snprintf(msg,sizeof(msg),"Error from recv");
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ goto errout; /* error from recv */
+ }
+ len=len+x;
+ }
+
+ if (ops == 0) {
+ MyTransaction = MyNdb.startTransaction();
+ if (MyTransaction == NULL)
+ error_handler(MyNdb.getNdbErrorString());
+ }//if
+
+ MyOperation = MyTransaction->getNdbOperation(tableName);
+ if (MyOperation == NULL)
+ error_handler(MyTransaction->getNdbErrorString());
+ /*------------------------------------------------------*/
+ /* Parse header of CDR records */
+ /*------------------------------------------------------*/
+
+ /*------------------------------------------------------*/
+ /* 1. Type of cdr */
+ /*------------------------------------------------------*/
+ /* Not used for the moment
+ cdrtype=(char)buf[0];
+ */
+ /*------------------------------------------------------*/
+ /* 2. Total length of CDR */
+ /*------------------------------------------------------*/
+ swab(buf+1,buf+1,2);
+ memcpy(&cdrlen,buf+1,2);
+ /*------------------------------------------------------*/
+ /* 3. Partial type of CDR */
+ /*------------------------------------------------------*/
+ cdrsubtype=(char)buf[3];
+ switch (cdrsubtype)
+ {
+ case 0:
+ c1++;
+ tmpcdrptr->CallAttemptState = 1;
+ check = MyOperation->insertTuple();
+ break;
+ case 1:
+ c2++;
+ tmpcdrptr->CallAttemptState = 2;
+ check = MyOperation->updateTuple();
+ break;
+ case 2:
+ c3++;
+ tmpcdrptr->CallAttemptState = 3;
+ check = MyOperation->deleteTuple();
+ break;
+ case 3:
+ c4++;
+ tmpcdrptr->CallAttemptState = 4;
+ check = MyOperation->deleteTuple();
+ break;
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ }
+ /*cdrsubtype=(cdrsubtype << 24) >> 24;*/
+ /*------------------------------------------------------*/
+ /* 4. ID number */
+ /*------------------------------------------------------*/
+ /*swab(buf+4,buf+4,4);*/ /* ABCD -> BADC */
+ /*
+ swab(buf+4,buf+4,4);
+ swab(buf+5,buf+5,2);
+ swab(buf+6,buf+6,2);
+ swab(buf+4,buf+4,2);
+ swab(buf+5,buf+5,2);
+ */
+ memcpy(&cdrid,buf+4,4);
+ tmpcdrptr->CallIdentificationNumber = cdrid;
+ #ifdef SETDBG
+ puts("CIN");
+ #endif
+ check = MyOperation->equal("CIN",(char*)&cdrid);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ #ifdef SETDBG
+ puts("CAS");
+ #endif
+
+ if (cdrsubtype < 2)
+ {
+ check = MyOperation->setValue("CAS",(char*)&cdrsubtype);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ }
+ /*------------------------------------------------------*/
+ /* 5. Time stamp */
+ /*------------------------------------------------------*/
+ swab(buf+12,buf+12,4);
+ swab(buf+13,buf+13,2);
+ swab(buf+14,buf+14,2);
+ swab(buf+12,buf+12,2);
+ swab(buf+13,buf+13,2);
+ memcpy(&cdrtime,buf+12,4);
+ switch (cdrsubtype)
+ {
+ case 0:
+ #ifdef SETDBG
+ puts("START_TIME");
+ #endif
+ check = MyOperation->setValue("START_TIME",(char*)&cdrtime);
+ break;
+ case 1:
+ #ifdef SETDBG
+ puts("Start1");
+ #endif
+ check = MyOperation->setValue("StartOfCharge",(char*)&cdrtime);
+ break;
+ case 2:
+ #ifdef SETDBG
+ puts("Start2");
+ #endif
+ /*
+ check = MyOperation->setValue("StopOfCharge",(char*)&cdrtime);
+ */
+ check = 0;
+ break;
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ }
+ /*------------------------------------------------------*/
+ /* 6. Milliseconds */
+ /*------------------------------------------------------*/
+ /* Not used by application
+ swab(buf+16,buf+16,2);
+ memcpy(&cdrmillisec,buf+16,2);
+ */
+ /*------------------------------------------------------*/
+ /* 7. CDR status reserverd for future use */
+ /*------------------------------------------------------*/
+ /* Not used by application
+ memcpy(&cdrstatus,buf+18,1);
+ */
+ /*------------------------------------------------------*/
+ /* 8. CDR equipe id, number of sending equipement */
+ /*------------------------------------------------------*/
+ /* Not used by application
+ memcpy(&cdrequipeid,buf+19,1);
+ */
+ /*cdrequipeid=(cdrequipeid << 24) >> 24;*/
+ /*------------------------------------------------------*/
+ /* 9. CDR reserverd for furter use */
+ /*------------------------------------------------------*/
+ /* Not used by applikation
+ swab(buf+20,buf+20,4);
+ swab(buf+21,buf+21,2);
+ swab(buf+22,buf+22,2);
+ swab(buf+20,buf+20,2);
+ swab(buf+21,buf+21,2);
+ memcpy(&cdrreserved1,buf+20,4);
+ */
+ /*------------------------------------------------------*/
+ /* calculate length of datapart in record */
+ /* Formula recordlength-headerlen-1 */
+ /*------------------------------------------------------*/
+ cdrrestlen=cdrlen-(headerlen-1);
+ /*------------------------------------------------------*/
+ /* Finished with header */
+ /*------------------------------------------------------*/
+ /* Read remaining cdr data into buffer for furter */
+ /* handling. */
+ /*------------------------------------------------------*/
+ len = recv(s,buf,cdrrestlen,MSG_WAITALL);
+ if (len == -1) {
+ snprintf(msg,sizeof(msg),"Error from recv");
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ goto errout; /* error from recv */
+ }
+ for (;len<cdrrestlen;) {
+ x = recv(s,buf,len-cdrrestlen,0);
+ if (x == -1) {
+ snprintf(msg,sizeof(msg),"Error from recv");
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ goto errout; /* error from recv */
+ }
+ len=len+x;
+ }
+ done=FALSE;
+
+ /* Count the transfer/sec */
+ tmptransfer += cdrlen;
+ if (cdrsubtype > 1)
+ {
+ #ifdef SETDBG
+ puts("Going to execute");
+ #endif
+ ops++;
+ if (ops == ops_before_exe) {
+ ops = 0;
+ check = MyTransaction->execute(Commit, CommitAsMuchAsPossible);
+ if ((check == -1) && (MyTransaction->getNdbError() != 0))
+ error_handler(MyTransaction->getNdbErrorString());
+ MyNdb.closeTransaction(MyTransaction);
+ #ifdef SETDBG
+ puts("Transaction closed");
+ #endif
+ }//if
+ reqcnt++;
+ continue;
+ }
+ for (x=0;x<=cdrrestlen && !done && cdrrestlen > 1;) {
+ uc=buf[x];
+ parmtype=uc;
+ /*parmtype=(parmtype << 24) >> 24;*/ /* Modified in sun worked in hp */
+
+ parmlen = buf[x+1];
+ /*parmlen =(parmlen << 24) >> 24;*/
+ x+=2;
+
+ switch (parmtype) {
+ case 4: /* Called party number */
+ bcd_decode2(parmlen,&buf[x],crap);
+ tmpcdrptr->BSubscriberNumberLength = (char)parmlen;
+ strcpy(tmpcdrptr->BSubscriberNumber,crap);
+ tmpcdrptr->BSubscriberNumber[parmlen] = '\0';
+ x=x+(parmlen/2);
+ if (parmlen % 2) x++;
+ tmpcdrptr->USED_FIELDS |= B_BSubscriberNumber;
+ #ifdef SETDBG
+ puts("BNumber");
+ #endif
+ check = MyOperation->setValue("BNumber",(char*)&tmpcdrptr->BSubscriberNumber);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 9: /* Calling Partys cataegory */
+ if (parmlen != 1) printf("ERROR: Calling partys category has wrong length %d\n",parmlen);
+ else tmpcdrptr->ACategory=(char)buf[x];
+ x+=parmlen;
+ tmpcdrptr->USED_FIELDS |= B_ACategory;
+ #ifdef SETDBG
+ puts("ACategory");
+ #endif
+ check = MyOperation->setValue("ACategory",(char*)&tmpcdrptr->ACategory);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 10: /* Calling Party Number */
+ bcd_decode2(parmlen,&buf[x],crap);
+ tmpcdrptr->ASubscriberNumberLength = (char)parmlen;
+ strcpy(tmpcdrptr->ASubscriberNumber,crap);
+ tmpcdrptr->ASubscriberNumber[parmlen] = '\0';
+ x=x+(parmlen/2);
+ if (parmlen % 2) x++;
+ tmpcdrptr->USED_FIELDS |= B_ASubscriberNumber;
+ #ifdef SETDBG
+ puts("ANumber");
+ #endif
+ check = MyOperation->setValue("ANumber",(char*)&tmpcdrptr->ASubscriberNumber);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 11: /* Redirecting number */
+ bcd_decode2(parmlen,&buf[x],crap);
+ strcpy(tmpcdrptr->RedirectingNumber,crap);
+ x=x+(parmlen/2);
+ if (parmlen % 2) x++;
+ tmpcdrptr->USED_FIELDS |= B_RedirectingNumber;
+ #ifdef SETDBG
+ puts("RNumber");
+ #endif
+ check = MyOperation->setValue("RNumber",(char*)&tmpcdrptr->RedirectingNumber);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 17: /* Called partys category */
+ if (parmlen != 1) printf("ERROR: Called partys category has wrong length %d\n",parmlen);
+ else tmpcdrptr->EndOfSelectionInformation=(char)buf[x];
+ x+=parmlen;
+ tmpcdrptr->USED_FIELDS |= B_EndOfSelectionInformation;
+ #ifdef SETDBG
+ puts("EndOfSelInf");
+ #endif
+ check = MyOperation->setValue("EndOfSelInf",(char*)&tmpcdrptr->EndOfSelectionInformation);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 18: /* Release reason */
+ if (parmlen != 1) printf("ERROR: Release reason has wrong length %d\n",parmlen);
+ else tmpcdrptr->CauseCode=(char)buf[x];
+ x+=parmlen;
+ tmpcdrptr->USED_FIELDS |= B_CauseCode;
+ #ifdef SETDBG
+ puts("CauseCode");
+ #endif
+ check = MyOperation->setValue("CauseCode",(char*)&tmpcdrptr->CauseCode);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 19: /* Redirection information */
+ switch (parmlen) {
+ case 1:
+ tmpcdrptr->ReroutingIndicator= (char)buf[x];
+ tmpcdrptr->USED_FIELDS |= B_ReroutingIndicator;
+ break;
+ case 2:
+ swab(buf+x,buf+x,2);
+ tmpcdrptr->ReroutingIndicator= buf[x];
+ tmpcdrptr->USED_FIELDS |= B_ReroutingIndicator;
+ break;
+ default :
+ BaseString::snprintf(msg,sizeof(msg),"ERROR: Redirection information has wrong length %d\n",parmlen);
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ break;
+ #ifdef SETDBG
+ puts("RI");
+ #endif
+ check = MyOperation->setValue("RI",(char*)&tmpcdrptr->ReroutingIndicator);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ }
+ x+=parmlen;
+ break;
+ case 32: /* User to user information */
+ if (parmlen != 1) printf("ERROR: User to User information has wrong length %d\n",parmlen);
+ else tmpcdrptr->UserToUserInformation=(char)buf[x];
+ x+=parmlen;
+ tmpcdrptr->USED_FIELDS |= B_UserToUserInformation;
+ #ifdef SETDBG
+ puts("UserToUserInf");
+ #endif
+ check = MyOperation->setValue("UserToUserInf",(char*)&tmpcdrptr->UserToUserInformation);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 40: /* Original called number */
+ bcd_decode2(parmlen,&buf[x],crap);
+ strcpy(tmpcdrptr->OriginalCalledNumber,crap);
+ x=x+(parmlen/2);
+ if (parmlen % 2) x++;
+ tmpcdrptr->USED_FIELDS |= B_OriginalCalledNumber;
+ #ifdef SETDBG
+ puts("ONumber");
+ #endif
+ check = MyOperation->setValue("ONumber",(char*)&tmpcdrptr->OriginalCalledNumber);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 42: /* User to user indicator */
+ if (parmlen != 1) printf("ERROR: User to User indicator has wrong length %d\n",parmlen);
+ else tmpcdrptr->UserToUserIndicatior=(char)buf[x];
+ x+=parmlen;
+ tmpcdrptr->USED_FIELDS |= B_UserToUserIndicatior;
+ #ifdef SETDBG
+ puts("UserToUserInd");
+ #endif
+ check = MyOperation->setValue("UserToUserInd",(char*)&tmpcdrptr->UserToUserIndicatior);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 63: /* Location number */
+ bcd_decode2(parmlen,&buf[x],crap);
+ strcpy(tmpcdrptr->LocationCode,crap);
+ x=x+(parmlen/2);
+ if (parmlen % 2) x++;
+ tmpcdrptr->USED_FIELDS |= B_LocationCode;
+ #ifdef SETDBG
+ puts("LocationCode");
+ #endif
+ check = MyOperation->setValue("LocationCode",(char*)&tmpcdrptr->LocationCode);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 240: /* Calling Partys cataegory */
+ if (parmlen != 1) printf("ERROR: Calling partys category has wrong length %d\n",parmlen);
+ else tmpcdrptr->NetworkIndicator=(char)buf[x];
+ x+=parmlen;
+ tmpcdrptr->USED_FIELDS |= B_NetworkIndicator;
+ #ifdef SETDBG
+ puts("NIndicator");
+ #endif
+ check = MyOperation->setValue("NIndicator",(char*)&tmpcdrptr->NetworkIndicator);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 241: /* Calling Partys cataegory */
+ if (parmlen != 1) printf("ERROR: Calling partys category has wrong length %d\n",parmlen);
+ else tmpcdrptr->TonASubscriberNumber=(char)buf[x];
+ x+=parmlen;
+ tmpcdrptr->USED_FIELDS |= B_TonASubscriberNumber;
+ #ifdef SETDBG
+ puts("TonANumber");
+ #endif
+ check = MyOperation->setValue("TonANumber",(char*)&tmpcdrptr->TonASubscriberNumber);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 242: /* Calling Partys cataegory */
+ if (parmlen != 1) printf("ERROR: Calling partys category has wrong length %d\n",parmlen);
+ else tmpcdrptr->TonBSubscriberNumber=(char)buf[x];
+ x+=parmlen;
+ tmpcdrptr->USED_FIELDS |= B_TonBSubscriberNumber;
+ #ifdef SETDBG
+ puts("TonBNumber");
+ #endif
+ check = MyOperation->setValue("TonBNumber",(char*)&tmpcdrptr->TonBSubscriberNumber);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 243: /* Calling Partys cataegory */
+ if (parmlen != 1) printf("ERROR: Calling partys category has wrong length %d\n",parmlen);
+ else tmpcdrptr->TonRedirectingNumber=(char)buf[x];
+ x+=parmlen;
+ tmpcdrptr->USED_FIELDS |= B_TonRedirectingNumber;
+ #ifdef SETDBG
+ puts("TonRNumber");
+ #endif
+ check = MyOperation->setValue("TonRNumber",(char*)&tmpcdrptr->TonRedirectingNumber);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 244: /* Calling Partys cataegory */
+ if (parmlen != 1) printf("ERROR: Calling partys category has wrong length %d\n",parmlen);
+ else tmpcdrptr->TonOriginalCalledNumber=(char)buf[x];
+ x+=parmlen;
+ tmpcdrptr->USED_FIELDS |= B_TonOriginalCalledNumber;
+ #ifdef SETDBG
+ puts("TonONumber");
+ #endif
+ check = MyOperation->setValue("TonONumber",(char*)&tmpcdrptr->TonOriginalCalledNumber);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 245: /* Calling Partys cataegory */
+ if (parmlen != 1) printf("ERROR: Calling partys category has wrong length %d\n",parmlen);
+ else tmpcdrptr->TonLocationCode=(char)buf[x];
+ x+=parmlen;
+ tmpcdrptr->USED_FIELDS |= B_TonLocationCode;
+ #ifdef SETDBG
+ puts("TonLocationCode");
+ #endif
+ check = MyOperation->setValue("TonLocationCode",(char*)&tmpcdrptr->TonLocationCode);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 252: /* RINParameter Parameter */
+ switch (parmlen) {
+ case 1:
+ tmpcdrptr->RINParameter=buf[x];
+ tmpcdrptr->USED_FIELDS |= B_RINParameter;
+ break;
+ case 2:
+ swab(buf+x,buf+x,2);
+ tmpcdrptr->RINParameter = buf[x] << 8;
+ tmpcdrptr->USED_FIELDS |= B_RINParameter;
+ break;
+ default :
+ BaseString::snprintf(msg,sizeof(msg),"ERROR: Rin parameter has wrong length %d\n",parmlen);
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ break;
+ }
+ x+=parmlen;
+ #ifdef SETDBG
+ puts("RINParameter");
+ #endif
+ check = MyOperation->setValue("RINParameter",(char*)&tmpcdrptr->RINParameter);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 253: /* OriginatingPointCode */
+ switch (parmlen) {
+ case 2:
+ swab(buf+x,buf+x,2);
+ memcpy(&tmpcdrptr->OriginatingPointCode,(buf+x),2);
+ tmpcdrptr->USED_FIELDS |= B_OriginatingPointCode;
+ break;
+ case 3:
+ swab(buf+x,buf+x,2);
+ swab(buf+(x+1),buf+(x+1),2);
+ swab(buf+x,buf+x,2);
+ memcpy(&tmpcdrptr->OriginatingPointCode,(buf+x),3);
+ tmpcdrptr->USED_FIELDS |= B_OriginatingPointCode;
+ break;
+ default :
+ BaseString::snprintf(msg,sizeof(msg),"ERROR: OriginatingPointCode parameter has wrong length %d\n",parmlen);
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ break;
+ }
+ x+=parmlen;
+ #ifdef SETDBG
+ puts("OPC");
+ #endif
+ check = MyOperation->setValue("OPC",(char*)&tmpcdrptr->OriginatingPointCode);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 254: /* DestinationPointCode */
+ switch (parmlen) {
+ case 2:
+ swab(buf+x,buf+x,2);
+ memcpy(&tmpcdrptr->DestinationPointCode,(buf+x),2);
+ /*
+ tmpcdrptr->DestinationPointCode = buf[x] << 8;
+ */
+ tmpcdrptr->USED_FIELDS |= B_DestinationPointCode;
+ break;
+ case 3:
+ swab(buf+x,buf+x,2);
+ swab(buf+(x+1),buf+(x+1),2);
+ swab(buf+x,buf+x,2);
+ memcpy(&tmpcdrptr->DestinationPointCode,(buf+x),3);
+ tmpcdrptr->USED_FIELDS |= B_DestinationPointCode;
+ break;
+ default :
+ BaseString::snprintf(msg,sizeof(msg),"ERROR: DestinationPointCode parameter has wrong length %d\n",parmlen);
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ break;
+ }
+ x+=parmlen;
+ #ifdef SETDBG
+ puts("DPC");
+ #endif
+ check = MyOperation->setValue("DPC",(char*)&tmpcdrptr->DestinationPointCode);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ case 255: /* CircuitIdentificationCode */
+ swab(buf+x,buf+x,2);
+ memcpy(&tmpcdrptr->CircuitIdentificationCode,(buf+x),2);
+ tmpcdrptr->USED_FIELDS |= B_CircuitIdentificationCode;
+ x+=parmlen;
+ #ifdef SETDBG
+ puts("CIC");
+ #endif
+ check = MyOperation->setValue("CIC",(char*)&tmpcdrptr->CircuitIdentificationCode);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+ default:
+ printf("ERROR: Undefined parmtype %d , previous %d, length %d\n",parmtype,parmtype_prev,parmlen);
+ BaseString::snprintf(msg,sizeof(msg),"ERROR: Undefined parmtype %d , previous %d, length %d\n",parmtype,parmtype_prev,parmlen);
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ if (parmlen == 0) {
+ x++;
+ }
+ x+=parmlen;
+ break;
+ }
+ parmtype_prev=parmtype;
+ if ((cdrrestlen-x) == 1) {
+ done=TRUE;
+ }
+ }
+ time(&ourtime);
+ if (ourtime != tmptime)
+ {
+ transfer = tmptransfer;
+ tmptransfer = 0;
+ if (++act_index == 30)
+ {
+ act_index = 0;
+ printf("Transfer=%d\n",transfer);
+ printf("Total operations=%d\n",reqcnt);
+ printf("CAS1=%d\n",c1/30);
+ printf("CAS2=%d\n",c2/30);
+ printf("CAS3=%d\n",c3/30);
+ c1=0;
+ c2=0;
+ c3=0;
+ }
+ tmptime = ourtime;
+ }
+ switch (cdrsubtype) {
+ case 0:
+ tmpcdrptr->ClientId = servernum;
+ #ifdef SETDBG
+ puts("ClientId");
+ #endif
+ check = MyOperation->setValue("ClientId",(char*)&tmpcdrptr->ClientId);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ tmpcdrptr->OurSTART_TIME = ourtime;
+ #ifdef SETDBG
+ puts("OurSTART_TIME");
+ #endif
+ check = MyOperation->setValue("OurSTART_TIME",(char*)&tmpcdrptr->OurSTART_TIME);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ tmpcdrptr->USED_FIELDS |= B_START_TIME;
+ #ifdef SETDBG
+ puts("USED_FIELDS");
+ #endif
+ check = MyOperation->setValue("USED_FIELDS",(char*)&tmpcdrptr->USED_FIELDS);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+
+ case 1:
+ tmpcdrptr->OurTimeForStartOfCharge = ourtime;
+ #ifdef SETDBG
+ puts("OurStartOfCharge");
+ #endif
+ check = MyOperation->setValue("OurStartOfCharge",(char*)&tmpcdrptr->OurTimeForStartOfCharge);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ tmpcdrptr->USED_FIELDS |= B_TimeForStartOfCharge;
+ #ifdef SETDBG
+ puts("USED_FIELDS");
+ #endif
+ check = MyOperation->setValue("USED_FIELDS",(char*)&tmpcdrptr->USED_FIELDS);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+
+ case 2:
+ tmpcdrptr->OurTimeForStopOfCharge = ourtime;
+ #ifdef SETDBG
+ puts("OurStopOfCharge");
+ #endif
+ check = MyOperation->setValue("OurStopOfCharge",(char*)&tmpcdrptr->OurTimeForStopOfCharge);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ tmpcdrptr->USED_FIELDS |= B_TimeForStopOfCharge;
+ #ifdef SETDBG
+ puts("USED_FIELDS");
+ #endif
+ check = MyOperation->setValue("USED_FIELDS",(char*)&tmpcdrptr->USED_FIELDS);
+ if (check == -1)
+ error_handler(MyTransaction->getNdbErrorString());
+ break;
+
+ case 3:
+ tmpcdrptr->CallAttemptState = 4;
+ break;
+ default:
+ snprintf(msg,sizeof(msg),"cdrtype %d unknown",cdrsubtype);
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ goto errout;
+ break;
+ }
+ ops++;
+ if (ops == ops_before_exe) {
+ ops = 0;
+ #ifdef SETDBG
+ puts("Going to execute");
+ #endif
+ check = MyTransaction->execute(Commit, CommitAsMuchAsPossible);
+ if ((check == -1) && (MyTransaction->getNdbError() != 0))
+ error_handler(MyTransaction->getNdbErrorString());
+ MyNdb.closeTransaction(MyTransaction);
+ #ifdef SETDBG
+ puts("Transaction closed");
+ #endif
+
+ #ifdef SETDBG
+ puts("New transaction initiated");
+ #endif
+ }//if
+ /* Increment the request count. */
+ reqcnt++;
+
+ /* Send a response back to the client. */
+
+ /* if (send(s, buf, 10, 0) != 10) goto errout; */
+ }
+
+ /* The loop has terminated, because there are no */
+ /* more requests to be serviced. As mentioned above, */
+ /* this close will block until all of the sent replies */
+ /* have been received by the remote host. The reason */
+ /* for lingering on the close is so that the server will */
+ /* have a better idea of when the remote has picked up */
+ /* all of the data. This will allow the start and finish */
+ /* times printed in the log file to reflect more accurately */
+ /* the length of time this connection was */
+ /* The port number must be converted first to host byte */
+ /* order before printing. On most hosts, this is not */
+ /* necessary, but the ntohs() call is included here so */
+ /* that this program could easily be ported to a host */
+ /* that does require it. */
+
+ BaseString::snprintf(msg,sizeof(msg),"Completed %s port %u, %d requests",hostname,ntohs(peeraddr_in.sin_port), reqcnt);
+ if ((checkchangelog(fi,temp))==0)
+ c2log(fi,msg);
+ error_from_client = 1;
+ BaseString::snprintf(msg,sizeof(msg),"Communicate with threads");
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ BaseString::snprintf(msg,sizeof(msg),"Waiting for threads to return from work");
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ BaseString::snprintf(msg,sizeof(msg),"Closing down");
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ close(s);
+ fclose(log);
+ return EXIT_SUCCESS;
+
+errout:
+ BaseString::snprintf(msg,sizeof(msg),"Connection with %s aborted on error\n", hostname);
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ if ((checkchangelog(fi,temp))==0)
+ c2log(fi,msg);
+ error_from_client = 1;
+ BaseString::snprintf(msg,sizeof(msg),"Communicate with threads");
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ BaseString::snprintf(msg,sizeof(msg),"Waiting for threads to return from work");
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ BaseString::snprintf(msg,sizeof(msg),"Closing down");
+ if ((checkchangelog(log,lognamn))==0)
+ n2log(log,msg);
+ close(s);
+ fclose(log);
+ return EXIT_FAILURE;
+}
+
+void
+create_table(Ndb* pMyNdb)
+{
+
+ /****************************************************************
+ * Create table and attributes.
+ *
+ * create table basictab1(
+ * col1 int,
+ * col2 int not null,
+ * col3 int not null,
+ * col4 int not null
+ * )
+ *
+ ***************************************************************/
+
+ int check;
+ int i;
+ NdbSchemaCon *MySchemaTransaction;
+ NdbSchemaOp *MySchemaOp;
+ int tAttributeSize;
+
+ tAttributeSize = 1;
+
+ cout << "Creating " << tableName << "..." << endl;
+
+ MySchemaTransaction = pMyNdb->startSchemaTransaction();
+ if( MySchemaTransaction == NULL )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // Createtable
+ check = MySchemaOp->createTable( tableName,
+ 8, // Table Size
+ TupleKey, // Key Type
+ 40 // Nr of Pages
+ );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // CallIdentificationNumber Create first column, primary key
+ check = MySchemaOp->createAttribute( "CIN",
+ TupleKey,
+ 32,
+ tAttributeSize,
+ UnSigned, MMBased,
+ NotNullAttribute
+ );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+
+ // USED_FIELDS Create attributes
+ check = MySchemaOp->createAttribute( "USED_FIELDS", NoKey, 32,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // ClientId Create attributes
+ check = MySchemaOp->createAttribute( "ClientId", NoKey, 32,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // START_TIME Create attributes
+ check = MySchemaOp->createAttribute( "START_TIME", NoKey, 32,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // OurSTART_TIME Create attributes
+ check = MySchemaOp->createAttribute( "OurSTART_TIME", NoKey, 32,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // TimeForStartOfCharge Create attributes
+ check = MySchemaOp->createAttribute( "StartOfCharge", NoKey, 32,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // TimeForStopOfCharge Create attributes
+ check = MySchemaOp->createAttribute( "StopOfCharge", NoKey, 32,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // OurTimeForStartOfCharge Create attributes
+ check = MySchemaOp->createAttribute( "OurStartOfCharge", NoKey, 32,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // OurTimeForStopOfCharge Create attributes
+ check = MySchemaOp->createAttribute( "OurStopOfCharge", NoKey, 32,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // DestinationPointCode Create attributes
+ check = MySchemaOp->createAttribute( "DPC", NoKey, 16,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // OriginatingPointCode Create attributes
+ check = MySchemaOp->createAttribute( "OPC", NoKey, 16,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // CircuitIdentificationCode Create attributes
+ check = MySchemaOp->createAttribute( "CIC", NoKey, 16,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // ReroutingIndicator Create attributes
+ check = MySchemaOp->createAttribute( "RI", NoKey, 16,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // RINParameter Create attributes
+ check = MySchemaOp->createAttribute( "RINParameter", NoKey, 16,
+ tAttributeSize, UnSigned, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // NetworkIndicator Create attributes
+ check = MySchemaOp->createAttribute( "NIndicator", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // CallAttemptState Create attributes
+ check = MySchemaOp->createAttribute( "CAS", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // ACategory Create attributes
+ check = MySchemaOp->createAttribute( "ACategory", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // EndOfSelectionInformation Create attributes
+ check = MySchemaOp->createAttribute( "EndOfSelInf", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // UserToUserInformation Create attributes
+ check = MySchemaOp->createAttribute( "UserToUserInf", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // UserToUserIndicator Create attributes
+ check = MySchemaOp->createAttribute( "UserToUserInd", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // CauseCode Create attributes
+ check = MySchemaOp->createAttribute( "CauseCode", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // ASubscriberNumber attributes
+ check = MySchemaOp->createAttribute( "ANumber", NoKey, 8,
+ ASubscriberNumber_SIZE, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // ASubscriberNumberLenght attributes
+ check = MySchemaOp->createAttribute( "ANumberLength", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // TonASubscriberNumber attributes
+ check = MySchemaOp->createAttribute( "TonANumber", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // BSubscriberNumber attributes
+ check = MySchemaOp->createAttribute( "BNumber", NoKey, 8,
+ BSubscriberNumber_SIZE, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // BSubscriberNumberLength attributes
+ check = MySchemaOp->createAttribute( "BNumberLength", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // TonBSubscriberNumber attributes
+ check = MySchemaOp->createAttribute( "TonBNumber", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // RedirectingNumber attributes
+ check = MySchemaOp->createAttribute( "RNumber", NoKey, 8,
+ ASubscriberNumber_SIZE, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // TonRedirectingNumber attributes
+ check = MySchemaOp->createAttribute( "TonRNumber", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // OriginalCalledNumber attributes
+ check = MySchemaOp->createAttribute( "ONumber", NoKey, 8,
+ ASubscriberNumber_SIZE, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // TonOriginalCalledNumber attributes
+ check = MySchemaOp->createAttribute( "TonONumber", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // LocationCode attributes
+ check = MySchemaOp->createAttribute( "LocationCode", NoKey, 8,
+ ASubscriberNumber_SIZE, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // TonLocationCode attributes
+ check = MySchemaOp->createAttribute( "TonLocationCode", NoKey, 8,
+ tAttributeSize, Signed, MMBased,
+ NullAttribute );
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ if( MySchemaTransaction->execute() == -1 ) {
+ cout << tableName << " already exist" << endl;
+ cout << "Message: " << MySchemaTransaction->getNdbErrorString() << endl;
+ }
+ else
+ {
+ cout << tableName << " created" << endl;
+ }
+ pMyNdb->closeSchemaTransaction(MySchemaTransaction);
+
+ return;
+}
+
+void
+error_handler(const char* errorText)
+{
+ // Test failed
+ cout << endl << "ErrorMessage: " << errorText << endl;
+ bTestPassed = -1;
+}
diff --git a/storage/ndb/test/ndbapi/celloDb.cpp b/storage/ndb/test/ndbapi/celloDb.cpp
new file mode 100644
index 00000000000..2d6401c355a
--- /dev/null
+++ b/storage/ndb/test/ndbapi/celloDb.cpp
@@ -0,0 +1,1504 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* ***************************************************
+ BASIC TEST 1
+ Test basic functions and status of NDB
+
+ Arguments:
+ none
+
+ Returns:
+ 0 - Test passed
+ -1 - Test failed
+ 1 - Invalid arguments
+flexBench
+ * *************************************************** */
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+
+#define MAXATTR 4
+#define MAXTABLES 4
+#define PAGESIZE 8192
+#define OVERHEAD 0.02
+#define NUMBEROFRECORDS 10
+#define PKSIZE 1
+#define ATTRNAMELEN 16
+
+
+static void createTable_IPACCT(Ndb*);
+static void createTable_RPACCT(Ndb*);
+static void createTable_SBMCALL(Ndb*);
+static void createTable_TODACCT(Ndb*);
+
+static void error_handler(const char*);
+static bool error_handler2(const char*, int) ;
+
+static void setAttrNames(void);
+static void setTableNames(void);
+static void create_table(Ndb*);
+static void insert_rows(Ndb*);
+static void update_rows(Ndb*);
+static void delete_rows(Ndb*);
+static void verify_deleted(Ndb*);
+static void read_and_verify_rows(Ndb*);
+
+static void insert_IPACCT(Ndb*, Uint32, Uint32, Uint32, Uint32, Uint32); //3 for Pk, and two data. just to test;
+
+static void read_IPACCT(Ndb* , Uint32 , Uint32 , Uint32 );
+
+static int tAttributeSize;
+static int bTestPassed;
+
+static char tableName[MAXTABLES][ATTRNAMELEN];static char attrName[MAXATTR][ATTRNAMELEN];
+static int attrValue[NUMBEROFRECORDS];
+static int pkValue[NUMBEROFRECORDS];
+static int failed = 0 ;
+#include <NdbOut.hpp>
+
+NDB_COMMAND(celloDb, "celloDb", "celloDb", "celloDb", 65535)
+{
+ ndb_init();
+
+ int tTableId;
+ int i;
+ Ndb MyNdb( "CELLO-SESSION-DB" );
+
+ MyNdb.init();
+
+ // Assume test passed
+ bTestPassed = 0;
+ /*
+ // Initialize global variables
+ for (i = 0; i < NUMBEROFRECORDS; i ++)
+ pkValue[i] = i;
+
+ for (i = 0; i < NUMBEROFRECORDS; i ++)
+ attrValue[i] = i;
+ */
+ tAttributeSize = 1;
+
+ // Wait for Ndb to become ready
+ if (MyNdb.waitUntilReady() == 0) {
+ ndbout << endl << "Cello session db - Starting " << endl;
+ ndbout << "Test basic functions and status of NDB" << endl;
+
+
+
+ createTable_SBMCALL (&MyNdb );
+ createTable_RPACCT (&MyNdb );
+ createTable_TODACCT (&MyNdb );
+ createTable_IPACCT (&MyNdb );
+
+ insert_IPACCT(&MyNdb, 1,2,1,2,2);
+ read_IPACCT(&MyNdb, 1, 2 , 1);
+ /*
+ insert_rows(&MyNdb);
+
+ read_and_verify_rows(&MyNdb);
+
+
+ // Create some new values to use for update
+ for (i = 0; i < NUMBEROFRECORDS; i++)
+ attrValue[i] = NUMBEROFRECORDS-i;
+
+ update_rows(&MyNdb);
+
+ read_and_verify_rows(&MyNdb);
+
+ delete_rows(&MyNdb);
+
+ verify_deleted(&MyNdb);
+ */
+ }
+ else {
+ bTestPassed = -1;
+ }
+
+
+ if (bTestPassed == 0)
+ {
+ // Test passed
+ ndbout << "OK - Test passed" << endl;
+ }
+ else
+ {
+ // Test failed
+ ndbout << "FAIL - Test failed" << endl;
+ exit(-1);
+ }
+ return NULL;
+}
+
+static void
+error_handler(const char* errorText)
+{
+ // Test failed
+ ndbout << endl << "ErrorMessage: " << errorText << endl;
+ bTestPassed = -1;
+}
+
+static void
+createTable_SBMCALL ( Ndb* pMyNdb )
+{
+ /****************************************************************
+ * Create table and attributes.
+ *
+ * create table SBMCALL(
+ * for attribs, see the REQ SPEC for cello session DB
+ * )
+ *
+ ***************************************************************/
+
+ const char* tname = "SBMCALL";
+ Uint32 recordsize = 244; //including 12 byte overhead
+ Uint32 pksize = 8; //size of total prim. key. in bytes. sum of entire composite PK.
+ Uint32 tTableId = pMyNdb->getTable()->openTable(tname);
+
+ if (tTableId == -1) {
+ Uint32 check;
+ Uint32 i;
+ NdbSchemaCon *MySchemaTransaction;
+ NdbSchemaOp *MySchemaOp;
+
+ ndbout << "Creating " << tname << "..." << endl;
+
+ MySchemaTransaction = pMyNdb->startSchemaTransaction();
+ if( ( MySchemaTransaction == NULL ) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( ( MySchemaOp == NULL ) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Createtable
+
+ Uint32 tablesize=(recordsize*NUMBEROFRECORDS + OVERHEAD * NUMBEROFRECORDS)/1024;
+ Uint32 noPages=(pksize*NUMBEROFRECORDS)/PAGESIZE;
+
+ ndbout << "table size " << tablesize << "for table name " << tname << endl;
+
+ check = MySchemaOp->createTable( tname,
+ tablesize, // Table Size
+ TupleKey, // Key Type
+ noPages, // Nr of Pages
+ All,
+ 6,
+ 78,
+ 80,
+ 1,
+ true
+ );
+
+ if( check == -1 ) {
+ error_handler(MySchemaTransaction->getNdbErrorString());
+ exit(-1);
+ }
+
+
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute( "SPBBOARDID",
+ TupleKey,
+ 32,
+ PKSIZE,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create second column, primary key
+ check = MySchemaOp->createAttribute( "CALLID",
+ TupleKey,
+ 32,
+ PKSIZE,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Creat thrid column, RP Session info, byte[16] represented as (String, 16)
+ check = MySchemaOp->createAttribute( "RPSESS",
+ NoKey,
+ 32,
+ 16,
+ String,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+// Creat fourth column, GRE Tunnel info, byte[16] represented as (String, 16)
+ check = MySchemaOp->createAttribute( "GRETUNNEL",
+ NoKey,
+ 32,
+ 16,
+ String,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+// Creat fifth column, PPP Session info, byte[24] represented as (String, 24)
+ check = MySchemaOp->createAttribute( "PPPSESS",
+ NoKey,
+ 32,
+ 24,
+ String,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ if( (MySchemaTransaction->execute() == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+ pMyNdb->closeSchemaTransaction(MySchemaTransaction);
+ ndbout << "done" << endl;
+
+
+ } //if
+
+ //else table already created , proceed
+}
+
+
+
+static void
+createTable_RPACCT(Ndb*pMyNdb)
+{
+
+ /****************************************************************
+ * Create table and attributes.
+ *
+ * create table RPACCT(
+ * for attribs, see the REQ SPEC for cello session DB
+ * )
+ *
+ ***************************************************************/
+
+ const char* tname = "RPACCT";
+ Uint32 recordsize = 380; //including 12 byte overhead
+ Uint32 pksize = 8; //size of total prim. key. in bytes.
+ Uint32 tTableId = pMyNdb->getTable()->openTable(tname);
+
+ if (tTableId == -1) {
+ Uint32 check;
+ Uint32 i;
+ NdbSchemaCon *MySchemaTransaction;
+ NdbSchemaOp *MySchemaOp;
+
+ ndbout << "Creating " << tname << "..." << endl;
+
+ MySchemaTransaction = pMyNdb->startSchemaTransaction();
+ if( MySchemaTransaction == NULL )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // Createtable
+
+ Uint32 tablesize=(recordsize*NUMBEROFRECORDS + OVERHEAD * NUMBEROFRECORDS)/1024;
+ Uint32 noPages=(pksize*NUMBEROFRECORDS)/PAGESIZE;
+
+ ndbout << "table size " << tablesize << "for table name " << tname << endl;
+
+ check = MySchemaOp->createTable( tname,
+ tablesize, // Table Size
+ TupleKey, // Key Type
+ noPages // Nr of Pages
+ );
+
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute( "SPBBOARDID",
+ TupleKey,
+ 32,
+ PKSIZE,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create second column, primary key
+ check = MySchemaOp->createAttribute( "CALLID",
+ TupleKey,
+ 32,
+ PKSIZE,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Creat thrid column MS ID, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "MSID",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create PDSN FA Address, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "PDSN",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create Serving PCF, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "SPCF",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+ // Create BS ID, 12 byte char, represented as String,12
+ check = MySchemaOp->createAttribute( "BSID",
+ NoKey,
+ 32,
+ 12,
+ String,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+
+
+ // Create User Zone, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "UZ",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create Forward Multiplex, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "FMO",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create Reverse Multiplex, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "RMO",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create Forward Fund rate, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "FFR",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create Reverse Fund rate, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "RFR",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+ // Create Service Option, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "SO",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+
+ // Create Forward Traffic Type, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "FTT",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+// Create Reverse Traffic Type, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "RTT",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+// Create Fund Frame Size, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "FFS",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+// Create Forware Fund RC, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "FFRC",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+ // Create Reverse Fund RC, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "RFRC",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create DCCH Frame Format, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "DCCH",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create Airlink QOS, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "AQOS",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+// Create Bad PPP Frame Count , 4 byte unsigned
+ check = MySchemaOp->createAttribute( "BPPPFC",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+
+// Create Active Time , 4 byte unsigned
+ check = MySchemaOp->createAttribute( "AT",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+// Create Nb Active Transitions , 4 byte unsigned
+ check = MySchemaOp->createAttribute( "NBAT",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+// Create SDB Octet Count In , 4 byte unsigned
+ check = MySchemaOp->createAttribute( "SDBOCI",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+
+// Create Nb SDB In, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "NBSDBI",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+
+// Create Nb SDB Out, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "NBSDBO",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+
+// Create HDLC Bytes received, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "HDLC",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+ if( (MySchemaTransaction->execute() == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+ pMyNdb->closeSchemaTransaction(MySchemaTransaction);
+ ndbout << "done" << endl;
+
+ } //if
+
+ //else table already created , proceed
+ }
+
+
+static void
+createTable_IPACCT(Ndb* pMyNdb)
+{
+
+
+ /****************************************************************
+ * Create table and attributes.
+ *
+ * create table IPACCT(
+ * for attribs, see the REQ SPEC for cello session DB
+ * )
+ *
+ ***************************************************************/
+
+ const char* tname = "IPACCT";
+ Uint32 recordsize = 70; //including 12 byte overhead
+ Uint32 pksize = 12; //size of total prim. key. in bytes.
+ Uint32 tTableId = pMyNdb->getTable()->openTable(tname);
+
+ if (tTableId == -1) {
+ Uint32 check;
+ Uint32 i;
+ NdbSchemaCon *MySchemaTransaction;
+ NdbSchemaOp *MySchemaOp;
+
+ ndbout << "Creating " << tname << "..." << endl;
+
+ MySchemaTransaction = pMyNdb->startSchemaTransaction();
+ if( MySchemaTransaction == NULL )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // Createtable
+
+ Uint32 tablesize=(recordsize*NUMBEROFRECORDS + OVERHEAD * NUMBEROFRECORDS)/1024;
+ Uint32 noPages=(pksize*NUMBEROFRECORDS)/PAGESIZE;
+
+ ndbout << "table size " << tablesize << "for table name " << tname << endl;
+
+ check = MySchemaOp->createTable( tname,
+ tablesize, // Table Size
+ TupleKey, // Key Type
+ noPages // Nr of Pages
+ );
+
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute( "SPBBOARDID",
+ TupleKey,
+ 32,
+ PKSIZE,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create second column, primary key
+ check = MySchemaOp->createAttribute( "CALLID",
+ TupleKey,
+ 32,
+ PKSIZE,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+ // Create third column, primary key
+ check = MySchemaOp->createAttribute( "IPADDR",
+ TupleKey,
+ 32,
+ PKSIZE,
+ String,
+ MMBased,
+ NotNullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+
+// Create Acct session id 4 byte unsigned
+ check = MySchemaOp->createAttribute( "ASID",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+// Create Correlation ID, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "CID",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+// Create MIP HA Address, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "MIPHA",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+
+
+// Create IP technology, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "IPT",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+// Create Compuls Tunnel ID, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "CTID",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+// Create IP QOS, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "IPQOS",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create Data octet count in, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "DOCI",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+ // Create Data octet count out, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "DOCO",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+ // Create Event time, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "ET",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+ // Create In mip sig count, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "IMSC",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+// Create Out mip sig count, 4 byte unsigned
+ check = MySchemaOp->createAttribute( "OMSC",
+ NoKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ if( (MySchemaTransaction->execute() == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+ pMyNdb->closeSchemaTransaction(MySchemaTransaction);
+ ndbout << "done" << endl;
+
+
+
+ } //if
+
+ //else table already created , proceed
+}
+
+
+static void
+createTable_TODACCT(Ndb* pMyNdb)
+{
+
+
+ /****************************************************************
+ * Create table and attributes.
+ *
+ * create table TODACCT(
+ * for attribs, see the REQ SPEC for cello session DB
+ * )
+ *
+ ***************************************************************/
+
+ const char* tname = "TODACCT";
+ Uint32 recordsize = 92; //including 12 byte overhead
+ Uint32 pksize = 12; //size of total prim. key. in bytes.
+ Uint32 tTableId = pMyNdb->getTable()->openTable(tname);
+
+ if (tTableId == -1) {
+ Uint32 check;
+ Uint32 i;
+ NdbSchemaCon *MySchemaTransaction;
+ NdbSchemaOp *MySchemaOp;
+
+ ndbout << "Creating " << tname << "..." << endl;
+
+ MySchemaTransaction = pMyNdb->startSchemaTransaction();
+ if( MySchemaTransaction == NULL )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+ // Createtable
+
+ Uint32 tablesize=(recordsize*NUMBEROFRECORDS + OVERHEAD * NUMBEROFRECORDS)/1024;
+ Uint32 noPages=(pksize*NUMBEROFRECORDS)/PAGESIZE;
+
+ ndbout << "table size " << tablesize << "for table name " << tname << endl;
+
+ check = MySchemaOp->createTable( tname,
+ tablesize, // Table Size
+ TupleKey, // Key Type
+ noPages // Nr of Pages
+ );
+
+ if( check == -1 )
+ error_handler(MySchemaTransaction->getNdbErrorString());
+
+
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute( "SPBBOARDID",
+ TupleKey,
+ 32,
+ PKSIZE,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+ // Create second column, primary key
+ check = MySchemaOp->createAttribute( "CALLID",
+ TupleKey,
+ 32,
+ PKSIZE,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+ // Create third column, primary key
+ check = MySchemaOp->createAttribute( "IPADDR",
+ TupleKey,
+ 32,
+ PKSIZE,
+ String,
+ MMBased,
+ NotNullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+ // Create third column, primary key
+ check = MySchemaOp->createAttribute( "INDEX",
+ TupleKey,
+ 32,
+ PKSIZE,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+
+// Create Acct session id 4 byte unsigned
+ check = MySchemaOp->createAttribute( "TOD",
+ NoKey,
+ 32,
+ 16,
+ String,
+ MMBased,
+ NullAttribute );
+
+
+ if( (check == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+
+
+ if( (MySchemaTransaction->execute() == -1) && (!error_handler2((const char*)MySchemaTransaction->getNdbErrorString(), MySchemaTransaction->getNdbError())) )
+ exit (-1) ;
+
+ pMyNdb->closeSchemaTransaction(MySchemaTransaction);
+ ndbout << "done" << endl;
+
+ } //if
+
+ //else table already created , proceed
+}
+
+
+
+
+
+
+static void read_IPACCT(Ndb* pMyNdb, Uint32 CALLID, Uint32 SPBBOARDID , Uint32 IPADDR)
+{
+
+
+ int check;
+ int loop_count_ops;
+ int count;
+ int count_attributes;
+ char* value;
+ NdbConnection *MyTransaction;
+ NdbOperation *MyOperation;
+ NdbRecAttr* tTmp;
+
+
+
+ MyTransaction = pMyNdb->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler(pMyNdb->getNdbErrorString());
+
+ MyOperation = MyTransaction->getNdbOperation("IPACCT");
+ if (MyOperation == NULL)
+ error_handler( MyTransaction->getNdbErrorString());
+
+ check = MyOperation->readTuple();
+ if( check == -1 )
+ error_handler( MyTransaction->getNdbErrorString());
+
+ check = MyOperation->equal( "SPBBOARDID",SPBBOARDID );
+ if( check == -1 )
+ error_handler( MyTransaction->getNdbErrorString());
+
+
+ check = MyOperation->equal( "IPADDR","IPADDR" );
+ if( check == -1 )
+ error_handler( MyTransaction->getNdbErrorString());
+
+
+ check = MyOperation->equal( "CALLID",CALLID );
+ if( check == -1 )
+ error_handler( MyTransaction->getNdbErrorString());
+
+
+
+
+ tTmp = MyOperation->getValue("IPQOS", NULL );
+ if( tTmp == NULL )
+ error_handler( MyTransaction->getNdbErrorString());
+ ndbout << " tTmp " << tTmp->isNULL() << endl;
+ MyTransaction->execute(Commit);
+
+ ndbout << " value read " << tTmp->int32_value() << endl;
+
+}
+
+
+
+static void insert_IPACCT(Ndb* pMyNdb, Uint32 CALLID, Uint32 SPBBOARDID , Uint32 IPADDR, Uint32 ASID, Uint32 IPQOS)
+{
+ /****************************************************************
+ * Insert rows
+ *
+ ***************************************************************/
+
+ int check;
+ int loop_count_ops;
+ int count;
+ int i;
+ NdbConnection *MyTransaction;
+ NdbOperation *MyOperation;
+
+ ndbout << "Inserting records..." << flush;
+
+ MyTransaction = pMyNdb->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler(pMyNdb->getNdbErrorString());
+
+ MyOperation = MyTransaction->getNdbOperation("IPACCT");
+ if (MyOperation == NULL)
+ error_handler(MyTransaction->getNdbErrorString());
+
+
+
+ check = MyOperation->insertTuple();
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+
+ ndbout << "insertTuple" << endl;
+
+ check = MyOperation->equal("CALLID",CALLID );
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+ ndbout << "equal" << endl;
+
+
+ check = MyOperation->equal("SPBBOARDID",SPBBOARDID );
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+ ndbout << "equal" << endl;
+
+
+ check = MyOperation->equal("IPADDR","IPADDR" );
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+ ndbout << "equal" << endl;
+
+
+ check = MyOperation->setValue( "IPQOS", IPQOS);
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+ ndbout << "Set Value" << endl;
+
+
+
+ check = MyOperation->setValue( "ASID", ASID);
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+ ndbout << "Set Value" << endl;
+
+
+ check = MyTransaction->execute( Commit );
+ if(check == -1 ) {
+ ndbout << "error at commit" << endl;
+ error_handler(MyTransaction->getNdbErrorString());
+ }
+ else
+ ;//ndbout << ".";
+
+ pMyNdb->closeTransaction(MyTransaction);
+
+
+
+ ndbout << "OK" << endl;
+
+ return;
+}
+
+static void
+update_rows(Ndb* pMyNdb){
+ /****************************************************************
+ * Update rows in SimpleTable
+ *
+ ***************************************************************/
+
+ int check;
+ int loop_count_ops;
+ int count;
+ int i;
+ NdbConnection *MyTransaction;
+ NdbOperation *MyOperation;
+
+ ndbout << "Updating records..." << flush;
+
+ loop_count_ops = NUMBEROFRECORDS;
+
+ for (count=0 ; count < loop_count_ops ; count++) {
+
+ MyTransaction = pMyNdb->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler( pMyNdb->getNdbErrorString() );
+
+ MyOperation = MyTransaction->getNdbOperation(tableName[0]);
+ if (MyOperation == NULL)
+ error_handler(MyTransaction->getNdbErrorString());
+
+ check = MyOperation->updateTuple();
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+
+ check = MyOperation->equal( attrName[0], (char*)&pkValue[count] );
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+
+ for (i = 1; i < MAXATTR; i++)
+ {
+ check = MyOperation->setValue( attrName[i], (char*)&attrValue[count]);
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+ }
+
+ if( MyTransaction->execute( Commit ) == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+ else
+ ;//ndbout << ".";
+
+ pMyNdb->closeTransaction(MyTransaction);
+
+ }
+
+ ndbout << "OK" << endl;
+ return;
+
+};
+
+static void
+delete_rows(Ndb* pMyNdb){
+
+ /****************************************************************
+ * Delete rows from SimpleTable
+ *
+ ***************************************************************/
+
+ int check;
+ int loop_count_ops;
+ int count;
+ NdbConnection *MyTransaction;
+ NdbOperation *MyOperation;
+
+ ndbout << "Deleting records..."<< flush;
+
+ loop_count_ops = NUMBEROFRECORDS;
+
+ for (count=0 ; count < loop_count_ops ; count++) {
+
+ MyTransaction = pMyNdb->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler( pMyNdb->getNdbErrorString() );
+
+ MyOperation = MyTransaction->getNdbOperation(tableName[0]);
+ if (MyOperation == NULL)
+ error_handler(MyTransaction->getNdbErrorString());
+
+
+ check = MyOperation->deleteTuple();
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+
+ check = MyOperation->equal( attrName[0], (char*)&pkValue[count] );
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+
+
+ if( MyTransaction->execute( Commit ) == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+ else
+ ;// ndbout << ".";
+
+ pMyNdb->closeTransaction(MyTransaction);
+
+ }
+
+ ndbout << "OK" << endl;
+ return;
+
+};
+
+static void
+verify_deleted(Ndb* pMyNdb){
+ int check;
+ int loop_count_ops;
+ int count;
+ NdbConnection *MyTransaction;
+ NdbOperation *MyOperation;
+
+ ndbout << "Verifying deleted records..."<< flush;
+
+ loop_count_ops = NUMBEROFRECORDS;
+
+ for (count=0 ; count < loop_count_ops ; count++)
+ {
+ MyTransaction = pMyNdb->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler(pMyNdb->getNdbErrorString());
+
+ MyOperation = MyTransaction->getNdbOperation(tableName[0]);
+ if (MyOperation == NULL)
+ error_handler( MyTransaction->getNdbErrorString());
+
+ check = MyOperation->readTuple();
+ if( check == -1 )
+ error_handler( MyTransaction->getNdbErrorString());
+
+ check = MyOperation->equal( attrName[0],(char*)&pkValue[count] );
+ if( check == -1 )
+ error_handler( MyTransaction->getNdbErrorString());
+
+ // Exepect to receive an error
+ if( MyTransaction->execute( Commit ) != -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+ else
+ {
+ ;//ndbout << ".";
+ }
+
+ pMyNdb->closeTransaction(MyTransaction);
+
+ }
+
+ ndbout << "OK" << endl;
+ return;
+};
+
+static void
+read_and_verify_rows(Ndb* pMyNdb)
+{
+
+ int check;
+ int loop_count_ops;
+ int count;
+ int count_attributes;
+
+ NdbConnection *MyTransaction;
+ NdbOperation *MyOperation;
+ NdbRecAttr* tTmp;
+
+ int readValue[MAXATTR];
+
+ ndbout << "Verifying records..."<< flush;
+
+ loop_count_ops = NUMBEROFRECORDS;
+
+ for (count=0 ; count < loop_count_ops ; count++)
+ {
+ MyTransaction = pMyNdb->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler(pMyNdb->getNdbErrorString());
+
+ MyOperation = MyTransaction->getNdbOperation(tableName[0]);
+ if (MyOperation == NULL)
+ error_handler( MyTransaction->getNdbErrorString());
+
+ check = MyOperation->readTuple();
+ if( check == -1 )
+ error_handler( MyTransaction->getNdbErrorString());
+
+ check = MyOperation->equal( attrName[0],(char*)&pkValue[count] );
+ if( check == -1 )
+ error_handler( MyTransaction->getNdbErrorString());
+
+ for (count_attributes = 1; count_attributes < MAXATTR; count_attributes++)
+ {
+ tTmp = MyOperation->getValue( (char*)attrName[count_attributes], (char*)&readValue[count_attributes] );
+ if( tTmp == NULL )
+ error_handler( MyTransaction->getNdbErrorString());
+ }
+
+ if( MyTransaction->execute( Commit ) == -1 )
+ error_handler(MyTransaction->getNdbErrorString());
+ else
+ {
+ // Check value in db against value in mem
+
+ //ndbout << readValue[1] << " == " << attrValue[count] << endl;
+
+ if ( readValue[1]!=attrValue[count] )
+ error_handler("Verification error!");
+ else
+ if ( readValue[2]!=attrValue[count] )
+ error_handler("Verification error!");
+ else
+ if ( readValue[3]!=attrValue[count] )
+ error_handler("Verification error!");
+ else
+ {
+ ;//ndbout << ".";
+ }
+ }
+ pMyNdb->closeTransaction(MyTransaction);
+
+ }
+
+ ndbout << "OK" << endl;
+ return;
+
+
+
+};
+
+
+static void
+setAttrNames()
+{
+ int i;
+
+ for (i = 0; i < MAXATTR ; i++)
+ {
+ sprintf(&attrName[i][0], "Col%d", i);
+ }
+}
+
+static void
+setTableNames()
+{
+ int i;
+
+ sprintf(&tableName[0][0], "SBMCALL", 0);
+ sprintf(&tableName[1][0], "RPACCT", 0);
+ sprintf(&tableName[2][0], "IPACCT", 0);
+ sprintf(&tableName[3][0], "TODACCT", 0);
+
+}
+
+
+bool error_handler2(const char* error_string, int error_int) {
+ failed++ ;
+ ndbout << error_string << endl ;
+ if ( 4008==error_int || 721==error_int || 266==error_int ){
+ ndbout << endl << "Attempting to recover and continue now..." << endl ;
+ return true ; // return true to retry
+ }
+ return false ; // return false to abort
+}
diff --git a/storage/ndb/test/ndbapi/create_all_tabs.cpp b/storage/ndb/test/ndbapi/create_all_tabs.cpp
new file mode 100644
index 00000000000..f06078d67a2
--- /dev/null
+++ b/storage/ndb/test/ndbapi/create_all_tabs.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 <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NDBT.hpp>
+
+#include <getarg.h>
+
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ int _temp = false;
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "temp", 't', arg_flag, &_temp, "Temporary table", "temp" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "This program will create all standard tables in Ndb.\n"\
+ "The tables is selected from a fixed list of tables\n"\
+ "defined in NDBT_Tables class\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ // Connect to Ndb
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb MyNdb(&con, "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ return NDBT_Tables::createAllTables(&MyNdb, _temp);
+
+ }
+
+
diff --git a/storage/ndb/test/ndbapi/create_tab.cpp b/storage/ndb/test/ndbapi/create_tab.cpp
new file mode 100644
index 00000000000..b35c8655236
--- /dev/null
+++ b/storage/ndb/test/ndbapi/create_tab.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 <ndb_global.h>
+
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NDBT.hpp>
+
+#include <getarg.h>
+
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ int _temp = false;
+ int _help = 0;
+ int _all = 0;
+ int _print = 0;
+ const char* _connectstr = NULL;
+
+ struct getargs args[] = {
+ { "all", 'a', arg_flag, &_all, "Create/print all tables" },
+ { "print", 'p', arg_flag, &_print, "Print table(s) instead of creating it"},
+ { "temp", 't', arg_flag, &_temp, "Temporary table", "temp" },
+ { "connstr", 'c', arg_string, &_connectstr, "connect string",
+ "How to connect to NDB"},
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "tabname\n"\
+ "This program will create one table in Ndb.\n"\
+ "The tables may be selected from a fixed list of tables\n"\
+ "defined in NDBT_Tables class\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help){
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ if(argv[optind] == NULL && !_all){
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ int res = 0;
+ if(_print){
+ /**
+ * Print instead of creating
+ */
+ if(optind < argc){
+ for(int i = optind; i<argc; i++){
+ NDBT_Tables::print(argv[i]);
+ }
+ } else {
+ NDBT_Tables::printAll();
+ }
+ } else {
+ /**
+ * Creating
+ */
+
+ // Connect to Ndb
+ Ndb_cluster_connection con(_connectstr);
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb MyNdb(&con, "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ if(_all){
+ res = NDBT_Tables::createAllTables(&MyNdb, _temp);
+ } else {
+ int tmp;
+ for(int i = optind; i<argc; i++){
+ ndbout << "Trying to create " << argv[i] << endl;
+ if((tmp = NDBT_Tables::createTable(&MyNdb, argv[i], _temp)) != 0)
+ res = tmp;
+ }
+ }
+ }
+
+ if(res != 0)
+ return NDBT_ProgramExit(NDBT_FAILED);
+ else
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
+
diff --git a/storage/ndb/test/ndbapi/drop_all_tabs.cpp b/storage/ndb/test/ndbapi/drop_all_tabs.cpp
new file mode 100644
index 00000000000..f12d750916e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/drop_all_tabs.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 <ndb_global.h>
+
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NDBT.hpp>
+
+#include <getarg.h>
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ int _help = 0;
+ struct getargs args[] = {
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "This program will drop all Ndb standard tables from NDB\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ // Connect to Ndb
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Ndb MyNdb(&con, "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ return NDBT_Tables::dropAllTables(&MyNdb);
+
+ }
+
+
diff --git a/storage/ndb/test/ndbapi/flexAsynch.cpp b/storage/ndb/test/ndbapi/flexAsynch.cpp
new file mode 100644
index 00000000000..8a7dbec1561
--- /dev/null
+++ b/storage/ndb/test/ndbapi/flexAsynch.cpp
@@ -0,0 +1,995 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 "NdbApi.hpp"
+#include <NdbSchemaCon.hpp>
+#include <NdbMain.h>
+#include <md5_hash.hpp>
+
+#include <NdbThread.h>
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <NdbOut.hpp>
+#include <NdbTimer.hpp>
+#include <NDBT_Error.hpp>
+
+#include <NdbTest.hpp>
+
+#define MAX_PARTS 4
+#define MAX_SEEK 16
+#define MAXSTRLEN 16
+#define MAXATTR 64
+#define MAXTABLES 64
+#define MAXTHREADS 128
+#define MAXPAR 1024
+#define MAXATTRSIZE 1000
+#define PKSIZE 2
+
+enum StartType {
+ stIdle,
+ stInsert,
+ stRead,
+ stUpdate,
+ stDelete,
+ stStop
+} ;
+
+extern "C" { static void* threadLoop(void*); }
+static void setAttrNames(void);
+static void setTableNames(void);
+static int readArguments(int argc, const char** argv);
+static int createTables(Ndb*);
+static void defineOperation(NdbConnection* aTransObject, StartType aType,
+ Uint32 base, Uint32 aIndex);
+static void execute(StartType aType);
+static bool executeThread(StartType aType, Ndb* aNdbObject, unsigned int);
+static void executeCallback(int result, NdbConnection* NdbObject,
+ void* aObject);
+static bool error_handler(const NdbError & err);
+static Uint32 getKey(Uint32, Uint32) ;
+static void input_error();
+
+
+static int retry_opt = 3 ;
+static int failed = 0 ;
+
+ErrorData * flexAsynchErrorData;
+
+struct ThreadNdb
+{
+ int NoOfOps;
+ int ThreadNo;
+};
+
+static NdbThread* threadLife[MAXTHREADS];
+static int tNodeId;
+static int ThreadReady[MAXTHREADS];
+static StartType ThreadStart[MAXTHREADS];
+static char tableName[MAXTABLES][MAXSTRLEN+1];
+static char attrName[MAXATTR][MAXSTRLEN+1];
+
+// Program Parameters
+static bool tLocal = false;
+static int tLocalPart = 0;
+static int tSendForce = 0;
+static int tNoOfLoops = 1;
+static int tAttributeSize = 1;
+static unsigned int tNoOfThreads = 1;
+static unsigned int tNoOfParallelTrans = 32;
+static unsigned int tNoOfAttributes = 25;
+static unsigned int tNoOfTransactions = 500;
+static unsigned int tNoOfOpsPerTrans = 1;
+static unsigned int tLoadFactor = 80;
+static bool tempTable = false;
+static bool startTransGuess = true;
+
+//Program Flags
+static int theTestFlag = 0;
+static int theSimpleFlag = 0;
+static int theDirtyFlag = 0;
+static int theWriteFlag = 0;
+static int theStdTableNameFlag = 0;
+static int theTableCreateFlag = 0;
+
+#define START_REAL_TIME
+#define STOP_REAL_TIME
+#define START_TIMER { NdbTimer timer; timer.doStart();
+#define STOP_TIMER timer.doStop();
+#define PRINT_TIMER(text, trans, opertrans) timer.printTransactionStatistics(text, trans, opertrans); };
+
+static void
+resetThreads(){
+
+ for (int i = 0; i < tNoOfThreads ; i++) {
+ ThreadReady[i] = 0;
+ ThreadStart[i] = stIdle;
+ }//for
+}
+
+static void
+waitForThreads(void)
+{
+ int cont = 0;
+ do {
+ cont = 0;
+ NdbSleep_MilliSleep(20);
+ for (int i = 0; i < tNoOfThreads ; i++) {
+ if (ThreadReady[i] == 0) {
+ cont = 1;
+ }//if
+ }//for
+ } while (cont == 1);
+}
+
+static void
+tellThreads(StartType what)
+{
+ for (int i = 0; i < tNoOfThreads ; i++)
+ ThreadStart[i] = what;
+}
+
+static Ndb_cluster_connection *g_cluster_connection= 0;
+
+NDB_COMMAND(flexAsynch, "flexAsynch", "flexAsynch", "flexAsynch", 65535)
+{
+ ndb_init();
+ ThreadNdb* pThreadData;
+ int tLoops=0, i;
+ int returnValue = NDBT_OK;
+
+ flexAsynchErrorData = new ErrorData;
+ flexAsynchErrorData->resetErrorCounters();
+
+ if (readArguments(argc, argv) != 0){
+ input_error();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ pThreadData = new ThreadNdb[MAXTHREADS];
+
+ ndbout << endl << "FLEXASYNCH - Starting normal mode" << endl;
+ ndbout << "Perform benchmark of insert, update and delete transactions";
+ ndbout << endl;
+ ndbout << " " << tNoOfThreads << " number of concurrent threads " << endl;
+ ndbout << " " << tNoOfParallelTrans;
+ ndbout << " number of parallel operation per thread " << endl;
+ ndbout << " " << tNoOfTransactions << " transaction(s) per round " << endl;
+ ndbout << " " << tNoOfLoops << " iterations " << endl;
+ ndbout << " " << "Load Factor is " << tLoadFactor << "%" << endl;
+ ndbout << " " << tNoOfAttributes << " attributes per table " << endl;
+ ndbout << " " << tAttributeSize;
+ ndbout << " is the number of 32 bit words per attribute " << endl;
+ if (tempTable == true) {
+ ndbout << " Tables are without logging " << endl;
+ } else {
+ ndbout << " Tables are with logging " << endl;
+ }//if
+ if (startTransGuess == true) {
+ ndbout << " Transactions are executed with hint provided" << endl;
+ } else {
+ ndbout << " Transactions are executed with round robin scheme" << endl;
+ }//if
+ if (tSendForce == 0) {
+ ndbout << " No force send is used, adaptive algorithm used" << endl;
+ } else if (tSendForce == 1) {
+ ndbout << " Force send used" << endl;
+ } else {
+ ndbout << " No force send is used, adaptive algorithm disabled" << endl;
+ }//if
+
+ ndbout << endl;
+
+ NdbThread_SetConcurrencyLevel(2 + tNoOfThreads);
+
+ /* print Setting */
+ flexAsynchErrorData->printSettings(ndbout);
+
+ setAttrNames();
+ setTableNames();
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ g_cluster_connection= &con;
+
+ Ndb * pNdb = new Ndb(g_cluster_connection, "TEST_DB");
+ pNdb->init();
+ tNodeId = pNdb->getNodeId();
+
+ ndbout << " NdbAPI node with id = " << pNdb->getNodeId() << endl;
+ ndbout << endl;
+
+ ndbout << "Waiting for ndb to become ready..." <<endl;
+ if (pNdb->waitUntilReady(10000) != 0){
+ ndbout << "NDB is not ready" << endl;
+ ndbout << "Benchmark failed!" << endl;
+ returnValue = NDBT_FAILED;
+ }
+
+ if(returnValue == NDBT_OK){
+ if (createTables(pNdb) != 0){
+ returnValue = NDBT_FAILED;
+ }
+ }
+
+ if(returnValue == NDBT_OK){
+ /****************************************************************
+ * Create NDB objects. *
+ ****************************************************************/
+ resetThreads();
+ for (i = 0; i < tNoOfThreads ; i++) {
+ pThreadData[i].ThreadNo = i
+;
+ threadLife[i] = NdbThread_Create(threadLoop,
+ (void**)&pThreadData[i],
+ 32768,
+ "flexAsynchThread",
+ NDB_THREAD_PRIO_LOW);
+ }//for
+ ndbout << endl << "All NDB objects and table created" << endl << endl;
+ int noOfTransacts = tNoOfParallelTrans*tNoOfTransactions*tNoOfThreads;
+ /****************************************************************
+ * Execute program. *
+ ****************************************************************/
+
+ for(;;) {
+
+ int loopCount = tLoops + 1 ;
+ ndbout << endl << "Loop # " << loopCount << endl << endl ;
+
+ /****************************************************************
+ * Perform inserts. *
+ ****************************************************************/
+
+ failed = 0 ;
+
+ START_TIMER;
+ execute(stInsert);
+ STOP_TIMER;
+ PRINT_TIMER("insert", noOfTransacts, tNoOfOpsPerTrans);
+
+ if (0 < failed) {
+ i = retry_opt ;
+ int ci = 1 ;
+ while (0 < failed && 0 < i){
+ ndbout << failed << " of the transactions returned errors!"
+ << endl << endl;
+ ndbout << "Attempting to redo the failed transactions now..."
+ << endl ;
+ ndbout << "Redo attempt " << ci <<" out of " << retry_opt
+ << endl << endl;
+ failed = 0 ;
+ START_TIMER;
+ execute(stInsert);
+ STOP_TIMER;
+ PRINT_TIMER("insert", noOfTransacts, tNoOfOpsPerTrans);
+ i-- ;
+ ci++;
+ }
+ if(0 == failed ){
+ ndbout << endl <<"Redo attempt succeeded" << endl << endl;
+ }else{
+ ndbout << endl <<"Redo attempt failed, moving on now..." << endl
+ << endl;
+ }//if
+ }//if
+
+ /****************************************************************
+ * Perform read. *
+ ****************************************************************/
+
+ failed = 0 ;
+
+ START_TIMER;
+ execute(stRead);
+ STOP_TIMER;
+ PRINT_TIMER("read", noOfTransacts, tNoOfOpsPerTrans);
+
+ if (0 < failed) {
+ i = retry_opt ;
+ int cr = 1;
+ while (0 < failed && 0 < i){
+ ndbout << failed << " of the transactions returned errors!"<<endl ;
+ ndbout << endl;
+ ndbout <<"Attempting to redo the failed transactions now..." << endl;
+ ndbout << endl;
+ ndbout <<"Redo attempt " << cr <<" out of ";
+ ndbout << retry_opt << endl << endl;
+ failed = 0 ;
+ START_TIMER;
+ execute(stRead);
+ STOP_TIMER;
+ PRINT_TIMER("read", noOfTransacts, tNoOfOpsPerTrans);
+ i-- ;
+ cr++ ;
+ }//while
+ if(0 == failed ) {
+ ndbout << endl <<"Redo attempt succeeded" << endl << endl ;
+ }else{
+ ndbout << endl <<"Redo attempt failed, moving on now..." << endl << endl ;
+ }//if
+ }//if
+
+
+ /****************************************************************
+ * Perform update. *
+ ****************************************************************/
+
+ failed = 0 ;
+
+ START_TIMER;
+ execute(stUpdate);
+ STOP_TIMER;
+ PRINT_TIMER("update", noOfTransacts, tNoOfOpsPerTrans) ;
+
+ if (0 < failed) {
+ i = retry_opt ;
+ int cu = 1 ;
+ while (0 < failed && 0 < i){
+ ndbout << failed << " of the transactions returned errors!"<<endl ;
+ ndbout << endl;
+ ndbout <<"Attempting to redo the failed transactions now..." << endl;
+ ndbout << endl <<"Redo attempt " << cu <<" out of ";
+ ndbout << retry_opt << endl << endl;
+ failed = 0 ;
+ START_TIMER;
+ execute(stUpdate);
+ STOP_TIMER;
+ PRINT_TIMER("update", noOfTransacts, tNoOfOpsPerTrans);
+ i-- ;
+ cu++ ;
+ }//while
+ if(0 == failed ){
+ ndbout << endl <<"Redo attempt succeeded" << endl << endl;
+ } else {
+ ndbout << endl;
+ ndbout <<"Redo attempt failed, moving on now..." << endl << endl;
+ }//if
+ }//if
+
+ /****************************************************************
+ * Perform read. *
+ ****************************************************************/
+
+ failed = 0 ;
+
+ START_TIMER;
+ execute(stRead);
+ STOP_TIMER;
+ PRINT_TIMER("read", noOfTransacts, tNoOfOpsPerTrans);
+
+ if (0 < failed) {
+ i = retry_opt ;
+ int cr2 = 1 ;
+ while (0 < failed && 0 < i){
+ ndbout << failed << " of the transactions returned errors!"<<endl ;
+ ndbout << endl;
+ ndbout <<"Attempting to redo the failed transactions now..." << endl;
+ ndbout << endl <<"Redo attempt " << cr2 <<" out of ";
+ ndbout << retry_opt << endl << endl;
+ failed = 0 ;
+ START_TIMER;
+ execute(stRead);
+ STOP_TIMER;
+ PRINT_TIMER("read", noOfTransacts, tNoOfOpsPerTrans);
+ i-- ;
+ cr2++ ;
+ }//while
+ if(0 == failed ){
+ ndbout << endl <<"Redo attempt succeeded" << endl << endl;
+ }else{
+ ndbout << endl;
+ ndbout << "Redo attempt failed, moving on now..." << endl << endl;
+ }//if
+ }//if
+
+
+ /****************************************************************
+ * Perform delete. *
+ ****************************************************************/
+
+ failed = 0 ;
+
+ START_TIMER;
+ execute(stDelete);
+ STOP_TIMER;
+ PRINT_TIMER("delete", noOfTransacts, tNoOfOpsPerTrans);
+
+ if (0 < failed) {
+ i = retry_opt ;
+ int cd = 1 ;
+ while (0 < failed && 0 < i){
+ ndbout << failed << " of the transactions returned errors!"<< endl ;
+ ndbout << endl;
+ ndbout <<"Attempting to redo the failed transactions now:" << endl ;
+ ndbout << endl <<"Redo attempt " << cd <<" out of ";
+ ndbout << retry_opt << endl << endl;
+ failed = 0 ;
+ START_TIMER;
+ execute(stDelete);
+ STOP_TIMER;
+ PRINT_TIMER("read", noOfTransacts, tNoOfOpsPerTrans);
+ i-- ;
+ cd++ ;
+ }//while
+ if(0 == failed ){
+ ndbout << endl <<"Redo attempt succeeded" << endl << endl ;
+ }else{
+ ndbout << endl;
+ ndbout << "Redo attempt failed, moving on now..." << endl << endl;
+ }//if
+ }//if
+
+ tLoops++;
+ ndbout << "--------------------------------------------------" << endl;
+
+ if(tNoOfLoops != 0){
+ if(tNoOfLoops <= tLoops)
+ break ;
+ }
+ }//for
+
+ execute(stStop);
+ void * tmp;
+ for(i = 0; i<tNoOfThreads; i++){
+ NdbThread_WaitFor(threadLife[i], &tmp);
+ NdbThread_Destroy(&threadLife[i]);
+ }
+ }
+ delete [] pThreadData;
+ delete pNdb;
+
+ //printing errorCounters
+ flexAsynchErrorData->printErrorCounters(ndbout);
+
+ return NDBT_ProgramExit(returnValue);
+}//main()
+
+
+static void execute(StartType aType)
+{
+ resetThreads();
+ tellThreads(aType);
+ waitForThreads();
+}//execute()
+
+static void*
+threadLoop(void* ThreadData)
+{
+ Ndb* localNdb;
+ StartType tType;
+ ThreadNdb* tabThread = (ThreadNdb*)ThreadData;
+ int threadNo = tabThread->ThreadNo;
+ localNdb = new Ndb(g_cluster_connection, "TEST_DB");
+ localNdb->init(1024);
+ localNdb->waitUntilReady(10000);
+ unsigned int threadBase = (threadNo << 16) + tNodeId ;
+
+ for (;;){
+ while (ThreadStart[threadNo] == stIdle) {
+ NdbSleep_MilliSleep(10);
+ }//while
+
+ // Check if signal to exit is received
+ if (ThreadStart[threadNo] == stStop) {
+ break;
+ }//if
+
+ tType = ThreadStart[threadNo];
+ ThreadStart[threadNo] = stIdle;
+ if(!executeThread(tType, localNdb, threadBase)){
+ break;
+ }
+ ThreadReady[threadNo] = 1;
+ }//for
+
+ delete localNdb;
+ ThreadReady[threadNo] = 1;
+
+ return NULL;
+}//threadLoop()
+
+static
+bool
+executeThread(StartType aType, Ndb* aNdbObject, unsigned int threadBase) {
+ int i, j, k;
+ NdbConnection* tConArray[1024];
+ unsigned int tBase;
+ unsigned int tBase2;
+
+ for (i = 0; i < tNoOfTransactions; i++) {
+ if (tLocal == false) {
+ tBase = i * tNoOfParallelTrans * tNoOfOpsPerTrans;
+ } else {
+ tBase = i * tNoOfParallelTrans * MAX_SEEK;
+ }//if
+ START_REAL_TIME;
+ for (j = 0; j < tNoOfParallelTrans; j++) {
+ if (tLocal == false) {
+ tBase2 = tBase + (j * tNoOfOpsPerTrans);
+ } else {
+ tBase2 = tBase + (j * MAX_SEEK);
+ tBase2 = getKey(threadBase, tBase2);
+ }//if
+ if (startTransGuess == true) {
+ Uint64 Tkey64;
+ Uint32* Tkey32 = (Uint32*)&Tkey64;
+ Tkey32[0] = threadBase;
+ Tkey32[1] = tBase2;
+ tConArray[j] = aNdbObject->startTransaction((Uint32)0, //Priority
+ (const char*)&Tkey64, //Main PKey
+ (Uint32)4); //Key Length
+ } else {
+ tConArray[j] = aNdbObject->startTransaction();
+ }//if
+ if (tConArray[j] == NULL &&
+ !error_handler(aNdbObject->getNdbError()) ){
+ ndbout << endl << "Unable to recover! Quiting now" << endl ;
+ return false;
+ }//if
+
+ for (k = 0; k < tNoOfOpsPerTrans; k++) {
+ //-------------------------------------------------------
+ // Define the operation, but do not execute it yet.
+ //-------------------------------------------------------
+ defineOperation(tConArray[j], aType, threadBase, (tBase2 + k));
+ }//for
+
+ tConArray[j]->executeAsynchPrepare(Commit, &executeCallback, NULL);
+ }//for
+ STOP_REAL_TIME;
+ //-------------------------------------------------------
+ // Now we have defined a set of operations, it is now time
+ // to execute all of them.
+ //-------------------------------------------------------
+ int Tcomp = aNdbObject->sendPollNdb(3000, 0, 0);
+ while (Tcomp < tNoOfParallelTrans) {
+ int TlocalComp = aNdbObject->pollNdb(3000, 0);
+ Tcomp += TlocalComp;
+ }//while
+ for (j = 0 ; j < tNoOfParallelTrans ; j++) {
+ aNdbObject->closeTransaction(tConArray[j]);
+ }//for
+ }//for
+ return true;
+}//executeThread()
+
+static
+Uint32
+getKey(Uint32 aBase, Uint32 anIndex) {
+ Uint32 Tfound = anIndex;
+ Uint64 Tkey64;
+ Uint32* Tkey32 = (Uint32*)&Tkey64;
+ Tkey32[0] = aBase;
+ Uint32 hash;
+ for (Uint32 i = anIndex; i < (anIndex + MAX_SEEK); i++) {
+ Tkey32[1] = (Uint32)i;
+ hash = md5_hash((Uint64*)&Tkey64, (Uint32)2);
+ hash = (hash >> 6) & (MAX_PARTS - 1);
+ if (hash == tLocalPart) {
+ Tfound = i;
+ break;
+ }//if
+ }//for
+ return Tfound;
+}//getKey()
+
+static void
+executeCallback(int result, NdbConnection* NdbObject, void* aObject)
+{
+ if (result == -1) {
+
+ // Add complete error handling here
+
+ int retCode = flexAsynchErrorData->handleErrorCommon(NdbObject->getNdbError());
+ if (retCode == 1) {
+ if (NdbObject->getNdbError().code != 626 && NdbObject->getNdbError().code != 630){
+ ndbout_c("execute: %s", NdbObject->getNdbError().message);
+ ndbout_c("Error code = %d", NdbObject->getNdbError().code);}
+ } else if (retCode == 2) {
+ ndbout << "4115 should not happen in flexAsynch" << endl;
+ } else if (retCode == 3) {
+ /* What can we do here? */
+ ndbout_c("execute: %s", NdbObject->getNdbError().message);
+ }//if(retCode == 3)
+
+ // ndbout << "Error occured in poll:" << endl;
+ // ndbout << NdbObject->getNdbError() << endl;
+ failed++ ;
+ return;
+ }//if
+ return;
+}//executeCallback()
+
+
+
+static void
+defineOperation(NdbConnection* localNdbConnection, StartType aType,
+ Uint32 threadBase, Uint32 aIndex)
+{
+ NdbOperation* localNdbOperation;
+ unsigned int loopCountAttributes = tNoOfAttributes;
+ unsigned int countAttributes;
+ Uint32 attrValue[MAXATTRSIZE];
+
+ //-------------------------------------------------------
+ // Set-up the attribute values for this operation.
+ //-------------------------------------------------------
+ attrValue[0] = threadBase;
+ attrValue[1] = aIndex;
+ for (int k = 2; k < loopCountAttributes; k++) {
+ attrValue[k] = aIndex;
+ }//for
+ localNdbOperation = localNdbConnection->getNdbOperation(tableName[0]);
+ if (localNdbOperation == NULL) {
+ error_handler(localNdbConnection->getNdbError());
+ }//if
+ switch (aType) {
+ case stInsert: { // Insert case
+ if (theWriteFlag == 1 && theDirtyFlag == 1) {
+ localNdbOperation->dirtyWrite();
+ } else if (theWriteFlag == 1) {
+ localNdbOperation->writeTuple();
+ } else {
+ localNdbOperation->insertTuple();
+ }//if
+ break;
+ }//case
+ case stRead: { // Read Case
+ if (theSimpleFlag == 1) {
+ localNdbOperation->simpleRead();
+ } else if (theDirtyFlag == 1) {
+ localNdbOperation->dirtyRead();
+ } else {
+ localNdbOperation->readTuple();
+ }//if
+ break;
+ }//case
+ case stUpdate: { // Update Case
+ if (theWriteFlag == 1 && theDirtyFlag == 1) {
+ localNdbOperation->dirtyWrite();
+ } else if (theWriteFlag == 1) {
+ localNdbOperation->writeTuple();
+ } else if (theDirtyFlag == 1) {
+ localNdbOperation->dirtyUpdate();
+ } else {
+ localNdbOperation->updateTuple();
+ }//if
+ break;
+ }//case
+ case stDelete: { // Delete Case
+ localNdbOperation->deleteTuple();
+ break;
+ }//case
+ default: {
+ error_handler(localNdbOperation->getNdbError());
+ }//default
+ }//switch
+ localNdbOperation->equal((Uint32)0,(char*)&attrValue[0]);
+ switch (aType) {
+ case stInsert: // Insert case
+ case stUpdate: // Update Case
+ {
+ for (countAttributes = 1;
+ countAttributes < loopCountAttributes; countAttributes++) {
+ localNdbOperation->setValue(countAttributes,
+ (char*)&attrValue[0]);
+ }//for
+ break;
+ }//case
+ case stRead: { // Read Case
+ for (countAttributes = 1;
+ countAttributes < loopCountAttributes; countAttributes++) {
+ localNdbOperation->getValue(countAttributes,
+ (char*)&attrValue[0]);
+ }//for
+ break;
+ }//case
+ case stDelete: { // Delete Case
+ break;
+ }//case
+ default: {
+ //goto error_handler; < epaulsa
+ error_handler(localNdbOperation->getNdbError());
+ }//default
+ }//switch
+ return;
+}//defineOperation()
+
+static void setAttrNames()
+{
+ int i;
+
+ for (i = 0; i < MAXATTR ; i++){
+ BaseString::snprintf(attrName[i], MAXSTRLEN, "COL%d", i);
+ }
+}
+
+
+static void setTableNames()
+{
+ // Note! Uses only uppercase letters in table name's
+ // so that we can look at the tables wits SQL
+ int i;
+ for (i = 0; i < MAXTABLES ; i++){
+ if (theStdTableNameFlag==0){
+ BaseString::snprintf(tableName[i], MAXSTRLEN, "TAB%d_%d", i,
+ (int)(NdbTick_CurrentMillisecond()/1000));
+ } else {
+ BaseString::snprintf(tableName[i], MAXSTRLEN, "TAB%d", i);
+ }
+ }
+}
+
+static
+int
+createTables(Ndb* pMyNdb){
+
+ NdbSchemaCon *MySchemaTransaction;
+ NdbSchemaOp *MySchemaOp;
+ int check;
+
+ if (theTableCreateFlag == 0) {
+ for(int i=0; i < 1 ;i++) {
+ ndbout << "Creating " << tableName[i] << "..." << endl;
+ MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pMyNdb);
+
+ if(MySchemaTransaction == NULL &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if(MySchemaOp == NULL &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+
+
+ check = MySchemaOp->createTable( tableName[i]
+ ,8 // Table Size
+ ,TupleKey // Key Type
+ ,40 // Nr of Pages
+ ,All
+ ,6
+ ,(tLoadFactor - 5)
+ ,(tLoadFactor)
+ ,1
+ ,!tempTable
+ );
+
+ if (check == -1 &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+
+ check = MySchemaOp->createAttribute( (char*)attrName[0],
+ TupleKey,
+ 32,
+ PKSIZE,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if (check == -1 &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+ for (int j = 1; j < tNoOfAttributes ; j++){
+ check = MySchemaOp->createAttribute( (char*)attrName[j],
+ NoKey,
+ 32,
+ tAttributeSize,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if (check == -1 &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+ }
+
+ if (MySchemaTransaction->execute() == -1 &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ }
+ }
+
+ return 0;
+}
+
+static
+bool error_handler(const NdbError & err){
+ ndbout << err << endl ;
+ switch(err.classification){
+ case NdbError::TemporaryResourceError:
+ case NdbError::OverloadError:
+ case NdbError::SchemaError:
+ ndbout << endl << "Attempting to recover and continue now..." << endl ;
+ return true;
+ }
+ return false ; // return false to abort
+}
+static
+bool error_handler(const char* error_string, int error_int) {
+ ndbout << error_string << endl ;
+ if ((4008 == error_int) ||
+ (721 == error_int) ||
+ (266 == error_int)){
+ ndbout << endl << "Attempting to recover and continue now..." << endl ;
+ return true ; // return true to retry
+ }
+ return false ; // return false to abort
+}
+
+static
+int
+readArguments(int argc, const char** argv){
+
+ int i = 1;
+ while (argc > 1){
+ if (strcmp(argv[i], "-t") == 0){
+ tNoOfThreads = atoi(argv[i+1]);
+ if ((tNoOfThreads < 1) || (tNoOfThreads > MAXTHREADS)){
+ ndbout_c("Invalid no of threads");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-p") == 0){
+ tNoOfParallelTrans = atoi(argv[i+1]);
+ if ((tNoOfParallelTrans < 1) || (tNoOfParallelTrans > MAXPAR)){
+ ndbout_c("Invalid no of parallell transactions");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-load_factor") == 0){
+ tLoadFactor = atoi(argv[i+1]);
+ if ((tLoadFactor < 40) || (tLoadFactor > 99)){
+ ndbout_c("Invalid load factor");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-c") == 0) {
+ tNoOfOpsPerTrans = atoi(argv[i+1]);
+ if (tNoOfOpsPerTrans < 1){
+ ndbout_c("Invalid no of operations per transaction");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-o") == 0) {
+ tNoOfTransactions = atoi(argv[i+1]);
+ if (tNoOfTransactions < 1){
+ ndbout_c("Invalid no of transactions");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-a") == 0){
+ tNoOfAttributes = atoi(argv[i+1]);
+ if ((tNoOfAttributes < 2) || (tNoOfAttributes > MAXATTR)){
+ ndbout_c("Invalid no of attributes");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-n") == 0){
+ theStdTableNameFlag = 1;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-l") == 0){
+ tNoOfLoops = atoi(argv[i+1]);
+ if ((tNoOfLoops < 0) || (tNoOfLoops > 100000)){
+ ndbout_c("Invalid no of loops");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-s") == 0){
+ tAttributeSize = atoi(argv[i+1]);
+ if ((tAttributeSize < 1) || (tAttributeSize > MAXATTRSIZE)){
+ ndbout_c("Invalid attributes size");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-local") == 0){
+ tLocalPart = atoi(argv[i+1]);
+ tLocal = true;
+ startTransGuess = true;
+ if ((tLocalPart < 0) || (tLocalPart > MAX_PARTS)){
+ ndbout_c("Invalid local part");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-simple") == 0){
+ theSimpleFlag = 1;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-adaptive") == 0){
+ tSendForce = 0;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-force") == 0){
+ tSendForce = 1;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-non_adaptive") == 0){
+ tSendForce = 2;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-write") == 0){
+ theWriteFlag = 1;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-dirty") == 0){
+ theDirtyFlag = 1;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-test") == 0){
+ theTestFlag = 1;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-no_table_create") == 0){
+ theTableCreateFlag = 1;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-temp") == 0){
+ tempTable = true;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-no_hint") == 0){
+ startTransGuess = false;
+ argc++;
+ i--;
+ } else {
+ return -1;
+ }
+
+ argc -= 2;
+ i = i + 2;
+ }//while
+ if (tLocal == true) {
+ if (tNoOfOpsPerTrans != 1) {
+ ndbout_c("Not valid to have more than one op per trans with local");
+ }//if
+ if (startTransGuess == false) {
+ ndbout_c("Not valid to use no_hint with local");
+ }//if
+ }//if
+ return 0;
+}
+
+static
+void
+input_error(){
+
+ ndbout_c("FLEXASYNCH");
+ ndbout_c(" Perform benchmark of insert, update and delete transactions");
+ ndbout_c("");
+ ndbout_c("Arguments:");
+ ndbout_c(" -t Number of threads to start, default 1");
+ ndbout_c(" -p Number of parallel transactions per thread, default 32");
+ ndbout_c(" -o Number of transactions per loop, default 500");
+ ndbout_c(" -l Number of loops to run, default 1, 0=infinite");
+ ndbout_c(" -load_factor Number Load factor in index in percent (40 -> 99)");
+ ndbout_c(" -a Number of attributes, default 25");
+ ndbout_c(" -c Number of operations per transaction");
+ ndbout_c(" -s Size of each attribute, default 1 ");
+ ndbout_c(" (PK is always of size 1, independent of this value)");
+ ndbout_c(" -simple Use simple read to read from database");
+ ndbout_c(" -dirty Use dirty read to read from database");
+ ndbout_c(" -write Use writeTuple in insert and update");
+ ndbout_c(" -n Use standard table names");
+ ndbout_c(" -no_table_create Don't create tables in db");
+ ndbout_c(" -temp Create table(s) without logging");
+ ndbout_c(" -no_hint Don't give hint on where to execute transaction coordinator");
+ ndbout_c(" -adaptive Use adaptive send algorithm (default)");
+ ndbout_c(" -force Force send when communicating");
+ ndbout_c(" -non_adaptive Send at a 10 millisecond interval");
+ ndbout_c(" -local Number of part, only use keys in one part out of 16");
+}
+
+
+
diff --git a/storage/ndb/test/ndbapi/flexBench.cpp b/storage/ndb/test/ndbapi/flexBench.cpp
new file mode 100644
index 00000000000..abddecfdc40
--- /dev/null
+++ b/storage/ndb/test/ndbapi/flexBench.cpp
@@ -0,0 +1,1166 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* ***************************************************
+FLEXBENCH
+Perform benchmark of insert, update and delete transactions
+
+Arguments:
+ -t Number of threads to start, default 1
+ -o Number of operations per loop, default 500
+ -l Number of loops to run, default 1, 0=infinite
+ -a Number of attributes, default 25
+ -c Number of tables, default 1
+ -s Size of each attribute, default 1 (Primary Key is always of size 1,
+ independent of this value)
+ -lkn Number of long primary keys, default 1
+ -lks Size of each long primary key, default 1
+ -simple Use simple read to read from database
+ -dirty Use dirty read to read from database
+ -write Use writeTuple in insert and update
+ -stdtables Use standard table names
+ -no_table_create Don't create tables in db
+ -sleep Sleep a number of seconds before running the test, this
+ can be used so that another flexBench have time to create tables
+ -temp Use tables without logging
+ -verify Verify inserts, updates and deletes
+#ifdef CEBIT_STAT
+ -statserv host:port statistics server to report to
+ -statfreq ops report every ops operations (default 100)
+#endif
+ Returns:
+ 0 - Test passed
+ 1 - Test failed
+ 2 - Invalid arguments
+
+* *************************************************** */
+
+#include <ndb_global.h>
+#include "NdbApi.hpp"
+
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <NdbTimer.hpp>
+#include <NdbThread.h>
+
+#include <NdbTest.hpp>
+
+#define MAXSTRLEN 16
+#define MAXATTR 64
+#define MAXTABLES 128
+#define MAXATTRSIZE 1000
+#define MAXNOLONGKEY 16 // Max number of long keys.
+#define MAXLONGKEYTOTALSIZE 1023 // words = 4092 bytes
+
+extern "C" { static void* flexBenchThread(void*); }
+static int readArguments(int argc, const char** argv);
+static int createTables(Ndb*);
+static void sleepBeforeStartingTest(int seconds);
+static void input_error();
+
+enum StartType {
+ stIdle,
+ stInsert,
+ stVerify,
+ stRead,
+ stUpdate,
+ stDelete,
+ stTryDelete,
+ stVerifyDelete,
+ stStop
+};
+
+struct ThreadData
+{
+ int threadNo;
+ NdbThread* threadLife;
+ int threadReady;
+ StartType threadStart;
+ int threadResult;
+};
+
+static int tNodeId = 0 ;
+static char tableName[MAXTABLES][MAXSTRLEN+1];
+static char attrName[MAXATTR][MAXSTRLEN+1];
+static char** longKeyAttrName;
+
+// Program Parameters
+static int tNoOfLoops = 1;
+static int tAttributeSize = 1;
+static unsigned int tNoOfThreads = 1;
+static unsigned int tNoOfTables = 1;
+static unsigned int tNoOfAttributes = 25;
+static unsigned int tNoOfOperations = 500;
+static unsigned int tSleepTime = 0;
+static unsigned int tNoOfLongPK = 1;
+static unsigned int tSizeOfLongPK = 1;
+
+//Program Flags
+static int theSimpleFlag = 0;
+static int theDirtyFlag = 0;
+static int theWriteFlag = 0;
+static int theStdTableNameFlag = 0;
+static int theTableCreateFlag = 0;
+static bool theTempTable = false;
+static bool VerifyFlag = true;
+static bool useLongKeys = false;
+
+
+static ErrorData theErrorData; // Part of flexBench-program
+
+#define START_TIMER { NdbTimer timer; timer.doStart();
+#define STOP_TIMER timer.doStop();
+#define PRINT_TIMER(text, trans, opertrans) timer.printTransactionStatistics(text, trans, opertrans); };
+
+#include <NdbTCP.h>
+
+#ifdef CEBIT_STAT
+#include <NdbMutex.h>
+static bool statEnable = false;
+static char statHost[100];
+static int statFreq = 100;
+static int statPort = 0;
+static int statSock = -1;
+static enum { statError = -1, statClosed, statOpen } statState;
+static NdbMutex statMutex = NDB_MUTEX_INITIALIZER;
+#endif
+
+//-------------------------------------------------------------------
+// Statistical Reporting routines
+//-------------------------------------------------------------------
+#ifdef CEBIT_STAT
+// Experimental client-side statistic for CeBIT
+
+static void
+statReport(enum StartType st, int ops)
+{
+ if (!statEnable)
+ return;
+ if (NdbMutex_Lock(&statMutex) < 0) {
+ if (statState != statError) {
+ ndbout_c("stat: lock mutex failed: %s", strerror(errno));
+ statState = statError;
+ }
+ return;
+ }
+ static int nodeid;
+ // open connection
+ if (statState != statOpen) {
+ char *p = getenv("NDB_NODEID"); // ndbnet sets NDB_NODEID
+ nodeid = p == 0 ? 0 : atoi(p);
+ if ((statSock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+ if (statState != statError) {
+ ndbout_c("stat: create socket failed: %s", strerror(errno));
+ statState = statError;
+ }
+ (void)NdbMutex_Unlock(&statMutex);
+ return;
+ }
+ struct sockaddr_in saddr;
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(statPort);
+ if (Ndb_getInAddr(&saddr.sin_addr, statHost) < 0) {
+ if (statState != statError) {
+ ndbout_c("stat: host %s not found", statHost);
+ statState = statError;
+ }
+ (void)close(statSock);
+ (void)NdbMutex_Unlock(&statMutex);
+ return;
+ }
+ if (connect(statSock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
+ if (statState != statError) {
+ ndbout_c("stat: connect failed: %s", strerror(errno));
+ statState = statError;
+ }
+ (void)close(statSock);
+ (void)NdbMutex_Unlock(&statMutex);
+ return;
+ }
+ statState = statOpen;
+ ndbout_c("stat: connection to %s:%d opened", statHost, (int)statPort);
+ }
+ const char *text;
+ switch (st) {
+ case stInsert:
+ text = "insert";
+ break;
+ case stVerify:
+ text = "verify";
+ break;
+ case stRead:
+ text = "read";
+ break;
+ case stUpdate:
+ text = "update";
+ break;
+ case stDelete:
+ text = "delete";
+ break;
+ case stVerifyDelete:
+ text = "verifydelete";
+ break;
+ default:
+ text = "unknown";
+ break;
+ }
+ char buf[100];
+ sprintf(buf, "%d %s %d\n", nodeid, text, ops);
+ int len = strlen(buf);
+ // assume SIGPIPE already ignored
+ if (write(statSock, buf, len) != len) {
+ if (statState != statError) {
+ ndbout_c("stat: write failed: %s", strerror(errno));
+ statState = statError;
+ }
+ (void)close(statSock);
+ (void)NdbMutex_Unlock(&statMutex);
+ return;
+ }
+ (void)NdbMutex_Unlock(&statMutex);
+}
+#endif // CEBIT_STAT
+
+static void
+resetThreads(ThreadData* pt){
+ for (unsigned int i = 0; i < tNoOfThreads; i++){
+ pt[i].threadReady = 0;
+ pt[i].threadResult = 0;
+ pt[i].threadStart = stIdle;
+ }
+}
+
+static int
+checkThreadResults(ThreadData* pt){
+ for (unsigned int i = 0; i < tNoOfThreads; i++){
+ if(pt[i].threadResult != 0){
+ ndbout_c("Thread%d reported fatal error %d", i, pt[i].threadResult);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static
+void
+waitForThreads(ThreadData* pt)
+{
+ int cont = 1;
+ while (cont){
+ NdbSleep_MilliSleep(100);
+ cont = 0;
+ for (unsigned int i = 0; i < tNoOfThreads; i++){
+ if (pt[i].threadReady == 0)
+ cont = 1;
+ }
+ }
+}
+
+static void
+tellThreads(ThreadData* pt, StartType what)
+{
+ for (unsigned int i = 0; i < tNoOfThreads; i++)
+ pt[i].threadStart = what;
+}
+
+static Ndb_cluster_connection *g_cluster_connection= 0;
+
+NDB_COMMAND(flexBench, "flexBench", "flexBench", "flexbench", 65535)
+{
+ ndb_init();
+ ThreadData* pThreadsData;
+ int tLoops = 0, i;
+ int returnValue = NDBT_OK;
+
+ if (readArguments(argc, argv) != 0){
+ input_error();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ if(useLongKeys){
+ longKeyAttrName = (char **) malloc(sizeof(char*) * tNoOfLongPK);
+ for (Uint32 i = 0; i < tNoOfLongPK; i++) {
+ longKeyAttrName[i] = (char *) malloc(strlen("KEYATTR ") + 1);
+ memset(longKeyAttrName[i], 0, strlen("KEYATTR ") + 1);
+ sprintf(longKeyAttrName[i], "KEYATTR%i", i);
+ }
+ }
+
+ pThreadsData = new ThreadData[tNoOfThreads];
+
+ ndbout << endl << "FLEXBENCH - Starting normal mode" << endl;
+ ndbout << "Perform benchmark of insert, update and delete transactions"<< endl;
+ ndbout << " " << tNoOfThreads << " thread(s) " << endl;
+ ndbout << " " << tNoOfLoops << " iterations " << endl;
+ ndbout << " " << tNoOfTables << " table(s) and " << 1 << " operation(s) per transaction " <<endl;
+ ndbout << " " << tNoOfAttributes << " attributes per table " << endl;
+ ndbout << " " << tNoOfOperations << " transaction(s) per thread and round " << endl;
+ ndbout << " " << tAttributeSize << " is the number of 32 bit words per attribute "<< endl;
+ ndbout << " " << "Table(s) without logging: " << (Uint32)theTempTable << endl;
+
+ if(useLongKeys)
+ ndbout << " " << "Using long keys with " << tNoOfLongPK << " keys a' " <<
+ tSizeOfLongPK * 4 << " bytes each." << endl;
+
+ ndbout << " " << "Verification is " ;
+ if(VerifyFlag) {
+ ndbout << "enabled" << endl ;
+ }else{
+ ndbout << "disabled" << endl ;
+ }
+ theErrorData.printSettings(ndbout);
+
+ NdbThread_SetConcurrencyLevel(tNoOfThreads + 2);
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ g_cluster_connection= &con;
+
+ Ndb* pNdb;
+ pNdb = new Ndb(&con, "TEST_DB" );
+ pNdb->init();
+
+ tNodeId = pNdb->getNodeId();
+ ndbout << " NdbAPI node with id = " << tNodeId << endl;
+ ndbout << endl;
+
+ ndbout << "Waiting for ndb to become ready..." <<endl;
+ if (pNdb->waitUntilReady(2000) != 0){
+ ndbout << "NDB is not ready" << endl;
+ ndbout << "Benchmark failed!" << endl;
+ returnValue = NDBT_FAILED;
+ }
+
+ if(returnValue == NDBT_OK){
+ if (createTables(pNdb) != 0){
+ returnValue = NDBT_FAILED;
+ }
+ }
+
+ if(returnValue == NDBT_OK){
+
+ sleepBeforeStartingTest(tSleepTime);
+
+ /****************************************************************
+ * Create threads. *
+ ****************************************************************/
+ resetThreads(pThreadsData);
+
+ for (i = 0; i < tNoOfThreads; i++){
+ pThreadsData[i].threadNo = i;
+ pThreadsData[i].threadLife = NdbThread_Create(flexBenchThread,
+ (void**)&pThreadsData[i],
+ 32768,
+ "flexBenchThread",
+ NDB_THREAD_PRIO_LOW);
+ }
+
+ waitForThreads(pThreadsData);
+
+ ndbout << endl << "All threads started" << endl << endl;
+
+ /****************************************************************
+ * Execute program. *
+ ****************************************************************/
+
+ for(;;){
+
+ int loopCount = tLoops + 1;
+ ndbout << endl << "Loop # " << loopCount << endl << endl;
+
+ /****************************************************************
+ * Perform inserts. *
+ ****************************************************************/
+ // Reset and start timer
+ START_TIMER;
+ // Give insert-command to all threads
+ resetThreads(pThreadsData);
+ tellThreads(pThreadsData, stInsert);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed in performing insert" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }
+ // stop timer and print results.
+ STOP_TIMER;
+ PRINT_TIMER("insert", tNoOfOperations*tNoOfThreads, tNoOfTables);
+ /****************************************************************
+ * Verify inserts. *
+ ****************************************************************/
+ if (VerifyFlag) {
+ resetThreads(pThreadsData);
+ ndbout << "Verifying inserts...\t" ;
+ tellThreads(pThreadsData, stVerify);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed while verifying inserts" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }else{
+ ndbout << "\t\tOK" << endl << endl ;
+ }
+ }
+
+ /****************************************************************
+ * Perform read. *
+ ****************************************************************/
+ // Reset and start timer
+ START_TIMER;
+ // Give read-command to all threads
+ resetThreads(pThreadsData);
+ tellThreads(pThreadsData, stRead);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed in performing read" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }
+ // stop timer and print results.
+ STOP_TIMER;
+ PRINT_TIMER("read", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ /****************************************************************
+ * Perform update. *
+ ****************************************************************/
+ // Reset and start timer
+ START_TIMER;
+ // Give insert-command to all threads
+ resetThreads(pThreadsData);
+ tellThreads(pThreadsData, stUpdate);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed in performing update" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }
+ // stop timer and print results.
+ STOP_TIMER;
+ PRINT_TIMER("update", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ /****************************************************************
+ * Verify updates. *
+ ****************************************************************/
+ if (VerifyFlag) {
+ resetThreads(pThreadsData);
+ ndbout << "Verifying updates...\t" ;
+ tellThreads(pThreadsData, stVerify);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed while verifying updates" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }else{
+ ndbout << "\t\tOK" << endl << endl ;
+ }
+ }
+
+ /****************************************************************
+ * Perform read. *
+ ****************************************************************/
+ // Reset and start timer
+ START_TIMER;
+ // Give insert-command to all threads
+ resetThreads(pThreadsData);
+ tellThreads(pThreadsData, stRead);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed in performing read" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }
+ // stop timer and print results.
+ STOP_TIMER;
+ PRINT_TIMER("read", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ /****************************************************************
+ * Perform delete. *
+ ****************************************************************/
+ // Reset and start timer
+ START_TIMER;
+ // Give insert-command to all threads
+ resetThreads(pThreadsData);
+ tellThreads(pThreadsData, stDelete);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed in performing delete" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }
+ // stop timer and print results.
+ STOP_TIMER;
+ PRINT_TIMER("delete", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ /****************************************************************
+ * Verify deletes. *
+ ****************************************************************/
+ if (VerifyFlag) {
+ resetThreads(pThreadsData);
+ ndbout << "Verifying tuple deletion..." ;
+ tellThreads(pThreadsData, stVerifyDelete);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed in verifying deletes" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }else{
+ ndbout << "\t\tOK" << endl << endl ;
+ }
+ }
+
+ ndbout << "--------------------------------------------------" << endl;
+
+ tLoops++;
+
+ if ( 0 != tNoOfLoops && tNoOfLoops <= tLoops )
+ break;
+ theErrorData.printErrorCounters();
+ }
+
+ resetThreads(pThreadsData);
+ tellThreads(pThreadsData, stStop);
+ waitForThreads(pThreadsData);
+
+ void * tmp;
+ for(i = 0; i<tNoOfThreads; i++){
+ NdbThread_WaitFor(pThreadsData[i].threadLife, &tmp);
+ NdbThread_Destroy(&pThreadsData[i].threadLife);
+ }
+ }
+
+ if (useLongKeys == true) {
+ // Only free these areas if they have been allocated
+ // Otherwise cores will happen
+ for (i = 0; i < tNoOfLongPK; i++)
+ free(longKeyAttrName[i]);
+ free(longKeyAttrName);
+ } // if
+
+ delete [] pThreadsData;
+ delete pNdb;
+ theErrorData.printErrorCounters();
+ return NDBT_ProgramExit(returnValue);
+}
+////////////////////////////////////////
+
+
+unsigned long get_hash(unsigned long * hash_key, int len)
+{
+ unsigned long hash_value = 147;
+ unsigned h_key;
+ int i;
+ for (i = 0; i < len; i++)
+ {
+ h_key = hash_key[i];
+ hash_value = (hash_value << 5) + hash_value + (h_key & 255);
+ hash_value = (hash_value << 5) + hash_value + ((h_key >> 8) & 255);
+ hash_value = (hash_value << 5) + hash_value + ((h_key >> 16) & 255);
+ hash_value = (hash_value << 5) + hash_value + ((h_key >> 24) & 255);
+ }
+ return hash_value;
+}
+
+// End of warming up phase
+
+
+
+static void* flexBenchThread(void* pArg)
+{
+ ThreadData* pThreadData = (ThreadData*)pArg;
+ unsigned int threadNo, threadBase;
+ Ndb* pNdb = NULL ;
+ NdbConnection *pTrans = NULL ;
+ NdbOperation** pOps = NULL ;
+ StartType tType ;
+ StartType tSaveType ;
+ NdbRecAttr* tTmp = NULL ;
+ int* attrValue = NULL ;
+ int* attrRefValue = NULL ;
+ int check = 0 ;
+ int loopCountOps, loopCountTables, loopCountAttributes;
+ int tAttemptNo = 0;
+ int tRetryAttempts = 20;
+ int tResult = 0;
+ int tSpecialTrans = 0;
+ int nRefLocalOpOffset = 0 ;
+ int nReadBuffSize =
+ tNoOfTables * tNoOfAttributes * sizeof(int) * tAttributeSize ;
+ int nRefBuffSize =
+ tNoOfOperations * tNoOfAttributes * sizeof(int) * tAttributeSize ;
+ unsigned*** longKeyAttrValue;
+
+
+ threadNo = pThreadData->threadNo ;
+
+ attrValue = (int*)malloc(nReadBuffSize) ;
+ attrRefValue = (int*)malloc(nRefBuffSize) ;
+ pOps = (NdbOperation**)malloc(tNoOfTables*sizeof(NdbOperation*)) ;
+ pNdb = new Ndb(g_cluster_connection, "TEST_DB" );
+
+ if(!attrValue || !attrRefValue || !pOps || !pNdb){
+ // Check allocations to make sure we got all the memory we asked for
+ ndbout << "One or more memory allocations failed when starting thread #";
+ ndbout << threadNo << endl ;
+ ndbout << "Thread #" << threadNo << " will now exit" << endl ;
+ tResult = 13 ;
+ free(attrValue) ;
+ free(attrRefValue) ;
+ free(pOps) ;
+ delete pNdb ;
+ return 0; // thread exits
+ }
+
+ pNdb->init();
+ pNdb->waitUntilReady();
+
+ // To make sure that two different threads doesn't operate on the same record
+ // Calculate an "unique" number to use as primary key
+ threadBase = (threadNo * 2000000) + (tNodeId * 260000000);
+
+ if(useLongKeys){
+ // Allocate and populate the longkey array.
+ longKeyAttrValue = (unsigned ***) malloc(sizeof(unsigned**) * tNoOfOperations );
+ Uint32 n;
+ for (n = 0; n < tNoOfOperations; n++)
+ longKeyAttrValue[n] = (unsigned **) malloc(sizeof(unsigned*) * tNoOfLongPK );
+ for (n = 0; n < tNoOfOperations; n++){
+ for (Uint32 i = 0; i < tNoOfLongPK ; i++) {
+ longKeyAttrValue[n][i] = (unsigned *) malloc(sizeof(unsigned) * tSizeOfLongPK);
+ memset(longKeyAttrValue[n][i], 0, sizeof(unsigned) * tSizeOfLongPK);
+ for(Uint32 j = 0; j < tSizeOfLongPK; j++) {
+ // Repeat the unique value to fill up the long key.
+ longKeyAttrValue[n][i][j] = threadBase + n;
+ }
+ }
+ }
+ }
+
+ int nRefOpOffset = 0 ;
+ //Assign reference attribute values to memory
+ for(Uint32 ops = 1 ; ops < tNoOfOperations ; ops++){
+ // Calculate offset value before going into the next loop
+ nRefOpOffset = tAttributeSize*tNoOfAttributes*(ops-1) ;
+ for(Uint32 a = 0 ; a < tNoOfAttributes ; a++){
+ *(int*)&attrRefValue[nRefOpOffset + tAttributeSize*a] =
+ (int)(threadBase + ops + a) ;
+ }
+ }
+
+#ifdef CEBIT_STAT
+ // ops not yet reported
+ int statOps = 0;
+#endif
+ for (;;) {
+ pThreadData->threadResult = tResult; // Report error to main thread,
+ // normally tResult is set to 0
+ pThreadData->threadReady = 1;
+
+ while (pThreadData->threadStart == stIdle){
+ NdbSleep_MilliSleep(100);
+ }//while
+
+ // Check if signal to exit is received
+ if (pThreadData->threadStart == stStop){
+ pThreadData->threadReady = 1;
+ // ndbout_c("Thread%d is stopping", threadNo);
+ // In order to stop this thread, the main thread has signaled
+ // stStop, break out of the for loop so that destructors
+ // and the proper exit functions are called
+ break;
+ }//if
+
+ tType = pThreadData->threadStart;
+ tSaveType = tType;
+ pThreadData->threadStart = stIdle;
+
+ // Start transaction, type of transaction
+ // is received in the array ThreadStart
+ loopCountOps = tNoOfOperations;
+ loopCountTables = tNoOfTables;
+ loopCountAttributes = tNoOfAttributes;
+
+ for (int count = 1; count < loopCountOps && tResult == 0;){
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ // This is a fatal error, abort program
+ ndbout << "Could not start transaction in thread" << threadNo;
+ ndbout << endl;
+ ndbout << pNdb->getNdbError() << endl;
+ tResult = 1; // Indicate fatal error
+ break; // Break out of for loop
+ }
+
+ // Calculate the current operation offset in the reference array
+ nRefLocalOpOffset = tAttributeSize*tNoOfAttributes*(count - 1) ;
+
+ for (int countTables = 0;
+ countTables < loopCountTables && tResult == 0;
+ countTables++) {
+
+ pOps[countTables] = pTrans->getNdbOperation(tableName[countTables]);
+ if (pOps[countTables] == NULL) {
+ // This is a fatal error, abort program
+ ndbout << "getNdbOperation: " << pTrans->getNdbError();
+ tResult = 2; // Indicate fatal error
+ break;
+ }//if
+
+ switch (tType) {
+ case stInsert: // Insert case
+ if (theWriteFlag == 1 && theDirtyFlag == 1)
+ pOps[countTables]->dirtyWrite();
+ else if (theWriteFlag == 1)
+ pOps[countTables]->writeTuple();
+ else
+ pOps[countTables]->insertTuple();
+ break;
+ case stRead: // Read Case
+ if (theSimpleFlag == 1)
+ pOps[countTables]->simpleRead();
+ else if (theDirtyFlag == 1)
+ pOps[countTables]->dirtyRead();
+ else
+ pOps[countTables]->readTuple();
+ break;
+ case stUpdate: // Update Case
+ if (theWriteFlag == 1 && theDirtyFlag == 1)
+ pOps[countTables]->dirtyWrite();
+ else if (theWriteFlag == 1)
+ pOps[countTables]->writeTuple();
+ else if (theDirtyFlag == 1)
+ pOps[countTables]->dirtyUpdate();
+ else
+ pOps[countTables]->updateTuple();
+ break;
+ case stDelete: // Delete Case
+ pOps[countTables]->deleteTuple();
+ break;
+ case stVerify:
+ pOps[countTables]->readTuple();
+ break;
+ case stVerifyDelete:
+ pOps[countTables]->readTuple();
+ break;
+ default:
+ assert(false);
+ }//switch
+
+
+ if(useLongKeys){
+ // Loop the equal call so the complete key is send to the kernel.
+ for(Uint32 i = 0; i < tNoOfLongPK; i++)
+ pOps[countTables]->equal(longKeyAttrName[i],
+ (char *)longKeyAttrValue[count - 1][i], tSizeOfLongPK*4);
+ }
+ else
+ pOps[countTables]->equal((char*)attrName[0],
+ (char*)&attrRefValue[nRefLocalOpOffset]);
+
+ if (tType == stInsert || tType == stUpdate){
+ for (int ca = 1; ca < loopCountAttributes; ca++){
+ pOps[countTables]->setValue((char*)attrName[ca],
+ (char*)&attrRefValue[nRefLocalOpOffset + tAttributeSize*ca]);
+ }//for
+ } else if (tType == stRead || stVerify == tType) {
+ int nTableOffset = tAttributeSize *
+ loopCountAttributes *
+ countTables ;
+ for (int ca = 1; ca < loopCountAttributes; ca++) {
+ tTmp = pOps[countTables]->getValue((char*)attrName[ca],
+ (char*)&attrValue[nTableOffset + tAttributeSize*ca]);
+ }//for
+ } else if (stVerifyDelete == tType) {
+ if(useLongKeys){
+ int nTableOffset = tAttributeSize *
+ loopCountAttributes *
+ countTables ;
+ tTmp = pOps[countTables]->getValue(longKeyAttrName[0],
+ (char*)&attrValue[nTableOffset]);
+ } else {
+ int nTableOffset = tAttributeSize *
+ loopCountAttributes *
+ countTables ;
+ tTmp = pOps[countTables]->getValue((char*)attrName[0],
+ (char*)&attrValue[nTableOffset]);
+ }
+ }//if
+ }//for Tables loop
+
+ if (tResult != 0)
+ break;
+ check = pTrans->execute(Commit);
+
+ // Decide what kind of error this is
+ if ((tSpecialTrans == 1) &&
+ (check == -1)) {
+ // --------------------------------------------------------------------
+ // A special transaction have been executed, change to check = 0 in
+ // certain situations.
+ // --------------------------------------------------------------------
+ switch (tType) {
+ case stInsert: // Insert case
+ if (630 == pTrans->getNdbError().code ) {
+ check = 0;
+ ndbout << "Insert with 4007 was successful" << endl;
+ }//if
+ break;
+ case stDelete: // Delete Case
+ if (626 == pTrans->getNdbError().code ) {
+ check = 0;
+ ndbout << "Delete with 4007 was successful" << endl;
+ }//if
+ break;
+ default:
+ assert(false);
+ }//switch
+ }//if
+ tSpecialTrans = 0;
+ if (check == -1) {
+ if ((stVerifyDelete == tType) &&
+ (626 == pTrans->getNdbError().code)) {
+ // ----------------------------------------------
+ // It's good news - the deleted tuple is gone,
+ // so reset "check" flag
+ // ----------------------------------------------
+ check = 0 ;
+ } else {
+ int retCode =
+ theErrorData.handleErrorCommon(pTrans->getNdbError());
+ if (retCode == 1) {
+ ndbout_c("execute: %d, %d, %s", count, tType,
+ pTrans->getNdbError().message );
+ ndbout_c("Error code = %d", pTrans->getNdbError().code );
+ tResult = 20;
+ } else if (retCode == 2) {
+ ndbout << "4115 should not happen in flexBench" << endl;
+ tResult = 20;
+ } else if (retCode == 3) {
+ // --------------------------------------------------------------------
+ // We are not certain if the transaction was successful or not.
+ // We must reexecute but might very well find that the transaction
+ // actually was updated. Updates and Reads are no problem here. Inserts
+ // will not cause a problem if error code 630 arrives. Deletes will
+ // not cause a problem if 626 arrives.
+ // --------------------------------------------------------------------
+ if ((tType == stInsert) || (tType == stDelete)) {
+ tSpecialTrans = 1;
+ }//if
+ }//if
+ }//if
+ }//if
+ // Check if retries should be made
+ if (check == -1 && tResult == 0) {
+ if (tAttemptNo < tRetryAttempts){
+ tAttemptNo++;
+ } else {
+ // --------------------------------------------------------------------
+ // Too many retries have been made, report error and break out of loop
+ // --------------------------------------------------------------------
+ ndbout << "Thread" << threadNo;
+ ndbout << ": too many errors reported" << endl;
+ tResult = 10;
+ break;
+ }//if
+ }//if
+
+ if (check == 0){
+ // Go to the next record
+ count++;
+ tAttemptNo = 0;
+#ifdef CEBIT_STAT
+ // report successful ops
+ if (statEnable) {
+ statOps += loopCountTables;
+ if (statOps >= statFreq) {
+ statReport(tType, statOps);
+ statOps = 0;
+ }//if
+ }//if
+#endif
+ }//if
+
+ if (stVerify == tType && 0 == check){
+ int nTableOffset = 0 ;
+ for (int a = 1 ; a < loopCountAttributes ; a++){
+ for (int tables = 0 ; tables < loopCountTables ; tables++){
+ nTableOffset = tables*loopCountAttributes*tAttributeSize ;
+ if (*(int*)&attrValue[nTableOffset + tAttributeSize*a] != *(int*)&attrRefValue[nRefLocalOpOffset + tAttributeSize*a]){
+ ndbout << "Error in verify:" << endl ;
+ ndbout << "attrValue[" << nTableOffset + tAttributeSize*a << "] = " << attrValue[a] << endl ;
+ ndbout << "attrRefValue[" << nRefLocalOpOffset + tAttributeSize*a << "]" << attrRefValue[nRefLocalOpOffset + tAttributeSize*a] << endl ;
+ tResult = 11 ;
+ break ;
+ }//if
+ }//for
+ }//for
+ }// if(stVerify ... )
+ pNdb->closeTransaction(pTrans) ;
+ }// operations loop
+#ifdef CEBIT_STAT
+ // report remaining successful ops
+ if (statEnable) {
+ if (statOps > 0) {
+ statReport(tType, statOps);
+ statOps = 0;
+ }//if
+ }//if
+#endif
+ }
+ delete pNdb;
+ free(attrValue) ;
+ free(attrRefValue) ;
+ free(pOps) ;
+
+ if (useLongKeys == true) {
+ // Only free these areas if they have been allocated
+ // Otherwise cores will occur
+ for (Uint32 n = 0; n < tNoOfOperations; n++){
+ for (Uint32 i = 0; i < tNoOfLongPK; i++) {
+ free(longKeyAttrValue[n][i]);
+ }
+ free(longKeyAttrValue[n]);
+ }
+ free(longKeyAttrValue);
+ } // if
+
+ return NULL; // Thread exits
+}
+
+
+static int readArguments(int argc, const char** argv)
+{
+
+ int i = 1;
+ while (argc > 1){
+ if (strcmp(argv[i], "-t") == 0){
+ tNoOfThreads = atoi(argv[i+1]);
+ if ((tNoOfThreads < 1))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-o") == 0){
+ tNoOfOperations = atoi(argv[i+1]);
+ if (tNoOfOperations < 1)
+ return -1;;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-a") == 0){
+ tNoOfAttributes = atoi(argv[i+1]);
+ if ((tNoOfAttributes < 2) || (tNoOfAttributes > MAXATTR))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-lkn") == 0){
+ tNoOfLongPK = atoi(argv[i+1]);
+ useLongKeys = true;
+ if ((tNoOfLongPK < 1) || (tNoOfLongPK > MAXNOLONGKEY) ||
+ (tNoOfLongPK * tSizeOfLongPK) > MAXLONGKEYTOTALSIZE){
+ ndbout << "Argument -lkn is not in the proper range." << endl;
+ return -1;
+ }
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-lks") == 0){
+ tSizeOfLongPK = atoi(argv[i+1]);
+ useLongKeys = true;
+ if ((tSizeOfLongPK < 1) || (tNoOfLongPK * tSizeOfLongPK) > MAXLONGKEYTOTALSIZE){
+ ndbout << "Argument -lks is not in the proper range 1 to " <<
+ MAXLONGKEYTOTALSIZE << endl;
+ return -1;
+ }
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-c") == 0){
+ tNoOfTables = atoi(argv[i+1]);
+ if ((tNoOfTables < 1) || (tNoOfTables > MAXTABLES))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-stdtables") == 0){
+ theStdTableNameFlag = 1;
+ }else if (strcmp(argv[i], "-l") == 0){
+ tNoOfLoops = atoi(argv[i+1]);
+ if ((tNoOfLoops < 0) || (tNoOfLoops > 100000))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-s") == 0){
+ tAttributeSize = atoi(argv[i+1]);
+ if ((tAttributeSize < 1) || (tAttributeSize > MAXATTRSIZE))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-sleep") == 0){
+ tSleepTime = atoi(argv[i+1]);
+ if ((tSleepTime < 1) || (tSleepTime > 3600))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-simple") == 0){
+ theSimpleFlag = 1;
+ }else if (strcmp(argv[i], "-write") == 0){
+ theWriteFlag = 1;
+ }else if (strcmp(argv[i], "-dirty") == 0){
+ theDirtyFlag = 1;
+ }else if (strcmp(argv[i], "-no_table_create") == 0){
+ theTableCreateFlag = 1;
+ }else if (strcmp(argv[i], "-temp") == 0){
+ theTempTable = true;
+ }else if (strcmp(argv[i], "-noverify") == 0){
+ VerifyFlag = false ;
+ }else if (theErrorData.parseCmdLineArg(argv, i) == true){
+ ; //empty, updated in errorArg(..)
+ }else if (strcmp(argv[i], "-verify") == 0){
+ VerifyFlag = true ;
+#ifdef CEBIT_STAT
+ }else if (strcmp(argv[i], "-statserv") == 0){
+ if (! (argc > 2))
+ return -1;
+ const char *p = argv[i+1];
+ const char *q = strrchr(p, ':');
+ if (q == 0)
+ return -1;
+ BaseString::snprintf(statHost, sizeof(statHost), "%.*s", q-p, p);
+ statPort = atoi(q+1);
+ statEnable = true;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-statfreq") == 0){
+ if (! (argc > 2))
+ return -1;
+ statFreq = atoi(argv[i+1]);
+ if (statFreq < 1)
+ return -1;
+ argc -= 1;
+ i++;
+#endif
+ }else{
+ return -1;
+ }
+ argc -= 1;
+ i++;
+ }
+ return 0;
+}
+
+static void sleepBeforeStartingTest(int seconds){
+ if (seconds > 0){
+ ndbout << "Sleeping(" <<seconds << ")...";
+ NdbSleep_SecSleep(seconds);
+ ndbout << " done!" << endl;
+ }
+}
+
+
+static int
+createTables(Ndb* pMyNdb){
+ int i;
+ for (i = 0; i < tNoOfAttributes; i++){
+ BaseString::snprintf(attrName[i], MAXSTRLEN, "COL%d", i);
+ }
+
+ // Note! Uses only uppercase letters in table name's
+ // so that we can look at the tables with SQL
+ for (i = 0; i < tNoOfTables; i++){
+ if (theStdTableNameFlag == 0){
+ BaseString::snprintf(tableName[i], MAXSTRLEN, "TAB%d_%d", i,
+ (int)(NdbTick_CurrentMillisecond() / 1000));
+ } else {
+ BaseString::snprintf(tableName[i], MAXSTRLEN, "TAB%d", i);
+ }
+ }
+
+ for(i = 0; i < tNoOfTables; i++){
+ ndbout << "Creating " << tableName[i] << "... ";
+
+ NdbDictionary::Table tmpTable(tableName[i]);
+
+ tmpTable.setStoredTable(!theTempTable);
+
+ if(useLongKeys){
+ for(Uint32 i = 0; i < tNoOfLongPK; i++) {
+ NdbDictionary::Column col(longKeyAttrName[i]);
+ col.setType(NdbDictionary::Column::Unsigned);
+ col.setLength(tSizeOfLongPK);
+ col.setPrimaryKey(true);
+ tmpTable.addColumn(col);
+ }
+ } else {
+ NdbDictionary::Column col(attrName[0]);
+ col.setType(NdbDictionary::Column::Unsigned);
+ col.setLength(1);
+ col.setPrimaryKey(true);
+ tmpTable.addColumn(col);
+ }
+
+
+ NdbDictionary::Column col;
+ col.setType(NdbDictionary::Column::Unsigned);
+ col.setLength(tAttributeSize);
+ for (unsigned j = 1; j < tNoOfAttributes; j++){
+ col.setName(attrName[j]);
+ tmpTable.addColumn(col);
+ }
+
+ if(pMyNdb->getDictionary()->createTable(tmpTable) == -1){
+ return -1;
+ }
+ ndbout << "done" << endl;
+ }
+
+ return 0;
+}
+
+
+static void input_error(){
+ ndbout << endl << "Invalid argument!" << endl;
+ ndbout << endl << "Arguments:" << endl;
+ ndbout << " -t Number of threads to start, default 1" << endl;
+ ndbout << " -o Number of operations per loop, default 500" << endl;
+ ndbout << " -l Number of loops to run, default 1, 0=infinite" << endl;
+ ndbout << " -a Number of attributes, default 25" << endl;
+ ndbout << " -c Number of tables, default 1" << endl;
+ ndbout << " -s Size of each attribute, default 1 (Primary Key is always of size 1," << endl;
+ ndbout << " independent of this value)" << endl;
+ ndbout << " -lkn Number of long primary keys, default 1" << endl;
+ ndbout << " -lks Size of each long primary key, default 1" << endl;
+
+ ndbout << " -simple Use simple read to read from database" << endl;
+ ndbout << " -dirty Use dirty read to read from database" << endl;
+ ndbout << " -write Use writeTuple in insert and update" << endl;
+ ndbout << " -stdtables Use standard table names" << endl;
+ ndbout << " -no_table_create Don't create tables in db" << endl;
+ ndbout << " -sleep Sleep a number of seconds before running the test, this" << endl;
+ ndbout << " can be used so that another flexBench have time to create tables" << endl;
+ ndbout << " -temp Use tables without logging" << endl;
+ ndbout << " -verify Verify inserts, updates and deletes" << endl ;
+ theErrorData.printCmdLineArgs(ndbout);
+ ndbout << endl <<"Returns:" << endl;
+ ndbout << "\t 0 - Test passed" << endl;
+ ndbout << "\t 1 - Test failed" << endl;
+ ndbout << "\t 2 - Invalid arguments" << endl << endl;
+}
+
+// vim: set sw=2:
diff --git a/storage/ndb/test/ndbapi/flexHammer.cpp b/storage/ndb/test/ndbapi/flexHammer.cpp
new file mode 100644
index 00000000000..f254b1e5ccf
--- /dev/null
+++ b/storage/ndb/test/ndbapi/flexHammer.cpp
@@ -0,0 +1,897 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* ***************************************************
+ FLEXHAMMER
+ Hammer ndb with read, insert, update and delete transactions.
+
+ Arguments:
+ -t Number of threads to start, default 1
+ -o Number of operations per hammering-round, default 500
+ -l Number of loops to run, default 1, 0=infinite
+ -a Number of attributes, default 25
+ -c Number of tables, default 1
+ -s Size of each attribute, default 1
+ -simple Use simple read to read from database
+ -dirty Use dirty read to read from database
+ -write Use writeTuple to write to db
+ -r Number of records to Hammer
+ -no_table_create Don't create tables in db
+ -regulate To be able to regulate the load flexHammer produces.
+ -stdtables Use standard table names
+ -sleep Sleep a number of seconds before running the test, this
+ can be used so that another flexProgram have tome to create tables
+
+ Returns:
+ 0 - Test passed
+ -1 - Test failed
+ 1 - Invalid arguments
+
+Revision history:
+ 1.7 020208 epesson: Adapted to use NDBT
+ 1.10 020222 epesson: Finalised handling of thread results
+ 1.11 020222 epesson: Bug in checking results during delete fixed
+
+ * *************************************************** */
+
+#include <ndb_global.h>
+#include <NdbApi.hpp>
+
+#include <NdbMain.h>
+#include <NdbThread.h>
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <NdbOut.hpp>
+#include <NdbTimer.hpp>
+#include <NdbTick.h>
+#include <NdbTest.hpp>
+#include <NDBT_Error.hpp>
+#include <NdbSchemaCon.hpp>
+
+ErrorData * flexHammerErrorData;
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+#include <outfmt.h>
+#endif
+
+#define MAXSTRLEN 16
+#define MAXATTR 64
+#define MAXTABLES 64
+#define MAXTHREADS 256
+#define MAXATTRSIZE 100
+// Max number of retries if something fails
+#define MaxNoOfAttemptsC 10
+
+enum StartType {
+ stIdle,
+ stHammer,
+ stStop,
+ stLast};
+
+enum MyOpType {
+ otInsert,
+ otRead,
+ otDelete,
+ otUpdate,
+ otLast};
+
+struct ThreadNdb {
+ int threadNo;
+ NdbThread* threadLife;
+ int threadReady;
+ StartType threadStart;
+ int threadResult;};
+
+extern "C" void* flexHammerThread(void*);
+static int setAttrNames(void);
+static int setTableNames(void);
+static int readArguments(int, const char**);
+static int createTables(Ndb*);
+static void sleepBeforeStartingTest(int seconds);
+static int checkThreadResults(ThreadNdb *threadArrayP, char* phase);
+
+//enum OperationType {
+// otInsert,
+// otRead,
+// otUpdate,
+// otDelete,
+// otVerifyDelete,
+// otLast };
+
+enum ReadyType {
+ stReady,
+ stRunning
+} ;
+static int tNoOfThreads;
+static int tNoOfAttributes;
+static int tNoOfTables;
+static int tNoOfBackups;
+static int tAttributeSize;
+static int tNoOfOperations;
+static int tNoOfRecords;
+static int tNoOfLoops;
+static ReadyType ThreadReady[MAXTHREADS];
+static StartType ThreadStart[MAXTHREADS];
+static char tableName[MAXTABLES][MAXSTRLEN];
+static char attrName[MAXATTR][MAXSTRLEN];
+static int theSimpleFlag = 0;
+static int theWriteFlag = 0;
+static int theDirtyFlag = 0;
+static int theTableCreateFlag = 0;
+static int theStandardTableNameFlag = 0;
+static unsigned int tSleepTime = 0;
+
+#define START_TIMER { NdbTimer timer; timer.doStart();
+#define STOP_TIMER timer.doStop();
+#define PRINT_TIMER(text, trans, opertrans) timer.printTransactionStatistics(text, trans, opertrans); };
+
+
+// Initialise thread data
+void
+resetThreads(ThreadNdb *threadArrayP) {
+
+ for (int i = 0; i < tNoOfThreads ; i++)
+ {
+ threadArrayP[i].threadReady = 0;
+ threadArrayP[i].threadResult = 0;
+ threadArrayP[i].threadStart = stIdle;
+ }
+} // resetThreads
+
+void
+waitForThreads(ThreadNdb *threadArrayP)
+{
+ int cont = 1;
+
+ while (cont) {
+ NdbSleep_MilliSleep(100);
+ cont = 0;
+ for (int i = 0; i < tNoOfThreads ; i++) {
+ if (threadArrayP[i].threadReady == 0) {
+ cont = 1;
+ } // if
+ } // for
+ } // while
+} // waitForThreads
+
+void
+tellThreads(ThreadNdb* threadArrayP, const StartType what)
+{
+ for (int i = 0; i < tNoOfThreads ; i++)
+ {
+ threadArrayP[i].threadStart = what;
+ } // for
+} // tellThreads
+
+static Ndb_cluster_connection *g_cluster_connection= 0;
+
+NDB_COMMAND(flexHammer, "flexHammer", "flexHammer", "flexHammer", 65535)
+//main(int argc, const char** argv)
+{
+ ndb_init();
+ ThreadNdb* pThreads = NULL; // Pointer to thread data array
+ Ndb* pMyNdb = NULL; // Pointer to Ndb object
+ int tLoops = 0;
+ int returnValue = 0;
+ int check = 0;
+
+ flexHammerErrorData = new ErrorData;
+
+ flexHammerErrorData->resetErrorCounters();
+
+ if (readArguments(argc, argv) != 0) {
+ ndbout << "Wrong arguments to flexHammer" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ } // if
+
+ /* print Setting */
+ flexHammerErrorData->printSettings(ndbout);
+
+ check = setAttrNames();
+ if (check == -1) {
+ ndbout << "Couldn't set attribute names" << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ } // if
+ check = setTableNames();
+ if (check == -1) {
+ ndbout << "Couldn't set table names" << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ } // if
+
+ // Create thread data array
+ pThreads = new ThreadNdb[tNoOfThreads];
+ // NdbThread_SetConcurrencyLevel(tNoOfThreads + 2);
+
+ // Create and init Ndb object
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ g_cluster_connection= &con;
+ pMyNdb = new Ndb(g_cluster_connection, "TEST_DB");
+ pMyNdb->init();
+
+ // Wait for Ndb to become ready
+ if (pMyNdb->waitUntilReady(10000) != 0) {
+ ndbout << "NDB is not ready" << endl << "Benchmark failed" << endl;
+ returnValue = NDBT_FAILED;
+ }
+
+ else {
+ check = createTables(pMyNdb);
+ if (check != 0) {
+ returnValue = NDBT_FAILED;
+ } // if
+ else {
+ sleepBeforeStartingTest(tSleepTime);
+
+ // Create threads. *
+ resetThreads(pThreads);
+ for (int i = 0; i < tNoOfThreads ; i++) {
+ pThreads[i].threadNo = i;
+ pThreads[i].threadLife = NdbThread_Create(flexHammerThread,
+ (void**)&pThreads[i],
+ 65535,
+ "flexHammerThread",
+ NDB_THREAD_PRIO_LOW);
+ } // for
+
+ // And wait until they are ready
+ waitForThreads(pThreads);
+ if (checkThreadResults(pThreads, "init") != 0) {
+ returnValue = NDBT_FAILED;
+ } // if
+
+
+ if (returnValue == NDBT_OK) {
+ ndbout << endl << "All threads started" << endl << endl;
+
+ for(;;) {
+
+ // Check if it's time to exit program
+ if((tNoOfLoops != 0) && (tNoOfLoops <= tLoops))
+ break;
+
+ // Tell all threads to start hammer
+ ndbout << "Hammering..." << endl;
+
+ resetThreads(pThreads);
+
+ START_TIMER;
+ tellThreads(pThreads, stHammer);
+
+ waitForThreads(pThreads);
+ ndbout << "Threads ready to continue..." << endl;
+ STOP_TIMER;
+
+ // Check here if anything went wrong
+ if (checkThreadResults(pThreads, "hammer") != 0) {
+ ndbout << "Thread(s) failed." << endl;
+ returnValue = NDBT_FAILED;
+ } // if
+
+ PRINT_TIMER("hammer", tNoOfOperations*tNoOfThreads, tNoOfTables*6);
+
+ ndbout << endl;
+
+ tLoops++;
+
+ } // for
+ } // if
+
+ // Signaling threads to stop
+ resetThreads(pThreads);
+ tellThreads(pThreads, stStop);
+
+ // Wait for threads to stop
+ waitForThreads(pThreads);
+
+ ndbout << "----------------------------------------------" << endl << endl;
+ ndbout << "Benchmark completed" << endl;
+ } // else
+ } // else
+ // Clean up
+
+ flexHammerErrorData->printErrorCounters(ndbout);
+
+ // Kill them all!
+ void* tmp;
+ for(int i = 0; i < tNoOfThreads; i++){
+ NdbThread_WaitFor(pThreads[i].threadLife, &tmp);
+ NdbThread_Destroy(&pThreads[i].threadLife);
+ }
+ delete flexHammerErrorData;
+ delete [] pThreads;
+ delete pMyNdb;
+
+ // Exit via NDBT
+ return NDBT_ProgramExit(returnValue);
+
+} //main
+
+extern "C"
+void*
+flexHammerThread(void* pArg)
+{
+ ThreadNdb* pThreadData = (ThreadNdb*)pArg;
+ unsigned int threadNo = pThreadData->threadNo;
+ Ndb* pMyNdb = NULL ;
+ NdbConnection *pMyTransaction = NULL ;
+ // NdbOperation* pMyOperation[MAXTABLES] = {NULL};
+ NdbOperation* pMyOperation[MAXTABLES];
+ int check = 0;
+ int loop_count_ops = 0;
+ int loop_count_tables = 0;
+ int loop_count_attributes = 0;
+ int count_round = 0;
+ int count = 0;
+ int count_tables = 0;
+ int count_attributes = 0;
+ int i = 0;
+ int j = 0;
+ int tThreadResult = 0;
+ MyOpType tMyOpType = otLast;
+ int pkValue = 0;
+ int readValue[MAXATTR][MAXATTRSIZE] = {0};
+ int attrValue[MAXATTRSIZE];
+ NdbRecAttr* tTmp = NULL;
+ int tNoOfAttempts = 0;
+
+ for (i = 0; i < MAXATTRSIZE; i++)
+ attrValue[i] = 0;
+ // Ndb object for each thread
+ pMyNdb = new Ndb(g_cluster_connection, "TEST_DB" );
+ pMyNdb->init();
+ if (pMyNdb->waitUntilReady(10000) != 0) {
+ // Error, NDB is not ready
+ tThreadResult = 99;
+ // Go to idle directly
+ pThreadData->threadStart = stIdle;
+ } // if
+
+ for(;;) {
+ pThreadData->threadResult = tThreadResult;
+ pThreadData->threadReady = 1; // Signalling ready to main
+
+ // If Idle just wait to be stopped from main
+ while (pThreadData->threadStart == stIdle) {
+ NdbSleep_MilliSleep(100);
+ } // while
+
+ // Check if signal to exit is received
+ if (pThreadData->threadStart == stStop) {
+ pThreadData->threadReady = 1;
+ // break out of eternal loop
+ break;
+ } // if
+
+ // Set to Idle to prepare for possible error break
+ pThreadData->threadStart = stIdle;
+
+ // Prepare transaction
+ loop_count_ops = tNoOfOperations;
+ loop_count_tables = tNoOfTables;
+ loop_count_attributes = tNoOfAttributes;
+
+ for (count=0 ; count < loop_count_ops ; count++) {
+
+ //pkValue = (int)(count + thread_base);
+ // This limits the number of records used in this test
+ pkValue = count % tNoOfRecords;
+
+ for (count_round = 0; count_round < 5; ) {
+ switch (count_round) {
+ case 0: // Insert
+ tMyOpType = otInsert;
+ // Increase attrValues
+ for (i=0; i < MAXATTRSIZE; i ++) {
+ attrValue[i]++;
+ }
+ break;
+ case 1:
+ case 3: // Read and verify
+ tMyOpType = otRead;
+ break;
+ case 2: // Update
+ // Increase attrValues
+ for(i=0; i < MAXATTRSIZE; i ++) {
+ attrValue[i]++;
+ }
+ tMyOpType = otUpdate;
+ break;
+ case 4: // Delete
+ tMyOpType = otDelete;
+ break;
+ default:
+ assert(false);
+ break;
+ } // switch
+
+ // Get transaction object
+ pMyTransaction = pMyNdb->startTransaction();
+ if (pMyTransaction == NULL) {
+ // Fatal error
+ tThreadResult = 1;
+ // break out of for count_round loop waiting to be stopped by main
+ break;
+ } // if
+
+ for (count_tables = 0; count_tables < loop_count_tables;
+ count_tables++) {
+ pMyOperation[count_tables] =
+ pMyTransaction->getNdbOperation(tableName[count_tables]);
+ if (pMyOperation[count_tables] == NULL) {
+ //Fatal error
+ tThreadResult = 2;
+ // break out of inner for count_tables loop
+ break;
+ } // if
+
+ switch (tMyOpType) {
+ case otInsert: // Insert case
+ if (theWriteFlag == 1 && theDirtyFlag == 1) {
+ check = pMyOperation[count_tables]->dirtyWrite();
+ } else if (theWriteFlag == 1) {
+ check = pMyOperation[count_tables]->writeTuple();
+ } else {
+ check = pMyOperation[count_tables]->insertTuple();
+ } // if else
+ break;
+ case otRead: // Read Case
+ if (theSimpleFlag == 1) {
+ check = pMyOperation[count_tables]->simpleRead();
+ } else if (theDirtyFlag == 1) {
+ check = pMyOperation[count_tables]->dirtyRead();
+ } else {
+ check = pMyOperation[count_tables]->readTuple();
+ } // if else
+ break;
+ case otUpdate: // Update Case
+ if (theWriteFlag == 1 && theDirtyFlag == 1) {
+ check = pMyOperation[count_tables]->dirtyWrite();
+ } else if (theWriteFlag == 1) {
+ check = pMyOperation[count_tables]->writeTuple();
+ } else if (theDirtyFlag == 1) {
+ check = pMyOperation[count_tables]->dirtyUpdate();
+ } else {
+ check = pMyOperation[count_tables]->updateTuple();
+ } // if else
+ break;
+ case otDelete: // Delete Case
+ check = pMyOperation[count_tables]->deleteTuple();
+ break;
+ default:
+ assert(false);
+ break;
+ } // switch
+ if (check == -1) {
+ // Fatal error
+ tThreadResult = 3;
+ // break out of inner for count_tables loop
+ break;
+ } // if
+
+ check = pMyOperation[count_tables]->equal( (char*)attrName[0],
+ (char*)&pkValue );
+
+ if (check == -1) {
+ // Fatal error
+ tThreadResult = 4;
+ ndbout << "pMyOperation equal failed" << endl;
+ // break out of inner for count_tables loop
+ break;
+ } // if
+
+ check = -1;
+ tTmp = NULL;
+ switch (tMyOpType) {
+ case otInsert: // Insert case
+ case otUpdate: // Update Case
+ for (count_attributes = 1; count_attributes < loop_count_attributes;
+ count_attributes++) {
+ check =
+ pMyOperation[count_tables]->setValue((char*)attrName[count_attributes], (char*)&attrValue[0]);
+ } // for
+ break;
+ case otRead: // Read Case
+ for (count_attributes = 1; count_attributes < loop_count_attributes;
+ count_attributes++) {
+ tTmp = pMyOperation[count_tables]->
+ getValue( (char*)attrName[count_attributes],
+ (char*)&readValue[count_attributes][0] );
+ } // for
+ break;
+ case otDelete: // Delete Case
+ break;
+ default:
+ assert(false);
+ break;
+ } // switch
+ if (check == -1 && tTmp == NULL && tMyOpType != otDelete) {
+ // Fatal error
+ tThreadResult = 5;
+ break;
+ } // if
+ } // for count_tables
+
+ // Only execute if everything is OK
+ if (tThreadResult != 0) {
+ // Close transaction (below)
+ // and continue with next count_round
+ count_round++;
+ tNoOfAttempts = 0;
+ } // if
+ else {
+ check = pMyTransaction->execute(Commit);
+ if (check == -1 ) {
+ const NdbError & err = pMyTransaction->getNdbError();
+
+ // Add complete error handling here
+
+ int retCode = flexHammerErrorData->handleErrorCommon(pMyTransaction->getNdbError());
+ if (retCode == 1) {
+ //if (strcmp(pMyTransaction->getNdbError().message, "Tuple did not exist") != 0 && strcmp(pMyTransaction->getNdbError().message,"Tuple already existed when attempting to insert") != 0) ndbout_c("execute: %s", pMyTransaction->getNdbError().message);
+
+ if (pMyTransaction->getNdbError().code != 626 && pMyTransaction->getNdbError().code != 630){
+ ndbout_c("Error code = %d", pMyTransaction->getNdbError().code);
+ ndbout_c("execute: %s", pMyTransaction->getNdbError().message);}
+
+ } else if (retCode == 2) {
+ ndbout << "4115 should not happen in flexHammer" << endl;
+ } else if (retCode == 3) {
+// --------------------------------------------------------------------
+// We are not certain if the transaction was successful or not.
+// We must reexecute but might very well find that the transaction
+// actually was updated. Updates and Reads are no problem here. Inserts
+// will not cause a problem if error code 630 arrives. Deletes will
+// not cause a problem if 626 arrives.
+// --------------------------------------------------------------------
+ /* What can we do here? */
+ ndbout_c("execute: %s", pMyTransaction->getNdbError().message);
+ }//if(retCode == 3)
+ // End of adding complete error handling
+
+ switch( err.classification) {
+ case NdbError::ConstraintViolation: // Tuple already existed
+ count_round++;
+ tNoOfAttempts = 0;
+ break;
+ case NdbError::TimeoutExpired:
+ case NdbError::NodeRecoveryError:
+ case NdbError::TemporaryResourceError:
+ case NdbError::OverloadError:
+ if (tNoOfAttempts <= MaxNoOfAttemptsC) {
+ // Retry
+ tNoOfAttempts++;
+ } else {
+ // Too many retries, continue with next
+ count_round++;
+ tNoOfAttempts = 0;
+ } // else if
+ break;
+ // Fatal, just continue
+ default:
+ count_round++;
+ tNoOfAttempts = 0;
+ break;
+ } // switch
+ } // if
+ else {
+ // Execute commit was OK
+ // This is verifying read values
+ //switch (tMyOpType) {
+ //case otRead: // Read case
+ //for (j = 0; j < tNoOfAttributes; j++) {
+ //for(i = 1; i < tAttributeSize; i++) {
+ //if ( readValue[j][i] != attrValue[i]) {
+ //ndbout << "pkValue = " << pkValue << endl;
+ //ndbout << "readValue != attrValue" << endl;
+ //ndbout << readValue[j][i] << " != " << attrValue[i] << endl;
+ //} // if
+ // } // for
+ //} // for
+ //break;
+ //} // switch
+ count_round++;
+ tNoOfAttempts = 0;
+ } // else if
+ } // else if
+ pMyNdb->closeTransaction(pMyTransaction);
+ } // for count_round
+ } // for count
+ } // for (;;)
+
+ // Clean up
+ delete pMyNdb;
+ pMyNdb = NULL;
+
+ flexHammerErrorData->resetErrorCounters();
+
+ return NULL; // thread exits
+
+} // flexHammerThread
+
+
+int
+readArguments (int argc, const char** argv)
+{
+ int i = 1;
+
+ tNoOfThreads = 5; // Default Value
+ tNoOfOperations = 500; // Default Value
+ tNoOfRecords = 1; // Default Value
+ tNoOfLoops = 1; // Default Value
+ tNoOfAttributes = 25; // Default Value
+ tNoOfTables = 1; // Default Value
+ tNoOfBackups = 0; // Default Value
+ tAttributeSize = 1; // Default Value
+ theTableCreateFlag = 0;
+
+ while (argc > 1) {
+ if (strcmp(argv[i], "-t") == 0) {
+ tNoOfThreads = atoi(argv[i+1]);
+ if ((tNoOfThreads < 1) || (tNoOfThreads > MAXTHREADS))
+ return(1);
+ }
+ else if (strcmp(argv[i], "-o") == 0) {
+ tNoOfOperations = atoi(argv[i+1]);
+ if (tNoOfOperations < 1)
+ return(1);
+ }
+ else if (strcmp(argv[i], "-r") == 0) {
+ tNoOfRecords = atoi(argv[i+1]);
+ if (tNoOfRecords < 1)
+ return(1);
+ }
+ else if (strcmp(argv[i], "-a") == 0) {
+ tNoOfAttributes = atoi(argv[i+1]);
+ if ((tNoOfAttributes < 2) || (tNoOfAttributes > MAXATTR))
+ return(1);
+ }
+ else if (strcmp(argv[i], "-c") == 0) {
+ tNoOfTables = atoi(argv[i+1]);
+ if ((tNoOfTables < 1) || (tNoOfTables > MAXTABLES))
+ return(1);
+ }
+ else if (strcmp(argv[i], "-l") == 0) {
+ tNoOfLoops = atoi(argv[i+1]);
+ if ((tNoOfLoops < 0) || (tNoOfLoops > 100000))
+ return(1);
+ }
+ else if (strcmp(argv[i], "-s") == 0) {
+ tAttributeSize = atoi(argv[i+1]);
+ if ((tAttributeSize < 1) || (tAttributeSize > MAXATTRSIZE))
+ return(1);
+ }
+ else if (strcmp(argv[i], "-sleep") == 0) {
+ tSleepTime = atoi(argv[i+1]);
+ if ((tSleepTime < 1) || (tSleepTime > 3600))
+ exit(-1);
+ }
+ else if (strcmp(argv[i], "-simple") == 0) {
+ theSimpleFlag = 1;
+ argc++;
+ i--;
+ }
+ else if (strcmp(argv[i], "-write") == 0) {
+ theWriteFlag = 1;
+ argc++;
+ i--;
+ }
+ else if (strcmp(argv[i], "-dirty") == 0) {
+ theDirtyFlag = 1;
+ argc++;
+ i--;
+ }
+ else if (strcmp(argv[i], "-no_table_create") == 0) {
+ theTableCreateFlag = 1;
+ argc++;
+ i--;
+ }
+ else if (strcmp(argv[i], "-stdtables") == 0) {
+ theStandardTableNameFlag = 1;
+ argc++;
+ i--;
+ } // if
+ else {
+ return(1);
+ }
+
+ argc -= 2;
+ i = i + 2;
+ } // while
+
+ ndbout << endl << "FLEXHAMMER - Starting normal mode" << endl;
+ ndbout << "Hammer ndb with read, insert, update and delete transactions"<< endl << endl;
+
+ ndbout << " " << tNoOfThreads << " thread(s) " << endl;
+ ndbout << " " << tNoOfLoops << " iterations " << endl;
+ ndbout << " " << tNoOfTables << " table(s) and " << 1 << " operation(s) per transaction " << endl;
+ ndbout << " " << tNoOfRecords << " records to hammer(limit this with the -r option)" << endl;
+ ndbout << " " << tNoOfAttributes << " attributes per table " << endl;
+ ndbout << " " << tNoOfOperations << " transaction(s) per thread and round " << endl;
+ ndbout << " " << tAttributeSize << " is the number of 32 bit words per attribute " << endl << endl;
+ return 0;
+} // readArguments
+
+
+void sleepBeforeStartingTest(int seconds)
+{
+ if (seconds > 0) {
+ ndbout << "Sleeping(" << seconds << ")...";
+ NdbSleep_SecSleep(seconds);
+ ndbout << " done!" << endl;
+ } // if
+} // sleepBeforeStartingTest
+
+static int
+createTables(Ndb* pMyNdb)
+{
+ int i = 0;
+ int j = 0;
+ int check = 0;
+ NdbSchemaCon *MySchemaTransaction = NULL;
+ NdbSchemaOp *MySchemaOp = NULL;
+
+ // Create Table and Attributes.
+ if (theTableCreateFlag == 0) {
+
+ for (i = 0; i < tNoOfTables; i++) {
+
+ ndbout << "Creating " << tableName[i] << "...";
+ // Check if table exists already
+ const void * p = pMyNdb->getDictionary()->getTable(tableName[i]);
+ if (p != 0) {
+ ndbout << " already exists." << endl;
+ // Continue with next table at once
+ continue;
+ } // if
+ ndbout << endl;
+
+ MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pMyNdb);
+ if (MySchemaTransaction == NULL) {
+ return(-1);
+ } // if
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if (MySchemaOp == NULL) {
+ // Clean up opened schema transaction
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return(-1);
+ } // if
+
+ // Create tables, rest of parameters are default right now
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ check = MySchemaOp->createTable(tableName[i],
+ 8, // Table Size
+ TupleKey, // Key Type
+ 40, // Nr of Pages
+ All,
+ 6,
+ 78,
+ 80,
+ 1,
+ false);
+
+#else
+ check = MySchemaOp->createTable(tableName[i],
+ 8, // Table Size
+ TupleKey, // Key Type
+ 40); // Nr of Pages
+#endif
+ if (check == -1) {
+ // Clean up opened schema transaction
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return(-1);
+ } // if
+
+ // Primary key
+ //ndbout << " pk " << (char*)&attrName[0] << "..." << endl;
+ check = MySchemaOp->createAttribute( (char*)attrName[0], TupleKey, 32,
+ 1, UnSigned, MMBased,
+ NotNullAttribute );
+ if (check == -1) {
+ // Clean up opened schema transaction
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return(-1);
+ } // if
+
+ // Rest of attributes
+ for (j = 1; j < tNoOfAttributes ; j++) {
+ //ndbout << " " << (char*)attrName[j] << "..." << endl;
+ check = MySchemaOp->createAttribute( (char*)attrName[j], NoKey, 32,
+ tAttributeSize, UnSigned, MMBased,
+ NotNullAttribute );
+ if (check == -1) {
+ // Clean up opened schema transaction
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return(-1);
+ } // if
+ } // for
+
+ // Execute creation
+ check = MySchemaTransaction->execute();
+ if (check == -1) {
+ // Clean up opened schema transaction
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return(-1);
+ } // if
+
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ } // for
+ } // if
+
+ return(0);
+
+} // createTables
+
+
+static int setAttrNames()
+{
+ int i = 0;
+ int retVal = 0;
+
+ for (i = 0; i < MAXATTR ; i++) {
+ retVal = BaseString::snprintf(attrName[i], MAXSTRLEN, "COL%d", i);
+ if (retVal < 0) {
+ // Error in conversion
+ return(-1);
+ } // if
+ } // for
+
+ return (0);
+} // setAttrNames
+
+static int setTableNames()
+{
+ // Note! Uses only uppercase letters in table name's
+ // so that we can look at the tables wits SQL
+ int i = 0;
+ int retVal = 0;
+
+ for (i = 0; i < MAXTABLES ; i++) {
+ if (theStandardTableNameFlag == 0) {
+ retVal = BaseString::snprintf(tableName[i], MAXSTRLEN, "TAB%d_%d", i,
+ NdbTick_CurrentMillisecond()/1000);
+ } // if
+ else {
+ retVal = BaseString::snprintf(tableName[i], MAXSTRLEN, "TAB%d", i);
+ } // else
+ if (retVal < 0) {
+ // Error in conversion
+ return(-1);
+ } // if
+ } // for
+
+ return(0);
+} // setTableNames
+
+static int checkThreadResults(ThreadNdb *threadArrayP, char* phase)
+{
+ int i = 0;
+
+ for (i = 0; i < tNoOfThreads; i++) {
+ if (threadArrayP[i].threadResult != 0) {
+ ndbout << "Thread " << i << " reported fatal error "
+ << threadArrayP[i].threadResult << " during " << phase << endl;
+ return(-1);
+ } // if
+ } // for
+
+ return(0);
+}
+
diff --git a/storage/ndb/test/ndbapi/flexScan.cpp b/storage/ndb/test/ndbapi/flexScan.cpp
new file mode 100644
index 00000000000..4d2c85d6955
--- /dev/null
+++ b/storage/ndb/test/ndbapi/flexScan.cpp
@@ -0,0 +1,1667 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* ***************************************************
+ FLEXSCAN
+ Perform benchmark of:
+ insert
+ read
+ scan read
+ update
+ scan update
+ read
+ scan delete
+ verify delete
+
+ Arguments:
+ -f Location of Ndb.cfg file, default Ndb.cfg
+ -t Number of threads to start, default 1
+ -o Number of operations per loop, default 500 -l Number of loops to run, default 1, 0=infinite
+ -a Number of attributes, default 25
+ -c Number of tables, default 1
+ -s Size of each attribute, default 1
+ -stdtables Use standard table names
+ -no_table_create Don't create tables in db
+ -sleep Sleep a number of seconds before running the test, this
+ can be used so that another flexBench hav etome to create tables
+ -p Parallellism to use 1-32, default:1
+ -abort <number> Test scan abort after a number of tuples
+ -h Print help text
+ -no_scan_update Don't do scan updates
+ -no_scan_delete Don't do scan deletes
+
+ Returns:
+ NDBT_OK - Test passed
+ NDBT_FAILED - Test failed
+
+ Revision history:
+ 1.12 020222 epesson: Rewritten to use NDBT. Major bugs fixed
+
+ * *************************************************** */
+
+#include "NdbApi.hpp"
+
+#include <NdbThread.h>
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <NdbOut.hpp>
+#include <NdbTimer.hpp>
+#include <NdbMain.h>
+#include <NdbTest.hpp>
+#include <NDBT_Error.hpp>
+#include <NdbSchemaCon.hpp>
+
+#define PKSIZE 1
+#define FOREVER 1
+#define MAXSTRLEN 16
+#define MAXATTR 64
+#define MAXTABLES 64
+#define MAXTHREADS 256
+#define MAXATTRSIZE 64
+
+enum StartType {
+ stIdle,
+ stInsert,
+ stRead,
+ stScanRead,
+ stUpdate,
+ stScanUpdate,
+ stDelete,
+ stVerifyDelete,
+ stScanDelete,
+ stStop,
+ stLast} ;
+
+
+struct ThreadNdb
+{
+ int ThreadNo;
+ NdbThread* threadLife;
+ StartType threadStart;
+ int threadResult;
+ int threadReady;
+};
+
+extern "C" void* flexScanThread(void*);
+static int setAttrNames(void);
+static int setTableNames(void);
+static int createTables(Ndb* pMyNdb);
+static void sleepBeforeStartingTest(int seconds);
+static int readArguments(int argc, const char** argv);
+static void setAttrValues(int* attrValue,
+ int* readValue,
+ int Offset);
+static int insertRows(Ndb* pNdb, int* pkValue, int* attrValue, StartType tType);
+static int readRows(Ndb* pNdb, int* pkValue, int* readValue);
+static int deleteRows(Ndb* pNdb, int* pkValue);
+static int scanReadRows(Ndb* pNdb, int* readValue);
+static int scanUpdateRows(Ndb* pNdb, int* readValue, int* attrValue);
+static int scanDeleteRows(Ndb* pNdb, int* readValue);
+static int verifyDeleteRows(Ndb* pNdb, int* pkValue, int* readValue);
+static void Compare(int* attrValue, int* readValue);
+static void UpdateArray(int *attrValue);
+
+static int tNoOfThreads = 1;
+static int tNoOfAttributes = 25;
+static int tNoOfTables = 1;
+static int tAttributeSize = 1;
+static int tNodeId = 0;
+static int tNoOfOperations = 500;
+static int tNoOfLoops = 1;
+static int tAbortAfter = 0;
+static int tParallellism = 1;
+
+static char tableName[MAXTABLES][MAXSTRLEN];
+static char attrName[MAXATTR][MAXSTRLEN];
+
+static unsigned int tSleepTime = 0;
+
+static int theStdTableNameFlag = 0;
+static int theTableCreateFlag = 0;
+static int theScanAbortTestFlag = 0;
+static int theNoScanUpdateFlag = 0;
+static int theNoScanDeleteFlag = 0;
+
+//flexScanErrorData = new ErrorData;
+ErrorData * flexScanErrorData;
+NdbError * anerror;
+
+//static errorData theErrorData;
+//static unsigned int tErrorCounter[6000];
+
+#define START_TIMER { NdbTimer timer; timer.doStart();
+#define STOP_TIMER timer.doStop();
+#define PRINT_TIMER(text, trans, opertrans) timer.printTransactionStatistics(text, trans, opertrans); };
+
+static void UpdateArray(int *attrValue)
+{
+ int tableCount = 0;
+ int attrCount = 0;
+ int opCount = 0;
+ int sizeCount = 0;
+ int* pValue = attrValue;
+
+ for (tableCount = 0; tableCount < tNoOfTables; tableCount++) {
+ for (attrCount = 0; attrCount < tNoOfAttributes-1; attrCount++) {
+ for (opCount = 0; opCount < tNoOfOperations; opCount++) {
+ for (sizeCount = 0; sizeCount < tAttributeSize; sizeCount++) {
+ // Update value in array
+ (*pValue)++;
+ //ndbout << "attrValue[" << tableCount*tNoOfAttributes*tNoOfOperations*tAttributeSize +
+ //attrCount*tNoOfOperations*tAttributeSize + opCount*tAttributeSize + sizeCount <<
+ //"] = " << attrValue[tableCount*tNoOfAttributes*tNoOfOperations*tAttributeSize +
+ //attrCount*tNoOfOperations*tAttributeSize + opCount*tAttributeSize + sizeCount] << endl;
+ // Increment pointer
+ pValue++;
+ } // sizeCount
+ } // for opCount
+ } // for attrCount
+ } // for tableCount
+
+} // Update
+
+static void Compare(int* attrValue, int* readValue)
+{
+ int tableCount = 0;
+ int attrCount = 0;
+ int OpCount = 0;
+ int first = 0;
+
+ for (tableCount = 0; tableCount < tNoOfTables; tableCount++) {
+ for (attrCount = 0; attrCount < tNoOfAttributes-1; attrCount++) {
+ for (OpCount = 0; OpCount < tNoOfOperations; OpCount++) {
+ if (memcmp(&(attrValue[tableCount*(tNoOfAttributes-1)*tNoOfOperations +
+ attrCount*tNoOfOperations + OpCount]),
+ &(readValue[tableCount*(tNoOfAttributes-1)*tNoOfOperations +
+ attrCount*tNoOfOperations + OpCount]),
+ tAttributeSize) != 0) {
+ // Values mismatch
+ if (first == 0) {
+ //ndbout << "Read and set values differ for:" << endl;
+ first = 1;
+ ndbout << "Mismatch found.";
+ } // if
+ // Comparision of values after scan update is meaningless right now
+ //ndbout << " table " << tableName[tableCount] <<
+ //" - attr " << attrName[attrCount+1];
+ //for (sizeCount = 0; sizeCount < tAttributeSize; sizeCount++) {
+ //ndbout << ": set " <<
+ //attrValue[tableCount*(tNoOfAttributes-1)*tNoOfOperations*tAttributeSize +
+ //attrCount*tNoOfOperations*tAttributeSize +
+ //tNoOfOperations*tAttributeSize + sizeCount] << " read " <<
+ //readValue[tableCount*(tNoOfAttributes-1)*tNoOfOperations*tAttributeSize +
+ //attrCount*tNoOfOperations*tAttributeSize +
+ //tNoOfOperations*tAttributeSize + sizeCount] << endl;
+ //} // for
+ } // if
+ } // for OpCount
+ } // for attrCount
+ } // for tableCount
+
+ // A final pretty-print
+ if (first == 1) {
+ ndbout << endl;
+ } // if
+} // Compare
+
+static void printInfo()
+{
+ ndbout << endl << "FLEXSCAN - Starting normal mode" << endl;
+ ndbout << "Perform benchmark of insert, update and delete transactions"<< endl;
+ ndbout << " NdbAPI node with id = " << tNodeId << endl;
+ ndbout << " " << tNoOfThreads << " thread(s) " << endl;
+ ndbout << " " << tNoOfLoops << " iterations " << endl;
+ ndbout << " " << tNoOfTables << " table(s) and " << 1 << " operation(s) per transaction "
+ << endl;
+ ndbout << " " << tNoOfAttributes << " attributes per table incl. pk" << endl;
+ ndbout << " " << tNoOfOperations << " transaction(s) per thread and round " << endl;
+ if (theScanAbortTestFlag == 1) {
+ ndbout << " Scan abort test after " << tAbortAfter << " tuples" << endl;
+ } // if
+ ndbout << " " << tParallellism << " parallellism in scans" << endl;
+ ndbout << " " << tAttributeSize << " is the number of 32 bit words per attribute " <<
+ endl << endl;
+
+} // printInfo
+
+static void tellThreads(ThreadNdb *threadArrayP, StartType what)
+{
+ int i = 0;
+
+ for (i = 0; i < tNoOfThreads ; i++)
+ threadArrayP[i].threadStart = what;
+} // tellThreads
+
+static void waitForThreads(ThreadNdb *threadArrayP)
+{
+ int i = 0;
+ int cont = 1;
+
+ while (cont == 1){
+
+ NdbSleep_MilliSleep(10);
+ cont = 0;
+
+ for (i = 0; i < tNoOfThreads ; i++) {
+ if (threadArrayP[i].threadReady == 0) {
+// ndbout << "Main is reporting thread " << i << " not ready" << endl;
+ cont = 1;
+ } // if
+ } // for
+ } // while
+} // waitForThreads
+
+
+static void resetThreads(ThreadNdb *threadArrayP)
+{
+ int i = 0;
+
+ for (i = 0; i < tNoOfThreads ; i++) {
+ threadArrayP[i].threadReady = 0;
+ threadArrayP[i].threadResult = 0;
+ threadArrayP[i].threadStart = stIdle;
+ //ndbout << "threadStart[" << i << "]=" <<
+ //threadArrayP[i].threadStart << endl;
+ } // for
+} // resetThreads
+
+static int checkThreadResults(ThreadNdb *threadArrayP, char *action)
+{
+ int i = 0;
+ int retValue = 0;
+
+ for (i = 0; i < tNoOfThreads; i++) {
+ if (threadArrayP[i].threadResult != 0) {
+ ndbout << "Thread " << i << " reported fatal error "
+ << threadArrayP[i].threadResult << " during " << action << endl;
+ retValue = -1;
+ break;
+ } // if
+ } // for
+
+ return(retValue);
+} // checkThreadResults
+
+NDB_COMMAND(flexScan, "flexScan", "flexScan", "flexScan", 65535)
+{
+ ndb_init();
+ ThreadNdb* pThreads = NULL;
+ Ndb* pMyNdb = NULL;
+ int tLoops = 0;
+ int check = 0;
+ int returnValue = NDBT_OK;
+ int every2ndScanDelete = 0; // Switch between scan delete and normal delete
+
+ flexScanErrorData = new ErrorData;
+
+ flexScanErrorData->resetErrorCounters();
+
+ if (readArguments(argc, argv) != 0) {
+ ndbout << "Wrong arguments to flexScan" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ } // if
+
+ /* print Setting */
+ flexScanErrorData->printSettings(ndbout);
+
+ check = setAttrNames();
+ if (check != 0) {
+ ndbout << "Couldn't set attribute names" << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ } // if
+ check = setTableNames();
+ if (check != 0) {
+ ndbout << "Couldn't set table names" << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ } // if
+
+ pMyNdb = new Ndb ("TEST_DB");
+ pMyNdb->init();
+ tNodeId = pMyNdb->getNodeId();
+
+ printInfo();
+
+ NdbThread_SetConcurrencyLevel(tNoOfThreads + 2);
+ //NdbThread_SetConcurrencyLevel(tNoOfThreads + 8);
+
+ pThreads = new ThreadNdb[tNoOfThreads];
+
+ if (pMyNdb->waitUntilReady(10000) != 0) {
+ ndbout << "NDB is not ready" << endl << "Benchmark failed" << endl;
+ returnValue = NDBT_FAILED;
+ } // if
+
+ else {
+
+ if (createTables(pMyNdb) != 0) {
+ ndbout << "Could not create tables" << endl;
+ returnValue = NDBT_FAILED;
+ } // if
+ else {
+ sleepBeforeStartingTest(tSleepTime);
+
+ resetThreads(pThreads);
+ // Create threads
+ for (int i = 0; i < tNoOfThreads ; i++){
+ pThreads[i].ThreadNo = i;
+ // Ignore the case that thread creation may fail
+ pThreads[i].threadLife = NdbThread_Create(flexScanThread,
+ (void**)&pThreads[i],
+ 327680,
+ "flexScanThread", NDB_THREAD_PRIO_LOW);
+ if (pThreads[i].threadLife == NULL) {
+ ndbout << "Could not create thread " << i << endl;
+ returnValue = NDBT_FAILED;
+ // Use the number of threads that were actually created
+ tNoOfThreads = i;
+ break; // break for loop
+ } // if
+ } // for
+
+ waitForThreads(pThreads);
+ if (checkThreadResults(pThreads, "init") != 0) {
+ returnValue = NDBT_FAILED;
+ } // if
+
+ if (returnValue == NDBT_OK) {
+ ndbout << "All threads started" << endl;
+
+ while (FOREVER) {
+
+ resetThreads(pThreads);
+
+ if ((tNoOfLoops != 0) && (tNoOfLoops <= tLoops)) {
+ break;
+ } // if
+
+ // Insert
+ START_TIMER;
+
+ tellThreads(pThreads, stInsert);
+ waitForThreads(pThreads);
+
+ STOP_TIMER;
+ if (checkThreadResults(pThreads, "insert") != 0) {
+ returnValue = NDBT_FAILED;
+ break;
+ } // if
+ PRINT_TIMER("insert", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ resetThreads(pThreads);
+
+ // Read
+ START_TIMER;
+
+ tellThreads(pThreads, stRead);
+ waitForThreads(pThreads);
+
+ STOP_TIMER;
+ if (checkThreadResults(pThreads, "read") != 0) {
+ returnValue = NDBT_FAILED;
+ break;
+ } // if
+ PRINT_TIMER("read", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ resetThreads(pThreads);
+
+ // Update
+ START_TIMER;
+
+ tellThreads(pThreads, stUpdate);
+ waitForThreads(pThreads);
+
+ STOP_TIMER;
+ if (checkThreadResults(pThreads, "update") != 0) {
+ returnValue = NDBT_FAILED;
+ break;
+ } // if
+ PRINT_TIMER("update", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ resetThreads(pThreads);
+
+ // Scan read
+ START_TIMER;
+
+ tellThreads(pThreads, stScanRead);
+ waitForThreads(pThreads);
+
+ STOP_TIMER;
+ if (checkThreadResults(pThreads, "scanread") != 0) {
+ returnValue = NDBT_FAILED;
+ break;
+ } // if
+ PRINT_TIMER("scanread", tNoOfTables*tNoOfThreads, 1);
+
+ resetThreads(pThreads);
+
+ // Update
+ START_TIMER;
+
+ tellThreads(pThreads, stUpdate);
+ waitForThreads(pThreads);
+
+ STOP_TIMER;
+ if (checkThreadResults(pThreads, "update") != 0) {
+ returnValue = NDBT_FAILED;
+ break;
+ } // if
+ PRINT_TIMER("update", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ resetThreads(pThreads);
+
+ // Read
+ START_TIMER;
+
+ tellThreads(pThreads, stRead);
+ waitForThreads(pThreads);
+
+ STOP_TIMER;
+ if (checkThreadResults(pThreads, "read") != 0) {
+ returnValue = NDBT_FAILED;
+ break;
+ } // if
+ PRINT_TIMER("read", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ resetThreads(pThreads);
+
+ // Only do scan update if told to do so
+ if (theNoScanUpdateFlag == 0) {
+ // Scan update
+ START_TIMER;
+
+ tellThreads(pThreads, stScanUpdate);
+ waitForThreads(pThreads);
+
+ STOP_TIMER;
+ if (checkThreadResults(pThreads, "scanupdate") != 0) {
+ returnValue = NDBT_FAILED;
+ break;
+ } // if
+ PRINT_TIMER("scanupdate", tNoOfTables*tNoOfThreads, 1);
+
+ resetThreads(pThreads);
+
+ // Read
+ START_TIMER;
+
+ tellThreads(pThreads, stRead);
+ // tellThreads(pThreads, stScanRead);
+ waitForThreads(pThreads);
+
+ STOP_TIMER;
+ if (checkThreadResults(pThreads, "read") != 0) {
+ returnValue = NDBT_FAILED;
+ break;
+ } // if
+ PRINT_TIMER("read", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ resetThreads(pThreads);
+ } // if theNoScanUpdateFlag
+
+ // Shift between delete and scan delete
+ if ((every2ndScanDelete % 2 == 0) || (theNoScanDeleteFlag == 1)){
+ // Delete
+ START_TIMER;
+ tellThreads(pThreads, stDelete);
+ waitForThreads(pThreads);
+
+ STOP_TIMER;
+ if (checkThreadResults(pThreads, "delete") != 0) {
+ returnValue = NDBT_FAILED;
+ break;
+ } // if
+ PRINT_TIMER("delete", tNoOfOperations*tNoOfThreads, tNoOfTables);
+ resetThreads(pThreads);
+ } // if
+ else {
+ resetThreads(pThreads); // Scan delete
+ START_TIMER;
+ tellThreads(pThreads, stScanDelete);
+ waitForThreads(pThreads);
+
+ STOP_TIMER;
+ if (checkThreadResults(pThreads, "scandelete") != 0) {
+ returnValue = NDBT_FAILED;
+ break;
+ } // if
+ PRINT_TIMER("scandelete", tNoOfTables*tNoOfThreads, 1);
+
+ resetThreads(pThreads);
+ } // else
+ every2ndScanDelete++;
+
+ resetThreads(pThreads); // Verify delete
+ START_TIMER;
+ tellThreads(pThreads, stVerifyDelete);
+ waitForThreads(pThreads);
+
+ STOP_TIMER;
+ if (checkThreadResults(pThreads, "verifydelete") != 0) {
+ returnValue = NDBT_FAILED;
+ break;
+ } // if
+ PRINT_TIMER("verifydelete", tNoOfOperations*tNoOfThreads*tNoOfTables, 1);
+
+ resetThreads(pThreads);
+
+ ndbout << "--------------------------------------------------" << endl;
+ tLoops++;
+
+ } // while
+ } // if
+ } // else
+ } // else
+
+ // Stop threads in a nice way
+ tellThreads(pThreads, stStop);
+ waitForThreads(pThreads);
+
+ // Clean up
+ delete [] pThreads;
+ delete pMyNdb;
+
+ flexScanErrorData->printErrorCounters(ndbout);
+
+ if (returnValue == NDBT_OK) {
+ ndbout << endl << "Benchmark completed successfully" << endl;
+ } // if
+ else {
+ ndbout << endl << "Benchmark failed" << endl;
+ } // else
+
+ // Exit via NDBT
+ return NDBT_ProgramExit(returnValue);;
+} // main
+
+void*
+flexScanThread(void* ThreadData)
+{
+ ThreadNdb* pThreadData = (ThreadNdb*)ThreadData;
+ unsigned int thread_no = pThreadData->ThreadNo;
+ unsigned int thread_base = (thread_no * 2000000) + (tNodeId * 26000);
+ int tThreadResult = 0;
+ Ndb* MyNdb = NULL;
+ int check = 0;
+ StartType tType = stLast;
+ int* pkValue = NULL;
+ int* attrValue = NULL;
+ int* readValue = NULL;
+ int AllocSize = 0;
+
+ AllocSize = tNoOfTables * (tNoOfAttributes-1) * tNoOfOperations *
+ tAttributeSize * sizeof(int);
+ attrValue = (int*)malloc(AllocSize);
+ readValue = (int*)malloc(AllocSize);
+ pkValue = (int*)malloc(tNoOfOperations * sizeof(int));
+ if ((attrValue == NULL) || (readValue == NULL) || (pkValue == NULL)) {
+ tThreadResult = 98;
+ pThreadData->threadStart = stIdle;
+ } // if
+
+ setAttrValues(attrValue, readValue, thread_base);
+
+ MyNdb = new Ndb( "TEST_DB" );
+ MyNdb->init();
+ if (MyNdb->waitUntilReady(10000) != 0) {
+ tThreadResult = 99;
+ pThreadData->threadStart = stIdle;
+ } // if
+
+ // Set primary key value, same for all tables
+ for (int c = 0; c < tNoOfOperations; c++) {
+ pkValue[c] = (int)(c + thread_base);
+ } // for
+
+ while (FOREVER) {
+ pThreadData->threadResult = tThreadResult;
+ pThreadData->threadReady = 1;
+
+ while (pThreadData->threadStart == stIdle) {
+ NdbSleep_MilliSleep(10);
+ } // while
+
+ // Check if signal to exit is received
+ if (pThreadData->threadStart >= stStop){
+ pThreadData->threadReady = 1;
+ break;
+ } // if
+ tType = pThreadData->threadStart;
+ pThreadData->threadStart = stIdle;
+
+ switch (tType) {
+ case stInsert:
+ check = insertRows(MyNdb, pkValue, attrValue, tType);
+ break;
+ case stRead:
+ check = readRows(MyNdb, pkValue, readValue);
+ Compare(attrValue, readValue);
+ break;
+ case stUpdate:
+ UpdateArray(attrValue);
+ check = insertRows(MyNdb, pkValue, attrValue, tType);
+ break;
+ case stScanRead:
+ //check = readRows(MyNdb, pkValue, readValue);
+ check = scanReadRows(MyNdb, readValue);
+ Compare(attrValue, readValue);
+ break;
+ case stScanUpdate:
+ UpdateArray(attrValue);
+ //tType = stUpdate;
+ //check = insertRows(MyNdb, pkValue, attrValue, tType);
+ check = scanUpdateRows(MyNdb, readValue, attrValue);
+ break;
+ case stDelete:
+ check = deleteRows(MyNdb, pkValue);
+ break;
+ case stScanDelete:
+ check = scanDeleteRows(MyNdb, readValue);
+ break;
+ case stVerifyDelete:
+ check = verifyDeleteRows(MyNdb, pkValue, readValue);
+ break;
+ default:
+ ndbout << "tType is " << tType << endl;
+ assert(false);
+ break;
+ } // switch
+
+ tThreadResult = check;
+
+ if (tThreadResult != 0) {
+ // Check if error is fatak or not
+ } // if
+ else {
+ continue;
+ } // else
+ } // while
+
+ // Clean up
+ delete MyNdb;
+ if (attrValue != NULL) {
+ free(attrValue);
+ } //if
+ if (readValue != NULL) {
+ free(readValue);
+ } // if
+ if (pkValue != NULL) {
+ free(pkValue);
+ } // if
+
+ return NULL; // thread exits
+
+} // flexScanThread
+
+
+static int setAttrNames()
+{
+ int i = 0;
+ int retVal = 0;
+
+ for (i = 0; i < MAXATTR ; i++) {
+ retVal = BaseString::snprintf(attrName[i], MAXSTRLEN, "COL%d", i);
+ if (retVal < 0) {
+ return(-1);
+ } // if
+ } // for
+
+ return(0);
+} // setAttrNames
+
+
+static int setTableNames()
+{
+ // Note! Uses only uppercase letters in table name's
+ // so that we can look at the tables with SQL
+ int i = 0;
+ int retVal = 0;
+
+ for (i = 0; i < MAXTABLES ; i++) {
+
+ if (theStdTableNameFlag == 0) {
+ retVal = BaseString::snprintf(tableName[i], MAXSTRLEN, "TAB%d_%d", i,
+ (int)(NdbTick_CurrentMillisecond() / 1000));
+ } // if
+ else {
+ retVal = BaseString::snprintf(tableName[i], MAXSTRLEN, "TAB%d", i);
+ } // if else
+
+ if (retVal < 0) {
+ return(-1);
+ } // if
+ } // for
+
+ return(0);
+} // setTableNames
+
+
+// Create Table and Attributes.
+static int createTables(Ndb* pMyNdb)
+{
+
+ NdbSchemaCon *MySchemaTransaction = NULL;
+ NdbSchemaOp *MySchemaOp = NULL;
+ int i = 0;
+ int j = 0;
+ int check = 0;
+
+ if (theTableCreateFlag == 0) {
+
+ i = 0;
+ do {
+ i++;
+ ndbout << endl << "Creating " << tableName[i - 1] << "..." << endl;
+
+ MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pMyNdb);
+ if( MySchemaTransaction == NULL ) {
+ return (-1);
+ } // if
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL ) {
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return (-1);
+ } // if
+
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ check = MySchemaOp->createTable(tableName[i - 1],
+ 8, // Table Size
+ TupleKey, // Key Type
+ 40, // Nr of Pages
+ All,
+ 6,
+ 78,
+ 80,
+ 1,
+ false);
+#else
+ check = MySchemaOp->createTable(tableName[i - 1]
+ ,8 // Table Size
+ ,TupleKey // Key Type
+ ,40); // Nr of Pages
+#endif
+ if (check == -1) {
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return -1;
+ } // if
+
+ check = MySchemaOp->createAttribute( (char*)attrName[0], TupleKey, 32, PKSIZE,
+ UnSigned, MMBased, NotNullAttribute );
+ if (check == -1) {
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return -1;
+ } // if
+
+ for (j = 1; j < tNoOfAttributes ; j++) {
+ check = MySchemaOp->createAttribute( (char*)attrName[j], NoKey, 32, tAttributeSize,
+ UnSigned, MMBased, NotNullAttribute );
+ if (check == -1) {
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return -1;
+ } // if
+ } // for
+
+ if (MySchemaTransaction->execute() == -1) {
+ ndbout << MySchemaTransaction->getNdbError().message << endl;
+ ndbout << "Probably, " << tableName[i - 1] << " already exist" << endl;
+ } // if
+
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ } while (tNoOfTables > i);
+ }
+
+ return 0;
+} // createTables
+
+static void printUsage()
+{
+ ndbout << "Usage of flexScan:" << endl;
+ ndbout << "-f <path> Location of Ndb.cfg file, default: Ndb.cfg" << endl;
+ ndbout << "-t <int> Number of threads to start, default 1" << endl;
+ ndbout << "-o <int> Number of operations per loop, default 500" << endl;
+ ndbout << "-l <int> Number of loops to run, default 1, 0=infinite" << endl;
+ ndbout << "-a <int> Number of attributes, default 25" << endl;
+ ndbout << "-c <int> Number of tables, default 1" << endl;
+ ndbout << "-s <int> Size of each attribute, default 1" << endl;
+ ndbout << "-stdtables Use standard table names" << endl;
+ ndbout << "-no_table_create Don't create tables in db" << endl;
+ ndbout << "-sleep <int> Sleep a number of seconds before running the test" << endl;
+ ndbout << "-p <int> Parallellism to use 1-32, default:1" << endl;
+ ndbout << "-abort <int> Test scan abort after a number of tuples" << endl;
+ ndbout << "-no_scan_update Don't do scan updates" << endl;
+ ndbout << "-no_scan_delete Don't do scan deletes" << endl;
+ ndbout << "-h Print this text" << endl;
+ // inputErrorArg();
+ flexScanErrorData->printCmdLineArgs(ndbout);
+}
+
+static int readArguments(int argc, const char** argv)
+{
+ int i = 1;
+ int retValue = 0;
+ int printFlag = 0;
+
+ tNoOfThreads = 1; // Set default Value
+ tNoOfTables = 1; // Default Value
+
+ while (argc > 1) {
+ if (strcmp(argv[i], "-t") == 0) {
+ if (argv[i + 1] != NULL) {
+ tNoOfThreads = atoi(argv[i + 1]);
+ if ((tNoOfThreads < 1) || (tNoOfThreads > MAXTHREADS)) {
+ retValue = -1;
+ } // if
+ } // if
+ else {
+ retValue = -1;
+ } // else
+ } // if
+ else if (strcmp(argv[i], "-o") == 0) {
+ if (argv[i + 1] != NULL) {
+ tNoOfOperations = atoi(argv[i + 1]);
+ if (tNoOfOperations < 1) {
+ retValue = -1;
+ } // if
+ } // if
+ else {
+ retValue = -1;
+ } // else
+ } // else if
+ else if (strcmp(argv[i], "-a") == 0) {
+ if (argv[i + 1] != NULL) {
+ tNoOfAttributes = atoi(argv[i + 1]);
+ if ((tNoOfAttributes < 2) || (tNoOfAttributes > MAXATTR)) {
+ retValue = -1;
+ } // if
+ } // if
+ else {
+ retValue = -1;
+ } // else
+ } // else if
+ else if (strcmp(argv[i], "-c") == 0) {
+ if (argv[i + 1] != NULL) {
+ tNoOfTables = atoi(argv[i+1]);
+ if ((tNoOfTables < 1) || (tNoOfTables > MAXTABLES)) {
+ retValue = -1;
+ } // if
+ } // if
+ else {
+ retValue = -1;
+ } // else
+ } // else if
+ else if (strcmp(argv[i], "-l") == 0) {
+ if (argv[i + 1] != NULL) {
+ tNoOfLoops = atoi(argv[i+1]);
+ if ((tNoOfLoops < 0) || (tNoOfLoops > 100000)) {
+ retValue = -1;
+ } // if
+ } // if
+ else {
+ retValue = -1;
+ } // else
+ } // else if
+ else if (strcmp(argv[i], "-s") == 0) {
+ if (argv[i + 1] != NULL) {
+ tAttributeSize = atoi(argv[i+1]);
+ if ((tAttributeSize < 1) || (tAttributeSize > MAXATTRSIZE)) {
+ retValue = -1;
+ } // if
+ } // if
+ else {
+ retValue = -1;
+ } // else
+ } // else if
+ else if (strcmp(argv[i], "-no_table_create") == 0) {
+ theTableCreateFlag = 1;
+ argc++;
+ i--;
+ } // else if
+ else if (strcmp(argv[i], "-stdtables") == 0) {
+ theStdTableNameFlag = 1;
+ argc++;
+ i--;
+ } // else if
+ else if (strcmp(argv[i], "-sleep") == 0) {
+ if (argv[i + 1] != NULL) {
+ tSleepTime = atoi(argv[i+1]);
+ if ((tSleepTime < 1) || (tSleepTime > 3600)) {
+ retValue = -1;
+ } // if
+ } // if
+ else {
+ retValue = -1;
+ } // else
+ } // else if
+ else if (strcmp(argv[i], "-abort") == 0) {
+ // Test scan abort after a number of tuples
+ theScanAbortTestFlag = 1;
+ if (argv[i + 1] != NULL) {
+ tAbortAfter = atoi(argv[i + 1]);
+ } // if
+ else {
+ retValue = -1;
+ } // else
+ } // else if
+ else if (strcmp(argv[i], "-p") == 0) {
+ if (argv[i + 1] != NULL) {
+ tParallellism = atoi(argv[i + 1]);
+ if ((tParallellism < 1) || (tParallellism > 32)) {
+ retValue = -1;
+ } // if
+ } // if
+ else {
+ retValue = -1;
+ } // else
+ } // else if
+ else if (strcmp(argv[i], "-h") == 0) {
+ printFlag = 1;
+ argc++;
+ i--;
+ } // else if
+ else if (strcmp(argv[i], "-no_scan_update") == 0) {
+ theNoScanUpdateFlag = 1;
+ argc++;
+ i--;
+ } // else if
+ else if (strcmp(argv[i], "-no_scan_delete") == 0) {
+ theNoScanDeleteFlag = 1;
+ argc++;
+ i--;
+ } // else if
+ else {
+ retValue = -1;
+ } // else
+
+ argc -= 2;
+ i = i + 2;
+ }
+
+ if ((retValue != 0) || (printFlag == 1)) {
+ printUsage();
+ } // if
+
+ return(retValue);
+
+} // readArguments
+
+static void sleepBeforeStartingTest(int seconds)
+{
+ if (seconds > 0) {
+ ndbout << "Sleeping(" <<seconds << ")...";
+ NdbSleep_SecSleep(seconds);
+ ndbout << " done!" << endl;
+ } // if
+} // sleepBeforeStartingTest
+
+static void setAttrValues(int* attrValue,
+ int* readValue,
+ int Offset)
+{
+ int tableCount = 0;
+ int attrCount = 0;
+ int OpCount = 0;
+ int attrSize = 0;
+ int* pAttr = NULL;
+ int* pRead = NULL;
+
+ // Set attribute values in memory array
+ for (tableCount = 0; tableCount < tNoOfTables; tableCount++) {
+ for (attrCount = 0; attrCount < tNoOfAttributes-1; attrCount++) {
+ for (OpCount = 0; OpCount < tNoOfOperations; OpCount++) {
+ pAttr = &(attrValue[tableCount*(tNoOfAttributes-1)*tNoOfOperations +
+ attrCount*tNoOfOperations + OpCount]);
+ pRead = &(readValue[tableCount*(tNoOfAttributes-1)*tNoOfOperations +
+ attrCount*tNoOfOperations + OpCount]);
+ for (attrSize = 0; attrSize < tAttributeSize; attrSize++){
+ *pAttr = (int)(Offset + tableCount + attrCount + OpCount + attrSize);
+ //ndbout << "attrValue[" << tableCount*(tNoOfAttributes-1)*tNoOfOperations +
+ //attrCount*tNoOfOperations + OpCount + attrSize << "] = " <<
+ //attrValue[tableCount*(tNoOfAttributes-1)*tNoOfOperations +
+ //attrCount*tNoOfOperations + OpCount + attrSize] << endl;
+ *pRead = 0;
+ pAttr++;
+ pRead++;
+ } // for attrSize
+ } // for OpCount
+ } // for attrCount
+ } // for tableCount
+
+} // setAttrValues
+
+static int insertRows(Ndb* pNdb, // NDB object
+ int* pkValue, // Primary key values
+ int* attrValue, // Attribute values
+ StartType tType)
+{
+ int tResult = 0;
+ int check = 0;
+ int tableCount = 0;
+ int attrCount = 0;
+ NdbConnection* MyTransaction = NULL;
+ NdbOperation* MyOperations[MAXTABLES] = {NULL};
+ int opCount = 0;
+
+ for (opCount = 0; opCount < tNoOfOperations; opCount++) {
+ MyTransaction = pNdb->startTransaction();
+ if (MyTransaction == NULL) {
+ tResult = 1;
+ } // if
+ else {
+ for (tableCount = 0; tableCount < tNoOfTables; tableCount++) {
+
+ MyOperations[tableCount] =
+ MyTransaction->getNdbOperation(tableName[tableCount]);
+ if (MyOperations[tableCount] == NULL) {
+ tResult = 2;
+ // Break for tableCount loop
+ break;
+ } // if
+
+ if (tType == stUpdate) {
+ check = MyOperations[tableCount]->updateTuple();
+ } // if
+ else if (tType == stInsert) {
+ check = MyOperations[tableCount]->insertTuple();
+ } // else if
+ else {
+ assert(false);
+ } // else
+
+ if (check == -1) {
+ tResult = 3;
+ break;
+ } // if
+ check = MyOperations[tableCount]->equal((char*)attrName[0],
+ (char*)&(pkValue[opCount]));
+ if (check == -1) {
+ tResult = 7;
+ break;
+ } // if
+
+ for (attrCount = 0; attrCount < tNoOfAttributes - 1; attrCount++) {
+ int Index = tableCount * (tNoOfAttributes - 1) * tNoOfOperations * tAttributeSize +
+ attrCount * tNoOfOperations * tAttributeSize + opCount * tAttributeSize;
+ check = MyOperations[tableCount]->
+ setValue((char*)attrName[attrCount + 1],
+ (char*)&(attrValue[Index]));
+ if (check == -1) {
+ tResult = 8;
+ break; // break attrCount loop
+ } // if
+ } // for
+ } // for tableCount
+
+ // Execute transaction with insert one tuple in every table
+ check = MyTransaction->execute(Commit);
+ if (check == -1) {
+ ndbout << MyTransaction->getNdbError().message << endl;
+
+ // Add complete error handling here
+
+ int retCode = flexScanErrorData->handleErrorCommon(MyTransaction->getNdbError());
+ if (retCode == 1) {
+ if (MyTransaction->getNdbError().code != 626 && MyTransaction->getNdbError().code != 630){
+ ndbout_c("execute: %d, %d, %s", opCount, tType, MyTransaction->getNdbError().message);
+ ndbout_c("Error code = %d", MyTransaction->getNdbError().code);}
+ tResult = 20;
+ } else if (retCode == 2) {
+ ndbout << "4115 should not happen in flexBench" << endl;
+ tResult = 20;
+ } else if (retCode == 3) {
+// --------------------------------------------------------------------
+// We are not certain if the transaction was successful or not.
+// We must reexecute but might very well find that the transaction
+// actually was updated. Updates and Reads are no problem here. Inserts
+// will not cause a problem if error code 630 arrives. Deletes will
+// not cause a problem if 626 arrives.
+// --------------------------------------------------------------------
+ /* What can we do here? */
+ ndbout_c("execute: %s", MyTransaction->getNdbError().message);
+ }//if(retCode == 3)
+
+ } // if(check == -1)
+
+ pNdb->closeTransaction(MyTransaction);
+ } // else
+ } // for opCount
+
+ return(tResult);
+} // insertRows
+
+static int readRows(Ndb* pNdb,
+ int* pkValue,
+ int* readValue)
+{
+ int tResult = 0;
+ int tableCount = 0;
+ int attrCount = 0;
+ int check = 0;
+ NdbConnection* MyTransaction = NULL;
+ NdbOperation* MyOperations[MAXTABLES] = {NULL};
+ NdbRecAttr* tmp = NULL;
+ int Value = 0;
+ int Index = 0;
+ int opCount = 0;
+
+ for (opCount = 0; opCount < tNoOfOperations; opCount++) {
+ MyTransaction = pNdb->startTransaction();
+ if (MyTransaction == NULL) {
+ tResult = 1;
+ } // if
+ else {
+ for (tableCount = 0; tableCount < tNoOfTables; tableCount++) {
+
+ MyOperations[tableCount] =
+ MyTransaction->getNdbOperation(tableName[tableCount]);
+ if (MyOperations[tableCount] == NULL) {
+ tResult = 2;
+ // Break for tableCount loop
+ break;
+ } // if
+
+ check = MyOperations[tableCount]->readTuple();
+ if (check == -1) {
+ tResult = 3;
+ break;
+ } // if
+
+ check = MyOperations[tableCount]->
+ equal((char*)attrName[0], (char*)&(pkValue[opCount]));
+ if (check == -1) {
+ tResult = 7;
+ break;
+ } // if
+
+ for (int attrCount = 0; attrCount < tNoOfAttributes - 1; attrCount++) {
+ Index = tableCount * (tNoOfAttributes - 1) * tNoOfOperations * tAttributeSize +
+ attrCount * tNoOfOperations * tAttributeSize + opCount * tAttributeSize;
+ tmp = MyOperations[tableCount]->
+ getValue((char*)attrName[attrCount + 1], (char*)&(readValue[Index]));
+
+ if (tmp == NULL) {
+ tResult = 9;
+ break;
+ } // if
+ } // for attrCount
+ } // for tableCount
+ // Execute transaction reading one tuple in every table
+ check = MyTransaction->execute(Commit);
+ if (check == -1) {
+ ndbout << MyTransaction->getNdbError().message << endl;
+
+ // Add complete error handling here
+
+ int retCode = flexScanErrorData->handleErrorCommon(MyTransaction->getNdbError());
+ if (retCode == 1) {
+ if (MyTransaction->getNdbError().code != 626 && MyTransaction->getNdbError().code != 630){
+ ndbout_c("execute: %d, %s", opCount, MyTransaction ->getNdbError().message );
+ ndbout_c("Error code = %d", MyTransaction->getNdbError().code );}
+ tResult = 20;
+ } else if (retCode == 2) {
+ ndbout << "4115 should not happen in flexBench" << endl;
+ tResult = 20;
+ } else if (retCode == 3) {
+// --------------------------------------------------------------------
+// We are not certain if the transaction was successful or not.
+// We must reexecute but might very well find that the transaction
+// actually was updated. Updates and Reads are no problem here. Inserts
+// will not cause a problem if error code 630 arrives. Deletes will
+// not cause a problem if 626 arrives.
+// --------------------------------------------------------------------
+ /* What can we do here? */
+ ndbout_c("execute: %s", MyTransaction ->getNdbError().message );
+ }//if(retCode == 3)
+
+ } // if
+
+ pNdb->closeTransaction(MyTransaction);
+ } // else
+ } // for opCount
+
+ return(tResult);
+} // readRows
+
+static int scanReadRows(Ndb* pNdb, int* readValue)
+{
+ int tResult = 0;
+ int tableCount = 0;
+ int attrCount = 0;
+ int check = 0;
+ int countAbort = 0; // Counts loops until scan abort if requested
+ NdbConnection* MyTransaction = NULL;
+ NdbOperation* MyOperation = NULL;
+ NdbRecAttr* tmp = NULL;
+
+
+ for (tableCount = 0; tableCount < tNoOfTables; tableCount++) {
+ MyTransaction = pNdb->startTransaction();
+ if (MyTransaction == NULL) {
+ tResult = 1;
+ break;
+ } // if
+ MyOperation = MyTransaction->getNdbOperation(tableName[tableCount]);
+ if (MyOperation == NULL) {
+ tResult = 2;
+ break;
+ } // if
+
+ check = MyOperation->openScanRead(tParallellism);
+ if (check == -1) {
+ tResult = 10;
+ break;
+ } // if
+
+ for (int attrCount = 0; attrCount < tNoOfAttributes-1; attrCount++) {
+ // Get all attributes
+ tmp = MyOperation->
+ getValue((char*)attrName[attrCount+1],
+ (char*)&(readValue[tableCount*(tNoOfAttributes-1)*tNoOfOperations*tAttributeSize +
+ attrCount*tNoOfOperations*tAttributeSize]));
+ if (tmp == NULL) {
+ tResult = 9;
+ break;
+ } // if
+ } // for attrCount
+
+ check = MyTransaction->executeScan();
+ if (check == -1) {
+ tResult = 12;
+ break;
+ } // if
+
+ check = MyTransaction->nextScanResult();
+ while (check == 0) {
+ // Check if scan abort is requested
+ if (theScanAbortTestFlag == 1) {
+ if (countAbort == tAbortAfter) {
+ MyTransaction->stopScan();
+ ndbout << "scanread aborted on request after " << countAbort*tParallellism <<
+ " tuples" << endl;
+ break; // break while loop
+ } // if
+ countAbort++;
+ } // if
+ check = MyTransaction->nextScanResult();
+ } // while
+
+ pNdb->closeTransaction(MyTransaction);
+ } // for tableCount
+
+ return(tResult);
+} // scanReadRows
+
+static int scanUpdateRows(Ndb* pNdb,
+ int* readValue,
+ int* attrValue)
+{
+ int tResult = 0;
+ int tableCount = 0;
+ int attrCount = 0;
+ int check = 0;
+ int opCount = 0;
+ NdbConnection* MyTransaction = NULL;
+ NdbOperation* MyOperation = NULL;
+ NdbConnection* MyTakeOverTrans = NULL;
+ NdbOperation* MyTakeOverOp = NULL;
+ NdbRecAttr* tTmp = NULL;
+
+ for (tableCount = 0; tableCount < tNoOfTables; tableCount++) {
+ MyTransaction = pNdb->startTransaction();
+ if (MyTransaction == NULL) {
+ tResult = 1;
+ break; // break tableCount for loop
+ } // if
+ MyOperation = MyTransaction->getNdbOperation(tableName[tableCount]);
+ if (MyOperation == NULL) {
+ tResult = 2;
+ break;
+ } // if
+
+ check = MyOperation->openScanExclusive(tParallellism);
+ if (check == -1) {
+ tResult = 11;
+ break;
+ } // if
+
+ MyOperation->interpret_exit_ok();
+ // Fetch all attributes
+ for (int attrCount = 0; attrCount < tNoOfAttributes-1; attrCount++) {
+ tTmp = MyOperation->
+ getValue((char*)attrName[attrCount+1],
+ (char*)&(readValue[tableCount*(tNoOfAttributes-1)*tNoOfOperations*tAttributeSize +
+ attrCount*tNoOfOperations*tAttributeSize]));
+ if (tTmp == NULL) {
+ tResult = 9;
+ break; // break for loop
+ } // if
+ } // for
+ if (tResult != 0) {
+ break; // break while loop also
+ } // if
+
+ check = MyTransaction->executeScan();
+ if (check == -1) {
+ tResult = 12;
+ break;
+ } // if
+ check = MyTransaction->nextScanResult();
+ opCount = 0;
+ while (check == 0) {
+ MyTakeOverTrans = pNdb->startTransaction();
+ MyTakeOverOp = MyOperation->takeOverForUpdate(MyTakeOverTrans);
+ for (attrCount = 0; attrCount < tNoOfAttributes-1; attrCount++) {
+ check = MyTakeOverOp->setValue((char*)attrName[attrCount+1],
+ (char*)&(attrValue[tableCount*(tNoOfAttributes-1)*tNoOfOperations*tAttributeSize +
+ attrCount*tNoOfOperations*tAttributeSize + opCount*tAttributeSize]));
+ } // for
+
+ check = MyTakeOverTrans->execute(Commit);
+ if (check == 0) {
+ check = MyTransaction->nextScanResult();
+ opCount++;
+ } // if
+ else {
+ tResult = 95;
+
+ /* MyTransaction, MyTakeOverTrans, Which one? */
+
+ // Any further error handling?
+ int retCode = flexScanErrorData->handleErrorCommon(MyTakeOverTrans->getNdbError());
+ if (retCode == 1) {
+ if (MyTakeOverTrans->getNdbError().code != 626 && MyTakeOverTrans->getNdbError().code != 630){
+ ndbout_c("execute: %s", MyTakeOverTrans->getNdbError().message);
+ ndbout_c("Error code = %d", MyTakeOverTrans->getNdbError().code);}
+ tResult = 20;
+ } else if (retCode == 2) {
+ ndbout << "4115 should not happen in flexBench" << endl;
+ tResult = 20;
+ } else if (retCode == 3) {
+ // --------------------------------------------------------------------
+ // We are not certain if the transaction was successful or not.
+ // We must reexecute but might very well find that the transaction
+ // actually was updated. Updates and Reads are no problem here. Inserts
+ // will not cause a problem if error code 630 arrives. Deletes will
+ // not cause a problem if 626 arrives.
+ // --------------------------------------------------------------------
+ /* What can we do here? */
+ ndbout_c("execute: %s", MyTakeOverTrans->getNdbError().message);
+ }//if(retCode == 3)
+
+ } // else
+ pNdb->closeTransaction(MyTakeOverTrans);
+ } // while
+
+ pNdb->closeTransaction(MyTransaction);
+ } // for
+
+ return(tResult);
+} // scanUpdateRows
+
+static int scanDeleteRows(Ndb* pNdb, int* readValue)
+{
+ int tResult = 0;
+ int tableCount = 0;
+ int attrCount = 0;
+ int check = 0;
+ NdbRecAttr* tTmp = NULL;
+ NdbConnection* MyTransaction = NULL;
+ NdbOperation* MyOperation = NULL;
+ NdbConnection* MyTakeOverTrans = NULL;
+ NdbOperation* MyTakeOverOp = NULL;
+
+ for (tableCount = 0; tableCount < tNoOfTables; tableCount++) {
+ MyTransaction = pNdb->startTransaction();
+ if (MyTransaction == NULL) {
+ tResult = 1;
+ break; // break tableCount for loop
+ } // if
+
+ MyOperation = MyTransaction->getNdbOperation(tableName[tableCount]);
+ if (MyOperation == NULL) {
+ tResult = 2;
+ break;
+ } // if
+
+ check = MyOperation->openScanExclusive(tParallellism);
+ if (check == -1) {
+ tResult = 11;
+ break;
+ } // if
+
+ MyOperation->interpret_exit_ok();
+ for (int attrCount = 0; attrCount < tNoOfAttributes-1; attrCount++) {
+ tTmp = MyOperation->
+ getValue((char*)attrName[attrCount+1],
+ (char*)&(readValue[tableCount*(tNoOfAttributes-1)*tNoOfOperations +
+ attrCount*tNoOfOperations]));
+ if (tTmp == NULL) {
+ tResult = 9;
+ break;
+ } // if
+ } // for
+
+ check = MyTransaction->executeScan();
+ if (check == -1) {
+ tResult = 12;
+ break;
+ } // if
+ check = MyTransaction->nextScanResult();
+ while (check == 0) {
+ MyTakeOverTrans = pNdb->startTransaction();
+ MyTakeOverOp = MyOperation->takeOverForDelete(MyTakeOverTrans);
+ check = MyTakeOverOp->deleteTuple();
+
+ check = MyTakeOverTrans->execute(Commit);
+
+ //Error handling here
+
+ int retCode =flexScanErrorData->handleErrorCommon(MyTakeOverTrans->getNdbError());
+ if (retCode == 1) {
+ if (MyTakeOverTrans->getNdbError().code != 626 && MyTakeOverTrans->getNdbError().code != 630){
+ ndbout_c("execute: %s", MyTakeOverTrans->getNdbError().message );
+ ndbout_c("Error code = %d", MyTakeOverTrans->getNdbError().code );}
+ tResult = 20;
+ } else if (retCode == 2) {
+ ndbout << "4115 should not happen in flexBench" << endl;
+ tResult = 20;
+ } else if (retCode == 3) {
+// --------------------------------------------------------------------
+// We are not certain if the transaction was successful or not.
+// We must reexecute but might very well find that the transaction
+// actually was updated. Updates and Reads are no problem here. Inserts
+// will not cause a problem if error code 630 arrives. Deletes will
+// not cause a problem if 626 arrives.
+// --------------------------------------------------------------------
+ /* What can we do here? */
+ ndbout_c("execute: %s", MyTakeOverTrans->getNdbError().message );
+ }//if(retCode == 3) End of error handling
+
+ pNdb->closeTransaction(MyTakeOverTrans);
+ check = MyTransaction->nextScanResult();
+ } // while
+ pNdb->closeTransaction(MyTransaction);
+ } // for tableCount
+ return(tResult);
+} // scanDeleteRows
+
+static int deleteRows(Ndb* pNdb,
+ int* pkValue)
+{
+ int tResult = 0;
+ NdbConnection* MyTransaction = NULL;
+ int tableCount = 0;
+ int opCount = 0;
+ int check = 0;
+ NdbOperation* MyOperations[MAXTABLES] = {NULL};
+
+ for (opCount = 0; opCount < tNoOfOperations; opCount++) {
+ MyTransaction = pNdb->startTransaction();
+ if (MyTransaction == NULL) {
+ tResult = 1;
+ } // if
+ else {
+ for (tableCount = 0; tableCount < tNoOfTables; tableCount++) {
+
+ MyOperations[tableCount] =
+ MyTransaction->getNdbOperation(tableName[tableCount]);
+ if (MyOperations[tableCount] == NULL) {
+ tResult = 2;
+ // Break for tableCount loop
+ break;
+ } // if
+
+ check = MyOperations[tableCount]->deleteTuple();
+ if (check == -1) {
+ tResult = 3;
+ break;
+ } // if
+
+ check = MyOperations[tableCount]->
+ equal((char*)attrName[0], (char*)&(pkValue[opCount]));
+ if (check == -1) {
+ tResult = 7;
+ break;
+ } // if
+
+ } // for tableCount
+
+ // Execute transaction deleting one tuple in every table
+ check = MyTransaction->execute(Commit);
+ if (check == -1) {
+ ndbout << MyTransaction->getNdbError().message << endl;
+ // Add complete error handling here
+
+ int retCode = flexScanErrorData->handleErrorCommon(MyTransaction->getNdbError());
+ if (retCode == 1) {
+ if (MyTransaction->getNdbError().code != 626 && MyTransaction->getNdbError().code != 630){
+ ndbout_c("execute: %d, %s", opCount, MyTransaction->getNdbError().message );
+ ndbout_c("Error code = %d", MyTransaction->getNdbError().code );}
+ tResult = 20;
+ } else if (retCode == 2) {
+ ndbout << "4115 should not happen in flexBench" << endl;
+ tResult = 20;
+ } else if (retCode == 3) {
+// --------------------------------------------------------------------
+// We are not certain if the transaction was successful or not.
+// We must reexecute but might very well find that the transaction
+// actually was updated. Updates and Reads are no problem here. Inserts
+// will not cause a problem if error code 630 arrives. Deletes will
+// not cause a problem if 626 arrives.
+// --------------------------------------------------------------------
+ /* What can we do here? */
+ ndbout_c("execute: %s", MyTransaction->getNdbError().message );
+ }//if(retCode == 3)
+
+ } // if
+
+ pNdb->closeTransaction(MyTransaction);
+ } // else
+ } // for opCount
+
+ return(tResult);
+
+} // deleteRows
+
+////////////////////////////////////////
+//
+// Name: verifyDeleteRows
+//
+// Purpose: Verifies that all tables are empty by reading every tuple
+// No deletions made here
+//
+// Returns: 'Standard' error codes
+//
+/////////////////////////////////////
+static int verifyDeleteRows(Ndb* pNdb,
+ int* pkValue,
+ int* readValue)
+{
+ int tResult = 0;
+ int tableCount = 0;
+ int attrCount = 0;
+ int check = 0;
+ NdbConnection* MyTransaction = NULL;
+ NdbOperation* MyOperations = NULL;
+ NdbRecAttr* tmp = NULL;
+ int Value = 0;
+ int Index = 0;
+ int opCount = 0;
+
+ for (opCount = 0; opCount < tNoOfOperations; opCount++) {
+ for (tableCount = 0; tableCount < tNoOfTables; tableCount++) {
+ MyTransaction = pNdb->startTransaction();
+ if (MyTransaction == NULL) {
+ tResult = 1;
+ } // if
+ else {
+
+ MyOperations =
+ MyTransaction->getNdbOperation(tableName[tableCount]);
+ if (MyOperations == NULL) {
+ tResult = 2;
+ // Break for tableCount loop
+ break;
+ } // if
+
+ check = MyOperations->readTuple();
+ if (check == -1) {
+ tResult = 3;
+ break;
+ } // if
+
+ check = MyOperations->
+ equal((char*)attrName[0], (char*)&(pkValue[opCount]));
+ if (check == -1) {
+ tResult = 7;
+ break;
+ } // if
+
+ for (int attrCount = 0; attrCount < tNoOfAttributes - 1; attrCount++) {
+ Index = tableCount * (tNoOfAttributes - 1) * tNoOfOperations * tAttributeSize +
+ attrCount * tNoOfOperations * tAttributeSize + opCount * tAttributeSize;
+ tmp = MyOperations->
+ getValue((char*)attrName[attrCount + 1], (char*)&(readValue[Index]));
+
+ if (tmp == NULL) {
+ tResult = 9;
+ break;
+ } // if
+ } // for attrCount
+ // Execute transaction reading one tuple in every table
+ check = MyTransaction->execute(Commit);
+ if ((check == -1) && (MyTransaction->getNdbError().code == 626)){
+ // This is expected because everything should be deleted
+ } // if
+ else if (check == 0) {
+ // We have found a tuple that should have been deleted
+ ndbout << "tuple " << tableName[tableCount] << ":" <<
+ opCount << " was never deleted" << endl;
+ tResult = 97;
+ } // else if
+ else {
+ // Unexpected error
+ ndbout << "Unexpected error during delete" << endl;
+ assert(false);
+ } // else
+
+ pNdb->closeTransaction(MyTransaction);
+
+ } // else
+ } // for tableCount
+ } // for opCount
+
+ return(tResult);
+} // verifyDeleteRows
diff --git a/storage/ndb/test/ndbapi/flexTT.cpp b/storage/ndb/test/ndbapi/flexTT.cpp
new file mode 100644
index 00000000000..7cd5ac8e3b4
--- /dev/null
+++ b/storage/ndb/test/ndbapi/flexTT.cpp
@@ -0,0 +1,937 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <NdbApi.hpp>
+#include <NdbSchemaCon.hpp>
+#include <NdbMain.h>
+#include <md5_hash.hpp>
+
+#include <NdbThread.h>
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <NdbOut.hpp>
+#include <NdbTimer.hpp>
+
+#include <NdbTest.hpp>
+#include <NDBT_Error.hpp>
+
+#define MAX_PARTS 4
+#define MAX_SEEK 16
+#define MAXSTRLEN 16
+#define MAXATTR 64
+#define MAXTABLES 64
+#define MAXTHREADS 128
+#define MAXPAR 1024
+#define MAXATTRSIZE 1000
+#define PKSIZE 1
+
+
+#ifdef NDB_WIN32
+inline long lrand48(void) { return rand(); };
+#endif
+
+
+enum StartType {
+ stIdle,
+ stInsert,
+ stRead,
+ stUpdate,
+ stDelete,
+ stStop
+} ;
+
+struct ThreadNdb
+{
+ int threadNo;
+ Ndb* threadNdb;
+ Uint32 threadBase;
+ Uint32 threadLoopCounter;
+ Uint32 threadNextStart;
+ Uint32 threadStop;
+ Uint32 threadLoopStop;
+ Uint32 threadIncrement;
+ Uint32 threadNoCompleted;
+ bool threadCompleted;
+ StartType threadStartType;
+};
+
+struct TransNdb
+{
+ char transRecord[128];
+ Ndb* transNdb;
+ StartType transStartType;
+ Uint32 vpn_number;
+ Uint32 vpn_identity;
+ Uint32 transErrorCount;
+ NdbOperation* transOperation;
+ ThreadNdb* transThread;
+};
+
+extern "C" { static void* threadLoop(void*); }
+static void setAttrNames(void);
+static void setTableNames(void);
+static int readArguments(int argc, const char** argv);
+static int createTables(Ndb*);
+static bool defineOperation(NdbConnection* aTransObject, TransNdb*,
+ Uint32 vpn_nb, Uint32 vpn_id);
+static bool executeTransaction(TransNdb* transNdbRef);
+static StartType random_choice();
+static void execute(StartType aType);
+static bool executeThread(ThreadNdb*, TransNdb*);
+static void executeCallback(int result, NdbConnection* NdbObject,
+ void* aObject);
+static bool error_handler(const NdbError & err) ;
+static Uint32 getKey(Uint32, Uint32) ;
+static void input_error();
+
+ErrorData * flexTTErrorData;
+
+static NdbThread* threadLife[MAXTHREADS];
+static int tNodeId;
+static int ThreadReady[MAXTHREADS];
+static StartType ThreadStart[MAXTHREADS];
+static char tableName[1][MAXSTRLEN+1];
+static char attrName[5][MAXSTRLEN+1];
+
+// Program Parameters
+static bool tInsert = false;
+static bool tDelete = false;
+static bool tReadUpdate = true;
+static int tUpdateFreq = 20;
+static bool tLocal = false;
+static int tLocalPart = 0;
+static int tMinEvents = 0;
+static int tSendForce = 0;
+static int tNoOfLoops = 1;
+static Uint32 tNoOfThreads = 1;
+static Uint32 tNoOfParallelTrans = 32;
+static Uint32 tNoOfTransactions = 500;
+static Uint32 tLoadFactor = 80;
+static bool tempTable = false;
+static bool startTransGuess = true;
+
+//Program Flags
+static int theSimpleFlag = 0;
+static int theDirtyFlag = 0;
+static int theWriteFlag = 0;
+static int theTableCreateFlag = 1;
+
+#define START_REAL_TIME
+#define STOP_REAL_TIME
+#define START_TIMER { NdbTimer timer; timer.doStart();
+#define STOP_TIMER timer.doStop();
+#define PRINT_TIMER(text, trans, opertrans) timer.printTransactionStatistics(text, trans, opertrans); };
+
+static void
+resetThreads(){
+
+ for (int i = 0; i < tNoOfThreads ; i++) {
+ ThreadReady[i] = 0;
+ ThreadStart[i] = stIdle;
+ }//for
+}
+
+static void
+waitForThreads(void)
+{
+ int cont = 0;
+ do {
+ cont = 0;
+ NdbSleep_MilliSleep(20);
+ for (int i = 0; i < tNoOfThreads ; i++) {
+ if (ThreadReady[i] == 0) {
+ cont = 1;
+ }//if
+ }//for
+ } while (cont == 1);
+}
+
+static void
+tellThreads(StartType what)
+{
+ for (int i = 0; i < tNoOfThreads ; i++)
+ ThreadStart[i] = what;
+}
+
+static Ndb_cluster_connection *g_cluster_connection= 0;
+
+NDB_COMMAND(flexTT, "flexTT", "flexTT", "flexTT", 65535)
+{
+ ndb_init();
+ ThreadNdb* pThreadData;
+ int returnValue = NDBT_OK;
+ int i;
+ flexTTErrorData = new ErrorData;
+ flexTTErrorData->resetErrorCounters();
+
+ if (readArguments(argc, argv) != 0){
+ input_error();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ pThreadData = new ThreadNdb[MAXTHREADS];
+
+ ndbout << endl << "FLEXTT - Starting normal mode" << endl;
+ ndbout << "Perform TimesTen benchmark" << endl;
+ ndbout << " " << tNoOfThreads << " number of concurrent threads " << endl;
+ ndbout << " " << tNoOfParallelTrans;
+ ndbout << " number of parallel transaction per thread " << endl;
+ ndbout << " " << tNoOfTransactions << " transaction(s) per round " << endl;
+ ndbout << " " << tNoOfLoops << " iterations " << endl;
+ ndbout << " " << "Update Frequency is " << tUpdateFreq << "%" << endl;
+ ndbout << " " << "Load Factor is " << tLoadFactor << "%" << endl;
+ if (tLocal == true) {
+ ndbout << " " << "We only use Local Part = ";
+ ndbout << tLocalPart << endl;
+ }//if
+ if (tempTable == true) {
+ ndbout << " Tables are without logging " << endl;
+ } else {
+ ndbout << " Tables are with logging " << endl;
+ }//if
+ if (startTransGuess == true) {
+ ndbout << " Transactions are executed with hint provided" << endl;
+ } else {
+ ndbout << " Transactions are executed with round robin scheme" << endl;
+ }//if
+ if (tSendForce == 0) {
+ ndbout << " No force send is used, adaptive algorithm used" << endl;
+ } else if (tSendForce == 1) {
+ ndbout << " Force send used" << endl;
+ } else {
+ ndbout << " No force send is used, adaptive algorithm disabled" << endl;
+ }//if
+
+ ndbout << endl;
+
+ /* print Setting */
+ flexTTErrorData->printSettings(ndbout);
+
+ NdbThread_SetConcurrencyLevel(2 + tNoOfThreads);
+
+ setAttrNames();
+ setTableNames();
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ g_cluster_connection= &con;
+
+ Ndb * pNdb = new Ndb(g_cluster_connection, "TEST_DB");
+ pNdb->init();
+ tNodeId = pNdb->getNodeId();
+
+ ndbout << " NdbAPI node with id = " << pNdb->getNodeId() << endl;
+ ndbout << endl;
+
+ ndbout << "Waiting for ndb to become ready..." <<endl;
+ if (pNdb->waitUntilReady(2000) != 0){
+ ndbout << "NDB is not ready" << endl;
+ ndbout << "Benchmark failed!" << endl;
+ returnValue = NDBT_FAILED;
+ }
+
+ if(returnValue == NDBT_OK){
+ if (createTables(pNdb) != 0){
+ returnValue = NDBT_FAILED;
+ }
+ }
+
+ if(returnValue == NDBT_OK){
+ /****************************************************************
+ * Create NDB objects. *
+ ****************************************************************/
+ resetThreads();
+ for (i = 0; i < tNoOfThreads ; i++) {
+ pThreadData[i].threadNo = i;
+ threadLife[i] = NdbThread_Create(threadLoop,
+ (void**)&pThreadData[i],
+ 32768,
+ "flexAsynchThread",
+ NDB_THREAD_PRIO_LOW);
+ }//for
+ ndbout << endl << "All NDB objects and table created" << endl << endl;
+ int noOfTransacts = tNoOfParallelTrans * tNoOfTransactions *
+ tNoOfThreads * tNoOfLoops;
+ /****************************************************************
+ * Execute program. *
+ ****************************************************************/
+ /****************************************************************
+ * Perform inserts. *
+ ****************************************************************/
+
+ if (tInsert == true) {
+ tInsert = false;
+ tReadUpdate = false;
+ START_TIMER;
+ execute(stInsert);
+ STOP_TIMER;
+ PRINT_TIMER("insert", noOfTransacts, 1);
+ }//if
+ /****************************************************************
+ * Perform read + updates. *
+ ****************************************************************/
+
+ if (tReadUpdate == true) {
+ START_TIMER;
+ execute(stRead);
+ STOP_TIMER;
+ PRINT_TIMER("update + read", noOfTransacts, 1);
+ }//if
+ /****************************************************************
+ * Perform delete. *
+ ****************************************************************/
+
+ if (tDelete == true) {
+ tDelete = false;
+ START_TIMER;
+ execute(stDelete);
+ STOP_TIMER;
+ PRINT_TIMER("delete", noOfTransacts, 1);
+ }//if
+ ndbout << "--------------------------------------------------" << endl;
+
+ execute(stStop);
+ void * tmp;
+ for(i = 0; i<tNoOfThreads; i++){
+ NdbThread_WaitFor(threadLife[i], &tmp);
+ NdbThread_Destroy(&threadLife[i]);
+ }
+ }
+ delete [] pThreadData;
+ delete pNdb;
+
+ //printing errorCounters
+ flexTTErrorData->printErrorCounters(ndbout);
+
+ return NDBT_ProgramExit(returnValue);
+}//main()
+
+
+static void execute(StartType aType)
+{
+ resetThreads();
+ tellThreads(aType);
+ waitForThreads();
+}//execute()
+
+static void*
+threadLoop(void* ThreadData)
+{
+ Ndb* localNdb;
+ ThreadNdb* tabThread = (ThreadNdb*)ThreadData;
+ int loc_threadNo = tabThread->threadNo;
+
+ void * mem = malloc(sizeof(TransNdb)*tNoOfParallelTrans);
+ TransNdb* pTransData = (TransNdb*)mem;
+
+ localNdb = new Ndb(g_cluster_connection, "TEST_DB");
+ localNdb->init(1024);
+ localNdb->waitUntilReady();
+
+ if (tLocal == false) {
+ tabThread->threadIncrement = 1;
+ } else {
+ tabThread->threadIncrement = MAX_SEEK;
+ }//if
+ tabThread->threadBase = (loc_threadNo << 16) + tNodeId;
+ tabThread->threadNdb = localNdb;
+ tabThread->threadStop = tNoOfParallelTrans * tNoOfTransactions;
+ tabThread->threadStop *= tabThread->threadIncrement;
+ tabThread->threadLoopStop = tNoOfLoops;
+ Uint32 i, j;
+ for (i = 0; i < tNoOfParallelTrans; i++) {
+ pTransData[i].transNdb = localNdb;
+ pTransData[i].transThread = tabThread;
+ pTransData[i].transOperation = NULL;
+ pTransData[i].transStartType = stIdle;
+ pTransData[i].vpn_number = tabThread->threadBase;
+ pTransData[i].vpn_identity = 0;
+ pTransData[i].transErrorCount = 0;
+ for (j = 0; j < 128; j++) {
+ pTransData[i].transRecord[j] = 0x30;
+ }//for
+ }//for
+
+ for (;;){
+ while (ThreadStart[loc_threadNo] == stIdle) {
+ NdbSleep_MilliSleep(10);
+ }//while
+
+ // Check if signal to exit is received
+ if (ThreadStart[loc_threadNo] == stStop) {
+ break;
+ }//if
+
+ tabThread->threadStartType = ThreadStart[loc_threadNo];
+ tabThread->threadLoopCounter = 0;
+ tabThread->threadCompleted = false;
+ tabThread->threadNoCompleted = 0;
+ tabThread->threadNextStart = 0;
+
+ ThreadStart[loc_threadNo] = stIdle;
+ if(!executeThread(tabThread, pTransData)){
+ break;
+ }
+ ThreadReady[loc_threadNo] = 1;
+ }//for
+
+ free(mem);
+ delete localNdb;
+ ThreadReady[loc_threadNo] = 1;
+
+ return NULL; // Thread exits
+}//threadLoop()
+
+static
+bool
+executeThread(ThreadNdb* tabThread, TransNdb* atransDataArrayPtr) {
+ Uint32 i;
+ for (i = 0; i < tNoOfParallelTrans; i++) {
+ TransNdb* transNdbPtr = &atransDataArrayPtr[i];
+ transNdbPtr->vpn_identity = i * tabThread->threadIncrement;
+ transNdbPtr->transStartType = tabThread->threadStartType;
+ if (executeTransaction(transNdbPtr) == false) {
+ return false;
+ }//if
+ }//for
+ tabThread->threadNextStart = tNoOfParallelTrans * tabThread->threadIncrement;
+ do {
+ tabThread->threadNdb->sendPollNdb(3000, tMinEvents, tSendForce);
+ } while (tabThread->threadCompleted == false);
+ return true;
+}//executeThread()
+
+static
+bool executeTransaction(TransNdb* transNdbRef)
+{
+ NdbConnection* MyTrans;
+ ThreadNdb* tabThread = transNdbRef->transThread;
+ Ndb* aNdbObject = transNdbRef->transNdb;
+ Uint32 threadBase = tabThread->threadBase;
+ Uint32 startKey = transNdbRef->vpn_identity;
+ if (tLocal == true) {
+ startKey = getKey(startKey, threadBase);
+ }//if
+ if (startTransGuess == true) {
+ Uint32 tKey[2];
+ tKey[0] = startKey;
+ tKey[1] = threadBase;
+ MyTrans = aNdbObject->startTransaction((Uint32)0, //Priority
+ (const char*)&tKey[0], //Main PKey
+ (Uint32)8); //Key Length
+ } else {
+ MyTrans = aNdbObject->startTransaction();
+ }//if
+ if (MyTrans == NULL) {
+ error_handler(aNdbObject->getNdbError());
+ ndbout << endl << "Unable to recover! Quiting now" << endl ;
+ return false;
+ }//if
+ //-------------------------------------------------------
+ // Define the operation, but do not execute it yet.
+ //-------------------------------------------------------
+ if (!defineOperation(MyTrans, transNdbRef, startKey, threadBase))
+ return false;
+
+ return true;
+}//executeTransaction()
+
+
+static
+Uint32
+getKey(Uint32 aBase, Uint32 aThreadBase) {
+ Uint32 Tfound = aBase;
+ Uint32 hash;
+ Uint64 Tkey64;
+ Uint32* tKey32 = (Uint32*)&Tkey64;
+ tKey32[0] = aThreadBase;
+ for (int i = aBase; i < (aBase + MAX_SEEK); i++) {
+ tKey32[1] = (Uint32)i;
+ hash = md5_hash((Uint64*)&Tkey64, (Uint32)2);
+ hash = (hash >> 6) & (MAX_PARTS - 1);
+ if (hash == tLocalPart) {
+ Tfound = i;
+ break;
+ }//if
+ }//for
+ return Tfound;
+}//getKey()
+
+static void
+executeCallback(int result, NdbConnection* NdbObject, void* aObject)
+{
+ TransNdb* transNdbRef = (TransNdb*)aObject;
+ ThreadNdb* tabThread = transNdbRef->transThread;
+ Ndb* tNdb = transNdbRef->transNdb;
+ Uint32 vpn_id = transNdbRef->vpn_identity;
+ Uint32 vpn_nb = tabThread->threadBase;
+
+ if (result == -1) {
+// Add complete error handling here
+ int retCode = flexTTErrorData->handleErrorCommon(NdbObject->getNdbError());
+ if (retCode == 1) {
+ if (NdbObject->getNdbError().code != 626 &&
+ NdbObject->getNdbError().code != 630) {
+ ndbout_c("execute: %s", NdbObject->getNdbError().message);
+ ndbout_c("Error code = %d", NdbObject->getNdbError().code);
+ }
+ } else if (retCode == 2) {
+ ndbout << "4115 should not happen in flexTT" << endl;
+ } else if (retCode == 3) {
+ /* What can we do here? */
+ ndbout_c("execute: %s", NdbObject->getNdbError().message);
+ }//if(retCode == 3)
+ transNdbRef->transErrorCount++;
+ const NdbError & err = NdbObject->getNdbError();
+ switch (err.classification) {
+ case NdbError::NoDataFound:
+ case NdbError::ConstraintViolation:
+ ndbout << "Error with vpn_id = " << vpn_id << " and vpn_nb = ";
+ ndbout << vpn_nb << endl;
+ ndbout << err << endl;
+ goto checkCompleted;
+ case NdbError::OverloadError:
+ NdbSleep_MilliSleep(10);
+ case NdbError::NodeRecoveryError:
+ case NdbError::UnknownResultError:
+ case NdbError::TimeoutExpired:
+ break;
+ default:
+ goto checkCompleted;
+ }//if
+ if ((transNdbRef->transErrorCount > 10) ||
+ (tabThread->threadNoCompleted > 0)) {
+ goto checkCompleted;
+ }//if
+ } else {
+ if (tabThread->threadNoCompleted == 0) {
+ transNdbRef->transErrorCount = 0;
+ transNdbRef->vpn_identity = tabThread->threadNextStart;
+ if (tabThread->threadNextStart == tabThread->threadStop) {
+ tabThread->threadLoopCounter++;
+ transNdbRef->vpn_identity = 0;
+ tabThread->threadNextStart = 0;
+ if (tabThread->threadLoopCounter == tNoOfLoops) {
+ goto checkCompleted;
+ }//if
+ }//if
+ tabThread->threadNextStart += tabThread->threadIncrement;
+ } else {
+ goto checkCompleted;
+ }//if
+ }//if
+ tNdb->closeTransaction(NdbObject);
+ executeTransaction(transNdbRef);
+ return;
+
+checkCompleted:
+ tNdb->closeTransaction(NdbObject);
+ tabThread->threadNoCompleted++;
+ if (tabThread->threadNoCompleted == tNoOfParallelTrans) {
+ tabThread->threadCompleted = true;
+ }//if
+ return;
+}//executeCallback()
+
+static
+StartType
+random_choice()
+{
+//----------------------------------------------------
+// Generate a random key between 0 and tNoOfRecords - 1
+//----------------------------------------------------
+ UintR random_number = lrand48() % 100;
+ if (random_number < tUpdateFreq)
+ return stUpdate;
+ else
+ return stRead;
+}//random_choice()
+
+static bool
+defineOperation(NdbConnection* localNdbConnection, TransNdb* transNdbRef,
+ unsigned int vpn_id, unsigned int vpn_nb)
+{
+ NdbOperation* localNdbOperation;
+ StartType TType = transNdbRef->transStartType;
+
+ //-------------------------------------------------------
+ // Set-up the attribute values for this operation.
+ //-------------------------------------------------------
+ localNdbOperation = localNdbConnection->getNdbOperation(tableName[0]);
+ if (localNdbOperation == NULL) {
+ error_handler(localNdbConnection->getNdbError());
+ return false;
+ }//if
+ switch (TType) {
+ case stInsert: // Insert case
+ if (theWriteFlag == 1 && theDirtyFlag == 1) {
+ localNdbOperation->dirtyWrite();
+ } else if (theWriteFlag == 1) {
+ localNdbOperation->writeTuple();
+ } else {
+ localNdbOperation->insertTuple();
+ }//if
+ break;
+ case stRead: // Read Case
+ TType = random_choice();
+ if (TType == stRead) {
+ if (theSimpleFlag == 1) {
+ localNdbOperation->simpleRead();
+ } else if (theDirtyFlag == 1) {
+ localNdbOperation->dirtyRead();
+ } else {
+ localNdbOperation->readTuple();
+ }//if
+ } else {
+ if (theWriteFlag == 1 && theDirtyFlag == 1) {
+ localNdbOperation->dirtyWrite();
+ } else if (theWriteFlag == 1) {
+ localNdbOperation->writeTuple();
+ } else if (theDirtyFlag == 1) {
+ localNdbOperation->dirtyUpdate();
+ } else {
+ localNdbOperation->updateTuple();
+ }//if
+ }//if
+ break;
+ case stDelete: // Delete Case
+ localNdbOperation->deleteTuple();
+ break;
+ default:
+ error_handler(localNdbOperation->getNdbError());
+ }//switch
+ localNdbOperation->equal((Uint32)0,vpn_id);
+ localNdbOperation->equal((Uint32)1,vpn_nb);
+ char* attrValue = &transNdbRef->transRecord[0];
+ switch (TType) {
+ case stInsert: // Insert case
+ localNdbOperation->setValue((Uint32)2, attrValue);
+ localNdbOperation->setValue((Uint32)3, attrValue);
+ localNdbOperation->setValue((Uint32)4, attrValue);
+ break;
+ case stUpdate: // Update Case
+ localNdbOperation->setValue((Uint32)3, attrValue);
+ break;
+ case stRead: // Read Case
+ localNdbOperation->getValue((Uint32)2, attrValue);
+ localNdbOperation->getValue((Uint32)3, attrValue);
+ localNdbOperation->getValue((Uint32)4, attrValue);
+ break;
+ case stDelete: // Delete Case
+ break;
+ default:
+ error_handler(localNdbOperation->getNdbError());
+ }//switch
+ localNdbConnection->executeAsynchPrepare(Commit, &executeCallback,
+ (void*)transNdbRef);
+ return true;
+}//defineOperation()
+
+
+static void setAttrNames()
+{
+ BaseString::snprintf(attrName[0], MAXSTRLEN, "VPN_ID");
+ BaseString::snprintf(attrName[1], MAXSTRLEN, "VPN_NB");
+ BaseString::snprintf(attrName[2], MAXSTRLEN, "DIRECTORY_NB");
+ BaseString::snprintf(attrName[3], MAXSTRLEN, "LAST_CALL_PARTY");
+ BaseString::snprintf(attrName[4], MAXSTRLEN, "DESCR");
+}
+
+
+static void setTableNames()
+{
+ BaseString::snprintf(tableName[0], MAXSTRLEN, "VPN_USERS");
+}
+
+static
+int
+createTables(Ndb* pMyNdb){
+
+ NdbSchemaCon *MySchemaTransaction;
+ NdbSchemaOp *MySchemaOp;
+ int check;
+
+ if (theTableCreateFlag == 0) {
+ ndbout << "Creating Table: vpn_users " << "..." << endl;
+ MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pMyNdb);
+
+ if(MySchemaTransaction == NULL &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if(MySchemaOp == NULL &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+
+ check = MySchemaOp->createTable( tableName[0]
+ ,8 // Table Size
+ ,TupleKey // Key Type
+ ,40 // Nr of Pages
+ ,All
+ ,6
+ ,(tLoadFactor - 5)
+ ,tLoadFactor
+ ,1
+ ,!tempTable
+ );
+
+ if (check == -1 &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+
+ check = MySchemaOp->createAttribute( (char*)attrName[0],
+ TupleKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if (check == -1 &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+ check = MySchemaOp->createAttribute( (char*)attrName[1],
+ TupleKey,
+ 32,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if (check == -1 &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+ check = MySchemaOp->createAttribute( (char*)attrName[2],
+ NoKey,
+ 8,
+ 10,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if (check == -1 &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+
+ check = MySchemaOp->createAttribute( (char*)attrName[3],
+ NoKey,
+ 8,
+ 10,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if (check == -1 &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+
+ check = MySchemaOp->createAttribute( (char*)attrName[4],
+ NoKey,
+ 8,
+ 100,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if (check == -1 &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+
+ if (MySchemaTransaction->execute() == -1 &&
+ (!error_handler(MySchemaTransaction->getNdbError())))
+ return -1;
+
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ }//if
+
+ return 0;
+}
+
+bool error_handler(const NdbError& err){
+ ndbout << err << endl ;
+ switch(err.classification){
+ case NdbError::NodeRecoveryError:
+ case NdbError::SchemaError:
+ case NdbError::TimeoutExpired:
+ ndbout << endl << "Attempting to recover and continue now..." << endl ;
+ return true ; // return true to retry
+ }
+ return false;
+}
+#if 0
+bool error_handler(const char* error_string, int error_int) {
+ ndbout << error_string << endl ;
+ if ((4008 == error_int) ||
+ (677 == error_int) ||
+ (891 == error_int) ||
+ (1221 == error_int) ||
+ (721 == error_int) ||
+ (266 == error_int)) {
+ ndbout << endl << "Attempting to recover and continue now..." << endl ;
+ return true ; // return true to retry
+ }
+ return false ; // return false to abort
+}
+#endif
+
+static
+int
+readArguments(int argc, const char** argv){
+
+ int i = 1;
+ while (argc > 1){
+ if (strcmp(argv[i], "-t") == 0){
+ tNoOfThreads = atoi(argv[i+1]);
+ if ((tNoOfThreads < 1) || (tNoOfThreads > MAXTHREADS)){
+ ndbout_c("Invalid no of threads");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-p") == 0){
+ tNoOfParallelTrans = atoi(argv[i+1]);
+ if ((tNoOfParallelTrans < 1) || (tNoOfParallelTrans > MAXPAR)){
+ ndbout_c("Invalid no of parallell transactions");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-o") == 0) {
+ tNoOfTransactions = atoi(argv[i+1]);
+ if (tNoOfTransactions < 1){
+ ndbout_c("Invalid no of transactions");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-l") == 0){
+ tNoOfLoops = atoi(argv[i+1]);
+ if (tNoOfLoops < 1) {
+ ndbout_c("Invalid no of loops");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-e") == 0){
+ tMinEvents = atoi(argv[i+1]);
+ if ((tMinEvents < 1) || (tMinEvents > tNoOfParallelTrans)) {
+ ndbout_c("Invalid no of loops");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-local") == 0){
+ tLocalPart = atoi(argv[i+1]);
+ tLocal = true;
+ startTransGuess = true;
+ if ((tLocalPart < 0) || (tLocalPart > MAX_PARTS)){
+ ndbout_c("Invalid local part");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-ufreq") == 0){
+ tUpdateFreq = atoi(argv[i+1]);
+ if ((tUpdateFreq < 0) || (tUpdateFreq > 100)){
+ ndbout_c("Invalid Update Frequency");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-load_factor") == 0){
+ tLoadFactor = atoi(argv[i+1]);
+ if ((tLoadFactor < 40) || (tLoadFactor >= 100)){
+ ndbout_c("Invalid LoadFactor");
+ return -1;
+ }
+ } else if (strcmp(argv[i], "-d") == 0){
+ tDelete = true;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-i") == 0){
+ tInsert = true;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-simple") == 0){
+ theSimpleFlag = 1;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-adaptive") == 0){
+ tSendForce = 0;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-force") == 0){
+ tSendForce = 1;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-non_adaptive") == 0){
+ tSendForce = 2;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-write") == 0){
+ theWriteFlag = 1;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-dirty") == 0){
+ theDirtyFlag = 1;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-table_create") == 0){
+ theTableCreateFlag = 0;
+ tInsert = true;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-temp") == 0){
+ tempTable = true;
+ argc++;
+ i--;
+ } else if (strcmp(argv[i], "-no_hint") == 0){
+ startTransGuess = false;
+ argc++;
+ i--;
+ } else {
+ return -1;
+ }
+
+ argc -= 2;
+ i = i + 2;
+ }//while
+ if (tLocal == true) {
+ if (startTransGuess == false) {
+ ndbout_c("Not valid to use no_hint with local");
+ }//if
+ }//if
+ return 0;
+}
+
+static
+void
+input_error(){
+
+ ndbout_c("FLEXTT");
+ ndbout_c(" Perform benchmark of insert, update and delete transactions");
+ ndbout_c("");
+ ndbout_c("Arguments:");
+ ndbout_c(" -t Number of threads to start, default 1");
+ ndbout_c(" -p Number of parallel transactions per thread, default 32");
+ ndbout_c(" -o Number of transactions per loop, default 500");
+ ndbout_c(" -ufreq Number Update Frequency in percent (0 -> 100), rest is read");
+ ndbout_c(" -load_factor Number Fill level in index in percent (40 -> 99)");
+ ndbout_c(" -l Number of loops to run, default 1, 0=infinite");
+ ndbout_c(" -i Start by inserting all records");
+ ndbout_c(" -d End by deleting all records (only one loop)");
+ ndbout_c(" -simple Use simple read to read from database");
+ ndbout_c(" -dirty Use dirty read to read from database");
+ ndbout_c(" -write Use writeTuple in insert and update");
+ ndbout_c(" -n Use standard table names");
+ ndbout_c(" -table_create Create tables in db");
+ ndbout_c(" -temp Create table(s) without logging");
+ ndbout_c(" -no_hint Don't give hint on where to execute transaction coordinator");
+ ndbout_c(" -adaptive Use adaptive send algorithm (default)");
+ ndbout_c(" -force Force send when communicating");
+ ndbout_c(" -non_adaptive Send at a 10 millisecond interval");
+ ndbout_c(" -local Number of part, only use keys in one part out of 16");
+}
diff --git a/storage/ndb/test/ndbapi/flexTimedAsynch.cpp b/storage/ndb/test/ndbapi/flexTimedAsynch.cpp
new file mode 100644
index 00000000000..2b8c0bdd5f8
--- /dev/null
+++ b/storage/ndb/test/ndbapi/flexTimedAsynch.cpp
@@ -0,0 +1,852 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* ***************************************************
+ FLEXTIMEDASYNCH
+ Perform benchmark of insert, update and delete transactions.
+
+ Arguments:
+ -t Number of threads to start, i.e., number of parallel loops, default 1
+ -p Number of transactions in a batch, default 32
+ -o Number of batches per loop, default 200
+ -i Time between batch starts, default 0
+ -l Number of loops to run, default 1, 0=infinite
+ -a Number of attributes, default 25
+ -c Number of operations per transaction
+ -s Size of each attribute in 32 bit word, default 1 (Primary Key is always of size 1,
+ independent of this value)
+ -simple Use simple read to read from database
+ -dirty Use dirty read to read from database
+ -write Use writeTuple in insert and update
+ -n Use standard table names
+ -no_table_create Don't create tables in db
+ -temp Use temporary tables, no writing to disk.
+
+ Returns:
+ 0 - Test passed
+ -1 - Test failed
+ 1 - Invalid arguments
+
+ * *************************************************** */
+
+#include "NdbApi.hpp"
+
+#include <NdbThread.h>
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <NdbOut.hpp>
+#include <NdbTimer.hpp>
+#include <string.h>
+#include <NdbMain.h>
+#include <NdbTest.hpp>
+
+#include <NDBT_Error.hpp>
+
+#define MAXSTRLEN 16
+#define MAXATTR 64
+#define MAXTABLES 64
+#define MAXTHREADS 256
+#define MAXATTRSIZE 1000
+#define PKSIZE 1
+
+enum StartType { stIdle,
+ stInsert,
+ stRead,
+ stUpdate,
+ stDelete,
+ stStop } ;
+
+ErrorData * flexTimedAsynchErrorData;
+
+struct ThreadNdb
+{
+ int NoOfOps;
+ int ThreadNo;
+ unsigned int threadBase;
+ unsigned int transactionCompleted;
+};
+
+extern "C" void* threadLoop(void*);
+void setAttrNames(void);
+void setTableNames(void);
+void readArguments(int argc, const char** argv);
+void createAttributeSpace();
+void createTables(Ndb*);
+void defineOperation(NdbConnection* aTransObject, StartType aType, unsigned int key, int *);
+void execute(StartType aType);
+void executeThread(StartType aType, Ndb* aNdbObject, ThreadNdb* threadInfo);
+void executeCallback(int result, NdbConnection* NdbObject, void* aObject);
+
+/* epaulsa > *************************************************************/
+bool error_handler(const NdbError &) ; //replaces 'goto' things
+static int failed = 0 ; // lame global variable that keeps track of failed transactions
+ // incremented in executeCallback() and reset in main()
+/************************************************************* < epaulsa */
+
+static NdbThread* threadLife[MAXTHREADS];
+static int tNodeId;
+static int ThreadReady[MAXTHREADS];
+static StartType ThreadStart[MAXTHREADS];
+static char tableName[MAXTABLES][MAXSTRLEN+1];
+static char attrName[MAXATTR][MAXSTRLEN+1];
+static int *getAttrValueTable;
+
+// Program Parameters
+static int tNoOfLoops = 1;
+static int tAttributeSize = 1;
+static unsigned int tNoOfThreads = 1;
+static unsigned int tNoOfTransInBatch = 32;
+static unsigned int tNoOfAttributes = 25;
+static unsigned int tNoOfBatchesInLoop = 200;
+static unsigned int tNoOfOpsPerTrans = 1;
+static unsigned int tTimeBetweenBatches = 0;
+
+//Program Flags
+static int theTestFlag = 0;
+static int theTempFlag = 1;
+static int theSimpleFlag = 0;
+static int theDirtyFlag = 0;
+static int theWriteFlag = 0;
+static int theStdTableNameFlag = 0;
+static int theTableCreateFlag = 0;
+
+#define START_REAL_TIME NdbTimer timer; timer.doStart();
+#define STOP_REAL_TIME timer.doStop();
+
+#define START_TIMER { NdbTimer timer; timer.doStart();
+#define STOP_TIMER timer.doStop();
+#define PRINT_TIMER(text, trans, opertrans) timer.printTransactionStatistics(text, trans, opertrans); };
+
+void
+resetThreads(){
+
+ for (int i = 0; i < tNoOfThreads ; i++) {
+ ThreadReady[i] = 0;
+ ThreadStart[i] = stIdle;
+ }
+}
+
+void
+waitForThreads(void)
+{
+ int cont;
+ do {
+ cont = 0;
+ NdbSleep_MilliSleep(20);
+ for (int i = 0; i < tNoOfThreads ; i++) {
+ if (ThreadReady[i] == 0) {
+ cont = 1;
+ }
+ }
+ } while (cont == 1);
+}
+
+void
+tellThreads(StartType what)
+{
+ for (int i = 0; i < tNoOfThreads ; i++)
+ ThreadStart[i] = what;
+}
+
+void createAttributeSpace(){
+ getAttrValueTable = new int[tAttributeSize*
+ tNoOfThreads *
+ tNoOfAttributes ];
+
+}
+
+void deleteAttributeSpace(){
+ delete [] getAttrValueTable;
+}
+
+NDB_COMMAND(flexTimedAsynch, "flexTimedAsynch", "flexTimedAsynch [-tpoilcas]", "flexTimedAsynch", 65535)
+{
+ ndb_init();
+ ThreadNdb tabThread[MAXTHREADS];
+ int tLoops=0;
+ int returnValue;
+ //NdbOut flexTimedAsynchNdbOut;
+
+ flexTimedAsynchErrorData = new ErrorData;
+ flexTimedAsynchErrorData->resetErrorCounters();
+
+ Ndb* pNdb;
+ pNdb = new Ndb( "TEST_DB" );
+ pNdb->init();
+
+ readArguments(argc, argv);
+
+ createAttributeSpace();
+
+ ndbout << endl << "FLEXTIMEDASYNCH - Starting normal mode" << endl;
+ ndbout << "Perform benchmark of insert, update and delete transactions" << endl << endl;
+
+ if(theTempFlag == 0)
+ ndbout << " " << "Using temporary tables. " << endl;
+
+ // -t, tNoOfThreads
+ ndbout << " " << tNoOfThreads << " number of concurrent threads " << endl;
+ // -c, tNoOfOpsPerTrans
+ ndbout << " " << tNoOfOpsPerTrans << " operations per transaction " << endl;
+ // -p, tNoOfTransInBatch
+ ndbout << " " << tNoOfTransInBatch << " number of transactions in a batch per thread " << endl;
+ // -o, tNoOfBatchesInLoop
+ ndbout << " " << tNoOfBatchesInLoop << " number of batches per loop " << endl;
+ // -i, tTimeBetweenBatches
+ ndbout << " " << tTimeBetweenBatches << " milli seconds at least between batch starts " << endl;
+ // -l, tNoOfLoops
+ ndbout << " " << tNoOfLoops << " loops " << endl;
+ // -a, tNoOfAttributes
+ ndbout << " " << tNoOfAttributes << " attributes per table " << endl;
+ // -s, tAttributeSize
+ ndbout << " " << tAttributeSize << " is the number of 32 bit words per attribute " << endl << endl;
+
+ NdbThread_SetConcurrencyLevel(2 + tNoOfThreads);
+
+ /* print Setting */
+ flexTimedAsynchErrorData->printSettings(ndbout);
+
+ setAttrNames();
+ setTableNames();
+
+ ndbout << "Waiting for ndb to become ready..." <<endl;
+ if (pNdb->waitUntilReady() == 0) {
+ tNodeId = pNdb->getNodeId();
+ ndbout << " NdbAPI node with id = " << tNodeId << endl;
+ createTables(pNdb);
+
+ /****************************************************************
+ * Create NDB objects. *
+ ****************************************************************/
+ resetThreads();
+ for (int i = 0; i < tNoOfThreads ; i++) {
+ tabThread[i].ThreadNo = i;
+
+ threadLife[i] = NdbThread_Create(threadLoop,
+ (void**)&tabThread[i],
+ 32768,
+ "flexTimedAsynchThread",
+ NDB_THREAD_PRIO_LOW);
+ }
+ ndbout << endl << "All NDB objects and table created" << endl << endl;
+ int noOfTransacts = tNoOfTransInBatch*tNoOfBatchesInLoop*tNoOfThreads;
+
+ /****************************************************************
+ * Execute program. *
+ ****************************************************************/
+
+
+ for(;;) {
+
+ int loopCount = tLoops + 1 ;
+ ndbout << endl << "Loop # " << loopCount << endl << endl ;
+
+ /****************************************************************
+ * Perform inserts. *
+ ****************************************************************/
+
+ failed = 0 ;
+
+ START_TIMER;
+ execute(stInsert);
+ STOP_TIMER;
+ PRINT_TIMER("insert", noOfTransacts, tNoOfOpsPerTrans);
+
+ if (0 < failed) {
+ ndbout << failed << " of the transactions returned errors!, moving on now..."<<endl ;
+ }
+
+ /****************************************************************
+ * Perform read. *
+ ****************************************************************/
+
+ failed = 0 ;
+
+ START_TIMER;
+ execute(stRead);
+ STOP_TIMER;
+ PRINT_TIMER("read", noOfTransacts, tNoOfOpsPerTrans);
+
+ if (0 < failed) {
+ ndbout << failed << " of the transactions returned errors!, moving on now..."<<endl ;
+ }
+
+
+
+ /****************************************************************
+ * Perform update. *
+ ***************************************************************/
+
+ failed = 0 ;
+
+ START_TIMER;
+ execute(stUpdate);
+ STOP_TIMER;
+ PRINT_TIMER("update", noOfTransacts, tNoOfOpsPerTrans) ;
+
+ if (0 < failed) {
+ ndbout << failed << " of the transactions returned errors!, moving on now..."<<endl ;
+ }
+
+ /****************************************************************
+ * Perform read after update.
+ ****************************************************************/
+
+ failed = 0 ;
+
+ START_TIMER;
+ execute(stRead);
+ STOP_TIMER;
+ PRINT_TIMER("read", noOfTransacts, tNoOfOpsPerTrans);
+
+ if (0 < failed) {
+ ndbout << failed << " of the transactions returned errors!, moving on now..."<<endl ;
+ }
+
+
+ /****************************************************************
+ * Perform delete. *
+ ****************************************************************/
+
+ failed = 0;
+
+ START_TIMER;
+ execute(stDelete);
+ STOP_TIMER;
+ PRINT_TIMER("delete", noOfTransacts, tNoOfOpsPerTrans);
+
+ if (0 < failed) {
+ ndbout << failed << " of the transactions returned errors!, moving on now..."<<endl ;
+ }
+
+ tLoops++;
+ ndbout << "--------------------------------------------------" << endl;
+
+ if(tNoOfLoops != 0){
+ if(tNoOfLoops <= tLoops)
+ break ;
+ }
+ }
+
+ ndbout << endl << "Benchmark completed!" << endl;
+ returnValue = NDBT_OK;
+
+ execute(stStop);
+ void * tmp;
+ for(int i = 0; i<tNoOfThreads; i++){
+ NdbThread_WaitFor(threadLife[i], &tmp);
+ NdbThread_Destroy(&threadLife[i]);
+ }
+ } else {
+ ndbout << "NDB is not ready" << endl;
+ ndbout << "Benchmark failed!" << endl;
+ returnValue = NDBT_FAILED;
+ }
+
+ deleteAttributeSpace();
+ delete pNdb;
+
+ //printing errorCounters
+ flexTimedAsynchErrorData->printErrorCounters(ndbout);
+
+ return NDBT_ProgramExit(returnValue);
+}//main()
+
+////////////////////////////////////////
+
+void execute(StartType aType)
+{
+ resetThreads();
+ tellThreads(aType);
+ waitForThreads();
+}
+
+void*
+threadLoop(void* ThreadData)
+{
+ // Do work until signaled to stop.
+
+ Ndb* localNdb;
+ StartType tType;
+ ThreadNdb* threadInfo = (ThreadNdb*)ThreadData;
+ int threadNo = threadInfo->ThreadNo;
+ localNdb = new Ndb("TEST_DB");
+ localNdb->init(512);
+ localNdb->waitUntilReady();
+ threadInfo->threadBase = (threadNo * 2000000) + (tNodeId * 260000000);
+
+ for (;;) {
+ while (ThreadStart[threadNo] == stIdle) {
+ NdbSleep_MilliSleep(10);
+ }
+
+ // Check if signal to exit is received
+ if (ThreadStart[threadNo] == stStop) {
+ break;
+ }
+
+ tType = ThreadStart[threadNo];
+ ThreadStart[threadNo] = stIdle;
+ executeThread(tType, localNdb, threadInfo);
+ ThreadReady[threadNo] = 1;
+ }
+
+ delete localNdb;
+ ThreadReady[threadNo] = 1;
+
+ return NULL; // thread exits
+}
+
+void executeThread(StartType aType, Ndb* aNdbObject, ThreadNdb* threadInfo)
+{
+ // Do all batch job in loop with start specified delay
+ int i, j, k;
+ NdbConnection* tConArray[1024];
+ unsigned int tBase;
+ unsigned int tBase2;
+ int threadId = threadInfo->ThreadNo;
+ int *getValueRowAddress = NULL;
+
+ NdbTimer timer;
+ timer.doStart();
+
+ for (i = 0; i < tNoOfBatchesInLoop; i++) {
+ //tBase = threadBase + (i * tNoOfTransInBatch * tNoOfOpsPerTrans);
+ tBase = threadInfo->threadBase + (i * tNoOfTransInBatch * tNoOfOpsPerTrans);
+ //tCompleted = 0;
+ threadInfo->transactionCompleted = 0;
+
+ for (j = 0; j < tNoOfTransInBatch; j++) {
+ tBase2 = tBase + (j * tNoOfOpsPerTrans);
+ tConArray[j] = aNdbObject->startTransaction();
+ if ( tConArray[j] == NULL && !error_handler(aNdbObject->getNdbError())) {
+ ndbout << endl << "Unable to recover! Quiting now" << endl ;
+ exit (-1) ;
+ return ;
+ }
+
+ for (k = 0; k < tNoOfOpsPerTrans; k++) {
+ //-------------------------------------------------------
+ // Define the operation, but do not execute it yet.
+ //-------------------------------------------------------
+ if(aType == stRead){
+ getValueRowAddress = getAttrValueTable +
+ threadId * tNoOfAttributes * tAttributeSize;
+ }
+ defineOperation(tConArray[j], aType, (tBase2 + k), getValueRowAddress);
+ }
+
+ tConArray[j]->executeAsynchPrepare(Commit, &executeCallback, threadInfo);
+ }
+
+ //-------------------------------------------------------
+ // Now we have defined a set of transactions (= batch), it is now time
+ // to execute all of them.
+ //-------------------------------------------------------
+ aNdbObject->sendPollNdb(3000, 0, 0);
+
+ //while (tCompleted < tNoOfTransInBatch) {
+ while (threadInfo->transactionCompleted < tNoOfTransInBatch) {
+ aNdbObject->pollNdb(3000, 0);
+ ndbout << "threadInfo->transactionCompleted = " <<
+ threadInfo->transactionCompleted << endl;
+ }
+
+ for (j = 0 ; j < tNoOfTransInBatch ; j++) {
+ aNdbObject->closeTransaction(tConArray[j]);
+ }
+
+ // Control the elapsed time since the last batch start.
+ // Wait at least tTimeBetweenBatches milli seconds.
+ timer.doStop();
+ while(timer.elapsedTime() < tTimeBetweenBatches){
+ NdbSleep_MilliSleep(1);
+ timer.doStop();
+ }
+ // Ready to start new batch
+ timer.doStart();
+ }
+ return;
+}
+
+void
+executeCallback(int result, NdbConnection* NdbObject, void* aObject)
+{
+ //tCompleted++;
+ ThreadNdb *threadInfo = (ThreadNdb *)aObject;
+ threadInfo->transactionCompleted++;
+
+ if (result == -1) {
+
+ // Add complete error handling here
+
+ int retCode = flexTimedAsynchErrorData->handleErrorCommon(NdbObject->getNdbError());
+ if (retCode == 1) {
+ if (NdbObject->getNdbError().code != 626 && NdbObject->getNdbError().code != 630){
+ ndbout_c("execute: %s", NdbObject->getNdbError().message);
+ ndbout_c("Error code = %d", NdbObject->getNdbError().code);}
+ } else if (retCode == 2) {
+ ndbout << "4115 should not happen in flexAsynch" << endl;
+ } else if (retCode == 3) {
+ /* What can we do here? */
+ ndbout_c("execute: %s", NdbObject->getNdbError().message);
+ }//if(retCode == 3)
+
+ // ndbout << "Error occured in poll:" << NdbObject->getNdbError() <<
+ // " ErrorCode = " << NdbObject->getNdbError() << endl;
+ ndbout << "executeCallback threadInfo->transactionCompleted = " <<
+ threadInfo->transactionCompleted << endl;
+ failed++ ;
+ return;
+ }
+ return;
+}
+
+void
+defineOperation(NdbConnection* localNdbConnection,
+ StartType aType,
+ unsigned int threadBase,
+ int *pRow )
+{
+ NdbOperation* localNdbOperation;
+ unsigned int loopCountAttributes = tNoOfAttributes;
+ unsigned int countAttributes;
+ int attrValue[MAXATTRSIZE];
+
+ //-------------------------------------------------------
+ // Set-up the attribute values for this operation.
+ //-------------------------------------------------------
+ for (int k = 0; k < loopCountAttributes; k++) {
+ *(int *)&attrValue[k] = (int)threadBase;
+ }
+ localNdbOperation = localNdbConnection->getNdbOperation(tableName[0]);
+ if (localNdbOperation == NULL) {
+ error_handler(localNdbOperation->getNdbError()) ;
+ }
+
+ switch (aType) {
+ case stInsert: { // Insert case
+ if (theWriteFlag == 1 && theDirtyFlag == 1) {
+ localNdbOperation->dirtyWrite();
+ } else if (theWriteFlag == 1) {
+ localNdbOperation->writeTuple();
+ } else {
+ localNdbOperation->insertTuple();
+ }
+ break;
+ }
+ case stRead: { // Read Case
+ if (theSimpleFlag == 1) {
+ localNdbOperation->simpleRead();
+ } else if (theDirtyFlag == 1) {
+ localNdbOperation->dirtyRead();
+ } else {
+ localNdbOperation->readTuple();
+ }
+ break;
+ }
+ case stUpdate: { // Update Case
+ if (theWriteFlag == 1 && theDirtyFlag == 1) {
+ localNdbOperation->dirtyWrite();
+ } else if (theWriteFlag == 1) {
+ localNdbOperation->writeTuple();
+ } else if (theDirtyFlag == 1) {
+ localNdbOperation->dirtyUpdate();
+ } else {
+ localNdbOperation->updateTuple();
+ }
+ break;
+ }
+ case stDelete: { // Delete Case
+ localNdbOperation->deleteTuple();
+ break;
+ }
+ default: {
+ error_handler(localNdbOperation->getNdbError());
+ }
+ }
+
+ localNdbOperation->equal((char*)attrName[0],(char*)&attrValue[0]);
+
+ switch (aType) {
+ case stInsert: // Insert case
+ case stUpdate: // Update Case
+ {
+ for (countAttributes = 1; countAttributes < loopCountAttributes; countAttributes++) {
+ localNdbOperation->setValue( (char*)attrName[countAttributes],(char*)&attrValue[0]);
+ }
+ break;
+ }
+ case stRead: { // Read Case
+ for (countAttributes = 1; countAttributes < loopCountAttributes; countAttributes++) {
+ //localNdbOperation->getValue((char*)attrName[countAttributes],(char*)&attrValue[0]);
+ localNdbOperation->getValue((char*)attrName[countAttributes],
+ (char *) (pRow + countAttributes*tAttributeSize));
+ }
+ break;
+ }
+ case stDelete: { // Delete Case
+ break;
+ }
+ default: {
+ error_handler(localNdbOperation->getNdbError());
+ }
+ }
+ return;
+}
+
+void readArguments(int argc, const char** argv)
+{
+ int i = 1;
+ while (argc > 1)
+ {
+ if (strcmp(argv[i], "-t") == 0)
+ {
+ tNoOfThreads = atoi(argv[i+1]);
+ // if ((tNoOfThreads < 1) || (tNoOfThreads > MAXTHREADS))
+ if ((tNoOfThreads < 1) || (tNoOfThreads > MAXTHREADS))
+ exit(-1);
+ }
+ else if (strcmp(argv[i], "-i") == 0)
+ {
+ tTimeBetweenBatches = atoi(argv[i+1]);
+ if (tTimeBetweenBatches < 0)
+ exit(-1);
+ }
+ else if (strcmp(argv[i], "-p") == 0)
+ {
+ tNoOfTransInBatch = atoi(argv[i+1]);
+ //if ((tNoOfTransInBatch < 1) || (tNoOfTransInBatch > MAXTHREADS))
+ if ((tNoOfTransInBatch < 1) || (tNoOfTransInBatch > 10000))
+ exit(-1);
+ }
+ else if (strcmp(argv[i], "-c") == 0)
+ {
+ tNoOfOpsPerTrans = atoi(argv[i+1]);
+ if (tNoOfOpsPerTrans < 1)
+ exit(-1);
+ }
+ else if (strcmp(argv[i], "-o") == 0)
+ {
+ tNoOfBatchesInLoop = atoi(argv[i+1]);
+ if (tNoOfBatchesInLoop < 1)
+ exit(-1);
+ }
+ else if (strcmp(argv[i], "-a") == 0)
+ {
+ tNoOfAttributes = atoi(argv[i+1]);
+ if ((tNoOfAttributes < 2) || (tNoOfAttributes > MAXATTR))
+ exit(-1);
+ }
+ else if (strcmp(argv[i], "-n") == 0)
+ {
+ theStdTableNameFlag = 1;
+ argc++;
+ i--;
+ }
+ else if (strcmp(argv[i], "-l") == 0)
+ {
+ tNoOfLoops = atoi(argv[i+1]);
+ if ((tNoOfLoops < 0) || (tNoOfLoops > 100000))
+ exit(-1);
+ }
+ else if (strcmp(argv[i], "-s") == 0)
+ {
+ tAttributeSize = atoi(argv[i+1]);
+ if ((tAttributeSize < 1) || (tAttributeSize > MAXATTRSIZE))
+ exit(-1);
+ }
+ else if (strcmp(argv[i], "-simple") == 0)
+ {
+ theSimpleFlag = 1;
+ argc++;
+ i--;
+ }
+ else if (strcmp(argv[i], "-write") == 0)
+ {
+ theWriteFlag = 1;
+ argc++;
+ i--;
+ }
+ else if (strcmp(argv[i], "-dirty") == 0)
+ {
+ theDirtyFlag = 1;
+ argc++;
+ i--;
+ }
+ else if (strcmp(argv[i], "-test") == 0)
+ {
+ theTestFlag = 1;
+ argc++;
+ i--;
+ }
+ else if (strcmp(argv[i], "-temp") == 0)
+ {
+ theTempFlag = 0; // 0 if temporary tables.
+ argc++;
+ i--;
+ }
+ else if (strcmp(argv[i], "-no_table_create") == 0)
+ {
+ theTableCreateFlag = 1;
+ argc++;
+ i--;
+ }
+ else
+ {
+ ndbout << "Arguments: " << endl;
+ ndbout << "-t Number of threads to start, i.e., number of parallel loops, default 1 " << endl;
+ ndbout << "-p Number of transactions in a batch, default 32 " << endl;
+ ndbout << "-o Number of batches per loop, default 200 " << endl;
+ ndbout << "-i Minimum time between batch starts in milli seconds, default 0 " << endl;
+ ndbout << "-l Number of loops to run, default 1, 0=infinite " << endl;
+ ndbout << "-a Number of attributes, default 25 " << endl;
+ ndbout << "-c Number of operations per transaction, default 1 " << endl;
+ ndbout << "-s Size of each attribute in 32 bit word, default 1"
+ "(Primary Key is always of size 1, independent of this value) " << endl;
+ ndbout << "-simple Use simple read to read from database " << endl;
+ ndbout << "-dirty Use dirty read to read from database " << endl;
+ ndbout << "-write Use writeTuple in insert and update " << endl;
+ ndbout << "-n Use standard table names " << endl;
+ ndbout << "-no_table_create Don't create tables in db " << endl;
+ ndbout << "-temp Use temporary tables, no writing to disk. " << endl;
+ exit(-1);
+ }
+
+ argc -= 2;
+ i = i + 2;
+ }
+}
+
+void setAttrNames()
+{
+ int i;
+
+ for (i = 0; i < MAXATTR ; i++)
+ {
+ sprintf(attrName[i], "COL%d", i);
+ }
+}
+
+
+void setTableNames()
+{
+ // Note! Uses only uppercase letters in table name's
+ // so that we can look at the tables with SQL
+ int i;
+ for (i = 0; i < MAXTABLES ; i++)
+ {
+ if (theStdTableNameFlag==1)
+ {
+ sprintf(tableName[i], "TAB%d_%d", tNoOfAttributes,
+ NdbTick_CurrentMillisecond()/1000);
+ } else {
+ sprintf(tableName[i], "TAB%d_%d", tNoOfAttributes, tAttributeSize*4);
+ }
+ }
+}
+
+void createTables(Ndb* pMyNdb)
+{
+
+ NdbSchemaCon *MySchemaTransaction;
+ NdbSchemaOp *MySchemaOp;
+ int check;
+
+ if (theTableCreateFlag == 0)
+ {
+ for(int i=0; i < 1 ;i++)
+ {
+ ndbout << "Creating " << tableName[i] << "..." << endl;
+ MySchemaTransaction = pMyNdb->startSchemaTransaction();
+
+ if( MySchemaTransaction ==
+ NULL && (!error_handler(MySchemaTransaction->getNdbError())))
+ exit(-1) ;/*goto error_handler; <epaulsa*/
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL
+ && (!error_handler(MySchemaTransaction->getNdbError())))
+ exit(-1) ;
+
+ check = MySchemaOp->createTable( tableName[i],
+ 8, // Table Size
+ TupleKey, // Key Type
+ 40, // Nr of Pages
+ All, // FragmentType
+ 6,
+ 78,
+ 80,
+ 1, // MemoryType
+ theTempFlag // 0 if temporary tables else 1
+ );
+
+ if ( check == -1 && (!error_handler(MySchemaTransaction->getNdbError())))
+ exit(-1) ; /* epaulsa > goto error_handler; < epaulsa */
+
+
+ check = MySchemaOp->createAttribute( (char*)attrName[0],
+ TupleKey,
+ 32,
+ PKSIZE,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if ( check == -1 &&(!error_handler(MySchemaTransaction->getNdbError())))
+ exit(-1) ; /* epaulsa > goto error_handler; < epaulsa */
+
+ for (int j = 1; j < tNoOfAttributes ; j++)
+ {
+ check = MySchemaOp->createAttribute( (char*)attrName[j],
+ NoKey,
+ 32,
+ tAttributeSize,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if ( check == -1
+ && (!error_handler(MySchemaTransaction->getNdbError())))
+ exit(-1) ; /* epaulsa > goto error_handler; < epaulsa */
+ }
+
+ if ( MySchemaTransaction->execute() == -1
+ &&(!error_handler(MySchemaTransaction->getNdbError())))
+ exit(-1) ; /* epaulsa > goto error_handler; < epaulsa */
+
+ pMyNdb->closeSchemaTransaction(MySchemaTransaction);
+ }
+ }
+
+ return;
+}
+
+bool error_handler(const NdbError & err) {
+ ndbout << err << endl ;
+ if ( 4008==err.code || 721==err.code || 266==err.code ){
+ ndbout << endl << "Attempting to recover and continue now..." << endl ;
+ return true ; // return true to retry
+ }
+ return false ; // return false to abort
+}
+
+
+//*******************************************************************************************
+
+
+
+
+
diff --git a/storage/ndb/test/ndbapi/flex_bench_mysql.cpp b/storage/ndb/test/ndbapi/flex_bench_mysql.cpp
new file mode 100644
index 00000000000..3efb7ee2094
--- /dev/null
+++ b/storage/ndb/test/ndbapi/flex_bench_mysql.cpp
@@ -0,0 +1,1751 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* ***************************************************
+FLEXBENCH
+Perform benchmark of insert, update and delete transactions
+
+Arguments:
+ -t Number of threads to start, default 1
+ -o Number of operations per loop, default 500
+ -l Number of loops to run, default 1, 0=infinite
+ -a Number of attributes, default 25
+ -c Number of tables, default 1
+ -s Size of each attribute, default 1 (Primary Key is always of size 1,
+ independent of this value)
+ -lkn Number of long primary keys, default 1
+ -lks Size of each long primary key, default 1
+ -simple Use simple read to read from database
+ -dirty Use dirty read to read from database
+ -write Use writeTuple in insert and update
+ -stdtables Use standard table names
+ -no_table_create Don't create tables in db
+ -sleep Sleep a number of seconds before running the test, this
+ can be used so that another flexBench have time to create tables
+ -temp Use tables without logging
+ -verify Verify inserts, updates and deletes
+ -use_ndb Use NDB API, otherwise use mysql client
+#ifdef CEBIT_STAT
+ -statserv host:port statistics server to report to
+ -statfreq ops report every ops operations (default 100)
+#endif
+ Returns:
+ 0 - Test passed
+ 1 - Test failed
+ 2 - Invalid arguments
+
+* *************************************************** */
+
+#define USE_MYSQL
+#ifdef USE_MYSQL
+#include <mysql.h>
+#endif
+
+#include "NdbApi.hpp"
+
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <NdbTimer.hpp>
+#include <NdbThread.h>
+#include <NdbAutoPtr.hpp>
+
+#include <NdbTest.hpp>
+
+#define MAXSTRLEN 16
+#define MAXATTR 64
+#define MAXTABLES 128
+#define MAXATTRSIZE 1000
+#define MAXNOLONGKEY 16 // Max number of long keys.
+#define MAXLONGKEYTOTALSIZE 1023 // words = 4092 bytes
+
+extern "C" { static void* flexBenchThread(void*); }
+static int readArguments(int argc, const char** argv);
+#ifdef USE_MYSQL
+static int createTables(MYSQL*);
+static int dropTables(MYSQL*);
+#endif
+static int createTables(Ndb*);
+static void sleepBeforeStartingTest(int seconds);
+static void input_error();
+
+enum StartType {
+ stIdle,
+ stInsert,
+ stVerify,
+ stRead,
+ stUpdate,
+ stDelete,
+ stTryDelete,
+ stVerifyDelete,
+ stStop
+};
+
+struct ThreadData
+{
+ int threadNo;
+ NdbThread* threadLife;
+ int threadReady;
+ StartType threadStart;
+ int threadResult;
+};
+
+static int tNodeId = 0 ;
+static char tableName[MAXTABLES][MAXSTRLEN+1];
+static char attrName[MAXATTR][MAXSTRLEN+1];
+static char** longKeyAttrName;
+
+// Program Parameters
+static int tNoOfLoops = 1;
+static int tAttributeSize = 1;
+static unsigned int tNoOfThreads = 1;
+static unsigned int tNoOfTables = 1;
+static unsigned int tNoOfAttributes = 25;
+static unsigned int tNoOfOperations = 500;
+static unsigned int tSleepTime = 0;
+static unsigned int tNoOfLongPK = 1;
+static unsigned int tSizeOfLongPK = 1;
+static unsigned int t_instances = 1;
+
+//Program Flags
+static int theSimpleFlag = 0;
+static int theDirtyFlag = 0;
+static int theWriteFlag = 0;
+static int theStdTableNameFlag = 0;
+static int theTableCreateFlag = 0;
+static bool theTempTable = false;
+static bool VerifyFlag = true;
+static bool useLongKeys = false;
+static bool verbose = false;
+#ifdef USE_MYSQL
+static bool use_ndb = false;
+static int engine_id = 0;
+static int sockets[16];
+static int n_sockets = 0;
+static char* engine[] =
+ {
+ " ENGINE = NDBCLUSTER ", // use default engine
+ " ENGINE = MEMORY ",
+ " ENGINE = MYISAM ",
+ " ENGINE = INNODB "
+ };
+#else
+static bool use_ndb = true;
+#endif
+
+static ErrorData theErrorData; // Part of flexBench-program
+
+#define START_TIMER { NdbTimer timer; timer.doStart();
+#define STOP_TIMER timer.doStop();
+#define PRINT_TIMER(text, trans, opertrans) timer.printTransactionStatistics(text, trans, opertrans); };
+
+#include <NdbTCP.h>
+
+#ifdef CEBIT_STAT
+#include <NdbMutex.h>
+static bool statEnable = false;
+static char statHost[100];
+static int statFreq = 100;
+static int statPort = 0;
+static int statSock = -1;
+static enum { statError = -1, statClosed, statOpen } statState;
+static NdbMutex statMutex = NDB_MUTEX_INITIALIZER;
+#endif
+
+//-------------------------------------------------------------------
+// Statistical Reporting routines
+//-------------------------------------------------------------------
+#ifdef CEBIT_STAT
+// Experimental client-side statistic for CeBIT
+
+static void
+statReport(enum StartType st, int ops)
+{
+ if (!statEnable)
+ return;
+ if (NdbMutex_Lock(&statMutex) < 0) {
+ if (statState != statError) {
+ ndbout_c("stat: lock mutex failed: %s", strerror(errno));
+ statState = statError;
+ }
+ return;
+ }
+ static int nodeid;
+ // open connection
+ if (statState != statOpen) {
+ char *p = getenv("NDB_NODEID"); // ndbnet sets NDB_NODEID
+ nodeid = p == 0 ? 0 : atoi(p);
+ if ((statSock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+ if (statState != statError) {
+ ndbout_c("stat: create socket failed: %s", strerror(errno));
+ statState = statError;
+ }
+ (void)NdbMutex_Unlock(&statMutex);
+ return;
+ }
+ struct sockaddr_in saddr;
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(statPort);
+ if (Ndb_getInAddr(&saddr.sin_addr, statHost) < 0) {
+ if (statState != statError) {
+ ndbout_c("stat: host %s not found", statHost);
+ statState = statError;
+ }
+ (void)close(statSock);
+ (void)NdbMutex_Unlock(&statMutex);
+ return;
+ }
+ if (connect(statSock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
+ if (statState != statError) {
+ ndbout_c("stat: connect failed: %s", strerror(errno));
+ statState = statError;
+ }
+ (void)close(statSock);
+ (void)NdbMutex_Unlock(&statMutex);
+ return;
+ }
+ statState = statOpen;
+ ndbout_c("stat: connection to %s:%d opened", statHost, (int)statPort);
+ }
+ const char *text;
+ switch (st) {
+ case stInsert:
+ text = "insert";
+ break;
+ case stVerify:
+ text = "verify";
+ break;
+ case stRead:
+ text = "read";
+ break;
+ case stUpdate:
+ text = "update";
+ break;
+ case stDelete:
+ text = "delete";
+ break;
+ case stVerifyDelete:
+ text = "verifydelete";
+ break;
+ default:
+ text = "unknown";
+ break;
+ }
+ char buf[100];
+ sprintf(buf, "%d %s %d\n", nodeid, text, ops);
+ int len = strlen(buf);
+ // assume SIGPIPE already ignored
+ if (write(statSock, buf, len) != len) {
+ if (statState != statError) {
+ ndbout_c("stat: write failed: %s", strerror(errno));
+ statState = statError;
+ }
+ (void)close(statSock);
+ (void)NdbMutex_Unlock(&statMutex);
+ return;
+ }
+ (void)NdbMutex_Unlock(&statMutex);
+}
+#endif // CEBIT_STAT
+
+static void
+resetThreads(ThreadData* pt){
+ for (unsigned int i = 0; i < tNoOfThreads; i++){
+ pt[i].threadReady = 0;
+ pt[i].threadResult = 0;
+ pt[i].threadStart = stIdle;
+ }
+}
+
+static int
+checkThreadResults(ThreadData* pt){
+ for (unsigned int i = 0; i < tNoOfThreads; i++){
+ if(pt[i].threadResult != 0){
+ ndbout_c("Thread%d reported fatal error %d", i, pt[i].threadResult);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static
+void
+waitForThreads(ThreadData* pt)
+{
+ int cont = 1;
+ while (cont){
+ NdbSleep_MilliSleep(100);
+ cont = 0;
+ for (unsigned int i = 0; i < tNoOfThreads; i++){
+ if (pt[i].threadReady == 0)
+ cont = 1;
+ }
+ }
+}
+
+static void
+tellThreads(ThreadData* pt, StartType what)
+{
+ for (unsigned int i = 0; i < tNoOfThreads; i++)
+ pt[i].threadStart = what;
+}
+
+NDB_COMMAND(flexBench, "flexBench", "flexBench", "flexbench", 65535)
+{
+ ndb_init();
+ ThreadData* pThreadsData;
+ int tLoops = 0;
+ int returnValue = NDBT_OK;
+ if (readArguments(argc, argv) != 0){
+ input_error();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ NdbAutoPtr<char> p10;
+ if(useLongKeys){
+ int e1 = sizeof(char*) * tNoOfLongPK;
+ int e2_1 = strlen("KEYATTR ") + 1;
+ int e2 = e2_1 * tNoOfLongPK;
+ char *tmp = (char *) malloc(e1 + e2);
+ p10.reset(tmp);
+ longKeyAttrName = (char **) tmp;
+ tmp += e1;
+ for (Uint32 i = 0; i < tNoOfLongPK; i++) {
+ // longKeyAttrName[i] = (char *) malloc(strlen("KEYATTR ") + 1);
+ longKeyAttrName[i] = tmp;
+ tmp += e2_1;
+ memset(longKeyAttrName[i], 0, e2_1);
+ sprintf(longKeyAttrName[i], "KEYATTR%i", i);
+ }
+ }
+
+ NdbAutoObjArrayPtr<ThreadData>
+ p12( pThreadsData = new ThreadData[tNoOfThreads] );
+
+
+ ndbout << endl << "FLEXBENCH - Starting normal mode" << endl;
+ ndbout << "Perform benchmark of insert, update and delete transactions"<< endl;
+ ndbout << " " << tNoOfThreads << " thread(s) " << endl;
+ ndbout << " " << tNoOfLoops << " iterations " << endl;
+ ndbout << " " << tNoOfTables << " table(s) and " << 1 << " operation(s) per transaction " <<endl;
+ ndbout << " " << tNoOfAttributes << " attributes per table " << endl;
+ ndbout << " " << tNoOfOperations << " transaction(s) per thread and round " << endl;
+ ndbout << " " << tAttributeSize << " is the number of 32 bit words per attribute "<< endl;
+ ndbout << " " << "Table(s) without logging: " << (Uint32)theTempTable << endl;
+
+ if(useLongKeys)
+ ndbout << " " << "Using long keys with " << tNoOfLongPK << " keys a' " <<
+ tSizeOfLongPK * 4 << " bytes each." << endl;
+
+ ndbout << " " << "Verification is " ;
+ if(VerifyFlag) {
+ ndbout << "enabled" << endl ;
+ }else{
+ ndbout << "disabled" << endl ;
+ }
+ if (use_ndb) {
+ ndbout << "Use NDB API with NdbPool in this test case" << endl;
+ ndbout << "Pool size = " << t_instances << endl;
+ } else {
+ ndbout << "Use mysql client with " << engine[engine_id];
+ ndbout << " as engine" << endl;
+ }
+ theErrorData.printSettings(ndbout);
+
+ NdbThread_SetConcurrencyLevel(tNoOfThreads + 2);
+
+#ifdef USE_MYSQL
+ MYSQL mysql;
+ if (!use_ndb) {
+ if ( mysql_thread_safe() == 0 ) {
+ ndbout << "Not thread safe mysql library..." << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ ndbout << "Connecting to MySQL..." <<endl;
+
+ mysql_init(&mysql);
+ {
+ int the_socket = sockets[0];
+ char the_socket_name[1024];
+ sprintf(the_socket_name, "%s%u%s", "/tmp/mysql.",the_socket,".sock");
+ // sprintf(the_socket_name, "%s", "/tmp/mysql.sock");
+ ndbout << the_socket_name << endl;
+ if ( mysql_real_connect(&mysql,
+ "localhost",
+ "root",
+ "",
+ "test",
+ the_socket,
+ the_socket_name,
+ 0) == NULL ) {
+ ndbout << "Connect failed" <<endl;
+ returnValue = NDBT_FAILED;
+ }
+ mysql.reconnect= 1;
+ }
+ if(returnValue == NDBT_OK){
+ mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON);
+ if (createTables(&mysql) != 0){
+ returnValue = NDBT_FAILED;
+ }
+ }
+ }
+#endif
+ if (use_ndb) {
+ Uint32 ndb_id = 0;
+ if (!create_instance(t_instances, 1, t_instances)) {
+ ndbout << "Creation of the NdbPool failed" << endl;
+ returnValue = NDBT_FAILED;
+ } else {
+ Ndb* pNdb = get_ndb_object(ndb_id, "test", "def");
+ if (pNdb == NULL) {
+ ndbout << "Failed to get a NDB object" << endl;
+ returnValue = NDBT_FAILED;
+ } else {
+ tNodeId = pNdb->getNodeId();
+ ndbout << " NdbAPI node with id = " << tNodeId << endl;
+ ndbout << endl;
+
+ ndbout << "Waiting for ndb to become ready..." <<endl;
+ if (pNdb->waitUntilReady(2000) != 0){
+ ndbout << "NDB is not ready" << endl;
+ ndbout << "Benchmark failed!" << endl;
+ returnValue = NDBT_FAILED;
+ }
+ if(returnValue == NDBT_OK){
+ if (createTables(pNdb) != 0){
+ returnValue = NDBT_FAILED;
+ }
+ }
+ return_ndb_object(pNdb, ndb_id);
+ }
+ }
+ }
+ if(returnValue == NDBT_OK){
+
+ sleepBeforeStartingTest(tSleepTime);
+
+ /****************************************************************
+ * Create threads. *
+ ****************************************************************/
+ resetThreads(pThreadsData);
+
+ for (unsigned int i = 0; i < tNoOfThreads; i++){
+ pThreadsData[i].threadNo = i;
+ pThreadsData[i].threadLife = NdbThread_Create(flexBenchThread,
+ (void**)&pThreadsData[i],
+ 32768,
+ "flexBenchThread",
+ NDB_THREAD_PRIO_LOW);
+ }
+
+ waitForThreads(pThreadsData);
+
+ ndbout << endl << "All threads started" << endl << endl;
+
+ /****************************************************************
+ * Execute program. *
+ ****************************************************************/
+
+ for(;;){
+
+ int loopCount = tLoops + 1;
+ ndbout << endl << "Loop # " << loopCount << endl << endl;
+
+ /****************************************************************
+ * Perform inserts. *
+ ****************************************************************/
+ // Reset and start timer
+ START_TIMER;
+ // Give insert-command to all threads
+ resetThreads(pThreadsData);
+ tellThreads(pThreadsData, stInsert);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed in performing insert" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }
+ // stop timer and print results.
+ STOP_TIMER;
+ PRINT_TIMER("insert", tNoOfOperations*tNoOfThreads, tNoOfTables);
+ /****************************************************************
+ * Verify inserts. *
+ ****************************************************************/
+ if (VerifyFlag) {
+ resetThreads(pThreadsData);
+ ndbout << "Verifying inserts...\t" ;
+ tellThreads(pThreadsData, stVerify);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed while verifying inserts" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }else{
+ ndbout << "\t\tOK" << endl << endl ;
+ }
+ }
+
+ /****************************************************************
+ * Perform read. *
+ ****************************************************************/
+ // Reset and start timer
+ START_TIMER;
+ // Give read-command to all threads
+ resetThreads(pThreadsData);
+ tellThreads(pThreadsData, stRead);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed in performing read" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }
+ // stop timer and print results.
+ STOP_TIMER;
+ PRINT_TIMER("read", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ /****************************************************************
+ * Perform update. *
+ ****************************************************************/
+ // Reset and start timer
+ START_TIMER;
+ // Give update-command to all threads
+ resetThreads(pThreadsData);
+ tellThreads(pThreadsData, stUpdate);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed in performing update" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }
+ // stop timer and print results.
+ STOP_TIMER;
+ PRINT_TIMER("update", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ /****************************************************************
+ * Verify updates. *
+ ****************************************************************/
+ if (VerifyFlag) {
+ resetThreads(pThreadsData);
+ ndbout << "Verifying updates...\t" ;
+ tellThreads(pThreadsData, stVerify);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed while verifying updates" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }else{
+ ndbout << "\t\tOK" << endl << endl ;
+ }
+ }
+
+ /****************************************************************
+ * Perform read. *
+ ****************************************************************/
+ // Reset and start timer
+ START_TIMER;
+ // Give read-command to all threads
+ resetThreads(pThreadsData);
+ tellThreads(pThreadsData, stRead);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed in performing read" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }
+ // stop timer and print results.
+ STOP_TIMER;
+ PRINT_TIMER("read", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ /****************************************************************
+ * Perform delete. *
+ ****************************************************************/
+ // Reset and start timer
+ START_TIMER;
+ // Give delete-command to all threads
+ resetThreads(pThreadsData);
+ tellThreads(pThreadsData, stDelete);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed in performing delete" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }
+ // stop timer and print results.
+ STOP_TIMER;
+ PRINT_TIMER("delete", tNoOfOperations*tNoOfThreads, tNoOfTables);
+
+ /****************************************************************
+ * Verify deletes. *
+ ****************************************************************/
+ if (VerifyFlag) {
+ resetThreads(pThreadsData);
+ ndbout << "Verifying tuple deletion..." ;
+ tellThreads(pThreadsData, stVerifyDelete);
+ waitForThreads(pThreadsData);
+ if (checkThreadResults(pThreadsData) != 0){
+ ndbout << "Error: Threads failed in verifying deletes" << endl;
+ returnValue = NDBT_FAILED;
+ break;
+ }else{
+ ndbout << "\t\tOK" << endl << endl ;
+ }
+ }
+
+ ndbout << "--------------------------------------------------" << endl;
+
+ tLoops++;
+
+ if ( 0 != tNoOfLoops && tNoOfLoops <= tLoops )
+ break;
+ theErrorData.printErrorCounters();
+ }
+
+ resetThreads(pThreadsData);
+ tellThreads(pThreadsData, stStop);
+ waitForThreads(pThreadsData);
+
+ void * tmp;
+ for(Uint32 i = 0; i<tNoOfThreads; i++){
+ NdbThread_WaitFor(pThreadsData[i].threadLife, &tmp);
+ NdbThread_Destroy(&pThreadsData[i].threadLife);
+ }
+ }
+#ifdef USE_MYSQL
+ if (!use_ndb) {
+ dropTables(&mysql);
+ mysql_close(&mysql);
+ }
+#endif
+ if (use_ndb) {
+ drop_instance();
+ }
+ theErrorData.printErrorCounters();
+ return NDBT_ProgramExit(returnValue);
+}
+////////////////////////////////////////
+
+
+unsigned long get_hash(unsigned long * hash_key, int len)
+{
+ unsigned long hash_value = 147;
+ unsigned h_key;
+ int i;
+ for (i = 0; i < len; i++)
+ {
+ h_key = hash_key[i];
+ hash_value = (hash_value << 5) + hash_value + (h_key & 255);
+ hash_value = (hash_value << 5) + hash_value + ((h_key >> 8) & 255);
+ hash_value = (hash_value << 5) + hash_value + ((h_key >> 16) & 255);
+ hash_value = (hash_value << 5) + hash_value + ((h_key >> 24) & 255);
+ }
+ return hash_value;
+}
+
+// End of warming up phase
+
+
+
+static void* flexBenchThread(void* pArg)
+{
+ ThreadData* pThreadData = (ThreadData*)pArg;
+ unsigned int threadNo, threadBase;
+ Ndb* pNdb = NULL ;
+ Uint32 ndb_id = 0;
+ NdbConnection *pTrans = NULL ;
+ NdbOperation** pOps = NULL ;
+ StartType tType ;
+ StartType tSaveType ;
+ NdbRecAttr* tTmp = NULL ;
+ int* attrValue = NULL ;
+ int* attrRefValue = NULL ;
+ int check = 0 ;
+ int loopCountOps, loopCountTables, loopCountAttributes;
+ int tAttemptNo = 0;
+ int tRetryAttempts = 20;
+ int tResult = 0;
+ int tSpecialTrans = 0;
+ int nRefLocalOpOffset = 0 ;
+ int nReadBuffSize =
+ tNoOfTables * tNoOfAttributes * sizeof(int) * tAttributeSize ;
+ int nRefBuffSize =
+ tNoOfOperations * tNoOfAttributes * sizeof(int) * tAttributeSize ;
+ unsigned*** longKeyAttrValue = NULL;
+
+
+ threadNo = pThreadData->threadNo ;
+
+#ifdef USE_MYSQL
+ MYSQL mysql;
+ int the_socket = sockets[threadNo % n_sockets];
+ char the_socket_name[1024];
+ //sprintf(the_socket_name, "%s", "/tmp/mysql.sock");
+ sprintf(the_socket_name, "%s%u%s", "/tmp/mysql.",the_socket,".sock");
+ if (!use_ndb) {
+ ndbout << the_socket_name << endl;
+ ndbout << "Thread connecting to MySQL... " << endl;
+ mysql_init(&mysql);
+
+ if ( mysql_real_connect(&mysql,
+ "localhost",
+ "root",
+ "",
+ "test",
+ the_socket,
+ the_socket_name,
+ 0) == NULL ) {
+ ndbout << "failed" << endl;
+ return 0;
+ }
+ mysql.reconnect= 1;
+ ndbout << "ok" << endl;
+
+ int r;
+ if (tNoOfTables > 1)
+ r = mysql_autocommit(&mysql, 0);
+ else
+ r = mysql_autocommit(&mysql, 1);
+
+ if (r) {
+ ndbout << "autocommit on/off failed" << endl;
+ return 0;
+ }
+ }
+#endif
+
+ NdbAutoPtr<int> p00( attrValue= (int*)malloc(nReadBuffSize) ) ;
+ NdbAutoPtr<int> p01( attrRefValue= (int*)malloc(nRefBuffSize) );
+ if (use_ndb) {
+ pOps = (NdbOperation**)malloc(tNoOfTables*sizeof(NdbOperation*)) ;
+ }
+ NdbAutoPtr<NdbOperation*> p02( pOps );
+
+ if( !attrValue || !attrRefValue ||
+ ( use_ndb && ( !pOps) ) ){
+ // Check allocations to make sure we got all the memory we asked for
+ ndbout << "One or more memory allocations failed when starting thread #";
+ ndbout << threadNo << endl ;
+ ndbout << "Thread #" << threadNo << " will now exit" << endl ;
+ tResult = 13 ;
+ return 0;
+ }
+
+ if (use_ndb) {
+ pNdb = get_ndb_object(ndb_id, "test", "def");
+ if (pNdb == NULL) {
+ ndbout << "Failed to get an NDB object" << endl;
+ ndbout << "Thread #" << threadNo << " will now exit" << endl ;
+ tResult = 13;
+ return 0;
+ }
+ pNdb->waitUntilReady();
+ return_ndb_object(pNdb, ndb_id);
+ pNdb = NULL;
+ }
+
+ // To make sure that two different threads doesn't operate on the same record
+ // Calculate an "unique" number to use as primary key
+ threadBase = (threadNo * 2000000) + (tNodeId * 260000000);
+
+ NdbAutoPtr<char> p22;
+ if(useLongKeys){
+ // Allocate and populate the longkey array.
+ int e1 = sizeof(unsigned**) * tNoOfOperations;
+ int e2 = sizeof(unsigned*) * tNoOfLongPK * tNoOfOperations;
+ int e3 = sizeof(unsigned) * tSizeOfLongPK * tNoOfLongPK * tNoOfOperations;
+ char* tmp;
+ p22.reset(tmp = (char*)malloc(e1+e2+e3));
+
+ longKeyAttrValue = (unsigned ***) tmp;
+ tmp += e1;
+ for (Uint32 n = 0; n < tNoOfOperations; n++) {
+ longKeyAttrValue[n] = (unsigned **) tmp;
+ tmp += sizeof(unsigned*) * tNoOfLongPK;
+ }
+
+ for (Uint32 n = 0; n < tNoOfOperations; n++){
+ for (Uint32 i = 0; i < tNoOfLongPK ; i++) {
+ longKeyAttrValue[n][i] = (unsigned *) tmp;
+ tmp += sizeof(unsigned) * tSizeOfLongPK;
+ memset(longKeyAttrValue[n][i], 0, sizeof(unsigned) * tSizeOfLongPK);
+ for(Uint32 j = 0; j < tSizeOfLongPK; j++) {
+ // Repeat the unique value to fill up the long key.
+ longKeyAttrValue[n][i][j] = threadBase + n;
+ }
+ }
+ }
+ }
+
+ int nRefOpOffset = 0 ;
+ //Assign reference attribute values to memory
+ for(Uint32 ops = 1 ; ops < tNoOfOperations ; ops++){
+ // Calculate offset value before going into the next loop
+ nRefOpOffset = tAttributeSize*tNoOfAttributes*(ops-1) ;
+ for(Uint32 a = 0 ; a < tNoOfAttributes ; a++){
+ *(int*)&attrRefValue[nRefOpOffset + tAttributeSize*a] =
+ (int)(threadBase + ops + a) ;
+ }
+ }
+
+#ifdef CEBIT_STAT
+ // ops not yet reported
+ int statOps = 0;
+#endif
+
+#ifdef USE_MYSQL
+ // temporary buffer to store prepared statement text
+ char buf[2048];
+ MYSQL_STMT** prep_read = NULL;
+ MYSQL_STMT** prep_delete = NULL;
+ MYSQL_STMT** prep_update = NULL;
+ MYSQL_STMT** prep_insert = NULL;
+ MYSQL_BIND* bind_delete = NULL;
+ MYSQL_BIND* bind_read = NULL;
+ MYSQL_BIND* bind_update = NULL;
+ MYSQL_BIND* bind_insert = NULL;
+ int* mysql_data = NULL;
+
+ NdbAutoPtr<char> p21;
+
+ if (!use_ndb) {
+ // data array to which prepared statements are bound
+ char* tmp;
+ int e1 = sizeof(int)*tAttributeSize*tNoOfAttributes;
+ int e2 = sizeof(MYSQL_BIND)*tNoOfAttributes;
+ int e3 = sizeof(MYSQL_BIND)*tNoOfAttributes;
+ int e4 = sizeof(MYSQL_BIND)*tNoOfAttributes;
+ int e5 = sizeof(MYSQL_BIND)*1;
+ int e6 = sizeof(MYSQL_STMT*)*tNoOfTables;
+ int e7 = sizeof(MYSQL_STMT*)*tNoOfTables;
+ int e8 = sizeof(MYSQL_STMT*)*tNoOfTables;
+ int e9 = sizeof(MYSQL_STMT*)*tNoOfTables;
+ p21.reset(tmp = (char*)malloc(e1+e2+e3+e4+e5+e6+e7+e8+e9));
+
+ mysql_data = (int*)tmp; tmp += e1;
+ bind_insert = (MYSQL_BIND*)tmp; tmp += e2;
+ bind_update = (MYSQL_BIND*)tmp; tmp += e3;
+ bind_read = (MYSQL_BIND*)tmp; tmp += e4;
+ bind_delete = (MYSQL_BIND*)tmp; tmp += e5;
+ prep_insert = (MYSQL_STMT**)tmp; tmp += e6;
+ prep_update = (MYSQL_STMT**)tmp; tmp += e7;
+ prep_read = (MYSQL_STMT**)tmp; tmp += e8;
+ prep_delete = (MYSQL_STMT**)tmp;
+
+ for (Uint32 ca = 0; ca < tNoOfAttributes; ca++){
+ MYSQL_BIND& bi = bind_insert[ca];
+ bi.buffer_type = MYSQL_TYPE_LONG;
+ bi.buffer = (char*)&mysql_data[ca*tAttributeSize];
+ bi.buffer_length = 0;
+ bi.length = NULL;
+ bi.is_null = NULL;
+ }//for
+
+ for (Uint32 ca = 0; ca < tNoOfAttributes; ca++){
+ MYSQL_BIND& bi = bind_update[ca];
+ bi.buffer_type = MYSQL_TYPE_LONG;
+ if ( ca == tNoOfAttributes-1 ) // the primary key comes last in statement
+ bi.buffer = (char*)&mysql_data[0];
+ else
+ bi.buffer = (char*)&mysql_data[(ca+1)*tAttributeSize];
+ bi.buffer_length = 0;
+ bi.length = NULL;
+ bi.is_null = NULL;
+ }//for
+
+ for (Uint32 ca = 0; ca < tNoOfAttributes; ca++){
+ MYSQL_BIND& bi = bind_read[ca];
+ bi.buffer_type = MYSQL_TYPE_LONG;
+ bi.buffer = (char*)&mysql_data[ca*tAttributeSize];
+ bi.buffer_length = 4;
+ bi.length = NULL;
+ bi.is_null = NULL;
+ }//for
+
+ for (Uint32 ca = 0; ca < 1; ca++){
+ MYSQL_BIND& bi = bind_delete[ca];
+ bi.buffer_type = MYSQL_TYPE_LONG;
+ bi.buffer = (char*)&mysql_data[ca*tAttributeSize];
+ bi.buffer_length = 0;
+ bi.length = NULL;
+ bi.is_null = NULL;
+ }//for
+
+ for (Uint32 i = 0; i < tNoOfTables; i++) {
+ int pos = 0;
+ pos += sprintf(buf+pos, "%s%s%s",
+ "INSERT INTO ",
+ tableName[i],
+ " VALUES(");
+ pos += sprintf(buf+pos, "%s", "?");
+ for (Uint32 j = 1; j < tNoOfAttributes; j++) {
+ pos += sprintf(buf+pos, "%s", ",?");
+ }
+ pos += sprintf(buf+pos, "%s", ")");
+ if (verbose)
+ ndbout << buf << endl;
+ prep_insert[i] = mysql_prepare(&mysql, buf, pos);
+ if (prep_insert[i] == 0) {
+ ndbout << "mysql_prepare: " << mysql_error(&mysql) << endl;
+ return 0;
+ }
+ if (mysql_bind_param(prep_insert[i], bind_insert)) {
+ ndbout << "mysql_bind_param: " << mysql_error(&mysql) << endl;
+ return 0;
+ }
+ }
+
+ for (Uint32 i = 0; i < tNoOfTables; i++) {
+ int pos = 0;
+ pos += sprintf(buf+pos, "%s%s%s",
+ "UPDATE ",
+ tableName[i],
+ " SET ");
+ for (Uint32 j = 1; j < tNoOfAttributes; j++) {
+ if (j != 1)
+ pos += sprintf(buf+pos, "%s", ",");
+ pos += sprintf(buf+pos, "%s%s", attrName[j],"=?");
+ }
+ pos += sprintf(buf+pos, "%s%s%s", " WHERE ", attrName[0], "=?");
+
+ if (verbose)
+ ndbout << buf << endl;
+ prep_update[i] = mysql_prepare(&mysql, buf, pos);
+ if (prep_update[i] == 0) {
+ ndbout << "mysql_prepare: " << mysql_error(&mysql) << endl;
+ return 0;
+ }
+ if (mysql_bind_param(prep_update[i], bind_update)) {
+ ndbout << "mysql_bind_param: " << mysql_error(&mysql) << endl;
+ return 0;
+ }
+ }
+
+ for (Uint32 i = 0; i < tNoOfTables; i++) {
+ int pos = 0;
+ pos += sprintf(buf+pos, "%s", "SELECT ");
+ for (Uint32 j = 1; j < tNoOfAttributes; j++) {
+ if (j != 1)
+ pos += sprintf(buf+pos, "%s", ",");
+ pos += sprintf(buf+pos, "%s", attrName[j]);
+ }
+ pos += sprintf(buf+pos, "%s%s%s%s%s",
+ " FROM ",
+ tableName[i],
+ " WHERE ",
+ attrName[0],
+ "=?");
+ if (verbose)
+ ndbout << buf << endl;
+ prep_read[i] = mysql_prepare(&mysql, buf, pos);
+ if (prep_read[i] == 0) {
+ ndbout << "mysql_prepare: " << mysql_error(&mysql) << endl;
+ return 0;
+ }
+ if (mysql_bind_param(prep_read[i], bind_read)) {
+ ndbout << "mysql_bind_param: " << mysql_error(&mysql) << endl;
+ return 0;
+ }
+ if (mysql_bind_result(prep_read[i], &bind_read[1])) {
+ ndbout << "mysql_bind_result: " << mysql_error(&mysql) << endl;
+ return 0;
+ }
+ }
+
+ for (Uint32 i = 0; i < tNoOfTables; i++) {
+ int pos = 0;
+ pos += sprintf(buf+pos, "%s%s%s%s%s",
+ "DELETE FROM ",
+ tableName[i],
+ " WHERE ",
+ attrName[0],
+ "=?");
+ if (verbose)
+ ndbout << buf << endl;
+ prep_delete[i] = mysql_prepare(&mysql, buf, pos);
+ if (prep_delete[i] == 0) {
+ ndbout << "mysql_prepare: " << mysql_error(&mysql) << endl;
+ return 0;
+ }
+ if (mysql_bind_param(prep_delete[i], bind_delete)) {
+ ndbout << "mysql_bind_param: " << mysql_error(&mysql) << endl;
+ return 0;
+ }
+ }
+ }
+#endif
+
+ for (;;) {
+ pThreadData->threadResult = tResult; // Report error to main thread,
+ // normally tResult is set to 0
+ pThreadData->threadReady = 1;
+
+ while (pThreadData->threadStart == stIdle){
+ NdbSleep_MilliSleep(100);
+ }//while
+
+ // Check if signal to exit is received
+ if (pThreadData->threadStart == stStop){
+ pThreadData->threadReady = 1;
+ // ndbout_c("Thread%d is stopping", threadNo);
+ // In order to stop this thread, the main thread has signaled
+ // stStop, break out of the for loop so that destructors
+ // and the proper exit functions are called
+ break;
+ }//if
+
+ tType = pThreadData->threadStart;
+ tSaveType = tType;
+ pThreadData->threadStart = stIdle;
+
+ // Start transaction, type of transaction
+ // is received in the array ThreadStart
+ loopCountOps = tNoOfOperations;
+ loopCountTables = tNoOfTables;
+ loopCountAttributes = tNoOfAttributes;
+ for (int count = 1; count < loopCountOps && tResult == 0;){
+
+ if (use_ndb) {
+ pNdb = get_ndb_object(ndb_id, "test", "def");
+ if (pNdb == NULL) {
+ ndbout << "Could not get Ndb object in thread" << threadNo;
+ ndbout << endl;
+ tResult = 1; //Indicate fatal error
+ break;
+ }
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ // This is a fatal error, abort program
+ ndbout << "Could not start transaction in thread" << threadNo;
+ ndbout << endl;
+ ndbout << pNdb->getNdbError() << endl;
+ tResult = 1; // Indicate fatal error
+ break; // Break out of for loop
+ }
+ }
+
+ // Calculate the current operation offset in the reference array
+ nRefLocalOpOffset = tAttributeSize*tNoOfAttributes*(count - 1) ;
+ int* tmpAttrRefValue = attrRefValue + nRefLocalOpOffset;
+
+ for (int countTables = 0;
+ countTables < loopCountTables && tResult == 0;
+ countTables++) {
+
+ int nTableOffset = tAttributeSize *
+ loopCountAttributes *
+ countTables ;
+
+ int* tmpAttrValue = attrValue + nTableOffset;
+
+ if (use_ndb) {
+ pOps[countTables] = pTrans->getNdbOperation(tableName[countTables]);
+ if (pOps[countTables] == NULL) {
+ // This is a fatal error, abort program
+ ndbout << "getNdbOperation: " << pTrans->getNdbError();
+ tResult = 2; // Indicate fatal error
+ break;
+ }//if
+
+ switch (tType) {
+ case stInsert: // Insert case
+ if (theWriteFlag == 1 && theDirtyFlag == 1)
+ pOps[countTables]->dirtyWrite();
+ else if (theWriteFlag == 1)
+ pOps[countTables]->writeTuple();
+ else
+ pOps[countTables]->insertTuple();
+ break;
+ case stRead: // Read Case
+ if (theSimpleFlag == 1)
+ pOps[countTables]->simpleRead();
+ else if (theDirtyFlag == 1)
+ pOps[countTables]->dirtyRead();
+ else
+ pOps[countTables]->readTuple();
+ break;
+ case stUpdate: // Update Case
+ if (theWriteFlag == 1 && theDirtyFlag == 1)
+ pOps[countTables]->dirtyWrite();
+ else if (theWriteFlag == 1)
+ pOps[countTables]->writeTuple();
+ else if (theDirtyFlag == 1)
+ pOps[countTables]->dirtyUpdate();
+ else
+ pOps[countTables]->updateTuple();
+ break;
+ case stDelete: // Delete Case
+ pOps[countTables]->deleteTuple();
+ break;
+ case stVerify:
+ pOps[countTables]->readTuple();
+ break;
+ case stVerifyDelete:
+ pOps[countTables]->readTuple();
+ break;
+ default:
+ assert(false);
+ }//switch
+
+ if(useLongKeys){
+ // Loop the equal call so the complete key is send to the kernel.
+ for(Uint32 i = 0; i < tNoOfLongPK; i++)
+ pOps[countTables]->equal(longKeyAttrName[i],
+ (char *)longKeyAttrValue[count - 1][i],
+ tSizeOfLongPK*4);
+ }
+ else
+ pOps[countTables]->equal((char*)attrName[0],
+ (char*)&tmpAttrRefValue[0]);
+
+ if (tType == stInsert) {
+ for (int ca = 1; ca < loopCountAttributes; ca++){
+ pOps[countTables]->setValue((char*)attrName[ca],
+ (char*)&tmpAttrRefValue[tAttributeSize*ca]);
+ }//for
+ } else if (tType == stUpdate) {
+ for (int ca = 1; ca < loopCountAttributes; ca++){
+ int* tmp = (int*)&tmpAttrRefValue[tAttributeSize*ca];
+ if (countTables == 0)
+ (*tmp)++;
+ pOps[countTables]->setValue((char*)attrName[ca],(char*)tmp);
+ }//for
+ } else if (tType == stRead || stVerify == tType) {
+ for (int ca = 1; ca < loopCountAttributes; ca++) {
+ tTmp =
+ pOps[countTables]->getValue((char*)attrName[ca],
+ (char*)&tmpAttrValue[tAttributeSize*ca]);
+ }//for
+ } else if (stVerifyDelete == tType) {
+ if(useLongKeys){
+ tTmp = pOps[countTables]->getValue(longKeyAttrName[0],
+ (char*)&tmpAttrValue[0]);
+ } else {
+ tTmp = pOps[countTables]->getValue((char*)attrName[0],
+ (char*)&tmpAttrValue[0]);
+ }
+ }//if
+ } else { // !use_ndb
+#ifndef USE_MYSQL
+ assert(false);
+#else
+ switch (tType)
+ {
+ case stInsert:
+ for (int ca = 0; ca < loopCountAttributes; ca++){
+ mysql_data[ca] = tmpAttrRefValue[tAttributeSize*ca];
+ }//for
+ if (mysql_execute(prep_insert[countTables])) {
+ ndbout << tableName[countTables];
+ ndbout << " mysql_execute: " << mysql_error(&mysql) << endl;
+ tResult = 1 ;
+ }
+ break;
+ case stUpdate: // Update Case
+ mysql_data[0] = tmpAttrRefValue[0];
+ for (int ca = 1; ca < loopCountAttributes; ca++){
+ int* tmp = (int*)&tmpAttrRefValue[tAttributeSize*ca];
+ if (countTables == 0)
+ (*tmp)++;
+ mysql_data[ca] = *tmp;
+ }//for
+ if (mysql_execute(prep_update[countTables])) {
+ ndbout << tableName[countTables];
+ ndbout << " mysql_execute: " << mysql_error(&mysql) << endl;
+ tResult = 2 ;
+ }
+ break;
+ case stVerify:
+ case stRead: // Read Case
+ mysql_data[0] = tmpAttrRefValue[0];
+ if (mysql_execute(prep_read[countTables])) {
+ ndbout << tableName[countTables];
+ ndbout << " mysql_execute: " << mysql_error(&mysql) << endl;
+ tResult = 3 ;
+ break;
+ }
+ if (mysql_stmt_store_result(prep_read[countTables])) {
+ ndbout << tableName[countTables];
+ ndbout << " mysql_stmt_store_result: "
+ << mysql_error(&mysql) << endl;
+ tResult = 4 ;
+ break;
+ }
+ {
+ int rows= 0;
+ int r;
+ while ( (r= mysql_fetch(prep_read[countTables])) == 0 ){
+ rows++;
+ }
+ if ( r == 1 ) {
+ ndbout << tableName[countTables];
+ ndbout << " mysql_fetch: " << mysql_error(&mysql) << endl;
+ tResult = 5 ;
+ break;
+ }
+ if ( rows != 1 ) {
+ ndbout << tableName[countTables];
+ ndbout << " mysql_fetch: rows = " << rows << endl;
+ tResult = 6 ;
+ break;
+ }
+ }
+ {
+ for (int ca = 1; ca < loopCountAttributes; ca++) {
+ tmpAttrValue[tAttributeSize*ca] = mysql_data[ca];
+ }
+ }
+ break;
+ case stDelete: // Delete Case
+ mysql_data[0] = tmpAttrRefValue[0];
+ if (mysql_execute(prep_delete[countTables])) {
+ ndbout << tableName[countTables];
+ ndbout << " mysql_execute: " << mysql_error(&mysql) << endl;
+ tResult = 7 ;
+ break;
+ }
+ break;
+ case stVerifyDelete:
+ {
+ sprintf(buf, "%s%s%s",
+ "SELECT COUNT(*) FROM ",tableName[countTables],";");
+ if (mysql_query(&mysql, buf)) {
+ ndbout << buf << endl;
+ ndbout << "Error: " << mysql_error(&mysql) << endl;
+ tResult = 8 ;
+ break;
+ }
+ MYSQL_RES *res = mysql_store_result(&mysql);
+ if ( res == NULL ) {
+ ndbout << "mysql_store_result: "
+ << mysql_error(&mysql) << endl
+ << "errno: " << mysql_errno(&mysql) << endl;
+ tResult = 9 ;
+ break;
+ }
+ int num_fields = mysql_num_fields(res);
+ int num_rows = mysql_num_rows(res);
+ if ( num_rows != 1 || num_fields != 1 ) {
+ ndbout << tableName[countTables];
+ ndbout << " mysql_store_result: num_rows = " << num_rows
+ << " num_fields = " << num_fields << endl;
+ tResult = 10 ;
+ break;
+ }
+ MYSQL_ROW row = mysql_fetch_row(res);
+ if ( row == NULL ) {
+ ndbout << "mysql_fetch_row: "
+ << mysql_error(&mysql) << endl;
+ tResult = 11 ;
+ break;
+ }
+ if ( *(char*)row[0] != '0' ) {
+ ndbout << tableName[countTables];
+ ndbout << " mysql_fetch_row: value = "
+ << (char*)(row[0]) << endl;
+ tResult = 12 ;
+ break;
+ }
+ mysql_free_result(res);
+ }
+ break;
+ default:
+ assert(false);
+ }
+#endif
+ }
+ }//for Tables loop
+
+ if (tResult != 0)
+ break;
+
+ if (use_ndb){
+ check = pTrans->execute(Commit);
+ } else {
+#ifdef USE_MYSQL
+ if (tNoOfTables > 1)
+ if (mysql_commit(&mysql)) {
+ ndbout << " mysql_commit: " << mysql_error(&mysql) << endl;
+ tResult = 13;
+ } else
+ check = 0;
+#endif
+ }
+
+ if (use_ndb) {
+ // Decide what kind of error this is
+ if ((tSpecialTrans == 1) &&
+ (check == -1)) {
+// --------------------------------------------------------------------
+// A special transaction have been executed, change to check = 0 in
+// certain situations.
+// --------------------------------------------------------------------
+ switch (tType) {
+ case stInsert: // Insert case
+ if (630 == pTrans->getNdbError().code ) {
+ check = 0;
+ ndbout << "Insert with 4007 was successful" << endl;
+ }//if
+ break;
+ case stDelete: // Delete Case
+ if (626 == pTrans->getNdbError().code ) {
+ check = 0;
+ ndbout << "Delete with 4007 was successful" << endl;
+ }//if
+ break;
+ default:
+ assert(false);
+ }//switch
+ }//if
+ tSpecialTrans = 0;
+ if (check == -1) {
+ if ((stVerifyDelete == tType) &&
+ (626 == pTrans->getNdbError().code)) {
+ // ----------------------------------------------
+ // It's good news - the deleted tuple is gone,
+ // so reset "check" flag
+ // ----------------------------------------------
+ check = 0 ;
+ } else {
+ int retCode =
+ theErrorData.handleErrorCommon(pTrans->getNdbError());
+ if (retCode == 1) {
+ ndbout_c("execute: %d, %d, %s", count, tType,
+ pTrans->getNdbError().message );
+ ndbout_c("Error code = %d", pTrans->getNdbError().code );
+ tResult = 20;
+ } else if (retCode == 2) {
+ ndbout << "4115 should not happen in flexBench" << endl;
+ tResult = 20;
+ } else if (retCode == 3) {
+// --------------------------------------------------------------------
+// We are not certain if the transaction was successful or not.
+// We must reexecute but might very well find that the transaction
+// actually was updated. Updates and Reads are no problem here. Inserts
+// will not cause a problem if error code 630 arrives. Deletes will
+// not cause a problem if 626 arrives.
+// --------------------------------------------------------------------
+ if ((tType == stInsert) || (tType == stDelete)) {
+ tSpecialTrans = 1;
+ }//if
+ }//if
+ }//if
+ }//if
+ // Check if retries should be made
+ if (check == -1 && tResult == 0) {
+ if (tAttemptNo < tRetryAttempts){
+ tAttemptNo++;
+ } else {
+// --------------------------------------------------------------------
+// Too many retries have been made, report error and break out of loop
+// --------------------------------------------------------------------
+ ndbout << "Thread" << threadNo;
+ ndbout << ": too many errors reported" << endl;
+ tResult = 10;
+ break;
+ }//if
+ }//if
+ }
+
+ if (check == 0){
+ // Go to the next record
+ count++;
+ tAttemptNo = 0;
+#ifdef CEBIT_STAT
+ // report successful ops
+ if (statEnable) {
+ statOps += loopCountTables;
+ if (statOps >= statFreq) {
+ statReport(tType, statOps);
+ statOps = 0;
+ }//if
+ }//if
+#endif
+ }//if
+
+ if (stVerify == tType && 0 == check){
+ int nTableOffset = 0 ;
+ for (int a = 1 ; a < loopCountAttributes ; a++){
+ for (int tables = 0 ; tables < loopCountTables ; tables++){
+ nTableOffset = tables*loopCountAttributes*tAttributeSize;
+ int ov =*(int*)&attrValue[nTableOffset + tAttributeSize*a];
+ int nv =*(int*)&tmpAttrRefValue[tAttributeSize*a];
+ if (ov != nv){
+ ndbout << "Error in verify ";
+ ndbout << "pk = " << tmpAttrRefValue[0] << ":" << endl;
+ ndbout << "attrValue[" << nTableOffset + tAttributeSize*a << "] = " << ov << endl ;
+ ndbout << "attrRefValue[" << nRefLocalOpOffset + tAttributeSize*a << "]" << nv << endl ;
+ tResult = 11 ;
+ break ;
+ }//if
+ }//for
+ }//for
+ }// if(stVerify ... )
+ if (use_ndb) {
+ pNdb->closeTransaction(pTrans);
+ return_ndb_object(pNdb, ndb_id);
+ pNdb = NULL;
+ }
+ }// operations loop
+#ifdef CEBIT_STAT
+ // report remaining successful ops
+ if (statEnable) {
+ if (statOps > 0) {
+ statReport(tType, statOps);
+ statOps = 0;
+ }//if
+ }//if
+#endif
+ if (pNdb) {
+ pNdb->closeTransaction(pTrans);
+ return_ndb_object(pNdb, ndb_id);
+ pNdb = NULL;
+ }
+ }
+
+#ifdef USE_MYSQL
+ if (!use_ndb) {
+ mysql_close(&mysql);
+ for (Uint32 i = 0; i < tNoOfTables; i++) {
+ mysql_stmt_close(prep_insert[i]);
+ mysql_stmt_close(prep_update[i]);
+ mysql_stmt_close(prep_delete[i]);
+ mysql_stmt_close(prep_read[i]);
+ }
+ }
+#endif
+ if (use_ndb && pNdb) {
+ ndbout << "I got here " << endl;
+ return_ndb_object(pNdb, ndb_id);
+ }
+ return NULL;
+}
+
+
+static int readArguments(int argc, const char** argv)
+{
+
+ int i = 1;
+ while (argc > 1){
+ if (strcmp(argv[i], "-t") == 0){
+ tNoOfThreads = atoi(argv[i+1]);
+ if ((tNoOfThreads < 1))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-o") == 0){
+ tNoOfOperations = atoi(argv[i+1]);
+ if (tNoOfOperations < 1)
+ return -1;;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-a") == 0){
+ tNoOfAttributes = atoi(argv[i+1]);
+ if ((tNoOfAttributes < 2) || (tNoOfAttributes > MAXATTR))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-c") == 0){
+ tNoOfTables = atoi(argv[i+1]);
+ if ((tNoOfTables < 1) || (tNoOfTables > MAXTABLES))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-stdtables") == 0){
+ theStdTableNameFlag = 1;
+ }else if (strcmp(argv[i], "-l") == 0){
+ tNoOfLoops = atoi(argv[i+1]);
+ if ((tNoOfLoops < 0) || (tNoOfLoops > 100000))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-pool_size") == 0){
+ t_instances = atoi(argv[i+1]);
+ if ((t_instances < 1) || (t_instances > 240))
+ return -1;
+ argc -= 1;
+ i++;
+#ifdef USE_MYSQL
+ }else if (strcmp(argv[i], "-engine") == 0){
+ engine_id = atoi(argv[i+1]);
+ if ((engine_id < 0) || (engine_id > 3))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-socket") == 0){
+ sockets[n_sockets] = atoi(argv[i+1]);
+ if (sockets[n_sockets] <= 0)
+ return -1;
+ n_sockets++;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-use_ndb") == 0){
+ use_ndb = true;
+#endif
+ }else if (strcmp(argv[i], "-s") == 0){
+ tAttributeSize = atoi(argv[i+1]);
+ if ((tAttributeSize < 1) || (tAttributeSize > MAXATTRSIZE))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-lkn") == 0){
+ tNoOfLongPK = atoi(argv[i+1]);
+ useLongKeys = true;
+ if ((tNoOfLongPK < 1) || (tNoOfLongPK > MAXNOLONGKEY) ||
+ (tNoOfLongPK * tSizeOfLongPK) > MAXLONGKEYTOTALSIZE){
+ ndbout << "Argument -lkn is not in the proper range." << endl;
+ return -1;
+ }
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-lks") == 0){
+ tSizeOfLongPK = atoi(argv[i+1]);
+ useLongKeys = true;
+ if ((tSizeOfLongPK < 1) || (tNoOfLongPK * tSizeOfLongPK) > MAXLONGKEYTOTALSIZE){
+ ndbout << "Argument -lks is not in the proper range 1 to " <<
+ MAXLONGKEYTOTALSIZE << endl;
+ return -1;
+ }
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-simple") == 0){
+ theSimpleFlag = 1;
+ }else if (strcmp(argv[i], "-write") == 0){
+ theWriteFlag = 1;
+ }else if (strcmp(argv[i], "-dirty") == 0){
+ theDirtyFlag = 1;
+ }else if (strcmp(argv[i], "-sleep") == 0){
+ tSleepTime = atoi(argv[i+1]);
+ if ((tSleepTime < 1) || (tSleepTime > 3600))
+ return -1;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-no_table_create") == 0){
+ theTableCreateFlag = 1;
+ }else if (strcmp(argv[i], "-temp") == 0){
+ theTempTable = true;
+ }else if (strcmp(argv[i], "-noverify") == 0){
+ VerifyFlag = false ;
+ }else if (theErrorData.parseCmdLineArg(argv, i) == true){
+ ; //empty, updated in errorArg(..)
+ }else if (strcmp(argv[i], "-verify") == 0){
+ VerifyFlag = true ;
+#ifdef CEBIT_STAT
+ }else if (strcmp(argv[i], "-statserv") == 0){
+ if (! (argc > 2))
+ return -1;
+ const char *p = argv[i+1];
+ const char *q = strrchr(p, ':');
+ if (q == 0)
+ return -1;
+ BaseString::snprintf(statHost, sizeof(statHost), "%.*s", q-p, p);
+ statPort = atoi(q+1);
+ statEnable = true;
+ argc -= 1;
+ i++;
+ }else if (strcmp(argv[i], "-statfreq") == 0){
+ if (! (argc > 2))
+ return -1;
+ statFreq = atoi(argv[i+1]);
+ if (statFreq < 1)
+ return -1;
+ argc -= 1;
+ i++;
+#endif
+ }else{
+ return -1;
+ }
+ argc -= 1;
+ i++;
+ }
+#ifdef USE_MYSQL
+ if (n_sockets == 0) {
+ n_sockets = 1;
+ sockets[0] = 3306;
+ }
+#endif
+ return 0;
+}
+
+static void sleepBeforeStartingTest(int seconds){
+ if (seconds > 0){
+ ndbout << "Sleeping(" <<seconds << ")...";
+ NdbSleep_SecSleep(seconds);
+ ndbout << " done!" << endl;
+ }
+}
+
+
+#ifdef USE_MYSQL
+static int
+dropTables(MYSQL* mysqlp){
+ char buf[2048];
+ for(unsigned i = 0; i < tNoOfTables; i++){
+ int pos = 0;
+ ndbout << "Dropping " << tableName[i] << "... ";
+ pos += sprintf(buf+pos, "%s", "DROP TABLE ");
+ pos += sprintf(buf+pos, "%s%s", tableName[i], ";");
+ if (verbose)
+ ndbout << endl << buf << endl;
+ if (mysql_query(mysqlp, buf) != 0){
+ ndbout << "Failed!"<<endl
+ <<mysql_error(mysqlp)<<endl
+ <<buf<<endl;
+ } else
+ ndbout << "OK!" << endl;
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef USE_MYSQL
+static int
+createTables(MYSQL* mysqlp){
+
+ for (Uint32 i = 0; i < tNoOfAttributes; i++){
+ BaseString::snprintf(attrName[i], MAXSTRLEN, "COL%d", i);
+ }
+
+ // Note! Uses only uppercase letters in table name's
+ // so that we can look at the tables with SQL
+ for (Uint32 i = 0; i < tNoOfTables; i++){
+ if (theStdTableNameFlag == 0){
+ BaseString::snprintf(tableName[i], MAXSTRLEN, "TAB%d_%d", i,
+ (int)(NdbTick_CurrentMillisecond() / 1000));
+ } else {
+ BaseString::snprintf(tableName[i], MAXSTRLEN, "TAB%d", i);
+ }
+ }
+
+ char buf[2048];
+ for(unsigned i = 0; i < tNoOfTables; i++){
+ int pos = 0;
+ ndbout << "Creating " << tableName[i] << "... ";
+
+ pos += sprintf(buf+pos, "%s", "CREATE TABLE ");
+ pos += sprintf(buf+pos, "%s%s", tableName[i], " ");
+ if(useLongKeys){
+ for(Uint32 i = 0; i < tNoOfLongPK; i++) {
+ }
+ } else {
+ pos += sprintf(buf+pos, "%s%s%s",
+ "(", attrName[0], " int unsigned primary key");
+ }
+ for (unsigned j = 1; j < tNoOfAttributes; j++)
+ pos += sprintf(buf+pos, "%s%s%s", ",", attrName[j], " int unsigned");
+ pos += sprintf(buf+pos, "%s%s%s", ")", engine[engine_id], ";");
+ if (verbose)
+ ndbout << endl << buf << endl;
+ if (mysql_query(mysqlp, buf) != 0)
+ return -1;
+ ndbout << "done" << endl;
+ }
+ return 0;
+}
+#endif
+
+static int
+createTables(Ndb* pMyNdb){
+
+ for (Uint32 i = 0; i < tNoOfAttributes; i++){
+ BaseString::snprintf(attrName[i], MAXSTRLEN, "COL%d", i);
+ }
+
+ // Note! Uses only uppercase letters in table name's
+ // so that we can look at the tables with SQL
+ for (Uint32 i = 0; i < tNoOfTables; i++){
+ if (theStdTableNameFlag == 0){
+ BaseString::snprintf(tableName[i], MAXSTRLEN, "TAB%d_%d", i,
+ (int)(NdbTick_CurrentMillisecond() / 1000));
+ } else {
+ BaseString::snprintf(tableName[i], MAXSTRLEN, "TAB%d", i);
+ }
+ }
+
+ for(unsigned i = 0; i < tNoOfTables; i++){
+ ndbout << "Creating " << tableName[i] << "... ";
+
+ NdbDictionary::Table tmpTable(tableName[i]);
+
+ tmpTable.setStoredTable(!theTempTable);
+
+ if(useLongKeys){
+ for(Uint32 i = 0; i < tNoOfLongPK; i++) {
+ NdbDictionary::Column col(longKeyAttrName[i]);
+ col.setType(NdbDictionary::Column::Unsigned);
+ col.setLength(tSizeOfLongPK);
+ col.setPrimaryKey(true);
+ tmpTable.addColumn(col);
+ }
+ } else {
+ NdbDictionary::Column col(attrName[0]);
+ col.setType(NdbDictionary::Column::Unsigned);
+ col.setLength(1);
+ col.setPrimaryKey(true);
+ tmpTable.addColumn(col);
+ }
+ NdbDictionary::Column col;
+ col.setType(NdbDictionary::Column::Unsigned);
+ col.setLength(tAttributeSize);
+ for (unsigned j = 1; j < tNoOfAttributes; j++){
+ col.setName(attrName[j]);
+ tmpTable.addColumn(col);
+ }
+ if(pMyNdb->getDictionary()->createTable(tmpTable) == -1){
+ return -1;
+ }
+ ndbout << "done" << endl;
+ }
+
+ return 0;
+}
+
+
+static void input_error(){
+ ndbout << endl << "Invalid argument!" << endl;
+ ndbout << endl << "Arguments:" << endl;
+ ndbout << " -t Number of threads to start, default 1" << endl;
+ ndbout << " -o Number of operations per loop, default 500" << endl;
+ ndbout << " -l Number of loops to run, default 1, 0=infinite" << endl;
+ ndbout << " -a Number of attributes, default 25" << endl;
+ ndbout << " -c Number of tables, default 1" << endl;
+ ndbout << " -s Size of each attribute, default 1 (Primary Key is always of size 1," << endl;
+ ndbout << " independent of this value)" << endl;
+ ndbout << " -lkn Number of long primary keys, default 1" << endl;
+ ndbout << " -lks Size of each long primary key, default 1" << endl;
+
+ ndbout << " -simple Use simple read to read from database" << endl;
+ ndbout << " -dirty Use dirty read to read from database" << endl;
+ ndbout << " -write Use writeTuple in insert and update" << endl;
+ ndbout << " -stdtables Use standard table names" << endl;
+ ndbout << " -no_table_create Don't create tables in db" << endl;
+ ndbout << " -sleep Sleep a number of seconds before running the test, this" << endl;
+ ndbout << " can be used so that another flexBench have time to create tables" << endl;
+ ndbout << " -temp Use tables without logging" << endl;
+ ndbout << " -verify Verify inserts, updates and deletes" << endl ;
+ ndbout << " -use_ndb Use NDB API (otherwise use mysql client)" << endl ;
+ ndbout << " -pool_size Number of Ndb objects in pool" << endl ;
+ theErrorData.printCmdLineArgs(ndbout);
+ ndbout << endl <<"Returns:" << endl;
+ ndbout << "\t 0 - Test passed" << endl;
+ ndbout << "\t 1 - Test failed" << endl;
+ ndbout << "\t 2 - Invalid arguments" << endl << endl;
+}
+
+// vim: set sw=2:
diff --git a/storage/ndb/test/ndbapi/index.cpp b/storage/ndb/test/ndbapi/index.cpp
new file mode 100644
index 00000000000..c22da594164
--- /dev/null
+++ b/storage/ndb/test/ndbapi/index.cpp
@@ -0,0 +1,998 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* ***************************************************
+ INDEX TEST 1
+ Test index functionality of NDB
+
+ Arguments:
+ -T create table
+ -L include a long attribute in key or index
+ -2 define primary key with two attributes
+ -c create index
+ -p make index unique (include primary key attribute)
+ -r read using index
+ -u update using index
+ -d delete using index
+ -n<no operations> do n operations (for -I -r -u -d -R -U -D)
+ -o<no parallel operations> (for -I -r -u -d -R -U -D)
+ -m<no indexes>
+
+ Returns:
+ 0 - Test passed
+ -1 - Test failed
+ 1 - Invalid arguments
+ * *************************************************** */
+
+#include <ndb_global.h>
+
+#include <NdbApi.hpp>
+#include <NdbOut.hpp>
+#include <NdbTick.h>
+#include <NdbMain.h>
+#include <NdbTest.hpp>
+#include <NDBT_Error.hpp>
+
+#ifndef MIN
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#endif
+
+#define MAX_NO_PARALLEL_OPERATIONS 100
+
+bool testPassed = true;
+
+static void
+error_handler(const NdbError & err)
+{
+ // Test failed
+ ndbout << endl << err << endl;
+ testPassed = false;
+}
+
+static void
+error_handler4(int line, const NdbError & err){
+ ndbout << endl << "Line " << line << endl;
+ // Test failed
+ ndbout << err << endl;
+ testPassed = false;
+}
+
+static char *longName, *sixtysix, *ninetynine, *hundred;
+
+static void createTable(Ndb &myNdb, bool storeInACC, bool twoKey, bool longKey)
+{
+ NdbDictionary::Dictionary* dict = myNdb.getDictionary();
+ NdbDictionary::Table table("PERSON");
+ //NdbDictionary::Column column(); // Bug
+ NdbDictionary::Column column;
+ int res;
+
+ column.setName("NAME");
+ column.setType(NdbDictionary::Column::Char);
+ column.setLength((longKey)?
+ 1024 // 1KB => long key
+ :12);
+ column.setPrimaryKey(true);
+ column.setNullable(false);
+ table.addColumn(column);
+
+ if (twoKey) {
+ column.setName("KEY2");
+ column.setType(NdbDictionary::Column::Unsigned);
+ column.setLength(1);
+ column.setPrimaryKey(true);
+ column.setNullable(false);
+ table.addColumn(column);
+ }
+
+ column.setName("PNUM1");
+ column.setType(NdbDictionary::Column::Unsigned);
+ column.setLength(1);
+ column.setPrimaryKey(false);
+ column.setNullable(false);
+ table.addColumn(column);
+
+ column.setName("PNUM2");
+ column.setType(NdbDictionary::Column::Unsigned);
+ column.setLength(1);
+ column.setPrimaryKey(false);
+ column.setNullable(false);
+ table.addColumn(column);
+
+ column.setName("PNUM3");
+ column.setType(NdbDictionary::Column::Unsigned);
+ column.setLength(1);
+ column.setPrimaryKey(false);
+ column.setNullable(false);
+ table.addColumn(column);
+
+ column.setName("PNUM4");
+ column.setType(NdbDictionary::Column::Unsigned);
+ column.setLength(1);
+ column.setPrimaryKey(false);
+ column.setNullable(false);
+ table.addColumn(column);
+
+ column.setName("AGE");
+ column.setType(NdbDictionary::Column::Unsigned);
+ column.setLength(1);
+ column.setPrimaryKey(false);
+ column.setNullable(false);
+ table.addColumn(column);
+
+ column.setName("STRING_AGE");
+ column.setType(NdbDictionary::Column::Char);
+ column.setLength(1);
+ column.setLength(256);
+ column.setPrimaryKey(false);
+ column.setNullable(false);
+ table.addColumn(column);
+
+ if ((res = dict->createTable(table)) == -1) {
+ error_handler(dict->getNdbError());
+ }
+ else
+ ndbout << "Created table" << ((longKey)?" with long key":"") <<endl;
+}
+
+static void createIndex(Ndb &myNdb, bool includePrimary, unsigned int noOfIndexes)
+{
+ Uint64 before, after;
+ NdbDictionary::Dictionary* dict = myNdb.getDictionary();
+ char indexName[] = "PNUMINDEX0000";
+ int res;
+
+ for(unsigned int indexNum = 0; indexNum < noOfIndexes; indexNum++) {
+ sprintf(indexName, "PNUMINDEX%.4u", indexNum);
+ NdbDictionary::Index index(indexName);
+ index.setTable("PERSON");
+ index.setType(NdbDictionary::Index::UniqueHashIndex);
+ if (includePrimary) {
+ const char* attr_arr[] = {"NAME", "PNUM1", "PNUM3"};
+ index.addIndexColumns(3, attr_arr);
+ }
+ else {
+ const char* attr_arr[] = {"PNUM1", "PNUM3"};
+ index.addIndexColumns(2, attr_arr);
+ }
+ before = NdbTick_CurrentMillisecond();
+ if ((res = dict->createIndex(index)) == -1) {
+ error_handler(dict->getNdbError());
+ }
+ after = NdbTick_CurrentMillisecond();
+ ndbout << "Created index " << indexName << ", " << after - before << " msec" << endl;
+ }
+}
+
+static void insertTable(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool oneTrans, bool twoKey, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbOperation *myOp;
+ char name[] = "Kalle0000000";
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError());
+
+ myOp = myTrans->getNdbOperation("PERSON");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError());
+
+ myOp->insertTuple();
+ sprintf(name, "Kalle%.7i", i);
+ if (longKey)
+ memcpy(longName, name, strlen(name));
+ if (myOp->equal("NAME", (longKey)?longName:name) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (twoKey)
+ if (myOp->equal("KEY2", i) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("PNUM1", 17) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("PNUM2", 18)) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("PNUM3", 19)) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("PNUM4", 20)) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("AGE", ((i % 2) == 0)?66:99) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("STRING_AGE", ((i % 2) == 0)?sixtysix:ninetynine) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ if (noOfOperations == 1)
+ printf("Trying to insert person %s\n", name);
+ else
+ printf("Trying to insert %u persons\n", noOfOperations);
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Inserted person %s, %u msec\n", name, (Uint32) after - before);
+ else
+ printf("Inserted %u persons, %u msec\n", noOfOperations, (Uint32) after - before);
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Inserted "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void updateTable(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool oneTrans, bool twoKey, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbOperation *myOp;
+ char name[] = "Kalle0000000";
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError());
+
+ myOp = myTrans->getNdbOperation("PERSON");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError());
+
+ myOp->updateTuple();
+ sprintf(name, "Kalle%.7i", i);
+ if (longKey)
+ memcpy(longName, name, strlen(name));
+ if (myOp->equal("NAME", (longKey)?longName:name) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (twoKey)
+ if (myOp->equal("KEY2", i) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("PNUM1", 77) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("PNUM2", 88)) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("PNUM4", 99)) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("AGE", 100) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("STRING_AGE", hundred) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ if (noOfOperations == 1)
+ printf("Trying to update person %s\n", name);
+ else
+ printf("Trying to update %u persons\n", noOfOperations);
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Updated person %s, %u msec\n", name, (Uint32) after - before);
+ else
+ printf("Update %u persons, %u msec\n", noOfOperations, (Uint32) after - before);
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Updated "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void deleteTable(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool oneTrans, bool twoKey, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbOperation *myOp;
+ char name[] = "Kalle0000000";
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError());
+
+ myOp = myTrans->getNdbOperation("PERSON");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError());
+
+ myOp->deleteTuple();
+ sprintf(name, "Kalle%.7i", i);
+ if (longKey)
+ memcpy(longName, name, strlen(name));
+ if (myOp->equal("NAME", (longKey)?longName:name) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (twoKey)
+ if (myOp->equal("KEY2", i) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ if (noOfOperations == 1)
+ printf("Trying to delete person %s\n", name);
+ else
+ printf("Trying to delete %u persons\n", noOfOperations);
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Deleted person %s, %u msec\n", name, (Uint32) after - before);
+ else
+ printf("Deleted %u persons, %u msec\n", noOfOperations, (Uint32) after - before);
+
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Deleted "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void readTable(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool oneTrans, bool twoKey, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbOperation *myOp;
+ char name[] = "Kalle0000000";
+ NdbRecAttr* myRecAttrArr[MAX_NO_PARALLEL_OPERATIONS];
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError());
+
+ myOp = myTrans->getNdbOperation("PERSON");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError());
+
+ myOp->readTuple();
+ sprintf(name, "Kalle%.7i", i);
+ if (longKey)
+ memcpy(longName, name, strlen(name));
+ if (myOp->equal("NAME", (longKey)?longName:name) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (twoKey)
+ if (myOp->equal("KEY2", i) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ myRecAttrArr[j-1] = myOp->getValue("PNUM2", NULL);
+ }
+ if (noOfOperations == 1)
+ printf("Trying to read person %s\n", name);
+ else
+ printf("Trying to read %u persons\n", noOfOperations);
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Read person %s, %u msec\n", name, (Uint32) after - before);
+ else
+ printf("Read %u persons, %u msec\n", noOfOperations, (Uint32) after - before);
+ for(unsigned int j = 0; j<noOfOperations; j++)
+ printf("PNUM2 = %u\n", myRecAttrArr[j]->u_32_value());
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Read "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void readIndex(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool includePrimary, bool oneTrans, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbIndexOperation *myOp;
+ char indexName[] = "PNUMINDEX0000";
+ char name[] = "Kalle0000000";
+ NdbRecAttr* myRecAttrArr[MAX_NO_PARALLEL_OPERATIONS];
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError());
+
+ myOp = myTrans->getNdbIndexOperation(indexName, "PERSON");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError());
+
+ myOp->readTuple();
+ if (includePrimary) {
+ sprintf(name, "Kalle%.7i", i);
+ if (longKey)
+ memcpy(longName, name, strlen(name));
+ if (myOp->equal("NAME", (longKey)?longName:name) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ if (myOp->equal("PNUM1", 17) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->equal("PNUM3", 19) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ myRecAttrArr[j-1] = myOp->getValue("PNUM2", NULL);
+ }
+ if (noOfOperations == 1)
+ printf("Trying to read person %s\n", name);
+ else
+ printf("Trying to read %u persons\n", noOfOperations);
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Read person %s, %u msec\n", name, (Uint32) after - before);
+ else
+ printf("Read %u persons, %u msec\n", noOfOperations, (Uint32) after - before);
+ for(unsigned int j = 0; j<noOfOperations; j++)
+ printf("PNUM2 = %u\n", myRecAttrArr[j]->u_32_value());
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Read "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void updateIndex(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool includePrimary, bool oneTrans, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbIndexOperation *myOp;
+ char indexName[] = "PNUMINDEX0000";
+ char name[] = "Kalle0000000";
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError());
+
+ myOp = myTrans->getNdbIndexOperation(indexName, "PERSON");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError());
+
+ myOp->updateTuple();
+ if (includePrimary) {
+ sprintf(name, "Kalle%.7i", i);
+ if (longKey)
+ memcpy(longName, name, strlen(name));
+ if (myOp->equal("NAME", (longKey)?longName:name) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ if (myOp->equal("PNUM1", 17) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->equal("PNUM3", 19) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ // Update index itself, should be possible
+ if (myOp->setValue("PNUM1", 77) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("PNUM2", 88)) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("PNUM4", 99)) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("AGE", 100) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("STRING_AGE", hundred) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ if (noOfOperations == 1)
+ printf("Trying to update person %s\n", name);
+ else
+ printf("Trying to update %u persons\n", noOfOperations);
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Updated person %s, %u msec\n", name, (Uint32) after - before);
+ else
+ printf("Updated %u persons, %u msec\n", noOfOperations, (Uint32) after - before);
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Updated "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void deleteIndex(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool includePrimary, bool oneTrans, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbIndexOperation *myOp;
+ char indexName[] = "PNUMINDEX0000";
+ char name[] = "Kalle0000000";
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError());
+
+ myOp = myTrans->getNdbIndexOperation(indexName, "PERSON");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError());
+
+ myOp->deleteTuple();
+ if (includePrimary) {
+ sprintf(name, "Kalle%.7i", i);
+ if (longKey)
+ memcpy(longName, name, strlen(name));
+ if (myOp->equal("NAME", (longKey)?longName:name) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ if (myOp->equal("PNUM1", 17) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->equal("PNUM3", 19) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ if (noOfOperations == 1)
+ printf("Trying to delete person %s\n", name);
+ else
+ printf("Trying to delete %u persons\n", noOfOperations);
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Deleted person %s, %u msec\n", name, (Uint32) after - before);
+ else
+ printf("Deleted %u persons, %u msec\n", noOfOperations, (Uint32) after - before);
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError());
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Deleted "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void dropIndex(Ndb &myNdb, unsigned int noOfIndexes)
+{
+ for(unsigned int indexNum = 0; indexNum < noOfIndexes; indexNum++) {
+ char indexName[255];
+ sprintf(indexName, "PNUMINDEX%.4u", indexNum);
+ const Uint64 before = NdbTick_CurrentMillisecond();
+ const int retVal = myNdb.getDictionary()->dropIndex(indexName, "PERSON");
+ const Uint64 after = NdbTick_CurrentMillisecond();
+
+ if(retVal == 0){
+ ndbout << "Dropped index " << indexName << ", "
+ << after - before << " msec" << endl;
+ } else {
+ ndbout << "Failed to drop index " << indexName << endl;
+ ndbout << myNdb.getDictionary()->getNdbError() << endl;
+ }
+ }
+}
+
+NDB_COMMAND(indexTest, "indexTest", "indexTest", "indexTest", 65535)
+{
+ ndb_init();
+ bool createTableOp, createIndexOp, dropIndexOp, insertOp, updateOp, deleteOp, readOp, readIndexOp, updateIndexOp, deleteIndexOp, twoKey, longKey;
+ unsigned int noOfTuples = 1;
+ unsigned int noOfOperations = 1;
+ unsigned int noOfIndexes = 1;
+ int i = 1;
+ Ndb myNdb( "TEST_DB" );
+ int check;
+ bool storeInACC = false;
+ bool includePrimary = false;
+ bool oneTransaction = false;
+
+ createTableOp = createIndexOp = dropIndexOp = insertOp = updateOp = deleteOp = readOp = readIndexOp = updateIndexOp = deleteIndexOp = twoKey = longKey = false;
+ // Read arguments
+ if (argc > 1)
+ while (argc > 1)
+ {
+ if (strcmp(argv[i], "-T") == 0)
+ {
+ createTableOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-c") == 0)
+ {
+ createIndexOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-X") == 0)
+ {
+ dropIndexOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-I") == 0)
+ {
+ insertOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-D") == 0)
+ {
+ deleteOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-U") == 0)
+ {
+ updateOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-R") == 0)
+ {
+ readOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-r") == 0)
+ {
+ readIndexOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-u") == 0)
+ {
+ updateIndexOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-d") == 0)
+ {
+ deleteIndexOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-s") == 0)
+ {
+ storeInACC = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-p") == 0)
+ {
+ includePrimary = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-L") == 0)
+ {
+ longKey = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-1") == 0)
+ {
+ oneTransaction = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-2") == 0)
+ {
+ twoKey = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strstr(argv[i], "-n") != 0)
+ {
+ noOfTuples = atoi(argv[i]+2);
+ argc -= 1;
+ i++;
+ }
+ else if (strstr(argv[i], "-o") != 0)
+ {
+ noOfOperations = MIN(MAX_NO_PARALLEL_OPERATIONS, atoi(argv[i]+2));
+ argc -= 1;
+ i++;
+ }
+ else if (strstr(argv[i], "-m") != 0)
+ {
+ noOfIndexes = atoi(argv[i]+2);
+ argc -= 1;
+ i++;
+ }
+ else if (strstr(argv[i], "-h") != 0)
+ {
+ printf("Synopsis:\n");
+ printf("index\n");
+ printf("\t-T create table\n");
+ printf("\t-L include a long attribute in key or index\n");
+ printf("\t-2 define primary key with two attributes\n");
+ printf("\t-c create index\n");
+ printf("\t-p make index unique (include primary key attribute)\n");
+ printf("\t-r read using index\n");
+ printf("\t-u update using index\n");
+ printf("\t-d delete using index\n");
+ printf("\t-n<no operations> do n operations (for -I -r -u -d -R -U -D)\n");
+ printf("\t-o<no parallel operations> (for -I -r -u -d -R -U -D)\n");
+ printf("\t-m<no indexes>\n");
+ argc -= 1;
+ i++;
+ }
+ else {
+ char errStr[256];
+
+ printf(errStr, "Illegal argument: %s", argv[i]);
+ exit(-1);
+ }
+ }
+ else
+ {
+ createTableOp = createIndexOp = dropIndexOp = insertOp = updateOp = deleteOp = true;
+ }
+ if (longKey) {
+ longName = (char *) malloc(1024);
+ for (int i = 0; i < 1023; i++)
+ longName[i] = 'x';
+ longName[1023] = '\0';
+ }
+ sixtysix = (char *) malloc(256);
+ for (int i = 0; i < 255; i++)
+ sixtysix[i] = ' ';
+ sixtysix[255] = '\0';
+ strncpy(sixtysix, "sixtysix", strlen("sixtysix"));
+ ninetynine = (char *) malloc(256);
+ for (int i = 0; i < 255; i++)
+ ninetynine[i] = ' ';
+ ninetynine[255] = '\0';
+ strncpy(ninetynine, "ninetynine", strlen("ninetynine"));
+ hundred = (char *) malloc(256);
+ for (int i = 0; i < 255; i++)
+ hundred[i] = ' ';
+ hundred[255] = '\0';
+ strncpy(hundred, "hundred", strlen("hundred"));
+ myNdb.init();
+ // Wait for Ndb to become ready
+ if (myNdb.waitUntilReady(30) == 0)
+ {
+ if (createTableOp)
+ createTable(myNdb, storeInACC, twoKey, longKey);
+
+ if (createIndexOp)
+ createIndex(myNdb, includePrimary, noOfIndexes);
+
+ if (insertOp)
+ insertTable(myNdb, noOfTuples, noOfOperations, oneTransaction, twoKey, longKey);
+
+ if (updateOp)
+ updateTable(myNdb, noOfTuples, noOfOperations, oneTransaction, twoKey, longKey);
+
+ if (deleteOp)
+ deleteTable(myNdb, noOfTuples, noOfOperations, oneTransaction, twoKey, longKey);
+
+ if (readOp)
+ readTable(myNdb, noOfTuples, noOfOperations, oneTransaction, twoKey, longKey);
+
+ if (readIndexOp)
+ readIndex(myNdb, noOfTuples, noOfOperations, includePrimary, oneTransaction, longKey);
+
+ if (updateIndexOp)
+ updateIndex(myNdb, noOfTuples, noOfOperations, includePrimary, oneTransaction, longKey);
+
+ if (deleteIndexOp)
+ deleteIndex(myNdb, noOfTuples, noOfOperations, includePrimary, oneTransaction, longKey);
+
+ if (dropIndexOp)
+ dropIndex(myNdb, noOfIndexes);
+ }
+
+ if (testPassed)
+ {
+ // Test passed
+ ndbout << "OK - Test passed" << endl;
+ }
+ else
+ {
+ // Test failed
+ ndbout << "FAIL - Test failed" << endl;
+ exit(-1);
+ }
+ return 0;
+}
+
+
diff --git a/storage/ndb/test/ndbapi/index2.cpp b/storage/ndb/test/ndbapi/index2.cpp
new file mode 100644
index 00000000000..f739468d7df
--- /dev/null
+++ b/storage/ndb/test/ndbapi/index2.cpp
@@ -0,0 +1,836 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* ***************************************************
+ INDEX TEST 1
+ Test index functionality of NDB
+
+ Arguments:
+ -T create table
+ -L include a long attribute in key or index
+ -2 define primary key with two attributes
+ -c create index
+ -p make index unique (include primary key attribute)
+ -r read using index
+ -u update using index
+ -d delete using index
+ -n<no operations> do n operations (for -I -r -u -d -R -U -D)
+ -o<no parallel operations> (for -I -r -u -d -R -U -D)
+ -m<no indexes>
+
+ Returns:
+ 0 - Test passed
+ -1 - Test failed
+ 1 - Invalid arguments
+ * *************************************************** */
+
+#include <ndb_global.h>
+
+#include <NdbApi.hpp>
+#include <NdbOut.hpp>
+#include <NdbTick.h>
+#include <NdbMain.h>
+#include <NdbTest.hpp>
+#include <NDBT_Error.hpp>
+
+#ifndef MIN
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#endif
+
+#define MAX_NO_PARALLEL_OPERATIONS 100
+
+bool testPassed = true;
+
+static void
+error_handler(const char* errorText)
+{
+ // Test failed
+ ndbout << endl << "ErrorMessage: " << errorText << endl;
+ testPassed = false;
+}
+
+static void
+error_handler4(int line, int status, int classif, int errNo, const char* errorText)
+{
+ ndbout << endl << "Line " << line << endl;
+ // Test failed
+ ndbout << "Status " << status << ", Classification " << classif<< ", Error code " << errNo << "\n" << errorText << endl;
+ testPassed = false;
+}
+
+static char *longName, *sixtysix, *ninetynine, *hundred;
+
+static void createTable(Ndb &myNdb, bool storeInACC, bool twoKey, bool longKey)
+{
+ NdbDictionary::Dictionary* dict = myNdb.getDictionary();
+ NdbDictionary::Table table("THE_TABLE");
+ NdbDictionary::Column column;
+ int res;
+
+ column.setName("X");
+ column.setType(NdbDictionary::Column::Unsigned);
+ column.setLength(1);
+ column.setPrimaryKey(true);
+ column.setNullable(false);
+ table.addColumn(column);
+
+ column.setName("Y");
+ column.setType(NdbDictionary::Column::Unsigned);
+ column.setLength(1);
+ column.setPrimaryKey(false);
+ column.setNullable(false);
+ table.addColumn(column);
+
+ if ((res = dict->createTable(table)) == -1) {
+ error_handler(dict->getNdbError().message);
+ }
+ else
+ ndbout << "Created table" << ((longKey)?" with long key":"") <<endl;
+}
+
+static void createIndex(Ndb &myNdb, bool includePrimary, unsigned int noOfIndexes)
+{
+ Uint64 before, after;
+ NdbDictionary::Dictionary* dict = myNdb.getDictionary();
+ char indexName[] = "INDEX0000";
+ int res;
+
+ for(unsigned int indexNum = 0; indexNum < noOfIndexes; indexNum++) {
+ sprintf(indexName, "INDEX%.4u", indexNum);
+ NdbDictionary::Index index(indexName);
+ index.setTable("THE_TABLE");
+ index.setType(NdbDictionary::Index::UniqueHashIndex);
+ if (includePrimary) {
+ const char* attr_arr[] = {"X", "Y"};
+ index.addIndexColumns(2, attr_arr);
+ }
+ else {
+ const char* attr_arr[] = {"Y"};
+ index.addIndexColumns(2, attr_arr);
+ }
+ before = NdbTick_CurrentMillisecond();
+ if ((res = dict->createIndex(index)) == -1) {
+ error_handler(dict->getNdbError().message);
+ }
+ after = NdbTick_CurrentMillisecond();
+ ndbout << "Created index " << indexName << ", " << after - before << " msec"<< endl;
+ }
+}
+
+static void insertTable(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool oneTrans, bool twoKey, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbOperation *myOp;
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError().status, myNdb.getNdbError().classification,
+ myNdb.getNdbError().code, myNdb.getNdbError().message);
+
+ myOp = myTrans->getNdbOperation("THE_TABLE");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+
+ myOp->insertTuple();
+ if (myOp->equal("X", i) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("Y", i+1) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Inserted 1 tuple, %u msec\n", (Uint32) after - before);
+ else
+ printf("Inserted %u tuples, %u msec\n", noOfOperations, (Uint32) after - before);
+
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Inserted "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void updateTable(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool oneTrans, bool twoKey, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbOperation *myOp;
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError().status, myNdb.getNdbError().classification,
+ myNdb.getNdbError().code, myNdb.getNdbError().message);
+
+ myOp = myTrans->getNdbOperation("THE_TABLE");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+
+ myOp->updateTuple();
+ if (myOp->equal("X", i) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ if (myOp->setValue("Y", i+2) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Updated 1 tuple, %u msec\n", (Uint32) after - before);
+ else
+ printf("Update %u tuples, %u msec\n", noOfOperations, (Uint32) after - before);
+
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Updated "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void deleteTable(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool oneTrans, bool twoKey, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbOperation *myOp;
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError().status, myNdb.getNdbError().classification,
+ myNdb.getNdbError().code, myNdb.getNdbError().message);
+
+ myOp = myTrans->getNdbOperation("THE_TABLE");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+
+ myOp->deleteTuple();
+ if (myOp->equal("X", i) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Deleted 1 tuple, %u msec\n", (Uint32) after - before);
+ else
+ printf("Deleted %u tuples, %u msec\n", noOfOperations, (Uint32) after - before);
+
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Deleted "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void readTable(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool oneTrans, bool twoKey, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbOperation *myOp;
+ NdbRecAttr* myRecAttrArr[MAX_NO_PARALLEL_OPERATIONS];
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError().status, myNdb.getNdbError().classification,
+ myNdb.getNdbError().code, myNdb.getNdbError().message);
+
+ myOp = myTrans->getNdbOperation("THE_TABLE");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+
+ myOp->readTuple();
+ if (myOp->equal("X", i) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ myRecAttrArr[j-1] = myOp->getValue("Y", NULL);
+ }
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Read 1 tuple, %u msec\n", (Uint32) after - before);
+ else
+ printf("Read %u tuples, %u msec\n", noOfOperations, (Uint32) after - before);
+ for(unsigned int j = 0; j<noOfOperations; j++)
+ printf("Y = %u\n", myRecAttrArr[j]->u_32_value());
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Read "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void readIndex(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool includePrimary, bool oneTrans, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbIndexOperation *myOp;
+ char indexName[] = "INDEX0000";
+ NdbRecAttr* myRecAttrArr[MAX_NO_PARALLEL_OPERATIONS];
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError().status, myNdb.getNdbError().classification,
+ myNdb.getNdbError().code, myNdb.getNdbError().message);
+
+ myOp = myTrans->getNdbIndexOperation(indexName, "THE_TABLE");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+
+ myOp->readTuple();
+ if (includePrimary) {
+ if (myOp->equal("X", i) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ if (myOp->equal("Y", i+1) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ myRecAttrArr[j-1] = myOp->getValue("Y", NULL);
+ }
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Read 1 tuple, %u msec\n", (Uint32) after - before);
+ else
+ printf("Read %u tuples, %u msec\n", noOfOperations, (Uint32) after - before);
+ for(unsigned int j = 0; j<noOfOperations; j++)
+ printf("Y = %u\n", myRecAttrArr[j]->u_32_value());
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Read "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void updateIndex(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool includePrimary, bool oneTrans, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbIndexOperation *myOp;
+ char indexName[] = "INDEX0000";
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError().status, myNdb.getNdbError().classification,
+ myNdb.getNdbError().code, myNdb.getNdbError().message);
+
+ myOp = myTrans->getNdbIndexOperation(indexName, "THE_TABLE");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+
+ myOp->updateTuple();
+ if (includePrimary) {
+ if (myOp->equal("X", i) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ if (myOp->equal("Y", i+1) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ // Update index itself, should be possible
+ if (myOp->setValue("Y", i+2) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Updated 1 tuple, %u msec\n", (Uint32) after - before);
+ else
+ printf("Updated %u tuples, %u msec\n", noOfOperations, (Uint32) after - before);
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Updated "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void deleteIndex(Ndb &myNdb, unsigned int noOfTuples, unsigned int noOfOperations, bool includePrimary, bool oneTrans, bool longKey)
+{
+ Uint64 tbefore, tafter, before, after;
+ NdbConnection *myTrans;
+ NdbIndexOperation *myOp;
+ char indexName[] = "INDEX0000";
+
+ tbefore = NdbTick_CurrentMillisecond();
+ if (oneTrans) myTrans = myNdb.startTransaction();
+ for (unsigned int i = 0; i<noOfTuples; i++) {
+ for(unsigned int j = 1;
+ ((j<=noOfOperations)&&(i<noOfTuples));
+ (++j<=noOfOperations)?i++:i) {
+ if (!oneTrans) myTrans = myNdb.startTransaction();
+ if (myTrans == NULL)
+ error_handler4(__LINE__, myNdb.getNdbError().status, myNdb.getNdbError().classification,
+ myNdb.getNdbError().code, myNdb.getNdbError().message);
+
+ myOp = myTrans->getNdbIndexOperation(indexName, "THE_TABLE");
+ if (myOp == NULL)
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+
+ myOp->deleteTuple();
+ if (includePrimary) {
+ if (myOp->equal("X", i) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ if (myOp->equal("Y", i+1) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ }
+ before = NdbTick_CurrentMillisecond();
+ if (myTrans->execute( (oneTrans) ? NoCommit : Commit ) == -1)
+ {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ myNdb.closeTransaction(myTrans);
+ break;
+ }
+ after = NdbTick_CurrentMillisecond();
+ if (noOfOperations == 1)
+ printf("Deleted 1 tuple, %u msec\n", (Uint32) after - before);
+ else
+ printf("Deleted %u tuples, %u msec\n", noOfOperations, (Uint32) after - before);
+ if (!oneTrans) myNdb.closeTransaction(myTrans);
+ }
+ if (oneTrans) {
+ if (myTrans->execute( Commit ) == -1) {
+ error_handler4(__LINE__, myTrans->getNdbError().status, myTrans->getNdbError().classification,
+ myTrans->getNdbError().code, myTrans->getNdbError().message);
+ }
+ myNdb.closeTransaction(myTrans);
+ }
+ tafter = NdbTick_CurrentMillisecond();
+
+ ndbout << "Deleted "<< noOfTuples << " tuples in " << ((oneTrans) ? 1 : noOfTuples) << " transaction(s), " << tafter - tbefore << " msec" << endl;
+}
+
+static void dropIndex(Ndb &myNdb, unsigned int noOfIndexes)
+{
+ for(unsigned int indexNum = 0; indexNum < noOfIndexes; indexNum++) {
+ char indexName[255];
+ sprintf(indexName, "INDEX%.4u", indexNum);
+ const Uint64 before = NdbTick_CurrentMillisecond();
+ const int retVal = myNdb.getDictionary()->dropIndex(indexName,"THE_TABLE");
+ const Uint64 after = NdbTick_CurrentMillisecond();
+
+ if(retVal == 0){
+ ndbout << "Dropped index " << indexName << ", "
+ << after - before << " msec" << endl;
+ } else {
+ ndbout << "Failed to drop index " << indexName << endl;
+ ndbout << myNdb.getDictionary()->getNdbError() << endl;
+ }
+ }
+}
+
+NDB_COMMAND(indexTest, "indexTest", "indexTest", "indexTest", 65535)
+{
+ ndb_init();
+ bool createTableOp, createIndexOp, dropIndexOp, insertOp, updateOp, deleteOp, readOp, readIndexOp, updateIndexOp, deleteIndexOp, twoKey, longKey;
+ unsigned int noOfTuples = 1;
+ unsigned int noOfOperations = 1;
+ unsigned int noOfIndexes = 1;
+ int i = 1;
+ Ndb myNdb( "TEST_DB" );
+ int check;
+ bool storeInACC = false;
+ bool includePrimary = false;
+ bool oneTransaction = false;
+
+ createTableOp = createIndexOp = dropIndexOp = insertOp = updateOp = deleteOp = readOp = readIndexOp = updateIndexOp = deleteIndexOp = twoKey = longKey = false;
+ // Read arguments
+ if (argc > 1)
+ while (argc > 1)
+ {
+ if (strcmp(argv[i], "-T") == 0)
+ {
+ createTableOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-c") == 0)
+ {
+ createIndexOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-X") == 0)
+ {
+ dropIndexOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-I") == 0)
+ {
+ insertOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-D") == 0)
+ {
+ deleteOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-U") == 0)
+ {
+ updateOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-R") == 0)
+ {
+ readOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-r") == 0)
+ {
+ readIndexOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-u") == 0)
+ {
+ updateIndexOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-d") == 0)
+ {
+ deleteIndexOp = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-s") == 0)
+ {
+ storeInACC = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-p") == 0)
+ {
+ includePrimary = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-L") == 0)
+ {
+ longKey = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-1") == 0)
+ {
+ oneTransaction = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strcmp(argv[i], "-2") == 0)
+ {
+ twoKey = true;
+ argc -= 1;
+ i++;
+ }
+ else if (strstr(argv[i], "-n") != 0)
+ {
+ noOfTuples = atoi(argv[i]+2);
+ argc -= 1;
+ i++;
+ }
+ else if (strstr(argv[i], "-o") != 0)
+ {
+ noOfOperations = MIN(MAX_NO_PARALLEL_OPERATIONS, atoi(argv[i]+2));
+ argc -= 1;
+ i++;
+ }
+ else if (strstr(argv[i], "-m") != 0)
+ {
+ noOfIndexes = atoi(argv[i]+2);
+ argc -= 1;
+ i++;
+ }
+ else if (strstr(argv[i], "-h") != 0)
+ {
+ printf("Synopsis: \
+ index\
+ -T create table\
+ -L include a long attribute in key or index\
+ -2 define primary key with two attributes\
+ -c create index\
+ -p make index unique (include primary key attribute)\
+ -r read using index\
+ -u update using index\
+ -d delete using index\
+ -n<no operations> do n operations (for -I -r -u -d -R -U -D)\
+ -o<no parallel operations> (for -I -r -u -d -R -U -D)\
+ -m<no indexes>\n");
+ argc -= 1;
+ i++;
+ }
+ else {
+ char errStr[256];
+
+ sprintf(errStr, "Illegal argument: %s", argv[i]);
+ error_handler(errStr);
+ exit(-1);
+ }
+ }
+ else
+ {
+ createTableOp = createIndexOp = dropIndexOp = insertOp = updateOp = deleteOp = true;
+ }
+ if (longKey) {
+ longName = (char *) malloc(1024);
+ for (int i = 0; i < 1023; i++)
+ longName[i] = 'x';
+ longName[1023] = '\0';
+ }
+ sixtysix = (char *) malloc(256);
+ for (int i = 0; i < 255; i++)
+ sixtysix[i] = ' ';
+ sixtysix[255] = '\0';
+ strncpy(sixtysix, "sixtysix", strlen("sixtysix"));
+ ninetynine = (char *) malloc(256);
+ for (int i = 0; i < 255; i++)
+ ninetynine[i] = ' ';
+ ninetynine[255] = '\0';
+ strncpy(ninetynine, "ninetynine", strlen("ninetynine"));
+ hundred = (char *) malloc(256);
+ for (int i = 0; i < 255; i++)
+ hundred[i] = ' ';
+ hundred[255] = '\0';
+ strncpy(hundred, "hundred", strlen("hundred"));
+ myNdb.init();
+ // Wait for Ndb to become ready
+ if (myNdb.waitUntilReady(30) == 0)
+ {
+ if (createTableOp)
+ createTable(myNdb, storeInACC, twoKey, longKey);
+
+ if (createIndexOp)
+ createIndex(myNdb, includePrimary, noOfIndexes);
+
+ if (insertOp)
+ insertTable(myNdb, noOfTuples, noOfOperations, oneTransaction, twoKey, longKey);
+
+ if (updateOp)
+ updateTable(myNdb, noOfTuples, noOfOperations, oneTransaction, twoKey, longKey);
+
+ if (deleteOp)
+ deleteTable(myNdb, noOfTuples, noOfOperations, oneTransaction, twoKey, longKey);
+
+ if (readOp)
+ readTable(myNdb, noOfTuples, noOfOperations, oneTransaction, twoKey, longKey);
+
+ if (readIndexOp)
+ readIndex(myNdb, noOfTuples, noOfOperations, includePrimary, oneTransaction, longKey);
+
+ if (updateIndexOp)
+ updateIndex(myNdb, noOfTuples, noOfOperations, includePrimary, oneTransaction, longKey);
+
+ if (deleteIndexOp)
+ deleteIndex(myNdb, noOfTuples, noOfOperations, includePrimary, oneTransaction, longKey);
+
+ if (dropIndexOp)
+ dropIndex(myNdb, noOfIndexes);
+ }
+
+ if (testPassed)
+ {
+ // Test passed
+ ndbout << "OK - Test passed" << endl;
+ }
+ else
+ {
+ // Test failed
+ ndbout << "FAIL - Test failed" << endl;
+ exit(-1);
+ }
+ return NULL;
+}
+
+
diff --git a/storage/ndb/test/ndbapi/initronja.cpp b/storage/ndb/test/ndbapi/initronja.cpp
new file mode 100644
index 00000000000..3ce274e4319
--- /dev/null
+++ b/storage/ndb/test/ndbapi/initronja.cpp
@@ -0,0 +1,351 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* ***************************************************
+ INITRONJA
+ Initialise benchmark for Ronja Database
+ * *************************************************** */
+
+#include "NdbApi.hpp"
+#include "NdbSchemaCon.hpp"
+#include <NdbOut.hpp>
+#include <NdbMain.h>
+#include <NdbTest.hpp>
+#include <string.h>
+
+#define MAXSTRLEN 16
+#define MAXATTR 64
+#define MAXTABLES 64
+#define MAXTHREADS 256
+#define MAXATTRSIZE 8000
+
+static unsigned int tNoOfRecords;
+static unsigned int tNoOfLoops;
+static unsigned int tNoOfTables;
+static int tAttributeSize;
+static int tNodeId;
+static unsigned int tValue;
+static unsigned int tNoOfOperations;
+static char tableName[MAXTABLES][MAXSTRLEN];
+static char attrName[MAXATTR][MAXSTRLEN];
+
+inline int InsertRecords(Ndb*, int) ;
+
+NDB_COMMAND(initronja, "initronja", "initronja", "initronja", 65535){
+ ndb_init();
+
+ Ndb* pNdb = NULL ;
+ NdbSchemaCon *MySchemaTransaction = NULL ;
+ NdbSchemaOp *MySchemaOp = NULL ;
+
+
+ int check, status, i, j, cont ;
+ check = status = i = j = cont = 0 ;
+ tNoOfRecords = 500 ;
+ tNoOfLoops = tNoOfRecords / 10;
+
+ i = 1;
+ while (argc > 1){
+
+ if (strcmp(argv[i], "-r") == 0){
+ if( NULL == argv[i+1] ) goto error_input ;
+ tNoOfRecords = atoi(argv[i+1]);
+ tNoOfRecords = tNoOfRecords - (tNoOfRecords % 10);
+ tNoOfLoops = tNoOfRecords / 10;
+ if ((tNoOfRecords < 1) || (tNoOfRecords > 1000000000)) goto error_input;
+ }else{
+ goto error_input;
+ }
+
+ argc -= 2;
+ i = i + 2; //
+ }
+
+ pNdb = new Ndb( "TEST_DB" ) ;
+ ndbout << "Initialisation started. " << endl;
+ pNdb->init();
+ ndbout << "Initialisation completed. " << endl;
+
+ tNodeId = pNdb->getNodeId();
+ ndbout << endl << "Initial loading of Ronja Database" << endl;
+ ndbout << " NdbAPI node with id = " << tNodeId << endl;
+
+ if (pNdb->waitUntilReady(30) != 0) {
+ ndbout << "Benchmark failed - NDB is not ready" << endl;
+ delete pNdb ;
+ return NDBT_ProgramExit(NDBT_FAILED) ;
+ }//if
+
+ ndbout << endl << "Creating the table SHORT_REC" << "..." << endl;
+
+ MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pNdb);
+ if(!MySchemaTransaction) goto error_handler;
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if(!MySchemaOp) goto error_handler;
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ check = MySchemaOp->createTable( "SHORT_REC"
+ ,8 // Table Size
+ ,TupleKey // Key Type
+ ,40 // Nr of Pages
+ ,All
+ ,6
+ ,78
+ ,80
+ ,1
+ ,false);
+#else
+ check = MySchemaOp->createTable( "SHORT_REC"
+ ,8 // Table Size
+ ,TupleKey // Key Type
+ ,40 // Nr of Pages
+ );
+#endif
+ if (check == -1) goto error_handler;
+
+ ndbout << "Key attribute..." ;
+ check = MySchemaOp->createAttribute( (char*)"Key", TupleKey, 32, 1,
+ UnSigned, MMBased, NotNullAttribute );
+ if (check == -1) goto error_handler;
+ ndbout << "\t\tOK" << endl ;
+
+ ndbout << "Flip attribute..." ;
+ check = MySchemaOp->createAttribute("Flip", NoKey, 32, 1,
+ UnSigned, MMBased, NotNullAttribute );
+ if (check == -1) goto error_handler;
+ ndbout << "\t\tOK" << endl ;
+
+ ndbout << "Count attribute..." ;
+ check = MySchemaOp->createAttribute("Count", NoKey, 32, 1,
+ UnSigned, MMBased, NotNullAttribute );
+ if (check == -1) goto error_handler;
+ ndbout << "\t\tOK" << endl ;
+
+ ndbout << "Placeholder attribute..." ;
+ check = MySchemaOp->createAttribute("Placeholder", NoKey, 8, 90,
+ UnSigned, MMBased, NotNullAttribute );
+ if (check == -1) goto error_handler;
+ ndbout << "\tOK" << endl ;
+
+ if (MySchemaTransaction->execute() == -1) {
+ if(721 == MySchemaOp->getNdbError().code){
+ ndbout << "Table SHORT_REC already exists" << endl ;
+ }else{
+ ndbout << MySchemaTransaction->getNdbError() << endl;
+ }
+ }else{
+ ndbout << "SHORT_REC created " << endl;
+ }// if
+
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+
+ ndbout << endl << "Creating the table LONG_REC..." << endl;
+
+ MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pNdb);
+ if(!MySchemaTransaction) goto error_handler;
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if(!MySchemaOp) goto error_handler;
+#if defined NDB_OSE || defined NDB_SOFTOSE
+ check = MySchemaOp->createTable( "LONG_REC"
+ ,8 // Table Size
+ ,TupleKey // Key Type
+ ,40 // Nr of Pages
+ ,All
+ ,6
+ ,78
+ ,80
+ ,1
+ ,false);
+#else
+ check = MySchemaOp->createTable( "LONG_REC"
+ ,8 // Table Size
+ ,TupleKey // Key Type
+ ,40 // Nr of Pages
+ );
+#endif
+
+ if (check == -1) goto error_handler;
+
+ ndbout << "Key attribute..." ;
+ check = MySchemaOp->createAttribute( (char*)"Key", TupleKey, 32, 1,
+ UnSigned, MMBased, NotNullAttribute );
+ if (check == -1) goto error_handler;
+ ndbout << "\t\tOK" << endl ;
+
+ ndbout << "Flip attribute..." ;
+ check = MySchemaOp->createAttribute("Flip", NoKey, 32, 1,
+ UnSigned, MMBased, NotNullAttribute );
+ if (check == -1) goto error_handler;
+ ndbout << "\t\tOK" << endl ;
+
+ ndbout << "Count attribute..." ;
+ check = MySchemaOp->createAttribute("Count", NoKey, 32, 1,
+ UnSigned, MMBased, NotNullAttribute );
+ if (check == -1) goto error_handler;
+ ndbout << "\t\tOK" << endl ;
+
+ ndbout << "Placeholder attribute..." ;
+ check = MySchemaOp->createAttribute("Placeholder", NoKey, 8, 1014,
+ UnSigned, MMBased, NotNullAttribute );
+ if (check == -1) goto error_handler;
+ ndbout << "\tOK" << endl ;
+
+ if (MySchemaTransaction->execute() == -1) {
+ if(721 == MySchemaOp->getNdbError().code){
+ ndbout << "Table LONG_REC already exists" << endl ;
+ }else{
+ ndbout << MySchemaTransaction->getNdbError() << endl;
+ }
+ }else{
+ ndbout << "LONG_REC created" << endl;
+ }// if
+
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+
+
+ check = InsertRecords(pNdb, tNoOfRecords);
+
+ delete pNdb ;
+
+ if(-1 == check){
+ ndbout << endl << "Initial loading of Ronja Database failed" << endl;
+ return NDBT_ProgramExit(NDBT_FAILED) ;
+ }else{
+ ndbout << endl << "Initial loading of Ronja Database completed" << endl;
+ return NDBT_ProgramExit(NDBT_OK) ;
+ }
+
+
+
+
+
+error_handler:
+ ndbout << "SchemaTransaction returned error:" ;
+ ndbout << MySchemaTransaction->getNdbError() << endl;
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ delete pNdb ;
+ NDBT_ProgramExit(NDBT_FAILED) ;
+ exit(-1);
+
+error_input:
+ ndbout << endl << " Ivalid parameter(s)" << endl;
+ ndbout << " Usage: initronja [-r n] , where 'n' is the number of records to be inserted" << endl;
+ ndbout << " If omitted, 500 records will be created by default" << endl;
+ ndbout << " Note: use this number in combination with '-r' argument when running 'benchronja'" << endl << endl;
+ NDBT_ProgramExit(NDBT_WRONGARGS) ;
+ exit(1);
+}
+////////////////////////////////////////
+
+inline int InsertRecords(Ndb* pNdb, int nNoRecords){
+
+ NdbConnection *MyTransaction = NULL ;
+ NdbOperation* MyOperation[10];
+
+ int Tsuccess = 0 ;
+ int loop_count_ops = 2 * tNoOfLoops;
+ int loop_count_tables = 10;
+ int loop_count_attributes = 0 ;
+ int check = 0;
+ int count = 0 ;
+ int count_tables = 0;
+ int count_attributes = 0 ;
+ int i = 0 ;
+ int tType = 0 ;
+ unsigned int attrValue[1000];
+ unsigned int setAttrValue = 0;
+ unsigned int keyValue[3];
+
+ for (i = 0; i < 1000; i ++) attrValue[i] = 1;
+
+ for (count=0 ; count < loop_count_ops ; count++){
+ if ((((count / 100)* 100) == count) && (count != 0)){
+ ndbout << "1000 records inserted again, " << (count/100) << "000 records now inserted" << endl;
+ }
+
+ MyTransaction = pNdb->startTransaction();
+ if(!MyTransaction){
+ ndbout << "startTransaction: " << pNdb->getNdbError();
+ ndbout << " count = " << count << endl;
+ return -1 ;
+ }
+
+ for (count_tables = 0; count_tables < loop_count_tables; count_tables++) {
+ if (count < tNoOfLoops) {
+ keyValue[0] = count*10 + count_tables ;
+ MyOperation[count_tables] = MyTransaction->getNdbOperation("SHORT_REC") ;
+ }else{
+ keyValue[0] = (count - tNoOfLoops)*10 + count_tables;
+ MyOperation[count_tables] = MyTransaction->getNdbOperation("LONG_REC");
+ }//if
+
+ if (!MyOperation[count_tables]) goto error_handler1;
+
+ check = MyOperation[count_tables]->insertTuple();
+ if (check == -1) goto error_handler2;
+
+ check = MyOperation[count_tables]->equal("Key",(char*)&keyValue[0]);
+ if (check == -1) goto error_handler4;
+
+ check = MyOperation[count_tables]->setValue("Flip",(char*)&setAttrValue);
+ if (check == -1) goto error_handler5;
+
+ check = MyOperation[count_tables]->setValue("Count",(char*)&setAttrValue);
+ if (check == -1) goto error_handler5;
+
+ check = MyOperation[count_tables]->setValue("Placeholder",(char*)&attrValue[0]);
+ if (check == -1) goto error_handler5;
+ }//for
+
+ if (MyTransaction->execute( Commit ) == -1){
+ ndbout << MyTransaction->getNdbError()<< endl ;
+ ndbout << "count = " << count << endl;
+ }//if
+
+ pNdb->closeTransaction(MyTransaction) ;
+ }//for
+ return 0;
+
+error_handler1:
+ ndbout << "Error occured in getNdbOperation " << endl;
+ ndbout << MyTransaction->getNdbError() << endl;
+ pNdb->closeTransaction(MyTransaction);
+ return -1 ;
+
+error_handler2:
+ ndbout << "Error occured in defining operation " << endl;
+ ndbout << MyOperation[count_tables]->getNdbError() << endl;
+ pNdb->closeTransaction(MyTransaction);
+ return -1 ;
+
+error_handler3:
+ pNdb->closeTransaction(MyTransaction);
+ return -1 ;
+
+error_handler4:
+ ndbout << "Error occured in equal " << endl;
+ ndbout << MyOperation[count_tables]->getNdbError() << endl;
+ pNdb->closeTransaction(MyTransaction);
+ return -1 ;
+
+error_handler5:
+ ndbout << "Error occured in get/setValue " << endl;
+ ndbout << MyOperation[count_tables]->getNdbError() << endl;
+ pNdb->closeTransaction(MyTransaction);
+ return -1 ;
+
+}
diff --git a/storage/ndb/test/ndbapi/interpreterInTup.cpp b/storage/ndb/test/ndbapi/interpreterInTup.cpp
new file mode 100644
index 00000000000..a07d5898213
--- /dev/null
+++ b/storage/ndb/test/ndbapi/interpreterInTup.cpp
@@ -0,0 +1,1518 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* ***************************************************
+ TEST OF INTERPRETER IN TUP
+ Verify that the interpreter in TUP is able to
+ handle and execute all the commands that the
+ NdbApi can isssue
+
+ Arguments:
+
+ operationType 1 openScanRead,
+ 2 openScanExclusive,
+ 3 interpretedUpdateTuple,
+ 4 interpretedDirtyUpdate,
+ 5 interpretedDeleteTuple
+ 6 deleteTuple
+ 7 insertTuple
+ 8 updateTuple
+ 9 writeTuple
+ 10 readTuple
+ 11 readTupleExclusive
+ 12 simpleRead
+ 13 dirtyRead
+ 14 dirtyUpdate
+ 15 dirtyWrite
+
+ tupTest 1 exitMethods
+ 2 incValue
+ 3 subValue
+ 4 readAttr
+ 5 writeAttr
+ 6 loadConst
+ 7 branch
+ 8 branchIfNull
+ 9 addReg
+ 10 subReg
+ 11 subroutineWithBranchLabel
+
+ scanTest Number of the test within each tupTest
+
+* *************************************************** */
+
+#include <NdbOut.hpp>
+#include <NdbThread.h>
+#include <NdbMutex.h>
+#include <NdbApi.hpp>
+#include <NdbSchemaCon.hpp>
+#include <NDBT.hpp>
+
+#define MAXATTR 3
+#define MAXTABLES 12
+#define MAXSTRLEN 8
+#define NUMBEROFRECORDS 1000
+
+typedef enum {
+ FAIL = 0,
+ NO_FAIL,
+ UNDEF
+} TTYPE;
+
+inline void setAttrNames() ;
+inline void setTableNames() ;
+void error_handler(const NdbError & err, TTYPE);
+void create_table(Ndb*);
+void write_rows(Ndb*);
+void update_rows(Ndb*, int, int);
+void delete_rows(Ndb*, int, int);
+void verify_deleted(Ndb*);
+void read_and_verify_rows(Ndb*, bool pre);
+void scan_rows(Ndb*, int, int, int);
+TTYPE t_exitMethods(int, NdbOperation*, int);
+TTYPE t_incValue(int, NdbOperation*);
+TTYPE t_subValue(int, NdbOperation*);
+TTYPE t_readAttr(int, NdbOperation*);
+TTYPE t_writeAttr(int, NdbOperation*);
+TTYPE t_loadConst(int, NdbOperation*, int);
+TTYPE t_branch(int, NdbOperation*);
+TTYPE t_branchIfNull(int, NdbOperation*);
+TTYPE t_addReg(int, NdbOperation*);
+TTYPE t_subReg(int, NdbOperation*);
+TTYPE t_subroutineWithBranchLabel(int, NdbOperation*);
+
+char tableName[MAXSTRLEN+1];
+char attrName[MAXATTR][MAXSTRLEN+1];
+int attrValue[NUMBEROFRECORDS] = {0};
+int pkValue[NUMBEROFRECORDS] = {0};
+const int tAttributeSize = 1 ;
+const int nRecords = 20 ;
+int bTestPassed = 0;
+
+
+
+int main(int argc, const char** argv) {
+ ndb_init();
+
+ int operationType = 0;
+ int tupTest = 0;
+ int scanTest = 0;
+
+ Ndb* pNdb = new Ndb("TEST_DB");
+ pNdb->init();
+
+ if (argc != 4 || sscanf(argv[1],"%d", &operationType) != 1) {
+ operationType = 1 ;
+ }
+ if (argc != 4 || sscanf(argv[2],"%d", &tupTest) != 1) {
+ tupTest = 1 ;
+ }
+ if (argc != 4 || sscanf(argv[3],"%d", &scanTest) != 1) {
+ scanTest = 1 ;
+ }
+
+ ndbout << endl
+ << "Test the interpreter in TUP using SimpleTable with\n"
+ << nRecords << " records" << endl << endl ;
+
+ if (pNdb->waitUntilReady(30) != 0) {
+ ndbout << "NDB is not ready" << endl;
+ return -1;
+ }
+
+ // Init the pk and attr values.
+ for (int i = 0; i < NUMBEROFRECORDS; i ++)
+ pkValue[i] = attrValue[i] = i ;
+
+ setAttrNames() ;
+ setTableNames() ;
+
+ const void * p = NDBT_Table::discoverTableFromDb(pNdb, tableName);
+ if (p != 0){
+ create_table(pNdb);
+ }
+
+ write_rows(pNdb);
+
+ ndbout << endl << "Starting interpreter in TUP test." << endl << "Operation type: " ;
+
+ switch(operationType) {
+ case 1:
+ ndbout << "openScanRead" << endl;
+ scan_rows(pNdb, operationType, tupTest, scanTest);
+ break;
+ case 2:
+ ndbout << "openScanExclusive" << endl;
+ scan_rows(pNdb, operationType, tupTest, scanTest);
+ break;
+ case 3:
+ ndbout << "interpretedUpdateTuple" << endl;
+ update_rows(pNdb, tupTest, operationType);
+ break;
+ case 4:
+ ndbout << "interpretedDirtyUpdate" << endl;
+ update_rows(pNdb, tupTest, operationType);
+ break;
+ case 5:
+ ndbout << "interpretedDeleteTuple" << endl;
+ delete_rows(pNdb, tupTest, operationType);
+ break;
+ case 6:
+ ndbout << "deleteTuple" << endl;
+ break;
+ case 7:
+ ndbout << "insertTuple" << endl;
+ break;
+ case 8:
+ ndbout << "updateTuple" << endl;
+ break;
+ case 9:
+ ndbout << "writeTuple" << endl;
+ break;
+ case 10:
+ ndbout << "readTuple" << endl;
+ break;
+ case 11:
+ ndbout << "readTupleExclusive" << endl;
+ break;
+ case 12:
+ ndbout << "simpleRead" << endl;
+ break;
+ case 13:
+ ndbout << "dirtyRead" << endl;
+ break;
+ case 14:
+ ndbout << "dirtyUpdate" << endl;
+ break;
+ case 15:
+ ndbout << "dirtyWrite" << endl;
+ break;
+ default:
+ break ;
+
+ }
+
+// read_and_verify_rows(pNdb, false);
+
+// delete_rows(pNdb, 0, 0) ;
+ delete pNdb ;
+
+ if (bTestPassed == 0) {
+ ndbout << "OK: test passed" << endl;
+ exit(0);
+ }else{
+ ndbout << "FAIL: test failed" << endl;
+ exit(-1);
+ }
+}
+
+void error_handler(const NdbError & err, TTYPE type_expected) {
+
+ ndbout << err << endl ;
+
+ switch (type_expected){
+ case NO_FAIL:
+ bTestPassed = -1 ;
+ break ;
+ case FAIL:
+ ndbout << "OK: error is expected" << endl;
+ break ;
+ case UNDEF:
+ ndbout << "assumed OK: expected result undefined" << endl ;
+ break ;
+ default:
+ break ;
+ }
+}
+
+void create_table(Ndb* pMyNdb) {
+
+ /****************************************************************
+ * Create SimpleTable and Attributes.
+ *
+ * create table SimpleTable1(
+ * col0 int,
+ * col1 int not null,
+ * col2 int not null,
+ * col3 int not null ... 129)
+ *
+ ***************************************************************/
+
+ int check = -1 ;
+ NdbSchemaOp *MySchemaOp = NULL ;
+
+ ndbout << endl << "Creating " << tableName << " ... " << endl;
+
+ NdbSchemaCon* MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pMyNdb);
+ if(!MySchemaTransaction) error_handler(MySchemaTransaction->getNdbError(), NO_FAIL);
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( !MySchemaOp ) error_handler(MySchemaTransaction->getNdbError(), NO_FAIL);
+ // Create table
+ check = MySchemaOp->createTable( tableName,
+ 8, // Table size
+ TupleKey, // Key Type
+ 40 // Nr of Pages
+ );
+
+ if( check == -1 ) error_handler(MySchemaTransaction->getNdbError(), NO_FAIL);
+
+ ndbout << "Creating attributes ... " << flush;
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute( attrName[0],
+ TupleKey,
+ 32,
+ 1/*3, tAttributeSize */,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if( check == -1 ) error_handler(MySchemaTransaction->getNdbError(), NO_FAIL);
+
+ // create the 2 .. n columns.
+ for ( int i = 1; i < MAXATTR; i++ ){
+ check = MySchemaOp->createAttribute( attrName[i],
+ NoKey,
+ 32,
+ tAttributeSize,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if( check == -1 ) error_handler(MySchemaTransaction->getNdbError(), NO_FAIL);
+ }
+
+ ndbout << "OK" << endl;
+
+ if( MySchemaTransaction->execute() == -1 ) {
+ ndbout << MySchemaTransaction->getNdbError() << endl;
+ }else{
+ ndbout << tableName[0] << " created" << endl;
+ }
+
+
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+
+ return;
+
+}
+
+
+
+void write_rows (Ndb* pMyNdb) {
+
+ /****************************************************************
+ * Insert rows into SimpleTable
+ *
+ ***************************************************************/
+
+ int check = -1 ;
+ int loop_count_ops = nRecords ;
+ NdbOperation *MyOperation = NULL ;
+ NdbConnection *MyTransaction = NULL ;
+
+ ndbout << endl << "Writing records ..." << flush;
+
+ for (int count=0 ; count < loop_count_ops ; count++){
+ MyTransaction = pMyNdb->startTransaction();
+ if (!MyTransaction) {
+ error_handler(pMyNdb->getNdbError(), NO_FAIL);
+ }//if
+
+ MyOperation = MyTransaction->getNdbOperation(tableName);
+ if (!MyOperation) {
+ error_handler(pMyNdb->getNdbError(), NO_FAIL);
+ }//if
+
+ check = MyOperation->writeTuple();
+ if( check == -1 ) error_handler(MyTransaction->getNdbError(), NO_FAIL);
+
+ check = MyOperation->equal( attrName[0],(char*)&pkValue[count] );
+ if( check == -1 ) error_handler(MyTransaction->getNdbError(), NO_FAIL);
+
+ // Update the columns, index column already ok.
+ for (int i = 1 ; i < MAXATTR; i++){
+ if ((i == 2) && (count > 4)){
+ check = MyOperation->setValue(attrName[i], (char*)&attrValue[count + 1]);
+ }else{
+ check = MyOperation->setValue(attrName[i], (char*)&attrValue[count]);
+ }
+ if( check == -1 ) error_handler(MyTransaction->getNdbError(), NO_FAIL);
+ }
+ check = MyTransaction->execute( Commit );
+ if(check == -1 ) error_handler(MyTransaction->getNdbError(), NO_FAIL);
+
+ pMyNdb->closeTransaction(MyTransaction);
+ }
+ ndbout <<" \tOK" << endl;
+ return;
+}
+
+void verify_deleted(Ndb* pMyNdb) {
+
+ int check = -1 ;
+ int loop_count_ops = nRecords;
+ NdbOperation* pMyOperation = NULL ;
+
+ ndbout << "Verifying deleted records..."<< flush;
+
+ for (int count=0 ; count < loop_count_ops ; count++){
+
+ NdbConnection* pMyTransaction = pMyNdb->startTransaction();
+ if (!pMyTransaction) error_handler(pMyNdb->getNdbError(), NO_FAIL);
+
+ pMyOperation = pMyTransaction->getNdbOperation(tableName);
+ if (!pMyOperation) error_handler(pMyNdb->getNdbError(), NO_FAIL);
+
+ check = pMyOperation->readTuple();
+ if( check == -1 ) error_handler( pMyTransaction->getNdbError(), NO_FAIL);
+
+ check = pMyOperation->equal( attrName[0],(char*)&pkValue[count] );
+ if( check == -1 ) error_handler( pMyTransaction->getNdbError(), NO_FAIL);
+
+ // Exepect to receive an error
+ if(pMyTransaction->execute(Commit) != -1)
+ if( 626 == pMyTransaction->getNdbError().code){
+ ndbout << pMyTransaction->getNdbError() << endl ;
+ ndbout << "OK" << endl ;
+ }else{
+ error_handler(pMyTransaction->getNdbError(), NO_FAIL) ;
+ }
+
+ pMyNdb->closeTransaction(pMyTransaction);
+ }
+
+ ndbout << "OK" << endl;
+ return;
+};
+
+void read_and_verify_rows(Ndb* pMyNdb, bool pre) {
+
+ int check = -1 ;
+ int loop_count_ops = nRecords;
+ char expectedCOL1[NUMBEROFRECORDS] = {0} ;
+ char expectedCOL2[NUMBEROFRECORDS] = {0} ;
+ NdbConnection *pMyTransaction = NULL ;
+ NdbOperation *MyOp = NULL ;
+ NdbRecAttr* tTmp = NULL ;
+ int readValue[MAXATTR] = {0} ;
+
+ ndbout << "Verifying records...\n"<< endl;
+
+ for (int count=0 ; count < loop_count_ops ; count++){
+
+ pMyTransaction = pMyNdb->startTransaction();
+ if (!pMyTransaction) error_handler(pMyNdb->getNdbError(), NO_FAIL);
+
+ MyOp = pMyTransaction->getNdbOperation(tableName);
+ if (!MyOp) error_handler( pMyTransaction->getNdbError(), NO_FAIL);
+
+
+ check = MyOp->readTuple();
+ if( check == -1 ) error_handler( MyOp->getNdbError(), NO_FAIL);
+
+ check = MyOp->equal( attrName[0],(char*)&pkValue[count] );
+ if( check == -1 ) error_handler( MyOp->getNdbError(), NO_FAIL);
+
+ for (int count_attributes = 1; count_attributes < MAXATTR; count_attributes++){
+
+ tTmp = MyOp->getValue( (char*)attrName[count_attributes], (char*)&readValue[count_attributes] );
+ if(!tTmp) error_handler( MyOp->getNdbError(), NO_FAIL);
+ }
+
+ if( pMyTransaction->execute( Commit ) == -1 ) {
+ error_handler(pMyTransaction->getNdbError(), NO_FAIL);
+ } else {
+ if (pre) {
+ expectedCOL1[count] = readValue[1];
+ expectedCOL2[count] = readValue[2];
+ }
+
+ ndbout << attrName[1] << "\t " << readValue[1] << "\t "
+ << attrName[2] << "\t " << readValue[2] << endl;
+ }
+
+ pMyNdb->closeTransaction(pMyTransaction);
+
+ }
+
+ ndbout << "\nOK\n" << endl;
+
+ return;
+
+};
+
+TTYPE t_exitMethods(int nCalls, NdbOperation * pOp, int opType) {
+
+ ndbout << "Defining exitMethods test " << nCalls << ": " << endl ;;
+ TTYPE ret_val = NO_FAIL ;
+
+ switch(nCalls){
+ case 1: // exit_nok if attr value matches
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 14);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_ok() ;
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ break;
+ case 2: // exit_ok if attr value doesn't match
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 14);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_nok() ;
+ pOp->def_label(0);
+ if (opType == 3) {
+ // For update transactions use incValue to update the tuple
+ Uint32 val32 = 5;
+ pOp->incValue("COL2", val32);
+ }
+ pOp->interpret_exit_ok();
+ break ;
+ case 3: // Non-existent value (128): exit_ok if if the value matches
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 128);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_nok();
+ pOp->def_label(0);
+ pOp->interpret_exit_ok();
+ ret_val = FAIL ;
+ break;
+ case 4: // Non-existent value (128): exit_nok if the value matches
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 128);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ ret_val = FAIL ;
+ break;
+ case 5: // exit_nok of the value matches
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 2);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ break ;
+ case 6: // exit_ok of the value matches
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 2);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_nok();
+ pOp->def_label(0);
+ if (opType == 3) {
+ // For update transactions use incValue to update the tuple
+ Uint32 val32 = 5;
+ pOp->incValue("COL2", val32);
+ }
+ pOp->interpret_exit_ok();
+ break;
+ case 7: // exit_nok if the value matches
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 6);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ break;
+ case 8: // exit_ok if the value matches
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 6);
+ pOp->branch_ne(1, 2, 0);
+ pOp->interpret_exit_nok();
+ pOp->def_label(0);
+ if (opType == 3) {
+ // For update transactions use incValue to update the tuple
+ Uint32 val32 = 5;
+ pOp->incValue("COL2", val32);
+ }
+ pOp->interpret_exit_ok();
+ break ;
+ case 9: // exit_nok if the value matches
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 8);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ break;
+ case 10: // exit_ok if the value matches
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 8);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_nok();
+ pOp->def_label(0);
+ if (opType == 3) {
+ // For update transactions use incValue to update the tuple
+ Uint32 val32 = 5;
+ pOp->incValue("COL2", val32);
+ }
+ pOp->interpret_exit_ok();
+ break;
+ case 11: // exit_nok if the value matches
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 10);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ break;
+ case 12:
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 10);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_nok();
+ pOp->def_label(0);
+ if (opType == 3) {
+ // For update transactions use incValue to update the tuple
+ Uint32 val32 = 5;
+ pOp->incValue("COL2", val32);
+ }
+ pOp->interpret_exit_ok();
+ break;
+ case 13:
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 10);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ break;
+ case 14: // exit_nok if the value matches
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 12);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ break;
+ case 15: // exit_ok if the value matches
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, 12);
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_nok();
+ pOp->def_label(0);
+ if (opType == 3) {
+ // For update transactions use incValue to update the tuple
+ Uint32 val32 = 5;
+ pOp->incValue("COL2", val32);
+ }
+ pOp->interpret_exit_ok();
+ case 16:
+ pOp->interpret_exit_nok();
+ ret_val = FAIL ;
+ break;
+ case 17:
+ pOp->interpret_exit_ok();
+ break ;
+ case 18:
+ pOp->interpret_exit_nok();
+ ret_val = FAIL ;
+ break ;
+ default:
+ break ;
+ }
+ return ret_val;
+}
+
+TTYPE t_incValue(int nCalls, NdbOperation* pOp) {
+
+ ndbout << "Defining incValue test " << nCalls << ": ";
+ TTYPE ret_val = NO_FAIL;
+
+ Uint32 val32 = 5;
+ Uint64 val64 = 5;
+ Uint32 attr0 = 0;
+ Uint32 attr1 = 1;
+ Uint32 attr20 = 20;
+
+ switch(nCalls) {
+ case 1:
+ pOp->incValue(attrName[1], val32);
+ break;
+ case 2:
+ pOp->incValue(attr1, val32);
+ break;
+ case 3:
+ pOp->incValue(attrName[1], val64);
+ break;
+ case 4:
+ pOp->incValue(attr1, val64);
+ break;
+ case 5:
+ pOp->incValue(attrName[0], val32);
+ ret_val = FAIL ;
+ break;
+ case 6:
+ pOp->incValue(attrName[0], val64);
+ ret_val = FAIL ;
+ break;
+ case 7:
+ pOp->incValue(attr0, val32);
+ ret_val = FAIL ;
+ break;
+ case 8:
+ pOp->incValue(attr0, val64);
+ ret_val = FAIL ;
+ break;
+ case 9:
+ pOp->incValue("COL20", val32);
+ ret_val = FAIL ;
+ break;
+ case 10:
+ pOp->incValue("COL20", val64);
+ ret_val = FAIL ;
+ break;
+ case 11:
+ pOp->incValue(attr20, val32);
+ ret_val = FAIL ;
+ break;
+ case 12:
+ pOp->incValue(attr20, val64);
+ ret_val = FAIL ;
+ break;
+ default:
+ break ;
+ }
+ return ret_val;
+}
+
+TTYPE t_subValue(int nCalls, NdbOperation* pOp) {
+
+ ndbout << "Defining subValue test " << nCalls << ": ";
+
+ Uint32 val32 = 5;
+ Uint64 val64 = 5;
+ Uint32 attr0 = 0;
+ Uint32 attr1 = 1;
+ Uint32 attr20 = 20;
+
+ TTYPE ret_val = NO_FAIL;
+
+ switch(nCalls) {
+ case 1:
+ pOp->subValue("COL2", val32);
+ break;
+ case 2:
+ pOp->subValue(attr1, val32);
+ break;
+ case 3:
+ pOp->subValue("COL0", val32);
+ ret_val = FAIL ;
+ break;
+ case 4:
+ pOp->subValue(attr0, val32);
+ ret_val = FAIL ;
+ break;
+ case 5:
+ pOp->subValue("COL20", val32);
+ ret_val = FAIL ;
+ break;
+ case 6:
+ pOp->subValue(attr20, val32);
+ ret_val = FAIL ;
+ break;
+ case 7:
+ // Missing implementation
+ //pOp->subValue("COL20", val64);
+ ndbout << "Missing implementation" << endl;
+ break;
+ case 8:
+ // Missing implementation
+ //pOp->subValue("COL2", val64);
+ ndbout << "Missing implementation" << endl;
+ break;
+ case 9:
+ // Missing implementation
+ //pOp->subValue("COL0", val64);
+ ndbout << "Missing implementation" << endl;
+ break;
+ case 10:
+ // Missing implementation
+ //pOp->subValue(attr1, val64);
+ ndbout << "Missing implementation" << endl;
+ break;
+ case 11:
+ // Missing implementation
+ //pOp->subValue(attr0, val64);
+ ndbout << "Missing implementation" << endl;
+ break;
+ case 12:
+ // Missing implementation
+ //pOp->subValue(attr20, val64);
+ ndbout << "Missing implementation" << endl;
+ break;
+ default:
+ break ;
+ }
+ return ret_val ;
+}
+
+TTYPE t_readAttr(int nCalls, NdbOperation* pOp) {
+
+ ndbout << "Defining readAttr test " << nCalls << ": ";
+
+ Uint32 attr0 = 0;
+ Uint32 attr1 = 1;
+ Uint32 attr20 = 20;
+ TTYPE ret_val = NO_FAIL;
+
+ switch(nCalls) {
+ case 1:
+ pOp->read_attr("COL1", 1);
+ break;
+ case 2:
+ pOp->read_attr(attr1, 1);
+ ret_val = NO_FAIL ;
+ break;
+ case 3:
+ pOp->read_attr("COL0", 1);
+ ret_val = NO_FAIL ;
+ break;
+ case 4:
+ pOp->read_attr(attr0, 1);
+ ret_val = NO_FAIL ;
+ break;
+ case 5:
+ pOp->read_attr("COL20", 1);
+ ret_val = FAIL ;
+ break;
+ case 6:
+ pOp->read_attr(20, 1);
+ ret_val = FAIL ;
+ break;
+ case 7:
+ pOp->read_attr("COL1", 8);
+ ret_val = FAIL ;
+ break;
+ case 8:
+ pOp->read_attr(attr1, 8);
+ ret_val = FAIL ;
+ break;
+ default:
+ break ;
+ }
+ return ret_val;
+}
+
+TTYPE t_writeAttr(int nCalls, NdbOperation* pOp) {
+
+ ndbout << "Defining writeAttr test " << nCalls << ": ";
+
+ pOp->load_const_u32(1, 5);
+
+ Uint32 attr0 = 0;
+ Uint32 attr1 = 1;
+ Uint32 attr20 = 20;
+ TTYPE ret_val = NO_FAIL ;
+
+ switch(nCalls) {
+ case 1:
+ pOp->write_attr("COL1", 1);
+ break;
+ case 2:
+ pOp->write_attr(attr1, 1);
+ break;
+ case 3:
+ pOp->write_attr("COL0", 1);
+ ret_val = FAIL ;
+ break;
+ case 4:
+ pOp->write_attr(attr0, 1);
+ ret_val = FAIL ;
+ break;
+ case 5:
+ pOp->write_attr("COL20", 1);
+ ret_val = FAIL ;
+ break;
+ case 6:
+ pOp->write_attr(20, 1);
+ ret_val = FAIL ;
+ break;
+ case 7:
+ pOp->write_attr("COL1", 2);
+ ret_val = FAIL ;
+ break;
+ case 8:
+ pOp->write_attr(attr1, 2);
+ ret_val = FAIL ;
+ break;
+ case 9:
+ pOp->write_attr("COL1", 8);
+ ret_val = FAIL ;
+ break;
+ case 10:
+ pOp->write_attr(attr1, 8);
+ ret_val = FAIL ;
+ break;
+ default:
+ break ;
+ }
+ return ret_val ;
+}
+
+TTYPE t_loadConst(int nCalls, NdbOperation* pOp, int opType) {
+
+ ndbout << "Defining loadConst test " << nCalls << " : ";
+ TTYPE ret_val = NO_FAIL ;
+
+ switch(nCalls) {
+ case 1:
+ // Loading null into a valid register
+ pOp->load_const_null(1);
+ break;
+ case 2:
+ // Loading null into an invalid register
+ pOp->load_const_null(8);
+ ret_val = FAIL ;
+ break;
+ case 3:
+ // Test loading a 32-bit value (>65536)
+ pOp->load_const_u32(1, 65600);
+ break;
+ case 4:
+ // Test loading a 16-bit value (<65536)
+ pOp->load_const_u32(1, 65500);
+ break;
+ case 5:
+ // Test by loading to a non-valid register
+ pOp->load_const_u32(8, 2);
+ ret_val = FAIL ;
+ break;
+ case 6:
+ // Test by loading a 64-bit value
+ pOp->load_const_u64(1, 65600);
+ break;
+ case 7:
+ // Test by loading a non-valid register
+ pOp->load_const_u64(8, 2);
+ ret_val = FAIL ;
+ break;
+ case 8:
+ // Test by loading a valid register with -1
+ pOp->load_const_u64(1, -1);
+ ret_val = FAIL ;
+ break;
+
+ default:
+ break ;
+ }
+
+ if (opType == 3 && FAIL != ret_val)
+ pOp->write_attr("COL1", 1);
+ return ret_val;
+}
+
+TTYPE t_branch(int nCalls, NdbOperation* pOp) {
+
+ ndbout << "Defining branch test " << nCalls << ": " ;
+
+ TTYPE ret_val = NO_FAIL ;
+ Uint32 val32=5;
+
+ pOp->read_attr("COL1", 1);
+ pOp->load_const_u32(2, val32);
+
+ switch(nCalls) {
+ case 1:
+ pOp->branch_eq(1, 2, 0);
+ pOp->interpret_exit_nok();
+ pOp->def_label(0);
+ pOp->interpret_exit_ok();
+ break;
+ case 2:
+ pOp->branch_eq(2, 1, 0);
+ pOp->interpret_exit_nok();
+ pOp->def_label(0);
+ pOp->interpret_exit_ok();
+ break;
+ case 3:
+ pOp->branch_eq(1, 1, 0);
+ pOp->interpret_exit_nok();
+ pOp->def_label(0);
+ pOp->interpret_exit_ok();
+ break;
+ default:
+ //ndbout << "t_branch: default case (no test case)" << endl ;
+ //return ret_val = NO_FAIL ;
+ break ;
+ }
+ return ret_val;
+}
+
+TTYPE t_branchIfNull(int nCalls, NdbOperation* pOp) {
+
+ TTYPE ret_val = NO_FAIL;
+ ndbout << "Defining branchIfNull test " << nCalls << ": " << endl ;
+
+ switch(nCalls) {
+ case 1:
+ pOp->load_const_u32(1, 1);
+ pOp->branch_ne_null(1, 0);
+ pOp->interpret_exit_nok();
+ pOp->def_label(0);
+ pOp->interpret_exit_ok();
+ break;
+ case 2:
+ pOp->load_const_null(1);
+ pOp->branch_ne_null(1, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ break;
+ case 3:
+ pOp->load_const_u32(1, 1);
+ pOp->branch_eq_null(1, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ break;
+ case 4:
+ pOp->load_const_null(1);
+ pOp->branch_ne_null(1, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ break;
+ case 5:
+ // Test with a non-initialized register
+ pOp->branch_ne_null(3, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ ret_val = FAIL ;
+ break;
+ case 6:
+ // Test with a non-existing register
+ pOp->branch_ne_null(8, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ ret_val = FAIL ;
+ break;
+ case 7:
+ // Test with a non-initialized register
+ pOp->branch_eq_null(3, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ ret_val = FAIL ;
+ break;
+ case 8:
+ // Test with a non-existing register
+ pOp->branch_ne_null(8, 0);
+ pOp->interpret_exit_ok();
+ pOp->def_label(0);
+ pOp->interpret_exit_nok();
+ ret_val = FAIL ;
+ break;
+ default:
+ break ;
+ }
+
+ return ret_val;
+}
+
+TTYPE t_addReg(int nCalls, NdbOperation* pOp) {
+
+ TTYPE ret_val = NO_FAIL ;
+
+ ndbout << "Defining addReg test " << nCalls << ": ";
+
+ pOp->load_const_u32(1, 65500);
+ pOp->load_const_u32(2, 500);
+ pOp->load_const_u64(3, 65600);
+ pOp->load_const_u64(4, 95600);
+
+ switch(nCalls) {
+ case 1:
+ pOp->add_reg(1, 2, 5);
+ break;
+ case 2:
+ pOp->add_reg(1, 3, 5);
+ break;
+ case 3:
+ pOp->add_reg(3, 1, 5);
+ break;
+ case 4:
+ pOp->add_reg(3, 4, 5);
+ break;
+ case 5:
+ pOp->add_reg(1, 6, 5);
+ break;
+ case 6:
+ pOp->add_reg(6, 1, 5);
+ break;
+ case 7: // illegal register
+ pOp->add_reg(1, 8, 5);
+ ret_val = FAIL ;
+ break;
+ case 8: // another illegal register
+ pOp->add_reg(8, 1, 5);
+ ret_val = FAIL ;
+ break;
+ case 9: // and another one
+ pOp->add_reg(1, 2, 8);
+ ret_val = FAIL ;
+ break;
+ default:
+ break ;
+ }
+ pOp->load_const_u32(7, 65000);
+ pOp->branch_eq(5, 7, 0);
+ pOp->interpret_exit_nok();
+ pOp->def_label(0);
+ pOp->interpret_exit_ok();
+
+ return ret_val ;
+}
+
+TTYPE t_subReg(int nCalls, NdbOperation* pOp) {
+
+ TTYPE ret_val = NO_FAIL ;
+ ndbout << "Defining subReg test: " << nCalls << endl;
+
+ pOp->load_const_u32(1, 65500);
+ pOp->load_const_u32(2, 500);
+ pOp->load_const_u64(3, 65600);
+ pOp->load_const_u64(4, 95600);
+
+ switch(nCalls) {
+ case 1:
+ pOp->sub_reg(1, 2, 5);
+ pOp->load_const_u32(7, 65000);
+ break;
+ case 2:
+ pOp->sub_reg(1, 3, 5);
+ pOp->load_const_u64(7, (Uint64)-100);
+ break;
+ case 3:
+ pOp->sub_reg(3, 1, 5);
+ pOp->load_const_u64(7, (Uint64)100);
+ break;
+ case 4:
+ pOp->sub_reg(3, 4, 5);
+ pOp->load_const_u64(7, (Uint64)-30000);
+ break;
+ case 5:
+ pOp->sub_reg(1, 6, 5);
+ pOp->load_const_u64(7, 0);
+ ret_val = FAIL ;
+ break;
+ case 6:
+ pOp->sub_reg(6, 1, 5);
+ pOp->load_const_u64(7, 0);
+ ret_val = FAIL ;
+ break;
+ case 7:
+ pOp->sub_reg(1, 8, 5);
+ pOp->load_const_u64(7, 0);
+ ret_val = FAIL ;
+ break;
+ case 8:
+ pOp->sub_reg(8, 1, 5);
+ pOp->load_const_u64(7, 0);
+ ret_val = FAIL ;
+ break;
+ case 9:
+ pOp->sub_reg(1, 2, 8);
+ pOp->load_const_u32(7, (Uint32)65000);
+ ret_val = FAIL;
+ break;
+ default:
+ //ndbout << "t_subReg: default case (no test case)" << endl ;
+ //return ret_val = NO_FAIL ;
+ break ;
+ }
+ pOp->branch_eq(5, 7, 0);
+ pOp->interpret_exit_nok() ;
+ pOp->def_label(0);
+ pOp->interpret_exit_ok() ;
+
+ return ret_val;
+}
+
+TTYPE t_subroutineWithBranchLabel(int nCalls, NdbOperation* pOp) {
+
+ TTYPE ret_val = NO_FAIL ;
+ ndbout << "Defining subroutineWithBranchLabel test:" << nCalls << endl;
+
+ pOp->load_const_u32(1, 65500);
+ pOp->load_const_u32(2, 500);
+ pOp->load_const_u64(3, 65600);
+ pOp->load_const_u64(4, 95600);
+ pOp->load_const_u32(7, 65000);
+ pOp->call_sub(0) ;
+
+ switch(nCalls) {
+ case 1:
+ pOp->def_subroutine(0) ;
+ pOp->add_reg(1, 2, 5);
+ break;
+ case 2:
+ pOp->def_subroutine(0) ;
+ pOp->add_reg(1, 3, 5);
+ break;
+ case 3:
+ pOp->def_subroutine(0) ;
+ pOp->add_reg(3, 1, 5);
+ break;
+ case 4:
+ pOp->def_subroutine(0) ;
+ pOp->add_reg(3, 4, 5);
+ break;
+ case 5:
+ pOp->def_subroutine(0) ;
+ pOp->add_reg(1, 6, 5);
+ break;
+ case 6:
+ pOp->def_subroutine(0) ;
+ pOp->add_reg(6, 1, 5);
+ break;
+ case 7: // illegal register
+ pOp->def_subroutine(0) ;
+ pOp->add_reg(1, 8, 5);
+ ret_val = FAIL ;
+ break;
+ case 8: // another illegal register
+ pOp->def_subroutine(0) ;
+ pOp->add_reg(8, 1, 5);
+ ret_val = FAIL ;
+ break;
+ case 9: // and another one
+ pOp->def_subroutine(0) ;
+ pOp->add_reg(1, 2, 8);
+ ret_val = FAIL ;
+ break;
+ case 10: // test subroutine nesting
+ for(int sub = 0; sub < 25 ; ++sub){
+ pOp->call_sub(sub) ;
+ pOp->def_subroutine(sub + 1) ;
+ pOp->interpret_exit_ok() ;
+ pOp->ret_sub() ;
+ }
+ ret_val = FAIL ;
+ default:
+ break ;
+ }
+
+ pOp->branch_label(0) ;
+ pOp->interpret_exit_nok() ;
+ pOp->def_label(0) ;
+ pOp->interpret_exit_ok() ;
+ pOp->ret_sub() ;
+
+ return ret_val ;
+}
+
+
+void scan_rows(Ndb* pMyNdb, int opType, int tupleType, int scanType) {
+
+ int check = -1 ;
+ int loop_count_ops = nRecords ;
+ int eOf = -1 ;
+ int readValue = 0 ;
+ int readValue2 = 0 ;
+ int scanCount = 0 ;
+ TTYPE fail = NO_FAIL ;
+
+ for (int count=0 ; count < loop_count_ops ; count++) {
+ NdbConnection* MyTransaction = pMyNdb->startTransaction();
+ if (!MyTransaction) error_handler(pMyNdb->getNdbError(), NO_FAIL);
+
+ NdbOperation* MyOperation = MyTransaction->getNdbOperation(tableName);
+ if (!MyOperation) error_handler(pMyNdb->getNdbError(), NO_FAIL);
+
+ if (opType == 1)
+ // Open for scan read, Creates the SCAN_TABREQ and if needed
+ // SCAN_TABINFO signals.
+ check = MyOperation->openScanRead(1);
+ else if (opType == 2)
+ // Open for scan with update of rows.
+ check = MyOperation->openScanExclusive(1);
+
+ // Create ATTRINFO signal(s) for interpreted program used for
+ // defining search criteria and column values that should be returned.
+
+ scanCount = count+1 ;
+
+ switch(tupleType) {
+ case 1:
+ fail = t_exitMethods(scanCount, MyOperation, opType);
+ break;
+ case 2:
+ fail = t_incValue(scanCount, MyOperation);
+ break;
+ case 3:
+ fail = t_subValue(scanCount, MyOperation);
+ break;
+ case 4:
+ fail = t_readAttr(scanCount, MyOperation);
+ break;
+ case 5:
+ fail = t_writeAttr(scanCount, MyOperation);
+ break;
+ case 6:
+ fail = t_loadConst(scanCount, MyOperation, opType);
+ break;
+ case 7:
+ fail = t_branch(scanCount, MyOperation);
+ break;
+ case 8:
+ fail = t_branchIfNull(scanCount, MyOperation);
+ break;
+ case 9:
+ fail = t_addReg(scanCount, MyOperation);
+ break;
+ case 10:
+ fail = t_subReg(scanCount, MyOperation);
+ break;
+ case 11:
+ fail = t_subroutineWithBranchLabel(scanCount, MyOperation);
+ break;
+ default:
+ break ;
+ }
+
+ if(11 != tupleType) MyOperation->getValue(attrName[1], (char*)&readValue);
+
+ // Sends the SCAN_TABREQ, (SCAN_TABINFO) and ATTRINFO signals and then
+ // reads the answer in TRANSID_AI. Confirmation is received through SCAN_TABCONF or
+ // SCAN_TABREF if failure.
+ check = MyTransaction->executeScan();
+ if (check == -1) {
+ //ndbout << endl << "executeScan returned: " << MyTransaction->getNdbError() << endl;
+ error_handler(MyTransaction->getNdbError(), fail) ;
+ pMyNdb->closeTransaction(MyTransaction);
+ }else{
+ // Sends the SCAN_NEXTREQ signal(s) and reads the answer in TRANS_ID signals.
+ // SCAN_TABCONF or SCAN_TABREF is the confirmation.
+ while ((eOf = MyTransaction->nextScanResult()) == 0) {
+ ndbout << readValue <<"; ";
+ // Here we call takeOverScanOp for update of the tuple.
+ }
+ ndbout << endl ;
+
+ pMyNdb->closeTransaction(MyTransaction);
+ if (eOf == -1) {
+ ndbout << endl << "nextScanResult returned: "<< MyTransaction->getNdbError() << endl;
+ } else {
+ ndbout << "OK" << endl;
+ }
+ }
+ }
+ return;
+
+};
+
+
+void update_rows(Ndb* pMyNdb, int tupleType, int opType) {
+ /****************************************************************
+ * Update rows in SimpleTable
+ * Only updates the first 3 cols.
+ ***************************************************************/
+
+ int check = -1 ;
+ int loop_count_ops = nRecords ;
+ int readValue[MAXATTR] = {0} ;
+ TTYPE ret_val = NO_FAIL ;
+ NdbConnection *MyTransaction = NULL ;
+ NdbOperation *MyOperation = NULL ;
+
+ ndbout << "Updating records ..." << endl << endl;
+
+ for (int count=0 ; count < loop_count_ops ; count++){
+
+ MyTransaction = pMyNdb->startTransaction();
+ if (!MyTransaction) {
+ error_handler(pMyNdb->getNdbError(), NO_FAIL);
+ return;
+ }//if
+
+ MyOperation = MyTransaction->getNdbOperation(tableName);
+ if (MyOperation == NULL) {
+ error_handler(pMyNdb->getNdbError(), NO_FAIL);
+ return;
+ }//if
+
+ // if (operationType == 3)
+ check = MyOperation->interpretedUpdateTuple();
+ // else if (operationType == 4)
+ // check = MyOperation->interpretedDirtyUpdate();
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbError(), NO_FAIL);
+
+ check = MyOperation->equal( attrName[0], (char*)&pkValue[count] );
+ if( check == -1 )
+ error_handler(MyTransaction->getNdbError(), NO_FAIL);
+
+ switch(tupleType) {
+ case 1:
+ ret_val = t_exitMethods(count+1, MyOperation, opType);
+ break;
+ case 2:
+ ret_val = t_incValue(count+1, MyOperation);
+ break;
+ case 3:
+ ret_val = t_subValue(count+1, MyOperation);
+ break;
+ case 4:
+ ret_val = t_readAttr(count+1, MyOperation);
+ break;
+ case 5:
+ ret_val = t_writeAttr(count+1, MyOperation);
+ break;
+ case 6:
+ ret_val = t_loadConst(count+1, MyOperation, opType);
+ break;
+ case 7:
+ ret_val = t_branch(count+1, MyOperation);
+ break;
+ case 8:
+ ret_val = t_branchIfNull(count+1, MyOperation);
+ break;
+ case 9:
+ ret_val = t_addReg(count+1, MyOperation);
+ break;
+ case 10:
+ ret_val = t_subReg(count+1, MyOperation);
+ break;
+ case 11:
+ ret_val = t_subroutineWithBranchLabel(count+1, MyOperation);
+ break;
+ default:
+ break ;
+ }
+
+ MyOperation->getValue("COL1", (char*)&readValue);
+
+ if (MyTransaction->execute( Commit ) == -1 ) {
+ error_handler(MyTransaction->getNdbError(), ret_val);
+ }else if (NO_FAIL == ret_val ) {
+ ndbout << "OK" << endl;
+ } else {
+ bTestPassed = -1;
+ ndbout << "Test passed when expected to fail" << endl;
+ }//if
+ pMyNdb->closeTransaction(MyTransaction);
+
+ }
+
+ ndbout << "Finished updating records" << endl;
+ return;
+};
+
+void delete_rows(Ndb* pMyNdb, int tupleTest, int opType) {
+
+ /****************************************************************
+ * Delete rows from SimpleTable
+ *
+ ***************************************************************/
+
+ int check = 1 ;
+ int loop_count_ops = nRecords ;
+ int readValue[MAXATTR] = {0};
+ NdbConnection *MyTransaction = NULL ;
+ NdbOperation *MyOperation = NULL ;
+ TTYPE ret_val = NO_FAIL ;
+
+ ndbout << "Deleting records ..."<< endl << endl;
+
+ for (int count=0 ; count < loop_count_ops ; count++) {
+
+ MyTransaction = pMyNdb->startTransaction();
+ if (!MyTransaction) error_handler(pMyNdb->getNdbError(), NO_FAIL) ;
+
+ MyOperation = MyTransaction->getNdbOperation(tableName);
+ if (!MyOperation) error_handler(pMyNdb->getNdbError(), NO_FAIL) ;
+
+ check = MyOperation->interpretedDeleteTuple();
+ if( check == -1 ) error_handler(MyTransaction->getNdbError(), NO_FAIL) ;
+
+ check = MyOperation->equal( attrName[0], (char*)&pkValue[count] );
+ if( check == -1 ) error_handler(MyTransaction->getNdbError(), NO_FAIL) ;
+
+ switch(tupleTest) {
+ case 1:
+ ret_val = t_exitMethods(count+1, MyOperation, opType);
+ break;
+ case 2:
+ ret_val = t_incValue(count+1, MyOperation);
+ break;
+ case 3:
+ ret_val = t_subValue(count+1, MyOperation);
+ break;
+ case 4:
+ ret_val = t_readAttr(count+1, MyOperation);
+ break;
+ case 5:
+ ret_val = t_writeAttr(count+1, MyOperation);
+ break;
+ case 6:
+ ret_val = t_loadConst(count+1, MyOperation, opType);
+ break;
+ case 7:
+ ret_val = t_branch(count+1, MyOperation);
+ break;
+ case 8:
+ ret_val = t_branchIfNull(count+1, MyOperation);
+ break;
+ case 9:
+ ret_val = t_addReg(count+1, MyOperation);
+ break;
+ case 10:
+ ret_val = t_subReg(count+1, MyOperation);
+ break;
+ case 11:
+ ret_val = t_subroutineWithBranchLabel(count+1, MyOperation);
+ break;
+ default:
+ break ;
+ }
+
+ if(11 != tupleTest)MyOperation->getValue(attrName[1], (char*)&readValue) ;
+
+ if (MyTransaction->execute( Commit ) == -1 ) {
+ error_handler(MyTransaction->getNdbError(), ret_val);
+ } else if (NO_FAIL == ret_val /*|| UNDEF == ret_val*/ ) {
+ ndbout << "OK" << endl;
+ } else {
+ bTestPassed = -1;
+ ndbout << "Test passed when expected to fail" << endl;
+ }//if
+ ndbout << endl;
+ pMyNdb->closeTransaction(MyTransaction);
+ }
+
+ ndbout << "Finished deleting records" << endl;
+ return;
+
+};
+
+
+inline void setAttrNames(){
+ for (int i = 0; i < MAXATTR; i++){
+ BaseString::snprintf(attrName[i], MAXSTRLEN, "COL%d", i);
+ }
+}
+
+
+inline void setTableNames(){
+ BaseString::snprintf(tableName, MAXSTRLEN, "TAB1");
+}
+
diff --git a/storage/ndb/test/ndbapi/mainAsyncGenerator.cpp b/storage/ndb/test/ndbapi/mainAsyncGenerator.cpp
new file mode 100644
index 00000000000..73a8b98ab57
--- /dev/null
+++ b/storage/ndb/test/ndbapi/mainAsyncGenerator.cpp
@@ -0,0 +1,391 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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>
+#include <NdbSleep.h>
+#include <NdbThread.h>
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbEnv.h>
+#include <NdbTest.hpp>
+
+#include "userInterface.h"
+#include "dbGenerator.h"
+
+static int numProcesses;
+static int numSeconds;
+static int numWarmSeconds;
+static int parallellism;
+static int millisSendPoll;
+static int minEventSendPoll;
+static int forceSendPoll;
+
+static ThreadData *data;
+
+static void usage(const char *prog)
+{
+ const char *progname;
+
+ /*--------------------------------------------*/
+ /* Get the name of the program (without path) */
+ /*--------------------------------------------*/
+ progname = strrchr(prog, '/');
+
+ if (progname == 0)
+ progname = prog;
+ else
+ ++progname;
+
+ ndbout_c(
+ "Usage: %s [-proc <num>] [-warm <num>] [-time <num>] [ -p <num>] "
+ "[-t <num> ] [ -e <num> ] [ -f <num>] \n"
+ " -proc <num> Specifies that <num> is the number of\n"
+ " threads. The default is 1.\n"
+ " -time <num> Specifies that the test will run for <num> sec.\n"
+ " The default is 10 sec\n"
+ " -warm <num> Specifies the warm-up/cooldown period of <num> "
+ "sec.\n"
+ " The default is 10 sec\n"
+ " -p <num> The no of parallell transactions started by "
+ "one thread\n"
+ " -e <num> Minimum no of events before wake up in call to "
+ "sendPoll\n"
+ " Default is 1\n"
+ " -f <num> force parameter to sendPoll\n"
+ " Default is 0\n",
+ progname);
+}
+
+static
+int
+parse_args(int argc, const char **argv)
+{
+ int i;
+
+ numProcesses = 1;
+ numSeconds = 10;
+ numWarmSeconds = 10;
+ parallellism = 1;
+ millisSendPoll = 10000;
+ minEventSendPoll = 1;
+ forceSendPoll = 0;
+
+
+ i = 1;
+ while (i < argc){
+ if (strcmp("-proc",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ return 1;
+ }
+ if (sscanf(argv[i+1], "%d", &numProcesses) == -1 ||
+ numProcesses <= 0 || numProcesses > 127) {
+ ndbout_c("-proc flag requires a positive integer argument [1..127]");
+ return 1;
+ }
+ i += 2;
+ } else if (strcmp("-p", argv[i]) == 0){
+ if(i + 1 >= argc){
+ usage(argv[0]);
+ return 1;
+ }
+ if (sscanf(argv[i+1], "%d", &parallellism) == -1 ||
+ parallellism <= 0){
+ ndbout_c("-p flag requires a positive integer argument");
+ return 1;
+ }
+ i += 2;
+ }
+ else if (strcmp("-time",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ return 1;
+ }
+ if (sscanf(argv[i+1], "%d", &numSeconds) == -1 ||
+ numSeconds < 0) {
+ ndbout_c("-time flag requires a positive integer argument");
+ return 1;
+ }
+ i += 2;
+ }
+ else if (strcmp("-warm",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ return 1;
+ }
+ if (sscanf(argv[i+1], "%d", &numWarmSeconds) == -1 ||
+ numWarmSeconds < 0) {
+ ndbout_c("-warm flag requires a positive integer argument");
+ return 1;
+ }
+ i += 2;
+ }
+ else if (strcmp("-e",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ return 1;
+ }
+ if (sscanf(argv[i+1], "%d", &minEventSendPoll) == -1 ||
+ minEventSendPoll < 0) {
+ ndbout_c("-e flag requires a positive integer argument");
+ return 1;
+ }
+ i += 2;
+ }
+ else if (strcmp("-f",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ usage(argv[0]);
+ return 1;
+ }
+ if (sscanf(argv[i+1], "%d", &forceSendPoll) == -1 ||
+ forceSendPoll < 0) {
+ ndbout_c("-f flag requires a positive integer argument");
+ return 1;
+ }
+ i += 2;
+ }
+ else {
+ return 1;
+ }
+ }
+
+ if(minEventSendPoll > parallellism){
+ ndbout_c("minEventSendPoll(%d) > parallellism(%d)",
+ minEventSendPoll, parallellism);
+ ndbout_c("not very good...");
+ ndbout_c("very bad...");
+ ndbout_c("exiting...");
+ return 1;
+ }
+ return 0;
+}
+
+static
+void
+print_transaction(const char *header,
+ unsigned long totalCount,
+ TransactionDefinition *trans,
+ unsigned int printBranch,
+ unsigned int printRollback)
+{
+ double f;
+
+ ndbout_c(" %s: %d (%.2f%%) "
+ "Latency(ms) avg: %d min: %d max: %d std: %d n: %d",
+ header,
+ trans->count,
+ (double)trans->count / (double)totalCount * 100.0,
+ (int)trans->latency.getMean(),
+ (int)trans->latency.getMin(),
+ (int)trans->latency.getMax(),
+ (int)trans->latency.getStddev(),
+ (int)trans->latency.getCount()
+ );
+
+ if( printBranch ){
+ if( trans->count == 0 )
+ f = 0.0;
+ else
+ f = (double)trans->branchExecuted / (double)trans->count * 100.0;
+ ndbout_c(" Branches Executed: %d (%.2f%%)", trans->branchExecuted, f);
+ }
+
+ if( printRollback ){
+ if( trans->count == 0 )
+ f = 0.0;
+ else
+ f = (double)trans->rollbackExecuted / (double)trans->count * 100.0;
+ ndbout_c(" Rollback Executed: %d (%.2f%%)",trans->rollbackExecuted,f);
+ }
+}
+
+void
+print_stats(const char *title,
+ unsigned int length,
+ unsigned int transactionFlag,
+ GeneratorStatistics *gen,
+ int numProc, int parallellism)
+{
+ int i;
+ char buf[10];
+ char name[MAXHOSTNAMELEN];
+
+ name[0] = 0;
+ NdbHost_GetHostName(name);
+
+ ndbout_c("\n------ %s ------",title);
+ ndbout_c("Length : %d %s",
+ length,
+ transactionFlag ? "Transactions" : "sec");
+ ndbout_c("Processor : %s", name);
+ ndbout_c("Number of Proc: %d",numProc);
+ ndbout_c("Parallellism : %d", parallellism);
+ ndbout_c("\n");
+
+ if( gen->totalTransactions == 0 ) {
+ ndbout_c(" No Transactions for this test");
+ }
+ else {
+ for(i = 0; i < 5; i++) {
+ sprintf(buf, "T%d",i+1);
+ print_transaction(buf,
+ gen->totalTransactions,
+ &gen->transactions[i],
+ i >= 2,
+ i >= 3 );
+ }
+
+ ndbout_c("\n");
+ ndbout_c(" Overall Statistics:");
+ ndbout_c(" Transactions: %d", gen->totalTransactions);
+ ndbout_c(" Outer : %.0f TPS",gen->outerTps);
+ ndbout_c("\n");
+ }
+}
+
+static
+void *
+threadRoutine(void *arg)
+{
+ int i;
+ ThreadData *data = (ThreadData *)arg;
+ Ndb * pNDB;
+
+ pNDB = asyncDbConnect(parallellism);
+ /* NdbSleep_MilliSleep(rand() % 10); */
+
+ for(i = 0; i<parallellism; i++){
+ data[i].pNDB = pNDB;
+ }
+ millisSendPoll = 30000;
+ asyncGenerator(data, parallellism,
+ millisSendPoll, minEventSendPoll, forceSendPoll);
+
+ asyncDbDisconnect(pNDB);
+
+ return NULL;
+}
+
+NDB_COMMAND(DbAsyncGenerator, "DbAsyncGenerator",
+ "DbAsyncGenerator", "DbAsyncGenerator", 65535)
+{
+ ndb_init();
+ int i;
+ int j;
+ int k;
+ struct NdbThread* pThread = NULL;
+ GeneratorStatistics stats;
+ GeneratorStatistics *p;
+ char threadName[32];
+ int rc = NDBT_OK;
+ void* tmp = NULL;
+ if(parse_args(argc,argv) != 0){
+ usage(argv[0]);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+
+ ndbout_c("\nStarting Test with %d process(es) for %d %s parallellism %d",
+ numProcesses,
+ numSeconds,
+ "sec",
+ parallellism);
+
+ ndbout_c(" WarmUp/coolDown = %d sec", numWarmSeconds);
+
+ data = (ThreadData*)malloc((numProcesses*parallellism)*sizeof(ThreadData));
+
+ for(i = 0; i < numProcesses; i++) {
+ for(j = 0; j<parallellism; j++){
+ data[i*parallellism+j].warmUpSeconds = numWarmSeconds;
+ data[i*parallellism+j].testSeconds = numSeconds;
+ data[i*parallellism+j].coolDownSeconds = numWarmSeconds;
+ data[i*parallellism+j].randomSeed =
+ NdbTick_CurrentMillisecond()+i+j;
+ data[i*parallellism+j].changedTime = 0;
+ data[i*parallellism+j].runState = Runnable;
+ }
+ sprintf(threadName, "AsyncThread[%d]", i);
+ pThread = NdbThread_Create(threadRoutine,
+ (void**)&data[i*parallellism],
+ 65535,
+ threadName,
+ NDB_THREAD_PRIO_LOW);
+ if(pThread != 0 && pThread != NULL){
+ (&data[i*parallellism])->pThread = pThread;
+ } else {
+ perror("Failed to create thread");
+ rc = NDBT_FAILED;
+ }
+ }
+
+ showTime();
+
+ /*--------------------------------*/
+ /* Wait for all processes to exit */
+ /*--------------------------------*/
+ for(i = 0; i < numProcesses; i++) {
+ NdbThread_WaitFor(data[i*parallellism].pThread, &tmp);
+ NdbThread_Destroy(&data[i*parallellism].pThread);
+ }
+
+ ndbout_c("All threads have finished");
+
+ /*-------------------------------------------*/
+ /* Clear all structures for total statistics */
+ /*-------------------------------------------*/
+ stats.totalTransactions = 0;
+ stats.outerTps = 0.0;
+
+ for(i = 0; i < NUM_TRANSACTION_TYPES; i++ ) {
+ stats.transactions[i].count = 0;
+ stats.transactions[i].branchExecuted = 0;
+ stats.transactions[i].rollbackExecuted = 0;
+ stats.transactions[i].latency.reset();
+ }
+
+ /*--------------------------------*/
+ /* Add the values for all Threads */
+ /*--------------------------------*/
+ for(i = 0; i < numProcesses; i++) {
+ for(k = 0; k<parallellism; k++){
+ p = &data[i*parallellism+k].generator;
+
+ stats.totalTransactions += p->totalTransactions;
+ stats.outerTps += p->outerTps;
+
+ for(j = 0; j < NUM_TRANSACTION_TYPES; j++ ) {
+ stats.transactions[j].count +=
+ p->transactions[j].count;
+ stats.transactions[j].branchExecuted +=
+ p->transactions[j].branchExecuted;
+ stats.transactions[j].rollbackExecuted +=
+ p->transactions[j].rollbackExecuted;
+ stats.transactions[j].latency +=
+ p->transactions[j].latency;
+ }
+ }
+ }
+
+ print_stats("Test Results",
+ numSeconds,
+ 0,
+ &stats,
+ numProcesses,
+ parallellism);
+
+ free(data);
+
+ NDBT_ProgramExit(rc);
+}
diff --git a/storage/ndb/test/ndbapi/msa.cpp b/storage/ndb/test/ndbapi/msa.cpp
new file mode 100644
index 00000000000..e39f7a8c64a
--- /dev/null
+++ b/storage/ndb/test/ndbapi/msa.cpp
@@ -0,0 +1,1206 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <NdbApi.hpp>
+#include <NdbSchemaCon.hpp>
+#include <NdbCondition.h>
+#include <NdbMutex.h>
+#include <NdbSleep.h>
+#include <NdbThread.h>
+#include <NdbTick.h>
+
+const char* const c_szDatabaseName = "TEST_DB";
+
+const char* const c_szTableNameStored = "CCStored";
+const char* const c_szTableNameTemp = "CCTemp";
+
+const char* const c_szContextId = "ContextId";
+const char* const c_szVersion = "Version";
+const char* const c_szLockFlag = "LockFlag";
+const char* const c_szLockTime = "LockTime";
+const char* const c_szLockTimeUSec = "LockTimeUSec";
+const char* const c_szContextData = "ContextData";
+
+const char* g_szTableName = c_szTableNameStored;
+
+
+#ifdef NDB_WIN32
+HANDLE hShutdownEvent = 0;
+#else
+#include <signal.h>
+bool bShutdownEvent = false;
+#endif
+long g_nMaxContextIdPerThread = 5000;
+long g_nNumThreads = 0;
+long g_nMaxCallsPerSecond = 0;
+long g_nMaxRetry = 50;
+bool g_bWriteTuple = false;
+bool g_bInsertInitial = false;
+bool g_bVerifyInitial = false;
+
+NdbMutex* g_pNdbMutexPrintf = 0;
+NdbMutex* g_pNdbMutexIncrement = 0;
+long g_nNumCallsProcessed = 0;
+NDB_TICKS g_tStartTime = 0;
+NDB_TICKS g_tEndTime = 0;
+
+long g_nNumberOfInitialInsert = 0;
+long g_nNumberOfInitialVerify = 0;
+
+const long c_nMaxMillisecForAllCall = 5000;
+long* g_plCountMillisecForCall = 0;
+const long c_nMaxMillisecForAllTrans = 5000;
+long* g_plCountMillisecForTrans = 0;
+bool g_bReport = false;
+bool g_bReportPlus = false;
+
+
+// data for CALL_CONTEXT and GROUP_RESOURCE
+static char STATUS_DATA[]=
+"000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+"101112131415161718191A1B1C1D1E1F000102030405060708090A0B0C0D0E0F"
+"202122232425262728292A2B2C2D2E2F000102030405060708090A0B0C0D0E0F"
+"303132333435363738393A3B3C3D3E3F000102030405060708090A0B0C0D0E0F"
+"404142434445464748494A4B4C4D4E4F000102030405060708090A0B0C0D0E0F"
+"505152535455565758595A5B5C5D5E5F000102030405060708090A0B0C0D0E0F"
+"606162636465666768696A6B6C6D6E6F000102030405060708090A0B0C0D0E0F"
+"707172737475767778797A7B7C7D7E7F000102030405060708090A0B0C0D0E0F"
+"808182838485868788898A8B8C8D8E8F000102030405060708090A0B0C0D0E0F"
+"909192939495969798999A9B9C9D9E9F000102030405060708090A0B0C0D0E0F"
+"10010110210310410510610710810910A000102030405060708090A0B0C0D0EF"
+"10B10C10D10E10F110111112113114115000102030405060708090A0B0C0D0EF"
+"11611711811911A11B11C11D11E11F120000102030405060708090A0B0C0D0EF"
+"12112212312412512612712812912A12B000102030405060708090A0B0C0D0EF"
+"12C12D12E12F130131132134135136137000102030405060708090A0B0C0D0EF"
+"13813913A13B13C13D13E13F140141142000102030405060708090A0B0C0D0EF"
+"14314414514614714814914A14B14C14D000102030405060708090A0B0C0D0EF"
+"14E14F150151152153154155156157158000102030405060708090A0B0C0D0EF"
+"15915A15B15C15D15E15F160161162163000102030405060708090A0B0C0D0EF"
+"16416516616716816916A16B16C16D16E000102030405060708090A0B0C0D0EF"
+"16F170171172173174175176177178179000102030405060708090A0B0C0D0EF"
+"17A17B17C17D17E17F180181182183184000102030405060708090A0B0C0D0EF"
+"18518618718818918A18B18C18D18E18F000102030405060708090A0B0C0D0EF"
+"19019119219319419519619719819919A000102030405060708090A0B0C0D0EF"
+"19B19C19D19E19F200201202203204205000102030405060708090A0B0C0D0EF"
+"20620720820920A20B20C20D20F210211000102030405060708090A0B0C0D0EF"
+"21221321421521621721821921A21B21C000102030405060708090A0B0C0D0EF"
+"21D21E21F220221222223224225226227000102030405060708090A0B0C0D0EF"
+"22822922A22B22C22D22E22F230231232000102030405060708090A0B0C0D0EF"
+"23323423523623723823923A23B23C23D000102030405060708090A0B0C0D0EF"
+"23E23F240241242243244245246247248000102030405060708090A0B0C0D0EF"
+"24924A24B24C24D24E24F250251252253000102030405060708090A0B0C0D0EF"
+"101112131415161718191A1B1C1D1E1F000102030405060708090A0B0C0D0E0F"
+"202122232425262728292A2B2C2D2E2F000102030405060708090A0B0C0D0E0F"
+"303132333435363738393A3B3C3D3E3F000102030405060708090A0B0C0D0E0F"
+"404142434445464748494A4B4C4D4E4F000102030405060708090A0B0C0D0E0F"
+"505152535455565758595A5B5C5D5E5F000102030405060708090A0B0C0D0E0F"
+"606162636465666768696A6B6C6D6E6F000102030405060708090A0B0C0D0E0F"
+"707172737475767778797A7B7C7D7E7F000102030405060708090A0B0C0D0E0F"
+"808182838485868788898A8B8C8D8E8F000102030405060708090A0B0C0D0E0F"
+"909192939495969798999A9B9C9D9E9F000102030405060708090A0B0C0D0E0F"
+"10010110210310410510610710810910A000102030405060708090A0B0C0D0EF"
+"10B10C10D10E10F110111112113114115000102030405060708090A0B0C0D0EF"
+"11611711811911A11B11C11D11E11F120000102030405060708090A0B0C0D0EF"
+"12112212312412512612712812912A12B000102030405060708090A0B0C0D0EF"
+"12C12D12E12F130131132134135136137000102030405060708090A0B0C0D0EF"
+"13813913A13B13C13D13E13F140141142000102030405060708090A0B0C0D0EF"
+"14314414514614714814914A14B14C14D000102030405060708090A0B0C0D0EF"
+"14E14F150151152153154155156157158000102030405060708090A0B0C0D0EF"
+"15915A15B15C15D15E15F160161162163000102030405060708090A0B0C0D0EF"
+"16416516616716816916A16B16C16D16E000102030405060708090A0B0C0D0EF"
+"16F170171172173174175176177178179000102030405060708090A0B0C0D0EF"
+"17A17B17C17D17E17F180181182183184000102030405060708090A0B0C0D0EF"
+"18518618718818918A18B18C18D18E18F000102030405060708090A0B0C0D0EF"
+"19019119219319419519619719819919A000102030405060708090A0B0C0D0EF"
+"19B19C19D19E19F200201202203204205000102030405060708090A0B0C0D0EF"
+"20620720820920A20B20C20D20F210211000102030405060708090A0B0C0D0EF"
+"21221321421521621721821921A21B21C000102030405060708090A0B0C0D0EF"
+"21D21E21F220221222223224225226227000102030405060708090A0B0C0D0EF"
+"22822922A22B22C22D22E22F230231232000102030405060708090A0B0C0D0EF"
+"23323423523623723823923A23B23C23D000102030405060708090A0B0C0D0EF"
+"2366890FE1438751097E7F6325DC0E6326F"
+"25425525625725825925A25B25C25D25E25F000102030405060708090A0B0C0F";
+
+long g_nStatusDataSize = sizeof(STATUS_DATA);
+
+
+// Thread function for Call Context Inserts
+
+
+#ifdef NDB_WIN32
+
+BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
+{
+ if(CTRL_C_EVENT == dwCtrlType)
+ {
+ SetEvent(hShutdownEvent);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#else
+
+void CtrlCHandler(int)
+{
+ bShutdownEvent = true;
+}
+
+#endif
+
+
+
+void ReportNdbError(const char* szMsg, const NdbError& err)
+{
+ NdbMutex_Lock(g_pNdbMutexPrintf);
+ printf("%s: %d: %s\n", szMsg, err.code, (err.message ? err.message : ""));
+ NdbMutex_Unlock(g_pNdbMutexPrintf);
+}
+
+
+void
+ReportCallsPerSecond(long nNumCallsProcessed,
+ NDB_TICKS tStartTime,
+ NDB_TICKS tEndTime)
+{
+ NDB_TICKS tElapsed = tEndTime - tStartTime;
+ long lCallsPerSec;
+ if(tElapsed>0)
+ lCallsPerSec = (long)((1000*nNumCallsProcessed)/tElapsed);
+ else
+ lCallsPerSec = 0;
+
+ NdbMutex_Lock(g_pNdbMutexPrintf);
+ printf("Time Taken for %ld Calls is %ld msec (= %ld calls/sec)\n",
+ nNumCallsProcessed, (long)tElapsed, lCallsPerSec);
+ NdbMutex_Unlock(g_pNdbMutexPrintf);
+}
+
+
+#ifndef NDB_WIN32
+void InterlockedIncrement(long* lp) // expensive
+{
+ NdbMutex_Lock(g_pNdbMutexIncrement);
+ (*lp)++;
+ NdbMutex_Unlock(g_pNdbMutexIncrement);
+}
+#endif
+
+
+void InterlockedIncrementAndReport(void)
+{
+ NdbMutex_Lock(g_pNdbMutexIncrement);
+ ++g_nNumCallsProcessed;
+ if((g_nNumCallsProcessed%1000)==0)
+ {
+ g_tEndTime = NdbTick_CurrentMillisecond();
+ if(g_tStartTime)
+ ReportCallsPerSecond(1000, g_tStartTime, g_tEndTime);
+
+ g_tStartTime = g_tEndTime;
+ }
+ NdbMutex_Unlock(g_pNdbMutexIncrement);
+}
+
+
+void SleepOneCall(void)
+{
+ int iMillisecToSleep;
+ if(g_nMaxCallsPerSecond>0)
+ iMillisecToSleep = (1000*g_nNumThreads)/g_nMaxCallsPerSecond;
+ else
+ iMillisecToSleep = 50;
+
+ if(iMillisecToSleep>0)
+ NdbSleep_MilliSleep(iMillisecToSleep);
+
+}
+
+
+
+int QueryTransaction(Ndb* pNdb,
+ long iContextId,
+ long* piVersion,
+ long* piLockFlag,
+ long* piLockTime,
+ long* piLockTimeUSec,
+ char* pchContextData,
+ NdbError& err)
+{
+ int iRes = -1;
+ NdbConnection* pNdbConnection = pNdb->startTransaction(0, (const char*)&iContextId, 4);
+ if(pNdbConnection)
+ {
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbOperation(g_szTableName);
+ if(pNdbOperation)
+ {
+ NdbRecAttr* pNdbRecAttrVersion;
+ NdbRecAttr* pNdbRecAttrLockFlag;
+ NdbRecAttr* pNdbRecAttrLockTime;
+ NdbRecAttr* pNdbRecAttrLockTimeUSec;
+ NdbRecAttr* pNdbRecAttrContextData;
+ if(!pNdbOperation->readTuple()
+ && !pNdbOperation->equal(c_szContextId, (Int32)iContextId)
+ && (pNdbRecAttrVersion=pNdbOperation->getValue(c_szVersion, (char*)piVersion))
+ && (pNdbRecAttrLockFlag=pNdbOperation->getValue(c_szLockFlag, (char*)piLockFlag))
+ && (pNdbRecAttrLockTime=pNdbOperation->getValue(c_szLockTime, (char*)piLockTime))
+ && (pNdbRecAttrLockTimeUSec=pNdbOperation->getValue(c_szLockTimeUSec, (char*)piLockTimeUSec))
+ && (pNdbRecAttrContextData=pNdbOperation->getValue(c_szContextData, pchContextData)))
+ {
+ if(!pNdbConnection->execute(Commit))
+ iRes = 0;
+ else
+ err = pNdbConnection->getNdbError();
+ }
+ else
+ err = pNdbOperation->getNdbError();
+ }
+ else
+ err = pNdbConnection->getNdbError();
+
+ pNdb->closeTransaction(pNdbConnection);
+ }
+ else
+ err = pNdb->getNdbError();
+
+ return iRes;
+}
+
+
+int RetryQueryTransaction(Ndb* pNdb,
+ long iContextId,
+ long* piVersion,
+ long* piLockFlag,
+ long* piLockTime,
+ long* piLockTimeUSec,
+ char* pchContextData,
+ NdbError& err,
+ int& nRetry)
+{
+ int iRes = -1;
+ nRetry = 0;
+ bool bRetry = true;
+ while(bRetry && nRetry<g_nMaxRetry)
+ {
+ if(!QueryTransaction(pNdb, iContextId, piVersion, piLockFlag,
+ piLockTime, piLockTimeUSec, pchContextData, err))
+ {
+ iRes = 0;
+ bRetry = false;
+ }
+ else
+ {
+ switch(err.status)
+ {
+ case NdbError::TemporaryError:
+ case NdbError::UnknownResult:
+ SleepOneCall();
+ ++nRetry;
+ break;
+
+ case NdbError::PermanentError:
+ default:
+ bRetry = false;
+ break;
+ }
+ }
+ }
+ return iRes;
+}
+
+
+int DeleteTransaction(Ndb* pNdb, long iContextId, NdbError& err)
+{
+ int iRes = -1;
+ NdbConnection* pNdbConnection = pNdb->startTransaction(0, (const char*)&iContextId, 4);
+ if(pNdbConnection)
+ {
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbOperation(g_szTableName);
+ if(pNdbOperation)
+ {
+ if(!pNdbOperation->deleteTuple()
+ && !pNdbOperation->equal(c_szContextId, (Int32)iContextId))
+ {
+ if(pNdbConnection->execute(Commit) == 0)
+ iRes = 0;
+ else
+ err = pNdbConnection->getNdbError();
+ }
+ else
+ err = pNdbOperation->getNdbError();
+ }
+ else
+ err = pNdbConnection->getNdbError();
+
+ pNdb->closeTransaction(pNdbConnection);
+ }
+ else
+ err = pNdb->getNdbError();
+
+ return iRes;
+}
+
+
+
+int RetryDeleteTransaction(Ndb* pNdb, long iContextId, NdbError& err, int& nRetry)
+{
+ int iRes = -1;
+ nRetry = 0;
+ bool bRetry = true;
+ bool bUnknown = false;
+ while(bRetry && nRetry<g_nMaxRetry)
+ {
+ if(!DeleteTransaction(pNdb, iContextId, err))
+ {
+ iRes = 0;
+ bRetry = false;
+ }
+ else
+ {
+ switch(err.status)
+ {
+ case NdbError::UnknownResult:
+ bUnknown = true;
+ ++nRetry;
+ break;
+
+ case NdbError::TemporaryError:
+ bUnknown = false;
+ SleepOneCall();
+ ++nRetry;
+ break;
+
+ case NdbError::PermanentError:
+ if(err.code==626 && bUnknown)
+ iRes = 0;
+ bRetry = false;
+ break;
+
+ default:
+ bRetry = false;
+ break;
+ }
+ }
+ }
+ return iRes;
+}
+
+
+
+int InsertTransaction(Ndb* pNdb,
+ long iContextID,
+ long iVersion,
+ long iLockFlag,
+ long iLockTime,
+ long iLockTimeUSec,
+ const char* pchContextData,
+ NdbError& err)
+{
+ int iRes = -1;
+ NdbConnection* pNdbConnection = pNdb->startTransaction(0, (const char*)&iContextID, 4);
+ if(pNdbConnection)
+ {
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbOperation(g_szTableName);
+ if(pNdbOperation)
+ {
+ if(!(g_bWriteTuple ? pNdbOperation->writeTuple() : pNdbOperation->insertTuple())
+ && !pNdbOperation->equal(c_szContextId, (Int32)iContextID)
+ && !pNdbOperation->setValue(c_szVersion, (Int32)iVersion)
+ && !pNdbOperation->setValue(c_szLockFlag, (Int32)iLockFlag)
+ && !pNdbOperation->setValue(c_szLockTime, (Int32)iLockTime)
+ && !pNdbOperation->setValue(c_szLockTimeUSec, (Int32)iLockTimeUSec)
+ && !pNdbOperation->setValue(c_szContextData, pchContextData, g_nStatusDataSize))
+ {
+ if(!pNdbConnection->execute(Commit))
+ iRes = 0;
+ else
+ err = pNdbConnection->getNdbError();
+ }
+ else
+ err = pNdbOperation->getNdbError();
+ }
+ else
+ err = pNdbConnection->getNdbError();
+
+ pNdb->closeTransaction(pNdbConnection);
+ }
+ else
+ err = pNdb->getNdbError();
+
+ return iRes;
+}
+
+
+
+int RetryInsertTransaction(Ndb* pNdb,
+ long iContextId,
+ long iVersion,
+ long iLockFlag,
+ long iLockTime,
+ long iLockTimeUSec,
+ const char* pchContextData,
+ NdbError& err, int& nRetry)
+{
+ int iRes = -1;
+ nRetry = 0;
+ bool bRetry = true;
+ bool bUnknown = false;
+ while(bRetry && nRetry<g_nMaxRetry)
+ {
+ if(!InsertTransaction(pNdb, iContextId, iVersion, iLockFlag,
+ iLockTime, iLockTimeUSec, pchContextData, err))
+ {
+ iRes = 0;
+ bRetry = false;
+ }
+ else
+ {
+ switch(err.status)
+ {
+ case NdbError::UnknownResult:
+ bUnknown = true;
+ ++nRetry;
+ break;
+
+ case NdbError::TemporaryError:
+ bUnknown = false;
+ SleepOneCall();
+ ++nRetry;
+ break;
+
+ case NdbError::PermanentError:
+ if(err.code==630 && bUnknown)
+ iRes = 0;
+ bRetry = false;
+ break;
+
+ default:
+ bRetry = false;
+ break;
+ }
+ }
+ }
+ return iRes;
+}
+
+
+int UpdateTransaction(Ndb* pNdb, long iContextId, NdbError& err)
+{
+ int iRes = -1;
+ NdbConnection* pNdbConnection = pNdb->startTransaction(0, (const char*)&iContextId, 4);
+ if(pNdbConnection)
+ {
+ NdbOperation* pNdbOperation = pNdbConnection->getNdbOperation(g_szTableName);
+ if(pNdbOperation)
+ {
+ if(!pNdbOperation->updateTuple()
+ && !pNdbOperation->equal(c_szContextId, (Int32)iContextId)
+ && !pNdbOperation->setValue(c_szContextData, STATUS_DATA, g_nStatusDataSize))
+ {
+ if(!pNdbConnection->execute(Commit))
+ iRes = 0;
+ else
+ err = pNdbConnection->getNdbError();
+ }
+ else
+ err = pNdbOperation->getNdbError();
+ }
+ else
+ err = pNdbConnection->getNdbError();
+
+ pNdb->closeTransaction(pNdbConnection);
+ }
+ else
+ err = pNdb->getNdbError();
+
+ return iRes;
+}
+
+
+int RetryUpdateTransaction(Ndb* pNdb, long iContextId, NdbError& err, int& nRetry)
+{
+ int iRes = -1;
+ nRetry = 0;
+ bool bRetry = true;
+ while(bRetry && nRetry<g_nMaxRetry)
+ {
+ if(!UpdateTransaction(pNdb, iContextId, err))
+ {
+ iRes = 0;
+ bRetry = false;
+ }
+ else
+ {
+ switch(err.status)
+ {
+ case NdbError::TemporaryError:
+ case NdbError::UnknownResult:
+ SleepOneCall();
+ ++nRetry;
+ break;
+
+ case NdbError::PermanentError:
+ default:
+ bRetry = false;
+ break;
+ }
+ }
+ }
+ return iRes;
+}
+
+
+
+int InsertInitialRecords(Ndb* pNdb, long nInsert, long nSeed)
+{
+ int iRes = -1;
+ char szMsg[100];
+ for(long i=0; i<nInsert; ++i)
+ {
+ int iContextID = i+nSeed;
+ int nRetry = 0;
+ NdbError err;
+ memset(&err, 0, sizeof(err));
+ NDB_TICKS tStartTrans = NdbTick_CurrentMillisecond();
+ iRes = RetryInsertTransaction(pNdb, iContextID, nSeed, iContextID,
+ (long)(tStartTrans/1000), (long)((tStartTrans%1000)*1000),
+ STATUS_DATA, err, nRetry);
+ NDB_TICKS tEndTrans = NdbTick_CurrentMillisecond();
+ long lMillisecForThisTrans = (long)(tEndTrans-tStartTrans);
+ if(nRetry>0)
+ {
+ sprintf(szMsg, "insert retried %d times, time %ld msec.",
+ nRetry, lMillisecForThisTrans);
+ ReportNdbError(szMsg, err);
+ }
+ if(iRes)
+ {
+ ReportNdbError("Insert initial record failed", err);
+ return iRes;
+ }
+ InterlockedIncrement(&g_nNumberOfInitialInsert);
+ }
+ return iRes;
+}
+
+
+
+int VerifyInitialRecords(Ndb* pNdb, long nVerify, long nSeed)
+{
+ int iRes = -1;
+ char* pchContextData = new char[g_nStatusDataSize];
+ char szMsg[100];
+ long iPrevLockTime = -1;
+ long iPrevLockTimeUSec = -1;
+ for(long i=0; i<nVerify; ++i)
+ {
+ int iContextID = i+nSeed;
+ long iVersion = 0;
+ long iLockFlag = 0;
+ long iLockTime = 0;
+ long iLockTimeUSec = 0;
+ int nRetry = 0;
+ NdbError err;
+ memset(&err, 0, sizeof(err));
+ NDB_TICKS tStartTrans = NdbTick_CurrentMillisecond();
+ iRes = RetryQueryTransaction(pNdb, iContextID, &iVersion, &iLockFlag,
+ &iLockTime, &iLockTimeUSec, pchContextData, err, nRetry);
+ NDB_TICKS tEndTrans = NdbTick_CurrentMillisecond();
+ long lMillisecForThisTrans = (long)(tEndTrans-tStartTrans);
+ if(nRetry>0)
+ {
+ sprintf(szMsg, "verify retried %d times, time %ld msec.",
+ nRetry, lMillisecForThisTrans);
+ ReportNdbError(szMsg, err);
+ }
+ if(iRes)
+ {
+ ReportNdbError("Read initial record failed", err);
+ delete[] pchContextData;
+ return iRes;
+ }
+ if(memcmp(pchContextData, STATUS_DATA, g_nStatusDataSize))
+ {
+ sprintf(szMsg, "wrong context data in tuple %d", iContextID);
+ ReportNdbError(szMsg, err);
+ delete[] pchContextData;
+ return -1;
+ }
+ if(iVersion!=nSeed
+ || iLockFlag!=iContextID
+ || iLockTime<iPrevLockTime
+ || (iLockTime==iPrevLockTime && iLockTimeUSec<iPrevLockTimeUSec))
+ {
+ sprintf(szMsg, "wrong call data in tuple %d", iContextID);
+ ReportNdbError(szMsg, err);
+ delete[] pchContextData;
+ return -1;
+ }
+ iPrevLockTime = iLockTime;
+ iPrevLockTimeUSec = iLockTimeUSec;
+ InterlockedIncrement(&g_nNumberOfInitialVerify);
+ }
+ delete[] pchContextData;
+ return iRes;
+}
+
+
+
+
+
+void* RuntimeCallContext(void* lpParam)
+{
+ long nNumCallsProcessed = 0;
+ int nStartingRecordID = *(int*)lpParam;
+
+ Ndb* pNdb;
+ char* pchContextData = new char[g_nStatusDataSize];
+ char szMsg[100];
+
+ int iRes;
+ const char* szOp;
+ long iVersion;
+ long iLockFlag;
+ long iLockTime;
+ long iLockTimeUSec;
+
+ pNdb = new Ndb("TEST_DB");
+ if(!pNdb)
+ {
+ NdbMutex_Lock(g_pNdbMutexPrintf);
+ printf("new Ndb failed\n");
+ NdbMutex_Unlock(g_pNdbMutexPrintf);
+ delete[] pchContextData;
+ return 0;
+ }
+
+ if(pNdb->init(1) || pNdb->waitUntilReady())
+ {
+ ReportNdbError("init of Ndb failed", pNdb->getNdbError());
+ delete pNdb;
+ delete[] pchContextData;
+ return 0;
+ }
+
+ if(g_bInsertInitial)
+ {
+ if(InsertInitialRecords(pNdb, g_nMaxContextIdPerThread, -nStartingRecordID-g_nMaxContextIdPerThread))
+ {
+ delete pNdb;
+ delete[] pchContextData;
+ return 0;
+ }
+ }
+
+ if(g_bVerifyInitial)
+ {
+ NdbError err;
+ memset(&err, 0, sizeof(err));
+ if(VerifyInitialRecords(pNdb, g_nMaxContextIdPerThread, -nStartingRecordID-g_nMaxContextIdPerThread))
+ {
+ delete pNdb;
+ delete[] pchContextData;
+ return 0;
+ }
+ }
+ if(g_bInsertInitial || g_bVerifyInitial)
+ {
+ delete[] pchContextData;
+ return 0;
+ }
+
+ long nContextID = nStartingRecordID;
+#ifdef NDB_WIN32
+ while(WaitForSingleObject(hShutdownEvent,0) != WAIT_OBJECT_0)
+#else
+ while(!bShutdownEvent)
+#endif
+ {
+ ++nContextID;
+ nContextID %= g_nMaxContextIdPerThread;
+ nContextID += nStartingRecordID;
+
+ bool bTimeLatency = (nContextID==100);
+
+ NDB_TICKS tStartCall = NdbTick_CurrentMillisecond();
+ for (int i=0; i < 20; i++)
+ {
+ int nRetry = 0;
+ NdbError err;
+ memset(&err, 0, sizeof(err));
+ NDB_TICKS tStartTrans = NdbTick_CurrentMillisecond();
+ switch(i)
+ {
+ case 3:
+ case 6:
+ case 9:
+ case 11:
+ case 12:
+ case 15:
+ case 18: // Query Record
+ szOp = "Read";
+ iRes = RetryQueryTransaction(pNdb, nContextID, &iVersion, &iLockFlag,
+ &iLockTime, &iLockTimeUSec, pchContextData, err, nRetry);
+ break;
+
+ case 19: // Delete Record
+ szOp = "Delete";
+ iRes = RetryDeleteTransaction(pNdb, nContextID, err, nRetry);
+ break;
+
+ case 0: // Insert Record
+ szOp = "Insert";
+ iRes = RetryInsertTransaction(pNdb, nContextID, 1, 1, 1, 1, STATUS_DATA, err, nRetry);
+ break;
+
+ default: // Update Record
+ szOp = "Update";
+ iRes = RetryUpdateTransaction(pNdb, nContextID, err, nRetry);
+ break;
+ }
+ NDB_TICKS tEndTrans = NdbTick_CurrentMillisecond();
+ long lMillisecForThisTrans = (long)(tEndTrans-tStartTrans);
+
+ if(g_bReport)
+ {
+ assert(lMillisecForThisTrans>=0 && lMillisecForThisTrans<c_nMaxMillisecForAllTrans);
+ InterlockedIncrement(g_plCountMillisecForTrans+lMillisecForThisTrans);
+ }
+
+ if(nRetry>0)
+ {
+ sprintf(szMsg, "%s retried %d times, time %ld msec.",
+ szOp, nRetry, lMillisecForThisTrans);
+ ReportNdbError(szMsg, err);
+ }
+ else if(bTimeLatency)
+ {
+ NdbMutex_Lock(g_pNdbMutexPrintf);
+ printf("%s = %ld msec.\n", szOp, lMillisecForThisTrans);
+ NdbMutex_Unlock(g_pNdbMutexPrintf);
+ }
+
+ if(iRes)
+ {
+ sprintf(szMsg, "%s failed after %ld calls, terminating thread",
+ szOp, nNumCallsProcessed);
+ ReportNdbError(szMsg, err);
+ delete pNdb;
+ delete[] pchContextData;
+ return 0;
+ }
+ }
+ NDB_TICKS tEndCall = NdbTick_CurrentMillisecond();
+ long lMillisecForThisCall = (long)(tEndCall-tStartCall);
+
+ if(g_bReport)
+ {
+ assert(lMillisecForThisCall>=0 && lMillisecForThisCall<c_nMaxMillisecForAllCall);
+ InterlockedIncrement(g_plCountMillisecForCall+lMillisecForThisCall);
+ }
+
+ if(bTimeLatency)
+ {
+ NdbMutex_Lock(g_pNdbMutexPrintf);
+ printf("Total time for call is %ld msec.\n", (long)lMillisecForThisCall);
+ NdbMutex_Unlock(g_pNdbMutexPrintf);
+ }
+
+ nNumCallsProcessed++;
+ InterlockedIncrementAndReport();
+ if(g_nMaxCallsPerSecond>0)
+ {
+ int iMillisecToSleep = (1000*g_nNumThreads)/g_nMaxCallsPerSecond;
+ iMillisecToSleep -= lMillisecForThisCall;
+ if(iMillisecToSleep>0)
+ {
+ NdbSleep_MilliSleep(iMillisecToSleep);
+ }
+ }
+ }
+
+ NdbMutex_Lock(g_pNdbMutexPrintf);
+ printf("Terminating thread after %ld calls\n", nNumCallsProcessed);
+ NdbMutex_Unlock(g_pNdbMutexPrintf);
+
+ delete pNdb;
+ delete[] pchContextData;
+ return 0;
+}
+
+
+int CreateCallContextTable(Ndb* pNdb, const char* szTableName, bool bStored)
+{
+ int iRes = -1;
+ NdbError err;
+ memset(&err, 0, sizeof(err));
+
+ NdbSchemaCon* pNdbSchemaCon = NdbSchemaCon::startSchemaTrans(pNdb);
+ if(pNdbSchemaCon)
+ {
+ NdbSchemaOp* pNdbSchemaOp = pNdbSchemaCon->getNdbSchemaOp();
+ if(pNdbSchemaOp)
+ {
+ if(!pNdbSchemaOp->createTable(szTableName, 8, TupleKey, 2,
+ All, 6, 78, 80, 1, bStored)
+ && !pNdbSchemaOp->createAttribute(c_szContextId, TupleKey, 32, 1, Signed)
+ && !pNdbSchemaOp->createAttribute(c_szVersion, NoKey, 32, 1, Signed)
+ && !pNdbSchemaOp->createAttribute(c_szLockFlag, NoKey, 32, 1, Signed)
+ && !pNdbSchemaOp->createAttribute(c_szLockTime, NoKey, 32, 1, Signed)
+ && !pNdbSchemaOp->createAttribute(c_szLockTimeUSec, NoKey, 32, 1, Signed)
+ && !pNdbSchemaOp->createAttribute(c_szContextData, NoKey, 8, g_nStatusDataSize, String))
+ {
+ if(!pNdbSchemaCon->execute())
+ iRes = 0;
+ else
+ err = pNdbSchemaCon->getNdbError();
+ }
+ else
+ err = pNdbSchemaOp->getNdbError();
+ }
+ else
+ err = pNdbSchemaCon->getNdbError();
+
+ NdbSchemaCon::closeSchemaTrans(pNdbSchemaCon);
+ }
+ else
+ err = pNdb->getNdbError();
+
+ if(iRes)
+ {
+ ReportNdbError("create call context table failed", err);
+ }
+ return iRes;
+}
+
+
+
+void ReportResponseTimeStatistics(const char* szStat, long* plCount, const long lSize)
+{
+ long lCount = 0;
+ Int64 llSum = 0;
+ Int64 llSum2 = 0;
+ long lMin = -1;
+ long lMax = -1;
+
+ for(long l=0; l<lSize; ++l)
+ {
+ if(plCount[l]>0)
+ {
+ lCount += plCount[l];
+ llSum += (Int64)l*(Int64)plCount[l];
+ llSum2 += (Int64)l*(Int64)l*(Int64)plCount[l];
+ if(lMin==-1 || l<lMin)
+ {
+ lMin = l;
+ }
+ if(lMax==-1 || l>lMax)
+ {
+ lMax = l;
+ }
+ }
+ }
+
+ long lAvg = long(llSum/lCount);
+ double dblVar = ((double)lCount*(double)llSum2 - (double)llSum*(double)llSum)/((double)lCount*(double)(lCount-1));
+ long lStd = long(sqrt(dblVar));
+
+ long lMed = -1;
+ long l95 = -1;
+ long lSel = -1;
+ for(long l=lMin; l<=lMax; ++l)
+ {
+ if(plCount[l]>0)
+ {
+ lSel += plCount[l];
+ if(lMed==-1 && lSel>=(lCount/2))
+ {
+ lMed = l;
+ }
+ if(l95==-1 && lSel>=((lCount*95)/100))
+ {
+ l95 = l;
+ }
+ if(g_bReportPlus)
+ {
+ printf("%ld\t%ld\n", l, plCount[l]);
+ }
+ }
+ }
+
+ printf("%s: Count=%ld, Min=%ld, Max=%ld, Avg=%ld, Std=%ld, Med=%ld, 95%%=%ld\n",
+ szStat, lCount, lMin, lMax, lAvg, lStd, lMed, l95);
+}
+
+
+
+void ShowHelp(const char* szCmd)
+{
+ printf("%s -t<threads> [-s<seed>] [-b<batch>] [-c<maxcps>] [-m<size>] [-d] [-i] [-v] [-f] [-w] [-r[+]]\n", szCmd);
+ printf("%s -?\n", szCmd);
+ puts("-d\t\tcreate the table");
+ puts("-i\t\tinsert initial records");
+ puts("-v\t\tverify initial records");
+ puts("-t<threads>\tnumber of threads making calls");
+ puts("-s<seed>\toffset for primary key");
+ puts("-b<batch>\tbatch size per thread");
+ puts("-c<maxcps>\tmax number of calls per second for this process");
+ puts("-m<size>\tsize of context data");
+ puts("-f\t\tno checkpointing and no logging");
+ puts("-w\t\tuse writeTuple instead of insertTuple");
+ puts("-r\t\treport response time statistics");
+ puts("-r+\t\treport response time distribution");
+ puts("-?\t\thelp");
+}
+
+
+int main(int argc, char* argv[])
+{
+ ndb_init();
+ int iRes = -1;
+ g_nNumThreads = 0;
+ g_nMaxCallsPerSecond = 0;
+ long nSeed = 0;
+ bool bStoredTable = true;
+ bool bCreateTable = false;
+ g_bWriteTuple = false;
+ g_bReport = false;
+ g_bReportPlus = false;
+
+ for(int i=1; i<argc; ++i)
+ {
+ if(argv[i][0]=='-' || argv[i][0]=='/')
+ {
+ switch(argv[i][1])
+ {
+ case 't':
+ g_nNumThreads = atol(argv[i]+2);
+ break;
+ case 's':
+ nSeed = atol(argv[i]+2);
+ break;
+ case 'b':
+ g_nMaxContextIdPerThread = atol(argv[i]+2);
+ break;
+ case 'm':
+ g_nStatusDataSize = atol(argv[i]+2);
+ if(g_nStatusDataSize>sizeof(STATUS_DATA))
+ {
+ g_nStatusDataSize = sizeof(STATUS_DATA);
+ }
+ break;
+ case 'i':
+ g_bInsertInitial = true;
+ break;
+ case 'v':
+ g_bVerifyInitial = true;
+ break;
+ case 'd':
+ bCreateTable = true;
+ break;
+ case 'f':
+ bStoredTable = false;
+ break;
+ case 'w':
+ g_bWriteTuple = true;
+ break;
+ case 'r':
+ g_bReport = true;
+ if(argv[i][2]=='+')
+ {
+ g_bReportPlus = true;
+ }
+ break;
+ case 'c':
+ g_nMaxCallsPerSecond = atol(argv[i]+2);
+ break;
+ case '?':
+ default:
+ ShowHelp(argv[0]);
+ return -1;
+ }
+ }
+ else
+ {
+ ShowHelp(argv[0]);
+ return -1;
+ }
+ }
+ if(bCreateTable)
+ puts("-d\tcreate the table");
+ if(g_bInsertInitial)
+ printf("-i\tinsert initial records\n");
+ if(g_bVerifyInitial)
+ printf("-v\tverify initial records\n");
+ if(g_nNumThreads>0)
+ printf("-t%ld\tnumber of threads making calls\n", g_nNumThreads);
+ if(g_nNumThreads>0)
+ {
+ printf("-s%ld\toffset for primary key\n", nSeed);
+ printf("-b%ld\tbatch size per thread\n", g_nMaxContextIdPerThread);
+ }
+ if(g_nMaxCallsPerSecond>0)
+ printf("-c%ld\tmax number of calls per second for this process\n", g_nMaxCallsPerSecond);
+ if(!bStoredTable)
+ puts("-f\tno checkpointing and no logging to disk");
+ if(g_bWriteTuple)
+ puts("-w\tuse writeTuple instead of insertTuple");
+ if(g_bReport)
+ puts("-r\treport response time statistics");
+ if(g_bReportPlus)
+ puts("-r+\treport response time distribution");
+
+ if(!bCreateTable && g_nNumThreads<=0)
+ {
+ ShowHelp(argv[0]);
+ return -1;
+ }
+ printf("-m%ld\tsize of context data\n", g_nStatusDataSize);
+
+ g_szTableName = (bStoredTable ? c_szTableNameStored : c_szTableNameTemp);
+
+#ifdef NDB_WIN32
+ SetConsoleCtrlHandler(ConsoleCtrlHandler, true);
+#else
+ signal(SIGINT, CtrlCHandler);
+#endif
+
+ if(g_bReport)
+ {
+ g_plCountMillisecForCall = new long[c_nMaxMillisecForAllCall];
+ memset(g_plCountMillisecForCall, 0, c_nMaxMillisecForAllCall*sizeof(long));
+ g_plCountMillisecForTrans = new long[c_nMaxMillisecForAllTrans];
+ memset(g_plCountMillisecForTrans, 0, c_nMaxMillisecForAllTrans*sizeof(long));
+ }
+
+ g_pNdbMutexIncrement = NdbMutex_Create();
+ g_pNdbMutexPrintf = NdbMutex_Create();
+#ifdef NDB_WIN32
+ hShutdownEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+#endif
+
+ Ndb* pNdb = new Ndb(c_szDatabaseName);
+ if(!pNdb)
+ {
+ printf("could not construct ndb\n");
+ return 1;
+ }
+
+ if(pNdb->init(1) || pNdb->waitUntilReady())
+ {
+ ReportNdbError("could not initialize ndb\n", pNdb->getNdbError());
+ delete pNdb;
+ return 2;
+ }
+
+ if(bCreateTable)
+ {
+ printf("Create CallContext table\n");
+ if (bStoredTable)
+ {
+ if (CreateCallContextTable(pNdb, c_szTableNameStored, true))
+ {
+ printf("Create table failed\n");
+ delete pNdb;
+ return 3;
+ }
+ }
+ else
+ {
+ if (CreateCallContextTable(pNdb, c_szTableNameTemp, false))
+ {
+ printf("Create table failed\n");
+ delete pNdb;
+ return 3;
+ }
+ }
+ }
+
+ if(g_nNumThreads>0)
+ {
+ printf("creating %d threads\n", (int)g_nNumThreads);
+ if(g_bInsertInitial)
+ {
+ printf("each thread will insert %ld initial records, total %ld inserts\n",
+ g_nMaxContextIdPerThread, g_nNumThreads*g_nMaxContextIdPerThread);
+ }
+ if(g_bVerifyInitial)
+ {
+ printf("each thread will verify %ld initial records, total %ld reads\n",
+ g_nMaxContextIdPerThread, g_nNumThreads*g_nMaxContextIdPerThread);
+ }
+
+ g_nNumberOfInitialInsert = 0;
+ g_nNumberOfInitialVerify = 0;
+
+ NDB_TICKS tStartTime = NdbTick_CurrentMillisecond();
+ NdbThread* pThreads[256];
+ int pnStartingRecordNum[256];
+ int ij;
+ for(ij=0;ij<g_nNumThreads;ij++)
+ {
+ pnStartingRecordNum[ij] = (ij*g_nMaxContextIdPerThread) + nSeed;
+ }
+
+ for(ij=0;ij<g_nNumThreads;ij++)
+ {
+ pThreads[ij] = NdbThread_Create(RuntimeCallContext,
+ (void**)(pnStartingRecordNum+ij),
+ 0, "RuntimeCallContext", NDB_THREAD_PRIO_LOW);
+ }
+
+ //Wait for the threads to finish
+ for(ij=0;ij<g_nNumThreads;ij++)
+ {
+ void* status;
+ NdbThread_WaitFor(pThreads[ij], &status);
+ }
+ NDB_TICKS tEndTime = NdbTick_CurrentMillisecond();
+
+ //Print time taken
+ printf("Time Taken for %ld Calls is %ld msec (= %ld calls/sec)\n",
+ g_nNumCallsProcessed,
+ (long)(tEndTime-tStartTime),
+ (long)((1000*g_nNumCallsProcessed)/(tEndTime-tStartTime)));
+
+ if(g_bInsertInitial)
+ printf("successfully inserted %ld tuples\n", g_nNumberOfInitialInsert);
+ if(g_bVerifyInitial)
+ printf("successfully verified %ld tuples\n", g_nNumberOfInitialVerify);
+ }
+
+ delete pNdb;
+
+#ifdef NDB_WIN32
+ CloseHandle(hShutdownEvent);
+#endif
+ NdbMutex_Destroy(g_pNdbMutexIncrement);
+ NdbMutex_Destroy(g_pNdbMutexPrintf);
+
+ if(g_bReport)
+ {
+ ReportResponseTimeStatistics("Calls", g_plCountMillisecForCall, c_nMaxMillisecForAllCall);
+ ReportResponseTimeStatistics("Transactions", g_plCountMillisecForTrans, c_nMaxMillisecForAllTrans);
+
+ delete[] g_plCountMillisecForCall;
+ delete[] g_plCountMillisecForTrans;
+ }
+
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/ndb_async1.cpp b/storage/ndb/test/ndbapi/ndb_async1.cpp
new file mode 100644
index 00000000000..2a84f6b2aca
--- /dev/null
+++ b/storage/ndb/test/ndbapi/ndb_async1.cpp
@@ -0,0 +1,647 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+#include "userInterface.h"
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <NdbApi.hpp>
+
+inline
+NdbConnection *
+startTransaction(Ndb * pNDB,
+ ServerId inServerId,
+ const SubscriberNumber inNumber){
+
+ const int keyDataLenBytes = sizeof(ServerId)+SUBSCRIBER_NUMBER_LENGTH;
+ const int keyDataLen_64Words = keyDataLenBytes >> 3;
+
+ Uint64 keyDataBuf[keyDataLen_64Words+1]; // The "+1" is for rounding...
+
+ char * keyDataBuf_charP = (char *)&keyDataBuf[0];
+ Uint32 * keyDataBuf_wo32P = (Uint32 *)&keyDataBuf[0];
+
+ // Server Id comes first
+ keyDataBuf_wo32P[0] = inServerId;
+ // Then subscriber number
+ memcpy(&keyDataBuf_charP[sizeof(ServerId)], inNumber,
+ SUBSCRIBER_NUMBER_LENGTH);
+
+ return pNDB->startTransaction(0, keyDataBuf_charP, keyDataLenBytes);
+}
+
+void T1_Callback(int result, NdbConnection * pCon, void * threadData);
+void T2_Callback(int result, NdbConnection * pCon, void * threadData);
+void T3_Callback_1(int result, NdbConnection * pCon, void * threadData);
+void T3_Callback_2(int result, NdbConnection * pCon, void * threadData);
+void T3_Callback_3(int result, NdbConnection * pCon, void * threadData);
+void T4_Callback_1(int result, NdbConnection * pCon, void * threadData);
+void T4_Callback_2(int result, NdbConnection * pCon, void * threadData);
+void T4_Callback_3(int result, NdbConnection * pCon, void * threadData);
+void T5_Callback_1(int result, NdbConnection * pCon, void * threadData);
+void T5_Callback_2(int result, NdbConnection * pCon, void * threadData);
+void T5_Callback_3(int result, NdbConnection * pCon, void * threadData);
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+void
+start_T1(Ndb * pNDB, ThreadData * td){
+
+ DEBUG2("T1(%.*s): - Starting\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number);
+
+ int check;
+ NdbConnection * pCON = pNDB->startTransaction();
+ if (pCON != NULL) {
+ NdbOperation *MyOp = pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ if (MyOp != NULL) {
+ MyOp->updateTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ pCON->executeAsynchPrepare( Commit , T1_Callback, td);
+ } else {
+ CHECK_NULL(MyOp, "T1: getNdbOperation", pCON);
+ }//if
+ } else {
+ error_handler("T1-1: startTranscation",
+ pNDB->getNdbErrorString(),
+ pNDB->getNdbError());
+ }//if
+}
+
+void
+T1_Callback(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+
+ DEBUG2("T1(%.*s): - Completing\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number);
+
+ CHECK_MINUS_ONE(result, "T1: Commit",
+ pCON);
+ td->pNDB->closeTransaction(pCON);
+ complete_T1(td);
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+void
+start_T2(Ndb * pNDB, ThreadData * td){
+
+ DEBUG3("T2(%.*s, %p): - Starting\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.location);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * pCON = pNDB->startTransaction();
+ if (pCON == NULL)
+ error_handler("T2-1: startTransaction",
+ pNDB->getNdbErrorString(),
+ pNDB->getNdbError());
+
+ NdbOperation *MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T2: getNdbOperation",
+ pCON);
+
+ MyOp->readTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_NAME,
+ td->transactionData.name);
+ pCON->executeAsynchPrepare( Commit, T2_Callback, td );
+}
+
+void
+T2_Callback(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ DEBUG3("T2(%.*s, %p): - Completing\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.location);
+
+ CHECK_MINUS_ONE(result, "T2: Commit", pCON);
+ td->pNDB->closeTransaction(pCON);
+ complete_T2(td);
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+void
+start_T3(Ndb * pNDB, ThreadData * td){
+
+ DEBUG3("T3(%.*s, %.2d): - Starting\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * pCON = startTransaction(pNDB,
+ td->transactionData.server_id,
+ td->transactionData.number);
+ if (pCON == NULL)
+ error_handler("T3-1: startTranscation",
+ pNDB->getNdbErrorString(),
+ pNDB->getNdbError());
+
+ NdbOperation *MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T3-1: getNdbOperation",
+ pCON);
+
+ MyOp->readTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&td->transactionData.group_id);
+ MyOp->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&td->transactionData.sessions);
+ pCON->executeAsynchPrepare( NoCommit , T3_Callback_1, td);
+}
+
+void
+T3_Callback_1(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ DEBUG3("T3(%.*s, %.2d): - Callback 1\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ CHECK_MINUS_ONE(result, "T3-1: NoCommit", pCON);
+
+ NdbOperation * MyOp = pCON->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOp, "T3-2: getNdbOperation",
+ pCON);
+
+ MyOp->readTuple();
+ MyOp->equal(IND_GROUP_ID,
+ (char*)&td->transactionData.group_id);
+ MyOp->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&td->transactionData.permission);
+ pCON->executeAsynchPrepare( NoCommit, T3_Callback_2, td );
+}
+
+void
+T3_Callback_2(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+
+ CHECK_MINUS_ONE(result, "T3-2: NoCommit", pCON);
+
+ Uint32 permission = td->transactionData.permission;
+ Uint32 sessions = td->transactionData.sessions;
+ Uint32 server_bit = td->transactionData.server_bit;
+
+ if(((permission & server_bit) == server_bit) &&
+ ((sessions & server_bit) == server_bit)){
+
+ memcpy(td->transactionData.suffix,
+ &td->transactionData.number
+ [SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH],
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+ DEBUG5("T3(%.*s, %.2d): - Callback 2 - reading(%.*s)\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ td->transactionData.suffix);
+
+ /* Operation 3 */
+ NdbOperation * MyOp = pCON->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOp, "T3-3: getNdbOperation",
+ pCON);
+
+ MyOp->simpleRead();
+ MyOp->equal(IND_SESSION_SUBSCRIBER,
+ (char*)td->transactionData.number);
+ MyOp->equal(IND_SESSION_SERVER,
+ (char*)&td->transactionData.server_id);
+ MyOp->getValue(IND_SESSION_DATA,
+ (char *)td->transactionData.session_details);
+
+ /* Operation 4 */
+ MyOp = pCON->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOp, "T3-4: getNdbOperation",
+ pCON);
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SERVER_ID,
+ (char*)&td->transactionData.server_id);
+ MyOp->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)td->transactionData.suffix);
+ MyOp->incValue(IND_SERVER_READS, (uint32)1);
+ td->transactionData.branchExecuted = 1;
+ } else {
+ DEBUG3("T3(%.*s, %.2d): - Callback 2 - no read\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+ td->transactionData.branchExecuted = 0;
+ }
+ pCON->executeAsynchPrepare( Commit, T3_Callback_3, td );
+}
+
+void
+T3_Callback_3(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ DEBUG3("T3(%.*s, %.2d): - Completing\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ CHECK_MINUS_ONE(result, "T3-3: Commit", pCON);
+
+ td->pNDB->closeTransaction(pCON);
+ complete_T3(td);
+}
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+void
+start_T4(Ndb * pNDB, ThreadData * td){
+
+ DEBUG3("T4(%.*s, %.2d): - Starting\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * pCON = startTransaction(pNDB,
+ td->transactionData.server_id,
+ td->transactionData.number);
+ if (pCON == NULL)
+ error_handler("T4-1: startTranscation",
+ pNDB->getNdbErrorString(),
+ pNDB->getNdbError());
+
+ NdbOperation *MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T4-1: getNdbOperation",
+ pCON);
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&td->transactionData.group_id);
+ MyOp->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&td->transactionData.sessions);
+ MyOp->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)td->transactionData.server_bit);
+ pCON->executeAsynchPrepare( NoCommit , T4_Callback_1, td);
+}
+
+void
+T4_Callback_1(int result, NdbConnection * pCON, void * threadData){
+ CHECK_MINUS_ONE(result, "T4-1: NoCommit", pCON);
+ ThreadData * td = (ThreadData *)threadData;
+
+ DEBUG3("T4(%.*s, %.2d): - Callback 1\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+
+ NdbOperation * MyOp = pCON->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOp, "T4-2: getNdbOperation",
+ pCON);
+
+ MyOp->readTuple();
+ MyOp->equal(IND_GROUP_ID,
+ (char*)&td->transactionData.group_id);
+ MyOp->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&td->transactionData.permission);
+ pCON->executeAsynchPrepare( NoCommit , T4_Callback_2, td);
+}
+
+void
+T4_Callback_2(int result, NdbConnection * pCON, void * threadData){
+ CHECK_MINUS_ONE(result, "T4-2: NoCommit", pCON);
+ ThreadData * td = (ThreadData *)threadData;
+
+ Uint32 permission = td->transactionData.permission;
+ Uint32 sessions = td->transactionData.sessions;
+ Uint32 server_bit = td->transactionData.server_bit;
+
+ if(((permission & server_bit) == server_bit) &&
+ ((sessions & server_bit) == 0)){
+
+ memcpy(td->transactionData.suffix,
+ &td->transactionData.number
+ [SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH],
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+
+ DEBUG5("T4(%.*s, %.2d): - Callback 2 - inserting(%.*s)\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ td->transactionData.suffix);
+
+ /* Operation 3 */
+
+ NdbOperation * MyOp = pCON->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOp, "T4-3: getNdbOperation",
+ pCON);
+
+ MyOp->insertTuple();
+ MyOp->equal(IND_SESSION_SUBSCRIBER,
+ (char*)td->transactionData.number);
+ MyOp->equal(IND_SESSION_SERVER,
+ (char*)&td->transactionData.server_id);
+ MyOp->setValue(SESSION_DATA,
+ (char *)td->transactionData.session_details);
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOp = pCON->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOp, "T4-5: getNdbOperation",
+ pCON);
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SERVER_ID,
+ (char*)&td->transactionData.server_id);
+ MyOp->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)td->transactionData.suffix);
+ MyOp->incValue(IND_SERVER_INSERTS, (uint32)1);
+ td->transactionData.branchExecuted = 1;
+ } else {
+ td->transactionData.branchExecuted = 0;
+ DEBUG5("T4(%.*s, %.2d): - Callback 2 - %s %s\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ ((permission & server_bit) ?
+ "permission - " : "no permission - "),
+ ((sessions & server_bit) ?
+ "in session - " : "no in session - "));
+ }
+
+ if(!td->transactionData.do_rollback && td->transactionData.branchExecuted){
+ pCON->executeAsynchPrepare(Commit, T4_Callback_3, td);
+ } else {
+ pCON->executeAsynchPrepare(Rollback, T4_Callback_3, td);
+ }
+}
+
+void
+T4_Callback_3(int result, NdbConnection * pCON, void * threadData){
+ CHECK_MINUS_ONE(result, "T4-3: Commit", pCON);
+ ThreadData * td = (ThreadData *)threadData;
+
+ DEBUG3("T4(%.*s, %.2d): - Completing\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ td->pNDB->closeTransaction(pCON);
+ complete_T4(td);
+}
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+void
+start_T5(Ndb * pNDB, ThreadData * td){
+
+ DEBUG3("T5(%.*s, %.2d): - Starting\n", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * pCON = pNDB->startTransaction();
+ if (pCON == NULL)
+ error_handler("T5-1: startTranscation",
+ pNDB->getNdbErrorString(),
+ pNDB->getNdbError());
+
+ NdbOperation * MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T5-1: getNdbOperation",
+ pCON);
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&td->transactionData.group_id);
+ MyOp->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&td->transactionData.sessions);
+ MyOp->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)td->transactionData.server_bit);
+ pCON->executeAsynchPrepare( NoCommit, T5_Callback_1, td );
+}
+
+void
+T5_Callback_1(int result, NdbConnection * pCON, void * threadData){
+ CHECK_MINUS_ONE(result, "T5-1: NoCommit", pCON);
+ ThreadData * td = (ThreadData *)threadData;
+
+ DEBUG3("T5(%.*s, %.2d): - Callback 1\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ NdbOperation * MyOp = pCON->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOp, "T5-2: getNdbOperation",
+ pCON);
+
+ MyOp->readTuple();
+ MyOp->equal(IND_GROUP_ID,
+ (char*)&td->transactionData.group_id);
+ MyOp->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&td->transactionData.permission);
+ pCON->executeAsynchPrepare( NoCommit, T5_Callback_2, td );
+}
+
+void
+T5_Callback_2(int result, NdbConnection * pCON, void * threadData){
+ CHECK_MINUS_ONE(result, "T5-2: NoCommit", pCON);
+ ThreadData * td = (ThreadData *)threadData;
+
+ Uint32 permission = td->transactionData.permission;
+ Uint32 sessions = td->transactionData.sessions;
+ Uint32 server_bit = td->transactionData.server_bit;
+
+ if(((permission & server_bit) == server_bit) &&
+ ((sessions & server_bit) == server_bit)){
+
+ memcpy(td->transactionData.suffix,
+ &td->transactionData.number
+ [SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH],
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+
+ DEBUG5("T5(%.*s, %.2d): - Callback 2 - deleting(%.*s)\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ td->transactionData.suffix);
+
+ /* Operation 3 */
+ NdbOperation * MyOp = pCON->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOp, "T5-3: getNdbOperation",
+ pCON);
+
+ MyOp->deleteTuple();
+ MyOp->equal(IND_SESSION_SUBSCRIBER,
+ (char*)td->transactionData.number);
+ MyOp->equal(IND_SESSION_SERVER,
+ (char*)&td->transactionData.server_id);
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOp = pCON->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOp, "T5-5: getNdbOperation",
+ pCON);
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SERVER_ID,
+ (char*)&td->transactionData.server_id);
+ MyOp->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)td->transactionData.suffix);
+ MyOp->incValue(IND_SERVER_DELETES, (uint32)1);
+ td->transactionData.branchExecuted = 1;
+ } else {
+ td->transactionData.branchExecuted = 0;
+
+ DEBUG5("T5(%.*s, %.2d): - Callback 2 - no delete - %s %s\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ ((permission & server_bit) ?
+ "permission - " : "no permission - "),
+ ((sessions & server_bit) ?
+ "in session - " : "no in session - "));
+ }
+
+ if(!td->transactionData.do_rollback && td->transactionData.branchExecuted){
+ pCON->executeAsynchPrepare(Commit, T5_Callback_3, td);
+ } else {
+ pCON->executeAsynchPrepare(Rollback, T5_Callback_3, td);
+ }
+}
+
+void
+T5_Callback_3(int result, NdbConnection * pCON, void * threadData){
+ CHECK_MINUS_ONE(result, "T5-3: Commit", pCON);
+ ThreadData * td = (ThreadData *)threadData;
+
+ DEBUG3("T5(%.*s, %.2d): - Completing\n",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ td->pNDB->closeTransaction(pCON);
+ complete_T5(td);
+}
diff --git a/storage/ndb/test/ndbapi/ndb_async2.cpp b/storage/ndb/test/ndbapi/ndb_async2.cpp
new file mode 100644
index 00000000000..0c1d138defb
--- /dev/null
+++ b/storage/ndb/test/ndbapi/ndb_async2.cpp
@@ -0,0 +1,754 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+#include <string.h>
+#include "userInterface.h"
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+#include <NdbSleep.h>
+
+#include <NdbApi.hpp>
+
+void T1_Callback(int result, NdbConnection * pCon, void * threadData);
+void T2_Callback(int result, NdbConnection * pCon, void * threadData);
+void T3_Callback_1(int result, NdbConnection * pCon, void * threadData);
+void T3_Callback_2(int result, NdbConnection * pCon, void * threadData);
+void T3_Callback_3(int result, NdbConnection * pCon, void * threadData);
+void T4_Callback_1(int result, NdbConnection * pCon, void * threadData);
+void T4_Callback_2(int result, NdbConnection * pCon, void * threadData);
+void T4_Callback_3(int result, NdbConnection * pCon, void * threadData);
+void T5_Callback_1(int result, NdbConnection * pCon, void * threadData);
+void T5_Callback_2(int result, NdbConnection * pCon, void * threadData);
+void T5_Callback_3(int result, NdbConnection * pCon, void * threadData);
+
+static int stat_async = 0;
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+
+#define SFX_START (SUBSCRIBER_NUMBER_LENGTH - SUBSCRIBER_NUMBER_SUFFIX_LENGTH)
+
+inline
+NdbConnection *
+startTransaction(Ndb * pNDB, ThreadData * td){
+ return pNDB->startTransactionDGroup (0,
+ &td->transactionData.number[SFX_START],
+ 1);
+}
+
+void
+start_T1(Ndb * pNDB, ThreadData * td, int async){
+
+ DEBUG2("T1(%.*s): - Starting", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number);
+
+ NdbConnection * pCON = 0;
+ while((pCON = startTransaction(pNDB, td)) == 0){
+ CHECK_ALLOWED_ERROR("T1: startTransaction", td, pNDB->getNdbError());
+ NdbSleep_MilliSleep(10);
+ }
+
+ NdbOperation *MyOp = pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ if (MyOp != NULL) {
+ MyOp->updateTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ if (async == 1) {
+ pCON->executeAsynchPrepare( Commit , T1_Callback, td);
+ } else {
+ int result = pCON->execute(Commit);
+ T1_Callback(result, pCON, (void*)td);
+ return;
+ }//if
+ } else {
+ CHECK_NULL(MyOp, "T1: getNdbOperation", td, pCON->getNdbError());
+ }//if
+}
+
+void
+T1_Callback(int result, NdbConnection * pCON, void * threadData) {
+ ThreadData * td = (ThreadData *)threadData;
+
+ DEBUG2("T1(%.*s): - Completing", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number);
+
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T1: Commit", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T1(td->pNDB, td, stat_async);
+ return;
+ }//if
+ td->pNDB->closeTransaction(pCON);
+ complete_T1(td);
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+void
+start_T2(Ndb * pNDB, ThreadData * td, int async){
+
+ DEBUG3("T2(%.*s, %d): - Starting", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.location);
+
+ NdbConnection * pCON = 0;
+
+ while((pCON = startTransaction(pNDB, td)) == 0){
+ CHECK_ALLOWED_ERROR("T2-1: startTransaction", td, pNDB->getNdbError());
+ NdbSleep_MilliSleep(10);
+ }
+
+ NdbOperation *MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T2: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->readTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_NAME,
+ td->transactionData.name);
+ if (async == 1) {
+ pCON->executeAsynchPrepare( Commit , T2_Callback, td);
+ } else {
+ int result = pCON->execute(Commit);
+ T2_Callback(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T2_Callback(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ DEBUG3("T2(%.*s, %d): - Completing", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.location);
+
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T2: Commit", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T2(td->pNDB, td, stat_async);
+ return;
+ }//if
+ td->pNDB->closeTransaction(pCON);
+ complete_T2(td);
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+void
+start_T3(Ndb * pNDB, ThreadData * td, int async){
+
+ DEBUG3("T3(%.*s, %.2d): - Starting", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ NdbConnection * pCON = 0;
+
+ while((pCON = startTransaction(pNDB, td)) == 0){
+ CHECK_ALLOWED_ERROR("T3-1: startTransaction", td, pNDB->getNdbError());
+ NdbSleep_MilliSleep(10);
+ }
+
+ NdbOperation *MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T3-1: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->readTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&td->transactionData.group_id);
+ MyOp->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&td->transactionData.sessions);
+ stat_async = async;
+ if (async == 1) {
+ pCON->executeAsynchPrepare( NoCommit , T3_Callback_1, td);
+ } else {
+ int result = pCON->execute( NoCommit );
+ T3_Callback_1(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T3_Callback_1(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ DEBUG3("T3(%.*s, %.2d): - Callback 1", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T3-1: execute", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T3(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ NdbOperation * MyOp = pCON->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOp, "T3-2: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->readTuple();
+ MyOp->equal(IND_GROUP_ID,
+ (char*)&td->transactionData.group_id);
+ MyOp->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&td->transactionData.permission);
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( NoCommit , T3_Callback_2, td);
+ } else {
+ int result = pCON->execute( NoCommit );
+ T3_Callback_2(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T3_Callback_2(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T3-2: execute", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T3(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ Uint32 permission = td->transactionData.permission;
+ Uint32 sessions = td->transactionData.sessions;
+ Uint32 server_bit = td->transactionData.server_bit;
+
+ if(((permission & server_bit) == server_bit) &&
+ ((sessions & server_bit) == server_bit)){
+
+ memcpy(td->transactionData.suffix,
+ &td->transactionData.number[SFX_START],
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+ DEBUG5("T3(%.*s, %.2d): - Callback 2 - reading(%.*s)",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ td->transactionData.suffix);
+
+ /* Operation 3 */
+ NdbOperation * MyOp = pCON->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOp, "T3-3: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->simpleRead();
+ MyOp->equal(IND_SESSION_SUBSCRIBER,
+ (char*)td->transactionData.number);
+ MyOp->equal(IND_SESSION_SERVER,
+ (char*)&td->transactionData.server_id);
+ MyOp->getValue(IND_SESSION_DATA,
+ (char *)td->transactionData.session_details);
+
+ /* Operation 4 */
+ MyOp = pCON->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOp, "T3-4: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SERVER_ID,
+ (char*)&td->transactionData.server_id);
+ MyOp->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)td->transactionData.suffix);
+ MyOp->incValue(IND_SERVER_READS, (uint32)1);
+ td->transactionData.branchExecuted = 1;
+ } else {
+ DEBUG3("T3(%.*s, %.2d): - Callback 2 - no read",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+ td->transactionData.branchExecuted = 0;
+ }
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( Commit , T3_Callback_3, td);
+ } else {
+ int result = pCON->execute( Commit );
+ T3_Callback_3(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T3_Callback_3(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ DEBUG3("T3(%.*s, %.2d): - Completing", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T3-3: Commit", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T3(td->pNDB, td, stat_async);
+ return;
+ }//if
+ td->pNDB->closeTransaction(pCON);
+ complete_T3(td);
+}
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+void
+start_T4(Ndb * pNDB, ThreadData * td, int async){
+
+ DEBUG3("T4(%.*s, %.2d): - Starting", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ NdbConnection * pCON = 0;
+ while((pCON = startTransaction(pNDB, td)) == 0){
+ CHECK_ALLOWED_ERROR("T4-1: startTransaction", td, pNDB->getNdbError());
+ NdbSleep_MilliSleep(10);
+ }
+
+ NdbOperation *MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T4-1: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&td->transactionData.group_id);
+ MyOp->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&td->transactionData.sessions);
+ MyOp->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)td->transactionData.server_bit);
+ stat_async = async;
+ if (async == 1) {
+ pCON->executeAsynchPrepare( NoCommit , T4_Callback_1, td);
+ } else {
+ int result = pCON->execute( NoCommit );
+ T4_Callback_1(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T4_Callback_1(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T4-1: execute", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T4(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ DEBUG3("T4(%.*s, %.2d): - Callback 1",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+
+ NdbOperation * MyOp = pCON->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOp, "T4-2: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->readTuple();
+ MyOp->equal(IND_GROUP_ID,
+ (char*)&td->transactionData.group_id);
+ MyOp->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&td->transactionData.permission);
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( NoCommit , T4_Callback_2, td);
+ } else {
+ int result = pCON->execute( NoCommit );
+ T4_Callback_2(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T4_Callback_2(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T4-2: execute", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T4(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ Uint32 permission = td->transactionData.permission;
+ Uint32 sessions = td->transactionData.sessions;
+ Uint32 server_bit = td->transactionData.server_bit;
+
+ if(((permission & server_bit) == server_bit) &&
+ ((sessions & server_bit) == 0)){
+
+ memcpy(td->transactionData.suffix,
+ &td->transactionData.number[SFX_START],
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+
+ DEBUG5("T4(%.*s, %.2d): - Callback 2 - inserting(%.*s)",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ td->transactionData.suffix);
+
+ /* Operation 3 */
+
+ NdbOperation * MyOp = pCON->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOp, "T4-3: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->insertTuple();
+ MyOp->equal(IND_SESSION_SUBSCRIBER,
+ (char*)td->transactionData.number);
+ MyOp->equal(IND_SESSION_SERVER,
+ (char*)&td->transactionData.server_id);
+ MyOp->setValue(SESSION_DATA,
+ (char *)td->transactionData.session_details);
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOp = pCON->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOp, "T4-5: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SERVER_ID,
+ (char*)&td->transactionData.server_id);
+ MyOp->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)td->transactionData.suffix);
+ MyOp->incValue(IND_SERVER_INSERTS, (uint32)1);
+ td->transactionData.branchExecuted = 1;
+ } else {
+ td->transactionData.branchExecuted = 0;
+ DEBUG5("T4(%.*s, %.2d): - Callback 2 - %s %s",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ ((permission & server_bit) ?
+ "permission - " : "no permission - "),
+ ((sessions & server_bit) ?
+ "in session - " : "no in session - "));
+ }
+
+ if(!td->transactionData.do_rollback && td->transactionData.branchExecuted){
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( Commit , T4_Callback_3, td);
+ } else {
+ int result = pCON->execute( Commit );
+ T4_Callback_3(result, pCON, (void*)td);
+ return;
+ }//if
+ } else {
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( Rollback , T4_Callback_3, td);
+ } else {
+ int result = pCON->execute( Rollback );
+ T4_Callback_3(result, pCON, (void*)td);
+ return;
+ }//if
+ }
+}
+
+void
+T4_Callback_3(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T4-3: Commit", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T4(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ DEBUG3("T4(%.*s, %.2d): - Completing",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ td->pNDB->closeTransaction(pCON);
+ complete_T4(td);
+}
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+void
+start_T5(Ndb * pNDB, ThreadData * td, int async){
+
+ DEBUG3("T5(%.*s, %.2d): - Starting", SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ NdbConnection * pCON = 0;
+ while((pCON = startTransaction(pNDB, td)) == 0){
+ CHECK_ALLOWED_ERROR("T5-1: startTransaction", td, pNDB->getNdbError());
+ NdbSleep_MilliSleep(10);
+ }
+
+ NdbOperation * MyOp= pCON->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOp, "T5-1: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SUBSCRIBER_NUMBER,
+ td->transactionData.number);
+ MyOp->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&td->transactionData.location);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ td->transactionData.changed_by);
+ MyOp->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ td->transactionData.changed_time);
+ MyOp->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&td->transactionData.group_id);
+ MyOp->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&td->transactionData.sessions);
+ MyOp->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)td->transactionData.server_bit);
+ stat_async = async;
+ if (async == 1) {
+ pCON->executeAsynchPrepare( NoCommit , T5_Callback_1, td);
+ } else {
+ int result = pCON->execute( NoCommit );
+ T5_Callback_1(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T5_Callback_1(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T5-1: execute", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T5(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ DEBUG3("T5(%.*s, %.2d): - Callback 1",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ NdbOperation * MyOp = pCON->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOp, "T5-2: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->readTuple();
+ MyOp->equal(IND_GROUP_ID,
+ (char*)&td->transactionData.group_id);
+ MyOp->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&td->transactionData.permission);
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( NoCommit , T5_Callback_2, td);
+ } else {
+ int result = pCON->execute( NoCommit );
+ T5_Callback_2(result, pCON, (void*)td);
+ return;
+ }//if
+}
+
+void
+T5_Callback_2(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T5-2: execute", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T5(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ Uint32 permission = td->transactionData.permission;
+ Uint32 sessions = td->transactionData.sessions;
+ Uint32 server_bit = td->transactionData.server_bit;
+
+ if(((permission & server_bit) == server_bit) &&
+ ((sessions & server_bit) == server_bit)){
+
+ memcpy(td->transactionData.suffix,
+ &td->transactionData.number[SFX_START],
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+
+ DEBUG5("T5(%.*s, %.2d): - Callback 2 - deleting(%.*s)",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ td->transactionData.suffix);
+
+ /* Operation 3 */
+ NdbOperation * MyOp = pCON->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOp, "T5-3: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->deleteTuple();
+ MyOp->equal(IND_SESSION_SUBSCRIBER,
+ (char*)td->transactionData.number);
+ MyOp->equal(IND_SESSION_SERVER,
+ (char*)&td->transactionData.server_id);
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOp = pCON->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOp, "T5-5: getNdbOperation", td,
+ pCON->getNdbError());
+
+ MyOp->interpretedUpdateTuple();
+ MyOp->equal(IND_SERVER_ID,
+ (char*)&td->transactionData.server_id);
+ MyOp->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)td->transactionData.suffix);
+ MyOp->incValue(IND_SERVER_DELETES, (uint32)1);
+ td->transactionData.branchExecuted = 1;
+ } else {
+ td->transactionData.branchExecuted = 0;
+
+ DEBUG5("T5(%.*s, %.2d): - Callback 2 - no delete - %s %s",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id,
+ ((permission & server_bit) ?
+ "permission - " : "no permission - "),
+ ((sessions & server_bit) ?
+ "in session - " : "no in session - "));
+ }
+
+ if(!td->transactionData.do_rollback && td->transactionData.branchExecuted){
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( Commit , T5_Callback_3, td);
+ } else {
+ int result = pCON->execute( Commit );
+ T5_Callback_3(result, pCON, (void*)td);
+ return;
+ }//if
+ } else {
+ if (stat_async == 1) {
+ pCON->executeAsynchPrepare( Rollback , T5_Callback_3, td);
+ } else {
+ int result = pCON->execute( Rollback );
+ T5_Callback_3(result, pCON, (void*)td);
+ return;
+ }//if
+ }
+}
+
+void
+T5_Callback_3(int result, NdbConnection * pCON, void * threadData){
+ ThreadData * td = (ThreadData *)threadData;
+ if (result == -1) {
+ CHECK_ALLOWED_ERROR("T5-3: Commit", td, pCON->getNdbError());
+ td->pNDB->closeTransaction(pCON);
+ start_T5(td->pNDB, td, stat_async);
+ return;
+ }//if
+
+ DEBUG3("T5(%.*s, %.2d): - Completing",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number,
+ td->transactionData.server_id);
+
+ td->pNDB->closeTransaction(pCON);
+ complete_T5(td);
+}
diff --git a/storage/ndb/test/ndbapi/ndb_user_populate.cpp b/storage/ndb/test/ndbapi/ndb_user_populate.cpp
new file mode 100644
index 00000000000..ce3a76cdd59
--- /dev/null
+++ b/storage/ndb/test/ndbapi/ndb_user_populate.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 */
+
+
+extern "C" {
+#include "user_populate.h"
+}
+
+#include <ndb_global.h>
+#include <NdbApi.hpp>
+
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+int
+insert_subscriber(void * obj,
+ SubscriberNumber number,
+ SubscriberName name,
+ GroupId groupId,
+ Location l,
+ ActiveSessions activeSessions,
+ ChangedBy changedBy,
+ ChangedTime changedTime){
+ Ndb * pNDB = (Ndb *)obj;
+ int check;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "getNdbOperation", MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "insertTuple", MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER, number);
+ CHECK_MINUS_ONE(check, "equal", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_NAME, name);
+ CHECK_MINUS_ONE(check, "setValue name", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_GROUP, (char*)&groupId);
+ CHECK_MINUS_ONE(check, "setValue group", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_LOCATION, (char*)&l);
+ CHECK_MINUS_ONE(check, "setValue location", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_SESSIONS, (char*)&activeSessions);
+ CHECK_MINUS_ONE(check, "setValue sessions", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_CHANGED_BY, changedBy);
+ CHECK_MINUS_ONE(check, "setValue changedBy", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_CHANGED_TIME, changedTime);
+ CHECK_MINUS_ONE(check, "setValue changedTime", MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "commit", MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+ return 0;
+}
+
+int
+insert_server(void * obj,
+ ServerId serverId,
+ SubscriberSuffix suffix,
+ ServerName name,
+ Counter noOfRead,
+ Counter noOfInsert,
+ Counter noOfDelete){
+ Ndb * pNDB = (Ndb *)obj;
+ int check;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "getNdbOperation", MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "insert tuple", MyTransaction);
+
+ check = MyOperation->equal(SERVER_ID, (char*)&serverId);
+ CHECK_MINUS_ONE(check, "setValue id", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_SUBSCRIBER_SUFFIX, suffix);
+ CHECK_MINUS_ONE(check, "setValue suffix", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_NAME, name);
+ CHECK_MINUS_ONE(check, "setValue name", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_READS, (char*)&noOfRead);
+ CHECK_MINUS_ONE(check, "setValue reads", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_INSERTS, (char*)&noOfInsert);
+ CHECK_MINUS_ONE(check, "setValue inserts", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_DELETES, (char*)&noOfDelete);
+ CHECK_MINUS_ONE(check, "setValue deletes", MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "commit", MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+ return 0;
+}
+
+int
+insert_group(void * obj,
+ GroupId groupId,
+ GroupName name,
+ Permission allowRead,
+ Permission allowInsert,
+ Permission allowDelete){
+ Ndb * pNDB = (Ndb *)obj;
+ int check;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "getNdbOperation", MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "insertTuple", MyTransaction);
+
+ check = MyOperation->equal(GROUP_ID, (char*)&groupId);
+ CHECK_MINUS_ONE(check, "equal", MyTransaction);
+
+ check = MyOperation->setValue(GROUP_NAME, name);
+ CHECK_MINUS_ONE(check, "setValue name", MyTransaction);
+
+ check = MyOperation->setValue(GROUP_ALLOW_READ, (char*)&allowRead);
+ CHECK_MINUS_ONE(check, "setValue allowRead", MyTransaction);
+
+ check = MyOperation->setValue(GROUP_ALLOW_INSERT, (char*)&allowInsert);
+ CHECK_MINUS_ONE(check, "setValue allowInsert", MyTransaction);
+
+ check = MyOperation->setValue(GROUP_ALLOW_DELETE, (char*)&allowDelete);
+ CHECK_MINUS_ONE(check, "setValue allowDelete", MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "commit", MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/ndb_user_transaction.cpp b/storage/ndb/test/ndbapi/ndb_user_transaction.cpp
new file mode 100644
index 00000000000..182f1f99586
--- /dev/null
+++ b/storage/ndb/test/ndbapi/ndb_user_transaction.cpp
@@ -0,0 +1,825 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+extern "C" {
+#include "user_transaction.h"
+};
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <time.h>
+#include <NdbApi.hpp>
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+int
+T1(void * obj,
+ const SubscriberNumber number,
+ const Location new_location,
+ const ChangedBy changed_by,
+ const ChangedTime changed_time,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T1: getNdbOperation", MyTransaction);
+
+ check = MyOperation->updateTuple();
+ CHECK_MINUS_ONE(check, "T1: updateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T1: equal subscriber",
+ MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_LOCATION,
+ (char *)&new_location);
+ CHECK_MINUS_ONE(check, "T1: setValue location",
+ MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_by",
+ MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_time",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T1: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+int
+T2(void * obj,
+ const SubscriberNumber number,
+ Location * readLocation,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T2: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T2: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_LOCATION,
+ (char *)readLocation);
+ CHECK_NULL(check2, "T2: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_NULL(check2, "T2: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_NULL(check2, "T2: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_NAME,
+ subscriberName);
+ CHECK_NULL(check2, "T2: getValue name",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T2: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+int
+T3(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ SessionDetails outSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T3-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T3-1: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T3-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T3-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T3-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T3-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T3-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T3-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T3-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T3-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(GROUP_ALLOW_READ,
+ (char *)&permission);
+ CHECK_NULL(check2, "T3-2: getValue allow_read",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T3(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("reading - ");
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T3-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-3: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T3-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-3: equal server id",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SESSION_DATA,
+ (char *)outSessionDetails);
+ CHECK_NULL(check2, "T3-3: getValue session details",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-3: NoCommit",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T3-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T3-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-4: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T3-4: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(SERVER_READS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T3-4: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-4: NoCommit",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ }
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T3: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T4(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ const SessionDetails inSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T4-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ DEBUG3("T4(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ NdbOperation * MyOperation = 0;
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTupleExclusive();
+ CHECK_MINUS_ONE(check, "T4-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T4-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T4-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T4-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T4-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T4-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T4-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T4-2: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T4-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T4-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(GROUP_ALLOW_INSERT,
+ (char *)&permission);
+ CHECK_NULL(check2, "T4-2: getValue allow_insert",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == 0)){
+
+ DEBUG("inserting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T4-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "T4-3: insertTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-3: equal server id",
+ MyTransaction);
+
+ check = MyOperation->setValue(SESSION_DATA,
+ (char *)inSessionDetails);
+ CHECK_MINUS_ONE(check, "T4-3: setValue session details",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-3: NoCommit",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-4: equal number",
+ MyTransaction);
+
+ check = MyOperation->incValue(SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T4-4: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-4: NoCommit",
+ MyTransaction);
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T4-5: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T4-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(SERVER_INSERTS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T4-5: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-5: NoCommit",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T4: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T4:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T5(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+ NdbConnection * MyTransaction = 0;
+ NdbOperation * MyOperation = 0;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T5-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-1: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTupleExclusive();
+ CHECK_MINUS_ONE(check, "T5-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T5-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T5-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T5-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T5-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T5-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T5-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T5-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T5-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T5-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(GROUP_ALLOW_DELETE,
+ (char *)&permission);
+ CHECK_NULL(check2, "T5-2: getValue allow_delete",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T5(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("deleting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T5-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->deleteTuple();
+ CHECK_MINUS_ONE(check, "T5-3: deleteTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-3: equal server id",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-3: NoCommit",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-4: equal number",
+ MyTransaction);
+
+ check = MyOperation->subValue(SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T5-4: dec value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-4: NoCommit",
+ MyTransaction);
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T5-5: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T5-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(SERVER_DELETES, (uint32)1);
+ CHECK_MINUS_ONE(check, "T5-5: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-5: NoCommit",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T5: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T5:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/ndb_user_transaction2.cpp b/storage/ndb/test/ndbapi/ndb_user_transaction2.cpp
new file mode 100644
index 00000000000..df3c7a7989e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/ndb_user_transaction2.cpp
@@ -0,0 +1,825 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+extern "C" {
+#include "user_transaction.h"
+};
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <time.h>
+#include <NdbApi.hpp>
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+int
+T1(void * obj,
+ const SubscriberNumber number,
+ const Location new_location,
+ const ChangedBy changed_by,
+ const ChangedTime changed_time,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T1: getNdbOperation", MyTransaction);
+
+ check = MyOperation->updateTuple();
+ CHECK_MINUS_ONE(check, "T1: updateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T1: equal subscriber",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&new_location);
+ CHECK_MINUS_ONE(check, "T1: setValue location",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_by",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_time",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T1: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+int
+T2(void * obj,
+ const SubscriberNumber number,
+ Location * readLocation,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T2: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T2: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)readLocation);
+ CHECK_NULL(check2, "T2: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_NULL(check2, "T2: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_NULL(check2, "T2: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_NAME,
+ subscriberName);
+ CHECK_NULL(check2, "T2: getValue name",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T2: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+int
+T3(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ SessionDetails outSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T3-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T3-1: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T3-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T3-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T3-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T3-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T3-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T3-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T3-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T3-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&permission);
+ CHECK_NULL(check2, "T3-2: getValue allow_read",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T3(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("reading - ");
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T3-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-3: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T3-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-3: equal server id",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SESSION_DATA,
+ (char *)outSessionDetails);
+ CHECK_NULL(check2, "T3-3: getValue session details",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-3: NoCommit",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T3-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T3-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-4: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T3-4: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_READS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T3-4: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-4: NoCommit",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ }
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T3: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T4(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ const SessionDetails inSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T4-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ DEBUG3("T4(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ NdbOperation * MyOperation = 0;
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTupleExclusive();
+ CHECK_MINUS_ONE(check, "T4-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T4-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T4-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T4-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T4-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T4-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T4-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T4-2: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T4-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T4-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&permission);
+ CHECK_NULL(check2, "T4-2: getValue allow_insert",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == 0)){
+
+ DEBUG("inserting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T4-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "T4-3: insertTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-3: equal server id",
+ MyTransaction);
+
+ check = MyOperation->setValue(SESSION_DATA,
+ (char *)inSessionDetails);
+ CHECK_MINUS_ONE(check, "T4-3: setValue session details",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-3: NoCommit",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-4: equal number",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T4-4: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-4: NoCommit",
+ MyTransaction);
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T4-5: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T4-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_INSERTS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T4-5: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-5: NoCommit",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T4: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T4:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T5(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+ NdbConnection * MyTransaction = 0;
+ NdbOperation * MyOperation = 0;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T5-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-1: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTupleExclusive();
+ CHECK_MINUS_ONE(check, "T5-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T5-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T5-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T5-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T5-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T5-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T5-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T5-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T5-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T5-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&permission);
+ CHECK_NULL(check2, "T5-2: getValue allow_delete",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T5(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("deleting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T5-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->deleteTuple();
+ CHECK_MINUS_ONE(check, "T5-3: deleteTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-3: equal server id",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-3: NoCommit",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-4: equal number",
+ MyTransaction);
+
+ check = MyOperation->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T5-4: dec value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-4: NoCommit",
+ MyTransaction);
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T5-5: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T5-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_DELETES, (uint32)1);
+ CHECK_MINUS_ONE(check, "T5-5: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-5: NoCommit",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T5: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T5:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/ndb_user_transaction3.cpp b/storage/ndb/test/ndbapi/ndb_user_transaction3.cpp
new file mode 100644
index 00000000000..d2c92ecd424
--- /dev/null
+++ b/storage/ndb/test/ndbapi/ndb_user_transaction3.cpp
@@ -0,0 +1,793 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+extern "C" {
+#include "user_transaction.h"
+};
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <time.h>
+#include <NdbApi.hpp>
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+int
+T1(void * obj,
+ const SubscriberNumber number,
+ const Location new_location,
+ const ChangedBy changed_by,
+ const ChangedTime changed_time,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T1: getNdbOperation", MyTransaction);
+
+ check = MyOperation->updateTuple();
+ CHECK_MINUS_ONE(check, "T1: updateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T1: equal subscriber",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&new_location);
+ CHECK_MINUS_ONE(check, "T1: setValue location",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_by",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_time",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T1: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+int
+T2(void * obj,
+ const SubscriberNumber number,
+ Location * readLocation,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T2: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T2: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)readLocation);
+ CHECK_NULL(check2, "T2: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_NULL(check2, "T2: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_NULL(check2, "T2: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_NAME,
+ subscriberName);
+ CHECK_NULL(check2, "T2: getValue name",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T2: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+int
+T3(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ SessionDetails outSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T3-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T3-1: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T3-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T3-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T3-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T3-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T3-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T3-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T3-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T3-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&permission);
+ CHECK_NULL(check2, "T3-2: getValue allow_read",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T3(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("reading - ");
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T3-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-3: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T3-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-3: equal server id",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SESSION_DATA,
+ (char *)outSessionDetails);
+ CHECK_NULL(check2, "T3-3: getValue session details",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T3-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T3-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-4: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T3-4: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_READS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T3-4: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ }
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T3: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T4(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ const SessionDetails inSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T4-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ DEBUG3("T4(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ NdbOperation * MyOperation = 0;
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTupleExclusive();
+ CHECK_MINUS_ONE(check, "T4-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T4-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T4-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T4-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T4-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T4-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T4-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T4-2: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T4-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T4-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&permission);
+ CHECK_NULL(check2, "T4-2: getValue allow_insert",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == 0)){
+
+ DEBUG("inserting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T4-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "T4-3: insertTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-3: equal server id",
+ MyTransaction);
+
+ check = MyOperation->setValue(SESSION_DATA,
+ (char *)inSessionDetails);
+ CHECK_MINUS_ONE(check, "T4-3: setValue session details",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-4: equal number",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T4-4: inc value",
+ MyTransaction);
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T4-5: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T4-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_INSERTS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T4-5: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T4: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T4:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T5(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+ NdbConnection * MyTransaction = 0;
+ NdbOperation * MyOperation = 0;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T5-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-1: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTupleExclusive();
+ CHECK_MINUS_ONE(check, "T5-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T5-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T5-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T5-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T5-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T5-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T5-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T5-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T5-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T5-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&permission);
+ CHECK_NULL(check2, "T5-2: getValue allow_delete",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T5(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("deleting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T5-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->deleteTuple();
+ CHECK_MINUS_ONE(check, "T5-3: deleteTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-3: equal server id",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-4: equal number",
+ MyTransaction);
+
+ check = MyOperation->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T5-4: dec value",
+ MyTransaction);
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T5-5: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T5-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_DELETES, (uint32)1);
+ CHECK_MINUS_ONE(check, "T5-5: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T5: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T5:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/ndb_user_transaction4.cpp b/storage/ndb/test/ndbapi/ndb_user_transaction4.cpp
new file mode 100644
index 00000000000..e652c7bfed8
--- /dev/null
+++ b/storage/ndb/test/ndbapi/ndb_user_transaction4.cpp
@@ -0,0 +1,770 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+extern "C" {
+#include "user_transaction.h"
+};
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <time.h>
+#include <NdbApi.hpp>
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+int
+T1(void * obj,
+ const SubscriberNumber number,
+ const Location new_location,
+ const ChangedBy changed_by,
+ const ChangedTime changed_time,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ DEBUG2("T1(%.*s):\n", SUBSCRIBER_NUMBER_LENGTH, number);
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T1-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T1: getNdbOperation", MyTransaction);
+
+ check = MyOperation->updateTuple();
+ CHECK_MINUS_ONE(check, "T1: updateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T1: equal subscriber",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&new_location);
+ CHECK_MINUS_ONE(check, "T1: setValue location",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_by",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_time",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T1: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+int
+T2(void * obj,
+ const SubscriberNumber number,
+ Location * readLocation,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ DEBUG2("T2(%.*s):\n", SUBSCRIBER_NUMBER_LENGTH, number);
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T2-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T2: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)readLocation);
+ CHECK_NULL(check2, "T2: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_NULL(check2, "T2: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_NULL(check2, "T2: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_NAME,
+ subscriberName);
+ CHECK_NULL(check2, "T2: getValue name",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T2: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+int
+T3(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ SessionDetails outSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ DEBUG3("T3(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T3-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T3-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T3-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T3-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T3-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T3-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T3-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T3-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T3-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T3-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&permission);
+ CHECK_NULL(check2, "T3-2: getValue allow_read",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("reading - ");
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T3-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-3: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T3-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-3: equal server id",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SESSION_DATA,
+ (char *)outSessionDetails);
+ CHECK_NULL(check2, "T3-3: getValue session details",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T3-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T3-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-4: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T3-4: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_READS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T3-4: inc value",
+ MyTransaction);
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ }
+ DEBUG("commit...");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T3: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ DEBUG("done\n");
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T4(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ const SessionDetails inSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ DEBUG3("T4(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T4-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T4-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T4-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T4-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T4-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T4-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T4-1: getValue sessions",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T4-4: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T4-2: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T4-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T4-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&permission);
+ CHECK_NULL(check2, "T4-2: getValue allow_insert",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == 0)){
+
+ DEBUG("inserting - ");
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T4-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "T4-3: insertTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-3: equal server id",
+ MyTransaction);
+
+ check = MyOperation->setValue(SESSION_DATA,
+ (char *)inSessionDetails);
+ CHECK_MINUS_ONE(check, "T4-3: setValue session details",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T4-5: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T4-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_INSERTS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T4-5: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback && (* outBranchExecuted)){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T4: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T4:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T5(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ DEBUG3("T5(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ Ndb * pNDB = (Ndb *) obj;
+ NdbConnection * MyTransaction = 0;
+ NdbOperation * MyOperation = 0;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T5-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T5-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T5-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T5-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T5-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T5-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T5-1: getValue sessions",
+ MyTransaction);
+
+ check = MyOperation->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T5-4: dec value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T5-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T5-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T5-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&permission);
+ CHECK_NULL(check2, "T5-2: getValue allow_delete",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("deleting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T5-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->deleteTuple();
+ CHECK_MINUS_ONE(check, "T5-3: deleteTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-3: equal server id",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T5-5: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T5-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_DELETES, (uint32)1);
+ CHECK_MINUS_ONE(check, "T5-5: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback && (* outBranchExecuted)){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T5: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T5:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/ndb_user_transaction5.cpp b/storage/ndb/test/ndbapi/ndb_user_transaction5.cpp
new file mode 100644
index 00000000000..86580008d10
--- /dev/null
+++ b/storage/ndb/test/ndbapi/ndb_user_transaction5.cpp
@@ -0,0 +1,769 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+extern "C" {
+#include "user_transaction.h"
+};
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <time.h>
+#include <NdbApi.hpp>
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+int
+T1(void * obj,
+ const SubscriberNumber number,
+ const Location new_location,
+ const ChangedBy changed_by,
+ const ChangedTime changed_time,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ DEBUG2("T1(%.*s):\n", SUBSCRIBER_NUMBER_LENGTH, number);
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T1-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T1: getNdbOperation", MyTransaction);
+
+ check = MyOperation->updateTuple();
+ CHECK_MINUS_ONE(check, "T1: updateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T1: equal subscriber",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&new_location);
+ CHECK_MINUS_ONE(check, "T1: setValue location",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_by",
+ MyTransaction);
+
+ check = MyOperation->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_MINUS_ONE(check, "T1: setValue changed_time",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T1: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+int
+T2(void * obj,
+ const SubscriberNumber number,
+ Location * readLocation,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName,
+ BenchmarkTime * transaction_time){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ DEBUG2("T2(%.*s):\n", SUBSCRIBER_NUMBER_LENGTH, number);
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T2-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ CHECK_MINUS_ONE(check, "T2: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)readLocation);
+ CHECK_NULL(check2, "T2: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ CHECK_NULL(check2, "T2: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ CHECK_NULL(check2, "T2: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_NAME,
+ subscriberName);
+ CHECK_NULL(check2, "T2: getValue name",
+ MyTransaction);
+
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T2: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(transaction_time);
+ time_diff(transaction_time, &start);
+ return 0;
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+int
+T3(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ SessionDetails outSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T3-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T3-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T3-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T3-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T3-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T3-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T3-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T3-1: getValue sessions",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T3-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T3-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T3-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&permission);
+ CHECK_NULL(check2, "T3-2: getValue allow_read",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T3(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("reading - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T3-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->simpleRead();
+ CHECK_MINUS_ONE(check, "T3-3: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T3-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-3: equal server id",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SESSION_DATA,
+ (char *)outSessionDetails);
+ CHECK_NULL(check2, "T3-3: getValue session details",
+ MyTransaction);
+
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T3-4: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T3-4: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T3-4: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T3-4: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_READS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T3-4: inc value",
+ MyTransaction);
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ }
+ DEBUG("commit...");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T3: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ DEBUG("done\n");
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T4(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ const SessionDetails inSessionDetails,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T4-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T4-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T4-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T4-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T4-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T4-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T4-1: getValue sessions",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T4-4: inc value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T4-2: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T4-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T4-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&permission);
+ CHECK_NULL(check2, "T4-2: getValue allow_insert",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T4(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == 0)){
+
+ DEBUG("inserting - ");
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T4-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "T4-3: insertTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T4-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-3: equal server id",
+ MyTransaction);
+
+ check = MyOperation->setValue(SESSION_DATA,
+ (char *)inSessionDetails);
+ CHECK_MINUS_ONE(check, "T4-3: setValue session details",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T4-5: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T4-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T4-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T4-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_INSERTS, (uint32)1);
+ CHECK_MINUS_ONE(check, "T4-5: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback && (* outBranchExecuted)){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T4: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T4:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+int
+T5(void * obj,
+ const SubscriberNumber inNumber,
+ const SubscriberSuffix inSuffix,
+ const ServerId inServerId,
+ const ServerBit inServerBit,
+ ChangedBy outChangedBy,
+ ChangedTime outChangedTime,
+ Location * outLocation,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted,
+ BenchmarkTime * outTransactionTime){
+
+ Ndb * pNDB = (Ndb *) obj;
+ NdbConnection * MyTransaction = 0;
+ NdbOperation * MyOperation = 0;
+
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+
+ BenchmarkTime start;
+ get_time(&start);
+
+ int check;
+ NdbRecAttr * check2;
+
+ MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T5-1: startTranscation", pNDB->getNdbErrorString(), 0);
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-1: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-1: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ CHECK_MINUS_ONE(check, "T5-1: equal subscriber",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)outLocation);
+ CHECK_NULL(check2, "T5-1: getValue location",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ CHECK_NULL(check2, "T5-1: getValue changed_by",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ CHECK_NULL(check2, "T5-1: getValue changed_time",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ CHECK_NULL(check2, "T5-1: getValue group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ CHECK_NULL(check2, "T5-1: getValue sessions",
+ MyTransaction);
+
+ check = MyOperation->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ CHECK_MINUS_ONE(check, "T5-4: dec value",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T5-2: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->readTuple();
+ CHECK_MINUS_ONE(check, "T5-2: readTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ CHECK_MINUS_ONE(check, "T5-2: equal group",
+ MyTransaction);
+
+ check2 = MyOperation->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&permission);
+ CHECK_NULL(check2, "T5-2: getValue allow_delete",
+ MyTransaction);
+
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-2: NoCommit",
+ MyTransaction);
+
+ DEBUG3("T5(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ DEBUG("deleting - ");
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T5-3: getNdbOperation",
+ MyTransaction);
+
+ check = MyOperation->deleteTuple();
+ CHECK_MINUS_ONE(check, "T5-3: deleteTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ CHECK_MINUS_ONE(check, "T5-3: equal number",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-3: equal server id",
+ MyTransaction);
+
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T5-5: getNdbOperation",
+ MyTransaction);
+
+
+ check = MyOperation->interpretedUpdateTuple();
+ CHECK_MINUS_ONE(check, "T5-5: interpretedUpdateTuple",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ CHECK_MINUS_ONE(check, "T5-5: equal serverId",
+ MyTransaction);
+
+ check = MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ CHECK_MINUS_ONE(check, "T5-5: equal suffix",
+ MyTransaction);
+
+ check = MyOperation->incValue(IND_SERVER_DELETES, (uint32)1);
+ CHECK_MINUS_ONE(check, "T5-5: inc value",
+ MyTransaction);
+
+ (* outBranchExecuted) = 1;
+ } else {
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ (* outBranchExecuted) = 0;
+ }
+
+ if(!inDoRollback && (* outBranchExecuted)){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T5: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T5:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+
+ get_time(outTransactionTime);
+ time_diff(outTransactionTime, &start);
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/ndb_user_transaction6.cpp b/storage/ndb/test/ndbapi/ndb_user_transaction6.cpp
new file mode 100644
index 00000000000..262f38e9ffb
--- /dev/null
+++ b/storage/ndb/test/ndbapi/ndb_user_transaction6.cpp
@@ -0,0 +1,561 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 DEBUG_ON
+
+#include <string.h>
+#include "userHandle.h"
+#include "userInterface.h"
+
+#include "macros.h"
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+
+#include <NdbApi.hpp>
+
+
+void
+userCheckpoint(UserHandle *uh){
+}
+
+inline
+NdbConnection *
+startTransaction(Ndb * pNDB, ServerId inServerId, const SubscriberNumber inNumber){
+
+ const int keyDataLenBytes = sizeof(ServerId)+SUBSCRIBER_NUMBER_LENGTH;
+ const int keyDataLen_64Words = keyDataLenBytes >> 3;
+
+ Uint64 keyDataBuf[keyDataLen_64Words+1]; // The "+1" is for rounding...
+
+ char * keyDataBuf_charP = (char *)&keyDataBuf[0];
+ Uint32 * keyDataBuf_wo32P = (Uint32 *)&keyDataBuf[0];
+
+ // Server Id comes first
+ keyDataBuf_wo32P[0] = inServerId;
+ // Then subscriber number
+ memcpy(&keyDataBuf_charP[sizeof(ServerId)], inNumber, SUBSCRIBER_NUMBER_LENGTH);
+
+ return pNDB->startTransaction(0, keyDataBuf_charP, keyDataLenBytes);
+}
+
+/**
+ * Transaction 1 - T1
+ *
+ * Update location and changed by/time on a subscriber
+ *
+ * Input:
+ * SubscriberNumber,
+ * Location,
+ * ChangedBy,
+ * ChangedTime
+ *
+ * Output:
+ */
+void
+userTransaction_T1(UserHandle * uh,
+ SubscriberNumber number,
+ Location new_location,
+ ChangedBy changed_by,
+ ChangedTime changed_time){
+ Ndb * pNDB = uh->pNDB;
+
+ DEBUG2("T1(%.*s):\n", SUBSCRIBER_NUMBER_LENGTH, number);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction != NULL) {
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ if (MyOperation != NULL) {
+ MyOperation->updateTuple();
+ MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ MyOperation->setValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&new_location);
+ MyOperation->setValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ MyOperation->setValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ check = MyTransaction->execute( Commit );
+ if (check != -1) {
+ pNDB->closeTransaction(MyTransaction);
+ return;
+ } else {
+ CHECK_MINUS_ONE(check, "T1: Commit",
+ MyTransaction);
+ }//if
+ } else {
+ CHECK_NULL(MyOperation, "T1: getNdbOperation", MyTransaction);
+ }//if
+ } else {
+ error_handler("T1-1: startTranscation", pNDB->getNdbErrorString(), pNDB->getNdbError());
+ }//if
+}
+
+/**
+ * Transaction 2 - T2
+ *
+ * Read from Subscriber:
+ *
+ * Input:
+ * SubscriberNumber
+ *
+ * Output:
+ * Location
+ * Changed by
+ * Changed Timestamp
+ * Name
+ */
+void
+userTransaction_T2(UserHandle * uh,
+ SubscriberNumber number,
+ Location * readLocation,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName){
+ Ndb * pNDB = uh->pNDB;
+
+ DEBUG2("T2(%.*s):\n", SUBSCRIBER_NUMBER_LENGTH, number);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T2-1: startTransaction", pNDB->getNdbErrorString(), pNDB->getNdbError());
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T2: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->readTuple();
+ MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ number);
+ MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)readLocation);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ changed_by);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ changed_time);
+ MyOperation->getValue(IND_SUBSCRIBER_NAME,
+ subscriberName);
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T2: Commit",
+ MyTransaction);
+ pNDB->closeTransaction(MyTransaction);
+}
+
+/**
+ * Transaction 3 - T3
+ *
+ * Read session details
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ *
+ * Output:
+ * BranchExecuted
+ * SessionDetails
+ * ChangedBy
+ * ChangedTime
+ * Location
+ */
+void
+userTransaction_T3(UserHandle * uh,
+ SubscriberNumber inNumber,
+ ServerId inServerId,
+ ServerBit inServerBit,
+ SessionDetails outSessionDetails,
+ BranchExecuted * outBranchExecuted){
+ Ndb * pNDB = uh->pNDB;
+
+ char outChangedBy [sizeof(ChangedBy) +(4-(sizeof(ChangedBy) & 3))];
+ char outChangedTime [sizeof(ChangedTime)+(4-(sizeof(ChangedTime) & 3))];
+ Location outLocation;
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+ SubscriberSuffix inSuffix;
+
+ DEBUG3("T3(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = startTransaction(pNDB, inServerId, inNumber);
+ if (MyTransaction == NULL)
+ error_handler("T3-1: startTranscation", pNDB->getNdbErrorString(), pNDB->getNdbError());
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T3-1: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->readTuple();
+ MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&outLocation);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-1: NoCommit",
+ MyTransaction);
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T3-2: getNdbOperation",
+ MyTransaction);
+
+
+ MyOperation->readTuple();
+ MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ MyOperation->getValue(IND_GROUP_ALLOW_READ,
+ (char *)&permission);
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T3-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ memcpy(inSuffix,
+ &inNumber[SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH], SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+ DEBUG2("reading(%.*s) - ", SUBSCRIBER_NUMBER_SUFFIX_LENGTH, inSuffix);
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T3-3: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->simpleRead();
+
+ MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ MyOperation->getValue(IND_SESSION_DATA,
+ (char *)outSessionDetails);
+ /* Operation 4 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T3-4: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->interpretedUpdateTuple();
+ MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ MyOperation->incValue(IND_SERVER_READS, (uint32)1);
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ }
+ DEBUG("commit...");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T3: Commit",
+ MyTransaction);
+
+ pNDB->closeTransaction(MyTransaction);
+
+ DEBUG("done\n");
+}
+
+
+/**
+ * Transaction 4 - T4
+ *
+ * Create session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * SessionDetails,
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+void
+userTransaction_T4(UserHandle * uh,
+ SubscriberNumber inNumber,
+ ServerId inServerId,
+ ServerBit inServerBit,
+ SessionDetails inSessionDetails,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted){
+
+ Ndb * pNDB = uh->pNDB;
+
+ char outChangedBy [sizeof(ChangedBy) +(4-(sizeof(ChangedBy) & 3))];
+ char outChangedTime [sizeof(ChangedTime)+(4-(sizeof(ChangedTime) & 3))];
+ Location outLocation;
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+ SubscriberSuffix inSuffix;
+
+ DEBUG3("T4(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ int check;
+ NdbRecAttr * check2;
+
+ NdbConnection * MyTransaction = startTransaction(pNDB, inServerId, inNumber);
+ if (MyTransaction == NULL)
+ error_handler("T4-1: startTranscation", pNDB->getNdbErrorString(), pNDB->getNdbError());
+
+ NdbOperation *MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T4-1: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->interpretedUpdateTuple();
+ MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&outLocation);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ outChangedBy);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ outChangedTime);
+ MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ MyOperation->incValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ check = MyTransaction->execute( NoCommit );
+
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T4-2: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->readTuple();
+ MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ MyOperation->getValue(IND_GROUP_ALLOW_INSERT,
+ (char *)&permission);
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T4-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == 0)){
+
+ memcpy(inSuffix,
+ &inNumber[SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH], SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+
+ DEBUG2("inserting(%.*s) - ", SUBSCRIBER_NUMBER_SUFFIX_LENGTH, inSuffix);
+
+ /* Operation 3 */
+
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T4-3: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->insertTuple();
+ MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ MyOperation->setValue(SESSION_DATA,
+ (char *)inSessionDetails);
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T4-5: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->interpretedUpdateTuple();
+ MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ MyOperation->incValue(IND_SERVER_INSERTS, (uint32)1);
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ }
+
+ if(!inDoRollback && (* outBranchExecuted)){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T4: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T4:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+}
+
+
+/**
+ * Transaction 5 - T5
+ *
+ * Delete session
+ *
+ * Input:
+ * SubscriberNumber
+ * ServerId
+ * ServerBit
+ * DoRollback
+ * Output:
+ * ChangedBy
+ * ChangedTime
+ * Location
+ * BranchExecuted
+ */
+void
+userTransaction_T5(UserHandle * uh,
+ SubscriberNumber inNumber,
+ ServerId inServerId,
+ ServerBit inServerBit,
+ DoRollback inDoRollback,
+ BranchExecuted * outBranchExecuted){
+ Ndb * pNDB = uh->pNDB;
+
+ DEBUG3("T5(%.*s, %.2d): ", SUBSCRIBER_NUMBER_LENGTH, inNumber, inServerId);
+
+ NdbConnection * MyTransaction = 0;
+ NdbOperation * MyOperation = 0;
+
+ char outChangedBy [sizeof(ChangedBy) +(4-(sizeof(ChangedBy) & 3))];
+ char outChangedTime [sizeof(ChangedTime)+(4-(sizeof(ChangedTime) & 3))];
+ Location outLocation;
+ GroupId groupId;
+ ActiveSessions sessions;
+ Permission permission;
+ SubscriberSuffix inSuffix;
+
+ int check;
+ NdbRecAttr * check2;
+
+ MyTransaction = pNDB->startTransaction();
+ if (MyTransaction == NULL)
+ error_handler("T5-1: startTranscation", pNDB->getNdbErrorString(), pNDB->getNdbError());
+
+ MyOperation= MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "T5-1: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->interpretedUpdateTuple();
+ MyOperation->equal(IND_SUBSCRIBER_NUMBER,
+ inNumber);
+ MyOperation->getValue(IND_SUBSCRIBER_LOCATION,
+ (char *)&outLocation);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_BY,
+ &outChangedBy[0]);
+ MyOperation->getValue(IND_SUBSCRIBER_CHANGED_TIME,
+ &outChangedTime[0]);
+ MyOperation->getValue(IND_SUBSCRIBER_GROUP,
+ (char *)&groupId);
+ MyOperation->getValue(IND_SUBSCRIBER_SESSIONS,
+ (char *)&sessions);
+ MyOperation->subValue(IND_SUBSCRIBER_SESSIONS,
+ (uint32)inServerBit);
+ MyTransaction->execute( NoCommit );
+ /* Operation 2 */
+
+ MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "T5-2: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->readTuple();
+ MyOperation->equal(IND_GROUP_ID,
+ (char*)&groupId);
+ MyOperation->getValue(IND_GROUP_ALLOW_DELETE,
+ (char *)&permission);
+ check = MyTransaction->execute( NoCommit );
+ CHECK_MINUS_ONE(check, "T5-2: NoCommit",
+ MyTransaction);
+
+ if(((permission & inServerBit) == inServerBit) &&
+ ((sessions & inServerBit) == inServerBit)){
+
+ memcpy(inSuffix,
+ &inNumber[SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH], SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+
+ DEBUG2("deleting(%.*s) - ", SUBSCRIBER_NUMBER_SUFFIX_LENGTH, inSuffix);
+
+ /* Operation 3 */
+ MyOperation = MyTransaction->getNdbOperation(SESSION_TABLE);
+ CHECK_NULL(MyOperation, "T5-3: getNdbOperation",
+ MyTransaction);
+
+ MyOperation->deleteTuple();
+ MyOperation->equal(IND_SESSION_SUBSCRIBER,
+ (char*)inNumber);
+ MyOperation->equal(IND_SESSION_SERVER,
+ (char*)&inServerId);
+ /* Operation 4 */
+
+ /* Operation 5 */
+ MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "T5-5: getNdbOperation",
+ MyTransaction);
+
+
+ MyOperation->interpretedUpdateTuple();
+ MyOperation->equal(IND_SERVER_ID,
+ (char*)&inServerId);
+ MyOperation->equal(IND_SERVER_SUBSCRIBER_SUFFIX,
+ (char*)inSuffix);
+ MyOperation->incValue(IND_SERVER_DELETES, (uint32)1);
+ (* outBranchExecuted) = 1;
+ } else {
+ (* outBranchExecuted) = 0;
+ DEBUG1("%s", ((permission & inServerBit) ? "permission - " : "no permission - "));
+ DEBUG1("%s", ((sessions & inServerBit) ? "in session - " : "no in session - "));
+ }
+
+ if(!inDoRollback && (* outBranchExecuted)){
+ DEBUG("commit\n");
+ check = MyTransaction->execute( Commit );
+ CHECK_MINUS_ONE(check, "T5: Commit",
+ MyTransaction);
+ } else {
+ DEBUG("rollback\n");
+ check = MyTransaction->execute(Rollback);
+ CHECK_MINUS_ONE(check, "T5:Rollback",
+ MyTransaction);
+
+ }
+
+ pNDB->closeTransaction(MyTransaction);
+}
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/acid/Makefile b/storage/ndb/test/ndbapi/old_dirs/acid/Makefile
new file mode 100644
index 00000000000..33dc49fcdea
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/acid/Makefile
@@ -0,0 +1,10 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := acid
+
+# Source files of non-templated classes (.C files)
+SOURCES = acid.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/acid2/Makefile b/storage/ndb/test/ndbapi/old_dirs/acid2/Makefile
new file mode 100644
index 00000000000..69c9d409b9e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/acid2/Makefile
@@ -0,0 +1,10 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := acid2
+
+# Source files of non-templated classes (.C files)
+SOURCES = acid2.cpp TraceNdbApi.cpp VerifyNdbApi.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/acid2/TraceNdbApi.hpp b/storage/ndb/test/ndbapi/old_dirs/acid2/TraceNdbApi.hpp
new file mode 100644
index 00000000000..2bd4eab6b70
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/acid2/TraceNdbApi.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 TraceNdbApi_hpp
+#define TraceNdbApi_hpp
+
+
+class CTraceNdbSchemaOp : public NdbSchemaOp
+{
+public:
+ int createTable(const char* aTableName);
+ int createAttribute(const char* aAttrName, KeyType aTupleyKey);
+};
+
+
+
+class CTraceNdbSchemaCon : public NdbSchemaCon
+{
+public:
+ CTraceNdbSchemaOp* getNdbSchemaOp();
+ int execute();
+};
+
+
+
+class CTraceNdbRecAttr : public NdbRecAttr
+{
+public:
+ Uint32 u_32_value();
+};
+
+
+class CTraceNdbOperation : public NdbOperation
+{
+public:
+ int insertTuple();
+ int updateTuple();
+ int interpretedUpdateTuple();
+ int readTuple();
+ int readTupleExclusive();
+ int deleteTuple();
+ int equal(const char* anAttrName, Uint32 aValue);
+ int setValue(const char* anAttrName, Uint32 aValue);
+ int incValue(const char* anAttrName, Uint32 aValue);
+ CTraceNdbRecAttr* getValue(const char* anAttrName);
+
+};
+
+
+class CTraceNdbIndexOperation : public NdbIndexOperation
+{
+public:
+ int insertTuple();
+ int updateTuple();
+ int interpretedUpdateTuple();
+ int readTuple();
+ int readTupleExclusive();
+ int deleteTuple();
+ int equal(const char* anAttrName, Uint32 aValue);
+ int setValue(const char* anAttrName, Uint32 aValue);
+ int incValue(const char* anAttrName, Uint32 aValue);
+ CTraceNdbRecAttr* getValue(const char* anAttrName);
+};
+
+
+
+class CTraceNdbConnection : public NdbConnection
+{
+public:
+ CTraceNdbOperation* getNdbOperation(const char* aTableName);
+ CTraceNdbIndexOperation* getNdbIndexOperation(const char* anIndexName, const char* aTableName);
+
+ int execute(ExecType aTypeOfExec);
+
+ int execute_ok(ExecType aTypeOfExec)
+ {
+ return execute(aTypeOfExec);
+ };
+
+ const NdbError & getNdbError(void) const;
+};
+
+
+
+class CTraceNdbDictionary : public NdbDictionary
+{
+public:
+ class CTraceTable : public Table
+ {
+ };
+
+ class CTraceIndex : public Index
+ {
+ };
+
+ class CTraceColumn : public Column
+ {
+ };
+
+ int createTable(const CTraceTable &);
+ int createIndex(const CTraceIndex &);
+};
+
+
+
+class CTraceNdb : public Ndb
+{
+public:
+ CTraceNdb(const char* aDataBase);
+ CTraceNdbSchemaCon* startSchemaTransaction();
+ void closeSchemaTransaction(CTraceNdbSchemaCon* aSchemaCon);
+ CTraceNdbConnection* startTransaction();
+ void closeTransaction(CTraceNdbConnection* aConnection);
+};
+
+
+
+#endif // TraceNdbApi_hpp
diff --git a/storage/ndb/test/ndbapi/old_dirs/acid2/VerifyNdbApi.hpp b/storage/ndb/test/ndbapi/old_dirs/acid2/VerifyNdbApi.hpp
new file mode 100644
index 00000000000..4a5b8cc8111
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/acid2/VerifyNdbApi.hpp
@@ -0,0 +1,466 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 VerifyNdbApi_hpp
+#define VerifyNdbApi_hpp
+
+
+class CVerifyNdbSchemaOp : public NdbSchemaOp
+{
+public:
+ int createTable(const char* aTableName)
+ {
+ int i = NdbSchemaOp::createTable(aTableName);
+ VerifyInt(i, "createTable");
+ return i;
+ };
+
+ int createAttribute(const char* aAttrName, KeyType aTupleyKey)
+ {
+ int i = NdbSchemaOp::createAttribute(aAttrName, aTupleyKey);
+ VerifyInt(i, "createAttribute");
+ return i;
+ };
+
+private:
+ void VerifyInt(const int i, const char* szMethod)
+ {
+ if(i)
+ {
+ VerifyIntError(i, szMethod);
+ }
+ }
+
+ void VerifyIntError(const int i, const char* szMethod);
+};
+
+
+
+class CVerifyNdbSchemaCon : public NdbSchemaCon
+{
+public:
+ CVerifyNdbSchemaOp* getNdbSchemaOp()
+ {
+ NdbSchemaOp* p = NdbSchemaCon::getNdbSchemaOp();
+ VerifyPtr(p, "getNdbSchemaOp");
+ return (CVerifyNdbSchemaOp*)p;
+ };
+
+ int execute()
+ {
+ int i = NdbSchemaCon::execute();
+ VerifyInt(i, "execute");
+ return i;
+ };
+
+private:
+ void VerifyInt(const int i, const char* szMethod)
+ {
+ if(i)
+ {
+ VerifyIntError(i, szMethod);
+ }
+ }
+
+ void VerifyPtr(void* p, const char* szMethod)
+ {
+ if(!p)
+ {
+ VerifyPtrError(p, szMethod);
+ }
+ }
+
+ void VerifyIntError(const int i, const char* szMethod);
+ void VerifyPtrError(void* p, const char* szMethod);
+};
+
+
+
+class CVerifyNdbRecAttr : public NdbRecAttr
+{
+public:
+ Uint32 u_32_value()
+ {
+ Uint32 n = NdbRecAttr::u_32_value();
+ VerifyValue("u_32_value");
+ return n;
+ };
+
+private:
+ void VerifyValue(const char* szMethod)
+ {
+ int iNull = NdbRecAttr::isNULL();
+ if(iNull)
+ {
+ VerifyValueError(iNull, szMethod);
+ }
+ };
+
+ void VerifyValueError(const int iNull, const char* szMethod);
+};
+
+
+class CVerifyNdbOperation : public NdbOperation
+{
+public:
+ int insertTuple()
+ {
+ int i = NdbOperation::insertTuple();
+ VerifyInt(i, "insertTuple");
+ return i;
+ };
+
+ int updateTuple()
+ {
+ int i = NdbOperation::updateTuple();
+ VerifyInt(i, "updateTuple");
+ return i;
+ };
+
+ int interpretedUpdateTuple()
+ {
+ int i = NdbOperation::interpretedUpdateTuple();
+ VerifyInt(i, "interpretedUpdateTuple");
+ return i;
+ }
+
+ int readTuple()
+ {
+ int i = NdbOperation::readTuple();
+ VerifyInt(i, "readTuple");
+ return i;
+ }
+
+ int readTupleExclusive()
+ {
+ int i = NdbOperation::readTupleExclusive();
+ VerifyInt(i, "readTupleExclusive");
+ return i;
+ }
+
+ int deleteTuple()
+ {
+ int i = NdbOperation::deleteTuple();
+ VerifyInt(i, "deleteTuple");
+ return i;
+ }
+
+ int equal(const char* anAttrName, Uint32 aValue)
+ {
+ int i = NdbOperation::equal(anAttrName, aValue);
+ VerifyInt(i, "equal");
+ return i;
+ }
+
+ int setValue(const char* anAttrName, Uint32 aValue)
+ {
+ int i = NdbOperation::setValue(anAttrName, aValue);
+ VerifyInt(i, "setValue");
+ return i;
+ }
+
+ int incValue(const char* anAttrName, Uint32 aValue)
+ {
+ int i = NdbOperation::incValue(anAttrName, aValue);
+ VerifyInt(i, "incValue");
+ return i;
+ }
+
+ CVerifyNdbRecAttr* getValue(const char* anAttrName)
+ {
+ NdbRecAttr* p = NdbOperation::getValue(anAttrName);
+ VerifyPtr(p, "getValue");
+ return (CVerifyNdbRecAttr*)p;
+ }
+
+
+private:
+ void VerifyInt(const int i, const char* szMethod)
+ {
+ if(i)
+ {
+ VerifyIntError(i, szMethod);
+ }
+ }
+
+ void VerifyPtr(void* p, const char* szMethod)
+ {
+ if(!p)
+ {
+ VerifyPtrError(p, szMethod);
+ }
+ }
+
+ void VerifyIntError(const int i, const char* szMethod);
+ void VerifyPtrError(void* p, const char* szMethod);
+};
+
+
+class CVerifyNdbIndexOperation : public NdbIndexOperation
+{
+public:
+ int insertTuple()
+ {
+ int i = NdbIndexOperation::insertTuple();
+ VerifyInt(i, "insertTuple");
+ return i;
+ };
+
+ int updateTuple()
+ {
+ int i = NdbIndexOperation::updateTuple();
+ VerifyInt(i, "updateTuple");
+ return i;
+ };
+
+ int interpretedUpdateTuple()
+ {
+ int i = NdbIndexOperation::interpretedUpdateTuple();
+ VerifyInt(i, "interpretedUpdateTuple");
+ return i;
+ }
+
+ int readTuple()
+ {
+ int i = NdbIndexOperation::readTuple();
+ VerifyInt(i, "readTuple");
+ return i;
+ }
+
+ int readTupleExclusive()
+ {
+ int i = NdbIndexOperation::readTupleExclusive();
+ VerifyInt(i, "readTupleExclusive");
+ return i;
+ }
+
+ int deleteTuple()
+ {
+ int i = NdbIndexOperation::deleteTuple();
+ VerifyInt(i, "deleteTuple");
+ return i;
+ }
+
+ int equal(const char* anAttrName, Uint32 aValue)
+ {
+ int i = NdbIndexOperation::equal(anAttrName, aValue);
+ VerifyInt(i, "equal");
+ return i;
+ }
+
+ int setValue(const char* anAttrName, Uint32 aValue)
+ {
+ int i = NdbIndexOperation::setValue(anAttrName, aValue);
+ VerifyInt(i, "setValue");
+ return i;
+ }
+
+ int incValue(const char* anAttrName, Uint32 aValue)
+ {
+ int i = NdbIndexOperation::incValue(anAttrName, aValue);
+ VerifyInt(i, "incValue");
+ return i;
+ }
+
+ CVerifyNdbRecAttr* getValue(const char* anAttrName)
+ {
+ NdbRecAttr* p = NdbIndexOperation::getValue(anAttrName);
+ VerifyPtr(p, "getValue");
+ return (CVerifyNdbRecAttr*)p;
+ }
+
+
+private:
+ void VerifyInt(const int i, const char* szMethod)
+ {
+ if(i)
+ {
+ VerifyIntError(i, szMethod);
+ }
+ }
+
+ void VerifyPtr(void* p, const char* szMethod)
+ {
+ if(!p)
+ {
+ VerifyPtrError(p, szMethod);
+ }
+ }
+
+ void VerifyIntError(const int i, const char* szMethod);
+ void VerifyPtrError(void* p, const char* szMethod);
+};
+
+
+class CVerifyNdbConnection : public NdbConnection
+{
+public:
+ CVerifyNdbOperation* getNdbOperation(const char* aTableName)
+ {
+ NdbOperation* p = NdbConnection::getNdbOperation(aTableName);
+ VerifyPtr(p, "getNdbOperation");
+ return (CVerifyNdbOperation*)p;
+ }
+
+ CVerifyNdbIndexOperation* getNdbIndexOperation(const char* anIndexName, const char* aTableName)
+ {
+ NdbIndexOperation* p = NdbConnection::getNdbIndexOperation(anIndexName, aTableName);
+ VerifyPtr(p, "getNdbIndexOperation");
+ return (CVerifyNdbIndexOperation*)p;
+ }
+
+ int execute(ExecType aTypeOfExec)
+ {
+ int i = NdbConnection::execute(aTypeOfExec);
+ VerifyInt(i, "execute");
+ return i;
+ }
+
+ int execute_ok(ExecType aTypeOfExec)
+ {
+ int iExec = NdbConnection::execute(aTypeOfExec);
+ NdbError err = NdbConnection::getNdbError();
+ int iCode = err.code;
+ if(iExec
+ && ((aTypeOfExec==NoCommit && iCode!=0)
+ || (aTypeOfExec==Commit && iCode!=626 && iCode!=630)))
+ {
+ VerifyInt(iExec, "execute");
+ }
+ return iExec;
+ }
+
+
+private:
+ void VerifyInt(const int i, const char* szMethod)
+ {
+ if(i)
+ {
+ VerifyIntError(i, szMethod);
+ }
+ }
+
+ void VerifyPtr(void* p, const char* szMethod)
+ {
+ if(!p)
+ {
+ VerifyPtrError(p, szMethod);
+ }
+ }
+
+ void VerifyIntError(const int i, const char* szMethod);
+ void VerifyPtrError(void* p, const char* szMethod);
+};
+
+
+//class CVerifyTable : public NdbDictionary::Table
+//{
+//public:
+//};
+
+
+class CVerifyNdbDictionary : public NdbDictionary
+{
+public:
+ class CVerifyTable : public Table
+ {
+ public:
+ private:
+ };
+
+ class CVerifyIndex : public Index
+ {
+ public:
+ private:
+ };
+
+ class CVerifyColumn : public Column
+ {
+ public:
+ private:
+ };
+
+ int createTable(const CVerifyTable &);
+ int createIndex(const CVerifyIndex &);
+
+
+private:
+};
+
+
+class CVerifyNdb : public Ndb
+{
+public:
+ CVerifyNdb(const char* aDataBase)
+ : Ndb(aDataBase)
+ {
+ VerifyVoid("Ndb");
+ };
+
+ CVerifyNdbSchemaCon* startSchemaTransaction()
+ {
+ NdbSchemaCon* p = Ndb::startSchemaTransaction();
+ VerifyPtr(p, "startSchemaTransaction");
+ return (CVerifyNdbSchemaCon*)p;
+ };
+
+ void closeSchemaTransaction(CVerifyNdbSchemaCon* aSchemaCon)
+ {
+ Ndb::closeSchemaTransaction(aSchemaCon);
+ VerifyVoid("closeSchemaTransaction");
+ };
+
+ CVerifyNdbConnection* startTransaction()
+ {
+ NdbConnection* p = Ndb::startTransaction();
+ VerifyPtr(p, "startTransaction");
+ return (CVerifyNdbConnection*)p;
+ };
+
+ void closeTransaction(CVerifyNdbConnection* aConnection)
+ {
+ Ndb::closeTransaction(aConnection);
+ VerifyVoid("closeTransaction");
+ };
+
+
+private:
+ void VerifyPtr(void* p, const char* szMethod)
+ {
+ if(!p)
+ {
+ VerifyPtrError(p, szMethod);
+ }
+ }
+
+ void VerifyVoid(const char* szMethod)
+ {
+ NdbError err = Ndb::getNdbError();
+ int iCode = err.code;
+ if(iCode)
+ {
+ VerifyVoidError(iCode, szMethod);
+ }
+ }
+
+ void VerifyPtrError(void* p, const char* szMethod);
+ void VerifyVoidError(const int iCode, const char* szMethod);
+};
+
+
+
+#endif // VerifyNdbApi_hpp
diff --git a/storage/ndb/test/ndbapi/old_dirs/basicAsynch/Makefile b/storage/ndb/test/ndbapi/old_dirs/basicAsynch/Makefile
new file mode 100755
index 00000000000..802c5e5a2bd
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/basicAsynch/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := testBasicAsynch
+
+SOURCES := testBasicAsynch.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/bulk_copy/Makefile b/storage/ndb/test/ndbapi/old_dirs/bulk_copy/Makefile
new file mode 100644
index 00000000000..22c05b138b7
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/bulk_copy/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := bulk_copy
+
+SOURCES := bulk_copy.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/create_all_tabs/Makefile b/storage/ndb/test/ndbapi/old_dirs/create_all_tabs/Makefile
new file mode 100644
index 00000000000..58309807682
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/create_all_tabs/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := create_all_tabs
+
+# Source files of non-templated classes (.C files)
+SOURCES = create_all_tabs.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/create_tab/Makefile b/storage/ndb/test/ndbapi/old_dirs/create_tab/Makefile
new file mode 100644
index 00000000000..c2ea0b52b15
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/create_tab/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := create_tab
+
+# Source files of non-templated classes (.C files)
+SOURCES = create_tab.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/drop_all_tabs/Makefile b/storage/ndb/test/ndbapi/old_dirs/drop_all_tabs/Makefile
new file mode 100644
index 00000000000..96db0781417
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/drop_all_tabs/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := drop_all_tabs
+
+# Source files of non-templated classes (.C files)
+SOURCES = drop_all_tabs.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/flexAsynch/Makefile b/storage/ndb/test/ndbapi/old_dirs/flexAsynch/Makefile
new file mode 100644
index 00000000000..2c77c8e21df
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/flexAsynch/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := flexAsynch
+
+# Source files of non-templated classes (.C files)
+SOURCES = flexAsynch.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/flexBench/Makefile.am b/storage/ndb/test/ndbapi/old_dirs/flexBench/Makefile.am
new file mode 100644
index 00000000000..d4de4b92b60
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/flexBench/Makefile.am
@@ -0,0 +1,10 @@
+
+bin_PROGRAMS = flexBench
+
+flexBench_SOURCES = flexBench.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_ndbapitest.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
diff --git a/storage/ndb/test/ndbapi/old_dirs/flexBench/ndbplot.pl b/storage/ndb/test/ndbapi/old_dirs/flexBench/ndbplot.pl
new file mode 100755
index 00000000000..b16f6d5897d
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/flexBench/ndbplot.pl
@@ -0,0 +1,305 @@
+#! /usr/bin/perl
+
+use strict;
+use Getopt::Long;
+use Symbol;
+use Socket;
+
+my $progname = $0;
+$progname =~ s!^.*/|\.pl$!!g;
+my $defaultport = 27127;
+my $defaulttotal = 120;
+my $defaultsample = 5;
+my $defaultrange = 5000;
+
+sub printhelp {
+ print <<END;
+$progname -- plot ndb operation counts in X11 window
+usage: $progname [options]
+--help print this summary and exit
+--debug print lots of debug information
+--port N port number to listen on, default $defaultport
+--total N total time interval shown, default $defaulttotal seconds
+--sample N sample interval, default $defaultsample seconds
+--range N range (max ops per second), default $defaultrange
+--nopct show no percentages in graph titles
+--z "..." add X11/gnuplot options, for example:
+ --z "-bg grey80 -geometry 500x300" --z "-persist"
+END
+ exit(0);
+}
+
+# get options
+use vars qw(
+ $helpflag $debug $serverport $totaltime $sampletime $range $nopct
+ @zopts
+);
+$helpflag = 0;
+$debug = 0;
+$serverport = $defaultport;
+$totaltime = $defaulttotal;
+$sampletime = $defaultsample;
+$range = $defaultrange;
+$nopct = 0;
+@zopts = ();
+GetOptions(
+ 'help' => \$helpflag,
+ 'debug' => \$debug,
+ 'port=i' => \$serverport,
+ 'total=i' => \$totaltime,
+ 'sample=i' => \$sampletime,
+ 'range=i' => \$range,
+ 'nopct' => \$nopct,
+ 'z=s' => \@zopts,
+) or die "try: $progname -h\n";
+$helpflag && printhelp();
+
+# calculate number of data points
+my $samplecnt;
+$samplecnt = int($totaltime / $sampletime) + 1;
+$totaltime = ($samplecnt - 1) * $sampletime;
+warn "total time = $totaltime sec, sample time = $sampletime sec\n";
+
+# open gnuplot
+my $plotfile;
+sub openplot {
+ $plotfile = gensym();
+ if (! open($plotfile, "| gnuplot @zopts")) {
+ die "open plot: $!\n";
+ }
+ my $sav = select($plotfile);
+ $| = 1;
+ select($sav);
+ print $plotfile "clear\n";
+}
+
+# samples
+my @sample; # samples 0..$samplecnt in time order
+my $sampleready = 0; # samples 1..$samplecnt are ready (true/false)
+
+@sample = map({ start => 0 }, 0..$samplecnt);
+
+sub adddata {
+ my($node, $type, $value) = @_;
+ my $now = time;
+ my $s = $sample[0];
+ if ($now - $s->{start} >= $sampletime) {
+ unshift(@sample, {
+ start => $now,
+ total => 0,
+ });
+ $s = $sample[0];
+ pop(@sample); # delete oldest
+ $sampleready = 1;
+ }
+ # if no type then this is just a time tick
+ if ($type) {
+ $s->{$type} += $value;
+ $s->{total} += $value;
+ }
+}
+
+# data file name
+my $datadir;
+if ($ENV{NDB_BASE}) {
+ $datadir = "$ENV{NDB_BASE}/var/plot";
+} else {
+ $datadir = "/var/tmp";
+}
+(-d $datadir || mkdir($datadir, 0777))
+ or die "mkdir $datadir failed: $!\n";
+my $datafile = "$datadir/plot$$.dat";
+warn "writing plot data to $datafile\n";
+
+# refresh the plot
+sub plotsample {
+ my $fh = gensym();
+ if (! open($fh, ">$datafile")) {
+ die "$datafile: $!\n";
+ }
+ # sample 0 is never ready
+ my $currops = "";
+ my $currpct = {};
+ for (my $i = @sample; $i >= 1; $i--) {
+ my $s = $sample[$i];
+ if (! $s->{start}) { # initial empty sample
+ next;
+ }
+ printf $fh "%d", -($i - 1) * $sampletime;
+ printf $fh " %.0f", 1.01 * $s->{"total"} / $sampletime;
+ for my $k (qw(insert update select delete)) {
+ printf $fh " %.0f", $s->{$k} / $sampletime;
+ }
+ printf $fh "\n";
+ if ($i == 1) {
+ $currops = sprintf("%.0f", $s->{"total"} / $sampletime);
+ if (! $nopct && $currops > 0) {
+ $currpct->{"total"} = sprintf("%5s", "");
+ for my $k (qw(insert update select delete)) {
+ $currpct->{$k} = sprintf(" %3.0f%%",
+ 100.0 * $s->{$k} / $s->{"total"});
+ }
+ }
+ }
+ }
+ close($fh);
+ print $plotfile <<END;
+clear
+set title "ops/sec [ $currops ]"
+set xrange [@{[ -($totaltime-1) ]}:0]
+set yrange [0:$range]
+plot \\
+ '$datafile' \\
+ using 1:3 \\
+ title "insert$currpct->{insert}" \\
+ with lines lt 2, \\
+ '$datafile' \\
+ using 1:4 \\
+ title "update$currpct->{update}" \\
+ with lines lt 3, \\
+ '$datafile' \\
+ using 1:5 \\
+ title "select$currpct->{select}" \\
+ with lines lt 4, \\
+ '$datafile' \\
+ using 1:6 \\
+ title "delete$currpct->{delete}" \\
+ with lines lt 5, \\
+ '$datafile' \\
+ using 1:2 \\
+ title "total$currpct->{total}" \\
+ with lines lt 1 lw 2
+END
+}
+
+# set up server socket
+my $sock = gensym();
+if (! socket($sock, PF_INET, SOCK_STREAM, getprotobyname("tcp"))) {
+ die "socket: $!\n";
+}
+if (! setsockopt($sock, SOL_SOCKET, SO_REUSEADDR, pack("l*", 1))) {
+ die "setsockopt: $!\n";
+}
+if (! bind($sock, pack_sockaddr_in($serverport, INADDR_ANY))) {
+ die "bind: $!\n";
+}
+if (! listen($sock, SOMAXCONN)) {
+ die "listen: $!\n";
+}
+
+# bit vectors for select on server socket and clients
+my $readin = '';
+vec($readin, fileno($sock), 1) = 1;
+
+# clients
+my @client = ();
+my $clientid = 0;
+sub addclient {
+ my($conn) = @_;
+ my $c = {
+ conn => $conn,
+ data => "",
+ name => "client " . ++$clientid,
+ };
+ push(@client, $c);
+ vec($readin, fileno($c->{conn}), 1) = 1;
+ if (1 || $debug) {
+ warn "added $c->{name}\n";
+ }
+}
+sub deleteclient {
+ my($c) = @_;
+ @client = grep($_ ne $c, @client);
+ vec($readin, fileno($c->{conn}), 1) = 0;
+ shutdown($c->{conn}, 2);
+ if (1 || $debug) {
+ warn "deleted $c->{name}\n";
+ }
+}
+sub readclient {
+ my($c) = @_;
+ my $data;
+ my $n;
+ eval {
+ local $SIG{ALRM} = sub { die "timeout\n" };
+ alarm(5);
+ $n = sysread($c->{conn}, $data, 512);
+ alarm(0);
+ };
+ if ($@) {
+ chomp($@);
+ warn "$c->{name}: read: $@\n";
+ return undef;
+ }
+ if (!defined($n)) {
+ warn "$c->{name}: read: $!\n";
+ return undef;
+ }
+ $c->{data} .= $data;
+ if ($debug) {
+ warn "$c->{name}: read @{[ length($data) ]} bytes\n";
+ }
+ return $n;
+}
+sub processclient {
+ my($c) = @_;
+ my $i;
+ while (($i = index($c->{data}, "\n")) >= 0) {
+ my $line = substr($c->{data}, 0, $i);
+ $c->{data} = substr($c->{data}, $i+1);
+ my($node, $type, $value) = split(' ', $line);
+ if ($node !~ /^\d+$/) {
+ warn "$c->{name}: $line: bad node id\n";
+ next;
+ }
+ if ($type !~ /^(insert|update|read|delete|verify|verifydelete)$/) {
+ warn "$c->{name}: $line: bad type\n";
+ next;
+ }
+ if ($value !~ /^\d+$/) {
+ warn "$c->{name}: $line: bad value\n";
+ next;
+ }
+ if ($type eq "read") {
+ $type = "select";
+ }
+ adddata($node, $type, $value);
+ }
+}
+
+# main loop
+openplot();
+while (1) {
+ my $readout = '';
+ my $ret = select($readout = $readin, undef, undef, 1.0);
+ if (vec($readout, fileno($sock), 1)) {
+ my $conn = gensym();
+ if (! accept($conn, $sock)) {
+ warn "accept failed: $!\n";
+ } else {
+ addclient($conn);
+ }
+ }
+ for my $c (@client) {
+ if (vec($readout, fileno($c->{conn}), 1)) {
+ my $n = readclient($c);
+ if (! defined($n)) {
+ deleteclient($c);
+ } else {
+ processclient($c);
+ if ($n == 0) { # end of file
+ deleteclient($c);
+ }
+ }
+ }
+ }
+ adddata(); # keep clock ticking
+ if ($sampleready) {
+ if ($debug) {
+ warn "sample ready\n";
+ }
+ plotsample();
+ $sampleready = 0;
+ }
+}
+# vim: set sw=4:
diff --git a/storage/ndb/test/ndbapi/old_dirs/flexHammer/Makefile b/storage/ndb/test/ndbapi/old_dirs/flexHammer/Makefile
new file mode 100644
index 00000000000..c8e436fb7f5
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/flexHammer/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := flexHammer
+
+SOURCES := flexHammer.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/flexHammer/README b/storage/ndb/test/ndbapi/old_dirs/flexHammer/README
new file mode 100644
index 00000000000..556582aab96
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/flexHammer/README
@@ -0,0 +1,67 @@
+
+Executing flexHammer-tests automatically
+========================================
+
+
+It is possible to execute almost al the flexHammer-tests
+automatically. The procedure contains three steps:
+- increase the number of tabels (flexHammer -c number)
+- increase the number of threads (flexHammer -t number)
+- increase the number of records (flexHammer -r number)
+- increase the number of tabels and threads alternately
+
+Each of these steps are performed by the scripts test1.sh,
+test2.sh, test3.sh and test4.sh. Each test will start Ndb,
+execute the test and close Ndb again in order to execute
+each test in a 'clean' Ndb-environment. So make sure that
+there is no Ndb running when you start the test.
+
+
+1. Setup
+
+To perform the tests automatically, the following issues
+have to be taken care of:
+
+- be sure that you have a directory bin in your home-directory.
+ In this directory, you need to have a link 'runndb' to the
+ ndb executable. You can do this by executing a shell-command like:
+ ln -s ndb/Emulator/Main/ndb runndb
+ The script is not yet so far that it performs checks, so if
+ you forget about this, things will get messy.
+- In this directory you need a Ndb.cfg for a server-configuration.
+
+
+2. Command
+
+I assume you have Ndb and the API compiled or you use the
+'released' version. Compile flexHammer as usual with 'make'.
+Now you can start the tests by typing 'make test'. The
+execution of the test will take a while.
+
+
+3. Results
+
+The scripts will write their results in the file report.txt.
+The scripts will start with a short summary on the test. Then
+it will add 1 line documenting each run of flexHammer that is
+ececuted. Finally, it will print highest 'score'. The file
+report.txt is probably good enough to check in directly as
+testprotocol in ndb/test/docs/testprotocols.
+
+
+4. Log files.
+
+To make it possible to investigate errors, the output from
+the flexScan-run where the error occurred is stored in
+test1.log, test2.log, test3.log or test4.log respectively.
+They are overwritten each time you start 'make test'.
+
+
+HINT
+
+The number of iterations in each test-script is not directly
+limited by the number of attributes or the size of the
+attributes but by the number of tables that you are allowed
+to create. Probably this will be the error that occurs if
+you execute the test. You migh adjust the begin-values and
+the step-size in the individual scripts if you want.
diff --git a/storage/ndb/test/ndbapi/old_dirs/flexScan/Makefile b/storage/ndb/test/ndbapi/old_dirs/flexScan/Makefile
new file mode 100644
index 00000000000..78f9d481063
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/flexScan/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := flexScan
+
+SOURCES := flexScan.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/flexScan/README b/storage/ndb/test/ndbapi/old_dirs/flexScan/README
new file mode 100644
index 00000000000..cddbdea5336
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/flexScan/README
@@ -0,0 +1,66 @@
+
+Executing flexScan-tests automatically
+======================================
+
+
+It is possible to execute almost al the flexBench-tests
+automatically. The procedure contains three steps:
+- increase the number of attributes (flexScan -a number)
+- increase the size of attributes (flexScan -s number)
+- increase the number of threads (flexScan -t number)
+
+Each of these steps are performed by the scripts test1.sh
+test2.sh and test3.sh. Each test will start Ndb, execute
+the test and close Ndb again in order to execute each test
+in a 'clean' Ndb-environment. So make sure that there is
+no Ndb running when you start the test.
+
+
+1. Setup
+
+To perform the tests automatically, the following issues
+have to be taken care of:
+
+- be sure that you have a directory bin in your home-directory.
+ In this directory, you need to have a link 'runndb' to the
+ ndb executable. You can do this by executing a shell-command like:
+ ln -s ndb/Emulator/Main/ndb runndb
+ The script is not yet so far that it performs checks, so if
+ you forget about this, things will get messy.
+- In this directory you need a Ndb.cfg for a server-configuration.
+
+
+2. Command
+
+I assume you have Ndb and the API compiled or you use the
+'released' version. Compile flexScan as usual with 'make'.
+Now you can start the tests by typing 'make test'. The
+execution of the test will take a while.
+
+
+3. Results
+
+The scripts will write their results in the file report.txt.
+The scripts will start with a short summary on the test. Then
+it will add 1 line documenting each run of flexScan that is
+ececuted. Finally, it will print highest 'score'. The file
+report.txt is probably good enough to check in directly as
+testprotocol in ndb/test/docs/testprotocols.
+
+
+4. Log files.
+
+To make it possible to investigate errors, the output from
+the flexScan-run where the error occurred is stored in
+test1.log, test2.log or test3.log respectively. They are
+overwritten each time you start 'make test'.
+
+
+HINT
+
+The number of iterations in each test-script is not directly
+limited by the number of attributes or the size of the
+attributes but by the number of tables that you are allowed
+to create. Probably this will be the error that occurs if
+you execute the test. You migh adjust the begin-values and
+the step-size in the individual scripts if you want.
diff --git a/storage/ndb/test/ndbapi/old_dirs/flexTT/Makefile b/storage/ndb/test/ndbapi/old_dirs/flexTT/Makefile
new file mode 100644
index 00000000000..a63bd803d95
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/flexTT/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := flexTT
+
+# Source files of non-templated classes (.C files)
+SOURCES = flexTT.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/flexTimedAsynch/Makefile b/storage/ndb/test/ndbapi/old_dirs/flexTimedAsynch/Makefile
new file mode 100644
index 00000000000..e9995dbd16f
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/flexTimedAsynch/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := flexTimedAsynch
+
+# Source files of non-templated classes (.C files)
+SOURCES = flexTimedAsynch.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/flex_bench_mysql/Makefile b/storage/ndb/test/ndbapi/old_dirs/flex_bench_mysql/Makefile
new file mode 100644
index 00000000000..d2608526cae
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/flex_bench_mysql/Makefile
@@ -0,0 +1,15 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := flex_bench_mysql
+
+# Source files of non-templated classes (.C files)
+SOURCES = flex_bench_mysql.cpp
+
+CCFLAGS_LOC += -I$(call fixpath,$(NDB_TOP)/../include)
+BIN_TARGET_LIBS_DIRS += $(NDB_TOP)/../libmysql_r/.libs
+BIN_TARGET_LIBS += z mysqlclient_r
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/indexTest/Makefile b/storage/ndb/test/ndbapi/old_dirs/indexTest/Makefile
new file mode 100644
index 00000000000..d842e487ee5
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/indexTest/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := index
+
+SOURCES := index.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/indexTest2/Makefile b/storage/ndb/test/ndbapi/old_dirs/indexTest2/Makefile
new file mode 100644
index 00000000000..ad78fd51986
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/indexTest2/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := index2
+
+SOURCES := index2.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/interpreterInTup/Makefile b/storage/ndb/test/ndbapi/old_dirs/interpreterInTup/Makefile
new file mode 100644
index 00000000000..074adbf674a
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/interpreterInTup/Makefile
@@ -0,0 +1,10 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+
+BIN_TARGET := interpreterInTup
+
+SOURCES := interpreterInTup.cc
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/Makefile b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/Makefile
new file mode 100644
index 00000000000..af472b1589f
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/Makefile
@@ -0,0 +1,6 @@
+include .defs.mk
+
+DIRS := src async-src script
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/Makefile b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/Makefile
new file mode 100644
index 00000000000..744d6171139
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/Makefile
@@ -0,0 +1,8 @@
+include .defs.mk
+
+DIRS = \
+ user \
+ generator
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/generator/Makefile b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/generator/Makefile
new file mode 100644
index 00000000000..c1f84a3ef70
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/generator/Makefile
@@ -0,0 +1,13 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+SOURCES = mainAsyncGenerator.cpp asyncGenerator.cpp
+
+CCFLAGS_LOC := -I../include -I../../include
+
+BIN_TARGET := DbAsyncGenerator
+BIN_TARGET_ARCHIVES := lmc_AsyncUser
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/dbGenerator.h b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/dbGenerator.h
new file mode 100644
index 00000000000..2256498e151
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/dbGenerator.h
@@ -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 DBGENERATOR_H
+#define DBGENERATOR_H
+
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+
+#include "testData.h"
+#include "userInterface.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void asyncGenerator(ThreadData *d, int parallellism,
+ int millisSendPoll,
+ int minEventSendPoll,
+ int forceSendPoll);
+
+#ifdef __cplusplus
+}
+#endif
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+
+
+#endif /* DBGENERATOR_H */
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/testData.h b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/testData.h
new file mode 100644
index 00000000000..3db85e7342e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/testData.h
@@ -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 TESTDATA_H
+#define TESTDATA_H
+
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+#include <NdbTick.h>
+#include <NdbThread.h>
+#include <NDBT_Stats.hpp>
+#include <random.h>
+#include "testDefinitions.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+#define NUM_TRANSACTION_TYPES 5
+#define SESSION_LIST_LENGTH 1000
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+typedef struct {
+ SubscriberNumber subscriberNumber;
+ ServerId serverId;
+} SessionElement;
+
+typedef struct {
+ SessionElement list[SESSION_LIST_LENGTH];
+ unsigned int readIndex;
+ unsigned int writeIndex;
+ unsigned int numberInList;
+} SessionList;
+
+typedef struct {
+ unsigned int count;
+ unsigned int branchExecuted;
+ unsigned int rollbackExecuted;
+
+ /**
+ * Latency measures
+ */
+ NDB_TICKS startTime;
+ NDBT_Stats latency;
+ unsigned int latencyCounter;
+
+ inline void startLatency(){
+ if((latencyCounter & 127) == 127)
+ startTime = NdbTick_CurrentMillisecond();
+ }
+
+ inline void stopLatency(){
+ if((latencyCounter & 127) == 127){
+ const NDB_TICKS tmp = NdbTick_CurrentMillisecond() - startTime;
+ latency.addObservation(tmp);
+ }
+ latencyCounter++;
+ }
+} TransactionDefinition;
+
+typedef struct {
+ RandomSequence transactionSequence;
+ RandomSequence rollbackSequenceT4;
+ RandomSequence rollbackSequenceT5;
+
+ TransactionDefinition transactions[NUM_TRANSACTION_TYPES];
+
+ unsigned int totalTransactions;
+
+ double outerLoopTime;
+ double outerTps;
+
+ SessionList activeSessions;
+
+} GeneratorStatistics;
+
+typedef enum{
+ Runnable,
+ Running
+} RunState ;
+
+typedef struct {
+ SubscriberNumber number;
+ SubscriberSuffix suffix;
+ SubscriberName name;
+ Location location;
+ ChangedBy changed_by;
+ ChangedTime changed_time;
+ ServerId server_id;
+ ServerBit server_bit;
+ SessionDetails session_details;
+
+ GroupId group_id;
+ ActiveSessions sessions;
+ Permission permission;
+
+ unsigned int do_rollback;
+
+ unsigned int branchExecuted;
+ unsigned int sessionElement;
+} TransactionData ;
+
+typedef struct {
+ struct NdbThread* pThread;
+
+ unsigned long randomSeed;
+ unsigned long changedTime;
+
+ unsigned int warmUpSeconds;
+ unsigned int testSeconds;
+ unsigned int coolDownSeconds;
+
+ GeneratorStatistics generator;
+
+ /**
+ * For async execution
+ */
+ RunState runState;
+ double startTime;
+ TransactionData transactionData;
+ struct Ndb * pNDB;
+} ThreadData;
+
+/***************************************************************
+ * P U B L I C F U N C T I O N S *
+ ***************************************************************/
+
+/***************************************************************
+ * E X T E R N A L D A T A *
+ ***************************************************************/
+
+
+
+#endif /* TESTDATA_H */
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/userInterface.h b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/userInterface.h
new file mode 100644
index 00000000000..94bd1e80ab3
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/userInterface.h
@@ -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 DBINTERFACE_H
+#define DBINTERFACE_H
+
+/***************************************************************/
+/* I N C L U D E D F I L E S */
+/***************************************************************/
+
+#include "testDefinitions.h"
+#include "testData.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+/*-----------------------*/
+/* Default Database Name */
+/*-----------------------*/
+#define DEFAULTDB "TestDbClient"
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+typedef struct Ndb Ndb;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern void showTime();
+ extern double userGetTime(void);
+ extern Ndb *asyncDbConnect(int parallellism);
+ extern void asyncDbDisconnect(Ndb* pNDB);
+
+ extern void start_T1(Ndb * uh, ThreadData * data, int async);
+ extern void start_T2(Ndb * uh, ThreadData * data, int async);
+ extern void start_T3(Ndb * uh, ThreadData * data, int async);
+ extern void start_T4(Ndb * uh, ThreadData * data, int async);
+ extern void start_T5(Ndb * uh, ThreadData * data, int async);
+
+ extern void complete_T1(ThreadData * data);
+ extern void complete_T2(ThreadData * data);
+ extern void complete_T3(ThreadData * data);
+ extern void complete_T4(ThreadData * data);
+ extern void complete_T5(ThreadData * data);
+
+#ifdef __cplusplus
+}
+#endif
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+#endif /* DBINTERFACE_H */
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/Makefile b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/Makefile
new file mode 100644
index 00000000000..c0b532a8359
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+ARCHIVE_TARGET := lmc_AsyncUser
+
+SOURCES := userInterface.C ndb_async2.C
+
+CCFLAGS_LOC = -I../include -I../../include
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/macros.h b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/macros.h
new file mode 100644
index 00000000000..22b7f564490
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/macros.h
@@ -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 MACROS_H
+#define MACROS_H
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+
+#define ERROR(x) {ndbout_c((x));}
+#define ERROR1(x,y) {ndbout_c((x), (y));}
+#define ERROR2(x,y,z) {ndbout_c((x), (y), (z));}
+#define ERROR3(x,y,z,u) {ndbout_c((x), (y), (z), (u));}
+#define ERROR4(x,y,z,u,w) {ndbout_c((x), (y), (z), (u), (w));}
+
+#define INIT_RANDOM(x) srand48((x))
+#define UI_RANDOM(x) ((unsigned int)(lrand48()%(x)))
+
+#define ASSERT(cond, message) \
+ { if(!(cond)) { ERROR(message); exit(-1); }}
+
+#ifdef DEBUG_ON
+#define DEBUG(x) {ndbout_c((x));}
+#define DEBUG1(x,y) {ndbout_c((x), (y));}
+#define DEBUG2(x,y,z) {ndbout_c((x), (y), (z));}
+#define DEBUG3(x,y,z,u) {ndbout_c((x), (y), (z), (u));}
+#define DEBUG4(x,y,z,u,w) {ndbout_c((x), (y), (z), (u), (w));}
+#define DEBUG5(x,y,z,u,w, v) {ndbout_c((x), (y), (z), (u), (w), (v));}
+#else
+#define DEBUG(x)
+#define DEBUG1(x,y)
+#define DEBUG2(x,y,z)
+#define DEBUG3(x,y,z,u)
+#define DEBUG4(x,y,z,u,w)
+#define DEBUG5(x,y,z,u,w, v)
+#endif
+
+#endif
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/ndb_error.hpp b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/ndb_error.hpp
new file mode 100644
index 00000000000..9e6c5e55e73
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/ndb_error.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 NDB_ERROR_H
+#define NDB_ERROR_H
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+#include "userInterface.h"
+#include <NdbError.hpp>
+
+inline
+void
+CHECK_ALLOWED_ERROR(const char * str,
+ const ThreadData * td,
+ const struct NdbError & error){
+
+ char buf[100];
+ snprintf(buf, sizeof(buf), "subscriber = %.*s ",
+ SUBSCRIBER_NUMBER_LENGTH,
+ td->transactionData.number);
+ ndbout << str << " " << error << endl
+ << buf;
+ showTime();
+
+ switch(error.classification) {
+ case NdbError::TimeoutExpired:
+ case NdbError::OverloadError:
+ case NdbError::TemporaryResourceError:
+ case NdbError::NodeRecoveryError:
+ break;
+ default:
+ if(error.status != NdbError::TemporaryError)
+ exit(-1);
+ }
+}
+
+inline
+void
+CHECK_NULL(void * null,
+ const char * str,
+ const ThreadData * td,
+ const struct NdbError & err){
+ if(null == 0){
+ CHECK_ALLOWED_ERROR(str, td, err);
+ exit(-1);
+ }
+}
+
+#endif
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/bin/.empty b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/bin/.empty
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/bin/.empty
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/include/ndb_schema.hpp b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/include/ndb_schema.hpp
new file mode 100644
index 00000000000..af08bc2eecd
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/include/ndb_schema.hpp
@@ -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 */
+
+#ifndef NDB_SCHEMA_H
+#define NDB_SCHEMA_H
+
+#include "testDefinitions.h"
+
+#define SUBSCRIBER_TABLE "SUBSCRIBER"
+#define SUBSCRIBER_NUMBER "NUMBER"
+#define SUBSCRIBER_LOCATION "LOCATION"
+#define SUBSCRIBER_NAME "NAME"
+#define SUBSCRIBER_GROUP "GROUP_ID"
+#define SUBSCRIBER_SESSIONS "SESSIONS"
+#define SUBSCRIBER_CHANGED_BY "CHANGED_BY"
+#define SUBSCRIBER_CHANGED_TIME "CHANGED_TIME"
+
+#define SERVER_TABLE "SERVER"
+#define SERVER_ID "SERVER_ID"
+#define SERVER_SUBSCRIBER_SUFFIX "SUFFIX"
+#define SERVER_NAME "NAME"
+#define SERVER_READS "NO_OF_READ"
+#define SERVER_INSERTS "NO_OF_INSERT"
+#define SERVER_DELETES "NO_OF_DELETE"
+
+#define GROUP_TABLE "GROUP"
+#define GROUP_ID "GROUP_ID"
+#define GROUP_NAME "GROUP_NAME"
+#define GROUP_ALLOW_READ "ALLOW_READ"
+#define GROUP_ALLOW_INSERT "ALLOW_INSERT"
+#define GROUP_ALLOW_DELETE "ALLOW_DELETE"
+
+#define SESSION_TABLE "SESSION"
+#define SESSION_SERVER "SERVER_ID"
+#define SESSION_SUBSCRIBER "NUMBER"
+#define SESSION_DATA "DATA"
+
+/** Numbers */
+
+#define IND_SUBSCRIBER_NUMBER (unsigned)0
+#define IND_SUBSCRIBER_NAME (unsigned)1
+#define IND_SUBSCRIBER_GROUP (unsigned)2
+#define IND_SUBSCRIBER_LOCATION (unsigned)3
+#define IND_SUBSCRIBER_SESSIONS (unsigned)4
+#define IND_SUBSCRIBER_CHANGED_BY (unsigned)5
+#define IND_SUBSCRIBER_CHANGED_TIME (unsigned)6
+
+#define IND_SERVER_SUBSCRIBER_SUFFIX (unsigned)0
+#define IND_SERVER_ID (unsigned)1
+#define IND_SERVER_NAME (unsigned)2
+#define IND_SERVER_READS (unsigned)3
+#define IND_SERVER_INSERTS (unsigned)4
+#define IND_SERVER_DELETES (unsigned)5
+
+#define IND_GROUP_ID (unsigned)0
+#define IND_GROUP_NAME (unsigned)1
+#define IND_GROUP_ALLOW_READ (unsigned)2
+#define IND_GROUP_ALLOW_INSERT (unsigned)3
+#define IND_GROUP_ALLOW_DELETE (unsigned)4
+
+#define IND_SESSION_SUBSCRIBER (unsigned)0
+#define IND_SESSION_SERVER (unsigned)1
+#define IND_SESSION_DATA (unsigned)2
+
+#endif
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/include/testDefinitions.h b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/include/testDefinitions.h
new file mode 100644
index 00000000000..2f4aeb30975
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/include/testDefinitions.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 */
+
+#ifndef TESTDEFINITIONS_H
+#define TESTDEFINITIONS_H
+
+/***************************************************************/
+/* I N C L U D E D F I L E S */
+/***************************************************************/
+
+#include <ndb_types.h>
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+#define OP_PER_TRANS 200
+#define NO_OF_SUBSCRIBERS 500000
+#define NO_OF_GROUPS 100
+#define NO_OF_SERVERS 20
+
+#define SUBSCRIBER_NUMBER_LENGTH 12
+#define SUBSCRIBER_NUMBER_SUFFIX_LENGTH 2
+
+#define SUBSCRIBER_NAME_LENGTH 32
+#define CHANGED_BY_LENGTH 32
+#define CHANGED_TIME_LENGTH 32
+#define SESSION_DETAILS_LENGTH 2000
+#define SERVER_NAME_LENGTH 32
+#define GROUP_NAME_LENGTH 32
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+#define PADDING 4
+
+typedef char SubscriberNumber[SUBSCRIBER_NUMBER_LENGTH];
+typedef char SubscriberSuffix[SUBSCRIBER_NUMBER_SUFFIX_LENGTH + 2];
+typedef char SubscriberName[SUBSCRIBER_NAME_LENGTH];
+typedef char ServerName[SERVER_NAME_LENGTH];
+typedef char GroupName[GROUP_NAME_LENGTH];
+typedef char ChangedBy[CHANGED_BY_LENGTH];
+typedef char ChangedTime[CHANGED_TIME_LENGTH];
+typedef char SessionDetails[SESSION_DETAILS_LENGTH];
+typedef Uint32 ServerId;
+typedef Uint32 ServerBit;
+typedef Uint32 GroupId;
+typedef Uint32 Location;
+typedef Uint32 Permission;
+
+typedef Uint32 Counter;
+typedef Uint32 ActiveSessions;
+typedef unsigned int BranchExecuted;
+typedef unsigned int DoRollback;
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+
+
+#endif /* TESTDEFINITIONS_H */
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/lib/.empty b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/lib/.empty
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/lib/.empty
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/Makefile b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/Makefile
new file mode 100644
index 00000000000..240b5957573
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/Makefile
@@ -0,0 +1,5 @@
+include .defs.mk
+
+SOURCES.sh := async-lmc-bench.sh async-lmc-bench-l.sh async-lmc-bench-p10.sh async-lmc-bench-l-p10.sh
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-l-p10.sh b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-l-p10.sh
new file mode 100755
index 00000000000..1ce3969f9fb
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-l-p10.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+DbCreate -l
+ret=$?
+if [ $ret -ne 0 ]
+then
+ echo "DbCreate failed"
+ exit $ret
+fi
+
+DbAsyncGenerator -time 300 -p 10 $*
+ret=$?
+exit $ret
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-l.sh b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-l.sh
new file mode 100755
index 00000000000..a5de71395c4
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-l.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+DbCreate -l
+ret=$?
+if [ $ret -ne 0 ]
+then
+ echo "DbCreate failed"
+ exit $ret
+fi
+
+DbAsyncGenerator -time 300 $*
+ret=$?
+exit $ret
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-p10.sh b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-p10.sh
new file mode 100755
index 00000000000..92c853cdd86
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-p10.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+DbCreate
+ret=$?
+if [ $ret -ne 0 ]
+then
+ echo "DbCreate failed"
+ exit $ret
+fi
+
+DbAsyncGenerator -time 300 -p 10 $*
+ret=$?
+exit $ret
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench.sh b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench.sh
new file mode 100755
index 00000000000..da8e9d9bf42
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+DbCreate
+ret=$?
+if [ $ret -ne 0 ]
+then
+ echo "DbCreate failed"
+ exit $ret
+fi
+
+DbAsyncGenerator -time 300 $*
+ret=$?
+exit $ret
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/Makefile b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/Makefile
new file mode 100644
index 00000000000..ae7fac9c49b
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/Makefile
@@ -0,0 +1,8 @@
+include .defs.mk
+
+DIRS = \
+ user \
+ populator \
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/README b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/README
new file mode 100644
index 00000000000..e81c8ba0051
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/README
@@ -0,0 +1,8 @@
+
+Note that you have to use gnumake to build
+
+On ndbs05:
+use 'gmake' instead of 'make'
+
+On hfXXX:
+do 'module add make'
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/Makefile b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/Makefile
new file mode 100644
index 00000000000..143d9ba655e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/Makefile
@@ -0,0 +1,17 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+SOURCES = mainGenerator.c dbGenerator.c
+
+CCFLAGS_LOC := -I../include -I../../include
+
+OBJECTS = \
+ mainGenerator.o\
+ dbGenerator.o
+
+BIN_TARGET := DbGenerator
+BIN_TARGET_ARCHIVES := lmc_User
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/dbGenerator.c b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/dbGenerator.c
new file mode 100644
index 00000000000..7484c7647f5
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/dbGenerator.c
@@ -0,0 +1,543 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 "dbGenerator.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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+static void getRandomSubscriberNumber(SubscriberNumber number);
+static void getRandomServerId(ServerId *serverId);
+static void getRandomChangedBy(ChangedBy changedBy);
+static void getRandomChangedTime(ChangedTime changedTime);
+
+static void clearTransaction(TransactionDefinition *trans);
+static void initGeneratorStatistics(GeneratorStatistics *gen);
+
+static void doOneTransaction(UserHandle *uh, GeneratorStatistics *gen);
+static void doTransaction_T1(UserHandle *uh, GeneratorStatistics *gen);
+static void doTransaction_T2(UserHandle *uh, GeneratorStatistics *gen);
+static void doTransaction_T3(UserHandle *uh, GeneratorStatistics *gen);
+static void doTransaction_T4(UserHandle *uh, GeneratorStatistics *gen);
+static void doTransaction_T5(UserHandle *uh, GeneratorStatistics *gen);
+
+/***************************************************************
+* L O C A L D A T A *
+***************************************************************/
+
+static SequenceValues transactionDefinition[] = {
+ {25, 1},
+ {25, 2},
+ {20, 3},
+ {15, 4},
+ {15, 5},
+ {0, 0}
+};
+
+static SequenceValues rollbackDefinition[] = {
+ {98, 0},
+ {2 , 1},
+ {0, 0}
+};
+
+static int maxsize = 0;
+
+/***************************************************************
+* 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 getRandomSubscriberNumber(SubscriberNumber number)
+{
+ uint32 tmp;
+ char sbuf[SUBSCRIBER_NUMBER_LENGTH + 1];
+ tmp = myRandom48(NO_OF_SUBSCRIBERS);
+ sprintf(sbuf, "%.*d", SUBSCRIBER_NUMBER_LENGTH, tmp);
+ memcpy(number, sbuf, SUBSCRIBER_NUMBER_LENGTH);
+}
+
+static void getRandomServerId(ServerId *serverId)
+{
+ *serverId = myRandom48(NO_OF_SERVERS);
+}
+
+static void getRandomChangedBy(ChangedBy changedBy)
+{
+ memset(changedBy, myRandom48(26)+'A', CHANGED_BY_LENGTH);
+ changedBy[CHANGED_BY_LENGTH] = 0;
+}
+
+static void getRandomChangedTime(ChangedTime changedTime)
+{
+ memset(changedTime, myRandom48(26)+'A', CHANGED_TIME_LENGTH);
+ changedTime[CHANGED_TIME_LENGTH] = 0;
+}
+
+static void clearTransaction(TransactionDefinition *trans)
+{
+ trans->benchTime = 0.0;
+ trans->count = 0;
+ trans->branchExecuted = 0;
+ trans->rollbackExecuted = 0;
+}
+
+static int listFull(SessionList *list)
+{
+ return(list->numberInList == SESSION_LIST_LENGTH);
+}
+
+static int listEmpty(SessionList *list)
+{
+ return(list->numberInList == 0);
+}
+
+static void insertSession(SessionList *list,
+ SubscriberNumber number,
+ ServerId serverId)
+{
+ SessionElement *e;
+ if( listFull(list) ) return;
+
+ e = &list->list[list->writeIndex];
+
+ strcpy(e->subscriberNumber, number);
+ e->serverId = serverId;
+
+ list->writeIndex = (list->writeIndex + 1) % SESSION_LIST_LENGTH;
+ list->numberInList++;
+
+if( list->numberInList > maxsize )
+maxsize = list->numberInList;
+}
+
+static SessionElement *getNextSession(SessionList *list)
+{
+ if( listEmpty(list) ) return(0);
+
+ return(&list->list[list->readIndex]);
+}
+
+static void deleteSession(SessionList *list)
+{
+ if( listEmpty(list) ) return;
+
+ list->readIndex = (list->readIndex + 1) % SESSION_LIST_LENGTH;
+ list->numberInList--;
+}
+
+static void initGeneratorStatistics(GeneratorStatistics *gen)
+{
+ int i;
+
+ if( initSequence(&gen->transactionSequence,
+ transactionDefinition) != 0 ) {
+ printf("could not set the transaction types\n");
+ exit(0);
+ }
+
+ if( initSequence(&gen->rollbackSequenceT4,
+ rollbackDefinition) != 0 ) {
+ printf("could not set the rollback sequence\n");
+ exit(0);
+ }
+
+ if( initSequence(&gen->rollbackSequenceT5,
+ rollbackDefinition) != 0 ) {
+ printf("could not set the rollback sequence\n");
+ exit(0);
+ }
+
+ for(i = 0; i < NUM_TRANSACTION_TYPES; i++ )
+ clearTransaction(&gen->transactions[i]);
+
+ gen->totalTransactions = 0;
+
+ gen->activeSessions.numberInList = 0;
+ gen->activeSessions.readIndex = 0;
+ gen->activeSessions.writeIndex = 0;
+}
+
+
+static void doOneTransaction(UserHandle *uh, GeneratorStatistics *gen)
+{
+ unsigned int transactionType;
+
+ transactionType = getNextRandom(&gen->transactionSequence);
+
+ switch(transactionType) {
+ case 1:
+ doTransaction_T1(uh, gen);
+ break;
+ case 2:
+ doTransaction_T2(uh, gen);
+ break;
+ case 3:
+ doTransaction_T3(uh, gen);
+ break;
+ case 4:
+ doTransaction_T4(uh, gen);
+ break;
+ case 5:
+ doTransaction_T5(uh, gen);
+ break;
+ default:
+ printf("Unknown transaction type: %d\n", transactionType);
+ }
+
+ gen->totalTransactions++;
+}
+
+static void doTransaction_T1(UserHandle *uh, GeneratorStatistics *gen)
+{
+ SubscriberNumber number;
+ Location new_location;
+ ChangedBy changed_by;
+ ChangedTime changed_time;
+
+ double start_time;
+ double end_time;
+ double transaction_time;
+
+ unsigned int tid = 0;
+
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ getRandomSubscriberNumber(number);
+ getRandomChangedBy(changed_by);
+ getRandomChangedTime(changed_time);
+ new_location = changed_by[0];
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ start_time = userGetTimeSync();
+ userTransaction_T1(uh,
+ number,
+ new_location,
+ changed_by,
+ changed_time);
+ end_time = userGetTimeSync();
+
+ /*-------------------*/
+ /* Update Statistics */
+ /*-------------------*/
+ transaction_time = end_time - start_time;
+ gen->transactions[tid].benchTime += transaction_time;
+ gen->transactions[tid].count++;
+}
+
+static void doTransaction_T2(UserHandle *uh, GeneratorStatistics *gen)
+{
+ SubscriberNumber number;
+ Location new_location;
+ ChangedBy changed_by;
+ ChangedTime changed_time;
+ SubscriberName subscriberName;
+
+ double start_time;
+ double end_time;
+ double transaction_time;
+
+ unsigned int tid = 1;
+
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ getRandomSubscriberNumber(number);
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ start_time = userGetTimeSync();
+ userTransaction_T2(uh,
+ number,
+ &new_location,
+ changed_by,
+ changed_time,
+ subscriberName);
+ end_time = userGetTimeSync();
+
+ /*-------------------*/
+ /* Update Statistics */
+ /*-------------------*/
+ transaction_time = end_time - start_time;
+ gen->transactions[tid].benchTime += transaction_time;
+ gen->transactions[tid].count++;
+}
+
+static void doTransaction_T3(UserHandle *uh, GeneratorStatistics *gen)
+{
+ SubscriberNumber number;
+ ServerId serverId;
+ ServerBit serverBit;
+ SessionDetails sessionDetails;
+ unsigned int branchExecuted;
+ SessionElement *se;
+
+ double start_time;
+ double end_time;
+ double transaction_time;
+
+ unsigned int tid = 2;
+
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ se = getNextSession(&gen->activeSessions);
+ if( se ) {
+ strcpy(number, se->subscriberNumber);
+ serverId = se->serverId;
+ }
+ else {
+ getRandomSubscriberNumber(number);
+ getRandomServerId(&serverId);
+ }
+
+ serverBit = 1 << serverId;
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ start_time = userGetTimeSync();
+ userTransaction_T3(uh,
+ number,
+ serverId,
+ serverBit,
+ sessionDetails,
+ &branchExecuted);
+ end_time = userGetTimeSync();
+
+ /*-------------------*/
+ /* Update Statistics */
+ /*-------------------*/
+ transaction_time = end_time - start_time;
+ gen->transactions[tid].benchTime += transaction_time;
+ gen->transactions[tid].count++;
+
+ if(branchExecuted)
+ gen->transactions[tid].branchExecuted++;
+}
+
+static void doTransaction_T4(UserHandle *uh, GeneratorStatistics *gen)
+{
+ SubscriberNumber number;
+ ServerId serverId;
+ ServerBit serverBit;
+ SessionDetails sessionDetails;
+ unsigned int branchExecuted;
+ unsigned int rollback;
+
+ double start_time;
+ double end_time;
+ double transaction_time;
+
+ unsigned int tid = 3;
+
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ getRandomSubscriberNumber(number);
+ getRandomServerId(&serverId);
+
+ serverBit = 1 << serverId;
+ rollback = getNextRandom(&gen->rollbackSequenceT4);
+
+ memset(sessionDetails, myRandom48(26)+'A', SESSION_DETAILS_LENGTH);
+ sessionDetails[SESSION_DETAILS_LENGTH] = 0;
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ start_time = userGetTimeSync();
+ userTransaction_T4(uh,
+ number,
+ serverId,
+ serverBit,
+ sessionDetails,
+ rollback,
+ &branchExecuted);
+ end_time = userGetTimeSync();
+
+ /*-------------------*/
+ /* Update Statistics */
+ /*-------------------*/
+ transaction_time = end_time - start_time;
+ gen->transactions[tid].benchTime += transaction_time;
+ gen->transactions[tid].count++;
+
+ if(branchExecuted)
+ gen->transactions[tid].branchExecuted++;
+ if(rollback)
+ gen->transactions[tid].rollbackExecuted++;
+
+ if( branchExecuted && !rollback ) {
+ insertSession(&gen->activeSessions, number, serverId);
+ }
+}
+
+static void doTransaction_T5(UserHandle *uh, GeneratorStatistics *gen)
+{
+ SubscriberNumber number;
+ ServerId serverId;
+ ServerBit serverBit;
+ unsigned int branchExecuted;
+ unsigned int rollback;
+ SessionElement *se;
+
+ double start_time;
+ double end_time;
+ double transaction_time;
+
+ unsigned int tid = 4;
+
+ /*----------------*/
+ /* Init arguments */
+ /*----------------*/
+ se = getNextSession(&gen->activeSessions);
+ if( se ) {
+ strcpy(number, se->subscriberNumber);
+ serverId = se->serverId;
+ }
+ else {
+ getRandomSubscriberNumber(number);
+ getRandomServerId(&serverId);
+ }
+
+ serverBit = 1 << serverId;
+ rollback = getNextRandom(&gen->rollbackSequenceT5);
+
+ /*-----------------*/
+ /* Run transaction */
+ /*-----------------*/
+ start_time = userGetTimeSync();
+ userTransaction_T5(uh,
+ number,
+ serverId,
+ serverBit,
+ rollback,
+ &branchExecuted);
+ end_time = userGetTimeSync();
+
+ /*-------------------*/
+ /* Update Statistics */
+ /*-------------------*/
+ transaction_time = end_time - start_time;
+ gen->transactions[tid].benchTime += transaction_time;
+ gen->transactions[tid].count++;
+
+ if(branchExecuted)
+ gen->transactions[tid].branchExecuted++;
+ if(rollback)
+ gen->transactions[tid].rollbackExecuted++;
+
+ if( se && !rollback) {
+ deleteSession(&gen->activeSessions);
+ }
+}
+
+/***************************************************************
+****************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+
+void dbGenerator(UserHandle *uh, ThreadData *data)
+{
+ GeneratorStatistics rg_warmUp;
+ GeneratorStatistics rg_coolDown;
+ GeneratorStatistics *st;
+ double periodStop;
+ double benchTimeStart;
+ double benchTimeEnd;
+ int i;
+
+ myRandom48Init(data->randomSeed);
+
+ initGeneratorStatistics(&rg_warmUp);
+ initGeneratorStatistics(&data->generator);
+ initGeneratorStatistics(&rg_coolDown);
+
+ /*----------------*/
+ /* warm up period */
+ /*----------------*/
+ periodStop = userGetTimeSync() + (double)data->warmUpSeconds;
+ while(userGetTimeSync() < periodStop){
+ doOneTransaction(uh, &rg_warmUp);
+ }
+
+ /*-------------------------*/
+ /* normal benchmark period */
+ /*-------------------------*/
+ benchTimeStart = userGetTimeSync();
+
+ if( data->numTransactions > 0 ) {
+ for(i = 0; i < data->numTransactions; i++)
+ doOneTransaction(uh, &data->generator);
+ }
+ else {
+ periodStop = benchTimeStart + (double)data->testSeconds;
+ while(userGetTimeSync() < periodStop)
+ doOneTransaction(uh, &data->generator);
+ }
+
+ benchTimeEnd = userGetTimeSync();
+
+ /*------------------*/
+ /* cool down period */
+ /*------------------*/
+ periodStop = benchTimeEnd + data->coolDownSeconds;
+ while(userGetTimeSync() < periodStop){
+ doOneTransaction(uh, &rg_coolDown);
+ }
+
+ /*---------------------------------------------------------*/
+ /* add the times for all transaction for inner loop timing */
+ /*---------------------------------------------------------*/
+ st = &data->generator;
+ st->innerLoopTime = 0.0;
+ for(i = 0 ; i < NUM_TRANSACTION_TYPES; i++) {
+ st->innerLoopTime += st->transactions[i].benchTime;
+ st->transactions[i].tps = getTps(st->transactions[i].count,
+ st->transactions[i].benchTime);
+ }
+
+ st->outerLoopTime = benchTimeEnd - benchTimeStart;
+ st->outerTps = getTps(st->totalTransactions, st->outerLoopTime);
+ st->innerTps = getTps(st->totalTransactions, st->innerLoopTime);
+
+ /* printf("maxsize = %d\n",maxsize); */
+}
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/dbGenerator.h b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/dbGenerator.h
new file mode 100644
index 00000000000..824688b6cf9
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/dbGenerator.h
@@ -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 */
+
+#ifndef DBGENERATOR_H
+#define DBGENERATOR_H
+
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+
+#include "testData.h"
+#include "userInterface.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void dbGenerator(UserHandle *uh, ThreadData *data);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+
+
+#endif /* DBGENERATOR_H */
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/mainGenerator.c b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/mainGenerator.c
new file mode 100644
index 00000000000..4a31db0b4e9
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/mainGenerator.c
@@ -0,0 +1,323 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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>
+#include <NdbSleep.h>
+#include <NdbThread.h>
+#include <NdbMain.h>
+
+#include "userInterface.h"
+#include "dbGenerator.h"
+
+
+static int numProcesses;
+static int numTransactions;
+static int numSeconds;
+static int numWarmSeconds;
+static char *testDbName;
+
+static ThreadData data[100];
+
+typedef struct {
+ pthread_t threadId;
+ int waitSeconds;
+ int toExit;
+}CheckpointData;
+
+static void usage(char *prog)
+{
+ char *progname;
+
+ /*--------------------------------------------*/
+ /* Get the name of the program (without path) */
+ /*--------------------------------------------*/
+ progname = strrchr(prog, '/');
+
+ if (progname == 0)
+ progname = prog;
+ else
+ ++progname;
+
+ fprintf(stderr,
+ "Usage: %s [-db <name>] [-proc <num>] [-transactions <num>] [-time <num>]\n"
+ " -db <name> Specifies the database name\n"
+ " default = '%s'\n"
+ " -proc <num> Specifies that <num> is the number of\n"
+ " concurrent processes. The default is 1.\n"
+ " -transactions <num> Specifies that <num> transactions will be\n"
+ " performed. The default is to do a specific time interval\n"
+ " -time <num> Specifies that the test will run for <num> sec.\n"
+ " The default is 10 sec\n"
+ " -warm <num> Specifies the warm-up/cooldown period of <num> sec.\n"
+ " The default is 10 sec\n",
+ progname, DEFAULTDB);
+ exit(1);
+}
+
+static void parse_args(int argc,char **argv)
+{
+ int i;
+
+ testDbName = DEFAULTDB;
+ numProcesses = 1;
+ numTransactions = 0;
+ numSeconds = 10;
+ numWarmSeconds = 10;
+
+ i = 1;
+ while (i < argc){
+ if (strcmp("-db",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ usage(argv[0]);
+ exit(1);
+ }
+ testDbName = argv[i + 1];
+ i += 2;
+ }
+ else if (strcmp("-proc",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ usage(argv[0]);
+ exit(1);
+ }
+ if (sscanf(argv[i+1], "%d", &numProcesses) == -1 ||
+ numProcesses <= 0 || numProcesses > 99) {
+ fprintf(stderr, "-proc flag requires a positive integer argument [1..99]\n");
+ usage(argv[0]);
+ exit(1);
+ }
+ i += 2;
+ }
+ else if (strcmp("-transactions",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ usage(argv[0]);
+ exit(1);
+ }
+ if (sscanf(argv[i+1], "%d", &numTransactions) == -1 ||
+ numTransactions < 0) {
+ fprintf(stderr, "-transactions flag requires a positive integer argument\n");
+ usage(argv[0]);
+ exit(1);
+ }
+ i += 2;
+ }
+ else if (strcmp("-time",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ usage(argv[0]);
+ exit(1);
+ }
+ if (sscanf(argv[i+1], "%d", &numSeconds) == -1 ||
+ numSeconds < 0) {
+ fprintf(stderr, "-time flag requires a positive integer argument\n");
+ usage(argv[0]);
+ exit(1);
+ }
+ i += 2;
+ }
+ else if (strcmp("-warm",argv[i]) == 0) {
+ if (i + 1 >= argc) {
+ usage(argv[0]);
+ exit(1);
+ }
+ if (sscanf(argv[i+1], "%d", &numWarmSeconds) == -1 ||
+ numWarmSeconds < 0) {
+ fprintf(stderr, "-warm flag requires a positive integer argument\n");
+ usage(argv[0]);
+ exit(1);
+ }
+ i += 2;
+ }
+ else
+ usage(argv[0]);
+ }
+}
+
+static void print_transaction(const char *header,
+ unsigned long totalCount,
+ TransactionDefinition *trans,
+ unsigned int printBranch,
+ unsigned int printRollback)
+{
+ double f;
+
+ printf(" %s: %d (%.2f%%) Time: %.4f sec TPS = %.0f\n",
+ header,
+ trans->count,
+ (double)trans->count / (double)totalCount * 100.0,
+ trans->benchTime,
+ trans->tps);
+
+ if( printBranch ){
+ if( trans->count == 0 )
+ f = 0.0;
+ else
+ f = (double)trans->branchExecuted / (double)trans->count * 100.0;
+ printf(" Branches Executed: %d (%.2f%%)\n", trans->branchExecuted, f);
+ }
+
+ if( printRollback ){
+ if( trans->count == 0 )
+ f = 0.0;
+ else
+ f = (double)trans->rollbackExecuted / (double)trans->count * 100.0;
+ printf(" Rollback Executed: %d (%.2f%%)\n", trans->rollbackExecuted, f);
+ }
+}
+
+void print_stats_sync(const char *title,
+ unsigned int length,
+ unsigned int transactionFlag,
+ GeneratorStatistics *gen,
+ int numProc)
+{
+ int i;
+ char buf[10];
+ char name[100];
+
+ name[0] = 0;
+ NdbHost_GetHostName(name);
+
+ printf("\n------ %s ------\n",title);
+ printf("Length : %d %s\n",
+ length,
+ transactionFlag ? "Transactions" : "sec");
+ printf("Processor : %s\n", name);
+ printf("Number of Proc: %d\n",numProc);
+ printf("\n");
+
+ if( gen->totalTransactions == 0 ) {
+ printf(" No Transactions for this test\n");
+ }
+ else {
+ for(i = 0; i < 5; i++) {
+ sprintf(buf, "T%d",i+1);
+ print_transaction(buf,
+ gen->totalTransactions,
+ &gen->transactions[i],
+ i >= 2,
+ i >= 3 );
+ }
+
+ printf("\n");
+ printf(" Overall Statistics:\n");
+ printf(" Transactions: %d\n", gen->totalTransactions);
+ printf(" Inner : %.0f TPS\n",gen->innerTps);
+ printf(" Outer : %.0f TPS\n",gen->outerTps);
+ printf("\n");
+ }
+}
+
+static void *threadRoutine(void *arg)
+{
+ UserHandle *uh;
+ ThreadData *data = (ThreadData *)arg;
+
+ uh = userDbConnect(0, testDbName);
+ NdbSleep_MilliSleep(data->threadId);
+ dbGenerator(uh,data);
+ userDbDisconnect(uh);
+
+ pthread_exit(0);
+ return(0);
+}
+
+NDB_COMMAND(DbGenerator, "DbGenerator", "DbGenerator", "DbGenerator", 16384)
+{
+ int i;
+ int j;
+ GeneratorStatistics stats;
+ GeneratorStatistics *p;
+ CheckpointData cd;
+
+ parse_args(argc,argv);
+
+ printf("\nStarting Test with %d process(es) for %d %s\n",
+ numProcesses,
+ numTransactions ? numTransactions : numSeconds,
+ numTransactions ? "Transactions" : "sec");
+ printf(" WarmUp/coolDown = %d sec\n", numWarmSeconds);
+
+ /*
+ cd.waitSeconds = 300;
+ cd.toExit = 0;
+ pthread_create(&cd.threadId, 0, checkpointRoutine, &cd);
+ */
+
+ for(i = 0; i < numProcesses; i++) {
+ data[i].warmUpSeconds = numWarmSeconds;
+ data[i].testSeconds = numSeconds;
+ data[i].coolDownSeconds = numWarmSeconds;
+ data[i].numTransactions = numTransactions;
+ data[i].randomSeed = time(0)+i;
+ j = pthread_create(&data[i].threadId, 0, threadRoutine, &data[i]);
+ if(j != 0){
+ perror("Failed to create thread");
+ }
+ }
+
+ /*--------------------------------*/
+ /* Wait for all processes to exit */
+ /*--------------------------------*/
+ for(i = 0; i < numProcesses; i++)
+ pthread_join(data[i].threadId, 0);
+
+ printf("All threads have finished\n");
+
+ cd.toExit = 1;
+
+ /*-------------------------------------------*/
+ /* Clear all structures for total statistics */
+ /*-------------------------------------------*/
+ stats.totalTransactions = 0;
+ stats.outerTps = 0.0;
+ stats.innerTps = 0.0;
+
+ for(i = 0; i < NUM_TRANSACTION_TYPES; i++ ) {
+ stats.transactions[i].benchTime = 0.0;
+ stats.transactions[i].count = 0;
+ stats.transactions[i].tps = 0.0;
+ stats.transactions[i].branchExecuted = 0;
+ stats.transactions[i].rollbackExecuted = 0;
+ }
+
+ /*--------------------------------*/
+ /* Add the values for all Threads */
+ /*--------------------------------*/
+ for(i = 0; i < numProcesses; i++) {
+ p = &data[i].generator;
+
+ stats.totalTransactions += p->totalTransactions;
+ stats.outerTps += p->outerTps;
+ stats.innerTps += p->innerTps;
+
+ for(j = 0; j < NUM_TRANSACTION_TYPES; j++ ) {
+ stats.transactions[j].benchTime += p->transactions[j].benchTime;
+ stats.transactions[j].count += p->transactions[j].count;
+ stats.transactions[j].tps += p->transactions[j].tps;
+ stats.transactions[j].branchExecuted += p->transactions[j].branchExecuted;
+ stats.transactions[j].rollbackExecuted += p->transactions[j].rollbackExecuted;
+ }
+ }
+
+ print_stats_sync("Test Results",
+ numTransactions ? numTransactions : numSeconds,
+ numTransactions ? 1 : 0,
+ &stats,
+ numProcesses);
+
+ return(0);
+}
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/include/testData.h b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/include/testData.h
new file mode 100644
index 00000000000..863c230502b
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/include/testData.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 TESTDATA_H
+#define TESTDATA_H
+
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+
+#include "testDefinitions.h"
+#include <random.h>
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+#define NUM_TRANSACTION_TYPES 5
+#define SESSION_LIST_LENGTH 1000
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+typedef struct {
+ SubscriberNumber subscriberNumber;
+ ServerId serverId;
+} SessionElement;
+
+typedef struct {
+ SessionElement list[SESSION_LIST_LENGTH];
+ unsigned int readIndex;
+ unsigned int writeIndex;
+ unsigned int numberInList;
+} SessionList;
+
+typedef struct {
+ double benchTime;
+ unsigned int count;
+ double tps;
+ unsigned int branchExecuted;
+ unsigned int rollbackExecuted;
+}TransactionDefinition;
+
+typedef struct {
+ RandomSequence transactionSequence;
+ RandomSequence rollbackSequenceT4;
+ RandomSequence rollbackSequenceT5;
+
+ TransactionDefinition transactions[NUM_TRANSACTION_TYPES];
+
+ unsigned int totalTransactions;
+
+ double innerLoopTime;
+ double innerTps;
+
+ double outerLoopTime;
+ double outerTps;
+
+ SessionList activeSessions;
+} GeneratorStatistics;
+
+typedef struct {
+ unsigned long threadId;
+ unsigned long randomSeed;
+
+ unsigned int warmUpSeconds;
+ unsigned int testSeconds;
+ unsigned int coolDownSeconds;
+ unsigned int numTransactions;
+
+ GeneratorStatistics generator;
+}ThreadData;
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+
+
+#endif /* TESTDATA_H */
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/include/userInterface.h b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/include/userInterface.h
new file mode 100644
index 00000000000..b70ded87756
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/include/userInterface.h
@@ -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 */
+
+#ifndef DBINTERFACE_H
+#define DBINTERFACE_H
+
+/***************************************************************/
+/* I N C L U D E D F I L E S */
+/***************************************************************/
+
+#include "testDefinitions.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+/*-----------------------*/
+/* Default Database Name */
+/*-----------------------*/
+#define DEFAULTDB "TestDbClient"
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+typedef struct {
+ struct Ndb * pNDB;
+ struct NdbConnection * pCurrTrans;
+} UserHandle;
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern double userGetTimeSync(void);
+
+extern void userCheckpoint(UserHandle *uh);
+
+extern UserHandle *userDbConnect(uint32 createDb, char *dbName);
+extern void userDbDisconnect(UserHandle *uh);
+
+extern int userDbInsertServer(UserHandle *uh,
+ ServerId serverId,
+ SubscriberSuffix suffix,
+ ServerName name);
+
+extern int userDbInsertSubscriber(UserHandle *uh,
+ SubscriberNumber number,
+ uint32 groupId,
+ SubscriberName name);
+
+extern int userDbInsertGroup(UserHandle *uh,
+ GroupId groupId,
+ GroupName name,
+ Permission allowRead,
+ Permission allowInsert,
+ Permission allowDelete);
+
+extern int userDbCommit(UserHandle *uh);
+extern int userDbRollback(UserHandle *uh);
+
+extern void userTransaction_T1(UserHandle *uh,
+ SubscriberNumber number,
+ Location new_location,
+ ChangedBy changed_by,
+ ChangedTime changed_time);
+
+extern void userTransaction_T2(UserHandle *uh,
+ SubscriberNumber number,
+ Location *new_location,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName);
+
+extern void userTransaction_T3(UserHandle *uh,
+ SubscriberNumber number,
+ ServerId server_id,
+ ServerBit server_bit,
+ SessionDetails session_details,
+ unsigned int *branch_executed);
+
+extern void userTransaction_T4(UserHandle *uh,
+ SubscriberNumber number,
+ ServerId server_id,
+ ServerBit server_bit,
+ SessionDetails session_details,
+ unsigned int do_rollback,
+ unsigned int *branch_executed);
+
+extern void userTransaction_T5(UserHandle *uh,
+ SubscriberNumber number,
+ ServerId server_id,
+ ServerBit server_bit,
+ unsigned int do_rollback,
+ unsigned int *branch_executed);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+#endif /* DBINTERFACE_H */
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/makevars.linux b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/makevars.linux
new file mode 100644
index 00000000000..a933669cfe7
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/makevars.linux
@@ -0,0 +1,6 @@
+PROJECT_TOP = /home/lmcritr/ecurlmc/users/lmcritr/dbBenchmark
+
+CFLAGS = -Wall -Wstrict-prototypes -O2 -I/opt/TimesTen4.1/32/include/ -I../include
+LDFLAGS = -L/opt/TimesTen4.1/32/lib -Wl,-rpath,/opt/TimesTen4.1/32/lib
+LIBS = -ltten -ldl
+LIBSCS = -lttclient -ldl
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/makevars.sparc b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/makevars.sparc
new file mode 100644
index 00000000000..57ab8bf982f
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/makevars.sparc
@@ -0,0 +1,15 @@
+
+include $(UAS_TOP)/Defs.mk
+
+LINK.CC = CC
+CC := /opt/as/forte6/SUNWspro/bin/cc
+export CC
+
+NDB_LIB = -L$(UAS_TOP)/API -lNDB_API \
+ -L$(UAS_OSPACE_LOC)/lib -lospace \
+ -lrt
+
+CFLAGS = -xO3 -I../include -mt
+LDFLAGS = $(NDB_LIB) -lpthread
+LIBS =
+LIBSCS =
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/Makefile b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/Makefile
new file mode 100644
index 00000000000..2107c948843
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/Makefile
@@ -0,0 +1,15 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := DbCreate
+BIN_TARGET_ARCHIVES := lmc_User
+
+CCFLAGS_LOC:= -I../include -I../../include
+
+SOURCES := \
+ mainPopulate.c\
+ dbPopulate.c
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/dbPopulate.c b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/dbPopulate.c
new file mode 100644
index 00000000000..42fbb52f3b2
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/dbPopulate.c
@@ -0,0 +1,244 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 "userInterface.h"
+
+#include "dbPopulate.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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+static void getRandomSubscriberData(int subscriberNo,
+ SubscriberNumber number,
+ SubscriberName name);
+
+static void populate(char *title,
+ int count,
+ void (*func)(UserHandle*,int),
+ UserHandle *uh);
+
+static void populateServers(UserHandle *uh, int count);
+static void populateSubscribers(UserHandle *uh, int count);
+static void populateGroups(UserHandle *uh, int count);
+
+/***************************************************************
+* L O C A L D A T A *
+***************************************************************/
+
+static SequenceValues permissionsDefinition[] = {
+ {90, 1},
+ {10, 0},
+ {0, 0}
+};
+
+/***************************************************************
+* 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 getRandomSubscriberData(int subscriberNo,
+ SubscriberNumber number,
+ SubscriberName name)
+{
+ char sbuf[SUBSCRIBER_NUMBER_LENGTH + 1];
+ sprintf(sbuf, "%.*d", SUBSCRIBER_NUMBER_LENGTH, subscriberNo);
+ memcpy(number, sbuf, SUBSCRIBER_NUMBER_LENGTH);
+
+ memset(name, myRandom48(26)+'A', SUBSCRIBER_NAME_LENGTH);
+}
+
+static void populate(char *title,
+ int count,
+ void (*func)(UserHandle*, int),
+ UserHandle *uh)
+{
+ ndbout_c("Populating %d '%s' ... ",count, title);
+ /* fflush(stdout); */
+ func(uh,count);
+ ndbout_c("done");
+}
+
+static void populateServers(UserHandle *uh, int count)
+{
+ int i, j;
+ int len;
+ char tmp[80];
+ int suffix_length = 1;
+ ServerName serverName;
+ SubscriberSuffix suffix;
+
+ int commitCount = 0;
+
+ for(i = 0; i < SUBSCRIBER_NUMBER_SUFFIX_LENGTH; i++)
+ suffix_length *= 10;
+
+ for(i = 0; i < count; i++) {
+ sprintf(tmp, "-Server %d-", i);
+
+ len = strlen(tmp);
+ for(j = 0; j < SERVER_NAME_LENGTH; j++){
+ serverName[j] = tmp[j % len];
+ }
+ /* serverName[j] = 0; not null-terminated */
+
+ for(j = 0; j < suffix_length; j++){
+ char sbuf[SUBSCRIBER_NUMBER_SUFFIX_LENGTH + 1];
+ sprintf(sbuf, "%.*d", SUBSCRIBER_NUMBER_SUFFIX_LENGTH, j);
+ memcpy(suffix, sbuf, SUBSCRIBER_NUMBER_SUFFIX_LENGTH);
+ userDbInsertServer(uh, i, suffix, serverName);
+ commitCount ++;
+ if((commitCount % OP_PER_TRANS) == 0)
+ userDbCommit(uh);
+ }
+ }
+ if((commitCount % OP_PER_TRANS) != 0)
+ userDbCommit(uh);
+}
+
+static void populateSubscribers(UserHandle *uh, int count)
+{
+ SubscriberNumber number;
+ SubscriberName name;
+ int i, j, k;
+ int res;
+
+ SequenceValues values[NO_OF_GROUPS+1];
+ RandomSequence seq;
+
+ for(i = 0; i < NO_OF_GROUPS; i++) {
+ values[i].length = 1;
+ values[i].value = i;
+ }
+
+ values[i].length = 0;
+ values[i].value = 0;
+
+ if( initSequence(&seq, values) != 0 ) {
+ ndbout_c("could not set the sequence of random groups");
+ exit(0);
+ }
+
+#define RETRIES 25
+
+ for(i = 0; i < count; i+= OP_PER_TRANS) {
+ for(j = 0; j<RETRIES; j++){
+ for(k = 0; k<OP_PER_TRANS && i+k < count; k++){
+ getRandomSubscriberData(i+k, number, name);
+ userDbInsertSubscriber(uh, number, getNextRandom(&seq), name);
+ }
+ res = userDbCommit(uh);
+ if(res == 0)
+ break;
+ if(res != 1){
+ ndbout_c("Terminating");
+ exit(0);
+ }
+ }
+ if(j == RETRIES){
+ ndbout_c("Terminating");
+ exit(0);
+ }
+ }
+}
+
+static void populateGroups(UserHandle *uh, int count)
+{
+ int i;
+ int j;
+ int len;
+ RandomSequence seq;
+ Permission allow[NO_OF_GROUPS];
+ ServerBit serverBit;
+ GroupName groupName;
+ char tmp[80];
+ int commitCount = 0;
+
+ if( initSequence(&seq, permissionsDefinition) != 0 ) {
+ ndbout_c("could not set the sequence of random permissions");
+ exit(0);
+ }
+
+ for(i = 0; i < NO_OF_GROUPS; i++)
+ allow[i] = 0;
+
+ for(i = 0; i < NO_OF_SERVERS; i++) {
+ serverBit = 1 << i;
+
+ for(j = 0; j < NO_OF_GROUPS; j++ ) {
+ if( getNextRandom(&seq) )
+ allow[j] |= serverBit;
+ }
+ }
+
+ for(i = 0; i < NO_OF_GROUPS; i++) {
+ sprintf(tmp, "-Group %d-", i);
+
+ len = strlen(tmp);
+
+ for(j = 0; j < GROUP_NAME_LENGTH; j++) {
+ groupName[j] = tmp[j % len];
+ }
+ /* groupName[j] = 0; not null-terminated */
+
+ userDbInsertGroup(uh,
+ i,
+ groupName,
+ allow[i],
+ allow[i],
+ allow[i]);
+ commitCount ++;
+ if((commitCount % OP_PER_TRANS) == 0)
+ userDbCommit(uh);
+ }
+ if((commitCount % OP_PER_TRANS) != 0)
+ userDbCommit(uh);
+}
+
+/***************************************************************
+****************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+void dbPopulate(UserHandle *uh)
+{
+ populate("servers", NO_OF_SERVERS, populateServers, uh);
+ populate("subscribers", NO_OF_SUBSCRIBERS, populateSubscribers, uh);
+ populate("groups", NO_OF_GROUPS, populateGroups, uh);
+}
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/dbPopulate.h b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/dbPopulate.h
new file mode 100644
index 00000000000..1916720e141
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/dbPopulate.h
@@ -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 DBPOPULATE_H
+#define DBPOPULATE_H
+
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+
+#include "userInterface.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void dbPopulate(UserHandle *uh);
+
+#ifdef __cplusplus
+}
+#endif
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+
+
+#endif /* DBPOPULATE_H */
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/mainPopulate.c b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/mainPopulate.c
new file mode 100644
index 00000000000..838ac8a7196
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/mainPopulate.c
@@ -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 "userInterface.h"
+#include "dbPopulate.h"
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <random.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int useTableLogging;
+int useIndexTables;
+#ifdef __cplusplus
+}
+#endif
+
+
+static
+void usage(const char *prog)
+{
+
+ ndbout_c(
+ "Usage: %s [-l]\n"
+ " -l Use logging and checkpointing on tables\n",
+ " -i Use index tables\n",
+ prog);
+
+ exit(1);
+}
+
+NDB_COMMAND(DbCreate, "DbCreate", "DbCreate", "DbCreate", 16384)
+{
+ int i;
+ UserHandle *uh;
+
+ useTableLogging = useIndexTables = 0;
+
+ for(i = 1; i<argc; i++){
+ if(strcmp(argv[i], "-l") == 0){
+ useTableLogging = 1;
+ } else if(strcmp(argv[i], "-i") == 0){
+ useIndexTables = 1;
+ } else {
+ usage(argv[0]);
+ return 0;
+ }
+ }
+
+ ndbout_c("Using %s tables and %s key storage",
+ useTableLogging ? "logging" : "temporary",
+ useIndexTables ? "index" : "normal");
+
+ myRandom48Init(0x3e6f);
+
+ uh = userDbConnect(1, 0);
+ dbPopulate(uh);
+ userDbDisconnect(uh);
+ return(0);
+}
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/Makefile b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/Makefile
new file mode 100644
index 00000000000..9bf229ac84c
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+ARCHIVE_TARGET := lmc_User
+
+SOURCES := userInterface.C
+
+CCFLAGS_LOC = -I../include -I../../include
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/localDbPrepare.c b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/localDbPrepare.c
new file mode 100644
index 00000000000..dd100507016
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/localDbPrepare.c
@@ -0,0 +1,648 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 "userInterface.h"
+#include "userHandle.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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+
+/***************************************************************
+* L O C A L D A T A *
+***************************************************************/
+
+/*----------------*/
+/* Transaction T1 */
+/*----------------*/
+static char *update_subscriber_stmnt = "update subscriber set \
+location = ?,changedBy = ?, changedTime = ? where subscriberNumber = ?";
+
+/*----------------*/
+/* Transaction T2 */
+/*----------------*/
+static char *read_subscriber_stmnt = "select subscriberName,location,\
+changedBy,changedTime from subscriber where subscriberNumber = ? for update";
+
+/*----------------*/
+/* Transaction T3 */
+/*----------------*/
+static char *read_subscriber_session_stmnt = "select activeSessions,groupId,\
+changedBy,changedTime from subscriber where subscriberNumber = ? for update";
+
+static char *read_group_allowRead_stmnt = "select allowRead from userGroup \
+where groupId = ?";
+static char *read_group_allowInsert_stmnt = "select allowInsert from userGroup \
+where groupId = ?";
+static char *read_group_allowDelete_stmnt = "select allowDelete from userGroup \
+where groupId = ?";
+
+static char *read_session_details_stmnt = "select sessionData from userSession \
+where subscriberNumber = ? and serverId = ? for update";
+
+static char *update_noOfRead_stmnt = "update server \
+set noOfRead = noOfRead + 1 where serverId = ? and subscriberSuffix = ?";
+static char *update_noOfInsert_stmnt = "update server \
+set noOfInsert = noOfInsert + 1 where serverId = ? and subscriberSuffix = ?";
+static char *update_noOfDelete_stmnt = "update server \
+set noOfDelete = noOfDelete + 1 where serverId = ? and subscriberSuffix = ?";
+
+static char *insert_session_stmnt = "insert into userSession values (?,?,?)";
+
+static char *delete_session_stmnt = "delete from userSession \
+where subscriberNumber = ? and serverId = ?";
+
+static char *update_subscriber_session_stmnt = "update subscriber set \
+activeSessions = ? where subscriberNumber = ?";
+
+/***************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+extern void handle_error(SQLHDBC hdbc,
+ SQLHENV henv,
+ SQLHSTMT hstmt,
+ SQLRETURN rc,
+ char *filename,
+ int lineno);
+
+/***************************************************************
+****************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+int localDbPrepare(UserHandle *uh)
+{
+ SQLRETURN rc;
+
+ if(!uh) return(-1);
+
+ /*-----------------------------*/
+ /* Update Subscriber Statement */
+ /*-----------------------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->updateSubscriber.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate insert group statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->updateSubscriber.stmt,(SQLCHAR *) update_subscriber_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+/*
+handle_error(uh->hdbc, uh->henv, uh->updateSubscriber.stmt, rc, __FILE__, __LINE__);
+*/
+ printf("Unable to prepare update subscriber statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->updateSubscriber.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,
+ 0,0,
+ &uh->updateSubscriber.values.location,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare update subscriber statement param 1\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->updateSubscriber.stmt,
+ 2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ CHANGED_BY_LENGTH+1,0,
+ uh->updateSubscriber.values.changedBy,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare update subscriber statement param 2\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->updateSubscriber.stmt,
+ 3,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ CHANGED_TIME_LENGTH+1,0,
+ uh->updateSubscriber.values.changedTime,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare update subscriber statement param 3\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->updateSubscriber.stmt,
+ 4,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ SUBSCRIBER_NUMBER_LENGTH+1,0,
+ uh->updateSubscriber.values.number,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare update subscriber statement param 3\n");
+ return(-1);
+ }
+
+ /*---------------------------*/
+ /* Read Subscriber Statement */
+ /*---------------------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->readSubscriber.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate read subscriber statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->readSubscriber.stmt,(SQLCHAR *) read_subscriber_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read subscriber statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->readSubscriber.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ SUBSCRIBER_NUMBER_LENGTH+1,0,
+ uh->readSubscriber.values.number,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read subscriber statement param 1\n");
+ return(-1);
+ }
+
+ rc = SQLBindCol(uh->readSubscriber.stmt, 1,
+ SQL_C_CHAR,
+ uh->readSubscriber.values.name, SUBSCRIBER_NAME_LENGTH+1,
+ NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to bind column 1 to read subscriber statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindCol(uh->readSubscriber.stmt, 2,
+ SQL_C_DEFAULT,
+ &uh->readSubscriber.values.location, 1,
+ NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to bind column 2 to read subscriber statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindCol(uh->readSubscriber.stmt, 3,
+ SQL_C_CHAR,
+ uh->readSubscriber.values.changedBy, CHANGED_BY_LENGTH+1,
+ NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to bind column 3 to read subscriber statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindCol(uh->readSubscriber.stmt, 4,
+ SQL_C_CHAR,
+ uh->readSubscriber.values.changedTime, CHANGED_TIME_LENGTH+1,
+ NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to bind column 4 to read subscriber statement\n");
+ return(-1);
+ }
+
+ /*------------------------------------*/
+ /* Read Subscriber Sessions Statement */
+ /*------------------------------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->readSubscriberSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate read subscriber session statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->readSubscriberSession.stmt,(SQLCHAR *) read_subscriber_session_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read subscriber sessions statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->readSubscriberSession.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ SUBSCRIBER_NUMBER_LENGTH+1,0,
+ uh->readSubscriberSession.values.number,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read subscriber statement param 1\n");
+ return(-1);
+ }
+
+ rc = SQLBindCol(uh->readSubscriberSession.stmt, 1,
+ SQL_C_DEFAULT,
+ &uh->readSubscriberSession.values.activeSessions, 0,
+ NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to bind column 1 to read subscriber sessions statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindCol(uh->readSubscriberSession.stmt, 2,
+ SQL_C_DEFAULT,
+ &uh->readSubscriberSession.values.groupId, 0,
+ NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to bind column 2 to read subscriber sessions statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindCol(uh->readSubscriberSession.stmt, 3,
+ SQL_C_CHAR,
+ uh->readSubscriberSession.values.changedBy, CHANGED_BY_LENGTH+1,
+ NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to bind column 3 to read subscriber sessions statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindCol(uh->readSubscriberSession.stmt, 4,
+ SQL_C_CHAR,
+ uh->readSubscriberSession.values.changedTime, CHANGED_TIME_LENGTH+1,
+ NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to bind column 4 to read subscriber sessions statement\n");
+ return(-1);
+ }
+
+ /*--------------------------------*/
+ /* Read Group AllowRead Statement */
+ /*--------------------------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->readGroupAllowRead.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate read subscriber session statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->readGroupAllowRead.stmt,(SQLCHAR *) read_group_allowRead_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read group allow read statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->readGroupAllowRead.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,
+ 0,0,
+ &uh->readGroupAllowRead.values.groupId,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read allow read statement param 1\n");
+ return(-1);
+ }
+
+ rc = SQLBindCol(uh->readGroupAllowRead.stmt, 1,
+ SQL_C_DEFAULT,
+ &uh->readGroupAllowRead.values.allowRead, 0,
+ NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to bind column 1 to read group allow read statement\n");
+ return(-1);
+ }
+
+ /*----------------------------------*/
+ /* Read Group AllowInsert Statement */
+ /*----------------------------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->readGroupAllowInsert.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate read subscriber session statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->readGroupAllowInsert.stmt,(SQLCHAR *) read_group_allowInsert_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read group allow read statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->readGroupAllowInsert.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,
+ 0,0,
+ &uh->readGroupAllowInsert.values.groupId,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read allow read statement param 1\n");
+ return(-1);
+ }
+
+ rc = SQLBindCol(uh->readGroupAllowInsert.stmt, 1,
+ SQL_C_DEFAULT,
+ &uh->readGroupAllowInsert.values.allowInsert, 0,
+ NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to bind column 1 to read group allow read statement\n");
+ return(-1);
+ }
+
+ /*----------------------------------*/
+ /* Read Group AllowDelete Statement */
+ /*----------------------------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->readGroupAllowDelete.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate read subscriber session statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->readGroupAllowDelete.stmt,(SQLCHAR *) read_group_allowDelete_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read group allow read statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->readGroupAllowDelete.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,
+ 0,0,
+ &uh->readGroupAllowDelete.values.groupId,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read allow read statement param 1\n");
+ return(-1);
+ }
+
+ rc = SQLBindCol(uh->readGroupAllowDelete.stmt, 1,
+ SQL_C_DEFAULT,
+ &uh->readGroupAllowDelete.values.allowDelete, 0,
+ NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to bind column 1 to read group allow read statement\n");
+ return(-1);
+ }
+
+ /*----------------------*/
+ /* read session details */
+ /*----------------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->readSessionDetails.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate read session details statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->readSessionDetails.stmt,(SQLCHAR *) read_session_details_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read session details statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->readSessionDetails.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ SUBSCRIBER_NUMBER_LENGTH+1,0,
+ uh->readSessionDetails.values.number,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read sessions param 1\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->readSessionDetails.stmt,
+ 2,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,
+ 0,0,
+ &uh->readSessionDetails.values.serverId,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read sessions param 2\n");
+ return(-1);
+ }
+
+ rc = SQLBindCol(uh->readSessionDetails.stmt, 1,
+ SQL_C_CHAR,
+ uh->readSessionDetails.values.details, SESSION_DETAILS_LENGTH+1,
+ NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to bind column 1 to read group allow read statement\n");
+ return(-1);
+ }
+
+ /*-------------------*/
+ /* Update no of Read */
+ /*-------------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->updateServerNoOfRead.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate update noOfRead statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->updateServerNoOfRead.stmt,(SQLCHAR *) update_noOfRead_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare update noOfRead statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->updateServerNoOfRead.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,
+ 0,0,
+ &uh->updateServerNoOfRead.values.serverId,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read noOfRead param 1\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->updateServerNoOfRead.stmt,
+ 2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH+1,0,
+ uh->updateServerNoOfRead.values.suffix,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read noOfRead param 2\n");
+ return(-1);
+ }
+
+ /*----------------*/
+ /* Insert Session */
+ /*----------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->insertSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate update noOfRead statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->insertSession.stmt,(SQLCHAR *) insert_session_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare insert session statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->insertSession.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ SUBSCRIBER_NUMBER_LENGTH+1,0,
+ uh->insertSession.values.number,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read sessions param 1\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->insertSession.stmt,
+ 2,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,
+ 0,0,
+ &uh->insertSession.values.serverId,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read noOfRead param 2\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->insertSession.stmt,
+ 3,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ SESSION_DETAILS_LENGTH+1,0,
+ uh->insertSession.values.details,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read sessions param 1\n");
+ return(-1);
+ }
+
+ /*----------------------------*/
+ /* Update subscriber sessions */
+ /*----------------------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->updateSubscriberSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate update noOfRead statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->updateSubscriberSession.stmt,(SQLCHAR *) update_subscriber_session_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare update subscriber session statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->updateSubscriberSession.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,
+ 0,0,
+ &uh->updateSubscriberSession.values.activeSessions,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read noOfRead param 2\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->updateSubscriberSession.stmt,
+ 2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ SUBSCRIBER_NUMBER_LENGTH+1,0,
+ uh->updateSubscriberSession.values.number,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read sessions param 1\n");
+ return(-1);
+ }
+
+ /*---------------------*/
+ /* Update no of Insert */
+ /*---------------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->updateServerNoOfInsert.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate update noOfRead statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->updateServerNoOfInsert.stmt,(SQLCHAR *) update_noOfInsert_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare update noOfRead statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->updateServerNoOfInsert.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,
+ 0,0,
+ &uh->updateServerNoOfInsert.values.serverId,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read noOfRead param 1\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->updateServerNoOfInsert.stmt,
+ 2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH+1,0,
+ uh->updateServerNoOfInsert.values.suffix,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read noOfRead param 2\n");
+ return(-1);
+ }
+
+ /*----------------*/
+ /* Delete Session */
+ /*----------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->deleteSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate update noOfRead statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->deleteSession.stmt,(SQLCHAR *) delete_session_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare insert session statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->deleteSession.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ SUBSCRIBER_NUMBER_LENGTH+1,0,
+ uh->deleteSession.values.number,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read sessions param 1\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->deleteSession.stmt,
+ 2,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,
+ 0,0,
+ &uh->deleteSession.values.serverId,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read noOfRead param 2\n");
+ return(-1);
+ }
+
+ /*---------------------*/
+ /* Update no of Delete */
+ /*---------------------*/
+ rc = SQLAllocStmt(uh->hdbc, &uh->updateServerNoOfDelete.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate update noOfRead statement\n");
+ return(-1);
+ }
+
+ rc = SQLPrepare(uh->updateServerNoOfDelete.stmt,(SQLCHAR *) update_noOfDelete_stmnt, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare update noOfRead statement\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->updateServerNoOfDelete.stmt,
+ 1,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,
+ 0,0,
+ &uh->updateServerNoOfDelete.values.serverId,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read noOfRead param 1\n");
+ return(-1);
+ }
+
+ rc = SQLBindParameter(uh->updateServerNoOfDelete.stmt,
+ 2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH+1,0,
+ uh->updateServerNoOfInsert.values.suffix,0,NULL);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to prepare read noOfRead param 2\n");
+ return(-1);
+ }
+
+ /*-------------------------------*/
+ /* Commit all prepare statements */
+ /*-------------------------------*/
+ rc = SQLTransact(uh->henv, uh->hdbc, SQL_COMMIT);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to commit all prepare insert statement\n");
+ return(-1);
+ }
+
+ return(0);
+}
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/macros.h b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/macros.h
new file mode 100644
index 00000000000..363f247b93f
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/macros.h
@@ -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 MACROS_H
+#define MACROS_H
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+
+#define ERROR(x) {ndbout_c((x)); }
+#define ERROR1(x,y) {ndbout_c((x), (y)); }
+#define ERROR2(x,y,z) {ndbout_c((x), (y), (z)); }
+#define ERROR3(x,y,z,u) {ndbout_c((x), (y), (z), (u)); }
+#define ERROR4(x,y,z,u,w) {ndbout_c((x), (y), (z), (u), (w)); }
+
+#define INIT_RANDOM(x) srand48((x))
+#define UI_RANDOM(x) ((unsigned int)(lrand48()%(x)))
+
+#define ASSERT(cond, message) \
+ { if(!(cond)) { ERROR(message); exit(-1); }}
+
+#ifdef DEBUG_ON
+#define DEBUG(x) {ndbout_c((x)); }
+#define DEBUG1(x,y) {ndbout_c((x), (y)); }
+#define DEBUG2(x,y,z) {ndbout_c((x), (y), (z)); }
+#define DEBUG3(x,y,z,u) {ndbout_c((x), (y), (z), (u)); }
+#define DEBUG4(x,y,z,u,w) {ndbout_c((x), (y), (z), (u), (w)); }
+#define DEBUG5(x,y,z,u,w, v) {ndbout_c((x), (y), (z), (u), (w), (v)); }
+#else
+#define DEBUG(x)
+#define DEBUG1(x,y)
+#define DEBUG2(x,y,z)
+#define DEBUG3(x,y,z,u)
+#define DEBUG4(x,y,z,u,w)
+#define DEBUG5(x,y,z,u,w, v)
+#endif
+
+#endif
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/ndb_error.hpp b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/ndb_error.hpp
new file mode 100644
index 00000000000..b3aaeac822e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/ndb_error.hpp
@@ -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 */
+
+#ifndef NDB_ERROR_H
+#define NDB_ERROR_H
+
+#include <NdbOut.hpp>
+
+#define error_handler(x,y, z) { \
+ ndbout << x << " " << y << endl; \
+ exit(-1); }
+
+#define CHECK_NULL(x,y, z) if(x == 0) \
+ error_handler(y,(z->getNdbError()), 0)
+#define CHECK_MINUS_ONE(x, y, z) if(x == -1) \
+ error_handler(y,(z->getNdbError()), 0)
+
+#endif
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/Makefile b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/Makefile
new file mode 100644
index 00000000000..9b1247d44af
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/Makefile
@@ -0,0 +1,10 @@
+include ../makevars.$(ARCH)
+
+LIBRARY = ../../lib/libUser.so
+
+OBJECTS = \
+ $(LIBRARY)(localDbPrepare.o)\
+ $(LIBRARY)(userInterface.o)\
+ $(LIBRARY)(userTransaction.o)
+
+$(LIBRARY): $(OBJECTS)
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userHandle.h b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userHandle.h
new file mode 100644
index 00000000000..1de468d4dad
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userHandle.h
@@ -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 */
+
+#ifndef USERHANDLE_H
+#define USERHANDLE_H
+
+/***************************************************************/
+/* I N C L U D E D F I L E S */
+/***************************************************************/
+
+#include "sql.h"
+#include "sqlext.h"
+#include "sqltypes.h"
+
+#include "testDefinitions.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+struct userHandle{
+ SQLHENV henv;
+ SQLHDBC hdbc;
+ SQLHSTMT stmt;
+
+ /*----------------*/
+ /* Transaction T1 */
+ /*----------------*/
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ SubscriberNumber number;
+ Location location;
+ ChangedBy changedBy;
+ ChangedTime changedTime;
+ }values;
+ }updateSubscriber;
+
+ /*----------------*/
+ /* Transaction T2 */
+ /*----------------*/
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ SubscriberNumber number;
+ SubscriberName name;
+ Location location;
+ ChangedBy changedBy;
+ ChangedTime changedTime;
+ }values;
+ }readSubscriber;
+
+ /*----------------*/
+ /* Transaction T3 */
+ /*----------------*/
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ GroupId groupId;
+ Permission allowRead;
+ }values;
+ }readGroupAllowRead;
+
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ SubscriberNumber number;
+ ServerId serverId;
+ SessionDetails details;
+ }values;
+ }readSessionDetails;
+
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ ServerId serverId;
+ SubscriberSuffix suffix;
+ }values;
+ }updateServerNoOfRead;
+
+ /*----------------*/
+ /* Transaction T4 */
+ /*----------------*/
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ GroupId groupId;
+ Permission allowInsert;
+ }values;
+ }readGroupAllowInsert;
+
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ SubscriberNumber number;
+ ServerId serverId;
+ SessionDetails details;
+ }values;
+ }insertSession;
+
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ ServerId serverId;
+ SubscriberSuffix suffix;
+ }values;
+ }updateServerNoOfInsert;
+
+ /*----------------*/
+ /* Transaction T5 */
+ /*----------------*/
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ GroupId groupId;
+ Permission allowDelete;
+ }values;
+ }readGroupAllowDelete;
+
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ SubscriberNumber number;
+ ServerId serverId;
+ }values;
+ }deleteSession;
+
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ ServerId serverId;
+ SubscriberSuffix suffix;
+ }values;
+ }updateServerNoOfDelete;
+
+ /*--------------------------*/
+ /* Transaction T3 + T4 + T5 */
+ /*--------------------------*/
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ SubscriberNumber number;
+ uint32 activeSessions;
+ GroupId groupId;
+ ChangedBy changedBy;
+ ChangedTime changedTime;
+ }values;
+ }readSubscriberSession;
+
+ struct {
+ SQLHSTMT stmt;
+ struct {
+ SubscriberNumber number;
+ uint32 activeSessions;
+ }values;
+ }updateSubscriberSession;
+};
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+
+#endif /* USERHANDLE_H */
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userInterface.c b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userInterface.c
new file mode 100644
index 00000000000..bacf1861dde
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userInterface.c
@@ -0,0 +1,453 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 "userInterface.h"
+#include "userHandle.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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+extern int localDbPrepare(UserHandle *uh);
+
+static int dbCreate(UserHandle *uh);
+
+/***************************************************************
+* L O C A L D A T A *
+***************************************************************/
+
+static char *create_subscriber_table =
+"CREATE TABLE subscriber(\
+subscriberNumber CHAR(12) NOT NULL primary key,\
+subscriberName CHAR(32) NOT NULL,\
+groupId INT NOT NULL,\
+location INT NOT NULL,\
+activeSessions INT NOT NULL,\
+changedBy CHAR(32) NOT NULL,\
+changedTime CHAR(32) NOT NULL)";
+
+static char *create_group_table =
+"CREATE TABLE userGroup(\
+groupId INT NOT NULL primary key,\
+groupName CHAR(32) NOT NULL,\
+allowRead INT NOT NULL,\
+allowInsert INT NOT NULL,\
+allowDelete INT NOT NULL)";
+
+static char *create_server_table = "CREATE TABLE server(\
+serverId INT NOT NULL,\
+subscriberSuffix CHAR(2) NOT NULL,\
+serverName CHAR(32) NOT NULL,\
+noOfRead INT NOT NULL,\
+noOfInsert INT NOT NULL,\
+noOfDelete INT NOT NULL,\
+PRIMARY KEY(serverId,subscriberSuffix))";
+
+static char *create_session_table =
+"CREATE TABLE userSession(\
+subscriberNumber CHAR(12) NOT NULL,\
+serverId INT NOT NULL,\
+sessionData CHAR(2000) NOT NULL,\
+PRIMARY KEY(subscriberNumber,serverId))";
+
+/***************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+/***************************************************************
+****************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+/*-----------------------------------*/
+/* Time related Functions */
+/* */
+/* Returns a double value in seconds */
+/*-----------------------------------*/
+double userGetTime(void)
+{
+ static int initialized = 0;
+ static struct timeval initTime;
+ double timeValue;
+
+ if( !initialized ) {
+ initialized = 1;
+ gettimeofday(&initTime, 0);
+ timeValue = 0.0;
+ }
+ else {
+ struct timeval tv;
+ double s;
+ double us;
+
+ gettimeofday(&tv, 0);
+ s = (double)tv.tv_sec - (double)initTime.tv_sec;
+ us = (double)tv.tv_usec - (double)initTime.tv_usec;
+
+ timeValue = s + (us / 1000000.0);
+ }
+
+ return(timeValue);
+}
+
+
+void handle_error(SQLHDBC hdbc,
+ SQLHENV henv,
+ SQLHSTMT hstmt,
+ SQLRETURN rc,
+ char *filename,
+ int lineno)
+{
+#define MSG_LNG 512
+
+ int isError = 0;
+ SQLRETURN ret = SQL_SUCCESS;
+ SQLCHAR szSqlState[MSG_LNG]; /* SQL state string */
+ SQLCHAR szErrorMsg[MSG_LNG]; /* Error msg text buffer pointer */
+ SQLINTEGER pfNativeError; /* Native error code */
+ SQLSMALLINT pcbErrorMsg; /* Error msg text Available bytes */
+
+ if ( rc == SQL_SUCCESS || rc == SQL_NO_DATA_FOUND )
+ return;
+ else if ( rc == SQL_INVALID_HANDLE ) {
+ printf("ERROR in %s, line %d: invalid handle\n",
+ filename, lineno);
+ isError = 1;
+ }
+ else if ( rc == SQL_SUCCESS_WITH_INFO ) {
+ printf("WARNING in %s, line %d\n",
+ filename, lineno);
+ isError = 0;
+ }
+ else if ( rc == SQL_ERROR ) {
+ printf("ERROR in %s, line %d\n",
+ filename, lineno);
+ isError = 1;
+ }
+
+ fflush(stdout);
+
+ while ( ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO ) {
+ ret = SQLError(henv, hdbc, hstmt, szSqlState, &pfNativeError, szErrorMsg,
+ MSG_LNG, &pcbErrorMsg);
+
+ switch (ret) {
+ case SQL_SUCCESS:
+ case SQL_SUCCESS_WITH_INFO:
+ printf("%s\n*** ODBC Error/Warning = %s, "
+ "Additional Error/Warning = %d\n",
+ szErrorMsg, szSqlState, pfNativeError);
+
+ if(ret == SQL_SUCCESS_WITH_INFO)
+ printf("(Note: error message was truncated.\n");
+ break;
+
+ case SQL_INVALID_HANDLE:
+ printf("Call to SQLError failed with return code of "
+ "SQL_INVALID_HANDLE.\n");
+ break;
+
+ case SQL_ERROR:
+ printf("Call to SQLError failed with return code of SQL_ERROR.\n");
+ break;
+
+ case SQL_NO_DATA_FOUND:
+ break;
+
+ default:
+ printf("Call to SQLError failed with return code of %d.\n", ret);
+ }
+ }
+
+ if ( isError )
+ exit(1);
+}
+
+static int dbCreate(UserHandle *uh)
+{
+ SQLRETURN rc;
+ SQLHSTMT creatstmt;
+
+ if(!uh) return(-1);
+
+ rc = SQLAllocStmt(uh->hdbc, &creatstmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate create statement\n");
+ return(-1);
+ }
+
+ rc = SQLExecDirect(creatstmt,(SQLCHAR *)create_subscriber_table, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to create subscriber table\n");
+ return(-1);
+ }
+
+ rc = SQLExecDirect(creatstmt,(SQLCHAR *)create_group_table, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to create group table\n");
+ return(-1);
+ }
+
+ rc = SQLExecDirect(creatstmt,(SQLCHAR *)create_server_table, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to create server table\n");
+ return(-1);
+ }
+
+ rc = SQLExecDirect(creatstmt,(SQLCHAR *)create_session_table, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to create session table\n");
+ return(-1);
+ }
+
+ rc = SQLTransact(uh->henv, uh->hdbc, SQL_COMMIT);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to commit all create table\n");
+ return(-1);
+ }
+
+ rc = SQLFreeStmt(creatstmt, SQL_DROP);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to free create statement\n");
+ return(-1);
+ }
+
+ return(0);
+}
+
+UserHandle *userDbConnect(uint32 createDb, char *dbName)
+{
+ char connStrIn[512]; /* ODBC Connection String */
+ char connStrOut[2048];
+ SQLRETURN rc;
+ UserHandle *uh;
+
+ /*--------------------------*/
+ /* Build the Connect string */
+ /*--------------------------*/
+ sprintf(connStrIn,
+ "AutoCreate=%d;OverWrite=%d;DSN=%s",
+ createDb ? 1 : 0,
+ createDb ? 1 : 0,
+ dbName);
+
+ uh = calloc(1, sizeof(UserHandle));
+ if( !uh ) {
+ printf("Unable to allocate memory for Handle\n");
+ return(0);
+ }
+
+ /*---------------------------------*/
+ /* Allocate the Environment Handle */
+ /*---------------------------------*/
+ rc = SQLAllocEnv(&uh->henv);
+
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate Environment Handle\n");
+ return(0);
+ }
+
+ /*--------------------------------*/
+ /* Allocate the DB Connect Handle */
+ /*--------------------------------*/
+ rc = SQLAllocConnect(uh->henv, &uh->hdbc);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate a connection handle\n");
+ return(0);
+ }
+
+ /*-------------------------*/
+ /* Connect to the Database */
+ /*-------------------------*/
+ rc = SQLDriverConnect(uh->hdbc, NULL,
+ (SQLCHAR *)connStrIn, SQL_NTS,
+ (SQLCHAR *)connStrOut, sizeof (connStrOut),
+ NULL, SQL_DRIVER_NOPROMPT);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+handle_error(uh->hdbc, uh->henv, NULL, rc, __FILE__, __LINE__);
+ printf("Unable to connect to database server\n");
+ return(0);
+ }
+
+ rc = SQLSetConnectOption(uh->hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to set connection option\n");
+ return(0);
+ }
+
+ rc = SQLAllocStmt(uh->hdbc, &uh->stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to allocate immediate statement\n");
+ return(0);
+ }
+
+ if( createDb )
+ dbCreate(uh);
+
+ if( localDbPrepare(uh) < 0 )
+ return(0);
+
+ return(uh);
+}
+
+void userDbDisconnect(UserHandle *uh)
+{
+ SQLRETURN rc;
+
+ if(!uh) return;
+
+ rc = SQLDisconnect(uh->hdbc);
+
+ SQLFreeConnect(uh->hdbc);
+ SQLFreeEnv(uh->henv);
+ free(uh);
+}
+
+int userDbInsertServer(UserHandle *uh,
+ ServerId serverId,
+ SubscriberSuffix suffix,
+ ServerName name)
+{
+ SQLRETURN rc;
+ char buf[1000];
+
+ if(!uh) return(-1);
+
+ sprintf(buf, "insert into server values (%d,'%.*s','%s',0,0,0)",
+ serverId,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH, suffix,
+ name);
+
+ rc = SQLExecDirect(uh->stmt, (unsigned char *)buf, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to execute insert server\n");
+ return(-1);
+ }
+
+ return( userDbCommit(uh) );
+}
+
+int userDbInsertSubscriber(UserHandle *uh,
+ SubscriberNumber number,
+ uint32 groupId,
+ SubscriberName name)
+{
+ SQLRETURN rc;
+ char buf[1000];
+
+ if(!uh) return(-1);
+
+ sprintf(buf, "insert into subscriber values ('%s','%s',%d,0,0,'','')",
+ number,
+ name,
+ groupId);
+
+ rc = SQLExecDirect(uh->stmt, (unsigned char*)buf, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to execute insert subscriber\n");
+ return(-1);
+ }
+
+ return( userDbCommit(uh) );
+}
+
+int userDbInsertGroup(UserHandle *uh,
+ GroupId groupId,
+ GroupName name,
+ Permission allowRead,
+ Permission allowInsert,
+ Permission allowDelete)
+{
+ SQLRETURN rc;
+ char buf[1000];
+
+ if(!uh) return(-1);
+
+ sprintf(buf, "insert into usergroup values (%d,'%s',%d,%d,%d)",
+ groupId,
+ name,
+ allowRead,
+ allowInsert,
+ allowDelete);
+
+ rc = SQLExecDirect(uh->stmt, (unsigned char*)buf, SQL_NTS);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to execute insert group\n");
+ return(-1);
+ }
+
+ return( userDbCommit(uh) );
+}
+
+int userDbCommit(UserHandle *uh)
+{
+ SQLRETURN rc;
+ if(!uh) return(-1);
+
+ rc = SQLTransact(uh->henv, uh->hdbc, SQL_COMMIT);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+handle_error(uh->hdbc, uh->henv, 0, rc, __FILE__, __LINE__);
+ printf("Unable to commit Transaction\n");
+ return(-1);
+ }
+
+ return(0);
+}
+
+int userDbRollback(UserHandle *uh)
+{
+ SQLRETURN rc;
+ if(!uh) return(-1);
+
+ rc = SQLTransact(uh->henv, uh->hdbc, SQL_ROLLBACK);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("Unable to rollback Transaction\n");
+ return(-1);
+ }
+
+ return(0);
+}
+
+void userCheckpoint(UserHandle *uh)
+{
+ SQLRETURN rc;
+ if(!uh) return;
+
+ rc = SQLExecDirect(uh->stmt, (SQLCHAR *)"call ttCheckpointFuzzy", SQL_NTS);
+ userDbCommit(uh);
+}
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userTransaction.c b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userTransaction.c
new file mode 100644
index 00000000000..a2f4787bb0c
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userTransaction.c
@@ -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 */
+
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+
+#include <ndb_global.h>
+#include <time.h>
+
+#include "sql.h"
+#include "sqlext.h"
+
+
+#include "userInterface.h"
+#include "userHandle.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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+static int readSubscriberSessions(UserHandle *uh,
+ SubscriberNumber number,
+ char *transactionType);
+
+/***************************************************************
+* L O C A L D A T A *
+***************************************************************/
+
+extern void handle_error(SQLHDBC hdbc,
+ SQLHENV henv,
+ SQLHSTMT hstmt,
+ SQLRETURN rc,
+ char *filename,
+ int lineno);
+
+/***************************************************************
+* 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 int readSubscriberSessions(UserHandle *uh,
+ SubscriberNumber number,
+ char *transactionType)
+{
+ SQLRETURN rc;
+
+ /*-----------------------------------------------------*/
+ /* SELECT activeSessions,groupId,changedBy,changedTime */
+ /* FROM SUBSCRIBER */
+ /* WHERE subscriberNumber=x; */
+ /*-----------------------------------------------------*/
+ strcpy(uh->readSubscriberSession.values.number,number);
+
+ rc = SQLExecute(uh->readSubscriberSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("%s %s\n",
+ transactionType,
+ "Unable to execute read subscriber session");
+ return(-1);
+ }
+
+ rc = SQLFetch(uh->readSubscriberSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("%s %s\n",
+ transactionType,
+ "Unable to fetch read subscriber session");
+ return(-1);
+ }
+
+ return(0);
+}
+
+/***************************************************************
+****************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+void userTransaction_T1(UserHandle *uh,
+ SubscriberNumber number,
+ Location new_location,
+ ChangedBy changed_by,
+ ChangedTime changed_time)
+{
+ SQLRETURN rc;
+
+ if(!uh) return;
+
+ /*---------------------------------------------*/
+ /* Update the subscriber information */
+ /* */
+ /* UPDATE SUBSCRIBER */
+ /* SET location=x, changedBy=x, changedTime=x */
+ /* WHERE subscriberNumber=x; */
+ /*---------------------------------------------*/
+ strcpy(uh->updateSubscriber.values.number, number);
+ uh->updateSubscriber.values.location = new_location;
+ strcpy(uh->updateSubscriber.values.changedBy, changed_by);
+ strcpy(uh->updateSubscriber.values.changedTime, changed_time);
+
+ rc = SQLExecute(uh->updateSubscriber.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T1 Unable to execute update subscriber\n");
+ return;
+ }
+
+ userDbCommit(uh);
+}
+
+void userTransaction_T2(UserHandle *uh,
+ SubscriberNumber number,
+ Location *new_location,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName)
+{
+ SQLRETURN rc;
+
+ if(!uh) return;
+
+ /*------------------------------------------------------*/
+ /* Read the information from the subscriber table */
+ /* */
+ /* SELECT location,subscriberName,changedBy,changedTime */
+ /* FROM SUBSCRIBER */
+ /* WHERE subscriberNumber=x; */
+ /*------------------------------------------------------*/
+ strcpy(uh->readSubscriber.values.number,number);
+
+ rc = SQLExecute(uh->readSubscriber.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T2 Unable to execute read subscriber\n");
+ return;
+ }
+
+ rc = SQLFetch(uh->readSubscriber.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T2 Unable to fetch read subscriber\n");
+ return;
+ }
+
+ userDbCommit(uh);
+
+ strcpy(subscriberName, uh->readSubscriber.values.name);
+ *new_location = uh->readSubscriber.values.location;
+ strcpy(changed_by, uh->readSubscriber.values.changedBy);
+ strcpy(changed_time, uh->readSubscriber.values.changedTime);
+}
+
+void userTransaction_T3(UserHandle *uh,
+ SubscriberNumber number,
+ ServerId server_id,
+ ServerBit server_bit,
+ SessionDetails session_details,
+ unsigned int *branch_executed)
+{
+ SQLRETURN rc;
+
+ if(!uh) return;
+
+ *branch_executed = 0;
+
+ /*--------------------------------------*/
+ /* Read active sessions from subscriber */
+ /*--------------------------------------*/
+ if( readSubscriberSessions(uh, number, "T3") < 0 )
+ return;
+
+ /*-----------------------------------------------*/
+ /* Read the 'read' Permissions for the userGroup */
+ /* */
+ /* SELECT allowRead */
+ /* FROM USERGROUP */
+ /* WHERE groupId=x */
+ /*-----------------------------------------------*/
+ uh->readGroupAllowRead.values.groupId = uh->readSubscriberSession.values.groupId;
+
+ rc = SQLExecute(uh->readGroupAllowRead.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T3 Unable to execute read group allow read\n");
+ return;
+ }
+
+ rc = SQLFetch(uh->readGroupAllowRead.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T3 Unable to fetch read group allow read\n");
+ return;
+ }
+
+ if( uh->readGroupAllowRead.values.allowRead & server_bit &&
+ uh->readSubscriberSession.values.activeSessions & server_bit ) {
+
+ /*----------------------------------------------------*/
+ /* Read the sessionDetails from the userSession table */
+ /* */
+ /* SELECT sessionData */
+ /* FROM userSession */
+ /* WHERE subscriberNumber=x, serverId=x */
+ /*----------------------------------------------------*/
+ strcpy(uh->readSessionDetails.values.number,number);
+ uh->readSessionDetails.values.serverId = server_id;
+
+ rc = SQLExecute(uh->readSessionDetails.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T3 Unable to execute read session details\n");
+ return;
+ }
+
+ rc = SQLFetch(uh->readSessionDetails.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T3 Unable to fetch read session details\n");
+ return;
+ }
+
+ strcpy(session_details, uh->readSessionDetails.values.details);
+
+ /*----------------------------------------*/
+ /* Increment noOfRead field in the server */
+ /* */
+ /* UPDATE server */
+ /* SET noOfRead=noOfRead+1 */
+ /* WHERE serverId=x,subscriberSuffix=x */
+ /*----------------------------------------*/
+ uh->updateServerNoOfRead.values.serverId = server_id;
+ strcpy(uh->updateServerNoOfRead.values.suffix,
+ &number[SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH]);
+
+ rc = SQLExecute(uh->updateServerNoOfRead.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T3 Unable to execute read no of read\n");
+ return;
+ }
+
+ *branch_executed = 1;
+ }
+
+ userDbCommit(uh);
+}
+
+void userTransaction_T4(UserHandle *uh,
+ SubscriberNumber number,
+ ServerId server_id,
+ ServerBit server_bit,
+ SessionDetails session_details,
+ unsigned int do_rollback,
+ unsigned int *branch_executed)
+{
+ SQLRETURN rc;
+
+ if(!uh) return;
+
+ *branch_executed = 0;
+
+ /*--------------------------------------*/
+ /* Read active sessions from subscriber */
+ /*--------------------------------------*/
+ if( readSubscriberSessions(uh, number, "T4") < 0 )
+ return;
+
+ /*-------------------------------------------------*/
+ /* Read the 'insert' Permissions for the userGroup */
+ /* */
+ /* SELECT allowInsert */
+ /* FROM USERGROUP */
+ /* WHERE groupId=x */
+ /*-------------------------------------------------*/
+ uh->readGroupAllowInsert.values.groupId = uh->readSubscriberSession.values.groupId;
+
+ rc = SQLExecute(uh->readGroupAllowInsert.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T4 Unable to execute read group allow insert\n");
+ return;
+ }
+
+ rc = SQLFetch(uh->readGroupAllowInsert.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T4 Unable to fetch read group allow insert\n");
+ return;
+ }
+
+ if( uh->readGroupAllowInsert.values.allowInsert & server_bit &&
+ !(uh->readSubscriberSession.values.activeSessions & server_bit) ) {
+
+ /*---------------------------------------------*/
+ /* Insert the session to the userSession table */
+ /* */
+ /* INSERT INTO userSession */
+ /* VALUES (x,x,x) */
+ /*---------------------------------------------*/
+ strcpy(uh->insertSession.values.number, number);
+ uh->insertSession.values.serverId = server_id;
+ strcpy(uh->insertSession.values.details, session_details);
+
+ rc = SQLExecute(uh->insertSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+handle_error(uh->hdbc, uh->henv, uh->insertSession.stmt, rc, __FILE__, __LINE__);
+ printf("T4 Unable to execute insert session \n");
+ return;
+ }
+
+ /*----------------------------------------*/
+ /* Update subscriber activeSessions field */
+ /* */
+ /* UPDATE subscriber */
+ /* SET activeSessions=x */
+ /* WHERE subscriberNumber=x */
+ /*----------------------------------------*/
+ strcpy(uh->updateSubscriberSession.values.number, number);
+ uh->updateSubscriberSession.values.activeSessions =
+ uh->readSubscriberSession.values.activeSessions | server_bit;
+
+ rc = SQLExecute(uh->updateSubscriberSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T4 Unable to execute update session \n");
+ return;
+ }
+
+ /*------------------------------------------*/
+ /* Increment noOfInsert field in the server */
+ /* */
+ /* UPDATE server */
+ /* SET noOfInsert=noOfInsert+1 */
+ /* WHERE serverId=x,subscriberSuffix=x */
+ /*------------------------------------------*/
+ uh->updateServerNoOfInsert.values.serverId = server_id;
+ strcpy(uh->updateServerNoOfInsert.values.suffix,
+ &number[SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH]);
+
+ rc = SQLExecute(uh->updateServerNoOfInsert.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T4 Unable to execute update no of read\n");
+ return;
+ }
+
+ *branch_executed = 1;
+ }
+
+ if(do_rollback)
+ userDbRollback(uh);
+ else
+ userDbCommit(uh);
+}
+
+void userTransaction_T5(UserHandle *uh,
+ SubscriberNumber number,
+ ServerId server_id,
+ ServerBit server_bit,
+ unsigned int do_rollback,
+ unsigned int *branch_executed)
+{
+ SQLRETURN rc;
+
+ if(!uh) return;
+
+ *branch_executed = 0;
+
+ /*--------------------------------------*/
+ /* Read active sessions from subscriber */
+ /*--------------------------------------*/
+ if( readSubscriberSessions(uh, number, "T5") < 0 )
+ return;
+
+ /*-------------------------------------------------*/
+ /* Read the 'delete' Permissions for the userGroup */
+ /* */
+ /* SELECT allowDelete */
+ /* FROM USERGROUP */
+ /* WHERE groupId=x */
+ /*-------------------------------------------------*/
+ uh->readGroupAllowDelete.values.groupId = uh->readSubscriberSession.values.groupId;
+
+ rc = SQLExecute(uh->readGroupAllowDelete.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T5 Unable to execute read group allow delete\n");
+ return;
+ }
+
+ rc = SQLFetch(uh->readGroupAllowDelete.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T5 Unable to fetch read group allow delete\n");
+ return;
+ }
+
+ if( uh->readGroupAllowDelete.values.allowDelete & server_bit &&
+ uh->readSubscriberSession.values.activeSessions & server_bit ) {
+
+ /*-----------------------------------------------*/
+ /* Delete the session from the userSession table */
+ /* */
+ /* DELETE FROM userSession */
+ /* WHERE subscriberNumber=x,serverId=x */
+ /*-----------------------------------------------*/
+ strcpy(uh->deleteSession.values.number,number);
+ uh->deleteSession.values.serverId = server_id;
+
+ rc = SQLExecute(uh->deleteSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T5 Unable to execute delete session\n");
+ return;
+ }
+
+ /*----------------------------------------*/
+ /* Update subscriber activeSessions field */
+ /* */
+ /* UPDATE subscriber */
+ /* SET activeSessions=x */
+ /* WHERE subscriberNumber=x */
+ /*----------------------------------------*/
+ strcpy(uh->updateSubscriberSession.values.number, number);
+ uh->updateSubscriberSession.values.activeSessions =
+ uh->readSubscriberSession.values.activeSessions & ~server_bit;
+
+ rc = SQLExecute(uh->updateSubscriberSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T5 Unable to execute update subscriber session \n");
+ return;
+ }
+
+ /*------------------------------------------*/
+ /* Increment noOfDelete field in the server */
+ /* */
+ /* UPDATE server */
+ /* SET noOfDelete=noOfDelete+1 */
+ /* WHERE serverId=x,subscriberSuffix=x */
+ /*------------------------------------------*/
+ uh->updateServerNoOfDelete.values.serverId = server_id;
+ strcpy(uh->updateServerNoOfDelete.values.suffix,
+ &number[SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH]);
+
+ rc = SQLExecute(uh->updateServerNoOfDelete.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T5 Unable to execute update no of delete\n");
+ return;
+ }
+
+ *branch_executed = 1;
+ }
+
+ if(do_rollback)
+ userDbRollback(uh);
+ else
+ userDbCommit(uh);
+}
+
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userHandle.h b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userHandle.h
new file mode 100644
index 00000000000..6da76fc2bff
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userHandle.h
@@ -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 USERHANDLE_H
+#define USERHANDLE_H
+
+/***************************************************************/
+/* I N C L U D E D F I L E S */
+/***************************************************************/
+
+#include <NdbApi.hpp>
+#include "testDefinitions.h"
+
+/***************************************************************
+* M A C R O S *
+***************************************************************/
+
+/***************************************************************/
+/* C O N S T A N T S */
+/***************************************************************/
+
+/***************************************************************
+* D A T A S T R U C T U R E S *
+***************************************************************/
+
+typedef Ndb userHandle;
+
+/***************************************************************
+* P U B L I C F U N C T I O N S *
+***************************************************************/
+
+/***************************************************************
+* E X T E R N A L D A T A *
+***************************************************************/
+
+
+#endif /* USERHANDLE_H */
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userInterface.cpp b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userInterface.cpp
new file mode 100644
index 00000000000..fe3c17acbf5
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userInterface.cpp
@@ -0,0 +1,739 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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>
+#ifndef NDB_WIN32
+#include <sys/time.h>
+#endif
+
+#include "ndb_error.hpp"
+#include "userHandle.h"
+#include "userInterface.h"
+#include <NdbThread.h>
+#include <NdbTick.h>
+#include <NdbMutex.h>
+#include <NdbSleep.h>
+#include "ndb_schema.hpp"
+#include <NDBT.hpp>
+#include <NdbSchemaCon.hpp>
+
+/***************************************************************
+* 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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+extern int localDbPrepare(UserHandle *uh);
+
+static int dbCreate(UserHandle *uh);
+
+/***************************************************************
+* L O C A L D A T A *
+***************************************************************/
+
+/***************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+/***************************************************************
+****************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+/*-----------------------------------*/
+/* Time related Functions */
+/* */
+/* Returns a double value in seconds */
+/*-----------------------------------*/
+double userGetTimeSync(void)
+{
+ static int initialized = 0;
+ static NDB_TICKS initSecs = 0;
+ static Uint32 initMicros = 0;
+ double timeValue = 0;
+
+ if ( !initialized ) {
+ initialized = 1;
+ NdbTick_CurrentMicrosecond(&initSecs, &initMicros);
+ timeValue = 0.0;
+ } else {
+ NDB_TICKS secs = 0;
+ Uint32 micros = 0;
+
+ NdbTick_CurrentMicrosecond(&secs, &micros);
+
+ double s = (double)secs - (double)initSecs;
+ double us = (double)secs - (double)initMicros;
+
+ timeValue = s + (us / 1000000.0);
+ }
+
+ return timeValue;
+}
+
+// 0 - OK
+// 1 - Retry transaction
+// 2 - Permanent
+int
+userDbCommit(UserHandle *uh){
+ if(uh->pCurrTrans != 0){
+ int check = uh->pCurrTrans->execute( Commit );
+ NdbError err = uh->pCurrTrans->getNdbError();
+ uh->pNDB->closeTransaction(uh->pCurrTrans);
+ uh->pCurrTrans = 0;
+
+ if(err.status != NdbError::Success)
+ ndbout << err << endl;
+
+ if(err.status == NdbError::TemporaryError &&
+ err.classification == NdbError::OverloadError){
+ NdbSleep_SecSleep(3);
+ }
+
+ return err.status;
+ }
+ return 2;
+}
+
+/**
+ * TRUE - Normal table
+ * FALSE - Table w.o. checkpoing and logging
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern int useTableLogging;
+extern int useIndexTables;
+#ifdef __cplusplus
+}
+#endif
+
+
+int
+create_table_server(Ndb * pNdb){
+ int check;
+ NdbSchemaCon * MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pNdb);
+ if( MySchemaTransaction == NULL )
+ error_handler("startSchemaTransaction", pNdb->getNdbError(), 0);
+
+ NdbSchemaOp * MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL )
+ error_handler("getNdbSchemaOp", MySchemaTransaction->getNdbError(), 0);
+
+ // Create table
+ check = MySchemaOp->createTable( SERVER_TABLE,
+ 8, // Table size
+ TupleKey, // Key Type
+ 1 // Nr of Pages
+ ,DistributionGroup,
+ 6,
+ 78,
+ 80,
+ 1,
+ useTableLogging
+ );
+ if( check == -1 )
+ error_handler("createTable", MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute
+ ( SERVER_SUBSCRIBER_SUFFIX,
+ TupleKey,
+ sizeof(char) << 3,
+ SUBSCRIBER_NUMBER_SUFFIX_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute,
+ NormalStorageAttribute,
+ 0,
+ 1,
+ 16);
+ if( check == -1 )
+ error_handler("createAttribute (subscriber suffix)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute( SERVER_ID,
+ TupleKey,
+ sizeof(ServerId) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (serverid)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( SERVER_NAME,
+ NoKey,
+ sizeof(char) << 3,
+ SERVER_NAME_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (server name)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( SERVER_READS,
+ NoKey,
+ sizeof(Counter) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (server reads)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SERVER_INSERTS,
+ NoKey,
+ sizeof(Counter) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (server inserts)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SERVER_DELETES,
+ NoKey,
+ sizeof(Counter) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (server deletes)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ if( MySchemaTransaction->execute() == -1 ) {
+ error_handler("schemaTransaction->execute()",
+ MySchemaTransaction->getNdbError(), 0);
+ }
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return 0;
+}
+
+int
+create_table_group(Ndb * pNdb){
+ int check;
+
+ NdbSchemaCon * MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pNdb);
+ if( MySchemaTransaction == NULL )
+ error_handler("startSchemaTransaction", pNdb->getNdbError(), 0);
+
+ NdbSchemaOp * MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL )
+ error_handler("getNdbSchemaOp", MySchemaTransaction->getNdbError(), 0);
+
+ // Create table
+ check = MySchemaOp->createTable( GROUP_TABLE,
+ 8, // Table size
+ TupleKey, // Key Type
+ 1 // Nr of Pages
+ ,All,
+ 6,
+ 78,
+ 80,
+ 1,
+ useTableLogging
+ );
+
+ if( check == -1 )
+ error_handler("createTable", MySchemaTransaction->getNdbError(), 0);
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute( GROUP_ID,
+ TupleKey,
+ sizeof(GroupId) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (group id)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( GROUP_NAME,
+ NoKey,
+ sizeof(char) << 3,
+ GROUP_NAME_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (group name)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( GROUP_ALLOW_READ,
+ NoKey,
+ sizeof(Permission) << 3,
+ 1,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (group read)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( GROUP_ALLOW_INSERT,
+ NoKey,
+ sizeof(Permission) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (group insert)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( GROUP_ALLOW_DELETE,
+ NoKey,
+ sizeof(Permission) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (group delete)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ if( MySchemaTransaction->execute() == -1 ) {
+ error_handler("schemaTransaction->execute()",
+ MySchemaTransaction->getNdbError(), 0);
+ }
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return 0;
+}
+
+int
+create_table_subscriber(Ndb * pNdb){
+ int check;
+ NdbSchemaCon * MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pNdb);
+ if( MySchemaTransaction == NULL )
+ error_handler("startSchemaTransaction", pNdb->getNdbError(), 0);
+
+ NdbSchemaOp * MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL )
+ error_handler("getNdbSchemaOp", MySchemaTransaction->getNdbError(), 0);
+
+ // Create table
+ check = MySchemaOp->createTable( SUBSCRIBER_TABLE,
+ 8, // Table size
+ TupleKey, // Key Type
+ 1 // Nr of Pages
+ ,DistributionGroup,
+ 6,
+ 78,
+ 80,
+ 1,
+ useTableLogging
+ );
+ if( check == -1 )
+ error_handler("createTable", MySchemaTransaction->getNdbError(), 0);
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute
+ ( SUBSCRIBER_NUMBER,
+ TupleKey,
+ sizeof(char) << 3,
+ SUBSCRIBER_NUMBER_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute,
+ (useIndexTables ? IndexStorageAttribute : NormalStorageAttribute),
+ 0,
+ 1,
+ 16);
+ if( check == -1 )
+ error_handler("createAttribute (subscriber number)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SUBSCRIBER_NAME,
+ NoKey,
+ sizeof(char) << 3,
+ SUBSCRIBER_NAME_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (subscriber name)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( SUBSCRIBER_GROUP,
+ NoKey,
+ sizeof(GroupId) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (subscriber_group)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( SUBSCRIBER_LOCATION,
+ NoKey,
+ sizeof(Location) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (server reads)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SUBSCRIBER_SESSIONS,
+ NoKey,
+ sizeof(ActiveSessions) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (subscriber_sessions)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SUBSCRIBER_CHANGED_BY,
+ NoKey,
+ sizeof(char) << 3,
+ CHANGED_BY_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (subscriber_changed_by)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SUBSCRIBER_CHANGED_TIME,
+ NoKey,
+ sizeof(char) << 3,
+ CHANGED_TIME_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (subscriber_changed_time)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ if( MySchemaTransaction->execute() == -1 ) {
+ error_handler("schemaTransaction->execute()",
+ MySchemaTransaction->getNdbError(), 0);
+ }
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return 0;
+}
+
+int
+create_table_session(Ndb * pNdb){
+ int check;
+ NdbSchemaCon * MySchemaTransaction = NdbSchemaCon::startSchemaTrans(pNdb);
+ if( MySchemaTransaction == NULL )
+ error_handler("startSchemaTransaction", pNdb->getNdbError(), 0);
+
+ NdbSchemaOp * MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ if( MySchemaOp == NULL )
+ error_handler("getNdbSchemaOp",
+ MySchemaTransaction->getNdbError(), 0);
+
+ // Create table
+ check = MySchemaOp->createTable( SESSION_TABLE,
+ 8, // Table size
+ TupleKey, // Key Type
+ 1 // Nr of Pages
+ ,DistributionGroup,
+ 6,
+ 78,
+ 80,
+ 1,
+ useTableLogging
+ );
+ if( check == -1 )
+ error_handler("createTable", MySchemaTransaction->getNdbError(), 0);
+
+ check = MySchemaOp->createAttribute( SESSION_SUBSCRIBER,
+ TupleKey,
+ sizeof(char) << 3,
+ SUBSCRIBER_NUMBER_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute,
+ NormalStorageAttribute,
+ 0,
+ 1,
+ 16);
+ if( check == -1 )
+ error_handler("createAttribute (session_subscriber)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ // Create first column, primary key
+ check = MySchemaOp->createAttribute( SESSION_SERVER,
+ TupleKey,
+ sizeof(ServerId) << 3,
+ 1,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (session_server)",
+ MySchemaTransaction->getNdbError(), 0);
+
+
+ check = MySchemaOp->createAttribute( SESSION_DATA,
+ NoKey,
+ sizeof(char) << 3,
+ SESSION_DETAILS_LENGTH,
+ String,
+ MMBased,
+ NotNullAttribute );
+ if( check == -1 )
+ error_handler("createAttribute (session_data)",
+ MySchemaTransaction->getNdbError(), 0);
+
+ if( MySchemaTransaction->execute() == -1 ) {
+ error_handler("schemaTransaction->execute()",
+ MySchemaTransaction->getNdbError(), 0);
+ }
+ NdbSchemaCon::closeSchemaTrans(MySchemaTransaction);
+ return 0;
+}
+
+void
+create_table(const char * name, int (* function)(Ndb * pNdb), Ndb* pNdb){
+ printf("creating table %s...", name);
+ if(pNdb->getDictionary()->getTable(name) != 0){
+ printf(" it already exists\n");
+ return;
+ } else {
+ printf("\n");
+ }
+ function(pNdb);
+ printf("creating table %s... done\n", name);
+}
+
+static int dbCreate(Ndb * pNdb)
+{
+ create_table(SUBSCRIBER_TABLE, create_table_subscriber, pNdb);
+ create_table(GROUP_TABLE , create_table_group, pNdb);
+ create_table(SESSION_TABLE , create_table_session, pNdb);
+ create_table(SERVER_TABLE , create_table_server, pNdb);
+ return 0;
+}
+
+#ifndef NDB_WIN32
+#include <unistd.h>
+#endif
+
+static NdbMutex* startupMutex = NdbMutex_Create();
+
+UserHandle*
+userDbConnect(uint32 createDb, char *dbName)
+{
+ NdbMutex_Lock(startupMutex);
+
+ Ndb * pNdb = new Ndb("");
+
+ //printf("Initializing...\n");
+ pNdb->init();
+
+ //printf("Waiting...");
+ while(pNdb->waitUntilReady() != 0){
+ //printf("...");
+ }
+ // printf("done\n");
+
+ if( createDb )
+ dbCreate(pNdb);
+
+
+ UserHandle * uh = new UserHandle;
+ uh->pNDB = pNdb;
+ uh->pCurrTrans = 0;
+
+ NdbMutex_Unlock(startupMutex);
+
+ return uh;
+}
+
+void userDbDisconnect(UserHandle *uh)
+{
+ delete uh;
+}
+
+int userDbInsertServer(UserHandle *uh,
+ ServerId serverId,
+ SubscriberSuffix suffix,
+ ServerName name)
+{
+ int check;
+
+ uint32 noOfRead = 0;
+ uint32 noOfInsert = 0;
+ uint32 noOfDelete = 0;
+
+ NdbConnection * MyTransaction = 0;
+ if(uh->pCurrTrans != 0){
+ MyTransaction = uh->pCurrTrans;
+ } else {
+ uh->pCurrTrans = MyTransaction = uh->pNDB->startTransaction();
+ }
+ if (MyTransaction == NULL)
+ error_handler("startTranscation", uh->pNDB->getNdbError(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SERVER_TABLE);
+ CHECK_NULL(MyOperation, "getNdbOperation", MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "insert tuple", MyTransaction);
+
+ check = MyOperation->equal(SERVER_ID, (char*)&serverId);
+ CHECK_MINUS_ONE(check, "setValue id", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_SUBSCRIBER_SUFFIX, suffix);
+ CHECK_MINUS_ONE(check, "setValue suffix", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_NAME, name);
+ CHECK_MINUS_ONE(check, "setValue name", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_READS, (char*)&noOfRead);
+ CHECK_MINUS_ONE(check, "setValue reads", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_INSERTS, (char*)&noOfInsert);
+ CHECK_MINUS_ONE(check, "setValue inserts", MyTransaction);
+
+ check = MyOperation->setValue(SERVER_DELETES, (char*)&noOfDelete);
+ CHECK_MINUS_ONE(check, "setValue deletes", MyTransaction);
+
+ return 0;
+}
+
+int userDbInsertSubscriber(UserHandle *uh,
+ SubscriberNumber number,
+ uint32 groupId,
+ SubscriberName name)
+{
+ int check;
+ uint32 activeSessions = 0;
+ Location l = 0;
+ ChangedBy changedBy; snprintf(changedBy, sizeof(changedBy), "ChangedBy");
+ ChangedTime changedTime; snprintf(changedTime, sizeof(changedTime), "ChangedTime");
+
+ NdbConnection * MyTransaction = 0;
+ if(uh->pCurrTrans != 0){
+ MyTransaction = uh->pCurrTrans;
+ } else {
+ uh->pCurrTrans = MyTransaction = uh->pNDB->startTransaction();
+ }
+ if (MyTransaction == NULL)
+ error_handler("startTranscation", uh->pNDB->getNdbError(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(SUBSCRIBER_TABLE);
+ CHECK_NULL(MyOperation, "getNdbOperation", MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "insertTuple", MyTransaction);
+
+ check = MyOperation->equal(SUBSCRIBER_NUMBER, number);
+ CHECK_MINUS_ONE(check, "equal", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_NAME, name);
+ CHECK_MINUS_ONE(check, "setValue name", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_GROUP, (char*)&groupId);
+ CHECK_MINUS_ONE(check, "setValue group", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_LOCATION, (char*)&l);
+ CHECK_MINUS_ONE(check, "setValue location", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_SESSIONS, (char*)&activeSessions);
+ CHECK_MINUS_ONE(check, "setValue sessions", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_CHANGED_BY, changedBy);
+ CHECK_MINUS_ONE(check, "setValue changedBy", MyTransaction);
+
+ check = MyOperation->setValue(SUBSCRIBER_CHANGED_TIME, changedTime);
+ CHECK_MINUS_ONE(check, "setValue changedTime", MyTransaction);
+
+ return 0;
+}
+
+int userDbInsertGroup(UserHandle *uh,
+ GroupId groupId,
+ GroupName name,
+ Permission allowRead,
+ Permission allowInsert,
+ Permission allowDelete)
+{
+ int check;
+
+ NdbConnection * MyTransaction = 0;
+ if(uh->pCurrTrans != 0){
+ MyTransaction = uh->pCurrTrans;
+ } else {
+ uh->pCurrTrans = MyTransaction = uh->pNDB->startTransaction();
+ }
+ if (MyTransaction == NULL)
+ error_handler("startTranscation", uh->pNDB->getNdbError(), 0);
+
+ NdbOperation *MyOperation = MyTransaction->getNdbOperation(GROUP_TABLE);
+ CHECK_NULL(MyOperation, "getNdbOperation", MyTransaction);
+
+ check = MyOperation->insertTuple();
+ CHECK_MINUS_ONE(check, "insertTuple", MyTransaction);
+
+ check = MyOperation->equal(GROUP_ID, (char*)&groupId);
+ CHECK_MINUS_ONE(check, "equal", MyTransaction);
+
+ check = MyOperation->setValue(GROUP_NAME, name);
+ CHECK_MINUS_ONE(check, "setValue name", MyTransaction);
+
+ check = MyOperation->setValue(GROUP_ALLOW_READ, (char*)&allowRead);
+ CHECK_MINUS_ONE(check, "setValue allowRead", MyTransaction);
+
+ check = MyOperation->setValue(GROUP_ALLOW_INSERT, (char*)&allowInsert);
+ CHECK_MINUS_ONE(check, "setValue allowInsert", MyTransaction);
+
+ check = MyOperation->setValue(GROUP_ALLOW_DELETE, (char*)&allowDelete);
+ CHECK_MINUS_ONE(check, "setValue allowDelete", MyTransaction);
+
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userTransaction.c b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userTransaction.c
new file mode 100644
index 00000000000..a2f4787bb0c
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userTransaction.c
@@ -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 */
+
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+
+#include <ndb_global.h>
+#include <time.h>
+
+#include "sql.h"
+#include "sqlext.h"
+
+
+#include "userInterface.h"
+#include "userHandle.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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+static int readSubscriberSessions(UserHandle *uh,
+ SubscriberNumber number,
+ char *transactionType);
+
+/***************************************************************
+* L O C A L D A T A *
+***************************************************************/
+
+extern void handle_error(SQLHDBC hdbc,
+ SQLHENV henv,
+ SQLHSTMT hstmt,
+ SQLRETURN rc,
+ char *filename,
+ int lineno);
+
+/***************************************************************
+* 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 int readSubscriberSessions(UserHandle *uh,
+ SubscriberNumber number,
+ char *transactionType)
+{
+ SQLRETURN rc;
+
+ /*-----------------------------------------------------*/
+ /* SELECT activeSessions,groupId,changedBy,changedTime */
+ /* FROM SUBSCRIBER */
+ /* WHERE subscriberNumber=x; */
+ /*-----------------------------------------------------*/
+ strcpy(uh->readSubscriberSession.values.number,number);
+
+ rc = SQLExecute(uh->readSubscriberSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("%s %s\n",
+ transactionType,
+ "Unable to execute read subscriber session");
+ return(-1);
+ }
+
+ rc = SQLFetch(uh->readSubscriberSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("%s %s\n",
+ transactionType,
+ "Unable to fetch read subscriber session");
+ return(-1);
+ }
+
+ return(0);
+}
+
+/***************************************************************
+****************************************************************
+* 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 *
+****************************************************************
+***************************************************************/
+
+void userTransaction_T1(UserHandle *uh,
+ SubscriberNumber number,
+ Location new_location,
+ ChangedBy changed_by,
+ ChangedTime changed_time)
+{
+ SQLRETURN rc;
+
+ if(!uh) return;
+
+ /*---------------------------------------------*/
+ /* Update the subscriber information */
+ /* */
+ /* UPDATE SUBSCRIBER */
+ /* SET location=x, changedBy=x, changedTime=x */
+ /* WHERE subscriberNumber=x; */
+ /*---------------------------------------------*/
+ strcpy(uh->updateSubscriber.values.number, number);
+ uh->updateSubscriber.values.location = new_location;
+ strcpy(uh->updateSubscriber.values.changedBy, changed_by);
+ strcpy(uh->updateSubscriber.values.changedTime, changed_time);
+
+ rc = SQLExecute(uh->updateSubscriber.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T1 Unable to execute update subscriber\n");
+ return;
+ }
+
+ userDbCommit(uh);
+}
+
+void userTransaction_T2(UserHandle *uh,
+ SubscriberNumber number,
+ Location *new_location,
+ ChangedBy changed_by,
+ ChangedTime changed_time,
+ SubscriberName subscriberName)
+{
+ SQLRETURN rc;
+
+ if(!uh) return;
+
+ /*------------------------------------------------------*/
+ /* Read the information from the subscriber table */
+ /* */
+ /* SELECT location,subscriberName,changedBy,changedTime */
+ /* FROM SUBSCRIBER */
+ /* WHERE subscriberNumber=x; */
+ /*------------------------------------------------------*/
+ strcpy(uh->readSubscriber.values.number,number);
+
+ rc = SQLExecute(uh->readSubscriber.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T2 Unable to execute read subscriber\n");
+ return;
+ }
+
+ rc = SQLFetch(uh->readSubscriber.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T2 Unable to fetch read subscriber\n");
+ return;
+ }
+
+ userDbCommit(uh);
+
+ strcpy(subscriberName, uh->readSubscriber.values.name);
+ *new_location = uh->readSubscriber.values.location;
+ strcpy(changed_by, uh->readSubscriber.values.changedBy);
+ strcpy(changed_time, uh->readSubscriber.values.changedTime);
+}
+
+void userTransaction_T3(UserHandle *uh,
+ SubscriberNumber number,
+ ServerId server_id,
+ ServerBit server_bit,
+ SessionDetails session_details,
+ unsigned int *branch_executed)
+{
+ SQLRETURN rc;
+
+ if(!uh) return;
+
+ *branch_executed = 0;
+
+ /*--------------------------------------*/
+ /* Read active sessions from subscriber */
+ /*--------------------------------------*/
+ if( readSubscriberSessions(uh, number, "T3") < 0 )
+ return;
+
+ /*-----------------------------------------------*/
+ /* Read the 'read' Permissions for the userGroup */
+ /* */
+ /* SELECT allowRead */
+ /* FROM USERGROUP */
+ /* WHERE groupId=x */
+ /*-----------------------------------------------*/
+ uh->readGroupAllowRead.values.groupId = uh->readSubscriberSession.values.groupId;
+
+ rc = SQLExecute(uh->readGroupAllowRead.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T3 Unable to execute read group allow read\n");
+ return;
+ }
+
+ rc = SQLFetch(uh->readGroupAllowRead.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T3 Unable to fetch read group allow read\n");
+ return;
+ }
+
+ if( uh->readGroupAllowRead.values.allowRead & server_bit &&
+ uh->readSubscriberSession.values.activeSessions & server_bit ) {
+
+ /*----------------------------------------------------*/
+ /* Read the sessionDetails from the userSession table */
+ /* */
+ /* SELECT sessionData */
+ /* FROM userSession */
+ /* WHERE subscriberNumber=x, serverId=x */
+ /*----------------------------------------------------*/
+ strcpy(uh->readSessionDetails.values.number,number);
+ uh->readSessionDetails.values.serverId = server_id;
+
+ rc = SQLExecute(uh->readSessionDetails.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T3 Unable to execute read session details\n");
+ return;
+ }
+
+ rc = SQLFetch(uh->readSessionDetails.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T3 Unable to fetch read session details\n");
+ return;
+ }
+
+ strcpy(session_details, uh->readSessionDetails.values.details);
+
+ /*----------------------------------------*/
+ /* Increment noOfRead field in the server */
+ /* */
+ /* UPDATE server */
+ /* SET noOfRead=noOfRead+1 */
+ /* WHERE serverId=x,subscriberSuffix=x */
+ /*----------------------------------------*/
+ uh->updateServerNoOfRead.values.serverId = server_id;
+ strcpy(uh->updateServerNoOfRead.values.suffix,
+ &number[SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH]);
+
+ rc = SQLExecute(uh->updateServerNoOfRead.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T3 Unable to execute read no of read\n");
+ return;
+ }
+
+ *branch_executed = 1;
+ }
+
+ userDbCommit(uh);
+}
+
+void userTransaction_T4(UserHandle *uh,
+ SubscriberNumber number,
+ ServerId server_id,
+ ServerBit server_bit,
+ SessionDetails session_details,
+ unsigned int do_rollback,
+ unsigned int *branch_executed)
+{
+ SQLRETURN rc;
+
+ if(!uh) return;
+
+ *branch_executed = 0;
+
+ /*--------------------------------------*/
+ /* Read active sessions from subscriber */
+ /*--------------------------------------*/
+ if( readSubscriberSessions(uh, number, "T4") < 0 )
+ return;
+
+ /*-------------------------------------------------*/
+ /* Read the 'insert' Permissions for the userGroup */
+ /* */
+ /* SELECT allowInsert */
+ /* FROM USERGROUP */
+ /* WHERE groupId=x */
+ /*-------------------------------------------------*/
+ uh->readGroupAllowInsert.values.groupId = uh->readSubscriberSession.values.groupId;
+
+ rc = SQLExecute(uh->readGroupAllowInsert.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T4 Unable to execute read group allow insert\n");
+ return;
+ }
+
+ rc = SQLFetch(uh->readGroupAllowInsert.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T4 Unable to fetch read group allow insert\n");
+ return;
+ }
+
+ if( uh->readGroupAllowInsert.values.allowInsert & server_bit &&
+ !(uh->readSubscriberSession.values.activeSessions & server_bit) ) {
+
+ /*---------------------------------------------*/
+ /* Insert the session to the userSession table */
+ /* */
+ /* INSERT INTO userSession */
+ /* VALUES (x,x,x) */
+ /*---------------------------------------------*/
+ strcpy(uh->insertSession.values.number, number);
+ uh->insertSession.values.serverId = server_id;
+ strcpy(uh->insertSession.values.details, session_details);
+
+ rc = SQLExecute(uh->insertSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+handle_error(uh->hdbc, uh->henv, uh->insertSession.stmt, rc, __FILE__, __LINE__);
+ printf("T4 Unable to execute insert session \n");
+ return;
+ }
+
+ /*----------------------------------------*/
+ /* Update subscriber activeSessions field */
+ /* */
+ /* UPDATE subscriber */
+ /* SET activeSessions=x */
+ /* WHERE subscriberNumber=x */
+ /*----------------------------------------*/
+ strcpy(uh->updateSubscriberSession.values.number, number);
+ uh->updateSubscriberSession.values.activeSessions =
+ uh->readSubscriberSession.values.activeSessions | server_bit;
+
+ rc = SQLExecute(uh->updateSubscriberSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T4 Unable to execute update session \n");
+ return;
+ }
+
+ /*------------------------------------------*/
+ /* Increment noOfInsert field in the server */
+ /* */
+ /* UPDATE server */
+ /* SET noOfInsert=noOfInsert+1 */
+ /* WHERE serverId=x,subscriberSuffix=x */
+ /*------------------------------------------*/
+ uh->updateServerNoOfInsert.values.serverId = server_id;
+ strcpy(uh->updateServerNoOfInsert.values.suffix,
+ &number[SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH]);
+
+ rc = SQLExecute(uh->updateServerNoOfInsert.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T4 Unable to execute update no of read\n");
+ return;
+ }
+
+ *branch_executed = 1;
+ }
+
+ if(do_rollback)
+ userDbRollback(uh);
+ else
+ userDbCommit(uh);
+}
+
+void userTransaction_T5(UserHandle *uh,
+ SubscriberNumber number,
+ ServerId server_id,
+ ServerBit server_bit,
+ unsigned int do_rollback,
+ unsigned int *branch_executed)
+{
+ SQLRETURN rc;
+
+ if(!uh) return;
+
+ *branch_executed = 0;
+
+ /*--------------------------------------*/
+ /* Read active sessions from subscriber */
+ /*--------------------------------------*/
+ if( readSubscriberSessions(uh, number, "T5") < 0 )
+ return;
+
+ /*-------------------------------------------------*/
+ /* Read the 'delete' Permissions for the userGroup */
+ /* */
+ /* SELECT allowDelete */
+ /* FROM USERGROUP */
+ /* WHERE groupId=x */
+ /*-------------------------------------------------*/
+ uh->readGroupAllowDelete.values.groupId = uh->readSubscriberSession.values.groupId;
+
+ rc = SQLExecute(uh->readGroupAllowDelete.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T5 Unable to execute read group allow delete\n");
+ return;
+ }
+
+ rc = SQLFetch(uh->readGroupAllowDelete.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T5 Unable to fetch read group allow delete\n");
+ return;
+ }
+
+ if( uh->readGroupAllowDelete.values.allowDelete & server_bit &&
+ uh->readSubscriberSession.values.activeSessions & server_bit ) {
+
+ /*-----------------------------------------------*/
+ /* Delete the session from the userSession table */
+ /* */
+ /* DELETE FROM userSession */
+ /* WHERE subscriberNumber=x,serverId=x */
+ /*-----------------------------------------------*/
+ strcpy(uh->deleteSession.values.number,number);
+ uh->deleteSession.values.serverId = server_id;
+
+ rc = SQLExecute(uh->deleteSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T5 Unable to execute delete session\n");
+ return;
+ }
+
+ /*----------------------------------------*/
+ /* Update subscriber activeSessions field */
+ /* */
+ /* UPDATE subscriber */
+ /* SET activeSessions=x */
+ /* WHERE subscriberNumber=x */
+ /*----------------------------------------*/
+ strcpy(uh->updateSubscriberSession.values.number, number);
+ uh->updateSubscriberSession.values.activeSessions =
+ uh->readSubscriberSession.values.activeSessions & ~server_bit;
+
+ rc = SQLExecute(uh->updateSubscriberSession.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T5 Unable to execute update subscriber session \n");
+ return;
+ }
+
+ /*------------------------------------------*/
+ /* Increment noOfDelete field in the server */
+ /* */
+ /* UPDATE server */
+ /* SET noOfDelete=noOfDelete+1 */
+ /* WHERE serverId=x,subscriberSuffix=x */
+ /*------------------------------------------*/
+ uh->updateServerNoOfDelete.values.serverId = server_id;
+ strcpy(uh->updateServerNoOfDelete.values.suffix,
+ &number[SUBSCRIBER_NUMBER_LENGTH-SUBSCRIBER_NUMBER_SUFFIX_LENGTH]);
+
+ rc = SQLExecute(uh->updateServerNoOfDelete.stmt);
+ if( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
+ printf("T5 Unable to execute update no of delete\n");
+ return;
+ }
+
+ *branch_executed = 1;
+ }
+
+ if(do_rollback)
+ userDbRollback(uh);
+ else
+ userDbCommit(uh);
+}
+
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/restarter/Makefile b/storage/ndb/test/ndbapi/old_dirs/restarter/Makefile
new file mode 100644
index 00000000000..041fbfd82ba
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/restarter/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := restarter
+
+# Source files of non-templated classes (.C files)
+SOURCES = restarter.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/restarter2/Makefile b/storage/ndb/test/ndbapi/old_dirs/restarter2/Makefile
new file mode 100644
index 00000000000..ba33a2e21dc
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/restarter2/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := restarter2
+
+# Source files of non-templated classes (.C files)
+SOURCES = restarter2.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/restarts/Makefile b/storage/ndb/test/ndbapi/old_dirs/restarts/Makefile
new file mode 100644
index 00000000000..9f14b81fae5
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/restarts/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := restarts
+
+# Source files of non-templated classes (.C files)
+SOURCES = restarts.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/ronja/Makefile b/storage/ndb/test/ndbapi/old_dirs/ronja/Makefile
new file mode 100644
index 00000000000..a11a27c5fd7
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/ronja/Makefile
@@ -0,0 +1,6 @@
+include .defs.mk
+
+DIRS = initronja \
+ benchronja
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/ronja/benchronja/Makefile b/storage/ndb/test/ndbapi/old_dirs/ronja/benchronja/Makefile
new file mode 100644
index 00000000000..f0521c3ba77
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/ronja/benchronja/Makefile
@@ -0,0 +1,10 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+
+BIN_TARGET := benchronja
+
+SOURCES := benchronja.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/ronja/initronja/Makefile b/storage/ndb/test/ndbapi/old_dirs/ronja/initronja/Makefile
new file mode 100644
index 00000000000..dd66dd813d1
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/ronja/initronja/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := initronja
+
+SOURCES := initronja.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/telco/Makefile b/storage/ndb/test/ndbapi/old_dirs/telco/Makefile
new file mode 100644
index 00000000000..8f82c714119
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/telco/Makefile
@@ -0,0 +1,10 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := telco
+
+# Source files of non-templated classes (.C files)
+SOURCES = msa.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/telco/readme b/storage/ndb/test/ndbapi/old_dirs/telco/readme
new file mode 100644
index 00000000000..627b4256eef
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/telco/readme
@@ -0,0 +1,9 @@
+
+adoInsertRecs.cpp - the original evaluation program
+
+InsertRecs.cpp - replaced ado with ndb api, still windows only
+
+msa.cpp - removed windows and exceptions, portable
+
+
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/testBackup/Makefile b/storage/ndb/test/ndbapi/old_dirs/testBackup/Makefile
new file mode 100644
index 00000000000..abf47dcfb2d
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testBackup/Makefile
@@ -0,0 +1,8 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testBackup
+SOURCES = testBackup.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testBasic/Makefile b/storage/ndb/test/ndbapi/old_dirs/testBasic/Makefile
new file mode 100644
index 00000000000..755b19939cb
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testBasic/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := testBasic
+
+SOURCES := testBasic.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testBlobs/Makefile b/storage/ndb/test/ndbapi/old_dirs/testBlobs/Makefile
new file mode 100644
index 00000000000..cc5bb629c17
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testBlobs/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testBlobs
+
+SOURCES = testBlobs.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
+CCFLAGS_LOC += -I$(NDB_TOP)/include/kernel
diff --git a/storage/ndb/test/ndbapi/old_dirs/testDataBuffers/Makefile b/storage/ndb/test/ndbapi/old_dirs/testDataBuffers/Makefile
new file mode 100644
index 00000000000..181fbc829d4
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testDataBuffers/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testDataBuffers
+
+SOURCES = testDataBuffers.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testDict/Makefile b/storage/ndb/test/ndbapi/old_dirs/testDict/Makefile
new file mode 100644
index 00000000000..75d493c3424
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testDict/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testDict
+
+SOURCES = testDict.cpp
+
+CFLAGS_testDict.cpp := -I$(call fixpath,$(NDB_TOP)/include/kernel)
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testGrep/Makefile b/storage/ndb/test/ndbapi/old_dirs/testGrep/Makefile
new file mode 100644
index 00000000000..6bad3d56a00
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testGrep/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE = ndbapitest
+DIRS = verify
+BIN_TARGET = testGrep
+SOURCES = testGrep.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/ndbapi/old_dirs/testGrep/verify/Makefile b/storage/ndb/test/ndbapi/old_dirs/testGrep/verify/Makefile
new file mode 100644
index 00000000000..256e3c98f36
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testGrep/verify/Makefile
@@ -0,0 +1,10 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testGrepVerify
+SOURCES = testGrepVerify.cpp
+
+CFLAGS_testGrepVerify.cpp += -I$(call fixpath,$(NDB_TOP)/include/kernel) -I$(call fixpath,$(NDB_TOP)/include/mgmcommon)
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testIndex/Makefile b/storage/ndb/test/ndbapi/old_dirs/testIndex/Makefile
new file mode 100644
index 00000000000..e5cd4542c9c
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testIndex/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testIndex
+
+SOURCES = testIndex.cpp
+
+CFLAGS_testIndex.cpp := -I$(call fixpath,$(NDB_TOP)/include/kernel)
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testInterpreter/Makefile b/storage/ndb/test/ndbapi/old_dirs/testInterpreter/Makefile
new file mode 100644
index 00000000000..e84287a1b16
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testInterpreter/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testInterpreter
+
+SOURCES = testInterpreter.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testMgm/Makefile b/storage/ndb/test/ndbapi/old_dirs/testMgm/Makefile
new file mode 100644
index 00000000000..be50d3dae7e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testMgm/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testMgm
+
+SOURCES = testMgm.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testNdbApi/Makefile b/storage/ndb/test/ndbapi/old_dirs/testNdbApi/Makefile
new file mode 100644
index 00000000000..3bb3cba427e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testNdbApi/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testNdbApi
+
+SOURCES = testNdbApi.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testNodeRestart/Makefile b/storage/ndb/test/ndbapi/old_dirs/testNodeRestart/Makefile
new file mode 100644
index 00000000000..8c13ab3beb4
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testNodeRestart/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testNodeRestart
+
+SOURCES = testNodeRestart.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testOIBasic/Makefile b/storage/ndb/test/ndbapi/old_dirs/testOIBasic/Makefile
new file mode 100644
index 00000000000..1bbbcf1d17e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testOIBasic/Makefile
@@ -0,0 +1,13 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testOIBasic
+
+SOURCES = testOIBasic.cpp
+
+ifeq ($(NDB_COMPILER),GCC)
+CCFLAGS_WARNINGS += -Wno-unused -Wformat
+endif
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testOIBasic/times.txt b/storage/ndb/test/ndbapi/old_dirs/testOIBasic/times.txt
new file mode 100644
index 00000000000..641e9ddb4bf
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testOIBasic/times.txt
@@ -0,0 +1,8 @@
+one db-node
+testOIBasic -case t -table 1 -index 1 -fragtype small -threads 10 -rows 5000 -subloop 1
+------------------------------------------------------------
+040331
+build index - 5769 ms per 50000 ( 115 ms per 1000 )
+update - 5962 ms per 50000 ( 119 ms per 1000 )
+update indexed - 14851 ms per 50000 ( 297 ms per 1000 )
+overhead - 149 pct
diff --git a/storage/ndb/test/ndbapi/old_dirs/testOperations/Makefile b/storage/ndb/test/ndbapi/old_dirs/testOperations/Makefile
new file mode 100644
index 00000000000..25546ade639
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testOperations/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := testOperations
+
+SOURCES := testOperations.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testOrderedIndex/Makefile b/storage/ndb/test/ndbapi/old_dirs/testOrderedIndex/Makefile
new file mode 100644
index 00000000000..d8899a37895
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testOrderedIndex/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testOrderedIndex
+
+SOURCES = testOrderedIndex.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testRestartGci/Makefile b/storage/ndb/test/ndbapi/old_dirs/testRestartGci/Makefile
new file mode 100644
index 00000000000..24f449b747d
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testRestartGci/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := testRestartGci
+
+SOURCES := testRestartGci.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testScan/Makefile b/storage/ndb/test/ndbapi/old_dirs/testScan/Makefile
new file mode 100644
index 00000000000..fe48f5bc926
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testScan/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testScan
+
+SOURCES = testScan.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testScanInterpreter/Makefile b/storage/ndb/test/ndbapi/old_dirs/testScanInterpreter/Makefile
new file mode 100644
index 00000000000..c7d96494148
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testScanInterpreter/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testScanInterpreter
+
+SOURCES = testScanInterpreter.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testSystemRestart/Makefile b/storage/ndb/test/ndbapi/old_dirs/testSystemRestart/Makefile
new file mode 100644
index 00000000000..7a306eb313d
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testSystemRestart/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testSystemRestart
+
+SOURCES = testSystemRestart.cpp
+
+CFLAGS_testSystemRestart.cpp := -I$(call fixpath,$(NDB_TOP)/include/kernel)
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testTimeout/Makefile b/storage/ndb/test/ndbapi/old_dirs/testTimeout/Makefile
new file mode 100644
index 00000000000..01a9df9887f
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testTimeout/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE = ndbapitest
+
+BIN_TARGET = testTimeout
+
+SOURCES = testTimeout.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/testTransactions/Makefile b/storage/ndb/test/ndbapi/old_dirs/testTransactions/Makefile
new file mode 100644
index 00000000000..0279a526923
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/testTransactions/Makefile
@@ -0,0 +1,10 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := testTransactions
+
+SOURCES := testTransactions.cpp
+CFLAGS_testTransactions.cpp := -I$(call fixpath,$(NDB_TOP)/include/kernel)
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/test_event/Makefile b/storage/ndb/test/ndbapi/old_dirs/test_event/Makefile
new file mode 100644
index 00000000000..6299fa47845
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/test_event/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := test_event
+
+SOURCES := test_event.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/vw_test/Makefile b/storage/ndb/test/ndbapi/old_dirs/vw_test/Makefile
new file mode 100644
index 00000000000..144873dcc69
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/vw_test/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := vw_test
+BIN_TARGET_LIBS := orafunctr decode cirk inifunc
+BIN_TARGET_LIBS_DIRS := /home/ndb/junk/vw/ndb/lib
+
+SOURCES := cdrserver.C
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/ndbapi/old_dirs/vw_test/bcd.h b/storage/ndb/test/ndbapi/old_dirs/vw_test/bcd.h
new file mode 100644
index 00000000000..d0aaffbd8b7
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/vw_test/bcd.h
@@ -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 <ndb_global.h>
+
+struct bcdtab {
+ char tab[3];
+};
+
+int dec2hex(int dec,int last);
+int bcd_code (char *bcd_in,char *bcd_out);
+int bcd_decode (int bcd_len,char *bcd_in,char *bcd_out);
+int bcd_decode2 (int bcd_len,char *bcd_in,char *bcd_out);
diff --git a/storage/ndb/test/ndbapi/old_dirs/vw_test/script/client_start b/storage/ndb/test/ndbapi/old_dirs/vw_test/script/client_start
new file mode 100644
index 00000000000..2965be6fbb5
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/vw_test/script/client_start
@@ -0,0 +1,10 @@
+# Argument to the client program is:
+# 1. ip-adress to the server
+# 2. location to the raw cdr-file
+# 3. nanoseconds (0-1000) between writing the buffer to the port
+# 4. how many writes to the buffer before the sleep command should accur
+# Argument 3 and 4 controlls the flow of the raw cdr-file to the cdrserver
+
+cd $VCDRPATH/bin
+# ./client stat181.xxx.com /ext06/data/indata_fraud1/port2file.data.-2089540139 1000 0 &
+./client xxx.xxx.xxx.xxx.xxx /export2/home/ndb/vw/data/port2file_data_-2089540139 0 100000 &
diff --git a/storage/ndb/test/ndbapi/old_dirs/vw_test/utv.h b/storage/ndb/test/ndbapi/old_dirs/vw_test/utv.h
new file mode 100644
index 00000000000..6f378e5595b
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/vw_test/utv.h
@@ -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 <semaphore.h>
+#include <thread.h>
+#include <limits.h>
+
+#define TESTLEV
+
+#define ASubscriberNumber_SIZE 16
+#define BSubscriberNumber_SIZE 29
+#define TRUE 1
+#define FALSE 0
+#define WRITE_LIMIT 100000
+#define EVER ;;
+#define CONNINFO "/"
+#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
+
+#define BIT_1 0x1
+#define BIT_2 0x2
+#define BIT_3 0x4
+#define BIT_4 0x8
+#define BIT_5 0x10
+#define BIT_6 0x20
+#define BIT_7 0x40
+#define BIT_8 0x80
+
+/*------------------------------------------------------*/
+/* record defines structure over an alarm thresholds */
+/* CallAttemptState Beskriver status på samtal */
+/* 0 - Subscriber is calling */
+/* 1 - Called part answer call */
+/* 2 - Release of call */
+/* 3-255 reserved for furter use */
+/* USED_FILEDS Indicates active fields within call */
+/* bit 1 - START_TIME */
+/* 2 - TimeForStartOfCharge */
+/* 3 - TimeForStopOfCharge */
+/* 4 - ReroutingIndicator */
+/* 5 - RINParameter */
+/* 6 - ACategory */
+/* 7 - EndOfSelectionInformation */
+/* 8 - UserToUserIndicatior */
+/* 9 - UserToUserInformation */
+/* 10 - CauseCode */
+/* 11 - ASubscriberNumber */
+/* 12 - BSubscriberNumber */
+/* 13 - RedirectingNumber */
+/* 14 - OriginalCalledNumber */
+/* 15 - LocationCode */
+/* 16 - OriginatingPointCode */
+/* 17 - DestinationPointCode */
+/* 18 - CircuitIdentificationCode */
+/* 19 - NetworkIndicator */
+/*------------------------------------------------------*/
+
+struct cdr_record
+{
+ unsigned int USED_FIELDS;
+ unsigned long ClientId;
+ unsigned int CallIdentificationNumber;
+ unsigned int START_TIME;
+ unsigned int OurSTART_TIME;
+ unsigned int TimeForStartOfCharge;
+ unsigned int TimeForStopOfCharge;
+ time_t OurTimeForStartOfCharge;
+ time_t OurTimeForStopOfCharge;
+ unsigned short DestinationPointCode;
+ unsigned short CircuitIdentificationCode;
+ unsigned short OriginatingPointCode;
+ unsigned short ReroutingIndicator;
+ unsigned short RINParameter;
+ char NetworkIndicator;
+ char CallAttemptState;
+ char ACategory;
+ char EndOfSelectionInformation;
+ char UserToUserInformation;
+ char UserToUserIndicatior;
+ char CauseCode;
+ char ASubscriberNumber[ASubscriberNumber_SIZE];
+ char ASubscriberNumberLength;
+ char TonASubscriberNumber;
+ char BSubscriberNumber[BSubscriberNumber_SIZE];
+ char BSubscriberNumberLength;
+ char TonBSubscriberNumber;
+ char RedirectingNumber[16];
+ char TonRedirectingNumber;
+ char OriginalCalledNumber[16];
+ char TonOriginalCalledNumber;
+ char LocationCode[16];
+ char TonLocationCode;
+};
+
+/*------------------------------------------------------*/
+/* Define switches for each tag */
+/*------------------------------------------------------*/
+
+#define B_START_TIME 0x1
+#define B_TimeForStartOfCharge 0x2
+#define B_TimeForStopOfCharge 0x4
+#define B_ReroutingIndicator 0x8
+#define B_RINParameter 0x10
+#define B_ACategory 0x20
+#define B_EndOfSelectionInformation 0x40
+#define B_UserToUserIndicatior 0x80
+#define B_UserToUserInformation 0x100
+#define B_CauseCode 0x200
+#define B_ASubscriberNumber 0x400
+#define B_BSubscriberNumber 0x800
+#define B_RedirectingNumber 0x1000
+#define B_OriginalCalledNumber 0x2000
+#define B_LocationCode 0x4000
+#define B_OriginatingPointCode 0x8000
+#define B_DestinationPointCode 0x10000
+#define B_CircuitIdentificationCode 0x20000
+
+#define B_NetworkIndicator 0x40000
+#define B_TonASubscriberNumber 0x80000
+#define B_TonBSubscriberNumber 0x100000
+#define B_TonRedirectingNumber 0x200000
+#define B_TonOriginalCalledNumber 0x4000000
+#define B_TonLocationCode 0x8000000
+
+#define K_START_TIME 0xFF01
+#define K_TimeForStartOfCharge 0xFF02
+#define K_TimeForStopOfCharge 0xFF03
+#define K_ReroutingIndicator 0x13
+#define K_RINParameter 0xFC
+#define K_ACategory 0x09
+#define K_EndOfSelectionInformation 0x11
+#define K_UserToUserIndicatior 0x2A
+#define K_UserToUserInformation 0x20
+#define K_CauseCode 0x12
+#define K_ASubscriberNumber 0x0A
+#define K_BSubscriberNumber 0x04
+#define K_RedirectingNumber 0x0B
+#define K_OriginalCalledNumber 0x28
+#define K_LocationCode 0x3F
+#define K_OriginatingPointCode 0xFD
+#define K_DestinationPointCode 0xFE
+#define K_CircuitIdentificationCode 0xFF
+
+#define K_NetworkIndicator 0xF0
+#define K_TonASubscriberNumber 0xF1
+#define K_TonBSubscriberNumber 0xF2
+#define K_TonRedirectingNumber 0xF3
+#define K_TonOriginalCalledNumber 0xF4
+#define K_TonLocationCode 0xF5
diff --git a/storage/ndb/test/ndbapi/old_dirs/vw_test/vcdrfunc.h b/storage/ndb/test/ndbapi/old_dirs/vw_test/vcdrfunc.h
new file mode 100644
index 00000000000..3c5444d733b
--- /dev/null
+++ b/storage/ndb/test/ndbapi/old_dirs/vw_test/vcdrfunc.h
@@ -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 */
+
+/********************************************************/
+/* Common functions */
+/* unix_ps checks if a process is running with a */
+/* name and pid rc 0=not running */
+/* 1=Running */
+/* logname create a log filename */
+/* Parm */
+/* 1 lvl1 name */
+/* 2 lvl2 name */
+/* 3 lvl3 name */
+/* m2log Skriv log rader som moder */
+/* Parm */
+/* 1 pointer to filehandler */
+/* 2 Log text max 600 tecken */
+/* c2log Skriv log rader som barn */
+/* Parm */
+/* 1 pointer to filehandler */
+/* 2 Log text max 600 tecken */
+/* n2log Skriv log rader utan relation */
+/* Parm */
+/* 1 pointer to filehandler */
+/* 2 Log text max 600 tecken */
+/********************************************************/
+
+int n2log(FILE *fi,char *text);
+int m2log(FILE *fi,char *text);
+int c2log(FILE *fi,char *text);
+int checkchangelog(FILE* fp,char *filename);
+void logname(char *filename, char *lvl1, char *lvl2, char *lvl3);
+void logname_unique_day(char *filename, char *lvl1, char *lvl2, char *lvl3);
+int unix_ps(char *proc_name,char *pid);
+/*
+int unix_ps2(char *proc_name,char *pid);
+*/
+int unix_ps3(char *proc_name);
+int replacetoken(const char* instring,char token,char replace);
+int CompAsciiNum(char *, char *);
+int CompareIt(char *,char *);
+int CompCdrNum(const void *,const void *,void *);
diff --git a/storage/ndb/test/ndbapi/restarter.cpp b/storage/ndb/test/ndbapi/restarter.cpp
new file mode 100644
index 00000000000..d6831494b48
--- /dev/null
+++ b/storage/ndb/test/ndbapi/restarter.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 "mgmapi.h"
+#include <string.h>
+#include <NdbMain.h>
+#include <OutputStream.hpp>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+
+#include <NdbRestarter.hpp>
+#include <NdbRestarts.hpp>
+#include <NDBT.hpp>
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ const char* _hostName = NULL;
+ int _loops = 10;
+ int _wait = 15;
+ int _help = 0;
+ int _error_insert = 0;
+ int _initial = 0;
+ int _master = 0;
+ int _maxwait = 120;
+ int _multiple = 0;
+
+ struct getargs args[] = {
+ { "seconds", 's', arg_integer, &_wait,
+ "Seconds to wait between each restart(0=random)", "secs" },
+ { "max seconds", 'm', arg_integer, &_maxwait,
+ "Max seconds to wait between each restart. Default is 120 seconds",
+ "msecs" },
+ { "loops", 'l', arg_integer, &_loops,
+ "Number of loops(0=forever)", "loops"},
+ { "initial", 'i', arg_flag, &_initial, "Initial node restart"},
+ { "error-insert", 'e', arg_flag, &_error_insert, "Use error insert"},
+ { "master", 'm', arg_flag, &_master,
+ "Restart the master"},
+ { "multiple", 'x', arg_flag, &_multiple,
+ "Multiple random node restarts. OBS! Even and odd node Ids must be separated into each node group"},
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "hostname:port\n"\
+ "This program will connect to the mgmsrv of a NDB cluster.\n"\
+ "It will then wait for all nodes to be started, then restart node(s)\n"\
+ "and wait for all to restart inbetween. It will do this \n"\
+ "loop number of times\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _hostName = argv[optind];
+
+
+ NdbRestarts restarts(_hostName);
+ NdbRestarter restarter(_hostName);
+
+ const char* restartName = "";
+ if (_multiple){
+ if (_master){
+ restartName = "TwoMasterNodeFailure";
+ }
+ else {
+ // Restart 50 percent of nodes
+ restartName = "FiftyPercentFail";
+ }
+ }
+ else if (_master){
+ restartName = "RestartMasterNodeError";
+ }else {
+ if (_error_insert)
+ restartName = "RestartRandomNodeError";
+ else if (_initial)
+ restartName = "RestartRandomNodeInitial";
+ else
+ restartName = "RestartRandomNode";
+ }
+
+ ndbout << "Performing " << restartName << endl;
+
+ int result = NDBT_OK;
+ int l = 0;
+ while (_loops == 0 || l<_loops){
+
+ g_info << "Waiting for cluster to start" << endl;
+ while (restarter.waitClusterStarted(1) != 0){
+ //g_warning << "Ndb failed to start in 2 minutes" << endl;
+ }
+
+ int seconds = _wait;
+ if(seconds==0) {
+ // Create random value, default 120 secs
+ seconds = (rand() % _maxwait) + 1;
+ }
+ g_info << "Waiting for " << seconds << "(" << _maxwait
+ << ") secs " << endl;
+ NdbSleep_SecSleep(seconds);
+
+ g_info << l << ": Restarting node(s) " << endl;
+
+ if (restarts.executeRestart(restartName) != 0){
+ result = NDBT_FAILED;
+ break;
+ }
+
+ l++;
+ }
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/test/ndbapi/restarter2.cpp b/storage/ndb/test/ndbapi/restarter2.cpp
new file mode 100644
index 00000000000..846748a7bba
--- /dev/null
+++ b/storage/ndb/test/ndbapi/restarter2.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 */
+
+
+#include <string.h>
+#include <NdbMain.h>
+#include <OutputStream.hpp>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+
+#include <NdbRestarter.hpp>
+#include <NDBT.hpp>
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ const char* _hostName = NULL;
+ int _loops = 10;
+ int _wait = 15;
+ int _help = 0;
+#if 0
+ int _crash = 0;
+ int _abort = 0;
+#endif
+
+ struct getargs args[] = {
+ { "seconds", 's', arg_integer, &_wait, "Seconds to wait between each restart(0=random)", "secs" },
+ { "loops", 'l', arg_integer, &_loops, "Number of loops", "loops 0=forever"},
+#if 0
+ // Not yet!
+ { "abort", 'a', arg_flag, &_abort, "Restart abort"},
+ { "crash", 'c', arg_flag, &_crash, "Crash instead of restart"},
+#endif
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "hostname:port\n"\
+ "This program will connect to the mgmsrv of a NDB cluster.\n"\
+ "It will wait for all nodes to be started, then restart all nodes\n"\
+ "into nostart state. Then after a random delay it will tell all nodes\n"\
+ "to start. It will do this loop number of times\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _hostName = argv[optind];
+
+ NdbRestarter restarter(_hostName);
+#if 0
+ if(_abort && _crash){
+ g_err << "You can't specify both abort and crash" << endl;
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ if(_abort){
+ restarter.setRestartType(NdbRestarter::AbortRestart);
+ }
+ if(_crash){
+ restarter.setRestartType(NdbRestarter::Crash);
+ }
+#endif
+
+ int l = 0;
+ while (_loops == 0 || l<_loops){
+ g_info << "Waiting for cluster to start" << endl;
+ while(restarter.waitClusterStarted(120) != 0){
+ g_warning << "Ndb failed to start in 2 minutes" << endl;
+ }
+
+ int seconds = _wait;
+ if(seconds==0)
+ seconds = (rand() % 120) + 1; // Create random value max 120 secs
+ g_info << "Waiting for "<<seconds<<" secs" << endl;
+ NdbSleep_SecSleep(seconds);
+
+ g_info << l << ": restarting all nodes with nostart" << endl;
+ const bool b = (restarter.restartAll(false, true, false) == 0);
+ assert(b);
+
+ g_info << "Waiting for cluster to enter nostart" << endl;
+ while(restarter.waitClusterNoStart(120) != 0){
+ g_warning << "Ndb failed to enter no start in 2 minutes" << endl;
+ }
+
+ seconds = _wait;
+ if(seconds==0)
+ seconds = (rand() % 120) + 1; // Create random value max 120 secs
+ g_info << "Waiting for " <<seconds<<" secs" << endl;
+ NdbSleep_SecSleep(seconds);
+
+ g_info << l << ": Telling all nodes to start" << endl;
+ const bool b2 = (restarter.startAll() == 0);
+ assert(b2);
+
+ l++;
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/test/ndbapi/restarts.cpp b/storage/ndb/test/ndbapi/restarts.cpp
new file mode 100644
index 00000000000..184e754de4a
--- /dev/null
+++ b/storage/ndb/test/ndbapi/restarts.cpp
@@ -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 */
+
+
+#include "mgmapi.h"
+#include <string.h>
+#include <NdbMain.h>
+#include <OutputStream.hpp>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+
+#include <NdbRestarts.hpp>
+#include <NDBT.hpp>
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ const char* _restartName = NULL;
+ int _loops = 1;
+ int _wait = -1;
+ int _maxwait = 120;
+ int _help = 0;
+ int _list = 0;
+ int _random = 0;
+ int _timeout = 0;
+ int _all = 0;
+
+ struct getargs args[] = {
+ { "seconds", 's', arg_integer, &_wait,
+ "Seconds to wait between each restart(0=random)", "secs" },
+ { "max seconds", 'm', arg_integer, &_maxwait,
+ "Max seconds to wait between each restart. Default is 120 seconds", "msecs" },
+ { "loops", 'l', arg_integer, &_loops, "Number of loops(0=forever)", "loops"},
+ { "timeout", 't', arg_integer, &_timeout, "Timeout waiting for nodes to start", "seconds"},
+ { "random", 'r', arg_flag, &_random, "Select restart case randomly",
+ ""},
+ { "all", 'a', arg_flag, &_all, "Run all restarts",
+ ""},
+ { "list-restarts", '\0', arg_flag, &_list, "List available restarts", ""},
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "testname\n" \
+ "This program will perform node restart, \n"\
+ "multiple node restart or system-restart.\n"\
+ "Use --list-restarts to see whats available\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ if (_list){
+ NdbRestarts restarts;
+ restarts.listRestarts();
+ return NDBT_ProgramExit(NDBT_OK);
+ }
+
+ if ((argv[optind] == NULL) && (_random == 0)){
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _restartName = argv[optind];
+
+ NdbRestarts restarts;
+
+ int res = NDBT_OK;
+ int l = 0;
+ while (_loops == 0 || l < _loops){
+
+ if (_all) {
+ // Execute all restarts, set loops to numRestarts
+ // so that ecvery restart is executed once
+ _loops = restarts.getNumRestarts();
+ res = restarts.executeRestart(l, _timeout);
+ } else if (_random) {
+ int num = rand() % restarts.getNumRestarts();
+ res = restarts.executeRestart(num, _timeout);
+ } else {
+ res = restarts.executeRestart(_restartName, _timeout);
+ }
+ if (res != NDBT_OK)
+ break;
+
+ if (_wait >= 0){
+ int seconds = _wait;
+ if(seconds==0) {
+ // Create random value, default 120 secs
+ seconds = (rand() % _maxwait) + 1;
+ }
+ g_info << "Waiting for " << seconds << "(" << _maxwait
+ << ") secs " << endl;
+ NdbSleep_SecSleep(seconds);
+ }
+
+ l++;
+ }
+ return NDBT_ProgramExit(res);
+}
diff --git a/storage/ndb/test/ndbapi/size.cpp b/storage/ndb/test/ndbapi/size.cpp
new file mode 100644
index 00000000000..ff178b11d68
--- /dev/null
+++ b/storage/ndb/test/ndbapi/size.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 <ndb_global.h>
+#include "utv.h"
+
+int main(void)
+{
+ ndb_init();
+ printf("cdrstruct=%d\n",sizeof(struct cdr_record));
+ printf("long int=%d\n",sizeof(long int));
+ printf("int=%d\n",sizeof(int));
+ printf("short int=%d\n",sizeof(short int));
+ return 0;
+}
diff --git a/storage/ndb/test/ndbapi/slow_select.cpp b/storage/ndb/test/ndbapi/slow_select.cpp
new file mode 100644
index 00000000000..8d615fa5771
--- /dev/null
+++ b/storage/ndb/test/ndbapi/slow_select.cpp
@@ -0,0 +1,228 @@
+
+#include <ndb_global.h>
+#include <NdbApi.hpp>
+#include <NdbOut.hpp>
+#include <NdbTick.h>
+
+struct
+S_Scan {
+ const char * m_table;
+ const char * m_index;
+ NdbIndexScanOperation * m_scan;
+ Uint32 metaid;
+ Uint32 match_count;
+ Uint32 row_count;
+};
+
+static S_Scan g_scans[] = {
+ { "affiliatestometa", "ind_affiliatestometa", 0, 0, 0, 0 },
+ { "media", "metaid", 0, 0, 0, 0 },
+ { "meta", "PRIMARY", 0, 0, 0, 0 },
+ { "artiststometamap", "PRIMARY", 0, 0, 0, 0 },
+ { "subgenrestometamap", "metaid", 0, 0, 0, 0 }
+};
+
+#define require(x) if(!(x)) { ndbout << "LINE: " << __LINE__ << endl;abort(); }
+#define require2(o, x) if(!(x)) { ndbout << o->getNdbError() << endl; abort(); }
+Uint32 g_affiliateid = 2;
+Uint32 g_formatids[] = { 8, 31, 76 };
+
+Uint64 start;
+Uint32 g_artistid = 0;
+Uint32 g_subgenreid = 0;
+
+NdbConnection* g_trans = 0;
+static void lookup();
+
+int
+main(void){
+ ndb_init();
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return 1;
+ }
+
+ Ndb g_ndb(&con, "test");
+ g_ndb.init(1024);
+
+ require(g_ndb.waitUntilReady() == 0);
+
+ while(true){
+ g_trans = g_ndb.startTransaction();
+ require(g_trans);
+
+ size_t i, j;
+ const size_t cnt = sizeof(g_scans)/sizeof(g_scans[0]);
+
+ start = NdbTick_CurrentMillisecond();
+
+ for(i = 0; i<cnt; i++){
+ ndbout_c("starting scan on: %s %s",
+ g_scans[i].m_table, g_scans[i].m_index);
+ g_scans[i].m_scan = g_trans->getNdbIndexScanOperation(g_scans[i].m_index,
+ g_scans[i].m_table);
+ NdbIndexScanOperation* scan = g_scans[i].m_scan;
+ require(scan);
+ require(scan->readTuples(NdbScanOperation::LM_CommittedRead,
+ 0, 0, true) == 0);
+ }
+
+ require(!g_scans[0].m_scan->setBound((Uint32)0,
+ NdbIndexScanOperation::BoundEQ,
+ &g_affiliateid,
+ sizeof(g_affiliateid)));
+#if 0
+ require(!g_scans[1].m_scan->setBound((Uint32)0,
+ NdbIndexScanOperation::BoundLE,
+ &g_formatids[0],
+ sizeof(g_formatids[0])));
+#endif
+
+ NdbScanFilter sf(g_scans[1].m_scan);
+ sf.begin(NdbScanFilter::OR);
+ sf.eq(2, g_formatids[0]);
+ sf.eq(2, g_formatids[1]);
+ sf.eq(2, g_formatids[2]);
+ sf.end();
+
+ // affiliatestometa
+ require(g_scans[0].m_scan->getValue("uniquekey"));
+ require(g_scans[0].m_scan->getValue("xml"));
+
+ // media
+ require(g_scans[1].m_scan->getValue("path"));
+ require(g_scans[1].m_scan->getValue("mediaid"));
+ require(g_scans[1].m_scan->getValue("formatid"));
+
+ // meta
+ require(g_scans[2].m_scan->getValue("name"));
+ require(g_scans[2].m_scan->getValue("xml"));
+
+ // artiststometamap
+ require(g_scans[3].m_scan->getValue("artistid", (char*)&g_artistid));
+
+ // subgenrestometamap
+ require(g_scans[4].m_scan->getValue("subgenreid", (char*)&g_subgenreid));
+
+ for(i = 0; i<cnt; i++){
+ g_scans[i].m_scan->getValue("metaid", (char*)&g_scans[i].metaid);
+ }
+
+ g_trans->execute(NoCommit, AbortOnError, 1);
+
+ Uint32 max_val = 0;
+ Uint32 match_val = 0;
+
+ S_Scan * F [5], * Q [5], * nextF [5];
+ Uint32 F_sz = 0, Q_sz = 0;
+ for(i = 0; i<cnt; i++){
+ F_sz++;
+ F[i] = &g_scans[i];
+ }
+
+ Uint32 match_count = 0;
+ while(F_sz > 0){
+ Uint32 prev_F_sz = F_sz;
+ F_sz = 0;
+ bool found = false;
+ //for(i = 0; i<cnt; i++)
+ //ndbout_c("%s - %d", g_scans[i].m_table, g_scans[i].metaid);
+
+ for(i = 0; i<prev_F_sz; i++){
+ int res = F[i]->m_scan->nextResult();
+ if(res == -1)
+ abort();
+
+ if(res == 1){
+ continue;
+ }
+
+ Uint32 metaid = F[i]->metaid;
+ F[i]->row_count++;
+
+ if(metaid == match_val){
+ //ndbout_c("flera");
+ nextF[F_sz++] = F[i];
+ require(F_sz >= 0 && F_sz <= cnt);
+ F[i]->match_count++;
+ Uint32 comb = 1;
+ for(j = 0; j<cnt; j++){
+ comb *= (&g_scans[j] == F[i] ? 1 : g_scans[j].match_count);
+ }
+ match_count += comb;
+ found = true;
+ continue;
+ }
+ if(metaid < max_val){
+ nextF[F_sz++] = F[i];
+ require(F_sz >= 0 && F_sz <= cnt);
+ continue;
+ }
+ if(metaid > max_val){
+ for(j = 0; j<Q_sz; j++)
+ nextF[F_sz++] = Q[j];
+ require(F_sz >= 0 && F_sz <= cnt);
+ Q_sz = 0;
+ max_val = metaid;
+ }
+ Q[Q_sz++] = F[i];
+ require(Q_sz >= 0 && Q_sz <= cnt);
+ }
+ if(F_sz == 0 && Q_sz > 0){
+ match_val = max_val;
+ for(j = 0; j<Q_sz; j++){
+ nextF[F_sz++] = Q[j];
+ Q[j]->match_count = 1;
+ }
+ require(F_sz >= 0 && F_sz <= cnt);
+ require(Q_sz >= 0 && Q_sz <= cnt);
+ Q_sz = 0;
+ match_count++;
+ lookup();
+ } else if(!found && F_sz + Q_sz < cnt){
+ F_sz = 0;
+ }
+ require(F_sz >= 0 && F_sz <= cnt);
+ for(i = 0; i<F_sz; i++)
+ F[i] = nextF[i];
+ }
+
+ start = NdbTick_CurrentMillisecond() - start;
+ ndbout_c("Elapsed: %lldms", start);
+
+ ndbout_c("rows: %d", match_count);
+ for(i = 0; i<cnt; i++){
+ ndbout_c("%s : %d", g_scans[i].m_table, g_scans[i].row_count);
+ }
+ g_trans->close();
+ }
+}
+
+static
+void
+lookup(){
+ {
+ NdbOperation* op = g_trans->getNdbOperation("artists");
+ require2(g_trans, op);
+ require2(op, op->readTuple() == 0);
+ require2(op, op->equal("artistid", g_artistid) == 0);
+ require2(op, op->getValue("name"));
+ }
+
+ {
+ NdbOperation* op = g_trans->getNdbOperation("subgenres");
+ require2(g_trans, op);
+ require2(op, op->readTuple() == 0);
+ require2(op, op->equal("subgenreid", g_subgenreid) == 0);
+ require2(op, op->getValue("name"));
+ }
+
+ static int loop = 0;
+ if(loop++ >= 16){
+ loop = 0;
+ require(g_trans->execute(NoCommit) == 0);
+ }
+ //require(g_trans->restart() == 0);
+}
diff --git a/storage/ndb/test/ndbapi/testBackup.cpp b/storage/ndb/test/ndbapi/testBackup.cpp
new file mode 100644
index 00000000000..f3594514c5b
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testBackup.cpp
@@ -0,0 +1,491 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbBackup.hpp>
+
+
+#define CHECK(b) if (!(b)) { \
+ g_err << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ continue; }
+
+
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+bool testMaster = true;
+bool testSlave = false;
+
+int setMaster(NDBT_Context* ctx, NDBT_Step* step){
+ testMaster = true;
+ testSlave = false;
+ return NDBT_OK;
+}
+int setMasterAsSlave(NDBT_Context* ctx, NDBT_Step* step){
+ testMaster = true;
+ testSlave = true;
+ return NDBT_OK;
+}
+int setSlave(NDBT_Context* ctx, NDBT_Step* step){
+ testMaster = false;
+ testSlave = true;
+ return NDBT_OK;
+}
+
+int runAbort(NDBT_Context* ctx, NDBT_Step* step){
+ NdbBackup backup(GETNDB(step)->getNodeId()+1);
+
+ NdbRestarter restarter;
+
+ if (restarter.getNumDbNodes() < 2){
+ ctx->stopTest();
+ return NDBT_OK;
+ }
+
+ if(restarter.waitClusterStarted(60) != 0){
+ g_err << "Cluster failed to start" << endl;
+ return NDBT_FAILED;
+ }
+
+ if (testMaster) {
+ if (testSlave) {
+ if (backup.NFMasterAsSlave(restarter) == -1){
+ return NDBT_FAILED;
+ }
+ } else {
+ if (backup.NFMaster(restarter) == -1){
+ return NDBT_FAILED;
+ }
+ }
+ } else {
+ if (backup.NFSlave(restarter) == -1){
+ return NDBT_FAILED;
+ }
+ }
+
+ return NDBT_OK;
+}
+
+int runFail(NDBT_Context* ctx, NDBT_Step* step){
+ NdbBackup backup(GETNDB(step)->getNodeId()+1);
+
+ NdbRestarter restarter;
+
+ if (restarter.getNumDbNodes() < 2){
+ ctx->stopTest();
+ return NDBT_OK;
+ }
+
+ if(restarter.waitClusterStarted(60) != 0){
+ g_err << "Cluster failed to start" << endl;
+ return NDBT_FAILED;
+ }
+
+ if (testMaster) {
+ if (testSlave) {
+ if (backup.FailMasterAsSlave(restarter) == -1){
+ return NDBT_FAILED;
+ }
+ } else {
+ if (backup.FailMaster(restarter) == -1){
+ return NDBT_FAILED;
+ }
+ }
+ } else {
+ if (backup.FailSlave(restarter) == -1){
+ return NDBT_FAILED;
+ }
+ }
+
+ return NDBT_OK;
+}
+
+int runBackupOne(NDBT_Context* ctx, NDBT_Step* step){
+ NdbBackup backup(GETNDB(step)->getNodeId()+1);
+ unsigned backupId = 0;
+
+ if (backup.start(backupId) == -1){
+ return NDBT_FAILED;
+ }
+ ndbout << "Started backup " << backupId << endl;
+ ctx->setProperty("BackupId", backupId);
+
+ return NDBT_OK;
+}
+
+int runRestartInitial(NDBT_Context* ctx, NDBT_Step* step){
+ NdbRestarter restarter;
+
+ Ndb* pNdb = GETNDB(step);
+
+ const NdbDictionary::Table *tab = ctx->getTab();
+ pNdb->getDictionary()->dropTable(tab->getName());
+
+ if (restarter.restartAll(true) != 0)
+ return NDBT_FAILED;
+
+ if (restarter.waitClusterStarted() != 0)
+ return NDBT_FAILED;
+
+ return NDBT_OK;
+}
+
+int runRestoreOne(NDBT_Context* ctx, NDBT_Step* step){
+ NdbBackup backup(GETNDB(step)->getNodeId()+1);
+ unsigned backupId = ctx->getProperty("BackupId");
+
+ ndbout << "Restoring backup " << backupId << endl;
+
+ if (backup.restore(backupId) == -1){
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int runVerifyOne(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int count = 0;
+
+ ndbout << *(const NDBT_Table*)ctx->getTab() << endl;
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ do{
+
+ // Check that there are as many records as we expected
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+
+ g_err << "count = " << count;
+ g_err << " records = " << records;
+ g_err << endl;
+
+ CHECK(count == records);
+
+ // Read and verify every record
+ CHECK(hugoTrans.pkReadRecords(pNdb, records) == 0);
+
+ } while (false);
+
+ return result;
+}
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable2(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runDropTable(NDBT_Context* ctx, NDBT_Step* step){
+ GETNDB(step)->getDictionary()->dropTable(ctx->getTab()->getName());
+ return NDBT_OK;
+}
+
+#include "bank/Bank.hpp"
+
+int runCreateBank(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank(ctx->m_cluster_connection);
+ int overWriteExisting = true;
+ if (bank.createAndLoadBank(overWriteExisting, 10) != NDBT_OK)
+ return NDBT_FAILED;
+ return NDBT_OK;
+}
+
+int runBankTimer(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank(ctx->m_cluster_connection);
+ int wait = 30; // Max seconds between each "day"
+ int yield = 1; // Loops before bank returns
+
+ while (ctx->isTestStopped() == false) {
+ bank.performIncreaseTime(wait, yield);
+ }
+ return NDBT_OK;
+}
+
+int runBankTransactions(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank(ctx->m_cluster_connection);
+ int wait = 10; // Max ms between each transaction
+ int yield = 100; // Loops before bank returns
+
+ while (ctx->isTestStopped() == false) {
+ bank.performTransactions(wait, yield);
+ }
+ return NDBT_OK;
+}
+
+int runBankGL(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank(ctx->m_cluster_connection);
+ int yield = 20; // Loops before bank returns
+ int result = NDBT_OK;
+
+ while (ctx->isTestStopped() == false) {
+ if (bank.performMakeGLs(yield) != NDBT_OK){
+ ndbout << "bank.performMakeGLs FAILED" << endl;
+ result = NDBT_FAILED;
+ }
+ }
+ return NDBT_OK;
+}
+
+int runBankSum(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank(ctx->m_cluster_connection);
+ int wait = 2000; // Max ms between each sum of accounts
+ int yield = 1; // Loops before bank returns
+ int result = NDBT_OK;
+
+ while (ctx->isTestStopped() == false) {
+ if (bank.performSumAccounts(wait, yield) != NDBT_OK){
+ ndbout << "bank.performSumAccounts FAILED" << endl;
+ result = NDBT_FAILED;
+ }
+ }
+ return result ;
+}
+
+int runDropBank(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank(ctx->m_cluster_connection);
+ if (bank.dropBank() != NDBT_OK)
+ return NDBT_FAILED;
+ return NDBT_OK;
+}
+
+int runBackupBank(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int l = 0;
+ int maxSleep = 30; // Max seconds between each backup
+ Ndb* pNdb = GETNDB(step);
+ NdbBackup backup(GETNDB(step)->getNodeId()+1);
+ unsigned minBackupId = ~0;
+ unsigned maxBackupId = 0;
+ unsigned backupId = 0;
+ int result = NDBT_OK;
+
+ while (l < loops && result != NDBT_FAILED){
+
+ if (pNdb->waitUntilReady() != 0){
+ result = NDBT_FAILED;
+ continue;
+ }
+
+ // Sleep for a while
+ NdbSleep_SecSleep(maxSleep);
+
+ // Perform backup
+ if (backup.start(backupId) != 0){
+ ndbout << "backup.start failed" << endl;
+ result = NDBT_FAILED;
+ continue;
+ }
+ ndbout << "Started backup " << backupId << endl;
+
+ // Remember min and max backupid
+ if (backupId < minBackupId)
+ minBackupId = backupId;
+
+ if (backupId > maxBackupId)
+ maxBackupId = backupId;
+
+ ndbout << " maxBackupId = " << maxBackupId
+ << ", minBackupId = " << minBackupId << endl;
+ ctx->setProperty("MinBackupId", minBackupId);
+ ctx->setProperty("MaxBackupId", maxBackupId);
+
+ l++;
+ }
+
+ ctx->stopTest();
+
+ return result;
+}
+
+int runRestoreBankAndVerify(NDBT_Context* ctx, NDBT_Step* step){
+ NdbRestarter restarter;
+ NdbBackup backup(GETNDB(step)->getNodeId()+1);
+ unsigned minBackupId = ctx->getProperty("MinBackupId");
+ unsigned maxBackupId = ctx->getProperty("MaxBackupId");
+ unsigned backupId = minBackupId;
+ int result = NDBT_OK;
+ int errSumAccounts = 0;
+ int errValidateGL = 0;
+
+ ndbout << " maxBackupId = " << maxBackupId << endl;
+ ndbout << " minBackupId = " << minBackupId << endl;
+
+ while (backupId <= maxBackupId){
+
+ // TEMPORARY FIX
+ // To erase all tables from cache(s)
+ // To be removed, maybe replaced by ndb.invalidate();
+ {
+ Bank bank(ctx->m_cluster_connection);
+
+ if (bank.dropBank() != NDBT_OK){
+ result = NDBT_FAILED;
+ break;
+ }
+ }
+ // END TEMPORARY FIX
+
+ ndbout << "Performing initial restart" << endl;
+ if (restarter.restartAll(true) != 0)
+ return NDBT_FAILED;
+
+ if (restarter.waitClusterStarted() != 0)
+ return NDBT_FAILED;
+
+ ndbout << "Restoring backup " << backupId << endl;
+ if (backup.restore(backupId) == -1){
+ return NDBT_FAILED;
+ }
+ ndbout << "Backup " << backupId << " restored" << endl;
+
+ // Let bank verify
+ Bank bank(ctx->m_cluster_connection);
+
+ int wait = 0;
+ int yield = 1;
+ if (bank.performSumAccounts(wait, yield) != 0){
+ ndbout << "bank.performSumAccounts FAILED" << endl;
+ ndbout << " backupId = " << backupId << endl << endl;
+ result = NDBT_FAILED;
+ errSumAccounts++;
+ }
+
+ if (bank.performValidateAllGLs() != 0){
+ ndbout << "bank.performValidateAllGLs FAILED" << endl;
+ ndbout << " backupId = " << backupId << endl << endl;
+ result = NDBT_FAILED;
+ errValidateGL++;
+ }
+
+ backupId++;
+ }
+
+ if (result != NDBT_OK){
+ ndbout << "Verification of backup failed" << endl
+ << " errValidateGL="<<errValidateGL<<endl
+ << " errSumAccounts="<<errSumAccounts<<endl << endl;
+ }
+
+ return result;
+}
+
+NDBT_TESTSUITE(testBackup);
+TESTCASE("BackupOne",
+ "Test that backup and restore works on one table \n"
+ "1. Load table\n"
+ "2. Backup\n"
+ "3. Restart -i\n"
+ "4. Restore\n"
+ "5. Verify count and content of table\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runBackupOne);
+ INITIALIZER(runRestartInitial);
+ INITIALIZER(runRestoreOne);
+ VERIFIER(runVerifyOne);
+ FINALIZER(runClearTable);
+}
+TESTCASE("BackupBank",
+ "Test that backup and restore works during transaction load\n"
+ " by backing up the bank"
+ "1. Create bank\n"
+ "2a. Start bank and let it run\n"
+ "2b. Perform loop number of backups of the bank\n"
+ " when backups are finished tell bank to close\n"
+ "3. Restart ndb -i and reload each backup\n"
+ " let bank verify that the backup is consistent\n"
+ "4. Drop bank\n"){
+ INITIALIZER(runCreateBank);
+ STEP(runBankTimer);
+ STEP(runBankTransactions);
+ STEP(runBankTransactions);
+ STEP(runBankTransactions);
+ STEP(runBankTransactions);
+ STEP(runBankTransactions);
+ STEP(runBankTransactions);
+ STEP(runBankTransactions);
+ STEP(runBankTransactions);
+ STEP(runBankTransactions);
+ STEP(runBankTransactions);
+ STEP(runBankGL);
+ // TODO STEP(runBankSum);
+ STEP(runBackupBank);
+ VERIFIER(runRestoreBankAndVerify);
+ // FINALIZER(runDropBank);
+}
+TESTCASE("NFMaster",
+ "Test that backup behaves during node failiure\n"){
+ INITIALIZER(setMaster);
+ STEP(runAbort);
+
+}
+TESTCASE("NFMasterAsSlave",
+ "Test that backup behaves during node failiure\n"){
+ INITIALIZER(setMasterAsSlave);
+ STEP(runAbort);
+
+}
+TESTCASE("NFSlave",
+ "Test that backup behaves during node failiure\n"){
+ INITIALIZER(setSlave);
+ STEP(runAbort);
+
+}
+TESTCASE("FailMaster",
+ "Test that backup behaves during node failiure\n"){
+ INITIALIZER(setMaster);
+ STEP(runFail);
+
+}
+TESTCASE("FailMasterAsSlave",
+ "Test that backup behaves during node failiure\n"){
+ INITIALIZER(setMasterAsSlave);
+ STEP(runFail);
+
+}
+TESTCASE("FailSlave",
+ "Test that backup behaves during node failiure\n"){
+ INITIALIZER(setSlave);
+ STEP(runFail);
+
+}
+NDBT_TESTSUITE_END(testBackup);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ return testBackup.execute(argc, argv);
+}
+
+
diff --git a/storage/ndb/test/ndbapi/testBasic.cpp b/storage/ndb/test/ndbapi/testBasic.cpp
new file mode 100644
index 00000000000..4d64b15ecfa
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testBasic.cpp
@@ -0,0 +1,1298 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT_Test.hpp>
+#include <NDBT_ReturnCodes.h>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+
+#define GETNDB(ps) ((NDBT_NdbApiStep*)ps)->getNdb()
+
+
+/**
+ * TODO
+ * dirtyWrite, write, dirtyUpdate
+ * delete should be visible to same transaction
+ *
+ */
+int runLoadTable2(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records, 512, false, 0, true) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runInsert(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ // Insert records, dont allow any
+ // errors(except temporary) while inserting
+ if (hugoTrans.loadTable(GETNDB(step), records, 1, false) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runInsertTwice(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ // Insert records, expect primary key violation 630
+ if (hugoTrans.loadTable(GETNDB(step), records, 1, false) != 630){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runVerifyInsert(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.pkDelRecords(GETNDB(step), records, 1, false) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runInsertUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ g_info << i << ": ";
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ g_info << endl;
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ g_info << endl;
+ return NDBT_OK;
+}
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.pkDelRecords(GETNDB(step), records, batchSize) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runPkDelete(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ g_info << i << ": ";
+ if (hugoTrans.pkDelRecords(GETNDB(step), records) != 0){
+ g_info << endl;
+ return NDBT_FAILED;
+ }
+ // Load table, don't allow any primary key violations
+ if (hugoTrans.loadTable(GETNDB(step), records, 512, false) != 0){
+ g_info << endl;
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ g_info << endl;
+ return NDBT_OK;
+}
+
+
+int runPkRead(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ g_info << i << ": ";
+ if (hugoTrans.pkReadRecords(GETNDB(step), records, batchSize) != NDBT_OK){
+ g_info << endl;
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ g_info << endl;
+ return NDBT_OK;
+}
+
+int runPkDirtyRead(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int i = 0;
+ bool dirty = true;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ g_info << i << ": ";
+ if (hugoTrans.pkReadRecords(GETNDB(step), records, batchSize,
+ NdbOperation::LM_CommittedRead) != NDBT_OK){
+ g_info << endl;
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ g_info << endl;
+ return NDBT_OK;
+}
+
+int runPkReadUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ g_info << i << ": ";
+ if (hugoTrans.pkReadRecords(GETNDB(step), records, batchSize) != 0){
+ g_info << endl;
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ g_info << endl;
+ return NDBT_OK;
+}
+
+int runPkUpdate(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ g_info << "|- " << i << ": ";
+ if (hugoTrans.pkUpdateRecords(GETNDB(step), records, batchSize) != 0){
+ g_info << endl;
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ g_info << endl;
+ return NDBT_OK;
+}
+
+int runPkUpdateUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped()) {
+ g_info << i << ": ";
+ if (hugoTrans.pkUpdateRecords(GETNDB(step), records, batchSize) != 0){
+ g_info << endl;
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ g_info << endl;
+ return NDBT_OK;
+}
+
+int runLocker(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ if (hugoTrans.lockRecords(GETNDB(step), records, 10, 500) != 0){
+ result = NDBT_FAILED;
+ }
+ ctx->stopTest();
+
+ return result;
+}
+
+int
+runInsertOne(NDBT_Context* ctx, NDBT_Step* step){
+
+ if(ctx->getProperty("InsertCommitted", (Uint32)0) != 0){
+ abort();
+ }
+
+ while(ctx->getProperty("Read1Performed", (Uint32)0) == 0){
+ NdbSleep_MilliSleep(20);
+ }
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ if (hugoTrans.loadTable(GETNDB(step), 1, 1) != 0){
+ return NDBT_FAILED;
+ }
+
+ ctx->setProperty("InsertCommitted", 1);
+
+ NdbSleep_SecSleep(2);
+
+ return NDBT_OK;
+}
+
+static
+int
+readOneNoCommit(Ndb* pNdb, NdbConnection* pTrans,
+ const NdbDictionary::Table* tab,NDBT_ResultRow * row){
+ int a;
+ NdbOperation * pOp = pTrans->getNdbOperation(tab->getName());
+ if (pOp == NULL){
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ HugoTransactions tmp(*tab);
+
+ int check = pOp->readTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Define primary keys
+ for(a = 0; a<tab->getNoOfColumns(); a++){
+ if (tab->getColumn(a)->getPrimaryKey() == true){
+ if(tmp.equalForAttr(pOp, a, 0) != 0){
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ // Define attributes to read
+ for(a = 0; a<tab->getNoOfColumns(); a++){
+ if((row->attributeStore(a) =
+ pOp->getValue(tab->getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ ERR(err);
+ return err.code;
+ }
+ return NDBT_OK;
+}
+
+int
+runReadOne(NDBT_Context* ctx, NDBT_Step* step){
+
+ Ndb* pNdb = GETNDB(step);
+ const NdbDictionary::Table* tab = ctx->getTab();
+ NDBT_ResultRow row1(*tab);
+ NDBT_ResultRow row2(*tab);
+
+ if(ctx->getProperty("Read1Performed", (Uint32)0) != 0){
+ abort();
+ }
+
+ if(ctx->getProperty("InsertCommitted", (Uint32)0) != 0){
+ abort();
+ }
+
+ NdbConnection * pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ abort();
+ }
+
+ // Read a record with NoCommit
+ // Since the record isn't inserted yet it wil return 626
+ const int res1 = readOneNoCommit(pNdb, pTrans, tab, &row1);
+ g_info << "|- res1 = " << res1 << endl;
+
+ ctx->setProperty("Read1Performed", 1);
+
+ while(ctx->getProperty("InsertCommitted", (Uint32)0) == 0 &&
+ !ctx->isTestStopped()){
+ g_info << "|- Waiting for insert" << endl;
+ NdbSleep_MilliSleep(20);
+ }
+
+ if(ctx->isTestStopped()){
+ abort();
+ }
+
+ // Now the record should have been inserted
+ // Read it once again in the same transaction
+ // Should also reutrn 626 if reads are consistent
+
+ // NOTE! Currently it's not possible to start a new operation
+ // on a transaction that has returned an error code
+ // This is wat fail in this test
+ // MASV 20030624
+ const int res2 = readOneNoCommit(pNdb, pTrans, tab, &row2);
+
+ pTrans->execute(Commit);
+ pNdb->closeTransaction(pTrans);
+ g_info << "|- res2 = " << res2 << endl;
+
+ if (res2 == 626 && res1 == res2)
+ return NDBT_OK;
+ else
+ return NDBT_FAILED;
+}
+
+int runFillTable(NDBT_Context* ctx, NDBT_Step* step){
+ int batch = 512; //4096;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.fillTable(GETNDB(step), batch ) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runClearTable2(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable2(GETNDB(step), records, 240) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+#define CHECK(b) if (!(b)) { \
+ ndbout << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ break; }
+
+int runNoCommitSleep(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+ int sleepTime = 100; // ms
+ for (int i = 2; i < 8; i++){
+
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ ndbout << i <<": Sleeping for " << sleepTime << " ms" << endl;
+ NdbSleep_MilliSleep(sleepTime);
+
+ // Dont care about result of these ops
+ hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Exclusive);
+ hugoOps.closeTransaction(pNdb);
+
+ sleepTime = sleepTime *i;
+ }
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runCommit626(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Commit transaction
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 626);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Commit transaction
+ // Multiple operations
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 2, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 3, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 626);
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runCommit630(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Commit transaction
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkInsertRecord(pNdb, 1) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 630);
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runCommit_TryCommit626(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Commit transaction, TryCommit
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb, TryCommit) == 626);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Commit transaction, TryCommit
+ // Several operations in one transaction
+ // The insert is OK
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 2, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 3, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.pkInsertRecord(pNdb, 1) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 4, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb, TryCommit) == 626);
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runCommit_TryCommit630(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Commit transaction, TryCommit
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkInsertRecord(pNdb, 1) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb, TryCommit) == 630);
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runCommit_CommitAsMuchAsPossible626(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Commit transaction, CommitAsMuchAsPossible
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb, CommitAsMuchAsPossible) == 626);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Commit transaction, CommitAsMuchAsPossible
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 2, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 3, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.pkInsertRecord(pNdb, 1) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb, CommitAsMuchAsPossible) == 626);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+ } while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runCommit_CommitAsMuchAsPossible630(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Commit transaction, CommitAsMuchAsPossible
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkInsertRecord(pNdb, 1) == 0);
+ CHECK(hugoOps.pkDeleteRecord(pNdb, 2) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb, CommitAsMuchAsPossible) == 630);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 2) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+ } while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runNoCommit626(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // No commit transaction, readTuple
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Read) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 626);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // No commit transaction, readTupleExcluive
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 626);
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runNoCommit630(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // No commit transaction
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkInsertRecord(pNdb, 1) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 630);
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runNoCommitRollback626(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // No commit transaction, rollback
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 626);
+ CHECK(hugoOps.execute_Rollback(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // No commit transaction, rollback
+ // Multiple operations
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 2, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 3, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 4, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 626);
+ CHECK(hugoOps.execute_Rollback(pNdb) == 0);
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runNoCommitRollback630(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // No commit transaction, rollback
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkInsertRecord(pNdb, 1) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 630);
+ CHECK(hugoOps.execute_Rollback(pNdb) == 0);
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+
+int runNoCommitAndClose(NDBT_Context* ctx, NDBT_Step* step){
+ int i, result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Read
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ for (i = 0; i < 10; i++)
+ CHECK(hugoOps.pkReadRecord(pNdb, i, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Update
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ for (i = 0; i < 10; i++)
+ CHECK(hugoOps.pkUpdateRecord(pNdb, i) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Delete
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ for (i = 0; i < 10; i++)
+ CHECK(hugoOps.pkDeleteRecord(pNdb, i) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Try to insert, record should already exist
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ for (i = 0; i < 10; i++)
+ CHECK(hugoOps.pkInsertRecord(pNdb, i) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 630);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+
+
+int runCheckRollbackDelete(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+
+ // Read value and save it for later
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 5) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+ CHECK(hugoOps.saveCopyOfRecord() == NDBT_OK);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Delete record 5
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkDeleteRecord(pNdb, 5) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ // Check record is deleted
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 626);
+ CHECK(hugoOps.execute_Rollback(pNdb) == 0);
+
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Check record is not deleted
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Check record is back to original value
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+ CHECK(hugoOps.compareRecordToCopy() == NDBT_OK);
+
+
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runCheckRollbackUpdate(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+ int numRecords = 5;
+ do{
+
+ // Read value and save it for later
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, numRecords) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+ CHECK(hugoOps.verifyUpdatesValue(0) == NDBT_OK); // Update value 0
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Update record 5
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkUpdateRecord(pNdb, 1, numRecords, 5) == 0);// Updates value 5
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ // Check record is updated
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, numRecords, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+ CHECK(hugoOps.verifyUpdatesValue(5) == NDBT_OK); // Updates value 5
+ CHECK(hugoOps.execute_Rollback(pNdb) == 0);
+
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Check record is back to original value
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 1, numRecords, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+ CHECK(hugoOps.verifyUpdatesValue(0) == NDBT_OK); // Updates value 0
+
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runCheckRollbackDeleteMultiple(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Read value and save it for later
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 10) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+ CHECK(hugoOps.verifyUpdatesValue(0) == NDBT_OK);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ Uint32 updatesValue = 0;
+ Uint32 j;
+ for(Uint32 i = 0; i<1; i++){
+ // Read record 5 - 10
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 10, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ for(j = 0; j<10; j++){
+ // Update record 5 - 10
+ updatesValue++;
+ CHECK(hugoOps.pkUpdateRecord(pNdb, 5, 10, updatesValue) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 10, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+ CHECK(hugoOps.verifyUpdatesValue(updatesValue) == 0);
+ }
+
+ for(j = 0; j<10; j++){
+ // Delete record 5 - 10 times
+ CHECK(hugoOps.pkDeleteRecord(pNdb, 5, 10) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+#if 0
+ // Check records are deleted
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 10, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 626);
+#endif
+
+ updatesValue++;
+ CHECK(hugoOps.pkInsertRecord(pNdb, 5, 10, updatesValue) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 10, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+ CHECK(hugoOps.verifyUpdatesValue(updatesValue) == 0);
+ }
+
+ CHECK(hugoOps.pkDeleteRecord(pNdb, 5, 10) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ // Check records are deleted
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 10, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 626);
+ CHECK(hugoOps.execute_Rollback(pNdb) == 0);
+
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+ }
+
+ // Check records are not deleted
+ // after rollback
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 10, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+ CHECK(hugoOps.verifyUpdatesValue(0) == NDBT_OK);
+
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+
+int runCheckImplicitRollbackDelete(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Read record 5
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Update record 5
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkUpdateRecord(pNdb, 5) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Delete record 5
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkDeleteRecord(pNdb, 5) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Check record is not deleted
+ // Close transaction should have rollbacked
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 1, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runCheckCommitDelete(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Read 10 records
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 10, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ // Update 10 records
+ CHECK(hugoOps.pkUpdateRecord(pNdb, 5, 10) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ // Delete 10 records
+ CHECK(hugoOps.pkDeleteRecord(pNdb, 5, 10) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Check record's are deleted
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 10, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 626);
+
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runRollbackNothing(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Delete record 5 - 15
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkDeleteRecord(pNdb, 5, 10) == 0);
+ // Rollback
+ CHECK(hugoOps.execute_Rollback(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ // Check records are not deleted
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, 5, 10, NdbOperation::LM_Exclusive) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.execute_Rollback(pNdb) == 0);
+
+ }while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int runMassiveRollback(NDBT_Context* ctx, NDBT_Step* step){
+
+ NdbRestarter restarter;
+ const int records = 4 * restarter.getNumDbNodes();
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ const Uint32 OPS_PER_TRANS = 256;
+ const Uint32 OPS_TOTAL = 4096;
+
+ for(int row = 0; row < records; row++){
+ int res;
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ for(Uint32 i = 0; i<OPS_TOTAL; i += OPS_PER_TRANS){
+ for(Uint32 j = 0; j<OPS_PER_TRANS; j++){
+ CHECK(hugoOps.pkUpdateRecord(pNdb, row, 1, i) == 0);
+ }
+ g_info << "Performed " << (i+OPS_PER_TRANS) << " updates on row: " << row
+ << endl;
+ if(result != NDBT_OK){
+ break;
+ }
+ res = hugoOps.execute_NoCommit(pNdb);
+ if(res != 0){
+ NdbError err = pNdb->getNdbError(res);
+ CHECK(err.classification == NdbError::TimeoutExpired);
+ break;
+ }
+ }
+ if(result != NDBT_OK){
+ break;
+ }
+ g_info << "executeRollback" << endl;
+ CHECK(hugoOps.execute_Rollback(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+ }
+
+ hugoOps.closeTransaction(pNdb);
+ return result;
+}
+
+int
+runMassiveRollback2(NDBT_Context* ctx, NDBT_Step* step){
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), 1) != 0){
+ return NDBT_FAILED;
+ }
+
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ const Uint32 OPS_TOTAL = 4096;
+ const Uint32 LOOPS = 10;
+
+ for(Uint32 loop = 0; loop<LOOPS; loop++){
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ for(Uint32 i = 0; i<OPS_TOTAL-1; i ++){
+ if((i & 1) == 0){
+ CHECK(hugoOps.pkUpdateRecord(pNdb, 0, 1, loop) == 0);
+ } else {
+ CHECK(hugoOps.pkUpdateRecord(pNdb, 1, 1, loop) == 0);
+ }
+ }
+ CHECK(hugoOps.execute_Commit(pNdb) == 626);
+ CHECK(hugoOps.execute_Rollback(pNdb) == 0);
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+ }
+
+ hugoOps.closeTransaction(pNdb);
+ return result;
+}
+
+
+NDBT_TESTSUITE(testBasic);
+TESTCASE("PkInsert",
+ "Verify that we can insert and delete from this table using PK"
+ "NOTE! No errors are allowed!" ){
+ INITIALIZER(runInsert);
+ VERIFIER(runVerifyInsert);
+}
+TESTCASE("PkRead",
+ "Verify that we can insert, read and delete from this table using PK"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkDirtyRead",
+ "Verify that we can insert, dirty read and delete from this table using PK"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkDirtyRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkUpdate",
+ "Verify that we can insert, update and delete from this table using PK"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkUpdate);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkDelete",
+ "Verify that we can delete from this table using PK"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkDelete);
+ FINALIZER(runClearTable);
+}
+TESTCASE("UpdateAndRead",
+ "Verify that we can read and update at the same time"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkRead);
+ STEP(runPkRead);
+ STEP(runPkRead);
+ STEP(runPkUpdate);
+ STEP(runPkUpdate);
+ STEP(runPkUpdate);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkReadAndLocker",
+ "Verify that we can read although there are "\
+ " a number of 1 second locks in the table"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkReadUntilStopped);
+ STEP(runLocker);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkReadAndLocker2",
+ "Verify that we can read and update although there are "\
+ " a number of 1 second locks in the table"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkReadUntilStopped);
+ STEP(runLocker);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkReadUpdateAndLocker",
+ "Verify that we can read and update although there are "\
+ " a number of 1 second locks in the table"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkUpdateUntilStopped);
+ STEP(runPkUpdateUntilStopped);
+ STEP(runLocker);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ReadWithLocksAndInserts",
+ "TR457: This test is added to verify that an insert of a records "\
+ "that is already in the database does not delete the record"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkReadUntilStopped);
+ STEP(runLocker);
+ STEP(runInsertUntilStopped);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkInsertTwice",
+ "Verify that we can't insert an already inserted record."
+ "Error should be returned" ){
+ INITIALIZER(runLoadTable);
+ STEP(runInsertTwice);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NoCommitSleep",
+ "Verify what happens when a NoCommit transaction is aborted by "
+ "NDB because the application is sleeping" ){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runNoCommitSleep);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("Commit626",
+ "Verify what happens when a Commit transaction is aborted by "
+ "NDB because the record does no exist" ){
+ INITIALIZER(runClearTable2);
+ INITIALIZER(runCommit626);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("CommitTry626",
+ "Verify what happens when a Commit(TryCommit) \n"
+ "transaction is aborted by "
+ "NDB because the record does no exist" ){
+ INITIALIZER(runClearTable2);
+ INITIALIZER(runCommit_TryCommit626);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("CommitAsMuch626",
+ "Verify what happens when a Commit(CommitAsMuchAsPossible) \n"
+ "transaction is aborted by\n"
+ "NDB because the record does no exist" ){
+ INITIALIZER(runClearTable2);
+ INITIALIZER(runCommit_CommitAsMuchAsPossible626);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("NoCommit626",
+ "Verify what happens when a NoCommit transaction is aborted by "
+ "NDB because the record does no exist" ){
+ INITIALIZER(runClearTable2);
+ INITIALIZER(runNoCommit626);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("NoCommitRollback626",
+ "Verify what happens when a NoCommit transaction is aborted by "
+ "NDB because the record does no exist and then we try to rollback\n"
+ "the transaction" ){
+ INITIALIZER(runClearTable2);
+ INITIALIZER(runNoCommitRollback626);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("Commit630",
+ "Verify what happens when a Commit transaction is aborted by "
+ "NDB because the record already exist" ){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCommit630);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("CommitTry630",
+ "Verify what happens when a Commit(TryCommit) \n"
+ "transaction is aborted by "
+ "NDB because the record already exist" ){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCommit_TryCommit630);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("CommitAsMuch630",
+ "Verify what happens when a Commit(CommitAsMuchAsPossible) \n"
+ "transaction is aborted by\n"
+ "NDB because the record already exist" ){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCommit_CommitAsMuchAsPossible630);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("NoCommit630",
+ "Verify what happens when a NoCommit transaction is aborted by "
+ "NDB because the record already exist" ){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runNoCommit630);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("NoCommitRollback630",
+ "Verify what happens when a NoCommit transaction is aborted by "
+ "NDB because the record already exist and then we try to rollback\n"
+ "the transaction" ){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runNoCommitRollback630);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("NoCommitAndClose",
+ "Verify what happens when a NoCommit transaction is closed "
+ "without rolling back the transaction " ){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runNoCommitAndClose);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("RollbackDelete",
+ "Test rollback of a no committed delete"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCheckRollbackDelete);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("RollbackUpdate",
+ "Test rollback of a no committed update"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCheckRollbackUpdate);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("RollbackDeleteMultiple",
+ "Test rollback of 10 non committed delete"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCheckRollbackDeleteMultiple);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("ImplicitRollbackDelete",
+ "Test close transaction after a no commited delete\n"
+ "this would give an implicit rollback of the delete\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCheckImplicitRollbackDelete);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("CommitDelete",
+ "Test close transaction after a no commited delete\n"
+ "this would give an implicit rollback of the delete\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCheckCommitDelete);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("RollbackNothing",
+ "Test rollback of nothing"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runRollbackNothing);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("MassiveRollback",
+ "Test rollback of 4096 operations"){
+ INITIALIZER(runClearTable2);
+ INITIALIZER(runMassiveRollback);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("MassiveRollback2",
+ "Test rollback of 4096 operations"){
+ INITIALIZER(runClearTable2);
+ INITIALIZER(runMassiveRollback2);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("MassiveTransaction",
+ "Test very large insert transaction"){
+ INITIALIZER(runLoadTable2);
+ FINALIZER(runClearTable2);
+}
+TESTCASE("Fill",
+ "Verify what happens when we fill the db" ){
+ INITIALIZER(runFillTable);
+ INITIALIZER(runPkRead);
+ FINALIZER(runClearTable2);
+}
+NDBT_TESTSUITE_END(testBasic);
+
+#if 0
+TESTCASE("ReadConsistency",
+ "Check that a read within a transaction returns the " \
+ "same result no matter"){
+ STEP(runInsertOne);
+ STEP(runReadOne);
+ FINALIZER(runClearTable2);
+}
+#endif
+
+int main(int argc, const char** argv){
+ ndb_init();
+ return testBasic.execute(argc, argv);
+}
+
+
+
diff --git a/storage/ndb/test/ndbapi/testBasicAsynch.cpp b/storage/ndb/test/ndbapi/testBasicAsynch.cpp
new file mode 100644
index 00000000000..6daa22fdc6a
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testBasicAsynch.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 "NDBT_Test.hpp"
+#include "NDBT_ReturnCodes.h"
+#include "HugoTransactions.hpp"
+#include "HugoAsynchTransactions.hpp"
+#include "UtilTransactions.hpp"
+
+#define GETNDB(ps) ((NDBT_NdbApiStep*)ps)->getNdb()
+
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int transactions = (records / 100) + 1;
+ int operations = (records / transactions) + 1;
+
+ HugoAsynchTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTableAsynch(GETNDB(step), records, batchSize,
+ transactions, operations) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runInsert(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int transactions = (records / 100) + 1;
+ int operations = (records / transactions) + 1;
+
+ HugoAsynchTransactions hugoTrans(*ctx->getTab());
+ // Insert records, dont allow any
+ // errors(except temporary) while inserting
+ if (hugoTrans.loadTableAsynch(GETNDB(step), records, batchSize,
+ transactions, operations) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runVerifyInsert(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int transactions = (records / 100) + 1;
+ int operations = (records / transactions) + 1;
+
+ HugoAsynchTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.pkDelRecordsAsynch(GETNDB(step), records, batchSize,
+ transactions, operations) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int transactions = (records / 100) + 1;
+ int operations = (records / transactions) + 1;
+
+ HugoAsynchTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.pkDelRecordsAsynch(GETNDB(step), records, batchSize,
+ transactions, operations) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runPkDelete(NDBT_Context* ctx, NDBT_Step* step){
+
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int transactions = (records / 100) + 1;
+ int operations = (records / transactions) + 1;
+
+ int i = 0;
+ HugoAsynchTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ ndbout << i << ": ";
+ if (hugoTrans.pkDelRecordsAsynch(GETNDB(step), records, batchSize,
+ transactions, operations) != 0){
+ return NDBT_FAILED;
+ }
+ // Load table, don't allow any primary key violations
+ if (hugoTrans.loadTableAsynch(GETNDB(step), records, batchSize,
+ transactions, operations) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+
+int runPkRead(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int transactions = (records / 100) + 1;
+ int operations = (records / transactions) + 1;
+
+ int i = 0;
+ HugoAsynchTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ ndbout << i << ": ";
+ if (hugoTrans.pkReadRecordsAsynch(GETNDB(step), records, batchSize,
+ transactions, operations) != NDBT_OK){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runPkUpdate(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int transactions = (records / 100) + 1;
+ int operations = (records / transactions) + 1;
+
+ int i = 0;
+ HugoAsynchTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ ndbout << i << ": ";
+ if (hugoTrans.pkUpdateRecordsAsynch(GETNDB(step), records,
+ batchSize, transactions,
+ operations) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+NDBT_TESTSUITE(testBasicAsynch);
+TESTCASE("PkInsertAsynch",
+ "Verify that we can insert and delete from this table using PK"
+ " NOTE! No errors are allowed!" ){
+ INITIALIZER(runInsert);
+ VERIFIER(runVerifyInsert);
+}
+TESTCASE("PkReadAsynch",
+ "Verify that we can insert, read and delete from this table"
+ " using PK"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkUpdateAsynch",
+ "Verify that we can insert, update and delete from this table"
+ " using PK"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkUpdate);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkDeleteAsynch",
+ "Verify that we can delete from this table using PK"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkDelete);
+ FINALIZER(runClearTable);
+}
+
+NDBT_TESTSUITE_END(testBasicAsynch);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ return testBasicAsynch.execute(argc, argv);
+}
+
diff --git a/storage/ndb/test/ndbapi/testBitfield.cpp b/storage/ndb/test/ndbapi/testBitfield.cpp
new file mode 100644
index 00000000000..e26f495f5a4
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testBitfield.cpp
@@ -0,0 +1,198 @@
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+#include <NDBT.hpp>
+#include <NdbApi.hpp>
+#include <HugoTransactions.hpp>
+
+static const char* _dbname = "TEST_DB";
+static int g_loops = 7;
+
+static void usage()
+{
+ ndb_std_print_version();
+}
+#if 0
+static my_bool
+get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
+ const char *argument)
+{
+ return ndb_std_get_one_option(optid, opt, argument ? argument :
+ "d:t:O,/tmp/testBitfield.trace");
+}
+#endif
+
+static const NdbDictionary::Table* create_random_table(Ndb*);
+static int transactions(Ndb*, const NdbDictionary::Table* tab);
+static int unique_indexes(Ndb*, const NdbDictionary::Table* tab);
+static int ordered_indexes(Ndb*, const NdbDictionary::Table* tab);
+static int node_restart(Ndb*, const NdbDictionary::Table* tab);
+static int system_restart(Ndb*, const NdbDictionary::Table* tab);
+
+int
+main(int argc, char** argv){
+ NDB_INIT(argv[0]);
+ const char *load_default_groups[]= { "mysql_cluster",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+ int ho_error;
+
+ argc--;
+ argv++;
+
+ Ndb_cluster_connection con(opt_connect_str);
+ if(con.connect(12, 5, 1))
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+
+ Ndb* pNdb;
+ pNdb = new Ndb(&con, _dbname);
+ pNdb->init();
+ while (pNdb->waitUntilReady() != 0);
+ int res = NDBT_FAILED;
+
+ NdbDictionary::Dictionary * dict = pNdb->getDictionary();
+
+ const NdbDictionary::Table* pTab = 0;
+ for (int i = 0; i < (argc ? argc : g_loops) ; i++)
+ {
+ res = NDBT_FAILED;
+ if(argc == 0)
+ {
+ pTab = create_random_table(pNdb);
+ }
+ else
+ {
+ dict->dropTable(argv[i]);
+ NDBT_Tables::createTable(pNdb, argv[i]);
+ pTab = dict->getTable(argv[i]);
+ }
+
+ if (pTab == 0)
+ {
+ ndbout << "Failed to create table" << endl;
+ ndbout << dict->getNdbError() << endl;
+ break;
+ }
+
+ if(transactions(pNdb, pTab))
+ break;
+
+ if(unique_indexes(pNdb, pTab))
+ break;
+
+ if(ordered_indexes(pNdb, pTab))
+ break;
+
+ if(node_restart(pNdb, pTab))
+ break;
+
+ if(system_restart(pNdb, pTab))
+ break;
+
+ dict->dropTable(pTab->getName());
+ res = NDBT_OK;
+ }
+
+ if(res != NDBT_OK && pTab)
+ {
+ dict->dropTable(pTab->getName());
+ }
+
+ delete pNdb;
+ return NDBT_ProgramExit(res);
+}
+
+static
+const NdbDictionary::Table*
+create_random_table(Ndb* pNdb)
+{
+ do {
+ NdbDictionary::Table tab;
+ Uint32 cols = 1 + (rand() % (NDB_MAX_ATTRIBUTES_IN_TABLE - 1));
+ Uint32 keys = NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY;
+ Uint32 length = 4090;
+ Uint32 key_size = NDB_MAX_KEYSIZE_IN_WORDS;
+
+ BaseString name;
+ name.assfmt("TAB_%d", rand() & 65535);
+ tab.setName(name.c_str());
+ for(int i = 0; i<cols && length > 2; i++)
+ {
+ NdbDictionary::Column col;
+ name.assfmt("COL_%d", i);
+ col.setName(name.c_str());
+ if(i == 0 || i == 1)
+ {
+ col.setType(NdbDictionary::Column::Unsigned);
+ col.setLength(1);
+ col.setNullable(false);
+ col.setPrimaryKey(i == 0);
+ tab.addColumn(col);
+ continue;
+ }
+
+ col.setType(NdbDictionary::Column::Bit);
+
+ Uint32 len = 1 + (rand() % (length - 1));
+ col.setLength(len); length -= len;
+ int nullable = (rand() >> 16) & 1;
+ col.setNullable(nullable); length -= nullable;
+ col.setPrimaryKey(false);
+ tab.addColumn(col);
+ }
+
+ pNdb->getDictionary()->dropTable(tab.getName());
+ if(pNdb->getDictionary()->createTable(tab) == 0)
+ {
+ ndbout << (NDBT_Table&)tab << endl;
+ return pNdb->getDictionary()->getTable(tab.getName());
+ }
+ } while(0);
+ return 0;
+}
+
+static
+int
+transactions(Ndb* pNdb, const NdbDictionary::Table* tab)
+{
+ int i = 0;
+ HugoTransactions trans(* tab);
+ i |= trans.loadTable(pNdb, 1000);
+ i |= trans.pkReadRecords(pNdb, 1000, 13);
+ i |= trans.scanReadRecords(pNdb, 1000, 25);
+ i |= trans.pkUpdateRecords(pNdb, 1000, 37);
+ i |= trans.scanUpdateRecords(pNdb, 1000, 25);
+ i |= trans.pkDelRecords(pNdb, 500, 23);
+ i |= trans.clearTable(pNdb);
+ return i;
+}
+
+static
+int
+unique_indexes(Ndb* pNdb, const NdbDictionary::Table* tab)
+{
+ return 0;
+}
+
+static
+int
+ordered_indexes(Ndb* pNdb, const NdbDictionary::Table* tab)
+{
+ return 0;
+}
+
+static
+int
+node_restart(Ndb* pNdb, const NdbDictionary::Table* tab)
+{
+ return 0;
+}
+
+static
+int
+system_restart(Ndb* pNdb, const NdbDictionary::Table* tab)
+{
+ return 0;
+}
diff --git a/storage/ndb/test/ndbapi/testBlobs.cpp b/storage/ndb/test/ndbapi/testBlobs.cpp
new file mode 100644
index 00000000000..3f5d1982522
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testBlobs.cpp
@@ -0,0 +1,1868 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ * testBlobs
+ */
+
+#include <ndb_global.h>
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbTest.hpp>
+#include <NdbTick.h>
+#include <ndb/src/ndbapi/NdbBlobImpl.hpp>
+
+struct Bcol {
+ bool m_nullable;
+ unsigned m_inline;
+ unsigned m_partsize;
+ unsigned m_stripe;
+ char m_btname[NdbBlobImpl::BlobTableNameSize];
+ Bcol(bool a, unsigned b, unsigned c, unsigned d) :
+ m_nullable(a),
+ m_inline(b),
+ m_partsize(c),
+ m_stripe(d)
+ {}
+};
+
+struct Opt {
+ unsigned m_batch;
+ bool m_core;
+ bool m_dbg;
+ bool m_dbgall;
+ const char* m_dbug;
+ bool m_full;
+ unsigned m_loop;
+ unsigned m_parts;
+ unsigned m_rows;
+ unsigned m_seed;
+ const char* m_skip;
+ const char* m_test;
+ // metadata
+ const char* m_tname;
+ const char* m_x1name; // hash index
+ const char* m_x2name; // ordered index
+ unsigned m_pk1off;
+ unsigned m_pk2len;
+ bool m_oneblob;
+ Bcol m_blob1;
+ Bcol m_blob2;
+ // perf
+ const char* m_tnameperf;
+ unsigned m_rowsperf;
+ // bugs
+ int m_bug;
+ int (*m_bugtest)();
+ Opt() :
+ m_batch(7),
+ m_core(false),
+ m_dbg(false),
+ m_dbgall(false),
+ m_dbug(0),
+ m_full(false),
+ m_loop(1),
+ m_parts(10),
+ m_rows(100),
+ m_seed(0),
+ m_skip(0),
+ m_test(0),
+ // metadata
+ m_tname("TBLOB1"),
+ m_x1name("TBLOB1X1"),
+ m_x2name("TBLOB1X2"),
+ m_pk1off(0x12340000),
+ m_pk2len(55),
+ m_oneblob(false),
+ m_blob1(false, 7, 1137, 10),
+ m_blob2(true, 99, 55, 1),
+ // perf
+ m_tnameperf("TBLOB2"),
+ m_rowsperf(10000),
+ // bugs
+ m_bug(0),
+ m_bugtest(0) {
+ }
+};
+
+static const unsigned g_max_pk2len = 256;
+
+static void
+printusage()
+{
+ Opt d;
+ ndbout
+ << "usage: testBlobs options [default/max]" << endl
+ << " -batch N number of pk ops in batch [" << d.m_batch << "]" << endl
+ << " -core dump core on error" << endl
+ << " -dbg print debug" << endl
+ << " -dbgall print also NDB API debug (if compiled in)" << endl
+ << " -dbug opt dbug options" << endl
+ << " -full read/write only full blob values" << endl
+ << " -loop N loop N times 0=forever [" << d.m_loop << "]" << endl
+ << " -parts N max parts in blob value [" << d.m_parts << "]" << endl
+ << " -rows N number of rows [" << d.m_rows << "]" << endl
+ << " -rowsperf N rows for performace test [" << d.m_rowsperf << "]" << endl
+ << " -seed N random seed 0=loop number [" << d.m_seed << "]" << endl
+ << " -skip xxx skip given tests (see list) [no tests]" << endl
+ << " -test xxx only given tests (see list) [all tests]" << endl
+ << "metadata" << endl
+ << " -pk2len N length of PK2 [" << d.m_pk2len << "/" << g_max_pk2len <<"]" << endl
+ << " -oneblob only 1 blob attribute [default 2]" << endl
+ << "testcases for test/skip" << endl
+ << " k primary key ops" << endl
+ << " i hash index ops" << endl
+ << " s table scans" << endl
+ << " r ordered index scans" << endl
+ << " p performance test" << endl
+ << "additional flags for test/skip" << endl
+ << " u update existing blob value" << endl
+ << " n normal insert and update" << endl
+ << " w insert and update using writeTuple" << endl
+ << " 0 getValue / setValue" << endl
+ << " 1 setActiveHook" << endl
+ << " 2 readData / writeData" << endl
+ << "bug tests (no blob test)" << endl
+ << " -bug 4088 ndb api hang with mixed ops on index table" << endl
+ << " -bug nnnn delete + write gives 626" << endl
+ << " -bug nnnn acc crash on delete and long key" << endl
+ ;
+}
+
+static Opt g_opt;
+
+static bool
+testcase(char x)
+{
+ if (x < 10)
+ x += '0';
+ return
+ (g_opt.m_test == 0 || strchr(g_opt.m_test, x) != 0) &&
+ (g_opt.m_skip == 0 || strchr(g_opt.m_skip, x) == 0);
+}
+
+static Ndb_cluster_connection* g_ncc = 0;
+static Ndb* g_ndb = 0;
+static NdbDictionary::Dictionary* g_dic = 0;
+static NdbConnection* g_con = 0;
+static NdbOperation* g_opr = 0;
+static NdbIndexOperation* g_opx = 0;
+static NdbScanOperation* g_ops = 0;
+static NdbBlob* g_bh1 = 0;
+static NdbBlob* g_bh2 = 0;
+static bool g_printerror = true;
+static unsigned g_loop = 0;
+
+static void
+printerror(int line, const char* msg)
+{
+ ndbout << "line " << line << " FAIL " << msg << endl;
+ if (! g_printerror) {
+ return;
+ }
+ if (g_ndb != 0 && g_ndb->getNdbError().code != 0) {
+ ndbout << "ndb: " << g_ndb->getNdbError() << endl;
+ }
+ if (g_dic != 0 && g_dic->getNdbError().code != 0) {
+ ndbout << "dic: " << g_dic->getNdbError() << endl;
+ }
+ if (g_con != 0 && g_con->getNdbError().code != 0) {
+ ndbout << "con: " << g_con->getNdbError() << endl;
+ if (g_opr != 0 && g_opr->getNdbError().code != 0) {
+ ndbout << "opr: table=" << g_opr->getTableName() << " " << g_opr->getNdbError() << endl;
+ }
+ if (g_opx != 0 && g_opx->getNdbError().code != 0) {
+ ndbout << "opx: table=" << g_opx->getTableName() << " " << g_opx->getNdbError() << endl;
+ }
+ if (g_ops != 0 && g_ops->getNdbError().code != 0) {
+ ndbout << "ops: table=" << g_ops->getTableName() << " " << g_ops->getNdbError() << endl;
+ }
+ NdbOperation* ope = g_con->getNdbErrorOperation();
+ if (ope != 0 && ope->getNdbError().code != 0) {
+ if (ope != g_opr && ope != g_opx && ope != g_ops)
+ ndbout << "ope: table=" << ope->getTableName() << " " << ope->getNdbError() << endl;
+ }
+ }
+ if (g_bh1 != 0 && g_bh1->getNdbError().code != 0) {
+ ndbout << "bh1: " << g_bh1->getNdbError() << endl;
+ }
+ if (g_bh2 != 0 && g_bh2->getNdbError().code != 0) {
+ ndbout << "bh2: " << g_bh2->getNdbError() << endl;
+ }
+ if (g_opt.m_core) {
+ abort();
+ }
+ g_printerror = false;
+}
+
+#define CHK(x) \
+ do { \
+ if (x) break; \
+ printerror(__LINE__, #x); return -1; \
+ } while (0)
+#define DBG(x) \
+ do { \
+ if (! g_opt.m_dbg) break; \
+ ndbout << "line " << __LINE__ << " " << x << endl; \
+ } while (0)
+
+static int
+dropTable()
+{
+ NdbDictionary::Table tab(g_opt.m_tname);
+ if (g_dic->getTable(g_opt.m_tname) != 0)
+ CHK(g_dic->dropTable(tab) == 0);
+ return 0;
+}
+
+static int
+createTable()
+{
+ NdbDictionary::Table tab(g_opt.m_tname);
+ tab.setLogging(false);
+ // col PK1 - Uint32
+ { NdbDictionary::Column col("PK1");
+ col.setType(NdbDictionary::Column::Unsigned);
+ col.setPrimaryKey(true);
+ tab.addColumn(col);
+ }
+ // col BL1 - Blob not-nullable
+ { NdbDictionary::Column col("BL1");
+ const Bcol& b = g_opt.m_blob1;
+ col.setType(NdbDictionary::Column::Blob);
+ col.setInlineSize(b.m_inline);
+ col.setPartSize(b.m_partsize);
+ col.setStripeSize(b.m_stripe);
+ tab.addColumn(col);
+ }
+ // col PK2 - Char[55]
+ if (g_opt.m_pk2len != 0)
+ { NdbDictionary::Column col("PK2");
+ col.setType(NdbDictionary::Column::Char);
+ col.setLength(g_opt.m_pk2len);
+ col.setPrimaryKey(true);
+ tab.addColumn(col);
+ }
+ // col BL2 - Text nullable
+ if (! g_opt.m_oneblob)
+ { NdbDictionary::Column col("BL2");
+ const Bcol& b = g_opt.m_blob2;
+ col.setType(NdbDictionary::Column::Text);
+ col.setNullable(true);
+ col.setInlineSize(b.m_inline);
+ col.setPartSize(b.m_partsize);
+ col.setStripeSize(b.m_stripe);
+ tab.addColumn(col);
+ }
+ // create table
+ CHK(g_dic->createTable(tab) == 0);
+ // unique hash index on PK2
+ if (g_opt.m_pk2len != 0)
+ { NdbDictionary::Index idx(g_opt.m_x1name);
+ idx.setType(NdbDictionary::Index::UniqueHashIndex);
+ idx.setLogging(false);
+ idx.setTable(g_opt.m_tname);
+ idx.addColumnName("PK2");
+ CHK(g_dic->createIndex(idx) == 0);
+ }
+ // ordered index on PK2
+ if (g_opt.m_pk2len != 0)
+ { NdbDictionary::Index idx(g_opt.m_x2name);
+ idx.setType(NdbDictionary::Index::OrderedIndex);
+ idx.setLogging(false);
+ idx.setTable(g_opt.m_tname);
+ idx.addColumnName("PK2");
+ CHK(g_dic->createIndex(idx) == 0);
+ }
+ return 0;
+}
+
+// tuples
+
+struct Bval {
+ char* m_val;
+ unsigned m_len;
+ char* m_buf;
+ unsigned m_buflen;
+ Bval() :
+ m_val(0),
+ m_len(0),
+ m_buf(0), // read/write buffer
+ m_buflen(0)
+ {}
+ ~Bval() { delete [] m_val; delete [] m_buf; }
+ void alloc(unsigned buflen) {
+ m_buflen = buflen;
+ delete [] m_buf;
+ m_buf = new char [m_buflen];
+ trash();
+ }
+ void copyfrom(const Bval& v) {
+ m_len = v.m_len;
+ delete [] m_val;
+ if (v.m_val == 0)
+ m_val = 0;
+ else
+ m_val = (char*)memcpy(new char [m_len], v.m_val, m_len);
+ }
+ void trash() const {
+ assert(m_buf != 0);
+ memset(m_buf, 'x', m_buflen);
+ }
+private:
+ Bval(const Bval&);
+ Bval& operator=(const Bval&);
+};
+
+struct Tup {
+ bool m_exists; // exists in table
+ Uint32 m_pk1; // primary keys concatenated like keyinfo
+ char m_pk2[g_max_pk2len + 1];
+ Bval m_blob1;
+ Bval m_blob2;
+ Tup() :
+ m_exists(false)
+ {}
+ ~Tup() { }
+ // alloc buffers of max size
+ void alloc() {
+ m_blob1.alloc(g_opt.m_blob1.m_inline + g_opt.m_blob1.m_partsize * g_opt.m_parts);
+ m_blob2.alloc(g_opt.m_blob2.m_inline + g_opt.m_blob2.m_partsize * g_opt.m_parts);
+ }
+ void copyfrom(const Tup& tup) {
+ assert(m_pk1 == tup.m_pk1);
+ m_blob1.copyfrom(tup.m_blob1);
+ m_blob2.copyfrom(tup.m_blob2);
+ }
+private:
+ Tup(const Tup&);
+ Tup& operator=(const Tup&);
+};
+
+static Tup* g_tups;
+
+static unsigned
+urandom(unsigned n)
+{
+ return n == 0 ? 0 : random() % n;
+}
+
+static void
+calcBval(const Bcol& b, Bval& v, bool keepsize)
+{
+ if (b.m_nullable && urandom(10) == 0) {
+ v.m_len = 0;
+ delete v.m_val;
+ v.m_val = 0;
+ v.m_buf = new char [1];
+ } else {
+ if (keepsize && v.m_val != 0)
+ ;
+ else if (urandom(10) == 0)
+ v.m_len = urandom(b.m_inline);
+ else
+ v.m_len = urandom(b.m_inline + g_opt.m_parts * b.m_partsize + 1);
+ delete v.m_val;
+ v.m_val = new char [v.m_len + 1];
+ for (unsigned i = 0; i < v.m_len; i++)
+ v.m_val[i] = 'a' + urandom(25);
+ v.m_val[v.m_len] = 0;
+ v.m_buf = new char [v.m_len];
+ }
+ v.m_buflen = v.m_len;
+ v.trash();
+}
+
+static void
+calcBval(Tup& tup, bool keepsize)
+{
+ calcBval(g_opt.m_blob1, tup.m_blob1, keepsize);
+ if (! g_opt.m_oneblob)
+ calcBval(g_opt.m_blob2, tup.m_blob2, keepsize);
+}
+
+static void
+calcTups(bool keepsize)
+{
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ tup.m_pk1 = g_opt.m_pk1off + k;
+ for (unsigned i = 0, n = k; i < g_opt.m_pk2len; i++) {
+ if (n != 0) {
+ tup.m_pk2[i] = '0' + n % 10;
+ n = n / 10;
+ } else {
+ tup.m_pk2[i] = 'a' + i % 26;
+ }
+ }
+ calcBval(tup, keepsize);
+ }
+}
+
+// blob handle ops
+
+static int
+getBlobHandles(NdbOperation* opr)
+{
+ CHK((g_bh1 = opr->getBlobHandle("BL1")) != 0);
+ if (! g_opt.m_oneblob)
+ CHK((g_bh2 = opr->getBlobHandle("BL2")) != 0);
+ return 0;
+}
+
+static int
+getBlobHandles(NdbIndexOperation* opx)
+{
+ CHK((g_bh1 = opx->getBlobHandle("BL1")) != 0);
+ if (! g_opt.m_oneblob)
+ CHK((g_bh2 = opx->getBlobHandle("BL2")) != 0);
+ return 0;
+}
+
+static int
+getBlobHandles(NdbScanOperation* ops)
+{
+ CHK((g_bh1 = ops->getBlobHandle("BL1")) != 0);
+ if (! g_opt.m_oneblob)
+ CHK((g_bh2 = ops->getBlobHandle("BL2")) != 0);
+ return 0;
+}
+
+static int
+getBlobLength(NdbBlob* h, unsigned& len)
+{
+ Uint64 len2 = (unsigned)-1;
+ CHK(h->getLength(len2) == 0);
+ len = (unsigned)len2;
+ assert(len == len2);
+ bool isNull;
+ CHK(h->getNull(isNull) == 0);
+ DBG("getBlobLength " << h->getColumn()->getName() << " len=" << len << " null=" << isNull);
+ return 0;
+}
+
+// setValue / getValue
+
+static int
+setBlobValue(NdbBlob* h, const Bval& v)
+{
+ bool null = (v.m_val == 0);
+ bool isNull;
+ unsigned len;
+ DBG("setValue " << h->getColumn()->getName() << " len=" << v.m_len << " null=" << null);
+ if (null) {
+ CHK(h->setNull() == 0);
+ isNull = false;
+ CHK(h->getNull(isNull) == 0 && isNull == true);
+ CHK(getBlobLength(h, len) == 0 && len == 0);
+ } else {
+ CHK(h->setValue(v.m_val, v.m_len) == 0);
+ CHK(h->getNull(isNull) == 0 && isNull == false);
+ CHK(getBlobLength(h, len) == 0 && len == v.m_len);
+ }
+ return 0;
+}
+
+static int
+setBlobValue(const Tup& tup)
+{
+ CHK(setBlobValue(g_bh1, tup.m_blob1) == 0);
+ if (! g_opt.m_oneblob)
+ CHK(setBlobValue(g_bh2, tup.m_blob2) == 0);
+ return 0;
+}
+
+static int
+getBlobValue(NdbBlob* h, const Bval& v)
+{
+ bool null = (v.m_val == 0);
+ DBG("getValue " << h->getColumn()->getName() << " buflen=" << v.m_buflen);
+ CHK(h->getValue(v.m_buf, v.m_buflen) == 0);
+ return 0;
+}
+
+static int
+getBlobValue(const Tup& tup)
+{
+ CHK(getBlobValue(g_bh1, tup.m_blob1) == 0);
+ if (! g_opt.m_oneblob)
+ CHK(getBlobValue(g_bh2, tup.m_blob2) == 0);
+ return 0;
+}
+
+static int
+verifyBlobValue(NdbBlob* h, const Bval& v)
+{
+ bool null = (v.m_val == 0);
+ bool isNull;
+ unsigned len;
+ if (null) {
+ isNull = false;
+ CHK(h->getNull(isNull) == 0 && isNull == true);
+ CHK(getBlobLength(h, len) == 0 && len == 0);
+ } else {
+ isNull = true;
+ CHK(h->getNull(isNull) == 0 && isNull == false);
+ CHK(getBlobLength(h, len) == 0 && len == v.m_len);
+ for (unsigned i = 0; i < v.m_len; i++)
+ CHK(v.m_val[i] == v.m_buf[i]);
+ }
+ return 0;
+}
+
+static int
+verifyBlobValue(const Tup& tup)
+{
+ CHK(verifyBlobValue(g_bh1, tup.m_blob1) == 0);
+ if (! g_opt.m_oneblob)
+ CHK(verifyBlobValue(g_bh2, tup.m_blob2) == 0);
+ return 0;
+}
+
+// readData / writeData
+
+static int
+writeBlobData(NdbBlob* h, const Bval& v)
+{
+ bool null = (v.m_val == 0);
+ bool isNull;
+ unsigned len;
+ DBG("write " << h->getColumn()->getName() << " len=" << v.m_len << " null=" << null);
+ if (null) {
+ CHK(h->setNull() == 0);
+ isNull = false;
+ CHK(h->getNull(isNull) == 0 && isNull == true);
+ CHK(getBlobLength(h, len) == 0 && len == 0);
+ } else {
+ CHK(h->truncate(v.m_len) == 0);
+ unsigned n = 0;
+ do {
+ unsigned m = g_opt.m_full ? v.m_len : urandom(v.m_len + 1);
+ if (m > v.m_len - n)
+ m = v.m_len - n;
+ DBG("write pos=" << n << " cnt=" << m);
+ CHK(h->writeData(v.m_val + n, m) == 0);
+ n += m;
+ } while (n < v.m_len);
+ assert(n == v.m_len);
+ isNull = true;
+ CHK(h->getNull(isNull) == 0 && isNull == false);
+ CHK(getBlobLength(h, len) == 0 && len == v.m_len);
+ }
+ return 0;
+}
+
+static int
+writeBlobData(const Tup& tup)
+{
+ CHK(writeBlobData(g_bh1, tup.m_blob1) == 0);
+ if (! g_opt.m_oneblob)
+ CHK(writeBlobData(g_bh2, tup.m_blob2) == 0);
+ return 0;
+}
+
+static int
+readBlobData(NdbBlob* h, const Bval& v)
+{
+ bool null = (v.m_val == 0);
+ bool isNull;
+ unsigned len;
+ DBG("read " << h->getColumn()->getName() << " len=" << v.m_len << " null=" << null);
+ if (null) {
+ isNull = false;
+ CHK(h->getNull(isNull) == 0 && isNull == true);
+ CHK(getBlobLength(h, len) == 0 && len == 0);
+ } else {
+ isNull = true;
+ CHK(h->getNull(isNull) == 0 && isNull == false);
+ CHK(getBlobLength(h, len) == 0 && len == v.m_len);
+ v.trash();
+ unsigned n = 0;
+ while (n < v.m_len) {
+ unsigned m = g_opt.m_full ? v.m_len : urandom(v.m_len + 1);
+ if (m > v.m_len - n)
+ m = v.m_len - n;
+ DBG("read pos=" << n << " cnt=" << m);
+ const unsigned m2 = m;
+ CHK(h->readData(v.m_buf + n, m) == 0);
+ CHK(m2 == m);
+ n += m;
+ }
+ assert(n == v.m_len);
+ // need to execute to see the data
+ CHK(g_con->execute(NoCommit) == 0);
+ for (unsigned i = 0; i < v.m_len; i++)
+ CHK(v.m_val[i] == v.m_buf[i]);
+ }
+ return 0;
+}
+
+static int
+readBlobData(const Tup& tup)
+{
+ CHK(readBlobData(g_bh1, tup.m_blob1) == 0);
+ if (! g_opt.m_oneblob)
+ CHK(readBlobData(g_bh2, tup.m_blob2) == 0);
+ return 0;
+}
+
+// hooks
+
+static NdbBlob::ActiveHook blobWriteHook;
+
+static int
+blobWriteHook(NdbBlob* h, void* arg)
+{
+ DBG("blobWriteHook");
+ Bval& v = *(Bval*)arg;
+ CHK(writeBlobData(h, v) == 0);
+ return 0;
+}
+
+static int
+setBlobWriteHook(NdbBlob* h, Bval& v)
+{
+ DBG("setBlobWriteHook");
+ CHK(h->setActiveHook(blobWriteHook, &v) == 0);
+ return 0;
+}
+
+static int
+setBlobWriteHook(Tup& tup)
+{
+ CHK(setBlobWriteHook(g_bh1, tup.m_blob1) == 0);
+ if (! g_opt.m_oneblob)
+ CHK(setBlobWriteHook(g_bh2, tup.m_blob2) == 0);
+ return 0;
+}
+
+static NdbBlob::ActiveHook blobReadHook;
+
+// no PK yet to identify tuple so just read the value
+static int
+blobReadHook(NdbBlob* h, void* arg)
+{
+ DBG("blobReadHook");
+ Bval& v = *(Bval*)arg;
+ unsigned len;
+ CHK(getBlobLength(h, len) == 0);
+ v.alloc(len);
+ Uint32 maxlen = 0xffffffff;
+ CHK(h->readData(v.m_buf, maxlen) == 0);
+ DBG("read " << maxlen << " bytes");
+ CHK(len == maxlen);
+ return 0;
+}
+
+static int
+setBlobReadHook(NdbBlob* h, Bval& v)
+{
+ DBG("setBlobReadHook");
+ CHK(h->setActiveHook(blobReadHook, &v) == 0);
+ return 0;
+}
+
+static int
+setBlobReadHook(Tup& tup)
+{
+ CHK(setBlobReadHook(g_bh1, tup.m_blob1) == 0);
+ if (! g_opt.m_oneblob)
+ CHK(setBlobReadHook(g_bh2, tup.m_blob2) == 0);
+ return 0;
+}
+
+// verify blob data
+
+static int
+verifyHeadInline(const Bcol& c, const Bval& v, NdbRecAttr* ra)
+{
+ if (v.m_val == 0) {
+ CHK(ra->isNULL() == 1);
+ } else {
+ CHK(ra->isNULL() == 0);
+ const NdbBlob::Head* head = (const NdbBlob::Head*)ra->aRef();
+ CHK(head->length == v.m_len);
+ const char* data = (const char*)(head + 1);
+ for (unsigned i = 0; i < head->length && i < c.m_inline; i++)
+ CHK(data[i] == v.m_val[i]);
+ }
+ return 0;
+}
+
+static int
+verifyHeadInline(const Tup& tup)
+{
+ DBG("verifyHeadInline pk1=" << hex << tup.m_pk1);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
+ CHK(g_opr->readTuple() == 0);
+ CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+ NdbRecAttr* ra1;
+ NdbRecAttr* ra2;
+ CHK((ra1 = g_opr->getValue("BL1")) != 0);
+ if (! g_opt.m_oneblob)
+ CHK((ra2 = g_opr->getValue("BL2")) != 0);
+ if (tup.m_exists) {
+ CHK(g_con->execute(Commit) == 0);
+ DBG("verifyHeadInline BL1");
+ CHK(verifyHeadInline(g_opt.m_blob1, tup.m_blob1, ra1) == 0);
+ if (! g_opt.m_oneblob) {
+ DBG("verifyHeadInline BL2");
+ CHK(verifyHeadInline(g_opt.m_blob2, tup.m_blob2, ra2) == 0);
+ }
+ } else {
+ CHK(g_con->execute(Commit) == -1 && g_con->getNdbError().code == 626);
+ }
+ g_ndb->closeTransaction(g_con);
+ g_opr = 0;
+ g_con = 0;
+ return 0;
+}
+
+static int
+verifyBlobTable(const Bcol& b, const Bval& v, Uint32 pk1, bool exists)
+{
+ DBG("verify " << b.m_btname << " pk1=" << hex << pk1);
+ NdbRecAttr* ra_pk;
+ NdbRecAttr* ra_part;
+ NdbRecAttr* ra_data;
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_ops = g_con->getNdbScanOperation(b.m_btname)) != 0);
+ CHK(g_ops->readTuples() == 0);
+ CHK((ra_pk = g_ops->getValue("PK")) != 0);
+ CHK((ra_part = g_ops->getValue("PART")) != 0);
+ CHK((ra_data = g_ops->getValue("DATA")) != 0);
+ CHK(g_con->execute(NoCommit) == 0);
+ unsigned partcount;
+ if (! exists || v.m_len <= b.m_inline)
+ partcount = 0;
+ else
+ partcount = (v.m_len - b.m_inline + b.m_partsize - 1) / b.m_partsize;
+ char* seen = new char [partcount];
+ memset(seen, 0, partcount);
+ while (1) {
+ int ret;
+ CHK((ret = g_ops->nextResult()) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ if (pk1 != ra_pk->u_32_value())
+ continue;
+ Uint32 part = ra_part->u_32_value();
+ DBG("part " << part << " of " << partcount);
+ const char* data = ra_data->aRef();
+ CHK(part < partcount && ! seen[part]);
+ seen[part] = 1;
+ unsigned n = b.m_inline + part * b.m_partsize;
+ assert(exists && v.m_val != 0 && n < v.m_len);
+ unsigned m = v.m_len - n;
+ if (m > b.m_partsize)
+ m = b.m_partsize;
+ CHK(memcmp(data, v.m_val + n, m) == 0);
+ }
+ for (unsigned i = 0; i < partcount; i++)
+ CHK(seen[i] == 1);
+ g_ndb->closeTransaction(g_con);
+ g_ops = 0;
+ g_con = 0;
+ return 0;
+}
+
+static int
+verifyBlobTable(const Tup& tup)
+{
+ CHK(verifyBlobTable(g_opt.m_blob1, tup.m_blob1, tup.m_pk1, tup.m_exists) == 0);
+ if (! g_opt.m_oneblob)
+ CHK(verifyBlobTable(g_opt.m_blob2, tup.m_blob2, tup.m_pk1, tup.m_exists) == 0);
+ return 0;
+}
+
+static int
+verifyBlob()
+{
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ const Tup& tup = g_tups[k];
+ DBG("verifyBlob pk1=" << hex << tup.m_pk1);
+ CHK(verifyHeadInline(tup) == 0);
+ CHK(verifyBlobTable(tup) == 0);
+ }
+ return 0;
+}
+
+// operations
+
+static const char* stylename[3] = {
+ "style=getValue/setValue",
+ "style=setActiveHook",
+ "style=readData/writeData"
+};
+
+// pk ops
+
+static int
+insertPk(int style)
+{
+ DBG("--- insertPk " << stylename[style] << " ---");
+ unsigned n = 0;
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ DBG("insertPk pk1=" << hex << tup.m_pk1);
+ CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
+ CHK(g_opr->insertTuple() == 0);
+ CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+ CHK(getBlobHandles(g_opr) == 0);
+ if (style == 0) {
+ CHK(setBlobValue(tup) == 0);
+ } else if (style == 1) {
+ // non-nullable must be set
+ CHK(g_bh1->setValue("", 0) == 0);
+ CHK(setBlobWriteHook(tup) == 0);
+ } else {
+ // non-nullable must be set
+ CHK(g_bh1->setValue("", 0) == 0);
+ CHK(g_con->execute(NoCommit) == 0);
+ CHK(writeBlobData(tup) == 0);
+ }
+ // just another trap
+ if (urandom(10) == 0)
+ CHK(g_con->execute(NoCommit) == 0);
+ if (++n == g_opt.m_batch) {
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ n = 0;
+ }
+ g_opr = 0;
+ tup.m_exists = true;
+ }
+ if (n != 0) {
+ CHK(g_con->execute(Commit) == 0);
+ n = 0;
+ }
+ g_ndb->closeTransaction(g_con);
+ g_con = 0;
+ return 0;
+}
+
+static int
+readPk(int style)
+{
+ DBG("--- readPk " << stylename[style] << " ---");
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ DBG("readPk pk1=" << hex << tup.m_pk1);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
+ CHK(g_opr->readTuple() == 0);
+ CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+ CHK(getBlobHandles(g_opr) == 0);
+ if (style == 0) {
+ CHK(getBlobValue(tup) == 0);
+ } else if (style == 1) {
+ CHK(setBlobReadHook(tup) == 0);
+ } else {
+ CHK(g_con->execute(NoCommit) == 0);
+ CHK(readBlobData(tup) == 0);
+ }
+ CHK(g_con->execute(Commit) == 0);
+ if (style == 0 || style == 1) {
+ CHK(verifyBlobValue(tup) == 0);
+ }
+ g_ndb->closeTransaction(g_con);
+ g_opr = 0;
+ g_con = 0;
+ }
+ return 0;
+}
+
+static int
+updatePk(int style)
+{
+ DBG("--- updatePk " << stylename[style] << " ---");
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ DBG("updatePk pk1=" << hex << tup.m_pk1);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
+ CHK(g_opr->updateTuple() == 0);
+ CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+ CHK(getBlobHandles(g_opr) == 0);
+ if (style == 0) {
+ CHK(setBlobValue(tup) == 0);
+ } else if (style == 1) {
+ CHK(setBlobWriteHook(tup) == 0);
+ } else {
+ CHK(g_con->execute(NoCommit) == 0);
+ CHK(writeBlobData(tup) == 0);
+ }
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ g_opr = 0;
+ g_con = 0;
+ tup.m_exists = true;
+ }
+ return 0;
+}
+
+static int
+writePk(int style)
+{
+ DBG("--- writePk " << stylename[style] << " ---");
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ DBG("writePk pk1=" << hex << tup.m_pk1);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
+ CHK(g_opr->writeTuple() == 0);
+ CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+ CHK(getBlobHandles(g_opr) == 0);
+ if (style == 0) {
+ CHK(setBlobValue(tup) == 0);
+ } else if (style == 1) {
+ // non-nullable must be set
+ CHK(g_bh1->setValue("", 0) == 0);
+ CHK(setBlobWriteHook(tup) == 0);
+ } else {
+ // non-nullable must be set
+ CHK(g_bh1->setValue("", 0) == 0);
+ CHK(g_con->execute(NoCommit) == 0);
+ CHK(writeBlobData(tup) == 0);
+ }
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ g_opr = 0;
+ g_con = 0;
+ tup.m_exists = true;
+ }
+ return 0;
+}
+
+static int
+deletePk()
+{
+ DBG("--- deletePk ---");
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ DBG("deletePk pk1=" << hex << tup.m_pk1);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
+ CHK(g_opr->deleteTuple() == 0);
+ CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ g_opr = 0;
+ g_con = 0;
+ tup.m_exists = false;
+ }
+ return 0;
+}
+
+// hash index ops
+
+static int
+readIdx(int style)
+{
+ DBG("--- readIdx " << stylename[style] << " ---");
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ DBG("readIdx pk1=" << hex << tup.m_pk1);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
+ CHK(g_opx->readTuple() == 0);
+ CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
+ CHK(getBlobHandles(g_opx) == 0);
+ if (style == 0) {
+ CHK(getBlobValue(tup) == 0);
+ } else if (style == 1) {
+ CHK(setBlobReadHook(tup) == 0);
+ } else {
+ CHK(g_con->execute(NoCommit) == 0);
+ CHK(readBlobData(tup) == 0);
+ }
+ CHK(g_con->execute(Commit) == 0);
+ if (style == 0 || style == 1) {
+ CHK(verifyBlobValue(tup) == 0);
+ }
+ g_ndb->closeTransaction(g_con);
+ g_opx = 0;
+ g_con = 0;
+ }
+ return 0;
+}
+
+static int
+updateIdx(int style)
+{
+ DBG("--- updateIdx " << stylename[style] << " ---");
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ DBG("updateIdx pk1=" << hex << tup.m_pk1);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
+ CHK(g_opx->updateTuple() == 0);
+ CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
+ CHK(getBlobHandles(g_opx) == 0);
+ if (style == 0) {
+ CHK(setBlobValue(tup) == 0);
+ } else if (style == 1) {
+ CHK(setBlobWriteHook(tup) == 0);
+ } else {
+ CHK(g_con->execute(NoCommit) == 0);
+ CHK(writeBlobData(tup) == 0);
+ }
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ g_opx = 0;
+ g_con = 0;
+ tup.m_exists = true;
+ }
+ return 0;
+}
+
+static int
+writeIdx(int style)
+{
+ DBG("--- writeIdx " << stylename[style] << " ---");
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ DBG("writeIdx pk1=" << hex << tup.m_pk1);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
+ CHK(g_opx->writeTuple() == 0);
+ CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
+ CHK(getBlobHandles(g_opx) == 0);
+ if (style == 0) {
+ CHK(setBlobValue(tup) == 0);
+ } else if (style == 1) {
+ // non-nullable must be set
+ CHK(g_bh1->setValue("", 0) == 0);
+ CHK(setBlobWriteHook(tup) == 0);
+ } else {
+ // non-nullable must be set
+ CHK(g_bh1->setValue("", 0) == 0);
+ CHK(g_con->execute(NoCommit) == 0);
+ CHK(writeBlobData(tup) == 0);
+ }
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ g_opx = 0;
+ g_con = 0;
+ tup.m_exists = true;
+ }
+ return 0;
+}
+
+static int
+deleteIdx()
+{
+ DBG("--- deleteIdx ---");
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ DBG("deleteIdx pk1=" << hex << tup.m_pk1);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
+ CHK(g_opx->deleteTuple() == 0);
+ CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ g_opx = 0;
+ g_con = 0;
+ tup.m_exists = false;
+ }
+ return 0;
+}
+
+// scan ops table and index
+
+static int
+readScan(int style, bool idx)
+{
+ DBG("--- " << "readScan" << (idx ? "Idx" : "") << " " << stylename[style] << " ---");
+ Tup tup;
+ tup.alloc(); // allocate buffers
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ if (! idx) {
+ CHK((g_ops = g_con->getNdbScanOperation(g_opt.m_tname)) != 0);
+ } else {
+ CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0);
+ }
+ CHK(g_ops->readTuples(NdbScanOperation::LM_Read) == 0);
+ CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_ops->getValue("PK2", tup.m_pk2) != 0);
+ CHK(getBlobHandles(g_ops) == 0);
+ if (style == 0) {
+ CHK(getBlobValue(tup) == 0);
+ } else if (style == 1) {
+ CHK(setBlobReadHook(tup) == 0);
+ }
+ CHK(g_con->execute(NoCommit) == 0);
+ unsigned rows = 0;
+ while (1) {
+ int ret;
+ tup.m_pk1 = (Uint32)-1;
+ memset(tup.m_pk2, 'x', g_opt.m_pk2len);
+ CHK((ret = g_ops->nextResult(true)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ DBG("readScan" << (idx ? "Idx" : "") << " pk1=" << hex << tup.m_pk1);
+ Uint32 k = tup.m_pk1 - g_opt.m_pk1off;
+ CHK(k < g_opt.m_rows && g_tups[k].m_exists);
+ tup.copyfrom(g_tups[k]);
+ if (style == 0) {
+ CHK(verifyBlobValue(tup) == 0);
+ } else if (style == 1) {
+ // execute ops generated by callbacks, if any
+ CHK(verifyBlobValue(tup) == 0);
+ } else {
+ CHK(readBlobData(tup) == 0);
+ }
+ rows++;
+ }
+ g_ndb->closeTransaction(g_con);
+ g_con = 0;
+ g_ops = 0;
+ CHK(g_opt.m_rows == rows);
+ return 0;
+}
+
+static int
+updateScan(int style, bool idx)
+{
+ DBG("--- " << "updateScan" << (idx ? "Idx" : "") << " " << stylename[style] << " ---");
+ Tup tup;
+ tup.alloc(); // allocate buffers
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ if (! idx) {
+ CHK((g_ops = g_con->getNdbScanOperation(g_opt.m_tname)) != 0);
+ } else {
+ CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0);
+ }
+ CHK(g_ops->readTuples(NdbScanOperation::LM_Exclusive) == 0);
+ CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_ops->getValue("PK2", tup.m_pk2) != 0);
+ CHK(g_con->execute(NoCommit) == 0);
+ unsigned rows = 0;
+ while (1) {
+ int ret;
+ tup.m_pk1 = (Uint32)-1;
+ memset(tup.m_pk2, 'x', g_opt.m_pk2len);
+ CHK((ret = g_ops->nextResult(true)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ DBG("updateScan" << (idx ? "Idx" : "") << " pk1=" << hex << tup.m_pk1);
+ Uint32 k = tup.m_pk1 - g_opt.m_pk1off;
+ CHK(k < g_opt.m_rows && g_tups[k].m_exists);
+ // calculate new blob values
+ calcBval(g_tups[k], false);
+ tup.copyfrom(g_tups[k]);
+ CHK((g_opr = g_ops->updateCurrentTuple()) != 0);
+ CHK(getBlobHandles(g_opr) == 0);
+ if (style == 0) {
+ CHK(setBlobValue(tup) == 0);
+ } else if (style == 1) {
+ CHK(setBlobWriteHook(tup) == 0);
+ } else {
+ CHK(g_con->execute(NoCommit) == 0);
+ CHK(writeBlobData(tup) == 0);
+ }
+ CHK(g_con->execute(NoCommit) == 0);
+ g_opr = 0;
+ rows++;
+ }
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ g_con = 0;
+ g_ops = 0;
+ CHK(g_opt.m_rows == rows);
+ return 0;
+}
+
+static int
+deleteScan(bool idx)
+{
+ DBG("--- " << "deleteScan" << (idx ? "Idx" : "") << " ---");
+ Tup tup;
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ if (! idx) {
+ CHK((g_ops = g_con->getNdbScanOperation(g_opt.m_tname)) != 0);
+ } else {
+ CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0);
+ }
+ CHK(g_ops->readTuples(NdbScanOperation::LM_Exclusive) == 0);
+ CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_ops->getValue("PK2", tup.m_pk2) != 0);
+ CHK(g_con->execute(NoCommit) == 0);
+ unsigned rows = 0;
+ while (1) {
+ int ret;
+ tup.m_pk1 = (Uint32)-1;
+ memset(tup.m_pk2, 'x', g_opt.m_pk2len);
+ CHK((ret = g_ops->nextResult()) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ DBG("deleteScan" << (idx ? "Idx" : "") << " pk1=" << hex << tup.m_pk1);
+ CHK(g_ops->deleteCurrentTuple() == 0);
+ CHK(g_con->execute(NoCommit) == 0);
+ Uint32 k = tup.m_pk1 - g_opt.m_pk1off;
+ CHK(k < g_opt.m_rows && g_tups[k].m_exists);
+ g_tups[k].m_exists = false;
+ rows++;
+ }
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ g_con = 0;
+ g_ops = 0;
+ CHK(g_opt.m_rows == rows);
+ return 0;
+}
+
+// main
+
+// from here on print always
+#undef DBG
+#define DBG(x) \
+ do { \
+ ndbout << "line " << __LINE__ << " " << x << endl; \
+ } while (0)
+
+static int
+testmain()
+{
+ g_ndb = new Ndb(g_ncc, "TEST_DB");
+ CHK(g_ndb->init() == 0);
+ CHK(g_ndb->waitUntilReady() == 0);
+ g_dic = g_ndb->getDictionary();
+ g_tups = new Tup [g_opt.m_rows];
+ CHK(dropTable() == 0);
+ CHK(createTable() == 0);
+ if (g_opt.m_bugtest != 0) {
+ // test a general bug instead of blobs
+ CHK((*g_opt.m_bugtest)() == 0);
+ return 0;
+ }
+ Bcol& b1 = g_opt.m_blob1;
+ CHK(NdbBlob::getBlobTableName(b1.m_btname, g_ndb, g_opt.m_tname, "BL1") == 0);
+ DBG("BL1: inline=" << b1.m_inline << " part=" << b1.m_partsize << " table=" << b1.m_btname);
+ if (! g_opt.m_oneblob) {
+ Bcol& b2 = g_opt.m_blob2;
+ CHK(NdbBlob::getBlobTableName(b2.m_btname, g_ndb, g_opt.m_tname, "BL2") == 0);
+ DBG("BL2: inline=" << b2.m_inline << " part=" << b2.m_partsize << " table=" << b2.m_btname);
+ }
+ if (g_opt.m_seed != 0)
+ srandom(g_opt.m_seed);
+ for (g_loop = 0; g_opt.m_loop == 0 || g_loop < g_opt.m_loop; g_loop++) {
+ int style;
+ DBG("=== loop " << g_loop << " ===");
+ if (g_opt.m_seed == 0)
+ srandom(g_loop);
+ // pk
+ for (style = 0; style <= 2; style++) {
+ if (! testcase('k') || ! testcase(style))
+ continue;
+ DBG("--- pk ops " << stylename[style] << " ---");
+ if (testcase('n')) {
+ calcTups(false);
+ CHK(insertPk(style) == 0);
+ CHK(verifyBlob() == 0);
+ CHK(readPk(style) == 0);
+ if (testcase('u')) {
+ calcTups(style);
+ CHK(updatePk(style) == 0);
+ CHK(verifyBlob() == 0);
+ CHK(readPk(style) == 0);
+ }
+ CHK(deletePk() == 0);
+ CHK(verifyBlob() == 0);
+ }
+ if (testcase('w')) {
+ calcTups(false);
+ CHK(writePk(style) == 0);
+ CHK(verifyBlob() == 0);
+ CHK(readPk(style) == 0);
+ if (testcase('u')) {
+ calcTups(style);
+ CHK(writePk(style) == 0);
+ CHK(verifyBlob() == 0);
+ CHK(readPk(style) == 0);
+ }
+ CHK(deletePk() == 0);
+ CHK(verifyBlob() == 0);
+ }
+ }
+ // hash index
+ for (style = 0; style <= 2; style++) {
+ if (! testcase('i') || ! testcase(style))
+ continue;
+ DBG("--- idx ops " << stylename[style] << " ---");
+ if (testcase('n')) {
+ calcTups(false);
+ CHK(insertPk(style) == 0);
+ CHK(verifyBlob() == 0);
+ CHK(readIdx(style) == 0);
+ if (testcase('u')) {
+ calcTups(style);
+ CHK(updateIdx(style) == 0);
+ CHK(verifyBlob() == 0);
+ CHK(readIdx(style) == 0);
+ }
+ CHK(deleteIdx() == 0);
+ CHK(verifyBlob() == 0);
+ }
+ if (testcase('w')) {
+ calcTups(false);
+ CHK(writePk(style) == 0);
+ CHK(verifyBlob() == 0);
+ CHK(readIdx(style) == 0);
+ if (testcase('u')) {
+ calcTups(style);
+ CHK(writeIdx(style) == 0);
+ CHK(verifyBlob() == 0);
+ CHK(readIdx(style) == 0);
+ }
+ CHK(deleteIdx() == 0);
+ CHK(verifyBlob() == 0);
+ }
+ }
+ // scan table
+ for (style = 0; style <= 2; style++) {
+ if (! testcase('s') || ! testcase(style))
+ continue;
+ DBG("--- table scan " << stylename[style] << " ---");
+ calcTups(false);
+ CHK(insertPk(style) == 0);
+ CHK(verifyBlob() == 0);
+ CHK(readScan(style, false) == 0);
+ if (testcase('u')) {
+ CHK(updateScan(style, false) == 0);
+ CHK(verifyBlob() == 0);
+ }
+ CHK(deleteScan(false) == 0);
+ CHK(verifyBlob() == 0);
+ }
+ // scan index
+ for (style = 0; style <= 2; style++) {
+ if (! testcase('r') || ! testcase(style))
+ continue;
+ DBG("--- index scan " << stylename[style] << " ---");
+ calcTups(false);
+ CHK(insertPk(style) == 0);
+ CHK(verifyBlob() == 0);
+ CHK(readScan(style, true) == 0);
+ if (testcase('u')) {
+ CHK(updateScan(style, true) == 0);
+ CHK(verifyBlob() == 0);
+ }
+ CHK(deleteScan(true) == 0);
+ CHK(verifyBlob() == 0);
+ }
+ }
+ delete g_ndb;
+ return 0;
+}
+
+// separate performance test
+
+struct Tmr { // stolen from testOIBasic
+ Tmr() {
+ clr();
+ }
+ void clr() {
+ m_on = m_ms = m_cnt = m_time[0] = m_text[0] = 0;
+ }
+ void on() {
+ assert(m_on == 0);
+ m_on = NdbTick_CurrentMillisecond();
+ }
+ void off(unsigned cnt = 0) {
+ NDB_TICKS off = NdbTick_CurrentMillisecond();
+ assert(m_on != 0 && off >= m_on);
+ m_ms += off - m_on;
+ m_cnt += cnt;
+ m_on = 0;
+ }
+ const char* time() {
+ if (m_cnt == 0)
+ sprintf(m_time, "%u ms", m_ms);
+ else
+ sprintf(m_time, "%u ms per %u ( %u ms per 1000 )", m_ms, m_cnt, (1000 * m_ms) / m_cnt);
+ return m_time;
+ }
+ const char* pct (const Tmr& t1) {
+ if (0 < t1.m_ms)
+ sprintf(m_text, "%u pct", (100 * m_ms) / t1.m_ms);
+ else
+ sprintf(m_text, "[cannot measure]");
+ return m_text;
+ }
+ const char* over(const Tmr& t1) {
+ if (0 < t1.m_ms) {
+ if (t1.m_ms <= m_ms)
+ sprintf(m_text, "%u pct", (100 * (m_ms - t1.m_ms)) / t1.m_ms);
+ else
+ sprintf(m_text, "-%u pct", (100 * (t1.m_ms - m_ms)) / t1.m_ms);
+ } else
+ sprintf(m_text, "[cannot measure]");
+ return m_text;
+ }
+ NDB_TICKS m_on;
+ unsigned m_ms;
+ unsigned m_cnt;
+ char m_time[100];
+ char m_text[100];
+};
+
+static int
+testperf()
+{
+ if (! testcase('p'))
+ return 0;
+ DBG("=== perf test ===");
+ g_ndb = new Ndb(g_ncc, "TEST_DB");
+ CHK(g_ndb->init() == 0);
+ CHK(g_ndb->waitUntilReady() == 0);
+ g_dic = g_ndb->getDictionary();
+ NdbDictionary::Table tab(g_opt.m_tnameperf);
+ if (g_dic->getTable(tab.getName()) != 0)
+ CHK(g_dic->dropTable(tab) == 0);
+ // col A - pk
+ { NdbDictionary::Column col("A");
+ col.setType(NdbDictionary::Column::Unsigned);
+ col.setPrimaryKey(true);
+ tab.addColumn(col);
+ }
+ // col B - char 20
+ { NdbDictionary::Column col("B");
+ col.setType(NdbDictionary::Column::Char);
+ col.setLength(20);
+ col.setNullable(true);
+ tab.addColumn(col);
+ }
+ // col C - text
+ { NdbDictionary::Column col("C");
+ col.setType(NdbDictionary::Column::Text);
+ col.setInlineSize(20);
+ col.setPartSize(512);
+ col.setStripeSize(1);
+ col.setNullable(true);
+ tab.addColumn(col);
+ }
+ // create
+ CHK(g_dic->createTable(tab) == 0);
+ Uint32 cA = 0, cB = 1, cC = 2;
+ // timers
+ Tmr t1;
+ Tmr t2;
+ // insert char (one trans)
+ {
+ DBG("--- insert char ---");
+ t1.on();
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) {
+ CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0);
+ CHK(g_opr->insertTuple() == 0);
+ CHK(g_opr->equal(cA, (char*)&k) == 0);
+ CHK(g_opr->setValue(cB, "b") == 0);
+ CHK(g_con->execute(NoCommit) == 0);
+ }
+ t1.off(g_opt.m_rowsperf);
+ CHK(g_con->execute(Rollback) == 0);
+ DBG(t1.time());
+ g_opr = 0;
+ g_con = 0;
+ }
+ // insert text (one trans)
+ {
+ DBG("--- insert text ---");
+ t2.on();
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) {
+ CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0);
+ CHK(g_opr->insertTuple() == 0);
+ CHK(g_opr->equal(cA, (char*)&k) == 0);
+ CHK((g_bh1 = g_opr->getBlobHandle(cC)) != 0);
+ CHK((g_bh1->setValue("c", 1) == 0));
+ CHK(g_con->execute(NoCommit) == 0);
+ }
+ t2.off(g_opt.m_rowsperf);
+ CHK(g_con->execute(Rollback) == 0);
+ DBG(t2.time());
+ g_bh1 = 0;
+ g_opr = 0;
+ g_con = 0;
+ }
+ // insert overhead
+ DBG("insert overhead: " << t2.over(t1));
+ t1.clr();
+ t2.clr();
+ // insert
+ {
+ DBG("--- insert for read test ---");
+ unsigned n = 0;
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) {
+ CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0);
+ CHK(g_opr->insertTuple() == 0);
+ CHK(g_opr->equal(cA, (char*)&k) == 0);
+ CHK(g_opr->setValue(cB, "b") == 0);
+ CHK((g_bh1 = g_opr->getBlobHandle(cC)) != 0);
+ CHK((g_bh1->setValue("c", 1) == 0));
+ if (++n == g_opt.m_batch) {
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ n = 0;
+ }
+ }
+ if (n != 0) {
+ CHK(g_con->execute(Commit) == 0);
+ n = 0;
+ }
+ g_bh1 = 0;
+ g_opr = 0;
+ g_con = 0;
+ }
+ // pk read char (one trans)
+ {
+ DBG("--- pk read char ---");
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ Uint32 a;
+ char b[20];
+ t1.on();
+ for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) {
+ CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0);
+ CHK(g_opr->readTuple() == 0);
+ CHK(g_opr->equal(cA, (char*)&k) == 0);
+ CHK(g_opr->getValue(cA, (char*)&a) != 0);
+ CHK(g_opr->getValue(cB, b) != 0);
+ a = (Uint32)-1;
+ b[0] = 0;
+ CHK(g_con->execute(NoCommit) == 0);
+ CHK(a == k && strcmp(b, "b") == 0);
+ }
+ CHK(g_con->execute(Commit) == 0);
+ t1.off(g_opt.m_rowsperf);
+ DBG(t1.time());
+ g_opr = 0;
+ g_con = 0;
+ }
+ // pk read text (one trans)
+ {
+ DBG("--- pk read text ---");
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ Uint32 a;
+ char c[20];
+ t2.on();
+ for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) {
+ CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0);
+ CHK(g_opr->readTuple() == 0);
+ CHK(g_opr->equal(cA, (char*)&k) == 0);
+ CHK(g_opr->getValue(cA, (char*)&a) != 0);
+ CHK((g_bh1 = g_opr->getBlobHandle(cC)) != 0);
+ a = (Uint32)-1;
+ c[0] = 0;
+ CHK(g_con->execute(NoCommit) == 0);
+ Uint32 m = 20;
+ CHK(g_bh1->readData(c, m) == 0);
+ CHK(a == k && m == 1 && strcmp(c, "c") == 0);
+ }
+ CHK(g_con->execute(Commit) == 0);
+ t2.off(g_opt.m_rowsperf);
+ DBG(t2.time());
+ g_opr = 0;
+ g_con = 0;
+ }
+ // pk read overhead
+ DBG("pk read overhead: " << t2.over(t1));
+ t1.clr();
+ t2.clr();
+ // scan read char
+ {
+ DBG("--- scan read char ---");
+ Uint32 a;
+ char b[20];
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_ops = g_con->getNdbScanOperation(tab.getName())) != 0);
+ CHK(g_ops->readTuples(NdbScanOperation::LM_Read) == 0);
+ CHK(g_ops->getValue(cA, (char*)&a) != 0);
+ CHK(g_ops->getValue(cB, b) != 0);
+ CHK(g_con->execute(NoCommit) == 0);
+ unsigned n = 0;
+ t1.on();
+ while (1) {
+ a = (Uint32)-1;
+ b[0] = 0;
+ int ret;
+ CHK((ret = g_ops->nextResult(true)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ CHK(a < g_opt.m_rowsperf && strcmp(b, "b") == 0);
+ n++;
+ }
+ CHK(n == g_opt.m_rowsperf);
+ t1.off(g_opt.m_rowsperf);
+ DBG(t1.time());
+ g_ops = 0;
+ g_con = 0;
+ }
+ // scan read text
+ {
+ DBG("--- read text ---");
+ Uint32 a;
+ char c[20];
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ CHK((g_ops = g_con->getNdbScanOperation(tab.getName())) != 0);
+ CHK(g_ops->readTuples(NdbScanOperation::LM_Read) == 0);
+ CHK(g_ops->getValue(cA, (char*)&a) != 0);
+ CHK((g_bh1 = g_ops->getBlobHandle(cC)) != 0);
+ CHK(g_con->execute(NoCommit) == 0);
+ unsigned n = 0;
+ t2.on();
+ while (1) {
+ a = (Uint32)-1;
+ c[0] = 0;
+ int ret;
+ CHK((ret = g_ops->nextResult(true)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ Uint32 m = 20;
+ CHK(g_bh1->readData(c, m) == 0);
+ CHK(a < g_opt.m_rowsperf && m == 1 && strcmp(c, "c") == 0);
+ n++;
+ }
+ CHK(n == g_opt.m_rowsperf);
+ t2.off(g_opt.m_rowsperf);
+ DBG(t2.time());
+ g_bh1 = 0;
+ g_ops = 0;
+ g_con = 0;
+ }
+ // scan read overhead
+ DBG("scan read overhead: " << t2.over(t1));
+ t1.clr();
+ t2.clr();
+ delete g_ndb;
+ return 0;
+}
+
+// bug tests
+
+static int
+bugtest_4088()
+{
+ unsigned i;
+ DBG("bug test 4088 - ndb api hang with mixed ops on index table");
+ // insert rows
+ calcTups(false);
+ CHK(insertPk(false) == 0);
+ // new trans
+ CHK((g_con = g_ndb->startTransaction()) != 0);
+ for (unsigned k = 0; k < g_opt.m_rows; k++) {
+ Tup& tup = g_tups[k];
+ // read table pk via index as a table
+ const unsigned pkcnt = 2;
+ Tup pktup[pkcnt];
+ for (i = 0; i < pkcnt; i++) {
+ char name[20];
+ // XXX guess table id
+ sprintf(name, "%d/%s", 4, g_opt.m_x1name);
+ CHK((g_opr = g_con->getNdbOperation(name)) != 0);
+ CHK(g_opr->readTuple() == 0);
+ CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+ CHK(g_opr->getValue("NDB$PK", (char*)&pktup[i].m_pk1) != 0);
+ }
+ // read blob inline via index as an index
+ CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0);
+ CHK(g_opx->readTuple() == 0);
+ CHK(g_opx->equal("PK2", tup.m_pk2) == 0);
+ assert(tup.m_blob1.m_buf != 0);
+ CHK(g_opx->getValue("BL1", (char*)tup.m_blob1.m_buf) != 0);
+ // execute
+ // BUG 4088: gets 1 tckeyconf, 1 tcindxconf, then hangs
+ CHK(g_con->execute(Commit) == 0);
+ // verify
+ for (i = 0; i < pkcnt; i++) {
+ CHK(pktup[i].m_pk1 == tup.m_pk1);
+ CHK(memcmp(pktup[i].m_pk2, tup.m_pk2, g_opt.m_pk2len) == 0);
+ }
+ CHK(memcmp(tup.m_blob1.m_val, tup.m_blob1.m_buf, 8 + g_opt.m_blob1.m_inline) == 0);
+ }
+ return 0;
+}
+
+static int
+bugtest_2222()
+{
+ return 0;
+}
+
+static int
+bugtest_3333()
+{
+ return 0;
+}
+
+static struct {
+ int m_bug;
+ int (*m_test)();
+} g_bugtest[] = {
+ { 4088, bugtest_4088 }
+};
+
+NDB_COMMAND(testOdbcDriver, "testBlobs", "testBlobs", "testBlobs", 65535)
+{
+ ndb_init();
+ while (++argv, --argc > 0) {
+ const char* arg = argv[0];
+ if (strcmp(arg, "-batch") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_batch = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-core") == 0) {
+ g_opt.m_core = true;
+ continue;
+ }
+ if (strcmp(arg, "-dbg") == 0) {
+ g_opt.m_dbg = true;
+ continue;
+ }
+ if (strcmp(arg, "-dbgall") == 0) {
+ g_opt.m_dbg = true;
+ g_opt.m_dbgall = true;
+ putenv(strdup("NDB_BLOB_DEBUG=1"));
+ continue;
+ }
+ if (strcmp(arg, "-dbug") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_dbug = strdup(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-full") == 0) {
+ g_opt.m_full = true;
+ continue;
+ }
+ if (strcmp(arg, "-loop") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_loop = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-parts") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_parts = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-rows") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_rows = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-rowsperf") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_rowsperf = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-seed") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_seed = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-skip") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_skip = strdup(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-test") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_test = strdup(argv[0]);
+ continue;
+ }
+ }
+ // metadata
+ if (strcmp(arg, "-pk2len") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_pk2len = atoi(argv[0]);
+ if (g_opt.m_pk2len <= g_max_pk2len)
+ continue;
+ }
+ }
+ if (strcmp(arg, "-oneblob") == 0) {
+ g_opt.m_oneblob = true;
+ continue;
+ }
+ // bugs
+ if (strcmp(arg, "-bug") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_bug = atoi(argv[0]);
+ for (unsigned i = 0; i < sizeof(g_bugtest)/sizeof(g_bugtest[0]); i++) {
+ if (g_opt.m_bug == g_bugtest[i].m_bug) {
+ g_opt.m_bugtest = g_bugtest[i].m_test;
+ break;
+ }
+ }
+ if (g_opt.m_bugtest != 0)
+ continue;
+ }
+ }
+ ndbout << "testOIBasic: unknown option " << arg << endl;
+ printusage();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ if (g_opt.m_dbug != 0) {
+ DBUG_PUSH(g_opt.m_dbug);
+ }
+ if (g_opt.m_pk2len == 0) {
+ char b[100];
+ b[0] = 0;
+ if (g_opt.m_skip != 0)
+ strcpy(b, g_opt.m_skip);
+ strcat(b, "i");
+ strcat(b, "r");
+ g_opt.m_skip = strdup(b);
+ }
+ g_ncc = new Ndb_cluster_connection();
+ if (g_ncc->connect(30) != 0 || testmain() == -1 || testperf() == -1) {
+ ndbout << "line " << __LINE__ << " FAIL loop=" << g_loop << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ delete g_ncc;
+ g_ncc = 0;
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
+// vim: set sw=2 et:
diff --git a/storage/ndb/test/ndbapi/testDataBuffers.cpp b/storage/ndb/test/ndbapi/testDataBuffers.cpp
new file mode 100644
index 00000000000..aaecb6ee61e
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testDataBuffers.cpp
@@ -0,0 +1,630 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ * testDataBuffers
+ *
+ * Test getValue() of byte arrays:
+ * - using application buffers of different alignments and sizes
+ * - using NdbApi allocated small (<32) and big (>=32) buffers
+ *
+ * Verifies fixes to tickets 189 and 206.
+ *
+ * Options: see printusage() below.
+ *
+ * Creates tables TB00 to TB15
+ */
+
+#include <ndb_global.h>
+
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NdbTest.hpp>
+#include <NdbSchemaCon.hpp>
+// limits
+static int const MaxAttr = 64;
+static int const MaxOper = 1000;
+static int const MaxSize = 10000;
+static int const MaxOff = 64; // max offset to add to data buffer
+static int const MaxData = MaxSize + MaxOff + 100;
+
+// options
+static int attrcnt = 25;
+static int existok = 0;
+static bool kontinue = false;
+static int loopcnt = 1;
+static int opercnt = 100; // also does this many scans
+static int randomizer = 171317;
+static int sizelim = 500;
+static int xverbose = 0;
+
+static void printusage() {
+ ndbout
+ << "usage: testDataBuffers options [default/max]"
+ << endl
+ << "NOTE: too large combinations result in NDB error"
+ << endl
+ << "-a N number of attributes (including the key) [25/64]"
+ << endl
+ << "-e no error if table exists (assumed to have same structure)"
+ << endl
+ << "-k on error continue with next test case"
+ << endl
+ << "-l N number of loops to run, 0 means infinite [1]"
+ << endl
+ << "-o N number of operations (rows in each table) [100/1000]"
+ << endl
+ << "-r N source of randomness (big number (prime)) [171317]"
+ << endl
+ << "-s N array size limit (rounded up in some tests) [500/10000]"
+ << endl
+ << "-x extremely verbose"
+ << endl
+ << "Tables: TB00 .. TB15"
+ << endl
+ ;
+}
+
+static Ndb* ndb = 0;
+static NdbSchemaCon* tcon = 0;
+static NdbSchemaOp* top = 0;
+static NdbConnection* con = 0;
+static NdbOperation* op = 0;
+static NdbScanOperation* sop = 0;
+static NdbResultSet* rs = 0;
+
+static int
+ndberror(char const* fmt, ...)
+{
+ va_list ap;
+ char buf[200];
+ va_start(ap, fmt);
+ BaseString::vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ ndbout << buf << " --" << endl;
+ if (ndb)
+ ndbout << "ndb : " << ndb->getNdbError() << endl;
+ if (tcon)
+ ndbout << "tcon: " << tcon->getNdbError() << endl;
+ if (top)
+ ndbout << "top: " << top->getNdbError() << endl;
+ if (con)
+ ndbout << "con : " << con->getNdbError() << endl;
+ if (op)
+ ndbout << "op : " << op->getNdbError() << endl;
+ return -1;
+}
+
+static int
+chkerror(char const* fmt, ...)
+{
+ va_list ap;
+ char buf[200];
+ va_start(ap, fmt);
+ BaseString::vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ ndbout << "*** check failed: " << buf << " ***" << endl;
+ return -1;
+}
+
+// alignment of addresses and data sizes
+
+static bool isAligned(UintPtr x)
+{
+ return ((x & 3) == 0);
+}
+static bool isAligned(char* p)
+{
+ return isAligned(UintPtr(p));
+}
+static unsigned toAligned(UintPtr x)
+{
+ while (! isAligned(x))
+ x++;
+ return x;
+}
+static char* toAligned(char* p)
+{
+ while (! isAligned(p))
+ p++;
+ return p;
+}
+
+// byte value for key k column i byte j
+static int byteVal(int k, int i, int j)
+{
+ return '0' + (k + i + j) % 10;
+}
+
+// tables
+
+static char tab[20] = "";
+
+static struct col {
+ char aAttrName[20];
+ AttrType aAttrType;
+ int aAttrSize;
+ int aArraySize;
+ KeyType aTupleKey;
+ bool nullable;
+ NdbRecAttr* aRa;
+ char* buf;
+ int bufsiz;
+ char data[MaxData];
+} ccol[MaxAttr];
+
+static int key = 0;
+
+// independent test bits
+static bool alignAddr; // align our buffer addresses to 4x
+static bool alignSize; // align data sizes to 4x
+static bool useBuf; // use our buffers for output
+static bool noRandom; // do not randomize sizes and offsets
+static int testbits = 4;
+
+static int
+makeSize(int i)
+{
+ int n;
+ if (noRandom)
+ n = i;
+ else
+ n = i * randomizer;
+ n %= sizelim;
+ if (n <= 0)
+ n = 1;
+ if (alignSize)
+ n = toAligned(n);
+ return n;
+}
+
+static int
+makeOff(int k)
+{
+ int n;
+ if (alignAddr)
+ n = 0;
+ else if (noRandom)
+ n = k;
+ else
+ n = k * randomizer;
+ n %= MaxOff;
+ if (n < 0)
+ n = -n;
+ return n;
+}
+
+static int
+testcase(Ndb_cluster_connection&cc, int flag)
+{
+ ndbout << "--- case " << flag << " ---" << endl;
+ sprintf(tab, "TB%02d", flag);
+
+ alignAddr = ! (flag & 1);
+ ndbout << (alignAddr ? "align addresses" : "mis-align addresses") << endl;
+ alignSize = ! (flag & 2);
+ ndbout << (alignSize ? "align data sizes" : "mis-align data sizes") << endl;
+ useBuf = ! (flag & 4);
+ ndbout << (useBuf ? "use our buffers" : "use ndbapi buffers") << endl;
+ noRandom = ! (flag & 8);
+ ndbout << (noRandom ? "simple sizes" : "randomize sizes") << endl;
+
+ int smax = 0, stot = 0, i;
+ if (xverbose)
+ ndbout << "- define table " << tab << endl;
+ for (i = 0; i < attrcnt; i++) {
+ col& c = ccol[i];
+ memset(&c, 0, sizeof(c));
+ sprintf(c.aAttrName, "C%d", i);
+ if (i == 0) {
+ c.aAttrType = UnSigned;
+ c.aAttrSize = 32;
+ c.aArraySize = 1;
+ c.aTupleKey = TupleKey;
+ c.nullable = false;
+ } else {
+ c.aAttrType = String;
+ c.aAttrSize = 8;
+ c.aArraySize = makeSize(i);
+ if (smax < c.aArraySize)
+ smax = c.aArraySize;
+ stot += c.aArraySize;
+ c.aTupleKey = NoKey;
+ c.nullable = true;
+ if (xverbose)
+ ndbout << "-- column " << i << " size=" << c.aArraySize << endl;
+ }
+ c.buf = toAligned(c.data);
+ c.bufsiz = sizeof(c.data) - (c.buf - c.data);
+ }
+ ndbout << "tab=" << tab << " cols=" << attrcnt
+ << " size max=" << smax << " tot=" << stot << endl;
+
+ ndb = new Ndb(&cc, "TEST_DB");
+ if (ndb->init() != 0)
+ return ndberror("init");
+ if (ndb->waitUntilReady(30) < 0)
+ return ndberror("waitUntilReady");
+
+ if ((tcon = NdbSchemaCon::startSchemaTrans(ndb)) == 0)
+ return ndberror("startSchemaTransaction");
+ if ((top = tcon->getNdbSchemaOp()) == 0)
+ return ndberror("getNdbSchemaOp");
+ if (top->createTable(tab) < 0)
+ return ndberror("createTable");
+ for (i = 0; i < attrcnt; i++) {
+ col& c = ccol[i];
+ if (top->createAttribute(
+ c.aAttrName,
+ c.aTupleKey,
+ c.aAttrSize,
+ c.aArraySize,
+ c.aAttrType,
+ MMBased,
+ c.nullable
+ ) < 0)
+ return ndberror("createAttribute col=%d", i);
+ }
+ if (tcon->execute() < 0) {
+ if (! (tcon->getNdbError().code == 721 && existok))
+ return ndberror("execute");
+ ndbout << "using " << tab << endl;
+ } else {
+ ndbout << "created " << tab << endl;
+ }
+ top = 0;
+ tcon = 0;
+
+ if (xverbose)
+ ndbout << "- delete" << endl;
+ int delcnt = 0;
+ for (key = 0; key < opercnt; key++) {
+ if ((con = ndb->startTransaction()) == 0)
+ return ndberror("startTransaction key=%d", key);
+ if ((op = con->getNdbOperation(tab)) == 0)
+ return ndberror("getNdbOperation key=%d", key);
+ if (op->deleteTuple() < 0)
+ return ndberror("deleteTuple key=%d", key);
+ for (i = 0; i < attrcnt; i++) {
+ col& c = ccol[i];
+ if (i == 0) {
+ if (op->equal(c.aAttrName, (char*)&key, sizeof(key)) < 0)
+ return ndberror("equal key=%d", key);
+ } else {
+ }
+ }
+ if (con->execute(Commit) < 0) {
+ if (con->getNdbError().code != 626)
+ return ndberror("execute key=%d", key);
+ } else {
+ delcnt++;
+ }
+ ndb->closeTransaction(con);
+ }
+ con = 0;
+ op = 0;
+ ndbout << "deleted " << delcnt << endl;
+
+ if (xverbose)
+ ndbout << "- insert" << endl;
+ for (key = 0; key < opercnt; key++) {
+ int off = makeOff(key);
+ if ((con = ndb->startTransaction()) == 0)
+ return ndberror("startTransaction key=%d", key);
+ if ((op = con->getNdbOperation(tab)) == 0)
+ return ndberror("getNdbOperation key=%d", key);
+ if (op->insertTuple() < 0)
+ return ndberror("insertTuple key=%d", key);
+ for (i = 0; i < attrcnt; i++) {
+ col& c = ccol[i];
+ if (i == 0) {
+ if (op->equal(c.aAttrName, (char*)&key, sizeof(key)) < 0)
+ return ndberror("equal key=%d", key);
+ } else {
+ memset(c.buf, 'A', c.bufsiz);
+ for (int j = 0; j < c.aArraySize; j++)
+ c.buf[j + off] = byteVal(key, i, j);
+ if (op->setValue(c.aAttrName, c.buf + off, c.aArraySize) < 0)
+ return ndberror("setValue key=%d col=%d", key, i);
+ }
+ }
+ if (con->execute(Commit) < 0)
+ return ndberror("execute key=%d", key);
+ ndb->closeTransaction(con);
+ }
+ con = 0;
+ op = 0;
+ ndbout << "inserted " << key << endl;
+
+ if (xverbose)
+ ndbout << "- select" << endl;
+ for (key = 0; key < opercnt; key++) {
+ int off = makeOff(key);
+ if (xverbose)
+ ndbout << "-- key " << key << " off=" << off << endl;
+ if ((con = ndb->startTransaction()) == 0)
+ return ndberror("startTransaction key=%d", key);
+ if ((op = con->getNdbOperation(tab)) == 0)
+ return ndberror("getNdbOperation key=%d", key);
+ if (op->readTuple() < 0)
+ return ndberror("readTuple key=%d", key);
+ for (i = 0; i < attrcnt; i++) {
+ col& c = ccol[i];
+ if (i == 0) {
+ if (op->equal(c.aAttrName, (char*)&key, sizeof(key)) < 0)
+ return ndberror("equal key=%d", key);
+ } else {
+ if (xverbose) {
+ char tmp[20];
+ if (useBuf)
+ sprintf(tmp, "0x%p", c.buf + off);
+ else
+ strcpy(tmp, "ndbapi");
+ ndbout << "--- column " << i << " addr=" << tmp << endl;
+ }
+ memset(c.buf, 'B', c.bufsiz);
+ if (useBuf) {
+ if (op->getValue(c.aAttrName, c.buf + off) < 0)
+ return ndberror("getValue key=%d col=%d", key, i);
+ } else {
+ if ((c.aRa = op->getValue(c.aAttrName)) == 0)
+ return ndberror("getValue key=%d col=%d", key, i);
+ }
+ }
+ }
+ if (con->execute(Commit) != 0)
+ return ndberror("execute key=%d", key);
+ for (i = 0; i < attrcnt; i++) {
+ col& c = ccol[i];
+ if (i == 0) {
+ } else if (useBuf) {
+ int j;
+ for (j = 0; j < off; j++) {
+ if (c.buf[j] != 'B') {
+ return chkerror("mismatch before key=%d col=%d pos=%d ok=%02x bad=%02x",
+ key, i, j, 'B', c.buf[j]);
+ }
+ }
+ for (j = 0; j < c.aArraySize; j++) {
+ if (c.buf[j + off] != byteVal(key, i, j)) {
+ return chkerror("mismatch key=%d col=%d pos=%d ok=%02x bad=%02x",
+ key, i, j, byteVal(key, i, j), c.buf[j]);
+ }
+ }
+ for (j = c.aArraySize + off; j < c.bufsiz; j++) {
+ if (c.buf[j] != 'B') {
+ return chkerror("mismatch after key=%d col=%d pos=%d ok=%02x bad=%02x",
+ key, i, j, 'B', c.buf[j]);
+ }
+ }
+ } else {
+ char* buf = c.aRa->aRef();
+ if (buf == 0)
+ return ndberror("null aRef key=%d col%d", key, i);
+ for (int j = 0; j < c.aArraySize; j++) {
+ if (buf[j] != byteVal(key, i, j)) {
+ return chkerror("mismatch key=%d col=%d pos=%d ok=%02x bad=%02x",
+ key, i, j, byteVal(key, i, j), buf[j]);
+ }
+ }
+ }
+ }
+ ndb->closeTransaction(con);
+ }
+ con = 0;
+ op = 0;
+ ndbout << "selected " << key << endl;
+
+ if (xverbose)
+ ndbout << "- scan" << endl;
+ char found[MaxOper];
+ int k;
+ for (k = 0; k < opercnt; k++)
+ found[k] = 0;
+ for (key = 0; key < opercnt; key++) {
+ int off = makeOff(key);
+ if (xverbose)
+ ndbout << "-- key " << key << " off=" << off << endl;
+ int newkey = 0;
+ if ((con = ndb->startTransaction()) == 0)
+ return ndberror("startTransaction key=%d", key);
+ if ((op = sop = con->getNdbScanOperation(tab)) == 0)
+ return ndberror("getNdbOperation key=%d", key);
+ if (sop->readTuples(1))
+ return ndberror("openScanRead key=%d", key);
+ {
+ col& c = ccol[0];
+ if (op->load_const_u32(1, key) < 0)
+ return ndberror("load_const_u32");
+ if (op->read_attr(c.aAttrName, 2) < 0)
+ return ndberror("read_attr");
+ if (op->branch_eq(1, 2, 0) < 0)
+ return ndberror("branch_eq");
+ if (op->interpret_exit_nok() < 0)
+ return ndberror("interpret_exit_nok");
+ if (op->def_label(0) < 0)
+ return ndberror("def_label");
+ if (op->interpret_exit_ok() < 0)
+ return ndberror("interpret_exit_ok");
+ }
+ for (i = 0; i < attrcnt; i++) {
+ col& c = ccol[i];
+ if (i == 0) {
+ if (op->getValue(c.aAttrName, (char*)&newkey) < 0)
+ return ndberror("getValue key=%d col=%d", key, i);
+ } else {
+ if (xverbose) {
+ char tmp[20];
+ if (useBuf)
+ sprintf(tmp, "0x%p", c.buf + off);
+ else
+ strcpy(tmp, "ndbapi");
+ ndbout << "--- column " << i << " addr=" << tmp << endl;
+ }
+ memset(c.buf, 'C', c.bufsiz);
+ if (useBuf) {
+ if (op->getValue(c.aAttrName, c.buf + off) < 0)
+ return ndberror("getValue key=%d col=%d", key, i);
+ } else {
+ if ((c.aRa = op->getValue(c.aAttrName)) == 0)
+ return ndberror("getValue key=%d col=%d", key, i);
+ }
+ }
+ }
+ if (con->execute(NoCommit) < 0)
+ return ndberror("executeScan key=%d", key);
+ int ret, cnt = 0;
+ while ((ret = sop->nextResult()) == 0) {
+ if (key != newkey)
+ return ndberror("unexpected key=%d newkey=%d", key, newkey);
+ for (i = 1; i < attrcnt; i++) {
+ col& c = ccol[i];
+ if (useBuf) {
+ int j;
+ for (j = 0; j < off; j++) {
+ if (c.buf[j] != 'C') {
+ return chkerror("mismatch before key=%d col=%d pos=%d ok=%02x bad=%02x",
+ key, i, j, 'C', c.buf[j]);
+ }
+ }
+ for (j = 0; j < c.aArraySize; j++) {
+ if (c.buf[j + off] != byteVal(key, i, j)) {
+ return chkerror("mismatch key=%d col=%d pos=%d ok=%02x bad=%02x",
+ key, i, j, byteVal(key, i, j), c.buf[j]);
+ }
+ }
+ for (j = c.aArraySize + off; j < c.bufsiz; j++) {
+ if (c.buf[j] != 'C') {
+ return chkerror("mismatch after key=%d col=%d pos=%d ok=%02x bad=%02x",
+ key, i, j, 'C', c.buf[j]);
+ }
+ }
+ } else {
+ char* buf = c.aRa->aRef();
+ if (buf == 0)
+ return ndberror("null aRef key=%d col%d", key, i);
+ for (int j = 0; j < c.aArraySize; j++) {
+ if (buf[j] != byteVal(key, i, j)) {
+ return chkerror("mismatch key=%d col=%d pos=%d ok=%02x bad=%02x",
+ key, i, j, byteVal(key, i, j), buf[j]);
+ }
+ }
+ }
+ }
+ cnt++;
+ }
+ if (ret < 0)
+ return ndberror("nextScanResult key=%d", key);
+ if (cnt != 1)
+ return ndberror("scan key=%d found %d", key, cnt);
+ found[key] = 1;
+ ndb->closeTransaction(con);
+ }
+ con = 0;
+ op = 0;
+ for (k = 0; k < opercnt; k++)
+ if (! found[k])
+ return ndberror("key %d not found", k);
+ ndbout << "scanned " << key << endl;
+
+ ndb = 0;
+ ndbout << "done" << endl;
+ return 0;
+}
+
+NDB_COMMAND(testDataBuffers, "testDataBuffers", "testDataBuffers", "testDataBuffers", 65535)
+{
+ int i;
+ ndb_init();
+ while (++argv, --argc > 0) {
+ char const* p = argv[0];
+ if (*p++ != '-' || strlen(p) != 1)
+ goto wrongargs;
+ switch (*p) {
+ case 'a':
+ if (++argv, --argc > 0) {
+ attrcnt = atoi(argv[0]);
+ if (1 <= attrcnt && attrcnt <= MaxAttr)
+ break;
+ }
+ goto wrongargs;
+ case 'e':
+ existok = 1;
+ break;
+ case 'k':
+ kontinue = true;
+ break;
+ case 'l':
+ if (++argv, --argc > 0) {
+ loopcnt = atoi(argv[0]);
+ if (0 <= loopcnt)
+ break;
+ }
+ goto wrongargs;
+ case 'o':
+ if (++argv, --argc > 0) {
+ opercnt = atoi(argv[0]);
+ if (0 <= opercnt && opercnt <= MaxOper)
+ break;
+ }
+ goto wrongargs;
+ case 'r':
+ if (++argv, --argc > 0) {
+ randomizer = atoi(argv[0]);
+ if (1 <= randomizer)
+ break;
+ }
+ goto wrongargs;
+ case 's':
+ if (++argv, --argc > 0) {
+ sizelim = atoi(argv[0]);
+ if (1 <= sizelim && sizelim <= MaxSize)
+ break;
+ }
+ goto wrongargs;
+ case 'x':
+ xverbose = 1;
+ break;
+ default:
+ wrongargs:
+ printusage();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ }
+ unsigned ok = true;
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1))
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ for (i = 1; 0 == loopcnt || i <= loopcnt; i++) {
+ ndbout << "=== loop " << i << " ===" << endl;
+ for (int flag = 0; flag < (1<<testbits); flag++) {
+ if (testcase(con, flag) < 0) {
+ ok = false;
+ if (! kontinue)
+ goto out;
+ }
+ }
+ }
+out:
+ return NDBT_ProgramExit(ok ? NDBT_OK : NDBT_FAILED);
+}
+
+// vim: set sw=4:
diff --git a/storage/ndb/test/ndbapi/testDeadlock.cpp b/storage/ndb/test/ndbapi/testDeadlock.cpp
new file mode 100644
index 00000000000..0070a7ecc83
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testDeadlock.cpp
@@ -0,0 +1,523 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <NdbApi.hpp>
+#include <NdbOut.hpp>
+#include <NdbMutex.h>
+#include <NdbCondition.h>
+#include <NdbThread.h>
+#include <NdbTest.hpp>
+
+struct Opt {
+ bool m_dbg;
+ const char* m_scan;
+ const char* m_tname;
+ const char* m_xname;
+ Opt() :
+ m_dbg(true),
+ m_scan("tx"),
+ m_tname("T"),
+ m_xname("X")
+ {}
+};
+
+static void
+printusage()
+{
+ Opt d;
+ ndbout
+ << "usage: testDeadlock" << endl
+ << "-scan tx scan table, index [" << d.m_scan << "]" << endl
+ ;
+}
+
+static Opt g_opt;
+
+static NdbMutex *ndbout_mutex= NULL;
+static Ndb_cluster_connection *g_cluster_connection= 0;
+#define DBG(x) \
+ do { \
+ if (! g_opt.m_dbg) break; \
+ NdbMutex_Lock(ndbout_mutex); \
+ ndbout << "line " << __LINE__ << " " << x << endl; \
+ NdbMutex_Unlock(ndbout_mutex); \
+ } while (0)
+
+#define CHK(x) \
+ do { \
+ if (x) break; \
+ ndbout << "line " << __LINE__ << ": " << #x << " failed" << endl; \
+ return -1; \
+ } while (0)
+
+#define CHN(p, x) \
+ do { \
+ if (x) break; \
+ ndbout << "line " << __LINE__ << ": " << #x << " failed" << endl; \
+ ndbout << (p)->getNdbError() << endl; \
+ return -1; \
+ } while (0)
+
+// threads
+
+typedef int (*Runstep)(struct Thr& thr);
+
+struct Thr {
+ enum State { Wait, Start, Stop, Stopped, Exit };
+ State m_state;
+ int m_no;
+ Runstep m_runstep;
+ int m_ret;
+ NdbMutex* m_mutex;
+ NdbCondition* m_cond;
+ NdbThread* m_thread;
+ void* m_status;
+ Ndb* m_ndb;
+ NdbConnection* m_con;
+ NdbScanOperation* m_scanop;
+ NdbIndexScanOperation* m_indexscanop;
+ //
+ Thr(int no);
+ ~Thr();
+ int run();
+ void start(Runstep runstep);
+ void stop();
+ void stopped();
+ void lock() { NdbMutex_Lock(m_mutex); }
+ void unlock() { NdbMutex_Unlock(m_mutex); }
+ void wait() { NdbCondition_Wait(m_cond, m_mutex); }
+ void signal() { NdbCondition_Signal(m_cond); }
+ void exit();
+ void join() { NdbThread_WaitFor(m_thread, &m_status); }
+};
+
+static NdbOut&
+operator<<(NdbOut& out, const Thr& thr) {
+ out << "thr " << thr.m_no;
+ return out;
+}
+
+extern "C" { static void* runthread(void* arg); }
+
+Thr::Thr(int no)
+{
+ m_state = Wait;
+ m_no = no;
+ m_runstep = 0;
+ m_ret = 0;
+ m_mutex = NdbMutex_Create();
+ m_cond = NdbCondition_Create();
+ assert(m_mutex != 0 && m_cond != 0);
+ const unsigned stacksize = 256 * 1024;
+ const NDB_THREAD_PRIO prio = NDB_THREAD_PRIO_LOW;
+ m_thread = NdbThread_Create(runthread, (void**)this, stacksize, "me", prio);
+ if (m_thread == 0) {
+ DBG("create thread failed: errno=" << errno);
+ m_ret = -1;
+ }
+ m_status = 0;
+ m_ndb = 0;
+ m_con = 0;
+ m_scanop = 0;
+ m_indexscanop = 0;
+}
+
+Thr::~Thr()
+{
+ if (m_thread != 0)
+ NdbThread_Destroy(&m_thread);
+ if (m_cond != 0)
+ NdbCondition_Destroy(m_cond);
+ if (m_mutex != 0)
+ NdbMutex_Destroy(m_mutex);
+}
+
+static void*
+runthread(void* arg) {
+ Thr& thr = *(Thr*)arg;
+ thr.run();
+ return 0;
+}
+
+int
+Thr::run()
+{
+ DBG(*this << " run");
+ while (true) {
+ lock();
+ while (m_state != Start && m_state != Exit) {
+ wait();
+ }
+ if (m_state == Exit) {
+ DBG(*this << " exit");
+ unlock();
+ break;
+ }
+ m_ret = (*m_runstep)(*this);
+ m_state = Stopped;
+ signal();
+ unlock();
+ if (m_ret != 0) {
+ DBG(*this << " error exit");
+ break;
+ }
+ }
+ delete m_ndb;
+ m_ndb = 0;
+ return 0;
+}
+
+void
+Thr::start(Runstep runstep)
+{
+ lock();
+ m_state = Start;
+ m_runstep = runstep;
+ signal();
+ unlock();
+}
+
+void
+Thr::stopped()
+{
+ lock();
+ while (m_state != Stopped) {
+ wait();
+ }
+ m_state = Wait;
+ unlock();
+}
+
+void
+Thr::exit()
+{
+ lock();
+ m_state = Exit;
+ signal();
+ unlock();
+}
+
+// general
+
+static int
+runstep_connect(Thr& thr)
+{
+ Ndb* ndb = thr.m_ndb = new Ndb(g_cluster_connection, "TEST_DB");
+ CHN(ndb, ndb->init() == 0);
+ CHN(ndb, ndb->waitUntilReady() == 0);
+ DBG(thr << " connected");
+ return 0;
+}
+
+static int
+runstep_starttx(Thr& thr)
+{
+ Ndb* ndb = thr.m_ndb;
+ assert(ndb != 0);
+ CHN(ndb, (thr.m_con = ndb->startTransaction()) != 0);
+ DBG("thr " << thr.m_no << " tx started");
+ return 0;
+}
+
+/*
+ * WL1822 flush locks
+ *
+ * Table T with 3 tuples X, Y, Z.
+ * Two transactions (* = lock wait).
+ *
+ * - tx1 reads and locks Z
+ * - tx2 scans X, Y, *Z
+ * - tx2 returns X, Y before lock wait on Z
+ * - tx1 reads and locks *X
+ * - api asks for next tx2 result
+ * - LQH unlocks X via ACC or TUX [*]
+ * - tx1 gets lock on X
+ * - tx1 returns X to api
+ * - api commits tx1
+ * - tx2 gets lock on Z
+ * - tx2 returs Z to api
+ *
+ * The point is deadlock is avoided due to [*].
+ * The test is for 1 db node and 1 fragment table.
+ */
+
+static char wl1822_scantx = 0;
+
+static const Uint32 wl1822_valA[3] = { 0, 1, 2 };
+static const Uint32 wl1822_valB[3] = { 3, 4, 5 };
+
+static Uint32 wl1822_bufA = ~0;
+static Uint32 wl1822_bufB = ~0;
+
+// map scan row to key (A) and reverse
+static unsigned wl1822_r2k[3] = { 0, 0, 0 };
+static unsigned wl1822_k2r[3] = { 0, 0, 0 };
+
+static int
+wl1822_createtable(Thr& thr)
+{
+ Ndb* ndb = thr.m_ndb;
+ assert(ndb != 0);
+ NdbDictionary::Dictionary* dic = ndb->getDictionary();
+ // drop T
+ if (dic->getTable(g_opt.m_tname) != 0)
+ CHN(dic, dic->dropTable(g_opt.m_tname) == 0);
+ // create T
+ NdbDictionary::Table tab(g_opt.m_tname);
+ tab.setFragmentType(NdbDictionary::Object::FragAllSmall);
+ { NdbDictionary::Column col("A");
+ col.setType(NdbDictionary::Column::Unsigned);
+ col.setPrimaryKey(true);
+ tab.addColumn(col);
+ }
+ { NdbDictionary::Column col("B");
+ col.setType(NdbDictionary::Column::Unsigned);
+ col.setPrimaryKey(false);
+ tab.addColumn(col);
+ }
+ CHN(dic, dic->createTable(tab) == 0);
+ // create X
+ NdbDictionary::Index ind(g_opt.m_xname);
+ ind.setTable(g_opt.m_tname);
+ ind.setType(NdbDictionary::Index::OrderedIndex);
+ ind.setLogging(false);
+ ind.addColumn("B");
+ CHN(dic, dic->createIndex(ind) == 0);
+ DBG("created " << g_opt.m_tname << ", " << g_opt.m_xname);
+ return 0;
+}
+
+static int
+wl1822_insertrows(Thr& thr)
+{
+ // insert X, Y, Z
+ Ndb* ndb = thr.m_ndb;
+ assert(ndb != 0);
+ NdbConnection* con;
+ NdbOperation* op;
+ for (unsigned k = 0; k < 3; k++) {
+ CHN(ndb, (con = ndb->startTransaction()) != 0);
+ CHN(con, (op = con->getNdbOperation(g_opt.m_tname)) != 0);
+ CHN(op, op->insertTuple() == 0);
+ CHN(op, op->equal("A", (char*)&wl1822_valA[k]) == 0);
+ CHN(op, op->setValue("B", (char*)&wl1822_valB[k]) == 0);
+ CHN(con, con->execute(Commit) == 0);
+ ndb->closeTransaction(con);
+ }
+ DBG("inserted X, Y, Z");
+ return 0;
+}
+
+static int
+wl1822_getscanorder(Thr& thr)
+{
+ // cheat, table order happens to be key order in my test
+ wl1822_r2k[0] = 0;
+ wl1822_r2k[1] = 1;
+ wl1822_r2k[2] = 2;
+ wl1822_k2r[0] = 0;
+ wl1822_k2r[1] = 1;
+ wl1822_k2r[2] = 2;
+ DBG("scan order determined");
+ return 0;
+}
+
+static int
+wl1822_tx1_readZ(Thr& thr)
+{
+ // tx1 read Z with exclusive lock
+ NdbConnection* con = thr.m_con;
+ assert(con != 0);
+ NdbOperation* op;
+ CHN(con, (op = con->getNdbOperation(g_opt.m_tname)) != 0);
+ CHN(op, op->readTupleExclusive() == 0);
+ CHN(op, op->equal("A", wl1822_valA[wl1822_r2k[2]]) == 0);
+ wl1822_bufB = ~0;
+ CHN(op, op->getValue("B", (char*)&wl1822_bufB) != 0);
+ CHN(con, con->execute(NoCommit) == 0);
+ CHK(wl1822_bufB == wl1822_valB[wl1822_r2k[2]]);
+ DBG("tx1 locked Z");
+ return 0;
+}
+
+static int
+wl1822_tx2_scanXY(Thr& thr)
+{
+ // tx2 scan X, Y with exclusive lock
+ NdbConnection* con = thr.m_con;
+ assert(con != 0);
+ NdbScanOperation* scanop;
+ NdbIndexScanOperation* indexscanop;
+ NdbResultSet* rs;
+ if (wl1822_scantx == 't') {
+ CHN(con, (scanop = thr.m_scanop = con->getNdbScanOperation(g_opt.m_tname)) != 0);
+ DBG("tx2 scan exclusive " << g_opt.m_tname);
+ }
+ if (wl1822_scantx == 'x') {
+ CHN(con, (scanop = thr.m_scanop = indexscanop = thr.m_indexscanop = con->getNdbIndexScanOperation(g_opt.m_xname, g_opt.m_tname)) != 0);
+ DBG("tx2 scan exclusive " << g_opt.m_xname);
+ }
+ CHN(scanop, scanop->readTuplesExclusive(16) == 0);
+ CHN(scanop, scanop->getValue("A", (char*)&wl1822_bufA) != 0);
+ CHN(scanop, scanop->getValue("B", (char*)&wl1822_bufB) != 0);
+ CHN(con, con->execute(NoCommit) == 0);
+ unsigned row = 0;
+ while (row < 2) {
+ DBG("before row " << row);
+ int ret;
+ wl1822_bufA = wl1822_bufB = ~0;
+ CHN(con, (ret = scanop->nextResult(true)) == 0);
+ DBG("got row " << row << " a=" << wl1822_bufA << " b=" << wl1822_bufB);
+ CHK(wl1822_bufA == wl1822_valA[wl1822_r2k[row]]);
+ CHK(wl1822_bufB == wl1822_valB[wl1822_r2k[row]]);
+ row++;
+ }
+ return 0;
+}
+
+static int
+wl1822_tx1_readX_commit(Thr& thr)
+{
+ // tx1 read X with exclusive lock and commit
+ NdbConnection* con = thr.m_con;
+ assert(con != 0);
+ NdbOperation* op;
+ CHN(con, (op = con->getNdbOperation(g_opt.m_tname)) != 0);
+ CHN(op, op->readTupleExclusive() == 0);
+ CHN(op, op->equal("A", wl1822_valA[wl1822_r2k[2]]) == 0);
+ wl1822_bufB = ~0;
+ CHN(op, op->getValue("B", (char*)&wl1822_bufB) != 0);
+ CHN(con, con->execute(NoCommit) == 0);
+ CHK(wl1822_bufB == wl1822_valB[wl1822_r2k[2]]);
+ DBG("tx1 locked X");
+ CHN(con, con->execute(Commit) == 0);
+ DBG("tx1 commit");
+ return 0;
+}
+
+static int
+wl1822_tx2_scanZ_close(Thr& thr)
+{
+ // tx2 scan Z with exclusive lock and close scan
+ Ndb* ndb = thr.m_ndb;
+ NdbConnection* con = thr.m_con;
+ NdbScanOperation* scanop = thr.m_scanop;
+ assert(ndb != 0 && con != 0 && scanop != 0);
+ unsigned row = 2;
+ while (true) {
+ DBG("before row " << row);
+ int ret;
+ wl1822_bufA = wl1822_bufB = ~0;
+ CHN(con, (ret = scanop->nextResult(true)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ DBG("got row " << row << " a=" << wl1822_bufA << " b=" << wl1822_bufB);
+ CHK(wl1822_bufA == wl1822_valA[wl1822_r2k[row]]);
+ CHK(wl1822_bufB == wl1822_valB[wl1822_r2k[row]]);
+ row++;
+ }
+ ndb->closeTransaction(con);
+ CHK(row == 3);
+ return 0;
+}
+
+// threads are synced between each step
+static Runstep wl1822_step[][2] = {
+ { runstep_connect, runstep_connect },
+ { wl1822_createtable, 0 },
+ { wl1822_insertrows, 0 },
+ { wl1822_getscanorder, 0 },
+ { runstep_starttx, runstep_starttx },
+ { wl1822_tx1_readZ, 0 },
+ { 0, wl1822_tx2_scanXY },
+ { wl1822_tx1_readX_commit, wl1822_tx2_scanZ_close }
+};
+const unsigned wl1822_stepcount = sizeof(wl1822_step)/sizeof(wl1822_step[0]);
+
+static int
+wl1822_main(char scantx)
+{
+ wl1822_scantx = scantx;
+ static const unsigned thrcount = 2;
+ // create threads for tx1 and tx2
+ Thr* thrlist[2];
+ int n;
+ for (n = 0; n < thrcount; n++) {
+ Thr& thr = *(thrlist[n] = new Thr(1 + n));
+ CHK(thr.m_ret == 0);
+ }
+ // run the steps
+ for (unsigned i = 0; i < wl1822_stepcount; i++) {
+ DBG("step " << i << " start");
+ for (n = 0; n < thrcount; n++) {
+ Thr& thr = *thrlist[n];
+ Runstep runstep = wl1822_step[i][n];
+ if (runstep != 0)
+ thr.start(runstep);
+ }
+ for (n = 0; n < thrcount; n++) {
+ Thr& thr = *thrlist[n];
+ Runstep runstep = wl1822_step[i][n];
+ if (runstep != 0)
+ thr.stopped();
+ }
+ }
+ // delete threads
+ for (n = 0; n < thrcount; n++) {
+ Thr& thr = *thrlist[n];
+ thr.exit();
+ thr.join();
+ delete &thr;
+ }
+ return 0;
+}
+
+NDB_COMMAND(testOdbcDriver, "testDeadlock", "testDeadlock", "testDeadlock", 65535)
+{
+ ndb_init();
+ if (ndbout_mutex == NULL)
+ ndbout_mutex= NdbMutex_Create();
+ while (++argv, --argc > 0) {
+ const char* arg = argv[0];
+ if (strcmp(arg, "-scan") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_scan = strdup(argv[0]);
+ continue;
+ }
+ }
+ printusage();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ g_cluster_connection= &con;
+
+ if (
+ strchr(g_opt.m_scan, 't') != 0 && wl1822_main('t') == -1 ||
+ strchr(g_opt.m_scan, 'x') != 0 && wl1822_main('x') == -1
+ ) {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
+// vim: set sw=2 et:
diff --git a/storage/ndb/test/ndbapi/testDict.cpp b/storage/ndb/test/ndbapi/testDict.cpp
new file mode 100644
index 00000000000..5240735dcc6
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testDict.cpp
@@ -0,0 +1,1680 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <Vector.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+#include <../../include/kernel/ndb_limits.h>
+#include <random.h>
+#include <NdbAutoPtr.hpp>
+
+#define CHECK(b) if (!(b)) { \
+ g_err << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ break; }
+
+#define CHECK2(b, c) if (!(b)) { \
+ g_err << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << ": " << c << endl; \
+ result = NDBT_FAILED; \
+ goto end; }
+
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(pNdb, records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+
+int runCreateInvalidTables(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+
+ char failTabName[256];
+
+ for (int i = 0; i < 10; i++){
+ BaseString::snprintf(failTabName, 256, "F%d", i);
+
+ const NdbDictionary::Table* pFailTab = NDBT_Tables::getTable(failTabName);
+ if (pFailTab != NULL){
+ ndbout << "|- " << failTabName << endl;
+
+ // Try to create table in db
+ if (pFailTab->createTableInDb(pNdb) == 0){
+ ndbout << failTabName << " created, this was not expected"<< endl;
+ result = NDBT_FAILED;
+ }
+
+ // Verify that table is not in db
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, failTabName) ;
+ if (pTab2 != NULL){
+ ndbout << failTabName << " was found in DB, this was not expected"<< endl;
+ result = NDBT_FAILED;
+ if (pFailTab->equal(*pTab2) == true){
+ ndbout << "It was equal" << endl;
+ } else {
+ ndbout << "It was not equal" << endl;
+ }
+ int records = 1000;
+ HugoTransactions hugoTrans(*pTab2);
+ if (hugoTrans.loadTable(pNdb, records) != 0){
+ ndbout << "It can NOT be loaded" << endl;
+ } else{
+ ndbout << "It can be loaded" << endl;
+
+ UtilTransactions utilTrans(*pTab2);
+ if (utilTrans.clearTable(pNdb, records, 64) != 0){
+ ndbout << "It can NOT be cleared" << endl;
+ } else{
+ ndbout << "It can be cleared" << endl;
+ }
+ }
+
+ if (pNdb->getDictionary()->dropTable(pTab2->getName()) == -1){
+ ndbout << "It can NOT be dropped" << endl;
+ } else {
+ ndbout << "It can be dropped" << endl;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+int runCreateTheTable(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ // Try to create table in db
+ if (pTab->createTableInDb(pNdb) != 0){
+ return NDBT_FAILED;
+ }
+
+ // Verify that table is in db
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab2 == NULL){
+ ndbout << pTab->getName() << " was not found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+ ctx->setTab(pTab2);
+
+ return NDBT_OK;
+}
+
+int runDropTheTable(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ // Try to create table in db
+ pNdb->getDictionary()->dropTable(pTab->getName());
+
+ return NDBT_OK;
+}
+
+int runCreateTableWhenDbIsFull(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ const char* tabName = "TRANSACTION"; //Use a util table
+
+ const NdbDictionary::Table* pTab = NDBT_Tables::getTable(tabName);
+ if (pTab != NULL){
+ ndbout << "|- " << tabName << endl;
+
+ // Verify that table is not in db
+ if (NDBT_Table::discoverTableFromDb(pNdb, tabName) != NULL){
+ ndbout << tabName << " was found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+
+ // Try to create table in db
+ if (pTab->createTableInDb(pNdb) == 0){
+ result = NDBT_FAILED;
+ }
+
+ // Verify that table is in db
+ if (NDBT_Table::discoverTableFromDb(pNdb, tabName) != NULL){
+ ndbout << tabName << " was found in DB"<< endl;
+ result = NDBT_FAILED;
+ }
+ }
+
+ return result;
+}
+
+int runDropTableWhenDbIsFull(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ const char* tabName = "TRANSACTION"; //Use a util table
+
+ const NdbDictionary::Table* pTab = NDBT_Table::discoverTableFromDb(pNdb, tabName);
+ if (pTab != NULL){
+ ndbout << "|- TRANSACTION" << endl;
+
+ // Try to drop table in db
+ if (pNdb->getDictionary()->dropTable(pTab->getName()) == -1){
+ result = NDBT_FAILED;
+ }
+
+ // Verify that table is not in db
+ if (NDBT_Table::discoverTableFromDb(pNdb, tabName) != NULL){
+ ndbout << tabName << " was found in DB"<< endl;
+ result = NDBT_FAILED;
+ }
+ }
+
+ return result;
+
+}
+
+
+int runCreateAndDrop(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int loops = ctx->getNumLoops();
+ int i = 0;
+
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ ndbout << "|- " << pTab->getName() << endl;
+
+ while (i < loops){
+
+ ndbout << i << ": ";
+ // Try to create table in db
+ if (pTab->createTableInDb(pNdb) != 0){
+ return NDBT_FAILED;
+ }
+
+ // Verify that table is in db
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab2 == NULL){
+ ndbout << pTab->getName() << " was not found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+
+ if (pNdb->getDictionary()->dropTable(pTab2->getName())){
+ ndbout << "Failed to drop "<<pTab2->getName()<<" in db" << endl;
+ return NDBT_FAILED;
+ }
+
+ // Verify that table is not in db
+ const NdbDictionary::Table* pTab3 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab3 != NULL){
+ ndbout << pTab3->getName() << " was found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+
+ return NDBT_OK;
+}
+
+int runCreateAndDropWithData(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int i = 0;
+
+ NdbRestarter restarter;
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ if(restarter.dumpStateAllNodes(&val, 1) != 0){
+ int result;
+ do { CHECK(0); } while (0);
+ g_err << "Unable to change timebetween LCP" << endl;
+ return NDBT_FAILED;
+ }
+
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ ndbout << "|- " << pTab->getName() << endl;
+
+ while (i < loops){
+ ndbout << i << ": ";
+ // Try to create table in db
+ if (pTab->createTableInDb(pNdb) != 0){
+ return NDBT_FAILED;
+ }
+
+ // Verify that table is in db
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab2 == NULL){
+ ndbout << pTab->getName() << " was not found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+
+ HugoTransactions hugoTrans(*pTab2);
+ if (hugoTrans.loadTable(pNdb, records) != 0){
+ return NDBT_FAILED;
+ }
+
+ int count = 0;
+ UtilTransactions utilTrans(*pTab2);
+ if (utilTrans.selectCount(pNdb, 64, &count) != 0){
+ return NDBT_FAILED;
+ }
+ if (count != records){
+ ndbout << count <<" != "<<records << endl;
+ return NDBT_FAILED;
+ }
+
+ if (pNdb->getDictionary()->dropTable(pTab2->getName()) != 0){
+ ndbout << "Failed to drop "<<pTab2->getName()<<" in db" << endl;
+ return NDBT_FAILED;
+ }
+
+ // Verify that table is not in db
+ const NdbDictionary::Table* pTab3 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab3 != NULL){
+ ndbout << pTab3->getName() << " was found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+
+
+ i++;
+ }
+
+ return NDBT_OK;
+}
+
+int runFillTable(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.fillTable(pNdb) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable(pNdb, records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runCreateAndDropDuring(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int i = 0;
+
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ ndbout << "|- " << pTab->getName() << endl;
+
+ while (i < loops && result == NDBT_OK){
+ ndbout << i << ": " << endl;
+ // Try to create table in db
+
+ Ndb* pNdb = GETNDB(step);
+ g_debug << "Creating table" << endl;
+
+ if (pTab->createTableInDb(pNdb) != 0){
+ g_err << "createTableInDb failed" << endl;
+ result = NDBT_FAILED;
+ continue;
+ }
+
+ g_debug << "Verifying creation of table" << endl;
+
+ // Verify that table is in db
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab2 == NULL){
+ g_err << pTab->getName() << " was not found in DB"<< endl;
+ result = NDBT_FAILED;
+ continue;
+ }
+
+ NdbSleep_MilliSleep(3000);
+
+ g_debug << "Dropping table" << endl;
+
+
+ if (pNdb->getDictionary()->dropTable(pTab2->getName()) != 0){
+ g_err << "Failed to drop "<<pTab2->getName()<<" in db" << endl;
+ result = NDBT_FAILED;
+ continue;
+ }
+
+ g_debug << "Verifying dropping of table" << endl;
+
+ // Verify that table is not in db
+ const NdbDictionary::Table* pTab3 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab3 != NULL){
+ g_err << pTab3->getName() << " was found in DB"<< endl;
+ result = NDBT_FAILED;
+ continue;
+ }
+ i++;
+ }
+ ctx->stopTest();
+
+ return result;
+}
+
+
+int runUseTableUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ while (ctx->isTestStopped() == false) {
+ // g_info << i++ << ": ";
+
+
+ // Delete and recreate Ndb object
+ // Otherwise you always get Invalid Schema Version
+ // It would be a nice feature to remove this two lines
+ //step->tearDown();
+ //step->setUp();
+
+ Ndb* pNdb = GETNDB(step);
+
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab2 == NULL)
+ continue;
+
+ int res;
+ HugoTransactions hugoTrans(*pTab2);
+ if ((res = hugoTrans.loadTable(pNdb, records)) != 0){
+ NdbError err = pNdb->getNdbError(res);
+ if(err.classification == NdbError::SchemaError){
+ pNdb->getDictionary()->invalidateTable(pTab->getName());
+ }
+ continue;
+ }
+
+ UtilTransactions utilTrans(*pTab2);
+ if ((res = utilTrans.clearTable(pNdb, records)) != 0){
+ NdbError err = pNdb->getNdbError(res);
+ if(err.classification == NdbError::SchemaError){
+ pNdb->getDictionary()->invalidateTable(pTab->getName());
+ }
+ continue;
+ }
+ }
+ g_info << endl;
+ return NDBT_OK;
+}
+
+
+int runCreateMaxTables(NDBT_Context* ctx, NDBT_Step* step){
+ int failures = 0;
+ char tabName[256];
+ int numTables = ctx->getProperty("tables", 1000);
+ Ndb* pNdb = GETNDB(step);
+
+ for (int i = 0; i < numTables && failures < 5; i++){
+ BaseString::snprintf(tabName, 256, "MAXTAB%d", i);
+
+ if (pNdb->waitUntilReady(30) != 0){
+ // Db is not ready, return with failure
+ return NDBT_FAILED;
+ }
+
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ ndbout << "|- " << tabName << endl;
+
+ // Set new name for T1
+ NdbDictionary::Table newTab(* pTab);
+ newTab.setName(tabName);
+
+ // Try to create table in db
+ if (newTab.createTableInDb(pNdb) != 0){
+ ndbout << tabName << " coult not be created"<< endl;
+ failures++;
+ continue;
+ }
+
+ // Verify that table exists in db
+ const NdbDictionary::Table* pTab3 =
+ NDBT_Table::discoverTableFromDb(pNdb, tabName) ;
+ if (pTab3 == NULL){
+ ndbout << tabName << " was not found in DB"<< endl;
+ failures++;
+ continue;
+ }
+
+ if (pTab->equal(*pTab3) == false){
+ ndbout << "It was not equal" << endl;
+ failures++;
+ }
+
+ int records = 1000;
+ HugoTransactions hugoTrans(*pTab3);
+ if (hugoTrans.loadTable(pNdb, records) != 0){
+ ndbout << "It can NOT be loaded" << endl;
+ } else{
+ ndbout << "It can be loaded" << endl;
+
+ UtilTransactions utilTrans(*pTab3);
+ if (utilTrans.clearTable(pNdb, records, 64) != 0){
+ ndbout << "It can NOT be cleared" << endl;
+ } else{
+ ndbout << "It can be cleared" << endl;
+ }
+ }
+
+ }
+ if (pNdb->waitUntilReady(30) != 0){
+ // Db is not ready, return with failure
+ return NDBT_FAILED;
+ }
+ // HURRAAA!
+ return NDBT_OK;
+}
+
+int runDropMaxTables(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ char tabName[256];
+ int numTables = ctx->getProperty("tables", 1000);
+ Ndb* pNdb = GETNDB(step);
+
+ for (int i = 0; i < numTables; i++){
+ BaseString::snprintf(tabName, 256, "MAXTAB%d", i);
+
+ if (pNdb->waitUntilReady(30) != 0){
+ // Db is not ready, return with failure
+ return NDBT_FAILED;
+ }
+
+ // Verify that table exists in db
+ const NdbDictionary::Table* pTab3 =
+ NDBT_Table::discoverTableFromDb(pNdb, tabName) ;
+ if (pTab3 == NULL){
+ ndbout << tabName << " was not found in DB"<< endl;
+ continue;
+ }
+
+
+ // Try to drop table in db
+ if (pNdb->getDictionary()->dropTable(pTab3->getName()) != 0){
+ ndbout << tabName << " coult not be dropped"<< endl;
+ result = NDBT_FAILED;
+ }
+
+ }
+ return result;
+}
+
+int runTestFragmentTypes(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int fragTtype = ctx->getProperty("FragmentType");
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ NdbRestarter restarter;
+
+ if (pNdb->waitUntilReady(30) != 0){
+ // Db is not ready, return with failure
+ return NDBT_FAILED;
+ }
+
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ pNdb->getDictionary()->dropTable(pTab->getName());
+
+ NdbDictionary::Table newTab(* pTab);
+ // Set fragment type for table
+ newTab.setFragmentType((NdbDictionary::Object::FragmentType)fragTtype);
+
+ // Try to create table in db
+ if (newTab.createTableInDb(pNdb) != 0){
+ ndbout << newTab.getName() << " could not be created"
+ << ", fragmentType = "<<fragTtype <<endl;
+ ndbout << pNdb->getDictionary()->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+
+ // Verify that table exists in db
+ const NdbDictionary::Table* pTab3 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName()) ;
+ if (pTab3 == NULL){
+ ndbout << pTab->getName() << " was not found in DB"<< endl;
+ return NDBT_FAILED;
+
+ }
+
+ if (pTab3->getFragmentType() != fragTtype){
+ ndbout << pTab->getName() << " fragmentType error "<< endl;
+ result = NDBT_FAILED;
+ goto drop_the_tab;
+ }
+/**
+ This test does not work since fragmentation is
+ decided by the kernel, hence the fragementation
+ attribute on the column will differ
+
+ if (newTab.equal(*pTab3) == false){
+ ndbout << "It was not equal" << endl;
+ result = NDBT_FAILED;
+ goto drop_the_tab;
+ }
+*/
+ do {
+
+ HugoTransactions hugoTrans(*pTab3);
+ UtilTransactions utilTrans(*pTab3);
+ int count;
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == (records/2));
+
+ // restart all
+ ndbout << "Restarting cluster" << endl;
+ CHECK(restarter.restartAll() == 0);
+ int timeout = 120;
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ // Verify content
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == (records/2));
+
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records, 64) == 0);
+
+ } while(false);
+
+ drop_the_tab:
+
+ // Try to drop table in db
+ if (pNdb->getDictionary()->dropTable(pTab3->getName()) != 0){
+ ndbout << pTab3->getName() << " could not be dropped"<< endl;
+ result = NDBT_FAILED;
+ }
+
+ return result;
+}
+
+
+int runTestTemporaryTables(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ Ndb* pNdb = GETNDB(step);
+ int i = 0;
+ NdbRestarter restarter;
+
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ ndbout << "|- " << pTab->getName() << endl;
+
+ NdbDictionary::Table newTab(* pTab);
+ // Set table as temporary
+ newTab.setStoredTable(false);
+
+ // Try to create table in db
+ if (newTab.createTableInDb(pNdb) != 0){
+ return NDBT_FAILED;
+ }
+
+ // Verify that table is in db
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab2 == NULL){
+ ndbout << pTab->getName() << " was not found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+
+ if (pTab2->getStoredTable() != false){
+ ndbout << pTab->getName() << " was not temporary in DB"<< endl;
+ result = NDBT_FAILED;
+ goto drop_the_tab;
+ }
+
+
+ while (i < loops && result == NDBT_OK){
+ ndbout << i << ": ";
+
+ HugoTransactions hugoTrans(*pTab2);
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+
+ int count = 0;
+ UtilTransactions utilTrans(*pTab2);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+
+ // restart all
+ ndbout << "Restarting cluster" << endl;
+ CHECK(restarter.restartAll() == 0);
+ int timeout = 120;
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == 0);
+
+ i++;
+ }
+
+ drop_the_tab:
+
+
+ if (pNdb->getDictionary()->dropTable(pTab2->getName()) != 0){
+ ndbout << "Failed to drop "<<pTab2->getName()<<" in db" << endl;
+ result = NDBT_FAILED;
+ }
+
+ // Verify that table is not in db
+ const NdbDictionary::Table* pTab3 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab3 != NULL){
+ ndbout << pTab3->getName() << " was found in DB"<< endl;
+ result = NDBT_FAILED;
+ }
+
+ return result;
+}
+
+int runPkSizes(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ char tabName[256];
+ int minPkSize = 1;
+ ndbout << "minPkSize=" <<minPkSize<<endl;
+ int maxPkSize = MAX_KEY_SIZE_IN_WORDS * 4;
+ ndbout << "maxPkSize=" <<maxPkSize<<endl;
+ Ndb* pNdb = GETNDB(step);
+ int numRecords = ctx->getNumRecords();
+
+ for (int i = minPkSize; i < maxPkSize; i++){
+ BaseString::snprintf(tabName, 256, "TPK_%d", i);
+
+ int records = numRecords;
+ int max = ~0;
+ // Limit num records for small PKs
+ if (i == 1)
+ max = 99;
+ if (i == 2)
+ max = 999;
+ if (i == 3)
+ max = 9999;
+ if (records > max)
+ records = max;
+ ndbout << "records =" << records << endl;
+
+ if (pNdb->waitUntilReady(30) != 0){
+ // Db is not ready, return with failure
+ return NDBT_FAILED;
+ }
+
+ ndbout << "|- " << tabName << endl;
+
+ if (NDBT_Tables::createTable(pNdb, tabName) != 0){
+ ndbout << tabName << " could not be created"<< endl;
+ return NDBT_FAILED;
+ }
+
+ // Verify that table exists in db
+ const NdbDictionary::Table* pTab3 =
+ NDBT_Table::discoverTableFromDb(pNdb, tabName) ;
+ if (pTab3 == NULL){
+ g_err << tabName << " was not found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+
+ // ndbout << *pTab3 << endl;
+
+ if (pTab3->equal(*NDBT_Tables::getTable(tabName)) == false){
+ g_err << "It was not equal" << endl;
+ return NDBT_FAILED;
+ }
+
+ do {
+ // Do it all
+ HugoTransactions hugoTrans(*pTab3);
+ UtilTransactions utilTrans(*pTab3);
+ int count;
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == (records/2));
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+
+#if 0
+ // Fill table
+ CHECK(hugoTrans.fillTable(pNdb) == 0);
+ CHECK(utilTrans.clearTable2(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == 0);
+#endif
+ } while(false);
+
+ // Drop table
+ if (pNdb->getDictionary()->dropTable(pTab3->getName()) != 0){
+ ndbout << "Failed to drop "<<pTab3->getName()<<" in db" << endl;
+ return NDBT_FAILED;
+ }
+ }
+ return result;
+}
+
+int runStoreFrm(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+
+ for (int l = 0; l < loops && result == NDBT_OK ; l++){
+
+ Uint32 dataLen = (Uint32)myRandom48(MAX_FRM_DATA_SIZE);
+ // size_t dataLen = 10;
+ unsigned char data[MAX_FRM_DATA_SIZE];
+
+ char start = l + 248;
+ for(Uint32 i = 0; i < dataLen; i++){
+ data[i] = start;
+ start++;
+ }
+#if 0
+ ndbout << "dataLen="<<dataLen<<endl;
+ for (Uint32 i = 0; i < dataLen; i++){
+ unsigned char c = data[i];
+ ndbout << hex << c << ", ";
+ }
+ ndbout << endl;
+#endif
+
+ NdbDictionary::Table newTab(* pTab);
+ void* pData = &data;
+ newTab.setFrm(pData, dataLen);
+
+ // Try to create table in db
+ if (newTab.createTableInDb(pNdb) != 0){
+ result = NDBT_FAILED;
+ continue;
+ }
+
+ // Verify that table is in db
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab2 == NULL){
+ g_err << pTab->getName() << " was not found in DB"<< endl;
+ result = NDBT_FAILED;
+ continue;
+ }
+
+ const void* pData2 = pTab2->getFrmData();
+ Uint32 resultLen = pTab2->getFrmLength();
+ if (dataLen != resultLen){
+ g_err << "Length of data failure" << endl
+ << " expected = " << dataLen << endl
+ << " got = " << resultLen << endl;
+ result = NDBT_FAILED;
+ }
+
+ // Verfiy the frm data
+ if (memcmp(pData, pData2, resultLen) != 0){
+ g_err << "Wrong data recieved" << endl;
+ for (size_t i = 0; i < dataLen; i++){
+ unsigned char c = ((unsigned char*)pData2)[i];
+ g_err << hex << c << ", ";
+ }
+ g_err << endl;
+ result = NDBT_FAILED;
+ }
+
+ if (pNdb->getDictionary()->dropTable(pTab2->getName()) != 0){
+ g_err << "It can NOT be dropped" << endl;
+ result = NDBT_FAILED;
+ }
+ }
+
+ return result;
+}
+
+int runStoreFrmError(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+
+ for (int l = 0; l < loops && result == NDBT_OK ; l++){
+
+ const Uint32 dataLen = MAX_FRM_DATA_SIZE + 10;
+ unsigned char data[dataLen];
+
+ char start = l + 248;
+ for(Uint32 i = 0; i < dataLen; i++){
+ data[i] = start;
+ start++;
+ }
+#if 0
+ ndbout << "dataLen="<<dataLen<<endl;
+ for (Uint32 i = 0; i < dataLen; i++){
+ unsigned char c = data[i];
+ ndbout << hex << c << ", ";
+ }
+ ndbout << endl;
+#endif
+
+ NdbDictionary::Table newTab(* pTab);
+
+ void* pData = &data;
+ newTab.setFrm(pData, dataLen);
+
+ // Try to create table in db
+ if (newTab.createTableInDb(pNdb) == 0){
+ result = NDBT_FAILED;
+ continue;
+ }
+
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab2 != NULL){
+ g_err << pTab->getName() << " was found in DB"<< endl;
+ result = NDBT_FAILED;
+ if (pNdb->getDictionary()->dropTable(pTab2->getName()) != 0){
+ g_err << "It can NOT be dropped" << endl;
+ result = NDBT_FAILED;
+ }
+
+ continue;
+ }
+
+ }
+
+ return result;
+}
+
+int verifyTablesAreEqual(const NdbDictionary::Table* pTab, const NdbDictionary::Table* pTab2){
+ // Verify that getPrimaryKey only returned true for primary keys
+ for (int i = 0; i < pTab2->getNoOfColumns(); i++){
+ const NdbDictionary::Column* col = pTab->getColumn(i);
+ const NdbDictionary::Column* col2 = pTab2->getColumn(i);
+ if (col->getPrimaryKey() != col2->getPrimaryKey()){
+ g_err << "col->getPrimaryKey() != col2->getPrimaryKey()" << endl;
+ return NDBT_FAILED;
+ }
+ }
+
+ if (!pTab->equal(*pTab2)){
+ g_err << "equal failed" << endl;
+ g_info << *pTab;
+ g_info << *pTab2;
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runGetPrimaryKey(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ ndbout << "|- " << pTab->getName() << endl;
+ g_info << *pTab;
+ // Try to create table in db
+ if (pTab->createTableInDb(pNdb) != 0){
+ return NDBT_FAILED;
+ }
+
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab2 == NULL){
+ ndbout << pTab->getName() << " was not found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+
+ int result = NDBT_OK;
+ if (verifyTablesAreEqual(pTab, pTab2) != NDBT_OK)
+ result = NDBT_FAILED;
+
+
+#if 0
+ // Create an index on the table and see what
+ // the function returns now
+ char name[200];
+ sprintf(name, "%s_X007", pTab->getName());
+ NDBT_Index* pInd = new NDBT_Index(name);
+ pInd->setTable(pTab->getName());
+ pInd->setType(NdbDictionary::Index::UniqueHashIndex);
+ // pInd->setLogging(false);
+ for (int i = 0; i < 2; i++){
+ const NDBT_Attribute* pAttr = pTab->getAttribute(i);
+ pInd->addAttribute(*pAttr);
+ }
+ g_info << "Create index:" << endl << *pInd;
+ if (pInd->createIndexInDb(pNdb, false) != 0){
+ result = NDBT_FAILED;
+ }
+ delete pInd;
+
+ const NdbDictionary::Table* pTab3 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab3 == NULL){
+ ndbout << pTab->getName() << " was not found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+
+ if (verifyTablesAreEqual(pTab, pTab3) != NDBT_OK)
+ result = NDBT_FAILED;
+ if (verifyTablesAreEqual(pTab2, pTab3) != NDBT_OK)
+ result = NDBT_FAILED;
+#endif
+
+#if 0
+ if (pTab2->getDictionary()->dropTable(pNdb) != 0){
+ ndbout << "Failed to drop "<<pTab2->getName()<<" in db" << endl;
+ return NDBT_FAILED;
+ }
+
+ // Verify that table is not in db
+ const NdbDictionary::Table* pTab4 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab4 != NULL){
+ ndbout << pTab4->getName() << " was found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+#endif
+
+ return result;
+}
+
+struct ErrorCodes { int error_id; bool crash;};
+ErrorCodes
+NF_codes[] = {
+ {6003, true},
+ {6004, true},
+ //,6005, true,
+ {7173, false}
+};
+
+int
+runNF1(NDBT_Context* ctx, NDBT_Step* step){
+ NdbRestarter restarter;
+ if(restarter.getNumDbNodes() < 2)
+ return NDBT_OK;
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+
+ Ndb* pNdb = GETNDB(step);
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ NdbDictionary::Dictionary* dict = pNdb->getDictionary();
+ dict->dropTable(pTab->getName());
+
+ int result = NDBT_OK;
+
+ /**
+ * Need to run LCP at high rate otherwise
+ * packed replicas become "to many"
+ */
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ if(restarter.dumpStateAllNodes(&val, 1) != 0){
+ do { CHECK(0); } while(0);
+ g_err << "Failed to set LCP to min value" << endl;
+ return NDBT_FAILED;
+ }
+
+ const int loops = ctx->getNumLoops();
+ for (int l = 0; l < loops && result == NDBT_OK ; l++){
+ const int sz = sizeof(NF_codes)/sizeof(NF_codes[0]);
+ for(int i = 0; i<sz; i++){
+ int rand = myRandom48(restarter.getNumDbNodes());
+ int nodeId = restarter.getRandomNotMasterNodeId(rand);
+ struct ErrorCodes err_struct = NF_codes[i];
+ int error = err_struct.error_id;
+ bool crash = err_struct.crash;
+
+ g_info << "NF1: node = " << nodeId << " error code = " << error << endl;
+
+ int val2[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 3};
+
+ CHECK2(restarter.dumpStateOneNode(nodeId, val2, 2) == 0,
+ "failed to set RestartOnErrorInsert");
+
+ CHECK2(restarter.insertErrorInNode(nodeId, error) == 0,
+ "failed to set error insert");
+
+ CHECK2(dict->createTable(* pTab) == 0,
+ "failed to create table");
+
+ if (crash) {
+ CHECK2(restarter.waitNodesNoStart(&nodeId, 1) == 0,
+ "waitNodesNoStart failed");
+
+ if(myRandom48(100) > 50){
+ CHECK2(restarter.startNodes(&nodeId, 1) == 0,
+ "failed to start node");
+
+ CHECK2(restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+
+ CHECK2(dict->dropTable(pTab->getName()) == 0,
+ "drop table failed");
+ } else {
+ CHECK2(dict->dropTable(pTab->getName()) == 0,
+ "drop table failed");
+
+ CHECK2(restarter.startNodes(&nodeId, 1) == 0,
+ "failed to start node");
+
+ CHECK2(restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+ }
+
+ CHECK2(restarter.dumpStateOneNode(nodeId, &val, 1) == 0,
+ "Failed to set LCP to min value");
+ }
+ }
+ }
+ end:
+ dict->dropTable(pTab->getName());
+
+ return result;
+}
+
+#define APIERROR(error) \
+ { g_err << "Error in " << __FILE__ << ", line:" << __LINE__ << ", code:" \
+ << error.code << ", msg: " << error.message << "." << endl; \
+ }
+
+int
+runCreateAutoincrementTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ Uint32 startvalues[5] = {256-2, 0, 256*256-2, ~0, 256*256*256-2};
+
+ int ret = NDBT_OK;
+
+ for (int jj = 0; jj < 5 && ret == NDBT_OK; jj++) {
+ char tabname[] = "AUTOINCTAB";
+ Uint32 startvalue = startvalues[jj];
+
+ NdbDictionary::Table myTable;
+ NdbDictionary::Column myColumn;
+
+ Ndb* myNdb = GETNDB(step);
+ NdbDictionary::Dictionary* myDict = myNdb->getDictionary();
+
+
+ if (myDict->getTable(tabname) != NULL) {
+ g_err << "NDB already has example table: " << tabname << endl;
+ APIERROR(myNdb->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ myTable.setName(tabname);
+
+ myColumn.setName("ATTR1");
+ myColumn.setType(NdbDictionary::Column::Unsigned);
+ myColumn.setLength(1);
+ myColumn.setPrimaryKey(true);
+ myColumn.setNullable(false);
+ myColumn.setAutoIncrement(true);
+ if (startvalue != ~0) // check that default value starts with 1
+ myColumn.setAutoIncrementInitialValue(startvalue);
+ myTable.addColumn(myColumn);
+
+ if (myDict->createTable(myTable) == -1) {
+ g_err << "Failed to create table " << tabname << endl;
+ APIERROR(myNdb->getNdbError());
+ return NDBT_FAILED;
+ }
+
+
+ if (startvalue == ~0) // check that default value starts with 1
+ startvalue = 1;
+
+ for (int i = 0; i < 16; i++) {
+
+ Uint64 value = myNdb->getAutoIncrementValue(tabname, 1);
+
+ if (value != (startvalue+i)) {
+ g_err << "value = " << value << " expected " << startvalue+i << endl;;
+ APIERROR(myNdb->getNdbError());
+ // ret = NDBT_FAILED;
+ // break;
+ }
+ }
+
+ if (myDict->dropTable(tabname) == -1) {
+ g_err << "Failed to drop table " << tabname << endl;
+ APIERROR(myNdb->getNdbError());
+ ret = NDBT_FAILED;
+ }
+ }
+
+ return ret;
+}
+
+int
+runTableRename(NDBT_Context* ctx, NDBT_Step* step){
+
+ int result = NDBT_OK;
+
+ Ndb* pNdb = GETNDB(step);
+ NdbDictionary::Dictionary* dict = pNdb->getDictionary();
+ int records = ctx->getNumRecords();
+ const int loops = ctx->getNumLoops();
+
+ ndbout << "|- " << ctx->getTab()->getName() << endl;
+
+ for (int l = 0; l < loops && result == NDBT_OK ; l++){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ // Try to create table in db
+ if (pTab->createTableInDb(pNdb) != 0){
+ return NDBT_FAILED;
+ }
+
+ // Verify that table is in db
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab2 == NULL){
+ ndbout << pTab->getName() << " was not found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+ ctx->setTab(pTab2);
+
+ // Load table
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(pNdb, records) != 0){
+ return NDBT_FAILED;
+ }
+
+ // Rename table
+ BaseString pTabName(pTab->getName());
+ BaseString pTabNewName(pTabName);
+ pTabNewName.append("xx");
+
+ const NdbDictionary::Table * oldTable = dict->getTable(pTabName.c_str());
+ if (oldTable) {
+ NdbDictionary::Table newTable = *oldTable;
+ newTable.setName(pTabNewName.c_str());
+ CHECK2(dict->alterTable(newTable) == 0,
+ "TableRename failed");
+ }
+ else {
+ result = NDBT_FAILED;
+ }
+
+ // Verify table contents
+ NdbDictionary::Table pNewTab(pTabNewName.c_str());
+
+ UtilTransactions utilTrans(pNewTab);
+ if (utilTrans.clearTable(pNdb, records) != 0){
+ continue;
+ }
+
+ // Drop table
+ dict->dropTable(pNewTab.getName());
+ }
+ end:
+
+ return result;
+}
+
+int
+runTableRenameNF(NDBT_Context* ctx, NDBT_Step* step){
+ NdbRestarter restarter;
+ if(restarter.getNumDbNodes() < 2)
+ return NDBT_OK;
+
+ int result = NDBT_OK;
+
+ Ndb* pNdb = GETNDB(step);
+ NdbDictionary::Dictionary* dict = pNdb->getDictionary();
+ int records = ctx->getNumRecords();
+ const int loops = ctx->getNumLoops();
+
+ ndbout << "|- " << ctx->getTab()->getName() << endl;
+
+ for (int l = 0; l < loops && result == NDBT_OK ; l++){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ // Try to create table in db
+ if (pTab->createTableInDb(pNdb) != 0){
+ return NDBT_FAILED;
+ }
+
+ // Verify that table is in db
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab2 == NULL){
+ ndbout << pTab->getName() << " was not found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+ ctx->setTab(pTab2);
+
+ // Load table
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(pNdb, records) != 0){
+ return NDBT_FAILED;
+ }
+
+ BaseString pTabName(pTab->getName());
+ BaseString pTabNewName(pTabName);
+ pTabNewName.append("xx");
+
+ const NdbDictionary::Table * oldTable = dict->getTable(pTabName.c_str());
+ if (oldTable) {
+ NdbDictionary::Table newTable = *oldTable;
+ newTable.setName(pTabNewName.c_str());
+ CHECK2(dict->alterTable(newTable) == 0,
+ "TableRename failed");
+ }
+ else {
+ result = NDBT_FAILED;
+ }
+
+ // Restart one node at a time
+
+ /**
+ * Need to run LCP at high rate otherwise
+ * packed replicas become "to many"
+ */
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ if(restarter.dumpStateAllNodes(&val, 1) != 0){
+ do { CHECK(0); } while(0);
+ g_err << "Failed to set LCP to min value" << endl;
+ return NDBT_FAILED;
+ }
+
+ const int numNodes = restarter.getNumDbNodes();
+ for(int i = 0; i<numNodes; i++){
+ int nodeId = restarter.getDbNodeId(i);
+ int error = NF_codes[i].error_id;
+
+ g_info << "NF1: node = " << nodeId << " error code = " << error << endl;
+
+ CHECK2(restarter.restartOneDbNode(nodeId) == 0,
+ "failed to set restartOneDbNode");
+
+ CHECK2(restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+
+ }
+
+ // Verify table contents
+ NdbDictionary::Table pNewTab(pTabNewName.c_str());
+
+ UtilTransactions utilTrans(pNewTab);
+ if (utilTrans.clearTable(pNdb, records) != 0){
+ continue;
+ }
+
+ // Drop table
+ dict->dropTable(pTabNewName.c_str());
+ }
+ end:
+ return result;
+}
+
+int
+runTableRenameSR(NDBT_Context* ctx, NDBT_Step* step){
+ NdbRestarter restarter;
+ if(restarter.getNumDbNodes() < 2)
+ return NDBT_OK;
+
+ int result = NDBT_OK;
+
+ Ndb* pNdb = GETNDB(step);
+ NdbDictionary::Dictionary* dict = pNdb->getDictionary();
+ int records = ctx->getNumRecords();
+ const int loops = ctx->getNumLoops();
+
+ ndbout << "|- " << ctx->getTab()->getName() << endl;
+
+ for (int l = 0; l < loops && result == NDBT_OK ; l++){
+ // Rename table
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ // Try to create table in db
+ if (pTab->createTableInDb(pNdb) != 0){
+ return NDBT_FAILED;
+ }
+
+ // Verify that table is in db
+ const NdbDictionary::Table* pTab2 =
+ NDBT_Table::discoverTableFromDb(pNdb, pTab->getName());
+ if (pTab2 == NULL){
+ ndbout << pTab->getName() << " was not found in DB"<< endl;
+ return NDBT_FAILED;
+ }
+ ctx->setTab(pTab2);
+
+ // Load table
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(pNdb, records) != 0){
+ return NDBT_FAILED;
+ }
+
+ BaseString pTabName(pTab->getName());
+ BaseString pTabNewName(pTabName);
+ pTabNewName.append("xx");
+
+ const NdbDictionary::Table * oldTable = dict->getTable(pTabName.c_str());
+ if (oldTable) {
+ NdbDictionary::Table newTable = *oldTable;
+ newTable.setName(pTabNewName.c_str());
+ CHECK2(dict->alterTable(newTable) == 0,
+ "TableRename failed");
+ }
+ else {
+ result = NDBT_FAILED;
+ }
+
+ // Restart cluster
+
+ /**
+ * Need to run LCP at high rate otherwise
+ * packed replicas become "to many"
+ */
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ if(restarter.dumpStateAllNodes(&val, 1) != 0){
+ do { CHECK(0); } while(0);
+ g_err << "Failed to set LCP to min value" << endl;
+ return NDBT_FAILED;
+ }
+
+ CHECK2(restarter.restartAll() == 0,
+ "failed to set restartOneDbNode");
+
+ CHECK2(restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+
+ // Verify table contents
+ NdbDictionary::Table pNewTab(pTabNewName.c_str());
+
+ UtilTransactions utilTrans(pNewTab);
+ if (utilTrans.clearTable(pNdb, records) != 0){
+ continue;
+ }
+
+ // Drop table
+ dict->dropTable(pTabNewName.c_str());
+ }
+ end:
+ return result;
+}
+
+static void
+f(const NdbDictionary::Column * col){
+ if(col == 0){
+ abort();
+ }
+}
+
+int
+runTestDictionaryPerf(NDBT_Context* ctx, NDBT_Step* step){
+ Vector<char*> cols;
+ Vector<const NdbDictionary::Table*> tabs;
+ int i;
+
+ Ndb* pNdb = GETNDB(step);
+
+ const Uint32 count = NDBT_Tables::getNumTables();
+ for (i=0; i < count; i++){
+ const NdbDictionary::Table * tab = NDBT_Tables::getTable(i);
+ pNdb->getDictionary()->createTable(* tab);
+
+ const NdbDictionary::Table * tab2 = pNdb->getDictionary()->getTable(tab->getName());
+
+ for(size_t j = 0; j<tab->getNoOfColumns(); j++){
+ cols.push_back((char*)tab2);
+ cols.push_back(strdup(tab->getColumn(j)->getName()));
+ }
+ }
+
+ const Uint32 times = 10000000;
+
+ ndbout_c("%d tables and %d columns",
+ NDBT_Tables::getNumTables(), cols.size()/2);
+
+ char ** tcols = cols.getBase();
+
+ srand(time(0));
+ Uint32 size = cols.size() / 2;
+ char ** columns = &cols[0];
+ Uint64 start = NdbTick_CurrentMillisecond();
+ for(i = 0; i<times; i++){
+ int j = 2 * (rand() % size);
+ const NdbDictionary::Table* tab = (const NdbDictionary::Table*)tcols[j];
+ const char * col = tcols[j+1];
+ const NdbDictionary::Column* column = tab->getColumn(col);
+ f(column);
+ }
+ Uint64 stop = NdbTick_CurrentMillisecond();
+ stop -= start;
+
+ Uint64 per = stop;
+ per *= 1000;
+ per /= times;
+
+ ndbout_c("%d random getColumn(name) in %Ld ms -> %d us/get",
+ times, stop, per);
+
+ return NDBT_OK;
+}
+
+int runFailAddFragment(NDBT_Context* ctx, NDBT_Step* step){
+ static int acclst[] = { 3001 };
+ static int tuplst[] = { 4007, 4008, 4009, 4010, 4011, 4012 };
+ static int tuxlst[] = { 12001, 12002, 12003, 12004, 12005, 12006 };
+ static unsigned acccnt = sizeof(acclst)/sizeof(acclst[0]);
+ static unsigned tupcnt = sizeof(tuplst)/sizeof(tuplst[0]);
+ static unsigned tuxcnt = sizeof(tuxlst)/sizeof(tuxlst[0]);
+
+ NdbRestarter restarter;
+ int nodeId = restarter.getMasterNodeId();
+ Ndb* pNdb = GETNDB(step);
+ NdbDictionary::Dictionary* pDic = pNdb->getDictionary();
+ NdbDictionary::Table tab(*ctx->getTab());
+ tab.setFragmentType(NdbDictionary::Object::FragAllLarge);
+
+ // ordered index on first few columns
+ NdbDictionary::Index idx("X");
+ idx.setTable(tab.getName());
+ idx.setType(NdbDictionary::Index::OrderedIndex);
+ idx.setLogging(false);
+ for (int i_hate_broken_compilers = 0;
+ i_hate_broken_compilers < 3 &&
+ i_hate_broken_compilers < tab.getNoOfColumns();
+ i_hate_broken_compilers++) {
+ idx.addColumn(*tab.getColumn(i_hate_broken_compilers));
+ }
+
+ const int loops = ctx->getNumLoops();
+ int result = NDBT_OK;
+ (void)pDic->dropTable(tab.getName());
+
+ for (int l = 0; l < loops; l++) {
+ for (unsigned i0 = 0; i0 < acccnt; i0++) {
+ unsigned j = (l == 0 ? i0 : myRandom48(acccnt));
+ int errval = acclst[j];
+ g_info << "insert error node=" << nodeId << " value=" << errval << endl;
+ CHECK2(restarter.insertErrorInNode(nodeId, errval) == 0,
+ "failed to set error insert");
+ CHECK2(pDic->createTable(tab) != 0,
+ "failed to fail after error insert " << errval);
+ CHECK2(pDic->createTable(tab) == 0,
+ pDic->getNdbError());
+ CHECK2(pDic->dropTable(tab.getName()) == 0,
+ pDic->getNdbError());
+ }
+ for (unsigned i1 = 0; i1 < tupcnt; i1++) {
+ unsigned j = (l == 0 ? i1 : myRandom48(tupcnt));
+ int errval = tuplst[j];
+ g_info << "insert error node=" << nodeId << " value=" << errval << endl;
+ CHECK2(restarter.insertErrorInNode(nodeId, errval) == 0,
+ "failed to set error insert");
+ CHECK2(pDic->createTable(tab) != 0,
+ "failed to fail after error insert " << errval);
+ CHECK2(pDic->createTable(tab) == 0,
+ pDic->getNdbError());
+ CHECK2(pDic->dropTable(tab.getName()) == 0,
+ pDic->getNdbError());
+ }
+ for (unsigned i2 = 0; i2 < tuxcnt; i2++) {
+ unsigned j = (l == 0 ? i2 : myRandom48(tuxcnt));
+ int errval = tuxlst[j];
+ g_info << "insert error node=" << nodeId << " value=" << errval << endl;
+ CHECK2(restarter.insertErrorInNode(nodeId, errval) == 0,
+ "failed to set error insert");
+ CHECK2(pDic->createTable(tab) == 0,
+ pDic->getNdbError());
+ CHECK2(pDic->createIndex(idx) != 0,
+ "failed to fail after error insert " << errval);
+ CHECK2(pDic->createIndex(idx) == 0,
+ pDic->getNdbError());
+ CHECK2(pDic->dropTable(tab.getName()) == 0,
+ pDic->getNdbError());
+ }
+ }
+end:
+ return result;
+}
+
+NDBT_TESTSUITE(testDict);
+TESTCASE("CreateAndDrop",
+ "Try to create and drop the table loop number of times\n"){
+ INITIALIZER(runCreateAndDrop);
+}
+TESTCASE("CreateAndDropWithData",
+ "Try to create and drop the table when it's filled with data\n"
+ "do this loop number of times\n"){
+ INITIALIZER(runCreateAndDropWithData);
+}
+TESTCASE("CreateAndDropDuring",
+ "Try to create and drop the table when other thread is using it\n"
+ "do this loop number of times\n"){
+ STEP(runCreateAndDropDuring);
+ STEP(runUseTableUntilStopped);
+}
+TESTCASE("CreateInvalidTables",
+ "Try to create the invalid tables we have defined\n"){
+ INITIALIZER(runCreateInvalidTables);
+}
+TESTCASE("CreateTableWhenDbIsFull",
+ "Try to create a new table when db already is full\n"){
+ INITIALIZER(runCreateTheTable);
+ INITIALIZER(runFillTable);
+ INITIALIZER(runCreateTableWhenDbIsFull);
+ INITIALIZER(runDropTableWhenDbIsFull);
+ FINALIZER(runDropTheTable);
+}
+TESTCASE("FragmentTypeSingle",
+ "Create the table with fragment type Single\n"){
+ TC_PROPERTY("FragmentType", NdbDictionary::Table::FragSingle);
+ INITIALIZER(runTestFragmentTypes);
+}
+TESTCASE("FragmentTypeAllSmall",
+ "Create the table with fragment type AllSmall\n"){
+ TC_PROPERTY("FragmentType", NdbDictionary::Table::FragAllSmall);
+ INITIALIZER(runTestFragmentTypes);
+}
+TESTCASE("FragmentTypeAllMedium",
+ "Create the table with fragment type AllMedium\n"){
+ TC_PROPERTY("FragmentType", NdbDictionary::Table::FragAllMedium);
+ INITIALIZER(runTestFragmentTypes);
+}
+TESTCASE("FragmentTypeAllLarge",
+ "Create the table with fragment type AllLarge\n"){
+ TC_PROPERTY("FragmentType", NdbDictionary::Table::FragAllLarge);
+ INITIALIZER(runTestFragmentTypes);
+}
+TESTCASE("TemporaryTables",
+ "Create the table as temporary and make sure it doesn't\n"
+ "contain any data when system is restarted\n"){
+ INITIALIZER(runTestTemporaryTables);
+}
+TESTCASE("CreateMaxTables",
+ "Create tables until db says that it can't create any more\n"){
+ TC_PROPERTY("tables", 1000);
+ INITIALIZER(runCreateMaxTables);
+ FINALIZER(runDropMaxTables);
+}
+TESTCASE("PkSizes",
+ "Create tables with all different primary key sizes.\n"\
+ "Test all data operations insert, update, delete etc.\n"\
+ "Drop table."){
+ INITIALIZER(runPkSizes);
+}
+TESTCASE("StoreFrm",
+ "Test that a frm file can be properly stored as part of the\n"
+ "data in Dict."){
+ INITIALIZER(runStoreFrm);
+}
+TESTCASE("GetPrimaryKey",
+ "Test the function NdbDictionary::Column::getPrimaryKey\n"
+ "It should return true only if the column is part of \n"
+ "the primary key in the table"){
+ INITIALIZER(runGetPrimaryKey);
+}
+TESTCASE("StoreFrmError",
+ "Test that a frm file with too long length can't be stored."){
+ INITIALIZER(runStoreFrmError);
+}
+TESTCASE("NF1",
+ "Test that create table can handle NF (not master)"){
+ INITIALIZER(runNF1);
+}
+TESTCASE("TableRename",
+ "Test basic table rename"){
+ INITIALIZER(runTableRename);
+}
+TESTCASE("TableRenameNF",
+ "Test that table rename can handle node failure"){
+ INITIALIZER(runTableRenameNF);
+}
+TESTCASE("TableRenameSR",
+ "Test that table rename can handle system restart"){
+ INITIALIZER(runTableRenameSR);
+}
+TESTCASE("DictionaryPerf",
+ ""){
+ INITIALIZER(runTestDictionaryPerf);
+}
+TESTCASE("FailAddFragment",
+ "Fail add fragment or attribute in ACC or TUP or TUX\n"){
+ INITIALIZER(runFailAddFragment);
+}
+NDBT_TESTSUITE_END(testDict);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ // Tables should not be auto created
+ testDict.setCreateTable(false);
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ return testDict.execute(argc, argv);
+}
diff --git a/storage/ndb/test/ndbapi/testGrep.cpp b/storage/ndb/test/ndbapi/testGrep.cpp
new file mode 100644
index 00000000000..713aefbeafa
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testGrep.cpp
@@ -0,0 +1,540 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbGrep.hpp>
+
+
+#define CHECK(b) if (!(b)) { \
+ g_err << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ continue; }
+
+
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runPkUpdate(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ g_info << "|- " << i << ": ";
+ if (hugoTrans.pkUpdateRecords(GETNDB(step), records, batchSize) != 0){
+ g_info << endl;
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ g_info << endl;
+ return NDBT_OK;
+}
+
+int runRestartInitial(NDBT_Context* ctx, NDBT_Step* step){
+ NdbRestarter restarter;
+
+ Ndb* pNdb = GETNDB(step);
+
+ const NdbDictionary::Table *tab = ctx->getTab();
+ pNdb->getDictionary()->dropTable(tab->getName());
+
+ if (restarter.restartAll(true) != 0)
+ return NDBT_FAILED;
+
+ return NDBT_OK;
+}
+
+int runRestarter(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ NdbRestarter restarter;
+ int i = 0;
+ int lastId = 0;
+
+ if (restarter.getNumDbNodes() < 2){
+ ctx->stopTest();
+ return NDBT_OK;
+ }
+
+ if(restarter.waitClusterStarted(60) != 0){
+ g_err << "Cluster failed to start" << endl;
+ return NDBT_FAILED;
+ }
+
+ loops *= restarter.getNumDbNodes();
+ while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){
+
+ int id = lastId % restarter.getNumDbNodes();
+ int nodeId = restarter.getDbNodeId(id);
+ ndbout << "Restart node " << nodeId << endl;
+ if(restarter.restartOneDbNode(nodeId) != 0){
+ g_err << "Failed to restartNextDbNode" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+
+ if(restarter.waitClusterStarted(60) != 0){
+ g_err << "Cluster failed to start" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+
+ NdbSleep_SecSleep(1);
+
+ lastId++;
+ i++;
+ }
+
+ ctx->stopTest();
+
+ return result;
+}
+
+int runCheckAllNodesStarted(NDBT_Context* ctx, NDBT_Step* step){
+ NdbRestarter restarter;
+
+ if(restarter.waitClusterStarted(1) != 0){
+ g_err << "All nodes was not started " << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+
+bool testMaster = true;
+bool testSlave = false;
+
+int setMaster(NDBT_Context* ctx, NDBT_Step* step){
+ testMaster = true;
+ testSlave = false;
+ return NDBT_OK;
+}
+int setMasterAsSlave(NDBT_Context* ctx, NDBT_Step* step){
+ testMaster = true;
+ testSlave = true;
+ return NDBT_OK;
+}
+int setSlave(NDBT_Context* ctx, NDBT_Step* step){
+ testMaster = false;
+ testSlave = true;
+ return NDBT_OK;
+}
+
+int runAbort(NDBT_Context* ctx, NDBT_Step* step){
+
+
+ NdbGrep grep(GETNDB(step)->getNodeId()+1);
+ NdbRestarter restarter;
+
+ if (restarter.getNumDbNodes() < 2){
+ ctx->stopTest();
+ return NDBT_OK;
+ }
+
+ if(restarter.waitClusterStarted(60) != 0){
+ g_err << "Cluster failed to start" << endl;
+ return NDBT_FAILED;
+ }
+
+ if (testMaster) {
+ if (testSlave) {
+ if (grep.NFMasterAsSlave(restarter) == -1){
+ return NDBT_FAILED;
+ }
+ } else {
+ if (grep.NFMaster(restarter) == -1){
+ return NDBT_FAILED;
+ }
+ }
+ } else {
+ if (grep.NFSlave(restarter) == -1){
+ return NDBT_FAILED;
+ }
+ }
+
+ return NDBT_OK;
+}
+
+int runFail(NDBT_Context* ctx, NDBT_Step* step){
+ NdbGrep grep(GETNDB(step)->getNodeId()+1);
+
+ NdbRestarter restarter;
+
+ if (restarter.getNumDbNodes() < 2){
+ ctx->stopTest();
+ return NDBT_OK;
+ }
+
+ if(restarter.waitClusterStarted(60) != 0){
+ g_err << "Cluster failed to start" << endl;
+ return NDBT_FAILED;
+ }
+
+ if (testMaster) {
+ if (testSlave) {
+ if (grep.FailMasterAsSlave(restarter) == -1){
+ return NDBT_FAILED;
+ }
+ } else {
+ if (grep.FailMaster(restarter) == -1){
+ return NDBT_FAILED;
+ }
+ }
+ } else {
+ if (grep.FailSlave(restarter) == -1){
+ return NDBT_FAILED;
+ }
+ }
+
+ return NDBT_OK;
+}
+
+int runGrepBasic(NDBT_Context* ctx, NDBT_Step* step){
+ NdbGrep grep(GETNDB(step)->getNodeId()+1);
+ unsigned grepId = 0;
+
+ if (grep.start() == -1){
+ return NDBT_FAILED;
+ }
+ ndbout << "Started grep " << grepId << endl;
+ ctx->setProperty("GrepId", grepId);
+
+ return NDBT_OK;
+}
+
+
+
+
+int runVerifyBasic(NDBT_Context* ctx, NDBT_Step* step){
+ NdbGrep grep(GETNDB(step)->getNodeId()+1, ctx->getRemoteMgm());
+ ndbout_c("no of nodes %d" ,grep.getNumDbNodes());
+ int result;
+ if ((result = grep.verify(ctx)) == -1){
+ return NDBT_FAILED;
+ }
+ return result;
+}
+
+
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable2(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+#include "bank/Bank.hpp"
+
+int runCreateBank(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank;
+ int overWriteExisting = true;
+ if (bank.createAndLoadBank(overWriteExisting) != NDBT_OK)
+ return NDBT_FAILED;
+ return NDBT_OK;
+}
+
+int runBankTimer(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank;
+ int wait = 30; // Max seconds between each "day"
+ int yield = 1; // Loops before bank returns
+
+ while (ctx->isTestStopped() == false) {
+ bank.performIncreaseTime(wait, yield);
+ }
+ return NDBT_OK;
+}
+
+int runBankTransactions(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank;
+ int wait = 10; // Max ms between each transaction
+ int yield = 100; // Loops before bank returns
+
+ while (ctx->isTestStopped() == false) {
+ bank.performTransactions(wait, yield);
+ }
+ return NDBT_OK;
+}
+
+int runBankGL(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank;
+ int yield = 20; // Loops before bank returns
+ int result = NDBT_OK;
+
+ while (ctx->isTestStopped() == false) {
+ if (bank.performMakeGLs(yield) != NDBT_OK){
+ ndbout << "bank.performMakeGLs FAILED" << endl;
+ result = NDBT_FAILED;
+ }
+ }
+ return NDBT_OK;
+}
+
+int runBankSum(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank;
+ int wait = 2000; // Max ms between each sum of accounts
+ int yield = 1; // Loops before bank returns
+ int result = NDBT_OK;
+
+ while (ctx->isTestStopped() == false) {
+ if (bank.performSumAccounts(wait, yield) != NDBT_OK){
+ ndbout << "bank.performSumAccounts FAILED" << endl;
+ result = NDBT_FAILED;
+ }
+ }
+ return result ;
+}
+
+int runDropBank(NDBT_Context* ctx, NDBT_Step* step){
+ Bank bank;
+ if (bank.dropBank() != NDBT_OK)
+ return NDBT_FAILED;
+ return NDBT_OK;
+}
+
+int runGrepBank(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int l = 0;
+ int maxSleep = 30; // Max seconds between each grep
+ Ndb* pNdb = GETNDB(step);
+ NdbGrep grep(GETNDB(step)->getNodeId()+1);
+ unsigned minGrepId = ~0;
+ unsigned maxGrepId = 0;
+ unsigned grepId = 0;
+ int result = NDBT_OK;
+
+ while (l < loops && result != NDBT_FAILED){
+
+ if (pNdb->waitUntilReady() != 0){
+ result = NDBT_FAILED;
+ continue;
+ }
+
+ // Sleep for a while
+ NdbSleep_SecSleep(maxSleep);
+
+ // Perform grep
+ if (grep.start() != 0){
+ ndbout << "grep.start failed" << endl;
+ result = NDBT_FAILED;
+ continue;
+ }
+ ndbout << "Started grep " << grepId << endl;
+
+ // Remember min and max grepid
+ if (grepId < minGrepId)
+ minGrepId = grepId;
+
+ if (grepId > maxGrepId)
+ maxGrepId = grepId;
+
+ ndbout << " maxGrepId = " << maxGrepId
+ << ", minGrepId = " << minGrepId << endl;
+ ctx->setProperty("MinGrepId", minGrepId);
+ ctx->setProperty("MaxGrepId", maxGrepId);
+
+ l++;
+ }
+
+ ctx->stopTest();
+
+ return result;
+}
+/*
+int runRestoreBankAndVerify(NDBT_Context* ctx, NDBT_Step* step){
+ NdbRestarter restarter;
+ NdbGrep grep(GETNDB(step)->getNodeId()+1);
+ unsigned minGrepId = ctx->getProperty("MinGrepId");
+ unsigned maxGrepId = ctx->getProperty("MaxGrepId");
+ unsigned grepId = minGrepId;
+ int result = NDBT_OK;
+ int errSumAccounts = 0;
+ int errValidateGL = 0;
+
+ ndbout << " maxGrepId = " << maxGrepId << endl;
+ ndbout << " minGrepId = " << minGrepId << endl;
+
+ while (grepId <= maxGrepId){
+
+ // TEMPORARY FIX
+ // To erase all tables from cache(s)
+ // To be removed, maybe replaced by ndb.invalidate();
+ {
+ Bank bank;
+
+ if (bank.dropBank() != NDBT_OK){
+ result = NDBT_FAILED;
+ break;
+ }
+ }
+ // END TEMPORARY FIX
+
+ ndbout << "Performing initial restart" << endl;
+ if (restarter.restartAll(true) != 0)
+ return NDBT_FAILED;
+
+ if (restarter.waitClusterStarted() != 0)
+ return NDBT_FAILED;
+
+ ndbout << "Restoring grep " << grepId << endl;
+ if (grep.restore(grepId) == -1){
+ return NDBT_FAILED;
+ }
+ ndbout << "Grep " << grepId << " restored" << endl;
+
+ // Let bank verify
+ Bank bank;
+
+ int wait = 0;
+ int yield = 1;
+ if (bank.performSumAccounts(wait, yield) != 0){
+ ndbout << "bank.performSumAccounts FAILED" << endl;
+ ndbout << " grepId = " << grepId << endl << endl;
+ result = NDBT_FAILED;
+ errSumAccounts++;
+ }
+
+ if (bank.performValidateAllGLs() != 0){
+ ndbout << "bank.performValidateAllGLs FAILED" << endl;
+ ndbout << " grepId = " << grepId << endl << endl;
+ result = NDBT_FAILED;
+ errValidateGL++;
+ }
+
+ grepId++;
+ }
+
+ if (result != NDBT_OK){
+ ndbout << "Verification of grep failed" << endl
+ << " errValidateGL="<<errValidateGL<<endl
+ << " errSumAccounts="<<errSumAccounts<<endl << endl;
+ }
+
+ return result;
+}
+*/
+
+NDBT_TESTSUITE(testGrep);
+TESTCASE("GrepBasic",
+ "Test that Global Replication works on one table \n"
+ "1. Load table\n"
+ "2. Grep\n"
+ "3. Restart -i\n"
+ "4. Restore\n"
+ "5. Verify count and content of table\n"){
+ INITIALIZER(runLoadTable);
+ VERIFIER(runVerifyBasic);
+ FINALIZER(runClearTable);
+
+}
+
+TESTCASE("GrepNodeRestart",
+ "Test that Global Replication works on one table \n"
+ "1. Load table\n"
+ "2. Grep\n"
+ "3. Restart -i\n"
+ "4. Restore\n"
+ "5. Verify count and content of table\n"){
+ INITIALIZER(runLoadTable);
+ STEP(runPkUpdate);
+ STEP(runRestarter);
+ VERIFIER(runVerifyBasic);
+ FINALIZER(runClearTable);
+}
+
+
+TESTCASE("GrepBank",
+ "Test that grep and restore works during transaction load\n"
+ " by backing up the bank"
+ "1. Create bank\n"
+ "2a. Start bank and let it run\n"
+ "2b. Perform loop number of greps of the bank\n"
+ " when greps are finished tell bank to close\n"
+ "3. Restart ndb -i and reload each grep\n"
+ " let bank verify that the grep is consistent\n"
+ "4. Drop bank\n"){
+ INITIALIZER(runCreateBank);
+ STEP(runBankTimer);
+ STEP(runBankTransactions);
+ STEP(runBankGL);
+ // TODO STEP(runBankSum);
+ STEP(runGrepBank);
+ // VERIFIER(runRestoreBankAndVerify);
+ // FINALIZER(runDropBank);
+
+}
+
+TESTCASE("NFMaster",
+ "Test that grep behaves during node failiure\n"){
+ INITIALIZER(setMaster);
+ STEP(runAbort);
+
+}
+TESTCASE("NFMasterAsSlave",
+ "Test that grep behaves during node failiure\n"){
+ INITIALIZER(setMasterAsSlave);
+ STEP(runAbort);
+
+}
+TESTCASE("NFSlave",
+ "Test that grep behaves during node failiure\n"){
+ INITIALIZER(setSlave);
+ STEP(runAbort);
+
+}
+TESTCASE("FailMaster",
+ "Test that grep behaves during node failiure\n"){
+ INITIALIZER(setMaster);
+ STEP(runFail);
+
+}
+TESTCASE("FailMasterAsSlave",
+ "Test that grep behaves during node failiure\n"){
+ INITIALIZER(setMasterAsSlave);
+ STEP(runFail);
+
+}
+TESTCASE("FailSlave",
+ "Test that grep behaves during node failiure\n"){
+ INITIALIZER(setSlave);
+ STEP(runFail);
+
+}
+NDBT_TESTSUITE_END(testGrep);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ return testGrep.execute(argc, argv);
+}
+
+
diff --git a/storage/ndb/test/ndbapi/testGrepVerify.cpp b/storage/ndb/test/ndbapi/testGrepVerify.cpp
new file mode 100644
index 00000000000..52dcda9a162
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testGrepVerify.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 "mgmapi.h"
+#include <string.h>
+#include <NdbMain.h>
+#include <OutputStream.hpp>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+
+#include <NdbApi.hpp>
+#include <NDBT.hpp>
+
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <ConfigRetriever.hpp>
+#include <ndb_version.h>
+
+
+#define CHECK(b) if (!(b)) { \
+ g_err << "ERR: "<< "getStep" \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ continue; }
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+
+ const char * connectString = NULL;
+ const char * table = NULL;
+ int records = 0;
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "connectString", 'c', arg_string, &connectString,
+ "ConnectString", "nodeid=<api id>;host=<hostname:port>" },
+ { "tableName", 't', arg_string, &table,
+ "table", "Table" },
+ { "records", 'r', arg_integer, &records, "Number of records", "recs"},
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "hostname:port\n"\
+ "This program will connect to the mgmsrv of a NDB cluster.\n"\
+ "It will then wait for all nodes to be started, then restart node(s)\n"\
+ "and wait for all to restart inbetween. It will do this \n"\
+ "loop number of times\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ ndbout_c("table %s connectStirng %s", table, connectString);
+ if(connectString == 0)
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ if(table == 0)
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+
+ Ndb * 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);
+ if (m_ndb->waitUntilReady() != 0){
+ ndbout_c("NDB Cluster not ready for connections");
+ }
+
+ int count = 0;
+ int result = NDBT_OK;
+
+
+ const NdbDictionary::Table * tab = NDBT_Table::discoverTableFromDb( m_ndb, table);
+// ndbout << *tab << endl;
+
+ UtilTransactions utilTrans(*tab);
+ HugoTransactions hugoTrans(*tab);
+
+ do{
+
+ // Check that there are as many records as we expected
+ CHECK(utilTrans.selectCount(m_ndb, 64, &count) == 0);
+
+ g_err << "count = " << count;
+ g_err << " records = " << records;
+ g_err << endl;
+
+ CHECK(count == records);
+
+ // Read and verify every record
+ CHECK(hugoTrans.pkReadRecords(m_ndb, records) == 0);
+
+ } while (false);
+
+
+ return NDBT_ProgramExit(result);
+
+}
diff --git a/storage/ndb/test/ndbapi/testIndex.cpp b/storage/ndb/test/ndbapi/testIndex.cpp
new file mode 100644
index 00000000000..3526326b680
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testIndex.cpp
@@ -0,0 +1,1522 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <NdbRestarts.hpp>
+#include <Vector.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+
+#define CHECK(b) if (!(b)) { \
+ g_err << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; break;\
+}
+
+
+struct Attrib {
+ bool indexCreated;
+ int numAttribs;
+ int attribs[1024];
+ Attrib(){
+ numAttribs = 0;
+ indexCreated = false;
+ }
+};
+class AttribList {
+public:
+ AttribList(){};
+ ~AttribList(){
+ for(size_t i = 0; i < attriblist.size(); i++){
+ delete attriblist[i];
+ }
+ };
+ void buildAttribList(const NdbDictionary::Table* pTab);
+ Vector<Attrib*> attriblist;
+};
+
+void AttribList::buildAttribList(const NdbDictionary::Table* pTab){
+ attriblist.clear();
+
+ Attrib* attr;
+ // Build attrib definitions that describes which attributes to build index
+ // Try to build strange combinations, not just "all" or all PK's
+
+ int i;
+
+ for(i = 1; i <= pTab->getNoOfColumns(); i++){
+ attr = new Attrib;
+ attr->numAttribs = i;
+ for(int a = 0; a<i; a++)
+ attr->attribs[a] = a;
+ attriblist.push_back(attr);
+ }
+ int b = 0;
+ for(i = pTab->getNoOfColumns()-1; i > 0; i--){
+ attr = new Attrib;
+ attr->numAttribs = i;
+ b++;
+ for(int a = 0; a<i; a++)
+ attr->attribs[a] = a+b;
+ attriblist.push_back(attr);
+ }
+ for(i = pTab->getNoOfColumns(); i > 0; i--){
+ attr = new Attrib;
+ attr->numAttribs = pTab->getNoOfColumns() - i;
+ for(int a = 0; a<pTab->getNoOfColumns() - i; a++)
+ attr->attribs[a] = pTab->getNoOfColumns()-a-1;
+ attriblist.push_back(attr);
+ }
+ for(i = 1; i < pTab->getNoOfColumns(); i++){
+ attr = new Attrib;
+ attr->numAttribs = pTab->getNoOfColumns() - i;
+ for(int a = 0; a<pTab->getNoOfColumns() - i; a++)
+ attr->attribs[a] = pTab->getNoOfColumns()-a-1;
+ attriblist.push_back(attr);
+ }
+ for(i = 1; i < pTab->getNoOfColumns(); i++){
+ attr = new Attrib;
+ attr->numAttribs = 2;
+ for(int a = 0; a<2; a++){
+ attr->attribs[a] = i%pTab->getNoOfColumns();
+ }
+ attriblist.push_back(attr);
+ }
+
+ // Last
+ attr = new Attrib;
+ attr->numAttribs = 1;
+ attr->attribs[0] = pTab->getNoOfColumns()-1;
+ attriblist.push_back(attr);
+
+ // Last and first
+ attr = new Attrib;
+ attr->numAttribs = 2;
+ attr->attribs[0] = pTab->getNoOfColumns()-1;
+ attr->attribs[1] = 0;
+ attriblist.push_back(attr);
+
+ // First and last
+ attr = new Attrib;
+ attr->numAttribs = 2;
+ attr->attribs[0] = 0;
+ attr->attribs[1] = pTab->getNoOfColumns()-1;
+ attriblist.push_back(attr);
+
+#if 0
+ for(size_t i = 0; i < attriblist.size(); i++){
+
+ ndbout << attriblist[i]->numAttribs << ": " ;
+ for(int a = 0; a < attriblist[i]->numAttribs; a++)
+ ndbout << attriblist[i]->attribs[a] << ", ";
+ ndbout << endl;
+ }
+#endif
+
+}
+
+char idxName[255];
+char pkIdxName[255];
+
+static const int SKIP_INDEX = 99;
+
+int create_index(NDBT_Context* ctx, int indxNum,
+ const NdbDictionary::Table* pTab,
+ Ndb* pNdb, Attrib* attr, bool logged){
+ bool orderedIndex = ctx->getProperty("OrderedIndex", (unsigned)0);
+ int result = NDBT_OK;
+
+ HugoCalculator calc(*pTab);
+
+ if (attr->numAttribs == 1 &&
+ calc.isUpdateCol(attr->attribs[0]) == true){
+ // Don't create index for the Hugo update column
+ // since it's not unique
+ return SKIP_INDEX;
+ }
+
+ // Create index
+ BaseString::snprintf(idxName, 255, "IDC%d", indxNum);
+ if (orderedIndex)
+ ndbout << "Creating " << ((logged)?"logged ": "temporary ") << "ordered index "<<idxName << " (";
+ else
+ ndbout << "Creating " << ((logged)?"logged ": "temporary ") << "unique index "<<idxName << " (";
+ ndbout << flush;
+ NdbDictionary::Index pIdx(idxName);
+ pIdx.setTable(pTab->getName());
+ if (orderedIndex)
+ pIdx.setType(NdbDictionary::Index::OrderedIndex);
+ else
+ pIdx.setType(NdbDictionary::Index::UniqueHashIndex);
+ for (int c = 0; c< attr->numAttribs; c++){
+ int attrNo = attr->attribs[c];
+ pIdx.addIndexColumn(pTab->getColumn(attrNo)->getName());
+ ndbout << pTab->getColumn(attrNo)->getName()<<" ";
+ }
+
+ pIdx.setStoredIndex(logged);
+ ndbout << ") ";
+ if (pNdb->getDictionary()->createIndex(pIdx) != 0){
+ attr->indexCreated = false;
+ ndbout << "FAILED!" << endl;
+ const NdbError err = pNdb->getDictionary()->getNdbError();
+ ERR(err);
+ if(err.classification == NdbError::ApplicationError)
+ return SKIP_INDEX;
+
+ return NDBT_FAILED;
+ } else {
+ ndbout << "OK!" << endl;
+ attr->indexCreated = true;
+ }
+ return result;
+}
+
+
+int drop_index(int indxNum, Ndb* pNdb,
+ const NdbDictionary::Table* pTab, Attrib* attr){
+ int result = NDBT_OK;
+
+ if (attr->indexCreated == false)
+ return NDBT_OK;
+
+ BaseString::snprintf(idxName, 255, "IDC%d", indxNum);
+
+ // Drop index
+ ndbout << "Dropping index "<<idxName<<"(" << pTab->getName() << ") ";
+ if (pNdb->getDictionary()->dropIndex(idxName, pTab->getName()) != 0){
+ ndbout << "FAILED!" << endl;
+ ERR(pNdb->getDictionary()->getNdbError());
+ result = NDBT_FAILED;
+ } else {
+ ndbout << "OK!" << endl;
+ }
+ return result;
+}
+
+int runCreateIndexes(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int l = 0;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ // NOTE If we need to test creating both logged and non logged indexes
+ // this should be divided into two testcases
+ // The paramater logged should then be specified
+ // as a TC_PROPERTY. ex TC_PROPERTY("LoggedIndexes", 1);
+ // and read into the test step like
+ bool logged = ctx->getProperty("LoggedIndexes", 1);
+
+ AttribList attrList;
+ attrList.buildAttribList(pTab);
+
+
+ while (l < loops && result == NDBT_OK){
+ unsigned int i;
+ for (i = 0; i < attrList.attriblist.size(); i++){
+
+ // Try to create index
+ if (create_index(ctx, i, pTab, pNdb, attrList.attriblist[i], logged) == NDBT_FAILED)
+ result = NDBT_FAILED;
+ }
+
+ // Now drop all indexes that where created
+ for (i = 0; i < attrList.attriblist.size(); i++){
+
+ // Try to drop index
+ if (drop_index(i, pNdb, pTab, attrList.attriblist[i]) != NDBT_OK)
+ result = NDBT_FAILED;
+ }
+
+ l++;
+ }
+
+ return result;
+}
+
+int createRandomIndex(NDBT_Context* ctx, NDBT_Step* step){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+ bool logged = ctx->getProperty("LoggedIndexes", 1);
+
+ AttribList attrList;
+ attrList.buildAttribList(pTab);
+
+ int retries = 100;
+ while(retries > 0){
+ const Uint32 i = rand() % attrList.attriblist.size();
+ int res = create_index(ctx, i, pTab, pNdb, attrList.attriblist[i],
+ logged);
+ if (res == SKIP_INDEX){
+ retries--;
+ continue;
+ }
+
+ if (res == NDBT_FAILED){
+ return NDBT_FAILED;
+ }
+
+ ctx->setProperty("createRandomIndex", i);
+ // Now drop all indexes that where created
+
+ return NDBT_OK;
+ }
+
+ return NDBT_FAILED;
+}
+
+int createRandomIndex_Drop(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+
+ Uint32 i = ctx->getProperty("createRandomIndex");
+
+ BaseString::snprintf(idxName, 255, "IDC%d", i);
+
+ // Drop index
+ ndbout << "Dropping index " << idxName << " ";
+ if (pNdb->getDictionary()->dropIndex(idxName,
+ ctx->getTab()->getName()) != 0){
+ ndbout << "FAILED!" << endl;
+ ERR(pNdb->getDictionary()->getNdbError());
+ return NDBT_FAILED;
+ } else {
+ ndbout << "OK!" << endl;
+ }
+
+ return NDBT_OK;
+}
+
+int createPkIndex(NDBT_Context* ctx, NDBT_Step* step){
+ bool orderedIndex = ctx->getProperty("OrderedIndex", (unsigned)0);
+
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+
+ bool logged = ctx->getProperty("LoggedIndexes", 1);
+
+ // Create index
+ BaseString::snprintf(pkIdxName, 255, "IDC_PK_%s", pTab->getName());
+ if (orderedIndex)
+ ndbout << "Creating " << ((logged)?"logged ": "temporary ") << "ordered index "
+ << pkIdxName << " (";
+ else
+ ndbout << "Creating " << ((logged)?"logged ": "temporary ") << "unique index "
+ << pkIdxName << " (";
+
+ NdbDictionary::Index pIdx(pkIdxName);
+ pIdx.setTable(pTab->getName());
+ if (orderedIndex)
+ pIdx.setType(NdbDictionary::Index::OrderedIndex);
+ else
+ pIdx.setType(NdbDictionary::Index::UniqueHashIndex);
+ for (int c = 0; c< pTab->getNoOfColumns(); c++){
+ const NdbDictionary::Column * col = pTab->getColumn(c);
+ if(col->getPrimaryKey()){
+ pIdx.addIndexColumn(col->getName());
+ ndbout << col->getName() <<" ";
+ }
+ }
+
+ pIdx.setStoredIndex(logged);
+ ndbout << ") ";
+ if (pNdb->getDictionary()->createIndex(pIdx) != 0){
+ ndbout << "FAILED!" << endl;
+ const NdbError err = pNdb->getDictionary()->getNdbError();
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ ndbout << "OK!" << endl;
+ return NDBT_OK;
+}
+
+int createPkIndex_Drop(NDBT_Context* ctx, NDBT_Step* step){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+
+ // Drop index
+ ndbout << "Dropping index " << pkIdxName << " ";
+ if (pNdb->getDictionary()->dropIndex(pkIdxName,
+ pTab->getName()) != 0){
+ ndbout << "FAILED!" << endl;
+ ERR(pNdb->getDictionary()->getNdbError());
+ return NDBT_FAILED;
+ } else {
+ ndbout << "OK!" << endl;
+ }
+
+ return NDBT_OK;
+}
+
+int
+runVerifyIndex(NDBT_Context* ctx, NDBT_Step* step){
+ // Verify that data in index match
+ // table data
+ Ndb* pNdb = GETNDB(step);
+ UtilTransactions utilTrans(*ctx->getTab());
+ const int batchSize = ctx->getProperty("BatchSize", 16);
+ const int parallelism = batchSize > 240 ? 240 : batchSize;
+
+ do {
+ if (utilTrans.verifyIndex(pNdb, idxName, parallelism, true) != 0){
+ g_err << "Inconsistent index" << endl;
+ return NDBT_FAILED;
+ }
+ } while(ctx->isTestStopped() == false);
+ return NDBT_OK;
+}
+
+int
+runTransactions1(NDBT_Context* ctx, NDBT_Step* step){
+ // Verify that data in index match
+ // table data
+ Ndb* pNdb = GETNDB(step);
+ HugoTransactions hugoTrans(*ctx->getTab());
+ const int batchSize = ctx->getProperty("BatchSize", 50);
+
+ int rows = ctx->getNumRecords();
+ while (ctx->isTestStopped() == false) {
+ if (hugoTrans.pkUpdateRecords(pNdb, rows, batchSize) != 0){
+ g_err << "Updated table failed" << endl;
+ return NDBT_FAILED;
+ }
+
+ ctx->sync_down("PauseThreads");
+ if(ctx->isTestStopped())
+ break;
+
+ if (hugoTrans.scanUpdateRecords(pNdb, rows, batchSize) != 0){
+ g_err << "Updated table failed" << endl;
+ return NDBT_FAILED;
+ }
+
+ ctx->sync_down("PauseThreads");
+ }
+ return NDBT_OK;
+}
+
+int
+runTransactions2(NDBT_Context* ctx, NDBT_Step* step){
+ // Verify that data in index match
+ // table data
+ Ndb* pNdb = GETNDB(step);
+ HugoTransactions hugoTrans(*ctx->getTab());
+ const int batchSize = ctx->getProperty("BatchSize", 50);
+
+ int rows = ctx->getNumRecords();
+ while (ctx->isTestStopped() == false) {
+#if 1
+ if (hugoTrans.indexReadRecords(pNdb, pkIdxName, rows, batchSize) != 0){
+ g_err << "Index read failed" << endl;
+ return NDBT_FAILED;
+ }
+#endif
+ ctx->sync_down("PauseThreads");
+ if(ctx->isTestStopped())
+ break;
+#if 1
+ if (hugoTrans.indexUpdateRecords(pNdb, pkIdxName, rows, batchSize) != 0){
+ g_err << "Index update failed" << endl;
+ return NDBT_FAILED;
+ }
+#endif
+ ctx->sync_down("PauseThreads");
+ }
+ return NDBT_OK;
+}
+
+int
+runTransactions3(NDBT_Context* ctx, NDBT_Step* step){
+ // Verify that data in index match
+ // table data
+ Ndb* pNdb = GETNDB(step);
+ HugoTransactions hugoTrans(*ctx->getTab());
+ UtilTransactions utilTrans(*ctx->getTab());
+ const int batchSize = ctx->getProperty("BatchSize", 32);
+ const int parallel = batchSize > 240 ? 240 : batchSize;
+
+ int rows = ctx->getNumRecords();
+ while (ctx->isTestStopped() == false) {
+ if(hugoTrans.loadTable(pNdb, rows, batchSize, false) != 0){
+ g_err << "Load table failed" << endl;
+ return NDBT_FAILED;
+ }
+ ctx->sync_down("PauseThreads");
+ if(ctx->isTestStopped())
+ break;
+
+ if (hugoTrans.pkUpdateRecords(pNdb, rows, batchSize) != 0){
+ g_err << "Updated table failed" << endl;
+ return NDBT_FAILED;
+ }
+
+ ctx->sync_down("PauseThreads");
+ if(ctx->isTestStopped())
+ break;
+
+ if (hugoTrans.indexReadRecords(pNdb, pkIdxName, rows, batchSize) != 0){
+ g_err << "Index read failed" << endl;
+ return NDBT_FAILED;
+ }
+
+ ctx->sync_down("PauseThreads");
+ if(ctx->isTestStopped())
+ break;
+
+ if (hugoTrans.indexUpdateRecords(pNdb, pkIdxName, rows, batchSize) != 0){
+ g_err << "Index update failed" << endl;
+ return NDBT_FAILED;
+ }
+
+ ctx->sync_down("PauseThreads");
+ if(ctx->isTestStopped())
+ break;
+
+ if (hugoTrans.scanUpdateRecords(pNdb, rows, 5, parallel) != 0){
+ g_err << "Scan updated table failed" << endl;
+ return NDBT_FAILED;
+ }
+
+ ctx->sync_down("PauseThreads");
+ if(ctx->isTestStopped())
+ break;
+
+ if(utilTrans.clearTable(pNdb, rows, parallel) != 0){
+ g_err << "Clear table failed" << endl;
+ return NDBT_FAILED;
+ }
+
+ ctx->sync_down("PauseThreads");
+ if(ctx->isTestStopped())
+ break;
+
+ int count = -1;
+ if(utilTrans.selectCount(pNdb, 64, &count) != 0 || count != 0)
+ return NDBT_FAILED;
+ ctx->sync_down("PauseThreads");
+ }
+ return NDBT_OK;
+}
+
+int runRestarts(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ NDBT_TestCase* pCase = ctx->getCase();
+ NdbRestarts restarts;
+ int i = 0;
+ int timeout = 240;
+ int sync_threads = ctx->getProperty("Threads", (unsigned)0);
+
+ while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){
+ if(restarts.executeRestart("RestartRandomNodeAbort", timeout) != 0){
+ g_err << "Failed to executeRestart(" <<pCase->getName() <<")" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+ ctx->sync_up_and_wait("PauseThreads", sync_threads);
+ i++;
+ }
+ ctx->stopTest();
+ return result;
+}
+
+int runCreateLoadDropIndex(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int l = 0;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int parallelism = batchSize > 240? 240: batchSize;
+ ndbout << "batchSize="<<batchSize<<endl;
+ bool logged = ctx->getProperty("LoggedIndexes", 1);
+
+ HugoTransactions hugoTrans(*pTab);
+ UtilTransactions utilTrans(*pTab);
+ AttribList attrList;
+ attrList.buildAttribList(pTab);
+
+ for (unsigned int i = 0; i < attrList.attriblist.size(); i++){
+
+ while (l < loops && result == NDBT_OK){
+
+ if ((l % 2) == 0){
+ // Create index first and then load
+
+ // Try to create index
+ if (create_index(ctx, i, pTab, pNdb, attrList.attriblist[i], logged) == NDBT_FAILED){
+ result = NDBT_FAILED;
+ }
+
+ // Load the table with data
+ ndbout << "Loading data after" << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records, batchSize) == 0);
+
+
+ } else {
+ // Load table then create index
+
+ // Load the table with data
+ ndbout << "Loading data before" << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records, batchSize) == 0);
+
+ // Try to create index
+ if (create_index(ctx, i, pTab, pNdb, attrList.attriblist[i], logged) == NDBT_FAILED)
+ result = NDBT_FAILED;
+
+ }
+
+ // Verify that data in index match
+ // table data
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, parallelism) == 0);
+
+ // Do it all...
+ ndbout <<"Doing it all"<<endl;
+ int count;
+ ndbout << " pkUpdateRecords" << endl;
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records, batchSize) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, parallelism) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records, batchSize) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, parallelism) == 0);
+ ndbout << " pkDelRecords half" << endl;
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2, batchSize) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, parallelism) == 0);
+ ndbout << " scanUpdateRecords" << endl;
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records/2, parallelism) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, parallelism) == 0);
+ ndbout << " clearTable" << endl;
+ CHECK(utilTrans.clearTable(pNdb, records/2, parallelism) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, parallelism) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == 0);
+ ndbout << " loadTable" << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records, batchSize) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, parallelism) == 0);
+ ndbout << " loadTable again" << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records, batchSize) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, parallelism) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+
+
+ if ((l % 2) == 0){
+ // Drop index first and then clear
+
+ // Try to create index
+ if (drop_index(i, pNdb, pTab, attrList.attriblist[i]) != NDBT_OK){
+ result = NDBT_FAILED;
+ }
+
+ // Clear table
+ ndbout << "Clearing table after" << endl;
+ CHECK(hugoTrans.clearTable(pNdb, records, parallelism) == 0);
+
+
+ } else {
+ // Clear table then drop index
+
+ //Clear table
+ ndbout << "Clearing table before" << endl;
+ CHECK(hugoTrans.clearTable(pNdb, records, parallelism) == 0);
+
+ // Try to drop index
+ if (drop_index(i, pNdb, pTab, attrList.attriblist[i]) != NDBT_OK)
+ result = NDBT_FAILED;
+ }
+
+ ndbout << " Done!" << endl;
+ l++;
+ }
+
+ // Make sure index is dropped
+ drop_index(i, pNdb, pTab, attrList.attriblist[i]);
+
+ }
+
+ return result;
+}
+
+int runInsertDelete(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ int parallelism = batchSize > 240? 240: batchSize;
+ ndbout << "batchSize="<<batchSize<<endl;
+ bool logged = ctx->getProperty("LoggedIndexes", 1);
+
+ HugoTransactions hugoTrans(*pTab);
+ UtilTransactions utilTrans(*pTab);
+
+ AttribList attrList;
+ attrList.buildAttribList(pTab);
+
+ for (unsigned int i = 0; i < attrList.attriblist.size(); i++){
+
+ Attrib* attr = attrList.attriblist[i];
+ // Create index
+ if (create_index(ctx, i, pTab, pNdb, attr, logged) == NDBT_OK){
+ int l = 1;
+ while (l <= loops && result == NDBT_OK){
+
+ CHECK(hugoTrans.loadTable(pNdb, records, batchSize) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, parallelism) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records, parallelism) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, parallelism) == 0);
+ l++;
+ }
+
+ // Drop index
+ if (drop_index(i, pNdb, pTab, attr) != NDBT_OK)
+ result = NDBT_FAILED;
+ }
+ }
+
+ return result;
+}
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ int batchSize = ctx->getProperty("BatchSize", 1);
+ if(hugoTrans.loadTable(GETNDB(step), records, batchSize) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runSystemRestart1(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int timeout = 300;
+ Uint32 loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int count;
+ NdbRestarter restarter;
+ Uint32 i = 1;
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+ const char * name = ctx->getTab()->getName();
+ while(i<=loops && result != NDBT_FAILED){
+
+ ndbout << "Loop " << i << "/"<< loops <<" started" << endl;
+ /*
+ 1. Load data
+ 2. Restart cluster and verify records
+ 3. Update records
+ 4. Restart cluster and verify records
+ 5. Delete half of the records
+ 6. Restart cluster and verify records
+ 7. Delete all records
+ 8. Restart cluster and verify records
+ 9. Insert, update, delete records
+ 10. Restart cluster and verify records
+ 11. Insert, update, delete records
+ 12. Restart cluster with error insert 5020 and verify records
+ */
+ ndbout << "Loading records..." << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records, 1) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+
+ ndbout << "Restarting cluster" << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(hugoTrans.pkReadRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+
+ ndbout << "Updating records..." << endl;
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(hugoTrans.pkReadRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+
+ ndbout << "Deleting 50% of records..." << endl;
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(hugoTrans.scanReadRecords(pNdb, records/2, 0, 64) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == (records/2));
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+
+ ndbout << "Deleting all records..." << endl;
+ CHECK(utilTrans.clearTable(pNdb, records/2) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+
+ ndbout << "Doing it all..." << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records, 1) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records, 1) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records, 1) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == 0);
+
+ ndbout << "Doing it all..." << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records, 1) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.verifyIndex(pNdb, idxName, 16, false) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records, 1) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+
+ ndbout << "Restarting cluster with error insert 5020..." << endl;
+ CHECK(restarter.restartAll(false, true) == 0);
+ CHECK(restarter.waitClusterNoStart(timeout) == 0);
+ CHECK(restarter.insertErrorInAllNodes(5020) == 0);
+ CHECK(restarter.startAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ i++;
+ }
+
+ ctx->stopTest();
+ ndbout << "runSystemRestart1 finished" << endl;
+
+ return result;
+}
+
+#define CHECK2(b, t) if(!b){ g_err << __LINE__ << ": " << t << endl; break;}
+
+int
+runMixed1(NDBT_Context* ctx, NDBT_Step* step){
+ // Verify that data in index match
+ // table data
+ Ndb* pNdb = GETNDB(step);
+ HugoOperations hugoOps(*ctx->getTab());
+
+
+ do {
+ // TC1
+ g_err << "pkRead, indexRead, Commit" << endl;
+ CHECK2(hugoOps.startTransaction(pNdb) == 0, "startTransaction");
+ CHECK2(hugoOps.indexReadRecords(pNdb, pkIdxName, 0) == 0, "indexReadRecords");
+ CHECK2(hugoOps.pkReadRecord(pNdb, 0) == 0, "pkReadRecord");
+ CHECK2(hugoOps.execute_Commit(pNdb) == 0, "executeCommit");
+ CHECK2(hugoOps.closeTransaction(pNdb) == 0, "closeTransaction");
+
+ // TC1
+ g_err << "pkRead, indexRead, Commit" << endl;
+ CHECK2(hugoOps.startTransaction(pNdb) == 0, "startTransaction");
+ CHECK2(hugoOps.pkReadRecord(pNdb, 0) == 0, "pkReadRecord");
+ CHECK2(hugoOps.indexReadRecords(pNdb, pkIdxName, 0) == 0, "indexReadRecords");
+ CHECK2(hugoOps.execute_Commit(pNdb) == 0, "executeCommit");
+ CHECK2(hugoOps.closeTransaction(pNdb) == 0, "closeTransaction");
+
+
+ // TC2
+ g_err << "pkRead, indexRead, NoCommit, Commit" << endl;
+ CHECK2(hugoOps.startTransaction(pNdb) == 0, "startTransaction");
+ CHECK2(hugoOps.pkReadRecord(pNdb, 0) == 0, "pkReadRecord");
+ CHECK2(hugoOps.indexReadRecords(pNdb, pkIdxName, 0) == 0,
+ "indexReadRecords");
+ CHECK2(hugoOps.execute_NoCommit(pNdb) == 0, "executeNoCommit");
+ CHECK2(hugoOps.execute_Commit(pNdb) == 0, "executeCommit");
+ CHECK2(hugoOps.closeTransaction(pNdb) == 0, "closeTransaction");
+
+ // TC3
+ g_err << "pkRead, pkRead, Commit" << endl;
+ CHECK2(hugoOps.startTransaction(pNdb) == 0, "startTransaction ");
+ CHECK2(hugoOps.pkReadRecord(pNdb, 0) == 0, "pkReadRecords ");
+ CHECK2(hugoOps.pkReadRecord(pNdb, 0) == 0, "pkReadRecords ");
+ CHECK2(hugoOps.execute_Commit(pNdb) == 0, "executeCommit");
+ CHECK2(hugoOps.closeTransaction(pNdb) == 0, "closeTransaction ");
+
+ // TC4
+ g_err << "indexRead, indexRead, Commit" << endl;
+
+ CHECK2(hugoOps.startTransaction(pNdb) == 0, "startTransaction ");
+ CHECK2(hugoOps.indexReadRecords(pNdb, pkIdxName, 0) == 0, "indexReadRecords");
+ CHECK2(hugoOps.indexReadRecords(pNdb, pkIdxName, 0) == 0, "indexReadRecords");
+ CHECK2(hugoOps.execute_Commit(pNdb) == 0, "executeCommit");
+
+ CHECK2(hugoOps.closeTransaction(pNdb) == 0, "closeTransaction ");
+
+ return NDBT_OK;
+ } while(false);
+
+
+ hugoOps.closeTransaction(pNdb);
+ return NDBT_FAILED;
+}
+
+int
+runBuildDuring(NDBT_Context* ctx, NDBT_Step* step){
+ // Verify that data in index match
+ // table data
+ const int Threads = ctx->getProperty("Threads", (Uint32)0);
+ const int loops = ctx->getNumLoops();
+
+ for(int i = 0; i<loops; i++){
+#if 1
+ if(createPkIndex(ctx, step) != NDBT_OK){
+ g_err << "Failed to create index" << endl;
+ return NDBT_FAILED;
+ }
+#endif
+
+ if(ctx->isTestStopped())
+ break;
+
+#if 1
+ if(createRandomIndex(ctx, step) != NDBT_OK){
+ g_err << "Failed to create index" << endl;
+ return NDBT_FAILED;
+ }
+#endif
+
+ if(ctx->isTestStopped())
+ break;
+
+ ctx->setProperty("pause", 1);
+ int count = 0;
+ for(int j = 0; count < Threads && !ctx->isTestStopped();
+ j = (j+1) % Threads){
+ char buf[255];
+ sprintf(buf, "Thread%d_paused", j);
+ int tmp = ctx->getProperty(buf, (Uint32)0);
+ count += tmp;
+ }
+
+ if(ctx->isTestStopped())
+ break;
+
+#if 1
+ if(createPkIndex_Drop(ctx, step) != NDBT_OK){
+ g_err << "Failed to drop index" << endl;
+ return NDBT_FAILED;
+ }
+#endif
+
+ if(ctx->isTestStopped())
+ break;
+
+#if 1
+ if(createRandomIndex_Drop(ctx, step) != NDBT_OK){
+ g_err << "Failed to drop index" << endl;
+ return NDBT_FAILED;
+ }
+#endif
+
+ ctx->setProperty("pause", (Uint32)0);
+ NdbSleep_SecSleep(2);
+ }
+
+ ctx->stopTest();
+ return NDBT_OK;
+}
+
+static NdbLockable g_lock;
+static int threadCounter = 0;
+
+void
+wait_paused(NDBT_Context* ctx, int id){
+ if(ctx->getProperty("pause", (Uint32)0) == 1){
+ char buf[255];
+ sprintf(buf, "Thread%d_paused", id);
+ ctx->setProperty(buf, 1);
+ while(!ctx->isTestStopped() && ctx->getProperty("pause", (Uint32)0) == 1){
+ NdbSleep_MilliSleep(250);
+ }
+ ctx->setProperty(buf, (Uint32)0);
+ }
+}
+
+int
+runTransactions4(NDBT_Context* ctx, NDBT_Step* step){
+
+ g_lock.lock();
+ const int ThreadId = threadCounter++;
+ g_lock.unlock();
+
+ // Verify that data in index match
+ // table data
+ Ndb* pNdb = GETNDB(step);
+ HugoTransactions hugoTrans(*ctx->getTab());
+ UtilTransactions utilTrans(*ctx->getTab());
+ const int batchSize = ctx->getProperty("BatchSize", 32);
+ const int parallel = batchSize > 240 ? 240 : batchSize;
+
+ int rows = ctx->getNumRecords();
+ while (ctx->isTestStopped() == false) {
+ if(hugoTrans.loadTable(pNdb, rows, batchSize, false) != 0){
+ g_err << "Load table failed" << endl;
+ return NDBT_FAILED;
+ }
+
+ wait_paused(ctx, ThreadId);
+
+ if(ctx->isTestStopped())
+ break;
+
+ if (hugoTrans.pkUpdateRecords(pNdb, rows, batchSize) != 0){
+ g_err << "Updated table failed" << endl;
+ return NDBT_FAILED;
+ }
+
+ wait_paused(ctx, ThreadId);
+
+ if(ctx->isTestStopped())
+ break;
+
+ if (hugoTrans.scanUpdateRecords(pNdb, rows, 5, parallel) != 0){
+ g_err << "Scan updated table failed" << endl;
+ return NDBT_FAILED;
+ }
+
+ wait_paused(ctx, ThreadId);
+
+ if(ctx->isTestStopped())
+ break;
+
+ if(utilTrans.clearTable(pNdb, rows, parallel) != 0){
+ g_err << "Clear table failed" << endl;
+ return NDBT_FAILED;
+ }
+ }
+ return NDBT_OK;
+}
+
+int
+runUniqueNullTransactions(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+
+ bool logged = ctx->getProperty("LoggedIndexes", 1);
+ bool orderedIndex = ctx->getProperty("OrderedIndex", (unsigned)0);
+ NdbConnection * pTrans = 0;
+
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ // Create index
+ char nullIndex[255];
+ BaseString::snprintf(nullIndex, 255, "IDC_PK_%s_NULL", pTab->getName());
+ if (orderedIndex)
+ ndbout << "Creating " << ((logged)?"logged ": "temporary ") << "ordered index "
+ << pkIdxName << " (";
+ else
+ ndbout << "Creating " << ((logged)?"logged ": "temporary ") << "unique index "
+ << pkIdxName << " (";
+
+ NdbDictionary::Index pIdx(pkIdxName);
+ pIdx.setTable(pTab->getName());
+ if (orderedIndex)
+ pIdx.setType(NdbDictionary::Index::OrderedIndex);
+ else
+ pIdx.setType(NdbDictionary::Index::UniqueHashIndex);
+ pIdx.setStoredIndex(logged);
+ int c;
+ for (c = 0; c< pTab->getNoOfColumns(); c++){
+ const NdbDictionary::Column * col = pTab->getColumn(c);
+ if(col->getPrimaryKey()){
+ pIdx.addIndexColumn(col->getName());
+ ndbout << col->getName() <<" ";
+ }
+ }
+
+ int colId = -1;
+ for (c = 0; c< pTab->getNoOfColumns(); c++){
+ const NdbDictionary::Column * col = pTab->getColumn(c);
+ if(col->getNullable()){
+ pIdx.addIndexColumn(col->getName());
+ ndbout << col->getName() <<" ";
+ colId = c;
+ break;
+ }
+ }
+ ndbout << ") ";
+
+ if(colId == -1){
+ ndbout << endl << "No nullable column found -> NDBT_FAILED" << endl;
+ return NDBT_FAILED;
+ }
+
+ if (pNdb->getDictionary()->createIndex(pIdx) != 0){
+ ndbout << "FAILED!" << endl;
+ const NdbError err = pNdb->getDictionary()->getNdbError();
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ int result = NDBT_OK;
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ const int batchSize = ctx->getProperty("BatchSize", 50);
+ int loops = ctx->getNumLoops();
+ int rows = ctx->getNumRecords();
+ while (loops-- > 0 && ctx->isTestStopped() == false) {
+ if (hugoTrans.pkUpdateRecords(pNdb, rows, batchSize) != 0){
+ g_err << "Updated table failed" << endl;
+ result = NDBT_FAILED;
+ goto done;
+ }
+ }
+
+ if(ctx->isTestStopped()){
+ goto done;
+ }
+
+ ctx->stopTest();
+ while(ctx->getNoOfRunningSteps() > 1){
+ NdbSleep_MilliSleep(100);
+ }
+
+ result = NDBT_FAILED;
+ pTrans = pNdb->startTransaction();
+ NdbScanOperation * sOp;
+ NdbOperation * uOp;
+ int eof;
+ if(!pTrans) goto done;
+ sOp = pTrans->getNdbScanOperation(pTab->getName());
+ if(!sOp) goto done;
+ if(sOp->readTuples(NdbScanOperation::LM_Exclusive)) goto done;
+ if(pTrans->execute(NoCommit) == -1) goto done;
+ while((eof = sOp->nextResult(true)) == 0){
+ do {
+ NdbOperation * uOp = sOp->updateCurrentTuple();
+ if(uOp == 0) goto done;
+ uOp->setValue(colId, 0);
+ } while((eof = sOp->nextResult(false)) == 0);
+ eof = pTrans->execute(Commit);
+ if(eof == -1) goto done;
+ }
+
+ done:
+ if(pTrans) pNdb->closeTransaction(pTrans);
+ pNdb->getDictionary()->dropIndex(nullIndex, pTab->getName());
+ return result;
+}
+
+int runLQHKEYREF(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops() * 100;
+ NdbRestarter restarter;
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+
+#if 0
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ if(restarter.dumpStateAllNodes(&val, 1) != 0){
+ g_err << "Failed to dump DihMinTimeBetweenLCP" << endl;
+ return NDBT_FAILED;
+ }
+#endif
+
+ for(int i = 0; i<loops && !ctx->isTestStopped(); i++){
+ int randomId = myRandom48(restarter.getNumDbNodes());
+ int nodeId = restarter.getDbNodeId(randomId);
+
+ const Uint32 error = 5031 + (i % 3);
+
+ if(restarter.insertErrorInNode(nodeId, error) != 0){
+ g_err << "Failed to error insert( " << error << ") in node "
+ << nodeId << endl;
+ return NDBT_FAILED;
+ }
+ }
+
+ ctx->stopTest();
+ return NDBT_OK;
+}
+
+NDBT_TESTSUITE(testIndex);
+TESTCASE("CreateAll",
+ "Test that we can create all various indexes on each table\n"
+ "Then drop the indexes\n"){
+ INITIALIZER(runCreateIndexes);
+}
+TESTCASE("CreateAll_O",
+ "Test that we can create all various indexes on each table\n"
+ "Then drop the indexes\n"){
+ TC_PROPERTY("OrderedIndex", 1);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ INITIALIZER(runCreateIndexes);
+}
+TESTCASE("InsertDeleteGentle",
+ "Create one index, then perform insert and delete in the table\n"
+ "loop number of times. Use batch size 1."){
+ TC_PROPERTY("BatchSize", 1);
+ INITIALIZER(runInsertDelete);
+ FINALIZER(runClearTable);
+}
+TESTCASE("InsertDeleteGentle_O",
+ "Create one index, then perform insert and delete in the table\n"
+ "loop number of times. Use batch size 1."){
+ TC_PROPERTY("OrderedIndex", 1);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("BatchSize", 1);
+ INITIALIZER(runInsertDelete);
+ FINALIZER(runClearTable);
+}
+TESTCASE("InsertDelete",
+ "Create one index, then perform insert and delete in the table\n"
+ "loop number of times. Use batchsize 512 to stress db more"){
+ TC_PROPERTY("BatchSize", 512);
+ INITIALIZER(runInsertDelete);
+ FINALIZER(runClearTable);
+
+}
+TESTCASE("InsertDelete_O",
+ "Create one index, then perform insert and delete in the table\n"
+ "loop number of times. Use batchsize 512 to stress db more"){
+ TC_PROPERTY("OrderedIndex", 1);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("BatchSize", 512);
+ INITIALIZER(runInsertDelete);
+ FINALIZER(runClearTable);
+
+}
+TESTCASE("CreateLoadDropGentle",
+ "Try to create, drop and load various indexes \n"
+ "on table loop number of times.Usa batch size 1.\n"){
+ TC_PROPERTY("BatchSize", 1);
+ INITIALIZER(runCreateLoadDropIndex);
+}
+TESTCASE("CreateLoadDropGentle_O",
+ "Try to create, drop and load various indexes \n"
+ "on table loop number of times.Usa batch size 1.\n"){
+ TC_PROPERTY("OrderedIndex", 1);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("BatchSize", 1);
+ INITIALIZER(runCreateLoadDropIndex);
+}
+TESTCASE("CreateLoadDrop",
+ "Try to create, drop and load various indexes \n"
+ "on table loop number of times. Use batchsize 512 to stress db more\n"){
+ TC_PROPERTY("BatchSize", 512);
+ INITIALIZER(runCreateLoadDropIndex);
+}
+TESTCASE("CreateLoadDrop_O",
+ "Try to create, drop and load various indexes \n"
+ "on table loop number of times. Use batchsize 512 to stress db more\n"){
+ TC_PROPERTY("OrderedIndex", 1);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("BatchSize", 512);
+ INITIALIZER(runCreateLoadDropIndex);
+}
+TESTCASE("NFNR1",
+ "Test that indexes are correctly maintained during node fail and node restart"){
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("PauseThreads", 2);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ STEP(runTransactions1);
+ STEP(runTransactions1);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NFNR1_O",
+ "Test that indexes are correctly maintained during node fail and node restart"){
+ TC_PROPERTY("OrderedIndex", 1);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("PauseThreads", 2);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ STEP(runTransactions1);
+ STEP(runTransactions1);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NFNR2",
+ "Test that indexes are correctly maintained during node fail and node restart"){
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("PauseThreads", 2);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(createPkIndex);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ STEP(runTransactions2);
+ STEP(runTransactions2);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(createPkIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NFNR2_O",
+ "Test that indexes are correctly maintained during node fail and node restart"){
+ TC_PROPERTY("OrderedIndex", 1);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("PauseThreads", 2);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(createPkIndex);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ STEP(runTransactions2);
+ STEP(runTransactions2);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(createPkIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NFNR3",
+ "Test that indexes are correctly maintained during node fail and node restart"){
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("PauseThreads", 2);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(createPkIndex);
+ STEP(runRestarts);
+ STEP(runTransactions3);
+ STEP(runVerifyIndex);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createPkIndex_Drop);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NFNR3_O",
+ "Test that indexes are correctly maintained during node fail and node restart"){
+ TC_PROPERTY("OrderedIndex", 1);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("PauseThreads", 2);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(createPkIndex);
+ STEP(runRestarts);
+ STEP(runTransactions3);
+ STEP(runVerifyIndex);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createPkIndex_Drop);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NFNR4",
+ "Test that indexes are correctly maintained during node fail and node restart"){
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("PauseThreads", 4);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(createPkIndex);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ STEP(runTransactions1);
+ STEP(runTransactions1);
+ STEP(runTransactions2);
+ STEP(runTransactions2);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(createPkIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NFNR4_O",
+ "Test that indexes are correctly maintained during node fail and node restart"){
+ TC_PROPERTY("OrderedIndex", 1);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("PauseThreads", 4);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(createPkIndex);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ STEP(runTransactions1);
+ STEP(runTransactions1);
+ STEP(runTransactions2);
+ STEP(runTransactions2);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(createPkIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NFNR5",
+ "Test that indexes are correctly maintained during node fail and node restart"){
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("BatchSize", (unsigned)1);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(createPkIndex);
+ INITIALIZER(runLoadTable);
+ STEP(runLQHKEYREF);
+ STEP(runTransactions1);
+ STEP(runTransactions1);
+ STEP(runTransactions2);
+ STEP(runTransactions2);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(createPkIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NFNR5_O",
+ "Test that indexes are correctly maintained during node fail and node restart"){
+ TC_PROPERTY("OrderedIndex", 1);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("BatchSize", (unsigned)1);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(createPkIndex);
+ INITIALIZER(runLoadTable);
+ STEP(runLQHKEYREF);
+ STEP(runTransactions1);
+ STEP(runTransactions1);
+ STEP(runTransactions2);
+ STEP(runTransactions2);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(createPkIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR1",
+ "Test that indexes are correctly maintained during SR"){
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(createPkIndex);
+ STEP(runSystemRestart1);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createPkIndex_Drop);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("MixedTransaction",
+ "Test mixing of index and normal operations"){
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createPkIndex);
+ INITIALIZER(runLoadTable);
+ STEP(runMixed1);
+ FINALIZER(createPkIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR1_O",
+ "Test that indexes are correctly maintained during SR"){
+ TC_PROPERTY("OrderedIndex", 1);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(createPkIndex);
+ STEP(runSystemRestart1);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createPkIndex_Drop);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("BuildDuring",
+ "Test that index build when running transactions work"){
+ TC_PROPERTY("OrderedIndex", (unsigned)0);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("Threads", 1); // # runTransactions4
+ INITIALIZER(runClearTable);
+ STEP(runBuildDuring);
+ STEP(runTransactions4);
+ //STEP(runTransactions4);
+ FINALIZER(runClearTable);
+}
+TESTCASE("BuildDuring_O",
+ "Test that index build when running transactions work"){
+ TC_PROPERTY("OrderedIndex", (unsigned)1);
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ TC_PROPERTY("Threads", 1); // # runTransactions4
+ INITIALIZER(runClearTable);
+ STEP(runBuildDuring);
+ STEP(runTransactions4);
+ //STEP(runTransactions4);
+ FINALIZER(runClearTable);
+}
+TESTCASE("UniqueNull",
+ "Test that unique indexes and nulls"){
+ TC_PROPERTY("LoggedIndexes", (unsigned)0);
+ INITIALIZER(runClearTable);
+ INITIALIZER(createRandomIndex);
+ INITIALIZER(createPkIndex);
+ INITIALIZER(runLoadTable);
+ STEP(runTransactions1);
+ STEP(runTransactions2);
+ STEP(runUniqueNullTransactions);
+ FINALIZER(runVerifyIndex);
+ FINALIZER(createRandomIndex_Drop);
+ FINALIZER(createPkIndex_Drop);
+ FINALIZER(runClearTable);
+}
+NDBT_TESTSUITE_END(testIndex);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ return testIndex.execute(argc, argv);
+}
+
+template class Vector<Attrib*>;
diff --git a/storage/ndb/test/ndbapi/testInterpreter.cpp b/storage/ndb/test/ndbapi/testInterpreter.cpp
new file mode 100644
index 00000000000..0baba33d2b2
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testInterpreter.cpp
@@ -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 */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <NdbRestarts.hpp>
+#include <Vector.hpp>
+#include <random.h>
+#include <NdbTick.h>
+
+
+#define CHECK(b) if (!(b)) { \
+ ndbout << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ continue; }
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int batchSize = ctx->getProperty("BatchSize", 1);
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.pkDelRecords(GETNDB(step), records, batchSize) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runTestIncValue64(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ // NDBT_Table* pTab = ctx->getTab();
+ //Ndb* pNdb = GETNDB(step);
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.pkInterpretedUpdateRecords(GETNDB(step),
+ records) != 0){
+ return NDBT_FAILED;
+ }
+
+ // Verify the update
+ if (hugoTrans.pkReadRecords(GETNDB(step),
+ records) != 0){
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+
+}
+
+int runTestIncValue32(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ const NdbDictionary::Table * pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+
+
+ NdbConnection* pTrans = pNdb->startTransaction();
+ if (pTrans == NULL){
+ ERR(pNdb->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ NdbOperation* pOp = pTrans->getNdbOperation(pTab->getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ int check = pOp->interpretedUpdateTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+
+ // Primary keys
+ Uint32 pkVal = 1;
+ check = pOp->equal("KOL1", pkVal );
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ // Attributes
+
+ // Update column
+ Uint32 valToIncWith = 1;
+ check = pOp->incValue("KOL2", valToIncWith);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbRecAttr* valueRec = pOp->getValue("KOL2");
+ if( valueRec == NULL ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ Uint32 value = valueRec->u_32_value();
+
+ pNdb->closeTransaction(pTrans);
+
+
+ return NDBT_OK;
+}
+
+
+NDBT_TESTSUITE(testInterpreter);
+TESTCASE("IncValue32",
+ "Test incValue for 32 bit integer\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runTestIncValue32);
+ FINALIZER(runClearTable);
+}
+TESTCASE("IncValue64",
+ "Test incValue for 64 bit integer\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runTestIncValue64);
+ FINALIZER(runClearTable);
+}
+#if 0
+TESTCASE("MaxTransactions",
+ "Start transactions until no more can be created\n"){
+ INITIALIZER(runTestMaxTransaction);
+}
+TESTCASE("MaxOperations",
+ "Get operations until no more can be created\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runTestMaxOperations);
+ FINALIZER(runClearTable);
+}
+TESTCASE("MaxGetValue",
+ "Call getValue loads of time\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runTestGetValue);
+ FINALIZER(runClearTable);
+}
+TESTCASE("MaxEqual",
+ "Call equal loads of time\n"){
+ INITIALIZER(runTestEqual);
+}
+TESTCASE("DeleteNdb",
+ "Make sure that a deleted Ndb object is properly deleted\n"
+ "and removed from transporter\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runTestDeleteNdb);
+ FINALIZER(runClearTable);
+}
+TESTCASE("WaitUntilReady",
+ "Make sure you get an error message when calling waitUntilReady\n"
+ "without an init'ed Ndb\n"){
+ INITIALIZER(runTestWaitUntilReady);
+}
+TESTCASE("GetOperationNoTab",
+ "Call getNdbOperation on a table that does not exist\n"){
+ INITIALIZER(runGetNdbOperationNoTab);
+}
+TESTCASE("MissingOperation",
+ "Missing operation request(insertTuple) should give an error code\n"){
+ INITIALIZER(runMissingOperation);
+}
+TESTCASE("GetValueInUpdate",
+ "Test that it's not possible to perform getValue in an update\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runGetValueInUpdate);
+ FINALIZER(runClearTable);
+}
+TESTCASE("UpdateWithoutKeys",
+ "Test that it's not possible to perform update without setting\n"
+ "PKs"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runUpdateWithoutKeys);
+ FINALIZER(runClearTable);
+}
+TESTCASE("UpdateWithoutValues",
+ "Test that it's not possible to perform update without setValues\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runUpdateWithoutValues);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NdbErrorOperation",
+ "Test that NdbErrorOperation is properly set"){
+ INITIALIZER(runCheckGetNdbErrorOperation);
+}
+#endif
+NDBT_TESTSUITE_END(testInterpreter);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ // TABLE("T1");
+ return testInterpreter.execute(argc, argv);
+}
+
+
diff --git a/storage/ndb/test/ndbapi/testLcp.cpp b/storage/ndb/test/ndbapi/testLcp.cpp
new file mode 100644
index 00000000000..8bfc7ccf9b9
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testLcp.cpp
@@ -0,0 +1,331 @@
+
+#include <NDBT.hpp>
+#include <NdbApi.hpp>
+#include <NdbRestarter.hpp>
+#include <HugoOperations.hpp>
+#include <UtilTransactions.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+
+struct CASE
+{
+ bool start_row;
+ bool end_row;
+ bool curr_row;
+ const char * op1;
+ const char * op2;
+ int val;
+};
+
+static CASE g_ops[] =
+{
+ { false, true, false, "INSERT", 0, 0 },
+ { false, true, false, "INSERT", "UPDATE", 0 },
+ { false, false, false, "INSERT", "DELETE", 0 },
+ { true, true, false, "UPDATE", 0, 0 },
+ { true, true, false, "UPDATE", "UPDATE", 0 },
+ { true, false, false, "UPDATE", "DELETE", 0 },
+ { true, false, false, "DELETE", 0, 0 },
+ { true, true, false, "DELETE", "INSERT", 0 }
+};
+const size_t OP_COUNT = (sizeof(g_ops)/sizeof(g_ops[0]));
+
+static Ndb* g_ndb = 0;
+static Ndb_cluster_connection *g_cluster_connection= 0;
+static CASE* g_cases;
+static HugoOperations* g_hugo_ops;
+
+static int g_rows = 1000;
+static int g_setup_tables = 1;
+static const char * g_tablename = "T1";
+static const NdbDictionary::Table* g_table = 0;
+static NdbRestarter g_restarter;
+
+static int init_ndb(int argc, char** argv);
+static int parse_args(int argc, char** argv);
+static int connect_ndb();
+static int drop_all_tables();
+static int load_table();
+static int pause_lcp();
+static int do_op(int row);
+static int continue_lcp(int error);
+static int commit();
+static int restart();
+static int validate();
+
+#define require(x) { bool b = x; if(!b){g_err << __LINE__ << endl; abort();}}
+
+int
+main(int argc, char ** argv){
+
+ require(!init_ndb(argc, argv));
+ require(!parse_args(argc, argv));
+ require(!connect_ndb());
+
+ if(g_setup_tables){
+ require(!drop_all_tables());
+
+ if(NDBT_Tables::createTable(g_ndb, g_tablename) != 0){
+ exit(-1);
+ }
+ }
+
+ g_table = g_ndb->getDictionary()->getTable(g_tablename);
+ if(g_table == 0){
+ g_err << "Failed to retreive table: " << g_tablename << endl;
+ exit(-1);
+ }
+ require(g_hugo_ops = new HugoOperations(* g_table));
+ require(!g_hugo_ops->startTransaction(g_ndb));
+
+ g_cases= new CASE[g_rows];
+ require(!load_table());
+
+ g_info << "Performing all ops wo/ inteference of LCP" << endl;
+
+ g_info << "Testing pre LCP operations, ZLCP_OP_WRITE_RT_BREAK" << endl;
+ g_info << " where ZLCP_OP_WRITE_RT_BREAK is finished before SAVE_PAGES"
+ << endl;
+ require(!pause_lcp());
+ size_t j;
+ for(j = 0; j<g_rows; j++){
+ require(!do_op(j));
+ }
+ require(!continue_lcp(5900));
+ require(!commit());
+ require(!restart());
+ require(!validate());
+
+ g_info << "Testing pre LCP operations, ZLCP_OP_WRITE_RT_BREAK" << endl;
+ g_info << " where ZLCP_OP_WRITE_RT_BREAK is finished after SAVE_PAGES"
+ << endl;
+ require(!load_table());
+ require(!pause_lcp());
+ for(j = 0; j<g_rows; j++){
+ require(!do_op(j));
+ }
+ require(!continue_lcp(5901));
+ require(!commit());
+ require(!restart());
+ require(!validate());
+
+ g_info << "Testing pre LCP operations, undo-ed at commit" << endl;
+ require(!load_table());
+ require(!pause_lcp());
+ for(j = 0; j<g_rows; j++){
+ require(!do_op(j));
+ }
+ require(!continue_lcp(5902));
+ require(!commit());
+ require(!continue_lcp(5903));
+ require(!restart());
+ require(!validate());
+}
+
+static int init_ndb(int argc, char** argv)
+{
+ ndb_init();
+ return 0;
+}
+
+static int parse_args(int argc, char** argv)
+{
+ return 0;
+}
+
+static int connect_ndb()
+{
+ g_cluster_connection = new Ndb_cluster_connection();
+ if(g_cluster_connection->connect(12, 5, 1) != 0)
+ {
+ return 1;
+ }
+
+ g_ndb = new Ndb(g_cluster_connection, "TEST_DB");
+ g_ndb->init();
+ if(g_ndb->waitUntilReady(30) == 0){
+ int args[] = { DumpStateOrd::DihMaxTimeBetweenLCP };
+ return g_restarter.dumpStateAllNodes(args, 1);
+ }
+ return -1;
+}
+
+static int disconnect_ndb()
+{
+ delete g_ndb;
+ delete g_cluster_connection;
+ g_ndb = 0;
+ g_table = 0;
+ g_cluster_connection= 0;
+ return 0;
+}
+
+static int drop_all_tables()
+{
+ NdbDictionary::Dictionary * dict = g_ndb->getDictionary();
+ require(dict);
+
+ BaseString db = g_ndb->getDatabaseName();
+ BaseString schema = g_ndb->getSchemaName();
+
+ NdbDictionary::Dictionary::List list;
+ if (dict->listObjects(list, NdbDictionary::Object::TypeUndefined) == -1){
+ g_err << "Failed to list tables: " << endl
+ << dict->getNdbError() << endl;
+ return -1;
+ }
+ for (unsigned i = 0; i < list.count; i++) {
+ NdbDictionary::Dictionary::List::Element& elt = list.elements[i];
+ switch (elt.type) {
+ case NdbDictionary::Object::SystemTable:
+ case NdbDictionary::Object::UserTable:
+ g_ndb->setDatabaseName(elt.database);
+ g_ndb->setSchemaName(elt.schema);
+ if(dict->dropTable(elt.name) != 0){
+ g_err << "Failed to drop table: "
+ << elt.database << "/" << elt.schema << "/" << elt.name <<endl;
+ g_err << dict->getNdbError() << endl;
+ return -1;
+ }
+ break;
+ case NdbDictionary::Object::UniqueHashIndex:
+ case NdbDictionary::Object::OrderedIndex:
+ case NdbDictionary::Object::HashIndexTrigger:
+ case NdbDictionary::Object::IndexTrigger:
+ case NdbDictionary::Object::SubscriptionTrigger:
+ case NdbDictionary::Object::ReadOnlyConstraint:
+ default:
+ break;
+ }
+ }
+
+ g_ndb->setDatabaseName(db.c_str());
+ g_ndb->setSchemaName(schema.c_str());
+
+ return 0;
+}
+
+static int load_table()
+{
+ UtilTransactions clear(* g_table);
+ require(!clear.clearTable(g_ndb));
+
+ HugoOperations ops(* g_table);
+ require(!ops.startTransaction(g_ndb));
+ for(size_t i = 0; i<g_rows; i++){
+ g_cases[i] = g_ops[ i % OP_COUNT];
+ if(g_cases[i].start_row){
+ g_cases[i].curr_row = true;
+ g_cases[i].val = rand();
+ require(!ops.pkInsertRecord(g_ndb, i, 1, g_cases[i].val));
+ }
+ if((i+1) % 100 == 0){
+ require(!ops.execute_Commit(g_ndb));
+ require(!ops.getTransaction()->restart());
+ }
+ }
+ if((g_rows+1) % 100 != 0)
+ require(!ops.execute_Commit(g_ndb));
+ return 0;
+}
+
+static int pause_lcp()
+{
+ return 0;
+}
+
+static int do_op(int row)
+{
+ HugoOperations & ops = * g_hugo_ops;
+ if(strcmp(g_cases[row].op1, "INSERT") == 0){
+ require(!g_cases[row].curr_row);
+ g_cases[row].curr_row = true;
+ g_cases[row].val = rand();
+ require(!ops.pkInsertRecord(g_ndb, row, 1, g_cases[row].val));
+ } else if(strcmp(g_cases[row].op1, "UPDATE") == 0){
+ require(g_cases[row].curr_row);
+ g_cases[row].val = rand();
+ require(!ops.pkUpdateRecord(g_ndb, row, 1, g_cases[row].val));
+ } else if(strcmp(g_cases[row].op1, "DELETE") == 0){
+ require(g_cases[row].curr_row);
+ g_cases[row].curr_row = false;
+ require(!ops.pkDeleteRecord(g_ndb, row, 1));
+ }
+
+ require(!ops.execute_NoCommit(g_ndb));
+
+ if(g_cases[row].op2 == 0){
+ } else if(strcmp(g_cases[row].op2, "INSERT") == 0){
+ require(!g_cases[row].curr_row);
+ g_cases[row].curr_row = true;
+ g_cases[row].val = rand();
+ require(!ops.pkInsertRecord(g_ndb, row, 1, g_cases[row].val));
+ } else if(strcmp(g_cases[row].op2, "UPDATE") == 0){
+ require(g_cases[row].curr_row);
+ g_cases[row].val = rand();
+ require(!ops.pkUpdateRecord(g_ndb, row, 1, g_cases[row].val));
+ } else if(strcmp(g_cases[row].op2, "DELETE") == 0){
+ require(g_cases[row].curr_row);
+ g_cases[row].curr_row = false;
+ require(!ops.pkDeleteRecord(g_ndb, row, 1));
+ }
+
+ if(g_cases[row].op2 != 0)
+ require(!ops.execute_NoCommit(g_ndb));
+ return 0;
+}
+
+static int continue_lcp(int error)
+{
+ error = 0;
+ if(g_restarter.insertErrorInAllNodes(error) == 0){
+ int args[] = { DumpStateOrd::DihStartLcpImmediately };
+ return g_restarter.dumpStateAllNodes(args, 1);
+ }
+ return -1;
+}
+
+static int commit()
+{
+ HugoOperations & ops = * g_hugo_ops;
+ int res = ops.execute_Commit(g_ndb);
+ if(res == 0){
+ return ops.getTransaction()->restart();
+ }
+ return res;
+}
+
+static int restart()
+{
+ g_info << "Restarting cluster" << endl;
+ disconnect_ndb();
+ delete g_hugo_ops;
+
+ require(!g_restarter.restartAll());
+ require(!g_restarter.waitClusterStarted(30));
+ require(!connect_ndb());
+
+ g_table = g_ndb->getDictionary()->getTable(g_tablename);
+ require(g_table);
+ require(g_hugo_ops = new HugoOperations(* g_table));
+ require(!g_hugo_ops->startTransaction(g_ndb));
+ return 0;
+}
+
+static int validate()
+{
+ HugoOperations ops(* g_table);
+ for(size_t i = 0; i<g_rows; i++){
+ require(g_cases[i].curr_row == g_cases[i].end_row);
+ require(!ops.startTransaction(g_ndb));
+ ops.pkReadRecord(g_ndb, i, 1);
+ int res = ops.execute_Commit(g_ndb);
+ if(g_cases[i].curr_row){
+ require(res == 0 && ops.verifyUpdatesValue(g_cases[i].val) == 0);
+ } else {
+ require(res == 626);
+ }
+ ops.closeTransaction(g_ndb);
+ }
+ return 0;
+}
+
diff --git a/storage/ndb/test/ndbapi/testMgm.cpp b/storage/ndb/test/ndbapi/testMgm.cpp
new file mode 100644
index 00000000000..ef653d3f972
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testMgm.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 <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <Vector.hpp>
+#include <random.h>
+
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable2(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+
+int create_index_on_pk(Ndb* pNdb, const char* tabName){
+ int result = NDBT_OK;
+
+ const NdbDictionary::Table * tab = NDBT_Table::discoverTableFromDb(pNdb,
+ tabName);
+
+ // Create index
+ const char* idxName = "IDX_ON_PK";
+ ndbout << "Create: " <<idxName << "( ";
+ NdbDictionary::Index pIdx(idxName);
+ pIdx.setTable(tabName);
+ pIdx.setType(NdbDictionary::Index::UniqueHashIndex);
+ for (int c = 0; c< tab->getNoOfPrimaryKeys(); c++){
+ pIdx.addIndexColumn(tab->getPrimaryKey(c));
+ ndbout << tab->getPrimaryKey(c)<<" ";
+ }
+
+ ndbout << ") ";
+ if (pNdb->getDictionary()->createIndex(pIdx) != 0){
+ ndbout << "FAILED!" << endl;
+ const NdbError err = pNdb->getDictionary()->getNdbError();
+ ERR(err);
+ result = NDBT_FAILED;
+ } else {
+ ndbout << "OK!" << endl;
+ }
+ return result;
+}
+
+int drop_index_on_pk(Ndb* pNdb, const char* tabName){
+ int result = NDBT_OK;
+ const char* idxName = "IDX_ON_PK";
+ ndbout << "Drop: " << idxName;
+ if (pNdb->getDictionary()->dropIndex(idxName, tabName) != 0){
+ ndbout << "FAILED!" << endl;
+ const NdbError err = pNdb->getDictionary()->getNdbError();
+ ERR(err);
+ result = NDBT_FAILED;
+ } else {
+ ndbout << "OK!" << endl;
+ }
+ return result;
+}
+
+
+#define CHECK(b) if (!(b)) { \
+ g_err << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ continue; }
+
+int runTestSingleUserMode(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ Ndb* pNdb = GETNDB(step);
+ NdbRestarter restarter;
+ char tabName[255];
+ strncpy(tabName, ctx->getTab()->getName(), 255);
+ ndbout << "tabName="<<tabName<<endl;
+
+ int i = 0;
+ int count;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ UtilTransactions utilTrans(*ctx->getTab());
+ while (i<loops && result == NDBT_OK) {
+ g_info << i << ": ";
+ int timeout = 120;
+ // Test that the single user mode api can do everything
+ CHECK(restarter.enterSingleUserMode(pNdb->getNodeId()) == 0);
+ CHECK(restarter.waitClusterSingleUser(timeout) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records, 128) == 0);
+ CHECK(hugoTrans.pkReadRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+ CHECK(hugoTrans.scanReadRecords(pNdb, records/2, 0, 64) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == (records/2));
+ CHECK(utilTrans.clearTable(pNdb, records/2) == 0);
+ CHECK(restarter.exitSingleUserMode() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+
+ // Test create index in single user mode
+ CHECK(restarter.enterSingleUserMode(pNdb->getNodeId()) == 0);
+ CHECK(restarter.waitClusterSingleUser(timeout) == 0);
+ CHECK(create_index_on_pk(pNdb, tabName) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records, 128) == 0);
+ CHECK(hugoTrans.pkReadRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+ CHECK(drop_index_on_pk(pNdb, tabName) == 0);
+ CHECK(restarter.exitSingleUserMode() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+
+ // Test recreate index in single user mode
+ CHECK(create_index_on_pk(pNdb, tabName) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records, 128) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(restarter.enterSingleUserMode(pNdb->getNodeId()) == 0);
+ CHECK(restarter.waitClusterSingleUser(timeout) == 0);
+ CHECK(drop_index_on_pk(pNdb, tabName) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(create_index_on_pk(pNdb, tabName) == 0);
+ CHECK(restarter.exitSingleUserMode() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(drop_index_on_pk(pNdb, tabName) == 0);
+
+ CHECK(utilTrans.clearTable(GETNDB(step), records) == 0);
+
+ ndbout << "Restarting cluster" << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ i++;
+
+ }
+ return result;
+}
+
+
+
+NDBT_TESTSUITE(testMgm);
+TESTCASE("SingleUserMode",
+ "Test single user mode"){
+ INITIALIZER(runTestSingleUserMode);
+ FINALIZER(runClearTable);
+}
+NDBT_TESTSUITE_END(testMgm);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ return testMgm.execute(argc, argv);
+}
+
diff --git a/storage/ndb/test/ndbapi/testNdbApi.cpp b/storage/ndb/test/ndbapi/testNdbApi.cpp
new file mode 100644
index 00000000000..4867ea11a9a
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testNdbApi.cpp
@@ -0,0 +1,1012 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <NdbRestarts.hpp>
+#include <Vector.hpp>
+#include <random.h>
+#include <NdbTick.h>
+
+#define MAX_NDB_OBJECTS 32678
+
+#define CHECK(b) if (!(b)) { \
+ ndbout << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ continue; }
+
+#define CHECKE(b) if (!(b)) { \
+ errors++; \
+ ndbout << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ continue; }
+
+
+int runTestMaxNdb(NDBT_Context* ctx, NDBT_Step* step){
+ Uint32 loops = ctx->getNumLoops();
+ Uint32 l = 0;
+ int oldi = 0;
+ int result = NDBT_OK;
+
+ while (l < loops && result == NDBT_OK){
+ ndbout_c("loop %d", l + 1);
+ int errors = 0;
+ int maxErrors = 5;
+
+ Vector<Ndb*> ndbVector;
+ int i = 0;
+ int init = 0;
+ do {
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ errors++;
+ continue;
+
+ }
+ i++;
+
+ ndbVector.push_back(pNdb);
+
+ if (pNdb->init()){
+ ERR(pNdb->getNdbError());
+ errors++;
+ continue;
+ }
+
+ init++;
+
+ } while (errors == 0);
+
+ ndbout << i << " ndb objects created" << endl;
+
+ if (l > 0 && i != oldi && init != MAX_NDB_OBJECTS){
+ ndbout << l << ": not as manyNdb objects created" << endl
+ << i << " != " << oldi << endl;
+ result = NDBT_FAILED;
+ }
+
+ oldi = i;
+
+
+ for(size_t j = 0; j < ndbVector.size(); j++){
+ delete ndbVector[j];
+ if(((j+1) % 250) == 0){
+ ndbout << "Deleted " << (Uint64) j << " ndb objects " << endl;
+ }
+ }
+ ndbVector.clear();
+
+ l++;
+ }
+
+ return result;
+}
+
+int runTestMaxTransaction(NDBT_Context* ctx, NDBT_Step* step){
+ Uint32 loops = ctx->getNumLoops();
+ Uint32 l = 0;
+ int oldi = 0;
+ int result = NDBT_OK;
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ return NDBT_FAILED;
+ }
+ if (pNdb->init(2048)){
+ ERR(pNdb->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ if (pTab == 0) abort();
+
+ while (l < loops && result == NDBT_OK){
+ int errors = 0;
+ int maxErrors = 5;
+
+ Vector<NdbConnection*> conVector;
+
+
+ int i = 0;
+ do {
+
+ NdbConnection* pCon;
+
+ int type = i%2;
+ switch (type){
+ case 0:
+ pCon = pNdb->startTransaction();
+ break;
+ case 1:
+ {
+ BaseString key;
+ key.appfmt("DATA-%d", i);
+ ndbout_c("%s", key.c_str());
+ pCon = pNdb->startTransaction(pTab,
+ key.c_str(),
+ key.length());
+ }
+ break;
+ default:
+ abort();
+ }
+
+ if (pCon == NULL){
+ ERR(pNdb->getNdbError());
+ errors++;
+ continue;
+ }
+
+ conVector.push_back(pCon);
+
+ i++;
+ } while (errors < maxErrors);
+
+ ndbout << i << " connections created" << endl;
+
+ if (l > 0 && i != oldi){
+ ndbout << l << ": not as many transactions created" << endl
+ << i << " != " << oldi << endl;
+ result = NDBT_FAILED;
+ }
+
+ oldi = i;
+
+
+ for(size_t j = 0; j < conVector.size(); j++){
+ pNdb->closeTransaction(conVector[j]);
+ }
+ conVector.clear();
+ l++;
+
+ }
+
+ // BONUS Test closeTransaction with null trans
+ pNdb->closeTransaction(NULL);
+
+ delete pNdb;
+
+
+ return result;
+}
+
+int runTestMaxOperations(NDBT_Context* ctx, NDBT_Step* step){
+ Uint32 l = 1;
+ int result = NDBT_OK;
+ int maxOpsLimit = 1;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ return NDBT_FAILED;
+ }
+ if (pNdb->init(2048)){
+ ERR(pNdb->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ HugoOperations hugoOps(*pTab);
+
+ bool endTest = false;
+ while (!endTest && result == NDBT_OK){
+ int errors = 0;
+ int maxErrors = 5;
+
+ maxOpsLimit = l*1000;
+
+ if (hugoOps.startTransaction(pNdb) != NDBT_OK){
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ int i = 0;
+ while (errors < maxErrors){
+
+ if(hugoOps.pkReadRecord(pNdb,1, 1) != NDBT_OK){
+ errors++;
+ continue;
+ }
+
+ i++;
+
+ if (i >= maxOpsLimit){
+ errors = maxErrors;
+ }
+
+ }
+
+ ndbout << i << " operations used" << endl;
+
+ int execResult = hugoOps.execute_Commit(pNdb);
+ switch(execResult){
+ case NDBT_OK:
+ break;
+ case 233: // Out of operation records in transaction coordinator
+ // OK - end test
+ endTest = true;
+ break;
+ default:
+ result = NDBT_FAILED;
+ break;
+ }
+
+ hugoOps.closeTransaction(pNdb);
+
+ l++;
+
+ }
+
+ delete pNdb;
+
+ return result;
+}
+
+int runTestGetValue(NDBT_Context* ctx, NDBT_Step* step){
+
+ int result = NDBT_OK;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ return NDBT_FAILED;
+ }
+ if (pNdb->init(2048)){
+ ERR(pNdb->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ HugoOperations hugoOps(*pTab);
+
+ for (int m = 1; m < 100; m++){
+ int errors = 0;
+ int maxErrors = 5;
+
+ NdbConnection* pCon = pNdb->startTransaction();
+ if (pCon == NULL){
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
+ if (pOp == NULL){
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ if (pOp->readTuple() != 0){
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ for(int a = 0; a<pTab->getNoOfColumns(); a++){
+ if (pTab->getColumn(a)->getPrimaryKey() == true){
+ if(hugoOps.equalForAttr(pOp, a, 1) != 0){
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ int i = 0;
+ int maxLimit = 1000*m;
+ do {
+
+ if (pOp->getValue(pTab->getColumn(1)->getName()) == NULL) {
+ const NdbError err = pCon->getNdbError();
+ ERR(err);
+ if (err.code == 0)
+ result = NDBT_FAILED;
+ errors++;
+ continue;
+ }
+
+ i++;
+
+ } while (errors < maxErrors && i < maxLimit);
+
+ ndbout << i << " getValues called" << endl;
+
+
+ if (pCon->execute(Commit) != 0){
+ const NdbError err = pCon->getNdbError();
+ switch(err.code){
+ case 880: // TUP - Read too much
+ case 823: // TUP - Too much AI
+ case 4257: // NDBAPI - Too much AI
+ // OK errors
+ ERR(pCon->getNdbError());
+ break;
+ default:
+ ERR(pCon->getNdbError());
+ ndbout << "Illegal error" << endl;
+ result= NDBT_FAILED;
+ break;
+ }
+ }
+
+ pNdb->closeTransaction(pCon);
+
+ }// m
+
+
+ delete pNdb;
+
+ return result;
+}
+
+int runTestEqual(NDBT_Context* ctx, NDBT_Step* step){
+ Uint32 loops = ctx->getNumLoops();
+ Uint32 l = 0;
+ int result = NDBT_OK;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ return NDBT_FAILED;
+ }
+ if (pNdb->init(2048)){
+ ERR(pNdb->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ HugoOperations hugoOps(*pTab);
+
+ while (l < loops){
+ for(int m = 1; m < 10; m++){
+ int errors = 0;
+ int maxErrors = 5;
+
+ NdbConnection* pCon = pNdb->startTransaction();
+ if (pCon == NULL){
+ ndbout << "Could not start transaction" << endl;
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
+ if (pOp == NULL){
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ if (pOp->readTuple() != 0){
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ int i = 0;
+ int maxLimit = 1000*m;
+ do {
+
+ if ((l%2)!=0){
+ // Forward
+ for(int a = 0; a<pTab->getNoOfColumns(); a++){
+ if (pTab->getColumn(a)->getPrimaryKey() == true){
+ if(hugoOps.equalForAttr(pOp, a, 1) != 0){
+ const NdbError err = pCon->getNdbError();
+ ERR(err);
+ if (err.code == 0)
+ result = NDBT_FAILED;
+ errors++;
+ }
+ }
+ }
+ } else {
+ // Backward
+ for(int a = pTab->getNoOfColumns()-1; a>=0; a--){
+ if (pTab->getColumn(a)->getPrimaryKey() == true){
+ if(hugoOps.equalForAttr(pOp, a, 1) != 0){
+ const NdbError err = pCon->getNdbError();
+ ERR(err);
+ if (err.code == 0)
+ result = NDBT_FAILED;
+ errors++;
+ }
+ }
+ }
+ }
+
+ i++;
+
+ } while (errors < maxErrors && i < maxLimit);
+
+ if (pOp->getValue(pTab->getColumn(1)->getName()) == NULL) {
+ const NdbError err = pCon->getNdbError();
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ if (err.code == 4225) {
+ return NDBT_OK;
+ } else {
+ return NDBT_FAILED;
+ }//if
+ }
+
+ ndbout << i << " equal called" << endl;
+
+
+ int check = pCon->execute(Commit);
+ if (check != 0){
+ ERR(pCon->getNdbError());
+ }
+
+ pNdb->closeTransaction(pCon);
+
+ }// m
+ l++;
+
+ }// l
+
+ delete pNdb;
+ return result;
+}
+
+int runTestDeleteNdb(NDBT_Context* ctx, NDBT_Step* step){
+ Uint32 loops = ctx->getNumLoops();
+ Uint32 l = 0;
+ int result = NDBT_OK;
+ NdbRestarts restarts;
+ Vector<Ndb*> ndbVector;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ HugoTransactions hugoTrans(*pTab);
+ int records = ctx->getNumRecords();
+
+ while (l < loops && result == NDBT_OK){
+
+ // Create 5 ndb objects
+ for( int i = 0; i < 5; i++){
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ result = NDBT_FAILED;
+ goto end_test;
+ }
+ ndbVector.push_back(pNdb);
+
+ if (pNdb->init()){
+ ERR(pNdb->getNdbError());
+ result = NDBT_FAILED;
+ goto end_test;
+ }
+ if (pNdb->waitUntilReady() != 0){
+ ERR(pNdb->getNdbError());
+ result = NDBT_FAILED;
+ goto end_test;
+ }
+ if (hugoTrans.pkReadRecords(pNdb, records) != 0){
+ result = NDBT_FAILED;
+ goto end_test;
+ }
+ }
+
+ if ((l % 2) == 0){
+ // Restart random node
+ ndbout << "Restart random node " << endl;
+ if(restarts.executeRestart("RestartRandomNodeAbort", 120) != 0){
+ g_err << "Failed to executeRestart(RestartRandomNode)"<<endl;
+ result = NDBT_FAILED;
+ goto end_test;
+ }
+ } else {
+ // Restart all nodes
+ ndbout << "Restart all nodes " << endl;
+ if(restarts.executeRestart("RestartAllNodesAbort", 120) != 0){
+ g_err << "Failed to executeRestart(RestartAllNodes)"<<endl;
+ result = NDBT_FAILED;
+ goto end_test;
+ }
+ }
+
+ // Delete the ndb objects
+ for(size_t j = 0; j < ndbVector.size(); j++)
+ delete ndbVector[j];
+ ndbVector.clear();
+ l++;
+ }
+
+
+ end_test:
+
+ for(size_t i = 0; i < ndbVector.size(); i++)
+ delete ndbVector[i];
+ ndbVector.clear();
+
+ return result;
+}
+
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable2(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runTestWaitUntilReady(NDBT_Context* ctx, NDBT_Step* step){
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+
+ // Forget about calling pNdb->init();
+
+ if (pNdb->waitUntilReady() == 0){
+ ndbout << "waitUntilReady returned OK" << endl;
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+ const NdbError err = pNdb->getNdbError();
+ delete pNdb;
+
+ ERR(err);
+ if (err.code != 4256)
+ return NDBT_FAILED;
+
+ return NDBT_OK;
+}
+
+int runGetNdbOperationNoTab(NDBT_Context* ctx, NDBT_Step* step){
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ return NDBT_FAILED;
+ }
+ if (pNdb->init()){
+ ERR(pNdb->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbConnection* pCon = pNdb->startTransaction();
+ if (pCon == NULL){
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ // Call getNdbOperation on an unknown table
+ NdbOperation* pOp = pCon->getNdbOperation("HUPP76");
+ if (pOp == NULL){
+ NdbError err = pCon->getNdbError();
+ ERR(err);
+ if (err.code == 0){
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+ }
+
+ pNdb->closeTransaction(pCon);
+
+ delete pNdb;
+
+ return NDBT_OK;
+}
+
+int runMissingOperation(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ return NDBT_FAILED;
+ }
+ if (pNdb->init()){
+ ERR(pNdb->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbConnection* pCon = pNdb->startTransaction();
+ if (pCon == NULL){
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
+ if (pOp == NULL){
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ // Forget about calling pOp->insertTuple();
+
+ // Call getValue should not work
+ if (pOp->getValue(pTab->getColumn(1)->getName()) == NULL) {
+ const NdbError err = pCon->getNdbError();
+ ERR(err);
+ if (err.code == 0){
+ ndbout << "hupp" << endl;
+ result = NDBT_FAILED;
+ }
+ } else {
+ ndbout << "hupp2" << endl;
+ result = NDBT_FAILED;
+ }
+
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+
+ return result;
+}
+
+int runGetValueInUpdate(NDBT_Context* ctx, NDBT_Step* step){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ return NDBT_FAILED;
+ }
+ if (pNdb->init()){
+ ERR(pNdb->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbConnection* pCon = pNdb->startTransaction();
+ if (pCon == NULL){
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
+ if (pOp == NULL){
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ if (pOp->updateTuple() != 0){
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ // Call getValue should not work
+ if (pOp->getValue(pTab->getColumn(1)->getName()) == NULL) {
+ // It didn't work
+ const NdbError err = pCon->getNdbError();
+ ERR(err);
+ if (err.code == 0){
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+ } else {
+ // It worked, not good!
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ int check = pCon->execute(Commit);
+ if (check != 0){
+ ERR(pCon->getNdbError());
+ }
+
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+
+ return NDBT_OK;
+}
+
+int runUpdateWithoutValues(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ HugoOperations hugoOps(*pTab);
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ return NDBT_FAILED;
+ }
+ if (pNdb->init()){
+ ERR(pNdb->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbConnection* pCon = pNdb->startTransaction();
+ if (pCon == NULL){
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
+ if (pOp == NULL){
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ if (pOp->updateTuple() != 0){
+ pNdb->closeTransaction(pCon);
+ ERR(pOp->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ for(int a = 0; a<pTab->getNoOfColumns(); a++){
+ if (pTab->getColumn(a)->getPrimaryKey() == true){
+ if(hugoOps.equalForAttr(pOp, a, 1) != 0){
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ // Dont' call any setValues
+
+ // Execute should work
+ int check = pCon->execute(Commit);
+ if (check == 0){
+ ndbout << "execute worked" << endl;
+ } else {
+ ERR(pCon->getNdbError());
+ result = NDBT_FAILED;
+ }
+
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+
+ return result;
+}
+
+int runUpdateWithoutKeys(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ return NDBT_FAILED;
+ }
+ if (pNdb->init()){
+ ERR(pNdb->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbConnection* pCon = pNdb->startTransaction();
+ if (pCon == NULL){
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
+ if (pOp == NULL){
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ if (pOp->updateTuple() != 0){
+ pNdb->closeTransaction(pCon);
+ ERR(pOp->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ // Dont' call any equal or setValues
+
+ // Execute should not work
+ int check = pCon->execute(Commit);
+ if (check == 0){
+ ndbout << "execute worked" << endl;
+ result = NDBT_FAILED;
+ } else {
+ ERR(pCon->getNdbError());
+ }
+
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+
+ return result;
+}
+
+int runCheckGetNdbErrorOperation(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ return NDBT_FAILED;
+ }
+ if (pNdb->init(2048)){
+ ERR(pNdb->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ HugoOperations hugoOps(*pTab);
+
+
+ NdbConnection* pCon = pNdb->startTransaction();
+ if (pCon == NULL){
+ ndbout << "Could not start transaction" << endl;
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbOperation* pOp = pCon->getNdbOperation(pTab->getName());
+ if (pOp == NULL){
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ // Dont call readTuple here
+ // That's the error!
+
+ for(int a = 0; a<pTab->getNoOfColumns(); a++){
+ if (pTab->getColumn(a)->getPrimaryKey() == true){
+ if(hugoOps.equalForAttr(pOp, a, 1) != 0){
+ // An error has occured, check that
+ // it's possible to get the NdbErrorOperation
+ const NdbError err = pCon->getNdbError();
+ ERR(err);
+ if (err.code == 0)
+ result = NDBT_FAILED;
+
+ NdbOperation* pOp2 = pCon->getNdbErrorOperation();
+ if (pOp2 == NULL)
+ result = NDBT_FAILED;
+ else {
+ const NdbError err2 = pOp2->getNdbError();
+ ERR(err2);
+ if (err.code == 0)
+ result = NDBT_FAILED;
+ }
+ }
+ }
+ }
+
+ pNdb->closeTransaction(pCon);
+
+ delete pNdb;
+ return result;
+}
+
+
+NDBT_TESTSUITE(testNdbApi);
+TESTCASE("MaxNdb",
+ "Create Ndb objects until no more can be created\n"){
+ INITIALIZER(runTestMaxNdb);
+}
+TESTCASE("MaxTransactions",
+ "Start transactions until no more can be created\n"){
+ INITIALIZER(runTestMaxTransaction);
+}
+TESTCASE("MaxOperations",
+ "Get operations until no more can be created\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runTestMaxOperations);
+ FINALIZER(runClearTable);
+}
+TESTCASE("MaxGetValue",
+ "Call getValue loads of time\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runTestGetValue);
+ FINALIZER(runClearTable);
+}
+TESTCASE("MaxEqual",
+ "Call equal loads of time\n"){
+ INITIALIZER(runTestEqual);
+}
+TESTCASE("DeleteNdb",
+ "Make sure that a deleted Ndb object is properly deleted\n"
+ "and removed from transporter\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runTestDeleteNdb);
+ FINALIZER(runClearTable);
+}
+TESTCASE("WaitUntilReady",
+ "Make sure you get an error message when calling waitUntilReady\n"
+ "without an init'ed Ndb\n"){
+ INITIALIZER(runTestWaitUntilReady);
+}
+TESTCASE("GetOperationNoTab",
+ "Call getNdbOperation on a table that does not exist\n"){
+ INITIALIZER(runGetNdbOperationNoTab);
+}
+TESTCASE("MissingOperation",
+ "Missing operation request(insertTuple) should give an error code\n"){
+ INITIALIZER(runMissingOperation);
+}
+TESTCASE("GetValueInUpdate",
+ "Test that it's not possible to perform getValue in an update\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runGetValueInUpdate);
+ FINALIZER(runClearTable);
+}
+TESTCASE("UpdateWithoutKeys",
+ "Test that it's not possible to perform update without setting\n"
+ "PKs"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runUpdateWithoutKeys);
+ FINALIZER(runClearTable);
+}
+TESTCASE("UpdateWithoutValues",
+ "Test that it's not possible to perform update without setValues\n"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runUpdateWithoutValues);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NdbErrorOperation",
+ "Test that NdbErrorOperation is properly set"){
+ INITIALIZER(runCheckGetNdbErrorOperation);
+}
+NDBT_TESTSUITE_END(testNdbApi);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ // TABLE("T1");
+ return testNdbApi.execute(argc, argv);
+}
+
+template class Vector<Ndb*>;
+template class Vector<NdbConnection*>;
diff --git a/storage/ndb/test/ndbapi/testNodeRestart.cpp b/storage/ndb/test/ndbapi/testNodeRestart.cpp
new file mode 100644
index 00000000000..1ce934a19ca
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testNodeRestart.cpp
@@ -0,0 +1,688 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <NdbRestarts.hpp>
+#include <Vector.hpp>
+
+
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runFillTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.fillTable(GETNDB(step)) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runInsertUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int records = ctx->getNumRecords();
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ g_info << i << ": ";
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return result;
+}
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runClearTableUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int i = 0;
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ g_info << i << ": ";
+ if (utilTrans.clearTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runScanReadUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int records = ctx->getNumRecords();
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ g_info << i << ": ";
+ if (hugoTrans.scanReadRecords(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return result;
+}
+
+int runPkReadUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int records = ctx->getNumRecords();
+ NdbOperation::LockMode lm =
+ (NdbOperation::LockMode)ctx->getProperty("ReadLockMode",
+ (Uint32)NdbOperation::LM_Read);
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ g_info << i << ": ";
+ int rows = (rand()%records)+1;
+ int batch = (rand()%rows)+1;
+ if (hugoTrans.pkReadRecords(GETNDB(step), rows, batch, lm) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return result;
+}
+
+int runPkUpdateUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int records = ctx->getNumRecords();
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ g_info << i << ": ";
+ int rows = (rand()%records)+1;
+ int batch = (rand()%rows)+1;
+ if (hugoTrans.pkUpdateRecords(GETNDB(step), rows, batch) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return result;
+}
+
+int runPkReadPkUpdateUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int records = ctx->getNumRecords();
+ Ndb* pNdb = GETNDB(step);
+ int i = 0;
+ HugoOperations hugoOps(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ g_info << i++ << ": ";
+ int rows = (rand()%records)+1;
+ int batch = (rand()%rows)+1;
+ int row = (records - rows) ? rand() % (records - rows) : 0;
+
+ int j,k;
+ for(j = 0; j<rows; j += batch)
+ {
+ k = batch;
+ if(j+k > rows)
+ k = rows - j;
+
+ if(hugoOps.startTransaction(pNdb) != 0)
+ goto err;
+
+ if(hugoOps.pkReadRecord(pNdb, row+j, k, NdbOperation::LM_Exclusive) != 0)
+ goto err;
+
+ if(hugoOps.execute_NoCommit(pNdb) != 0)
+ goto err;
+
+ if(hugoOps.pkUpdateRecord(pNdb, row+j, k, rand()) != 0)
+ goto err;
+
+ if(hugoOps.execute_Commit(pNdb) != 0)
+ goto err;
+
+ if(hugoOps.closeTransaction(pNdb) != 0)
+ return NDBT_FAILED;
+ }
+
+ continue;
+err:
+ NdbConnection* pCon = hugoOps.getTransaction();
+ if(pCon == 0)
+ continue;
+ NdbError error = pCon->getNdbError();
+ hugoOps.closeTransaction(pNdb);
+ if (error.status == NdbError::TemporaryError){
+ NdbSleep_MilliSleep(50);
+ continue;
+ }
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runScanUpdateUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int records = ctx->getNumRecords();
+ int parallelism = ctx->getProperty("Parallelism", 1);
+ int abort = ctx->getProperty("AbortProb", (Uint32)0);
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ g_info << i << ": ";
+ if (hugoTrans.scanUpdateRecords(GETNDB(step), records, abort,
+ parallelism) == NDBT_FAILED){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return result;
+}
+
+int runScanReadVerify(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ if (hugoTrans.scanReadRecords(GETNDB(step), records, 0, 64) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runRestarter(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int sync_threads = ctx->getProperty("SyncThreads", (unsigned)0);
+ NdbRestarter restarter;
+ int i = 0;
+ int lastId = 0;
+
+ if (restarter.getNumDbNodes() < 2){
+ ctx->stopTest();
+ return NDBT_OK;
+ }
+
+ if(restarter.waitClusterStarted(60) != 0){
+ g_err << "Cluster failed to start" << endl;
+ return NDBT_FAILED;
+ }
+
+ loops *= restarter.getNumDbNodes();
+ while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){
+
+ int id = lastId % restarter.getNumDbNodes();
+ int nodeId = restarter.getDbNodeId(id);
+ ndbout << "Restart node " << nodeId << endl;
+ if(restarter.restartOneDbNode(nodeId, false, false, true) != 0){
+ g_err << "Failed to restartNextDbNode" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+
+ if(restarter.waitClusterStarted(60) != 0){
+ g_err << "Cluster failed to start" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+
+ ctx->sync_up_and_wait("PauseThreads", sync_threads);
+
+ lastId++;
+ i++;
+ }
+
+ ctx->stopTest();
+
+ return result;
+}
+
+int runCheckAllNodesStarted(NDBT_Context* ctx, NDBT_Step* step){
+ NdbRestarter restarter;
+
+ if(restarter.waitClusterStarted(1) != 0){
+ g_err << "All nodes was not started " << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+
+
+int runRestarts(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ NDBT_TestCase* pCase = ctx->getCase();
+ NdbRestarts restarts;
+ int i = 0;
+ int timeout = 240;
+
+ while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){
+
+ if(restarts.executeRestart(pCase->getName(), timeout) != 0){
+ g_err << "Failed to executeRestart(" <<pCase->getName() <<")" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+ i++;
+ }
+ return result;
+}
+
+int runDirtyRead(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ NdbRestarter restarter;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ int i = 0;
+ while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){
+ g_info << i << ": ";
+
+ int id = i % restarter.getNumDbNodes();
+ int nodeId = restarter.getDbNodeId(id);
+ ndbout << "Restart node " << nodeId << endl;
+ restarter.insertErrorInNode(nodeId, 5041);
+ restarter.insertErrorInAllNodes(8048 + (i & 1));
+
+ for(int j = 0; j<records; j++){
+ if(hugoOps.startTransaction(pNdb) != 0)
+ return NDBT_FAILED;
+
+ if(hugoOps.pkReadRecord(pNdb, j, 1, NdbOperation::LM_CommittedRead) != 0)
+ goto err;
+
+ int res;
+ if((res = hugoOps.execute_Commit(pNdb)) == 4119)
+ goto done;
+
+ if(res != 0)
+ goto err;
+
+ if(hugoOps.closeTransaction(pNdb) != 0)
+ return NDBT_FAILED;
+ }
+done:
+ if(hugoOps.closeTransaction(pNdb) != 0)
+ return NDBT_FAILED;
+
+ i++;
+ restarter.waitClusterStarted(60) ;
+ }
+ return result;
+err:
+ hugoOps.closeTransaction(pNdb);
+ return NDBT_FAILED;
+}
+
+int runLateCommit(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ NdbRestarter restarter;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ int i = 0;
+ while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){
+ g_info << i << ": ";
+
+ if(hugoOps.startTransaction(pNdb) != 0)
+ return NDBT_FAILED;
+
+ if(hugoOps.pkUpdateRecord(pNdb, 1) != 0)
+ return NDBT_FAILED;
+
+ if(hugoOps.execute_NoCommit(pNdb) != 0)
+ return NDBT_FAILED;
+
+ Uint32 transNode= hugoOps.getTransaction()->getConnectedNodeId();
+ int id = i % restarter.getNumDbNodes();
+ int nodeId;
+ while((nodeId = restarter.getDbNodeId(id)) == transNode)
+ id = (id + 1) % restarter.getNumDbNodes();
+
+ ndbout << "Restart node " << nodeId << endl;
+
+ restarter.restartOneDbNode(nodeId,
+ /** initial */ false,
+ /** nostart */ true,
+ /** abort */ true);
+
+ restarter.waitNodesNoStart(&nodeId, 1);
+
+ int res;
+ if(i & 1)
+ res= hugoOps.execute_Commit(pNdb);
+ else
+ res= hugoOps.execute_Rollback(pNdb);
+
+ ndbout_c("res= %d", res);
+
+ hugoOps.closeTransaction(pNdb);
+
+ restarter.startNodes(&nodeId, 1);
+ restarter.waitNodesStarted(&nodeId, 1);
+
+ if(i & 1)
+ {
+ if(res != 286)
+ return NDBT_FAILED;
+ }
+ else
+ {
+ if(res != 0)
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+
+ return NDBT_OK;
+}
+
+NDBT_TESTSUITE(testNodeRestart);
+TESTCASE("NoLoad",
+ "Test that one node at a time can be stopped and then restarted "\
+ "when there are no load on the system. Do this loop number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarter);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkRead",
+ "Test that one node at a time can be stopped and then restarted "\
+ "perform pk read while restarting. Do this loop number of times"){
+ TC_PROPERTY("ReadLockMode", NdbOperation::LM_Read);
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarter);
+ STEP(runPkReadUntilStopped);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkReadCommitted",
+ "Test that one node at a time can be stopped and then restarted "\
+ "perform pk read while restarting. Do this loop number of times"){
+ TC_PROPERTY("ReadLockMode", NdbOperation::LM_CommittedRead);
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarter);
+ STEP(runPkReadUntilStopped);
+ FINALIZER(runClearTable);
+}
+TESTCASE("MixedPkRead",
+ "Test that one node at a time can be stopped and then restarted "\
+ "perform pk read while restarting. Do this loop number of times"){
+ TC_PROPERTY("ReadLockMode", -1);
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarter);
+ STEP(runPkReadUntilStopped);
+ FINALIZER(runClearTable);
+}
+TESTCASE("PkReadPkUpdate",
+ "Test that one node at a time can be stopped and then restarted "\
+ "perform pk read and pk update while restarting. Do this loop number of times"){
+ TC_PROPERTY("ReadLockMode", NdbOperation::LM_Read);
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarter);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkUpdateUntilStopped);
+ STEP(runPkReadPkUpdateUntilStopped);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkUpdateUntilStopped);
+ STEP(runPkReadPkUpdateUntilStopped);
+ FINALIZER(runClearTable);
+}
+TESTCASE("MixedPkReadPkUpdate",
+ "Test that one node at a time can be stopped and then restarted "\
+ "perform pk read and pk update while restarting. Do this loop number of times"){
+ TC_PROPERTY("ReadLockMode", -1);
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarter);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkUpdateUntilStopped);
+ STEP(runPkReadPkUpdateUntilStopped);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkUpdateUntilStopped);
+ STEP(runPkReadPkUpdateUntilStopped);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ReadUpdateScan",
+ "Test that one node at a time can be stopped and then restarted "\
+ "perform pk read, pk update and scan reads while restarting. Do this loop number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarter);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkUpdateUntilStopped);
+ STEP(runPkReadPkUpdateUntilStopped);
+ STEP(runScanReadUntilStopped);
+ STEP(runScanUpdateUntilStopped);
+ FINALIZER(runClearTable);
+}
+TESTCASE("MixedReadUpdateScan",
+ "Test that one node at a time can be stopped and then restarted "\
+ "perform pk read, pk update and scan reads while restarting. Do this loop number of times"){
+ TC_PROPERTY("ReadLockMode", -1);
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarter);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkUpdateUntilStopped);
+ STEP(runPkReadPkUpdateUntilStopped);
+ STEP(runScanReadUntilStopped);
+ STEP(runScanUpdateUntilStopped);
+ FINALIZER(runClearTable);
+}
+TESTCASE("Terror",
+ "Test that one node at a time can be stopped and then restarted "\
+ "perform all kind of transactions while restarting. Do this loop number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarter);
+ STEP(runPkReadUntilStopped);
+ STEP(runPkUpdateUntilStopped);
+ STEP(runScanReadUntilStopped);
+ STEP(runScanUpdateUntilStopped);
+ FINALIZER(runClearTable);
+}
+TESTCASE("FullDb",
+ "Test that one node at a time can be stopped and then restarted "\
+ "when db is full. Do this loop number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runFillTable);
+ STEP(runRestarter);
+ FINALIZER(runClearTable);
+}
+TESTCASE("RestartRandomNode",
+ "Test that we can execute the restart RestartRandomNode loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("RestartRandomNodeError",
+ "Test that we can execute the restart RestartRandomNodeError loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("RestartRandomNodeInitial",
+ "Test that we can execute the restart RestartRandomNodeInitial loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("RestartNFDuringNR",
+ "Test that we can execute the restart RestartNFDuringNR loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("RestartMasterNodeError",
+ "Test that we can execute the restart RestartMasterNodeError loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+
+TESTCASE("TwoNodeFailure",
+ "Test that we can execute the restart TwoNodeFailure\n"\
+ "(which is a multiple node failure restart) loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("TwoMasterNodeFailure",
+ "Test that we can execute the restart TwoMasterNodeFailure\n"\
+ "(which is a multiple node failure restart) loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("FiftyPercentFail",
+ "Test that we can execute the restart FiftyPercentFail\n"\
+ "(which is a multiple node failure restart) loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("RestartAllNodes",
+ "Test that we can execute the restart RestartAllNodes\n"\
+ "(which is a system restart) loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("RestartAllNodesAbort",
+ "Test that we can execute the restart RestartAllNodesAbort\n"\
+ "(which is a system restart) loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("RestartAllNodesError9999",
+ "Test that we can execute the restart RestartAllNodesError9999\n"\
+ "(which is a system restart) loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("FiftyPercentStopAndWait",
+ "Test that we can execute the restart FiftyPercentStopAndWait\n"\
+ "(which is a system restart) loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("RestartNodeDuringLCP",
+ "Test that we can execute the restart RestartRandomNode loop\n"\
+ "number of times"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("StopOnError",
+ "Test StopOnError. A node that has StopOnError set to false "\
+ "should restart automatically when an error occurs"){
+ INITIALIZER(runCheckAllNodesStarted);
+ INITIALIZER(runLoadTable);
+ STEP(runRestarts);
+ FINALIZER(runScanReadVerify);
+ FINALIZER(runClearTable);
+}
+TESTCASE("CommittedRead",
+ "Test committed read"){
+ INITIALIZER(runLoadTable);
+ STEP(runDirtyRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("LateCommit",
+ "Test commit after node failure"){
+ INITIALIZER(runLoadTable);
+ STEP(runLateCommit);
+ FINALIZER(runClearTable);
+}
+NDBT_TESTSUITE_END(testNodeRestart);
+
+int main(int argc, const char** argv){
+ ndb_init();
+#if 0
+ // It might be interesting to have longer defaults for num
+ // loops in this test
+ // Just performing 100 node restarts would not be enough?
+ // We can have initialisers in the NDBT_Testcase class like
+ // this...
+ testNodeRestart.setDefaultLoops(1000);
+#endif
+ return testNodeRestart.execute(argc, argv);
+}
+
diff --git a/storage/ndb/test/ndbapi/testOIBasic.cpp b/storage/ndb/test/ndbapi/testOIBasic.cpp
new file mode 100644
index 00000000000..96926a421fb
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testOIBasic.cpp
@@ -0,0 +1,5027 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ * testOIBasic - ordered index test
+ */
+
+#include <ndb_global.h>
+
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NdbTest.hpp>
+#include <NdbMutex.h>
+#include <NdbCondition.h>
+#include <NdbThread.h>
+#include <NdbTick.h>
+#include <NdbSleep.h>
+#include <my_sys.h>
+#include <NdbSqlUtil.hpp>
+
+// options
+
+struct Opt {
+ // common options
+ unsigned m_batch;
+ const char* m_bound;
+ const char* m_case;
+ bool m_collsp;
+ bool m_core;
+ const char* m_csname;
+ CHARSET_INFO* m_cs;
+ int m_die;
+ bool m_dups;
+ NdbDictionary::Object::FragmentType m_fragtype;
+ unsigned m_subsubloop;
+ const char* m_index;
+ unsigned m_loop;
+ bool m_msglock;
+ bool m_nologging;
+ bool m_noverify;
+ unsigned m_pctnull;
+ unsigned m_rows;
+ unsigned m_samples;
+ unsigned m_scanbat;
+ unsigned m_scanpar;
+ unsigned m_scanstop;
+ int m_seed;
+ unsigned m_subloop;
+ const char* m_table;
+ unsigned m_threads;
+ int m_v; // int for lint
+ Opt() :
+ m_batch(32),
+ m_bound("01234"),
+ m_case(0),
+ m_collsp(false),
+ m_core(false),
+ m_csname("random"),
+ m_cs(0),
+ m_die(0),
+ m_dups(false),
+ m_fragtype(NdbDictionary::Object::FragUndefined),
+ m_subsubloop(4),
+ m_index(0),
+ m_loop(1),
+ m_msglock(true),
+ m_nologging(false),
+ m_noverify(false),
+ m_pctnull(10),
+ m_rows(1000),
+ m_samples(0),
+ m_scanbat(0),
+ m_scanpar(0),
+ m_scanstop(0),
+ m_seed(-1),
+ m_subloop(4),
+ m_table(0),
+ m_threads(4),
+ m_v(1) {
+ }
+};
+
+static Opt g_opt;
+
+static void printcases();
+static void printtables();
+
+static void
+printhelp()
+{
+ Opt d;
+ ndbout
+ << "usage: testOIbasic [options]" << endl
+ << " -batch N pk operations in batch [" << d.m_batch << "]" << endl
+ << " -bound xyz use only these bound types 0-4 [" << d.m_bound << "]" << endl
+ << " -case abc only given test cases (letters a-z)" << endl
+ << " -collsp use strnncollsp instead of strnxfrm" << endl
+ << " -core core dump on error [" << d.m_core << "]" << endl
+ << " -csname S charset or collation [" << d.m_csname << "]" << endl
+ << " -die nnn exit immediately on NDB error code nnn" << endl
+ << " -dups allow duplicate tuples from index scan [" << d.m_dups << "]" << endl
+ << " -fragtype T fragment type single/small/medium/large" << endl
+ << " -index xyz only given index numbers (digits 0-9)" << endl
+ << " -loop N loop count full suite 0=forever [" << d.m_loop << "]" << endl
+ << " -nologging create tables in no-logging mode" << endl
+ << " -noverify skip index verifications" << endl
+ << " -pctnull N pct NULL values in nullable column [" << d.m_pctnull << "]" << endl
+ << " -rows N rows per thread [" << d.m_rows << "]" << endl
+ << " -samples N samples for some timings (0=all) [" << d.m_samples << "]" << endl
+ << " -scanbat N scan batch per fragment (ignored by ndb api) [" << d.m_scanbat << "]" << endl
+ << " -scanpar N scan parallelism [" << d.m_scanpar << "]" << endl
+ << " -seed N srandom seed 0=loop number -1=random [" << d.m_seed << "]" << endl
+ << " -subloop N subtest loop count [" << d.m_subloop << "]" << endl
+ << " -table xyz only given table numbers (digits 0-9)" << endl
+ << " -threads N number of threads [" << d.m_threads << "]" << endl
+ << " -vN verbosity [" << d.m_v << "]" << endl
+ << " -h or -help print this help text" << endl
+ ;
+ printcases();
+ printtables();
+}
+
+// not yet configurable
+static const bool g_store_null_key = true;
+
+// compare NULL like normal value (NULL < not NULL, NULL == NULL)
+static const bool g_compare_null = true;
+
+static const char* hexstr = "0123456789abcdef";
+
+// random ints
+
+static unsigned
+urandom(unsigned n)
+{
+ if (n == 0)
+ return 0;
+ unsigned i = random() % n;
+ return i;
+}
+
+static int
+irandom(unsigned n)
+{
+ if (n == 0)
+ return 0;
+ int i = random() % n;
+ if (random() & 0x1)
+ i = -i;
+ return i;
+}
+
+// log and error macros
+
+static NdbMutex *ndbout_mutex = NULL;
+
+static unsigned getthrno();
+
+static const char*
+getthrstr()
+{
+ static char buf[20];
+ unsigned n = getthrno();
+ if (n == (unsigned)-1)
+ strcpy(buf, "");
+ else {
+ unsigned m =
+ g_opt.m_threads < 10 ? 1 :
+ g_opt.m_threads < 100 ? 2 : 3;
+ sprintf(buf, "[%0*u] ", m, n);
+ }
+ return buf;
+}
+
+#define LLN(n, s) \
+ do { \
+ if ((n) > g_opt.m_v) break; \
+ if (g_opt.m_msglock) NdbMutex_Lock(ndbout_mutex); \
+ ndbout << getthrstr() << s << endl; \
+ if (g_opt.m_msglock) NdbMutex_Unlock(ndbout_mutex); \
+ } while(0)
+
+#define LL0(s) LLN(0, s)
+#define LL1(s) LLN(1, s)
+#define LL2(s) LLN(2, s)
+#define LL3(s) LLN(3, s)
+#define LL4(s) LLN(4, s)
+#define LL5(s) LLN(5, s)
+
+// following check a condition and return -1 on failure
+
+#undef CHK // simple check
+#undef CHKTRY // check with action on fail
+#undef CHKCON // print NDB API errors on failure
+
+#define CHK(x) CHKTRY(x, ;)
+
+#define CHKTRY(x, act) \
+ do { \
+ if (x) break; \
+ LL0("line " << __LINE__ << ": " << #x << " failed"); \
+ if (g_opt.m_core) abort(); \
+ act; \
+ return -1; \
+ } while (0)
+
+#define CHKCON(x, con) \
+ do { \
+ if (x) break; \
+ LL0("line " << __LINE__ << ": " << #x << " failed"); \
+ (con).printerror(ndbout); \
+ if (g_opt.m_core) abort(); \
+ return -1; \
+ } while (0)
+
+// method parameters
+
+class Thr;
+class Con;
+class Tab;
+class Set;
+class Tmr;
+
+struct Par : public Opt {
+ unsigned m_no;
+ Con* m_con;
+ Con& con() const { assert(m_con != 0); return *m_con; }
+ const Tab* m_tab;
+ const Tab& tab() const { assert(m_tab != 0); return *m_tab; }
+ Set* m_set;
+ Set& set() const { assert(m_set != 0); return *m_set; }
+ Tmr* m_tmr;
+ Tmr& tmr() const { assert(m_tmr != 0); return *m_tmr; }
+ unsigned m_lno;
+ unsigned m_slno;
+ unsigned m_totrows;
+ // value calculation
+ unsigned m_range;
+ unsigned m_pctrange;
+ unsigned m_pctbrange;
+ int m_bdir;
+ // choice of key
+ bool m_randomkey;
+ // do verify after read
+ bool m_verify;
+ // deadlock possible
+ bool m_deadlock;
+ NdbOperation::LockMode m_lockmode;
+ // ordered range scan
+ bool m_ordered;
+ bool m_descending;
+ // timer location
+ Par(const Opt& opt) :
+ Opt(opt),
+ m_no(0),
+ m_con(0),
+ m_tab(0),
+ m_set(0),
+ m_tmr(0),
+ m_lno(0),
+ m_slno(0),
+ m_totrows(m_threads * m_rows),
+ m_range(m_rows),
+ m_pctrange(40),
+ m_pctbrange(80),
+ m_bdir(0),
+ m_randomkey(false),
+ m_verify(false),
+ m_deadlock(false),
+ m_lockmode(NdbOperation::LM_Read),
+ m_ordered(false),
+ m_descending(false) {
+ }
+};
+
+static bool
+usetable(Par par, unsigned i)
+{
+ return par.m_table == 0 || strchr(par.m_table, '0' + i) != 0;
+}
+
+static bool
+useindex(Par par, unsigned i)
+{
+ return par.m_index == 0 || strchr(par.m_index, '0' + i) != 0;
+}
+
+static unsigned
+thrrow(Par par, unsigned j)
+{
+ return par.m_threads * j + par.m_no;
+}
+
+static bool
+isthrrow(Par par, unsigned i)
+{
+ return i % par.m_threads == par.m_no;
+}
+
+// timer
+
+struct Tmr {
+ void clr();
+ void on();
+ void off(unsigned cnt = 0);
+ const char* time();
+ const char* pct(const Tmr& t1);
+ const char* over(const Tmr& t1);
+ NDB_TICKS m_on;
+ unsigned m_ms;
+ unsigned m_cnt;
+ char m_time[100];
+ char m_text[100];
+ Tmr() { clr(); }
+};
+
+void
+Tmr::clr()
+{
+ m_on = m_ms = m_cnt = m_time[0] = m_text[0] = 0;
+}
+
+void
+Tmr::on()
+{
+ assert(m_on == 0);
+ m_on = NdbTick_CurrentMillisecond();
+}
+
+void
+Tmr::off(unsigned cnt)
+{
+ NDB_TICKS off = NdbTick_CurrentMillisecond();
+ assert(m_on != 0 && off >= m_on);
+ m_ms += off - m_on;
+ m_cnt += cnt;
+ m_on = 0;
+}
+
+const char*
+Tmr::time()
+{
+ if (m_cnt == 0) {
+ sprintf(m_time, "%u ms", m_ms);
+ } else {
+ sprintf(m_time, "%u ms per %u ( %u ms per 1000 )", m_ms, m_cnt, (1000 * m_ms) / m_cnt);
+ }
+ return m_time;
+}
+
+const char*
+Tmr::pct(const Tmr& t1)
+{
+ if (0 < t1.m_ms) {
+ sprintf(m_text, "%u pct", (100 * m_ms) / t1.m_ms);
+ } else {
+ sprintf(m_text, "[cannot measure]");
+ }
+ return m_text;
+}
+
+const char*
+Tmr::over(const Tmr& t1)
+{
+ if (0 < t1.m_ms) {
+ if (t1.m_ms <= m_ms)
+ sprintf(m_text, "%u pct", (100 * (m_ms - t1.m_ms)) / t1.m_ms);
+ else
+ sprintf(m_text, "-%u pct", (100 * (t1.m_ms - m_ms)) / t1.m_ms);
+ } else {
+ sprintf(m_text, "[cannot measure]");
+ }
+ return m_text;
+}
+
+// list of ints
+
+struct Lst {
+ Lst();
+ unsigned m_arr[1000];
+ unsigned m_cnt;
+ void push(unsigned i);
+ unsigned cnt() const;
+ void reset();
+};
+
+Lst::Lst() :
+ m_cnt(0)
+{
+}
+
+void
+Lst::push(unsigned i)
+{
+ assert(m_cnt < sizeof(m_arr)/sizeof(m_arr[0]));
+ m_arr[m_cnt++] = i;
+}
+
+unsigned
+Lst::cnt() const
+{
+ return m_cnt;
+}
+
+void
+Lst::reset()
+{
+ m_cnt = 0;
+}
+
+// character sets
+
+static const unsigned maxcsnumber = 512;
+static const unsigned maxcharcount = 32;
+static const unsigned maxcharsize = 4;
+static const unsigned maxxmulsize = 8;
+
+// single mb char
+struct Chr {
+ unsigned char m_bytes[maxcharsize];
+ unsigned char m_xbytes[maxxmulsize * maxcharsize];
+ unsigned m_size;
+ Chr();
+};
+
+Chr::Chr()
+{
+ memset(m_bytes, 0, sizeof(m_bytes));
+ memset(m_xbytes, 0, sizeof(m_xbytes));
+ m_size = 0;
+}
+
+// charset and random valid chars to use
+struct Chs {
+ CHARSET_INFO* m_cs;
+ unsigned m_xmul;
+ Chr* m_chr;
+ Chs(CHARSET_INFO* cs);
+ ~Chs();
+};
+
+static NdbOut&
+operator<<(NdbOut& out, const Chs& chs);
+
+Chs::Chs(CHARSET_INFO* cs) :
+ m_cs(cs)
+{
+ m_xmul = m_cs->strxfrm_multiply;
+ if (m_xmul == 0)
+ m_xmul = 1;
+ assert(m_xmul <= maxxmulsize);
+ m_chr = new Chr [maxcharcount];
+ unsigned i = 0;
+ unsigned miss1 = 0;
+ unsigned miss2 = 0;
+ unsigned miss3 = 0;
+ unsigned miss4 = 0;
+ while (i < maxcharcount) {
+ unsigned char* bytes = m_chr[i].m_bytes;
+ unsigned char* xbytes = m_chr[i].m_xbytes;
+ unsigned& size = m_chr[i].m_size;
+ bool ok;
+ size = m_cs->mbminlen + urandom(m_cs->mbmaxlen - m_cs->mbminlen + 1);
+ assert(m_cs->mbminlen <= size && size <= m_cs->mbmaxlen);
+ // prefer longer chars
+ if (size == m_cs->mbminlen && m_cs->mbminlen < m_cs->mbmaxlen && urandom(5) != 0)
+ continue;
+ for (unsigned j = 0; j < size; j++) {
+ bytes[j] = urandom(256);
+ }
+ // check wellformed
+ const char* sbytes = (const char*)bytes;
+ if ((*cs->cset->well_formed_len)(cs, sbytes, sbytes + size, 1) != size) {
+ miss1++;
+ continue;
+ }
+ // check no proper prefix wellformed
+ ok = true;
+ for (unsigned j = 1; j < size; j++) {
+ if ((*cs->cset->well_formed_len)(cs, sbytes, sbytes + j, 1) == j) {
+ ok = false;
+ break;
+ }
+ }
+ if (! ok) {
+ miss2++;
+ continue;
+ }
+ // normalize
+ memset(xbytes, 0, sizeof(xbytes));
+ // currently returns buffer size always
+ int xlen = (*cs->coll->strnxfrm)(cs, xbytes, m_xmul * size, bytes, size);
+ // check we got something
+ ok = false;
+ for (unsigned j = 0; j < xlen; j++) {
+ if (xbytes[j] != 0) {
+ ok = true;
+ break;
+ }
+ }
+ if (! ok) {
+ miss3++;
+ continue;
+ }
+ // check for duplicate (before normalize)
+ ok = true;
+ for (unsigned j = 0; j < i; j++) {
+ const Chr& chr = m_chr[j];
+ if (chr.m_size == size && memcmp(chr.m_bytes, bytes, size) == 0) {
+ ok = false;
+ break;
+ }
+ }
+ if (! ok) {
+ miss4++;
+ continue;
+ }
+ i++;
+ }
+ bool disorder = true;
+ unsigned bubbles = 0;
+ while (disorder) {
+ disorder = false;
+ for (unsigned i = 1; i < maxcharcount; i++) {
+ unsigned len = sizeof(m_chr[i].m_xbytes);
+ if (memcmp(m_chr[i-1].m_xbytes, m_chr[i].m_xbytes, len) > 0) {
+ Chr chr = m_chr[i];
+ m_chr[i] = m_chr[i-1];
+ m_chr[i-1] = chr;
+ disorder = true;
+ bubbles++;
+ }
+ }
+ }
+ LL3("inited charset " << *this << " miss=" << miss1 << "," << miss2 << "," << miss3 << "," << miss4 << " bubbles=" << bubbles);
+}
+
+Chs::~Chs()
+{
+ delete [] m_chr;
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const Chs& chs)
+{
+ CHARSET_INFO* cs = chs.m_cs;
+ out << cs->name << "[" << cs->mbminlen << "-" << cs->mbmaxlen << "," << chs.m_xmul << "]";
+ return out;
+}
+
+static Chs* cslist[maxcsnumber];
+
+static void
+resetcslist()
+{
+ for (unsigned i = 0; i < maxcsnumber; i++) {
+ delete cslist[i];
+ cslist[i] = 0;
+ }
+}
+
+static Chs*
+getcs(Par par)
+{
+ CHARSET_INFO* cs;
+ if (par.m_cs != 0) {
+ cs = par.m_cs;
+ } else {
+ while (1) {
+ unsigned n = urandom(maxcsnumber);
+ cs = get_charset(n, MYF(0));
+ if (cs != 0) {
+ // prefer complex charsets
+ if (cs->mbmaxlen != 1 || urandom(5) == 0)
+ break;
+ }
+ }
+ }
+ if (cslist[cs->number] == 0)
+ cslist[cs->number] = new Chs(cs);
+ return cslist[cs->number];
+}
+
+// tables and indexes
+
+// Col - table column
+
+struct Col {
+ enum Type {
+ Unsigned = NdbDictionary::Column::Unsigned,
+ Char = NdbDictionary::Column::Char,
+ Varchar = NdbDictionary::Column::Varchar,
+ Longvarchar = NdbDictionary::Column::Longvarchar
+ };
+ const class Tab& m_tab;
+ unsigned m_num;
+ const char* m_name;
+ bool m_pk;
+ Type m_type;
+ unsigned m_length;
+ unsigned m_bytelength; // multiplied by char width
+ unsigned m_attrsize; // base type size
+ unsigned m_headsize; // length bytes
+ unsigned m_bytesize; // full value size
+ bool m_nullable;
+ const Chs* m_chs;
+ Col(const class Tab& tab, unsigned num, const char* name, bool pk, Type type, unsigned length, bool nullable, const Chs* chs);
+ ~Col();
+ bool equal(const Col& col2) const;
+ void wellformed(const void* addr) const;
+};
+
+Col::Col(const class Tab& tab, unsigned num, const char* name, bool pk, Type type, unsigned length, bool nullable, const Chs* chs) :
+ m_tab(tab),
+ m_num(num),
+ m_name(strcpy(new char [strlen(name) + 1], name)),
+ m_pk(pk),
+ m_type(type),
+ m_length(length),
+ m_bytelength(length * (chs == 0 ? 1 : chs->m_cs->mbmaxlen)),
+ m_attrsize(
+ type == Unsigned ? sizeof(Uint32) :
+ type == Char ? sizeof(char) :
+ type == Varchar ? sizeof(char) :
+ type == Longvarchar ? sizeof(char) : ~0),
+ m_headsize(
+ type == Unsigned ? 0 :
+ type == Char ? 0 :
+ type == Varchar ? 1 :
+ type == Longvarchar ? 2 : ~0),
+ m_bytesize(m_headsize + m_attrsize * m_bytelength),
+ m_nullable(nullable),
+ m_chs(chs)
+{
+ // fix long varchar
+ if (type == Varchar && m_bytelength > 255) {
+ m_type = Longvarchar;
+ m_headsize += 1;
+ m_bytesize += 1;
+ }
+}
+
+Col::~Col()
+{
+ delete [] m_name;
+}
+
+bool
+Col::equal(const Col& col2) const
+{
+ return m_type == col2.m_type && m_length == col2.m_length && m_chs == col2.m_chs;
+}
+
+void
+Col::wellformed(const void* addr) const
+{
+ switch (m_type) {
+ case Col::Unsigned:
+ break;
+ case Col::Char:
+ {
+ CHARSET_INFO* cs = m_chs->m_cs;
+ const char* src = (const char*)addr;
+ unsigned len = m_bytelength;
+ assert((*cs->cset->well_formed_len)(cs, src, src + len, 0xffff) == len);
+ }
+ break;
+ case Col::Varchar:
+ {
+ CHARSET_INFO* cs = m_chs->m_cs;
+ const unsigned char* src = (const unsigned char*)addr;
+ const char* ssrc = (const char*)src;
+ unsigned len = src[0];
+ assert(len <= m_bytelength);
+ assert((*cs->cset->well_formed_len)(cs, ssrc + 1, ssrc + 1 + len, 0xffff) == len);
+ }
+ break;
+ case Col::Longvarchar:
+ {
+ CHARSET_INFO* cs = m_chs->m_cs;
+ const unsigned char* src = (const unsigned char*)addr;
+ const char* ssrc = (const char*)src;
+ unsigned len = src[0] + (src[1] << 8);
+ assert(len <= m_bytelength);
+ assert((*cs->cset->well_formed_len)(cs, ssrc + 2, ssrc + 2 + len, 0xffff) == len);
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const Col& col)
+{
+ out << "col[" << col.m_num << "] " << col.m_name;
+ switch (col.m_type) {
+ case Col::Unsigned:
+ out << " unsigned";
+ break;
+ case Col::Char:
+ {
+ CHARSET_INFO* cs = col.m_chs->m_cs;
+ out << " char(" << col.m_length << "*" << cs->mbmaxlen << ";" << cs->name << ")";
+ }
+ break;
+ case Col::Varchar:
+ {
+ CHARSET_INFO* cs = col.m_chs->m_cs;
+ out << " varchar(" << col.m_length << "*" << cs->mbmaxlen << ";" << cs->name << ")";
+ }
+ break;
+ case Col::Longvarchar:
+ {
+ CHARSET_INFO* cs = col.m_chs->m_cs;
+ out << " longvarchar(" << col.m_length << "*" << cs->mbmaxlen << ";" << cs->name << ")";
+ }
+ break;
+ default:
+ out << "type" << (int)col.m_type;
+ assert(false);
+ break;
+ }
+ out << (col.m_pk ? " pk" : "");
+ out << (col.m_nullable ? " nullable" : "");
+ return out;
+}
+
+// ICol - index column
+
+struct ICol {
+ const class ITab& m_itab;
+ unsigned m_num;
+ const Col& m_col;
+ ICol(const class ITab& itab, unsigned num, const Col& col);
+ ~ICol();
+};
+
+ICol::ICol(const class ITab& itab, unsigned num, const Col& col) :
+ m_itab(itab),
+ m_num(num),
+ m_col(col)
+{
+}
+
+ICol::~ICol()
+{
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const ICol& icol)
+{
+ out << "icol[" << icol.m_num << "] " << icol.m_col;
+ return out;
+}
+
+// ITab - index
+
+struct ITab {
+ enum Type {
+ OrderedIndex = NdbDictionary::Index::OrderedIndex,
+ UniqueHashIndex = NdbDictionary::Index::UniqueHashIndex
+ };
+ const class Tab& m_tab;
+ const char* m_name;
+ Type m_type;
+ unsigned m_icols;
+ const ICol** m_icol;
+ unsigned m_colmask;
+ ITab(const class Tab& tab, const char* name, Type type, unsigned icols);
+ ~ITab();
+ void icoladd(unsigned k, const ICol* icolptr);
+};
+
+ITab::ITab(const class Tab& tab, const char* name, Type type, unsigned icols) :
+ m_tab(tab),
+ m_name(strcpy(new char [strlen(name) + 1], name)),
+ m_type(type),
+ m_icols(icols),
+ m_icol(new const ICol* [icols + 1]),
+ m_colmask(0)
+{
+ for (unsigned k = 0; k <= m_icols; k++)
+ m_icol[k] = 0;
+}
+
+ITab::~ITab()
+{
+ delete [] m_name;
+ for (unsigned i = 0; i < m_icols; i++)
+ delete m_icol[i];
+ delete [] m_icol;
+}
+
+void
+ITab::icoladd(unsigned k, const ICol* icolptr)
+{
+ assert(k == icolptr->m_num && k < m_icols && m_icol[k] == 0);
+ m_icol[k] = icolptr;
+ m_colmask |= (1 << icolptr->m_col.m_num);
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const ITab& itab)
+{
+ out << "itab " << itab.m_name << " icols=" << itab.m_icols;
+ for (unsigned k = 0; k < itab.m_icols; k++) {
+ const ICol& icol = *itab.m_icol[k];
+ out << endl << icol;
+ }
+ return out;
+}
+
+// Tab - table
+
+struct Tab {
+ const char* m_name;
+ unsigned m_cols;
+ const Col** m_col;
+ unsigned m_itabs;
+ const ITab** m_itab;
+ // pk must contain an Unsigned column
+ unsigned m_keycol;
+ void coladd(unsigned k, Col* colptr);
+ void itabadd(unsigned j, ITab* itab);
+ Tab(const char* name, unsigned cols, unsigned itabs, unsigned keycol);
+ ~Tab();
+};
+
+Tab::Tab(const char* name, unsigned cols, unsigned itabs, unsigned keycol) :
+ m_name(strcpy(new char [strlen(name) + 1], name)),
+ m_cols(cols),
+ m_col(new const Col* [cols + 1]),
+ m_itabs(itabs),
+ m_itab(new const ITab* [itabs + 1]),
+ m_keycol(keycol)
+{
+ for (unsigned k = 0; k <= cols; k++)
+ m_col[k] = 0;
+ for (unsigned j = 0; j <= itabs; j++)
+ m_itab[j] = 0;
+}
+
+Tab::~Tab()
+{
+ delete [] m_name;
+ for (unsigned i = 0; i < m_cols; i++)
+ delete m_col[i];
+ delete [] m_col;
+ for (unsigned i = 0; i < m_itabs; i++)
+ delete m_itab[i];
+ delete [] m_itab;
+}
+
+void
+Tab::coladd(unsigned k, Col* colptr)
+{
+ assert(k == colptr->m_num && k < m_cols && m_col[k] == 0);
+ m_col[k] = colptr;
+}
+
+void
+Tab::itabadd(unsigned j, ITab* itabptr)
+{
+ assert(j < m_itabs && m_itab[j] == 0);
+ m_itab[j] = itabptr;
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const Tab& tab)
+{
+ out << "tab " << tab.m_name << " cols=" << tab.m_cols;
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Col& col = *tab.m_col[k];
+ out << endl << col;
+ }
+ for (unsigned i = 0; i < tab.m_itabs; i++) {
+ if (tab.m_itab[i] == 0)
+ continue;
+ const ITab& itab = *tab.m_itab[i];
+ out << endl << itab;
+ }
+ return out;
+}
+
+// make table structs
+
+static const Tab** tablist = 0;
+static unsigned tabcount = 0;
+
+static void
+verifytables()
+{
+ for (unsigned j = 0; j < tabcount; j++) {
+ const Tab* t = tablist[j];
+ if (t == 0)
+ continue;
+ assert(t->m_cols != 0 && t->m_col != 0);
+ for (unsigned k = 0; k < t->m_cols; k++) {
+ const Col* c = t->m_col[k];
+ assert(c != 0 && c->m_num == k);
+ assert(! (c->m_pk && c->m_nullable));
+ }
+ assert(t->m_col[t->m_cols] == 0);
+ {
+ assert(t->m_keycol < t->m_cols);
+ const Col* c = t->m_col[t->m_keycol];
+ assert(c->m_pk && c->m_type == Col::Unsigned);
+ }
+ assert(t->m_itabs != 0 && t->m_itab != 0);
+ for (unsigned i = 0; i < t->m_itabs; i++) {
+ const ITab* x = t->m_itab[i];
+ if (x == 0)
+ continue;
+ assert(x != 0 && x->m_icols != 0 && x->m_icol != 0);
+ for (unsigned k = 0; k < x->m_icols; k++) {
+ const ICol* c = x->m_icol[k];
+ assert(c != 0 && c->m_num == k && c->m_col.m_num < t->m_cols);
+ if (x->m_type == ITab::UniqueHashIndex) {
+ assert(! c->m_col.m_nullable);
+ }
+ }
+ }
+ assert(t->m_itab[t->m_itabs] == 0);
+ }
+}
+
+static void
+makebuiltintables(Par par)
+{
+ LL2("makebuiltintables");
+ resetcslist();
+ tabcount = 3;
+ if (tablist == 0) {
+ tablist = new const Tab* [tabcount];
+ for (unsigned j = 0; j < tabcount; j++) {
+ tablist[j] = 0;
+ }
+ } else {
+ for (unsigned j = 0; j < tabcount; j++) {
+ delete tablist[j];
+ tablist[j] = 0;
+ }
+ }
+ // ti0 - basic
+ if (usetable(par, 0)) {
+ Tab* t = new Tab("ti0", 5, 7, 0);
+ // name - pk - type - length - nullable - cs
+ t->coladd(0, new Col(*t, 0, "a", 1, Col::Unsigned, 1, 0, 0));
+ t->coladd(1, new Col(*t, 1, "b", 0, Col::Unsigned, 1, 1, 0));
+ t->coladd(2, new Col(*t, 2, "c", 0, Col::Unsigned, 1, 0, 0));
+ t->coladd(3, new Col(*t, 3, "d", 0, Col::Unsigned, 1, 1, 0));
+ t->coladd(4, new Col(*t, 4, "e", 0, Col::Unsigned, 1, 0, 0));
+ if (useindex(par, 0)) {
+ // a
+ ITab* x = new ITab(*t, "ti0x0", ITab::OrderedIndex, 1);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[0]));
+ t->itabadd(0, x);
+ }
+ if (useindex(par, 1)) {
+ // b
+ ITab* x = new ITab(*t, "ti0x1", ITab::OrderedIndex, 1);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[1]));
+ t->itabadd(1, x);
+ }
+ if (useindex(par, 2)) {
+ // b, c
+ ITab* x = new ITab(*t, "ti0x2", ITab::OrderedIndex, 2);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[1]));
+ x->icoladd(1, new ICol(*x, 1, *t->m_col[2]));
+ t->itabadd(2, x);
+ }
+ if (useindex(par, 3)) {
+ // b, e, c, d
+ ITab* x = new ITab(*t, "ti0x3", ITab::OrderedIndex, 4);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[1]));
+ x->icoladd(1, new ICol(*x, 1, *t->m_col[4]));
+ x->icoladd(2, new ICol(*x, 2, *t->m_col[2]));
+ x->icoladd(3, new ICol(*x, 3, *t->m_col[3]));
+ t->itabadd(3, x);
+ }
+ if (useindex(par, 4)) {
+ // a, c
+ ITab* x = new ITab(*t, "ti0z4", ITab::UniqueHashIndex, 2);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[0]));
+ x->icoladd(1, new ICol(*x, 1, *t->m_col[2]));
+ t->itabadd(4, x);
+ }
+ if (useindex(par, 5)) {
+ // a, e
+ ITab* x = new ITab(*t, "ti0z5", ITab::UniqueHashIndex, 2);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[0]));
+ x->icoladd(1, new ICol(*x, 1, *t->m_col[4]));
+ t->itabadd(5, x);
+ }
+ tablist[0] = t;
+ }
+ // ti1 - simple char fields
+ if (usetable(par, 1)) {
+ Tab* t = new Tab("ti1", 5, 7, 1);
+ // name - pk - type - length - nullable - cs
+ t->coladd(0, new Col(*t, 0, "a", 0, Col::Unsigned, 1, 0, 0));
+ t->coladd(1, new Col(*t, 1, "b", 1, Col::Unsigned, 1, 0, 0));
+ t->coladd(2, new Col(*t, 2, "c", 0, Col::Char, 20, 1, getcs(par)));
+ t->coladd(3, new Col(*t, 3, "d", 0, Col::Varchar, 5, 0, getcs(par)));
+ t->coladd(4, new Col(*t, 4, "e", 0, Col::Longvarchar, 5, 1, getcs(par)));
+ if (useindex(par, 0)) {
+ // b
+ ITab* x = new ITab(*t, "ti1x0", ITab::OrderedIndex, 1);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[1]));
+ t->itabadd(0, x);
+ }
+ if (useindex(par, 1)) {
+ // c, a
+ ITab* x = new ITab(*t, "ti1x1", ITab::OrderedIndex, 2);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[2]));
+ x->icoladd(1, new ICol(*x, 1, *t->m_col[0]));
+ t->itabadd(1, x);
+ }
+ if (useindex(par, 2)) {
+ // d
+ ITab* x = new ITab(*t, "ti1x2", ITab::OrderedIndex, 1);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[3]));
+ t->itabadd(2, x);
+ }
+ if (useindex(par, 3)) {
+ // e, d, c, b
+ ITab* x = new ITab(*t, "ti1x3", ITab::OrderedIndex, 4);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[4]));
+ x->icoladd(1, new ICol(*x, 1, *t->m_col[3]));
+ x->icoladd(2, new ICol(*x, 2, *t->m_col[2]));
+ x->icoladd(3, new ICol(*x, 3, *t->m_col[1]));
+ t->itabadd(3, x);
+ }
+ if (useindex(par, 4)) {
+ // a, b
+ ITab* x = new ITab(*t, "ti1z4", ITab::UniqueHashIndex, 2);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[0]));
+ x->icoladd(1, new ICol(*x, 1, *t->m_col[1]));
+ t->itabadd(4, x);
+ }
+ if (useindex(par, 5)) {
+ // a, b, d
+ ITab* x = new ITab(*t, "ti1z5", ITab::UniqueHashIndex, 3);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[0]));
+ x->icoladd(1, new ICol(*x, 1, *t->m_col[1]));
+ x->icoladd(2, new ICol(*x, 2, *t->m_col[3]));
+ t->itabadd(5, x);
+ }
+ tablist[1] = t;
+ }
+ // ti2 - complex char fields
+ if (usetable(par, 2)) {
+ Tab* t = new Tab("ti2", 5, 7, 2);
+ // name - pk - type - length - nullable - cs
+ t->coladd(0, new Col(*t, 0, "a", 1, Col::Char, 31, 0, getcs(par)));
+ t->coladd(1, new Col(*t, 1, "b", 0, Col::Char, 4, 1, getcs(par)));
+ t->coladd(2, new Col(*t, 2, "c", 1, Col::Unsigned, 1, 0, 0));
+ t->coladd(3, new Col(*t, 3, "d", 1, Col::Varchar, 128, 0, getcs(par)));
+ t->coladd(4, new Col(*t, 4, "e", 0, Col::Varchar, 7, 0, getcs(par)));
+ if (useindex(par, 0)) {
+ // a, c, d
+ ITab* x = new ITab(*t, "ti2x0", ITab::OrderedIndex, 3);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[0]));
+ x->icoladd(1, new ICol(*x, 1, *t->m_col[2]));
+ x->icoladd(2, new ICol(*x, 2, *t->m_col[3]));
+ t->itabadd(0, x);
+ }
+ if (useindex(par, 1)) {
+ // e, d, c, b, a
+ ITab* x = new ITab(*t, "ti2x1", ITab::OrderedIndex, 5);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[4]));
+ x->icoladd(1, new ICol(*x, 1, *t->m_col[3]));
+ x->icoladd(2, new ICol(*x, 2, *t->m_col[2]));
+ x->icoladd(3, new ICol(*x, 3, *t->m_col[1]));
+ x->icoladd(4, new ICol(*x, 4, *t->m_col[0]));
+ t->itabadd(1, x);
+ }
+ if (useindex(par, 2)) {
+ // d
+ ITab* x = new ITab(*t, "ti2x2", ITab::OrderedIndex, 1);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[3]));
+ t->itabadd(2, x);
+ }
+ if (useindex(par, 3)) {
+ // b
+ ITab* x = new ITab(*t, "ti2x3", ITab::OrderedIndex, 1);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[1]));
+ t->itabadd(3, x);
+ }
+ if (useindex(par, 4)) {
+ // a, c
+ ITab* x = new ITab(*t, "ti2z4", ITab::UniqueHashIndex, 2);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[0]));
+ x->icoladd(1, new ICol(*x, 1, *t->m_col[2]));
+ t->itabadd(4, x);
+ }
+ if (useindex(par, 5)) {
+ // a, c, d, e
+ ITab* x = new ITab(*t, "ti2z5", ITab::UniqueHashIndex, 4);
+ x->icoladd(0, new ICol(*x, 0, *t->m_col[0]));
+ x->icoladd(1, new ICol(*x, 1, *t->m_col[2]));
+ x->icoladd(2, new ICol(*x, 2, *t->m_col[3]));
+ x->icoladd(3, new ICol(*x, 3, *t->m_col[4]));
+ t->itabadd(5, x);
+ }
+ tablist[2] = t;
+ }
+ verifytables();
+}
+
+// connections
+
+static Ndb_cluster_connection* g_ncc = 0;
+
+struct Con {
+ Ndb* m_ndb;
+ NdbDictionary::Dictionary* m_dic;
+ NdbConnection* m_tx;
+ NdbOperation* m_op;
+ NdbIndexOperation* m_indexop;
+ NdbScanOperation* m_scanop;
+ NdbIndexScanOperation* m_indexscanop;
+ NdbScanFilter* m_scanfilter;
+ enum ScanMode { ScanNo = 0, Committed, Latest, Exclusive };
+ ScanMode m_scanmode;
+ enum ErrType { ErrNone = 0, ErrDeadlock, ErrOther };
+ ErrType m_errtype;
+ Con() :
+ m_ndb(0), m_dic(0), m_tx(0), m_op(0), m_indexop(0),
+ m_scanop(0), m_indexscanop(0), m_scanfilter(0),
+ m_scanmode(ScanNo), m_errtype(ErrNone) {}
+ ~Con() {
+ if (m_tx != 0)
+ closeTransaction();
+ }
+ int connect();
+ void connect(const Con& con);
+ void disconnect();
+ int startTransaction();
+ int getNdbOperation(const Tab& tab);
+ int getNdbIndexOperation1(const ITab& itab, const Tab& tab);
+ int getNdbIndexOperation(const ITab& itab, const Tab& tab);
+ int getNdbScanOperation(const Tab& tab);
+ int getNdbIndexScanOperation1(const ITab& itab, const Tab& tab);
+ int getNdbIndexScanOperation(const ITab& itab, const Tab& tab);
+ int getNdbScanFilter();
+ int equal(int num, const char* addr);
+ int getValue(int num, NdbRecAttr*& rec);
+ int setValue(int num, const char* addr);
+ int setBound(int num, int type, const void* value);
+ int beginFilter(int group);
+ int endFilter();
+ int setFilter(int num, int cond, const void* value, unsigned len);
+ int execute(ExecType t);
+ int execute(ExecType t, bool& deadlock);
+ int readTuples(Par par);
+ int readIndexTuples(Par par);
+ int executeScan();
+ int nextScanResult(bool fetchAllowed);
+ int nextScanResult(bool fetchAllowed, bool& deadlock);
+ int updateScanTuple(Con& con2);
+ int deleteScanTuple(Con& con2);
+ void closeScan();
+ void closeTransaction();
+ void printerror(NdbOut& out);
+};
+
+int
+Con::connect()
+{
+ assert(m_ndb == 0);
+ m_ndb = new Ndb(g_ncc, "TEST_DB");
+ CHKCON(m_ndb->init() == 0, *this);
+ CHKCON(m_ndb->waitUntilReady(30) == 0, *this);
+ m_tx = 0, m_op = 0;
+ return 0;
+}
+
+void
+Con::connect(const Con& con)
+{
+ assert(m_ndb == 0);
+ m_ndb = con.m_ndb;
+}
+
+void
+Con::disconnect()
+{
+ delete m_ndb;
+ m_ndb = 0, m_dic = 0, m_tx = 0, m_op = 0;
+}
+
+int
+Con::startTransaction()
+{
+ assert(m_ndb != 0);
+ if (m_tx != 0)
+ closeTransaction();
+ CHKCON((m_tx = m_ndb->startTransaction()) != 0, *this);
+ return 0;
+}
+
+int
+Con::getNdbOperation(const Tab& tab)
+{
+ assert(m_tx != 0);
+ CHKCON((m_op = m_tx->getNdbOperation(tab.m_name)) != 0, *this);
+ return 0;
+}
+
+int
+Con::getNdbIndexOperation1(const ITab& itab, const Tab& tab)
+{
+ assert(m_tx != 0);
+ CHKCON((m_op = m_indexop = m_tx->getNdbIndexOperation(itab.m_name, tab.m_name)) != 0, *this);
+ return 0;
+}
+
+int
+Con::getNdbIndexOperation(const ITab& itab, const Tab& tab)
+{
+ assert(m_tx != 0);
+ unsigned tries = 0;
+ while (1) {
+ if (getNdbIndexOperation1(itab, tab) == 0)
+ break;
+ CHK(++tries < 10);
+ NdbSleep_MilliSleep(100);
+ }
+ return 0;
+}
+
+int
+Con::getNdbScanOperation(const Tab& tab)
+{
+ assert(m_tx != 0);
+ CHKCON((m_op = m_scanop = m_tx->getNdbScanOperation(tab.m_name)) != 0, *this);
+ return 0;
+}
+
+int
+Con::getNdbIndexScanOperation1(const ITab& itab, const Tab& tab)
+{
+ assert(m_tx != 0);
+ CHKCON((m_op = m_scanop = m_indexscanop = m_tx->getNdbIndexScanOperation(itab.m_name, tab.m_name)) != 0, *this);
+ return 0;
+}
+
+int
+Con::getNdbIndexScanOperation(const ITab& itab, const Tab& tab)
+{
+ assert(m_tx != 0);
+ unsigned tries = 0;
+ while (1) {
+ if (getNdbIndexScanOperation1(itab, tab) == 0)
+ break;
+ CHK(++tries < 10);
+ NdbSleep_MilliSleep(100);
+ }
+ return 0;
+}
+
+int
+Con::getNdbScanFilter()
+{
+ assert(m_tx != 0 && m_scanop != 0);
+ delete m_scanfilter;
+ m_scanfilter = new NdbScanFilter(m_scanop);
+ return 0;
+}
+
+int
+Con::equal(int num, const char* addr)
+{
+ assert(m_tx != 0 && m_op != 0);
+ CHKCON(m_op->equal(num, addr) == 0, *this);
+ return 0;
+}
+
+int
+Con::getValue(int num, NdbRecAttr*& rec)
+{
+ assert(m_tx != 0 && m_op != 0);
+ CHKCON((rec = m_op->getValue(num, 0)) != 0, *this);
+ return 0;
+}
+
+int
+Con::setValue(int num, const char* addr)
+{
+ assert(m_tx != 0 && m_op != 0);
+ CHKCON(m_op->setValue(num, addr) == 0, *this);
+ return 0;
+}
+
+int
+Con::setBound(int num, int type, const void* value)
+{
+ assert(m_tx != 0 && m_indexscanop != 0);
+ CHKCON(m_indexscanop->setBound(num, type, value) == 0, *this);
+ return 0;
+}
+
+int
+Con::beginFilter(int group)
+{
+ assert(m_tx != 0 && m_scanfilter != 0);
+ CHKCON(m_scanfilter->begin((NdbScanFilter::Group)group) == 0, *this);
+ return 0;
+}
+
+int
+Con::endFilter()
+{
+ assert(m_tx != 0 && m_scanfilter != 0);
+ CHKCON(m_scanfilter->end() == 0, *this);
+ return 0;
+}
+
+int
+Con::setFilter(int num, int cond, const void* value, unsigned len)
+{
+ assert(m_tx != 0 && m_scanfilter != 0);
+ CHKCON(m_scanfilter->cmp((NdbScanFilter::BinaryCondition)cond, num, value, len) == 0, *this);
+ return 0;
+}
+
+int
+Con::execute(ExecType t)
+{
+ assert(m_tx != 0);
+ CHKCON(m_tx->execute(t) == 0, *this);
+ return 0;
+}
+
+int
+Con::execute(ExecType t, bool& deadlock)
+{
+ int ret = execute(t);
+ if (ret != 0) {
+ if (deadlock && m_errtype == ErrDeadlock) {
+ LL3("caught deadlock");
+ ret = 0;
+ }
+ } else {
+ deadlock = false;
+ }
+ CHK(ret == 0);
+ return 0;
+}
+
+int
+Con::readTuples(Par par)
+{
+ assert(m_tx != 0 && m_scanop != 0);
+ CHKCON(m_scanop->readTuples(par.m_lockmode, par.m_scanbat, par.m_scanpar) == 0, *this);
+ return 0;
+}
+
+int
+Con::readIndexTuples(Par par)
+{
+ assert(m_tx != 0 && m_indexscanop != 0);
+ CHKCON(m_indexscanop->readTuples(par.m_lockmode, par.m_scanbat, par.m_scanpar, par.m_ordered, par.m_descending) == 0, *this);
+ return 0;
+}
+
+int
+Con::executeScan()
+{
+ CHKCON(m_tx->execute(NoCommit) == 0, *this);
+ return 0;
+}
+
+int
+Con::nextScanResult(bool fetchAllowed)
+{
+ int ret;
+ assert(m_scanop != 0);
+ CHKCON((ret = m_scanop->nextResult(fetchAllowed)) != -1, *this);
+ assert(ret == 0 || ret == 1 || (! fetchAllowed && ret == 2));
+ return ret;
+}
+
+int
+Con::nextScanResult(bool fetchAllowed, bool& deadlock)
+{
+ int ret = nextScanResult(fetchAllowed);
+ if (ret == -1) {
+ if (deadlock && m_errtype == ErrDeadlock) {
+ LL3("caught deadlock");
+ ret = 0;
+ }
+ } else {
+ deadlock = false;
+ }
+ CHK(ret == 0 || ret == 1 || (! fetchAllowed && ret == 2));
+ return ret;
+}
+
+int
+Con::updateScanTuple(Con& con2)
+{
+ assert(con2.m_tx != 0);
+ CHKCON((con2.m_op = m_scanop->updateCurrentTuple(con2.m_tx)) != 0, *this);
+ return 0;
+}
+
+int
+Con::deleteScanTuple(Con& con2)
+{
+ assert(con2.m_tx != 0);
+ CHKCON(m_scanop->deleteCurrentTuple(con2.m_tx) == 0, *this);
+ return 0;
+}
+
+void
+Con::closeScan()
+{
+ assert(m_scanop != 0);
+ m_scanop->close();
+ m_scanop = 0, m_indexscanop = 0;
+
+}
+
+void
+Con::closeTransaction()
+{
+ assert(m_ndb != 0 && m_tx != 0);
+ m_ndb->closeTransaction(m_tx);
+ m_tx = 0, m_op = 0;
+ m_scanop = 0, m_indexscanop = 0;
+}
+
+void
+Con::printerror(NdbOut& out)
+{
+ m_errtype = ErrOther;
+ unsigned any = 0;
+ int code;
+ int die = 0;
+ if (m_ndb) {
+ if ((code = m_ndb->getNdbError().code) != 0) {
+ LL0(++any << " ndb: error " << m_ndb->getNdbError());
+ die += (code == g_opt.m_die);
+ }
+ if (m_dic && (code = m_dic->getNdbError().code) != 0) {
+ LL0(++any << " dic: error " << m_dic->getNdbError());
+ die += (code == g_opt.m_die);
+ }
+ if (m_tx) {
+ if ((code = m_tx->getNdbError().code) != 0) {
+ LL0(++any << " con: error " << m_tx->getNdbError());
+ die += (code == g_opt.m_die);
+ // 631 is new, occurs only on 4 db nodes, needs to be checked out
+ if (code == 266 || code == 274 || code == 296 || code == 297 || code == 499 || code == 631)
+ m_errtype = ErrDeadlock;
+ }
+ if (m_op && m_op->getNdbError().code != 0) {
+ LL0(++any << " op : error " << m_op->getNdbError());
+ die += (code == g_opt.m_die);
+ }
+ }
+ }
+ if (! any) {
+ LL0("failed but no NDB error code");
+ }
+ if (die) {
+ if (g_opt.m_core)
+ abort();
+ exit(1);
+ }
+}
+
+// dictionary operations
+
+static int
+invalidateindex(Par par, const ITab& itab)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ con.m_ndb->getDictionary()->invalidateIndex(itab.m_name, tab.m_name);
+ return 0;
+}
+
+static int
+invalidateindex(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ for (unsigned i = 0; i < tab.m_itabs; i++) {
+ if (tab.m_itab[i] == 0)
+ continue;
+ const ITab& itab = *tab.m_itab[i];
+ invalidateindex(par, itab);
+ }
+ return 0;
+}
+
+static int
+invalidatetable(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ invalidateindex(par);
+ con.m_ndb->getDictionary()->invalidateTable(tab.m_name);
+ return 0;
+}
+
+static int
+droptable(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ con.m_dic = con.m_ndb->getDictionary();
+ if (con.m_dic->getTable(tab.m_name) == 0) {
+ // how to check for error
+ LL4("no table " << tab.m_name);
+ } else {
+ LL3("drop table " << tab.m_name);
+ CHKCON(con.m_dic->dropTable(tab.m_name) == 0, con);
+ }
+ con.m_dic = 0;
+ return 0;
+}
+
+static int
+createtable(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ LL3("create table " << tab.m_name);
+ LL4(tab);
+ NdbDictionary::Table t(tab.m_name);
+ if (par.m_fragtype != NdbDictionary::Object::FragUndefined) {
+ t.setFragmentType(par.m_fragtype);
+ }
+ if (par.m_nologging) {
+ t.setLogging(false);
+ }
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Col& col = *tab.m_col[k];
+ NdbDictionary::Column c(col.m_name);
+ c.setType((NdbDictionary::Column::Type)col.m_type);
+ c.setLength(col.m_bytelength); // for char NDB API uses length in bytes
+ c.setPrimaryKey(col.m_pk);
+ c.setNullable(col.m_nullable);
+ if (col.m_chs != 0)
+ c.setCharset(col.m_chs->m_cs);
+ t.addColumn(c);
+ }
+ con.m_dic = con.m_ndb->getDictionary();
+ CHKCON(con.m_dic->createTable(t) == 0, con);
+ con.m_dic = 0;
+ return 0;
+}
+
+static int
+dropindex(Par par, const ITab& itab)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ con.m_dic = con.m_ndb->getDictionary();
+ if (con.m_dic->getIndex(itab.m_name, tab.m_name) == 0) {
+ // how to check for error
+ LL4("no index " << itab.m_name);
+ } else {
+ LL3("drop index " << itab.m_name);
+ CHKCON(con.m_dic->dropIndex(itab.m_name, tab.m_name) == 0, con);
+ }
+ con.m_dic = 0;
+ return 0;
+}
+
+static int
+dropindex(Par par)
+{
+ const Tab& tab = par.tab();
+ for (unsigned i = 0; i < tab.m_itabs; i++) {
+ if (tab.m_itab[i] == 0)
+ continue;
+ const ITab& itab = *tab.m_itab[i];
+ CHK(dropindex(par, itab) == 0);
+ }
+ return 0;
+}
+
+static int
+createindex(Par par, const ITab& itab)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ LL3("create index " << itab.m_name);
+ LL4(itab);
+ NdbDictionary::Index x(itab.m_name);
+ x.setTable(tab.m_name);
+ x.setType((NdbDictionary::Index::Type)itab.m_type);
+ if (par.m_nologging || itab.m_type == ITab::OrderedIndex) {
+ x.setLogging(false);
+ }
+ for (unsigned k = 0; k < itab.m_icols; k++) {
+ const ICol& icol = *itab.m_icol[k];
+ const Col& col = icol.m_col;
+ x.addColumnName(col.m_name);
+ }
+ con.m_dic = con.m_ndb->getDictionary();
+ CHKCON(con.m_dic->createIndex(x) == 0, con);
+ con.m_dic = 0;
+ return 0;
+}
+
+static int
+createindex(Par par)
+{
+ const Tab& tab = par.tab();
+ for (unsigned i = 0; i < tab.m_itabs; i++) {
+ if (tab.m_itab[i] == 0)
+ continue;
+ const ITab& itab = *tab.m_itab[i];
+ CHK(createindex(par, itab) == 0);
+ }
+ return 0;
+}
+
+// data sets
+
+// Val - typed column value
+
+struct Val {
+ const Col& m_col;
+ union {
+ Uint32 m_uint32;
+ unsigned char* m_char;
+ unsigned char* m_varchar;
+ unsigned char* m_longvarchar;
+ };
+ Val(const Col& col);
+ ~Val();
+ void copy(const Val& val2);
+ void copy(const void* addr);
+ const void* dataaddr() const;
+ bool m_null;
+ int equal(Par par) const;
+ int equal(Par par, const ICol& icol) const;
+ int setval(Par par) const;
+ void calc(Par par, unsigned i);
+ void calckey(Par par, unsigned i);
+ void calckeychars(Par par, unsigned i, unsigned& n, unsigned char* buf);
+ void calcnokey(Par par);
+ void calcnokeychars(Par par, unsigned& n, unsigned char* buf);
+ int verify(Par par, const Val& val2) const;
+ int cmp(Par par, const Val& val2) const;
+ int cmpchars(Par par, const unsigned char* buf1, unsigned len1, const unsigned char* buf2, unsigned len2) const;
+private:
+ Val& operator=(const Val& val2);
+};
+
+static NdbOut&
+operator<<(NdbOut& out, const Val& val);
+
+Val::Val(const Col& col) :
+ m_col(col)
+{
+ switch (col.m_type) {
+ case Col::Unsigned:
+ break;
+ case Col::Char:
+ m_char = new unsigned char [col.m_bytelength];
+ break;
+ case Col::Varchar:
+ m_varchar = new unsigned char [1 + col.m_bytelength];
+ break;
+ case Col::Longvarchar:
+ m_longvarchar = new unsigned char [2 + col.m_bytelength];
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+Val::~Val()
+{
+ const Col& col = m_col;
+ switch (col.m_type) {
+ case Col::Unsigned:
+ break;
+ case Col::Char:
+ delete [] m_char;
+ break;
+ case Col::Varchar:
+ delete [] m_varchar;
+ break;
+ case Col::Longvarchar:
+ delete [] m_longvarchar;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+void
+Val::copy(const Val& val2)
+{
+ const Col& col = m_col;
+ const Col& col2 = val2.m_col;
+ assert(col.m_type == col2.m_type && col.m_length == col2.m_length);
+ if (val2.m_null) {
+ m_null = true;
+ return;
+ }
+ copy(val2.dataaddr());
+}
+
+void
+Val::copy(const void* addr)
+{
+ const Col& col = m_col;
+ switch (col.m_type) {
+ case Col::Unsigned:
+ m_uint32 = *(const Uint32*)addr;
+ break;
+ case Col::Char:
+ memcpy(m_char, addr, col.m_bytelength);
+ break;
+ case Col::Varchar:
+ memcpy(m_varchar, addr, 1 + col.m_bytelength);
+ break;
+ case Col::Longvarchar:
+ memcpy(m_longvarchar, addr, 2 + col.m_bytelength);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ m_null = false;
+}
+
+const void*
+Val::dataaddr() const
+{
+ const Col& col = m_col;
+ switch (col.m_type) {
+ case Col::Unsigned:
+ return &m_uint32;
+ case Col::Char:
+ return m_char;
+ case Col::Varchar:
+ return m_varchar;
+ case Col::Longvarchar:
+ return m_longvarchar;
+ default:
+ break;
+ }
+ assert(false);
+ return 0;
+}
+
+int
+Val::equal(Par par) const
+{
+ Con& con = par.con();
+ const Col& col = m_col;
+ assert(col.m_pk && ! m_null);
+ const char* addr = (const char*)dataaddr();
+ LL5("equal [" << col << "] " << *this);
+ CHK(con.equal(col.m_num, addr) == 0);
+ return 0;
+}
+
+int
+Val::equal(Par par, const ICol& icol) const
+{
+ Con& con = par.con();
+ assert(! m_null);
+ const char* addr = (const char*)dataaddr();
+ LL5("equal [" << icol << "] " << *this);
+ CHK(con.equal(icol.m_num, addr) == 0);
+ return 0;
+}
+
+int
+Val::setval(Par par) const
+{
+ Con& con = par.con();
+ const Col& col = m_col;
+ assert(! col.m_pk);
+ const char* addr = ! m_null ? (const char*)dataaddr() : 0;
+ LL5("setval [" << col << "] " << *this);
+ CHK(con.setValue(col.m_num, addr) == 0);
+ return 0;
+}
+
+void
+Val::calc(Par par, unsigned i)
+{
+ const Col& col = m_col;
+ col.m_pk ? calckey(par, i) : calcnokey(par);
+ if (! m_null)
+ col.wellformed(dataaddr());
+}
+
+void
+Val::calckey(Par par, unsigned i)
+{
+ const Col& col = m_col;
+ m_null = false;
+ switch (col.m_type) {
+ case Col::Unsigned:
+ m_uint32 = i;
+ break;
+ case Col::Char:
+ {
+ const Chs* chs = col.m_chs;
+ CHARSET_INFO* cs = chs->m_cs;
+ unsigned n = 0;
+ calckeychars(par, i, n, m_char);
+ // extend by appropriate space
+ (*cs->cset->fill)(cs, (char*)&m_char[n], col.m_bytelength - n, 0x20);
+ }
+ break;
+ case Col::Varchar:
+ {
+ unsigned n = 0;
+ calckeychars(par, i, n, m_varchar + 1);
+ // set length and pad with nulls
+ m_varchar[0] = n;
+ memset(&m_varchar[1 + n], 0, col.m_bytelength - n);
+ }
+ break;
+ case Col::Longvarchar:
+ {
+ unsigned n = 0;
+ calckeychars(par, i, n, m_longvarchar + 2);
+ // set length and pad with nulls
+ m_longvarchar[0] = (n & 0xff);
+ m_longvarchar[1] = (n >> 8);
+ memset(&m_longvarchar[2 + n], 0, col.m_bytelength - n);
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+void
+Val::calckeychars(Par par, unsigned i, unsigned& n, unsigned char* buf)
+{
+ const Col& col = m_col;
+ const Chs* chs = col.m_chs;
+ CHARSET_INFO* cs = chs->m_cs;
+ n = 0;
+ unsigned len = 0;
+ while (len < col.m_length) {
+ if (i % (1 + n) == 0) {
+ break;
+ }
+ const Chr& chr = chs->m_chr[i % maxcharcount];
+ assert(n + chr.m_size <= col.m_bytelength);
+ memcpy(buf + n, chr.m_bytes, chr.m_size);
+ n += chr.m_size;
+ len++;
+ }
+}
+
+void
+Val::calcnokey(Par par)
+{
+ const Col& col = m_col;
+ m_null = false;
+ if (col.m_nullable && urandom(100) < par.m_pctnull) {
+ m_null = true;
+ return;
+ }
+ int r = irandom((par.m_pctrange * par.m_range) / 100);
+ if (par.m_bdir != 0 && urandom(10) != 0) {
+ if (r < 0 && par.m_bdir > 0 || r > 0 && par.m_bdir < 0)
+ r = -r;
+ }
+ unsigned v = par.m_range + r;
+ switch (col.m_type) {
+ case Col::Unsigned:
+ m_uint32 = v;
+ break;
+ case Col::Char:
+ {
+ const Chs* chs = col.m_chs;
+ CHARSET_INFO* cs = chs->m_cs;
+ unsigned n = 0;
+ calcnokeychars(par, n, m_char);
+ // extend by appropriate space
+ (*cs->cset->fill)(cs, (char*)&m_char[n], col.m_bytelength - n, 0x20);
+ }
+ break;
+ case Col::Varchar:
+ {
+ unsigned n = 0;
+ calcnokeychars(par, n, m_varchar + 1);
+ // set length and pad with nulls
+ m_varchar[0] = n;
+ memset(&m_varchar[1 + n], 0, col.m_bytelength - n);
+ }
+ break;
+ case Col::Longvarchar:
+ {
+ unsigned n = 0;
+ calcnokeychars(par, n, m_longvarchar + 2);
+ // set length and pad with nulls
+ m_longvarchar[0] = (n & 0xff);
+ m_longvarchar[1] = (n >> 8);
+ memset(&m_longvarchar[2 + n], 0, col.m_bytelength - n);
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+void
+Val::calcnokeychars(Par par, unsigned& n, unsigned char* buf)
+{
+ const Col& col = m_col;
+ const Chs* chs = col.m_chs;
+ CHARSET_INFO* cs = chs->m_cs;
+ n = 0;
+ unsigned len = 0;
+ while (len < col.m_length) {
+ if (urandom(1 + col.m_bytelength) == 0) {
+ break;
+ }
+ unsigned half = maxcharcount / 2;
+ int r = irandom((par.m_pctrange * half) / 100);
+ if (par.m_bdir != 0 && urandom(10) != 0) {
+ if (r < 0 && par.m_bdir > 0 || r > 0 && par.m_bdir < 0)
+ r = -r;
+ }
+ unsigned i = half + r;
+ assert(i < maxcharcount);
+ const Chr& chr = chs->m_chr[i];
+ assert(n + chr.m_size <= col.m_bytelength);
+ memcpy(buf + n, chr.m_bytes, chr.m_size);
+ n += chr.m_size;
+ len++;
+ }
+}
+
+int
+Val::verify(Par par, const Val& val2) const
+{
+ CHK(cmp(par, val2) == 0);
+ return 0;
+}
+
+int
+Val::cmp(Par par, const Val& val2) const
+{
+ const Col& col = m_col;
+ const Col& col2 = val2.m_col;
+ assert(col.equal(col2));
+ if (m_null || val2.m_null) {
+ if (! m_null)
+ return +1;
+ if (! val2.m_null)
+ return -1;
+ return 0;
+ }
+ // verify data formats
+ col.wellformed(dataaddr());
+ col.wellformed(val2.dataaddr());
+ // compare
+ switch (col.m_type) {
+ case Col::Unsigned:
+ {
+ if (m_uint32 < val2.m_uint32)
+ return -1;
+ if (m_uint32 > val2.m_uint32)
+ return +1;
+ return 0;
+ }
+ break;
+ case Col::Char:
+ {
+ unsigned len = col.m_bytelength;
+ return cmpchars(par, m_char, len, val2.m_char, len);
+ }
+ break;
+ case Col::Varchar:
+ {
+ unsigned len1 = m_varchar[0];
+ unsigned len2 = val2.m_varchar[0];
+ return cmpchars(par, m_varchar + 1, len1, val2.m_varchar + 1, len2);
+ }
+ break;
+ case Col::Longvarchar:
+ {
+ unsigned len1 = m_longvarchar[0] + (m_longvarchar[1] << 8);
+ unsigned len2 = val2.m_longvarchar[0] + (val2.m_longvarchar[1] << 8);
+ return cmpchars(par, m_longvarchar + 2, len1, val2.m_longvarchar + 2, len2);
+ }
+ break;
+ default:
+ break;
+ }
+ assert(false);
+ return 0;
+}
+
+int
+Val::cmpchars(Par par, const unsigned char* buf1, unsigned len1, const unsigned char* buf2, unsigned len2) const
+{
+ const Col& col = m_col;
+ const Chs* chs = col.m_chs;
+ CHARSET_INFO* cs = chs->m_cs;
+ int k;
+ if (! par.m_collsp) {
+ unsigned char x1[maxxmulsize * 8000];
+ unsigned char x2[maxxmulsize * 8000];
+ // make strxfrm pad both to same length
+ unsigned len = maxxmulsize * col.m_bytelength;
+ int n1 = NdbSqlUtil::strnxfrm_bug7284(cs, x1, chs->m_xmul * len, buf1, len1);
+ int n2 = NdbSqlUtil::strnxfrm_bug7284(cs, x2, chs->m_xmul * len, buf2, len2);
+ assert(n1 != -1 && n1 == n2);
+ k = memcmp(x1, x2, n1);
+ } else {
+ k = (*cs->coll->strnncollsp)(cs, buf1, len1, buf2, len2, false);
+ }
+ return k < 0 ? -1 : k > 0 ? +1 : 0;
+}
+
+static void
+printstring(NdbOut& out, const unsigned char* str, unsigned len, bool showlen)
+{
+ char buf[4 * 8000];
+ char *p = buf;
+ *p++ = '[';
+ if (showlen) {
+ sprintf(p, "%u:", len);
+ p += strlen(p);
+ }
+ for (unsigned i = 0; i < len; i++) {
+ unsigned char c = str[i];
+ if (c == '\\') {
+ *p++ = '\\';
+ *p++ = c;
+ } else if (0x20 <= c && c < 0x7e) {
+ *p++ = c;
+ } else {
+ *p++ = '\\';
+ *p++ = hexstr[c >> 4];
+ *p++ = hexstr[c & 15];
+ }
+ }
+ *p++ = ']';
+ *p = 0;
+ out << buf;
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const Val& val)
+{
+ const Col& col = val.m_col;
+ if (val.m_null) {
+ out << "NULL";
+ return out;
+ }
+ switch (col.m_type) {
+ case Col::Unsigned:
+ out << val.m_uint32;
+ break;
+ case Col::Char:
+ {
+ unsigned len = col.m_bytelength;
+ printstring(out, val.m_char, len, false);
+ }
+ break;
+ case Col::Varchar:
+ {
+ unsigned len = val.m_varchar[0];
+ printstring(out, val.m_varchar + 1, len, true);
+ }
+ break;
+ case Col::Longvarchar:
+ {
+ unsigned len = val.m_longvarchar[0] + (val.m_longvarchar[1] << 8);
+ printstring(out, val.m_longvarchar + 2, len, true);
+ }
+ break;
+ default:
+ out << "type" << col.m_type;
+ assert(false);
+ break;
+ }
+ return out;
+}
+
+// Row - table tuple
+
+struct Row {
+ const Tab& m_tab;
+ Val** m_val;
+ bool m_exist;
+ enum Op { NoOp = 0, ReadOp = 1, InsOp = 2, UpdOp = 4, DelOp = 8, AnyOp = 15 };
+ Op m_pending;
+ Row* m_dbrow; // copy of db row before update
+ Row(const Tab& tab);
+ ~Row();
+ void copy(const Row& row2);
+ void calc(Par par, unsigned i, unsigned mask = 0);
+ const Row& dbrow() const;
+ int verify(Par par, const Row& row2) const;
+ int insrow(Par par);
+ int updrow(Par par);
+ int updrow(Par par, const ITab& itab);
+ int delrow(Par par);
+ int delrow(Par par, const ITab& itab);
+ int selrow(Par par);
+ int selrow(Par par, const ITab& itab);
+ int setrow(Par par);
+ int cmp(Par par, const Row& row2) const;
+ int cmp(Par par, const Row& row2, const ITab& itab) const;
+private:
+ Row& operator=(const Row& row2);
+};
+
+Row::Row(const Tab& tab) :
+ m_tab(tab)
+{
+ m_val = new Val* [tab.m_cols];
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Col& col = *tab.m_col[k];
+ m_val[k] = new Val(col);
+ }
+ m_exist = false;
+ m_pending = NoOp;
+ m_dbrow = 0;
+}
+
+Row::~Row()
+{
+ const Tab& tab = m_tab;
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ delete m_val[k];
+ }
+ delete [] m_val;
+ delete m_dbrow;
+}
+
+void
+Row::copy(const Row& row2)
+{
+ const Tab& tab = m_tab;
+ assert(&tab == &row2.m_tab);
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ Val& val = *m_val[k];
+ const Val& val2 = *row2.m_val[k];
+ val.copy(val2);
+ }
+ m_exist = row2.m_exist;
+ m_pending = row2.m_pending;
+ if (row2.m_dbrow == 0) {
+ m_dbrow = 0;
+ } else {
+ assert(row2.m_dbrow->m_dbrow == 0);
+ if (m_dbrow == 0)
+ m_dbrow = new Row(tab);
+ m_dbrow->copy(*row2.m_dbrow);
+ }
+}
+
+void
+Row::calc(Par par, unsigned i, unsigned mask)
+{
+ const Tab& tab = m_tab;
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ if (! (mask & (1 << k))) {
+ Val& val = *m_val[k];
+ val.calc(par, i);
+ }
+ }
+}
+
+const Row&
+Row::dbrow() const
+{
+ if (m_dbrow == 0)
+ return *this;
+ assert(m_pending == Row::UpdOp || m_pending == Row::DelOp);
+ return *m_dbrow;
+}
+
+int
+Row::verify(Par par, const Row& row2) const
+{
+ const Tab& tab = m_tab;
+ const Row& row1 = *this;
+ assert(&row1.m_tab == &row2.m_tab && row1.m_exist && row2.m_exist);
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Val& val1 = *row1.m_val[k];
+ const Val& val2 = *row2.m_val[k];
+ CHK(val1.verify(par, val2) == 0);
+ }
+ return 0;
+}
+
+int
+Row::insrow(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = m_tab;
+ assert(! m_exist);
+ CHK(con.getNdbOperation(tab) == 0);
+ CHKCON(con.m_op->insertTuple() == 0, con);
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Val& val = *m_val[k];
+ const Col& col = val.m_col;
+ if (col.m_pk)
+ CHK(val.equal(par) == 0);
+ }
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Val& val = *m_val[k];
+ const Col& col = val.m_col;
+ if (! col.m_pk)
+ CHK(val.setval(par) == 0);
+ }
+ m_pending = InsOp;
+ return 0;
+}
+
+int
+Row::updrow(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = m_tab;
+ assert(m_exist);
+ CHK(con.getNdbOperation(tab) == 0);
+ CHKCON(con.m_op->updateTuple() == 0, con);
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Val& val = *m_val[k];
+ const Col& col = val.m_col;
+ if (col.m_pk)
+ CHK(val.equal(par) == 0);
+ }
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Val& val = *m_val[k];
+ const Col& col = val.m_col;
+ if (! col.m_pk)
+ CHK(val.setval(par) == 0);
+ }
+ m_pending = UpdOp;
+ return 0;
+}
+
+int
+Row::updrow(Par par, const ITab& itab)
+{
+ Con& con = par.con();
+ const Tab& tab = m_tab;
+ assert(itab.m_type == ITab::UniqueHashIndex && &itab.m_tab == &tab);
+ assert(m_exist);
+ CHK(con.getNdbIndexOperation(itab, tab) == 0);
+ CHKCON(con.m_op->updateTuple() == 0, con);
+ for (unsigned k = 0; k < itab.m_icols; k++) {
+ const ICol& icol = *itab.m_icol[k];
+ const Col& col = icol.m_col;
+ unsigned m = col.m_num;
+ const Val& val = *m_val[m];
+ CHK(val.equal(par, icol) == 0);
+ }
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Val& val = *m_val[k];
+ const Col& col = val.m_col;
+ if (! col.m_pk)
+ CHK(val.setval(par) == 0);
+ }
+ m_pending = UpdOp;
+ return 0;
+}
+
+int
+Row::delrow(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = m_tab;
+ assert(m_exist);
+ CHK(con.getNdbOperation(m_tab) == 0);
+ CHKCON(con.m_op->deleteTuple() == 0, con);
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Val& val = *m_val[k];
+ const Col& col = val.m_col;
+ if (col.m_pk)
+ CHK(val.equal(par) == 0);
+ }
+ m_pending = DelOp;
+ return 0;
+}
+
+int
+Row::delrow(Par par, const ITab& itab)
+{
+ Con& con = par.con();
+ const Tab& tab = m_tab;
+ assert(itab.m_type == ITab::UniqueHashIndex && &itab.m_tab == &tab);
+ assert(m_exist);
+ CHK(con.getNdbIndexOperation(itab, tab) == 0);
+ CHKCON(con.m_op->deleteTuple() == 0, con);
+ for (unsigned k = 0; k < itab.m_icols; k++) {
+ const ICol& icol = *itab.m_icol[k];
+ const Col& col = icol.m_col;
+ unsigned m = col.m_num;
+ const Val& val = *m_val[m];
+ CHK(val.equal(par, icol) == 0);
+ }
+ m_pending = DelOp;
+ return 0;
+}
+
+int
+Row::selrow(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = m_tab;
+ CHK(con.getNdbOperation(m_tab) == 0);
+ CHKCON(con.m_op->readTuple() == 0, con);
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Val& val = *m_val[k];
+ const Col& col = val.m_col;
+ if (col.m_pk)
+ CHK(val.equal(par) == 0);
+ }
+ return 0;
+}
+
+int
+Row::selrow(Par par, const ITab& itab)
+{
+ Con& con = par.con();
+ const Tab& tab = m_tab;
+ assert(itab.m_type == ITab::UniqueHashIndex && &itab.m_tab == &tab);
+ CHK(con.getNdbIndexOperation(itab, tab) == 0);
+ CHKCON(con.m_op->readTuple() == 0, con);
+ for (unsigned k = 0; k < itab.m_icols; k++) {
+ const ICol& icol = *itab.m_icol[k];
+ const Col& col = icol.m_col;
+ unsigned m = col.m_num;
+ const Val& val = *m_val[m];
+ CHK(val.equal(par, icol) == 0);
+ }
+ return 0;
+}
+
+int
+Row::setrow(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = m_tab;
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Val& val = *m_val[k];
+ const Col& col = val.m_col;
+ if (! col.m_pk)
+ CHK(val.setval(par) == 0);
+ }
+ m_pending = UpdOp;
+ return 0;
+}
+
+int
+Row::cmp(Par par, const Row& row2) const
+{
+ const Tab& tab = m_tab;
+ assert(&tab == &row2.m_tab);
+ int c = 0;
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Val& val = *m_val[k];
+ const Val& val2 = *row2.m_val[k];
+ if ((c = val.cmp(par, val2)) != 0)
+ break;
+ }
+ return c;
+}
+
+int
+Row::cmp(Par par, const Row& row2, const ITab& itab) const
+{
+ const Tab& tab = m_tab;
+ int c = 0;
+ for (unsigned i = 0; i < itab.m_icols; i++) {
+ const ICol& icol = *itab.m_icol[i];
+ const Col& col = icol.m_col;
+ unsigned k = col.m_num;
+ assert(k < tab.m_cols);
+ const Val& val = *m_val[k];
+ const Val& val2 = *row2.m_val[k];
+ if ((c = val.cmp(par, val2)) != 0)
+ break;
+ }
+ return c;
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const Row::Op op)
+{
+ if (op == Row::NoOp)
+ out << "NoOp";
+ else if (op == Row::InsOp)
+ out << "InsOp";
+ else if (op == Row::UpdOp)
+ out << "UpdOp";
+ else if (op == Row::DelOp)
+ out << "DelOp";
+ else
+ out << op;
+ return out;
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const Row& row)
+{
+ const Tab& tab = row.m_tab;
+ for (unsigned i = 0; i < tab.m_cols; i++) {
+ if (i > 0)
+ out << " ";
+ out << *row.m_val[i];
+ }
+ out << " exist=" << row.m_exist;
+ if (row.m_pending)
+ out << " pending=" << row.m_pending;
+ if (row.m_dbrow != 0)
+ out << " [dbrow=" << *row.m_dbrow << "]";
+ return out;
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const Row* rowptr)
+{
+ if (rowptr == 0)
+ out << "null";
+ else
+ out << *rowptr;
+ return out;
+}
+
+// Set - set of table tuples
+
+struct Set {
+ const Tab& m_tab;
+ unsigned m_rows;
+ Row** m_row;
+ unsigned* m_rowkey; // maps row number (from 0) in scan to tuple key
+ Row* m_keyrow;
+ NdbRecAttr** m_rec;
+ Set(const Tab& tab, unsigned rows);
+ ~Set();
+ void reset();
+ unsigned count() const;
+ // old and new values
+ bool exist(unsigned i) const;
+ void dbsave(unsigned i);
+ void calc(Par par, unsigned i, unsigned mask = 0);
+ bool pending(unsigned i, unsigned mask) const;
+ void notpending(unsigned i);
+ void notpending(const Lst& lst);
+ void dbdiscard(unsigned i);
+ void dbdiscard(const Lst& lst);
+ const Row& dbrow(unsigned i) const;
+ // operations
+ int insrow(Par par, unsigned i);
+ int updrow(Par par, unsigned i);
+ int updrow(Par par, const ITab& itab, unsigned i);
+ int delrow(Par par, unsigned i);
+ int delrow(Par par, const ITab& itab, unsigned i);
+ int selrow(Par par, const Row& keyrow);
+ int selrow(Par par, const ITab& itab, const Row& keyrow);
+ // set and get
+ void setkey(Par par, const Row& keyrow);
+ void setkey(Par par, const ITab& itab, const Row& keyrow);
+ int setrow(Par par, unsigned i);
+ int getval(Par par);
+ int getkey(Par par, unsigned* i);
+ int putval(unsigned i, bool force, unsigned n = ~0);
+ // verify
+ int verify(Par par, const Set& set2) const;
+ int verifyorder(Par par, const ITab& itab, bool descending) const;
+ // protect structure
+ NdbMutex* m_mutex;
+ void lock() const {
+ NdbMutex_Lock(m_mutex);
+ }
+ void unlock() const {
+ NdbMutex_Unlock(m_mutex);
+ }
+private:
+ Set& operator=(const Set& set2);
+};
+
+Set::Set(const Tab& tab, unsigned rows) :
+ m_tab(tab)
+{
+ m_rows = rows;
+ m_row = new Row* [m_rows];
+ for (unsigned i = 0; i < m_rows; i++) {
+ // allocate on need to save space
+ m_row[i] = 0;
+ }
+ m_rowkey = new unsigned [m_rows];
+ for (unsigned n = 0; n < m_rows; n++) {
+ // initialize to null
+ m_rowkey[n] = ~0;
+ }
+ m_keyrow = new Row(tab);
+ m_rec = new NdbRecAttr* [tab.m_cols];
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ m_rec[k] = 0;
+ }
+ m_mutex = NdbMutex_Create();
+ assert(m_mutex != 0);
+}
+
+Set::~Set()
+{
+ for (unsigned i = 0; i < m_rows; i++) {
+ delete m_row[i];
+ }
+ delete [] m_row;
+ delete [] m_rowkey;
+ delete m_keyrow;
+ delete [] m_rec;
+ NdbMutex_Destroy(m_mutex);
+}
+
+void
+Set::reset()
+{
+ for (unsigned i = 0; i < m_rows; i++) {
+ if (m_row[i] != 0) {
+ Row& row = *m_row[i];
+ row.m_exist = false;
+ }
+ }
+}
+
+unsigned
+Set::count() const
+{
+ unsigned count = 0;
+ for (unsigned i = 0; i < m_rows; i++) {
+ if (m_row[i] != 0) {
+ Row& row = *m_row[i];
+ if (row.m_exist)
+ count++;
+ }
+ }
+ return count;
+}
+
+// old and new values
+
+bool
+Set::exist(unsigned i) const
+{
+ assert(i < m_rows);
+ if (m_row[i] == 0) // not allocated => not exist
+ return false;
+ return m_row[i]->m_exist;
+}
+
+void
+Set::dbsave(unsigned i)
+{
+ const Tab& tab = m_tab;
+ assert(i < m_rows && m_row[i] != 0);
+ Row& row = *m_row[i];
+ LL5("dbsave " << i << ": " << row);
+ assert(row.m_exist && ! row.m_pending && row.m_dbrow == 0);
+ // could swap pointers but making copy is safer
+ Row* rowptr = new Row(tab);
+ rowptr->copy(row);
+ row.m_dbrow = rowptr;
+}
+
+void
+Set::calc(Par par, unsigned i, unsigned mask)
+{
+ const Tab& tab = m_tab;
+ if (m_row[i] == 0)
+ m_row[i] = new Row(tab);
+ Row& row = *m_row[i];
+ row.calc(par, i, mask);
+}
+
+bool
+Set::pending(unsigned i, unsigned mask) const
+{
+ assert(i < m_rows);
+ if (m_row[i] == 0) // not allocated => not pending
+ return Row::NoOp;
+ return m_row[i]->m_pending & mask;
+}
+
+void
+Set::notpending(unsigned i)
+{
+ assert(m_row[i] != 0);
+ Row& row = *m_row[i];
+ if (row.m_pending == Row::InsOp) {
+ row.m_exist = true;
+ } else if (row.m_pending == Row::UpdOp) {
+ ;
+ } else if (row.m_pending == Row::DelOp) {
+ row.m_exist = false;
+ }
+ row.m_pending = Row::NoOp;
+}
+
+void
+Set::notpending(const Lst& lst)
+{
+ for (unsigned j = 0; j < lst.m_cnt; j++) {
+ unsigned i = lst.m_arr[j];
+ notpending(i);
+ }
+}
+
+void
+Set::dbdiscard(unsigned i)
+{
+ assert(m_row[i] != 0);
+ Row& row = *m_row[i];
+ LL5("dbdiscard " << i << ": " << row);
+ assert(row.m_dbrow != 0);
+ delete row.m_dbrow;
+ row.m_dbrow = 0;
+}
+
+const Row&
+Set::dbrow(unsigned i) const
+{
+ assert(m_row[i] != 0);
+ Row& row = *m_row[i];
+ return row.dbrow();
+}
+
+void
+Set::dbdiscard(const Lst& lst)
+{
+ for (unsigned j = 0; j < lst.m_cnt; j++) {
+ unsigned i = lst.m_arr[j];
+ dbdiscard(i);
+ }
+}
+
+// operations
+
+int
+Set::insrow(Par par, unsigned i)
+{
+ assert(m_row[i] != 0);
+ Row& row = *m_row[i];
+ CHK(row.insrow(par) == 0);
+ return 0;
+}
+
+int
+Set::updrow(Par par, unsigned i)
+{
+ assert(m_row[i] != 0);
+ Row& row = *m_row[i];
+ CHK(row.updrow(par) == 0);
+ return 0;
+}
+
+int
+Set::updrow(Par par, const ITab& itab, unsigned i)
+{
+ assert(m_row[i] != 0);
+ Row& row = *m_row[i];
+ CHK(row.updrow(par, itab) == 0);
+ return 0;
+}
+
+int
+Set::delrow(Par par, unsigned i)
+{
+ assert(m_row[i] != 0);
+ Row& row = *m_row[i];
+ CHK(row.delrow(par) == 0);
+ return 0;
+}
+
+int
+Set::delrow(Par par, const ITab& itab, unsigned i)
+{
+ assert(m_row[i] != 0);
+ Row& row = *m_row[i];
+ CHK(row.delrow(par, itab) == 0);
+ return 0;
+}
+
+int
+Set::selrow(Par par, const Row& keyrow)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ setkey(par, keyrow);
+ LL5("selrow " << tab.m_name << ": keyrow: " << keyrow);
+ CHK(m_keyrow->selrow(par) == 0);
+ CHK(getval(par) == 0);
+ return 0;
+}
+
+int
+Set::selrow(Par par, const ITab& itab, const Row& keyrow)
+{
+ Con& con = par.con();
+ setkey(par, itab, keyrow);
+ LL5("selrow " << itab.m_name << ": keyrow: " << keyrow);
+ CHK(m_keyrow->selrow(par, itab) == 0);
+ CHK(getval(par) == 0);
+ return 0;
+}
+
+// set and get
+
+void
+Set::setkey(Par par, const Row& keyrow)
+{
+ const Tab& tab = m_tab;
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ const Col& col = *tab.m_col[k];
+ if (col.m_pk) {
+ Val& val1 = *m_keyrow->m_val[k];
+ const Val& val2 = *keyrow.dbrow().m_val[k];
+ val1.copy(val2);
+ }
+ }
+}
+
+void
+Set::setkey(Par par, const ITab& itab, const Row& keyrow)
+{
+ const Tab& tab = m_tab;
+ for (unsigned k = 0; k < itab.m_icols; k++) {
+ const ICol& icol = *itab.m_icol[k];
+ const Col& col = icol.m_col;
+ unsigned m = col.m_num;
+ Val& val1 = *m_keyrow->m_val[m];
+ const Val& val2 = *keyrow.dbrow().m_val[m];
+ val1.copy(val2);
+ }
+}
+
+int
+Set::setrow(Par par, unsigned i)
+{
+ Con& con = par.con();
+ assert(m_row[i] != 0);
+ CHK(m_row[i]->setrow(par) == 0);
+ return 0;
+}
+
+int
+Set::getval(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = m_tab;
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ CHK(con.getValue(k, m_rec[k]) == 0);
+ }
+ return 0;
+}
+
+int
+Set::getkey(Par par, unsigned* i)
+{
+ const Tab& tab = m_tab;
+ unsigned k = tab.m_keycol;
+ assert(m_rec[k] != 0);
+ const char* aRef = m_rec[k]->aRef();
+ Uint32 key = *(const Uint32*)aRef;
+ CHK(key < m_rows);
+ *i = key;
+ return 0;
+}
+
+int
+Set::putval(unsigned i, bool force, unsigned n)
+{
+ const Tab& tab = m_tab;
+ if (m_row[i] == 0)
+ m_row[i] = new Row(tab);
+ Row& row = *m_row[i];
+ CHK(! row.m_exist || force);
+ for (unsigned k = 0; k < tab.m_cols; k++) {
+ Val& val = *row.m_val[k];
+ NdbRecAttr* rec = m_rec[k];
+ assert(rec != 0);
+ if (rec->isNULL()) {
+ val.m_null = true;
+ continue;
+ }
+ const char* aRef = m_rec[k]->aRef();
+ val.copy(aRef);
+ val.m_null = false;
+ }
+ if (! row.m_exist)
+ row.m_exist = true;
+ if (n != ~0)
+ m_rowkey[n] = i;
+ return 0;
+}
+
+// verify
+
+int
+Set::verify(Par par, const Set& set2) const
+{
+ assert(&m_tab == &set2.m_tab && m_rows == set2.m_rows);
+ LL4("verify set1 count=" << count() << " vs set2 count=" << set2.count());
+ for (unsigned i = 0; i < m_rows; i++) {
+ bool ok = true;
+ if (exist(i) != set2.exist(i)) {
+ ok = false;
+ } else if (exist(i)) {
+ if (dbrow(i).verify(par, set2.dbrow(i)) != 0)
+ ok = false;
+ }
+ if (! ok) {
+ LL1("verify failed: key=" << i << " row1=" << m_row[i] << " row2=" << set2.m_row[i]);
+ CHK(0 == 1);
+ }
+ }
+ return 0;
+}
+
+int
+Set::verifyorder(Par par, const ITab& itab, bool descending) const
+{
+ const Tab& tab = m_tab;
+ for (unsigned n = 0; n < m_rows; n++) {
+ unsigned i2 = m_rowkey[n];
+ if (i2 == ~0)
+ break;
+ if (n == 0)
+ continue;
+ unsigned i1 = m_rowkey[n - 1];
+ assert(i1 < m_rows && i2 < m_rows);
+ const Row& row1 = *m_row[i1];
+ const Row& row2 = *m_row[i2];
+ assert(row1.m_exist && row2.m_exist);
+ if (! descending)
+ CHK(row1.cmp(par, row2, itab) <= 0);
+ else
+ CHK(row1.cmp(par, row2, itab) >= 0);
+ }
+ return 0;
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const Set& set)
+{
+ for (unsigned i = 0; i < set.m_rows; i++) {
+ const Row& row = *set.m_row[i];
+ if (i > 0)
+ out << endl;
+ out << row;
+ }
+ return out;
+}
+
+// BVal - range scan bound
+
+struct BVal : public Val {
+ const ICol& m_icol;
+ int m_type;
+ BVal(const ICol& icol);
+ int setbnd(Par par) const;
+ int setflt(Par par) const;
+};
+
+BVal::BVal(const ICol& icol) :
+ Val(icol.m_col),
+ m_icol(icol)
+{
+}
+
+int
+BVal::setbnd(Par par) const
+{
+ Con& con = par.con();
+ assert(g_compare_null || ! m_null);
+ const char* addr = ! m_null ? (const char*)dataaddr() : 0;
+ const ICol& icol = m_icol;
+ CHK(con.setBound(icol.m_num, m_type, addr) == 0);
+ return 0;
+}
+
+int
+BVal::setflt(Par par) const
+{
+ static unsigned index_bound_to_filter_bound[5] = {
+ NdbScanFilter::COND_GE,
+ NdbScanFilter::COND_GT,
+ NdbScanFilter::COND_LE,
+ NdbScanFilter::COND_LT,
+ NdbScanFilter::COND_EQ
+ };
+ Con& con = par.con();
+ assert(g_compare_null || ! m_null);
+ const char* addr = ! m_null ? (const char*)dataaddr() : 0;
+ const ICol& icol = m_icol;
+ const Col& col = icol.m_col;
+ unsigned length = col.m_bytesize;
+ unsigned cond = index_bound_to_filter_bound[m_type];
+ CHK(con.setFilter(col.m_num, cond, addr, length) == 0);
+ return 0;
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const BVal& bval)
+{
+ const ICol& icol = bval.m_icol;
+ const Col& col = icol.m_col;
+ const Val& val = bval;
+ out << "type=" << bval.m_type;
+ out << " icol=" << icol.m_num;
+ out << " col=" << col.m_num << "," << col.m_name;
+ out << " value=" << val;
+ return out;
+}
+
+// BSet - set of bounds
+
+struct BSet {
+ const Tab& m_tab;
+ const ITab& m_itab;
+ unsigned m_alloc;
+ unsigned m_bvals;
+ BVal** m_bval;
+ BSet(const Tab& tab, const ITab& itab, unsigned rows);
+ ~BSet();
+ void reset();
+ void calc(Par par);
+ void calcpk(Par par, unsigned i);
+ int setbnd(Par par) const;
+ int setflt(Par par) const;
+ void filter(Par par, const Set& set, Set& set2) const;
+};
+
+BSet::BSet(const Tab& tab, const ITab& itab, unsigned rows) :
+ m_tab(tab),
+ m_itab(itab),
+ m_alloc(2 * itab.m_icols),
+ m_bvals(0)
+{
+ m_bval = new BVal* [m_alloc];
+ for (unsigned i = 0; i < m_alloc; i++) {
+ m_bval[i] = 0;
+ }
+}
+
+BSet::~BSet()
+{
+ delete [] m_bval;
+}
+
+void
+BSet::reset()
+{
+ while (m_bvals > 0) {
+ unsigned i = --m_bvals;
+ delete m_bval[i];
+ m_bval[i] = 0;
+ }
+}
+
+void
+BSet::calc(Par par)
+{
+ const ITab& itab = m_itab;
+ par.m_pctrange = par.m_pctbrange;
+ reset();
+ for (unsigned k = 0; k < itab.m_icols; k++) {
+ const ICol& icol = *itab.m_icol[k];
+ const Col& col = icol.m_col;
+ for (unsigned i = 0; i <= 1; i++) {
+ if (m_bvals == 0 && urandom(100) == 0)
+ return;
+ if (m_bvals != 0 && urandom(3) == 0)
+ return;
+ assert(m_bvals < m_alloc);
+ BVal& bval = *new BVal(icol);
+ m_bval[m_bvals++] = &bval;
+ bval.m_null = false;
+ unsigned sel;
+ do {
+ // equality bound only on i==0
+ sel = urandom(5 - i);
+ } while (strchr(par.m_bound, '0' + sel) == 0);
+ if (sel < 2)
+ bval.m_type = 0 | (1 << i);
+ else if (sel < 4)
+ bval.m_type = 1 | (1 << i);
+ else
+ bval.m_type = 4;
+ if (k + 1 < itab.m_icols)
+ bval.m_type = 4;
+ if (! g_compare_null)
+ par.m_pctnull = 0;
+ if (bval.m_type == 0 || bval.m_type == 1)
+ par.m_bdir = -1;
+ if (bval.m_type == 2 || bval.m_type == 3)
+ par.m_bdir = +1;
+ do {
+ bval.calcnokey(par);
+ if (i == 1) {
+ assert(m_bvals >= 2);
+ const BVal& bv1 = *m_bval[m_bvals - 2];
+ const BVal& bv2 = *m_bval[m_bvals - 1];
+ if (bv1.cmp(par, bv2) > 0 && urandom(100) != 0)
+ continue;
+ }
+ } while (0);
+ // equality bound only once
+ if (bval.m_type == 4)
+ break;
+ }
+ }
+}
+
+void
+BSet::calcpk(Par par, unsigned i)
+{
+ const ITab& itab = m_itab;
+ reset();
+ for (unsigned k = 0; k < itab.m_icols; k++) {
+ const ICol& icol = *itab.m_icol[k];
+ const Col& col = icol.m_col;
+ assert(col.m_pk);
+ assert(m_bvals < m_alloc);
+ BVal& bval = *new BVal(icol);
+ m_bval[m_bvals++] = &bval;
+ bval.m_type = 4;
+ bval.calc(par, i);
+ }
+}
+
+int
+BSet::setbnd(Par par) const
+{
+ if (m_bvals != 0) {
+ unsigned p1 = urandom(m_bvals);
+ unsigned p2 = 10009; // prime
+ // random order
+ for (unsigned j = 0; j < m_bvals; j++) {
+ unsigned k = p1 + p2 * j;
+ const BVal& bval = *m_bval[k % m_bvals];
+ CHK(bval.setbnd(par) == 0);
+ }
+ // duplicate
+ if (urandom(5) == 0) {
+ unsigned k = urandom(m_bvals);
+ const BVal& bval = *m_bval[k];
+ CHK(bval.setbnd(par) == 0);
+ }
+ }
+ return 0;
+}
+
+int
+BSet::setflt(Par par) const
+{
+ Con& con = par.con();
+ CHK(con.getNdbScanFilter() == 0);
+ CHK(con.beginFilter(NdbScanFilter::AND) == 0);
+ if (m_bvals != 0) {
+ unsigned p1 = urandom(m_bvals);
+ unsigned p2 = 10009; // prime
+ const unsigned extras = 5;
+ // random order
+ for (unsigned j = 0; j < m_bvals + extras; j++) {
+ unsigned k = p1 + p2 * j;
+ const BVal& bval = *m_bval[k % m_bvals];
+ CHK(bval.setflt(par) == 0);
+ }
+ // duplicate
+ if (urandom(5) == 0) {
+ unsigned k = urandom(m_bvals);
+ const BVal& bval = *m_bval[k];
+ CHK(bval.setflt(par) == 0);
+ }
+ }
+ CHK(con.endFilter() == 0);
+ return 0;
+}
+
+void
+BSet::filter(Par par, const Set& set, Set& set2) const
+{
+ const Tab& tab = m_tab;
+ const ITab& itab = m_itab;
+ assert(&tab == &set2.m_tab && set.m_rows == set2.m_rows);
+ assert(set2.count() == 0);
+ for (unsigned i = 0; i < set.m_rows; i++) {
+ if (! set.exist(i))
+ continue;
+ set.lock();
+ const Row& row = set.dbrow(i);
+ set.unlock();
+ if (! g_store_null_key) {
+ bool ok1 = false;
+ for (unsigned k = 0; k < itab.m_icols; k++) {
+ const ICol& icol = *itab.m_icol[k];
+ const Col& col = icol.m_col;
+ const Val& val = *row.m_val[col.m_num];
+ if (! val.m_null) {
+ ok1 = true;
+ break;
+ }
+ }
+ if (! ok1)
+ continue;
+ }
+ bool ok2 = true;
+ for (unsigned j = 0; j < m_bvals; j++) {
+ const BVal& bval = *m_bval[j];
+ const ICol& icol = bval.m_icol;
+ const Col& col = icol.m_col;
+ const Val& val = *row.m_val[col.m_num];
+ int ret = bval.cmp(par, val);
+ LL5("cmp: ret=" << ret << " " << bval << " vs " << val);
+ if (bval.m_type == 0)
+ ok2 = (ret <= 0);
+ else if (bval.m_type == 1)
+ ok2 = (ret < 0);
+ else if (bval.m_type == 2)
+ ok2 = (ret >= 0);
+ else if (bval.m_type == 3)
+ ok2 = (ret > 0);
+ else if (bval.m_type == 4)
+ ok2 = (ret == 0);
+ else {
+ assert(false);
+ }
+ if (! ok2)
+ break;
+ }
+ if (! ok2)
+ continue;
+ if (set2.m_row[i] == 0)
+ set2.m_row[i] = new Row(tab);
+ Row& row2 = *set2.m_row[i];
+ assert(! row2.m_exist);
+ row2.copy(row);
+ }
+}
+
+static NdbOut&
+operator<<(NdbOut& out, const BSet& bset)
+{
+ out << "bounds=" << bset.m_bvals;
+ for (unsigned j = 0; j < bset.m_bvals; j++) {
+ const BVal& bval = *bset.m_bval[j];
+ out << " [bound " << j << ": " << bval << "]";
+ }
+ return out;
+}
+
+// pk operations
+
+static int
+pkinsert(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ Set& set = par.set();
+ LL3("pkinsert " << tab.m_name);
+ CHK(con.startTransaction() == 0);
+ Lst lst;
+ for (unsigned j = 0; j < par.m_rows; j++) {
+ unsigned j2 = ! par.m_randomkey ? j : urandom(par.m_rows);
+ unsigned i = thrrow(par, j2);
+ set.lock();
+ if (set.exist(i) || set.pending(i, Row::AnyOp)) {
+ set.unlock();
+ continue;
+ }
+ set.calc(par, i);
+ CHK(set.insrow(par, i) == 0);
+ set.unlock();
+ LL4("pkinsert " << i << ": " << *set.m_row[i]);
+ lst.push(i);
+ if (lst.cnt() == par.m_batch) {
+ bool deadlock = par.m_deadlock;
+ CHK(con.execute(Commit, deadlock) == 0);
+ con.closeTransaction();
+ if (deadlock) {
+ LL1("pkinsert: stop on deadlock [at 1]");
+ return 0;
+ }
+ set.lock();
+ set.notpending(lst);
+ set.unlock();
+ lst.reset();
+ CHK(con.startTransaction() == 0);
+ }
+ }
+ if (lst.cnt() != 0) {
+ bool deadlock = par.m_deadlock;
+ CHK(con.execute(Commit, deadlock) == 0);
+ con.closeTransaction();
+ if (deadlock) {
+ LL1("pkinsert: stop on deadlock [at 2]");
+ return 0;
+ }
+ set.lock();
+ set.notpending(lst);
+ set.unlock();
+ return 0;
+ }
+ con.closeTransaction();
+ return 0;
+}
+
+static int
+pkupdate(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ Set& set = par.set();
+ LL3("pkupdate " << tab.m_name);
+ CHK(con.startTransaction() == 0);
+ Lst lst;
+ bool deadlock = false;
+ for (unsigned j = 0; j < par.m_rows; j++) {
+ unsigned j2 = ! par.m_randomkey ? j : urandom(par.m_rows);
+ unsigned i = thrrow(par, j2);
+ set.lock();
+ if (! set.exist(i) || set.pending(i, Row::AnyOp)) {
+ set.unlock();
+ continue;
+ }
+ set.dbsave(i);
+ set.calc(par, i);
+ CHK(set.updrow(par, i) == 0);
+ set.unlock();
+ LL4("pkupdate " << i << ": " << *set.m_row[i]);
+ lst.push(i);
+ if (lst.cnt() == par.m_batch) {
+ deadlock = par.m_deadlock;
+ CHK(con.execute(Commit, deadlock) == 0);
+ if (deadlock) {
+ LL1("pkupdate: stop on deadlock [at 1]");
+ break;
+ }
+ con.closeTransaction();
+ set.lock();
+ set.notpending(lst);
+ set.dbdiscard(lst);
+ set.unlock();
+ lst.reset();
+ CHK(con.startTransaction() == 0);
+ }
+ }
+ if (! deadlock && lst.cnt() != 0) {
+ deadlock = par.m_deadlock;
+ CHK(con.execute(Commit, deadlock) == 0);
+ if (deadlock) {
+ LL1("pkupdate: stop on deadlock [at 1]");
+ } else {
+ set.lock();
+ set.notpending(lst);
+ set.dbdiscard(lst);
+ set.unlock();
+ }
+ }
+ con.closeTransaction();
+ return 0;
+}
+
+static int
+pkdelete(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ Set& set = par.set();
+ LL3("pkdelete " << tab.m_name);
+ CHK(con.startTransaction() == 0);
+ Lst lst;
+ bool deadlock = false;
+ for (unsigned j = 0; j < par.m_rows; j++) {
+ unsigned j2 = ! par.m_randomkey ? j : urandom(par.m_rows);
+ unsigned i = thrrow(par, j2);
+ set.lock();
+ if (! set.exist(i) || set.pending(i, Row::AnyOp)) {
+ set.unlock();
+ continue;
+ }
+ CHK(set.delrow(par, i) == 0);
+ set.unlock();
+ LL4("pkdelete " << i << ": " << *set.m_row[i]);
+ lst.push(i);
+ if (lst.cnt() == par.m_batch) {
+ deadlock = par.m_deadlock;
+ CHK(con.execute(Commit, deadlock) == 0);
+ if (deadlock) {
+ LL1("pkdelete: stop on deadlock [at 1]");
+ break;
+ }
+ con.closeTransaction();
+ set.lock();
+ set.notpending(lst);
+ set.unlock();
+ lst.reset();
+ CHK(con.startTransaction() == 0);
+ }
+ }
+ if (! deadlock && lst.cnt() != 0) {
+ deadlock = par.m_deadlock;
+ CHK(con.execute(Commit, deadlock) == 0);
+ if (deadlock) {
+ LL1("pkdelete: stop on deadlock [at 2]");
+ } else {
+ set.lock();
+ set.notpending(lst);
+ set.unlock();
+ }
+ }
+ con.closeTransaction();
+ return 0;
+}
+
+static int
+pkread(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ Set& set = par.set();
+ LL3("pkread " << tab.m_name << " verify=" << par.m_verify);
+ // expected
+ const Set& set1 = set;
+ Set set2(tab, set.m_rows);
+ for (unsigned i = 0; i < set.m_rows; i++) {
+ set.lock();
+ if (! set.exist(i)) {
+ set.unlock();
+ continue;
+ }
+ set.unlock();
+ CHK(con.startTransaction() == 0);
+ CHK(set2.selrow(par, *set1.m_row[i]) == 0);
+ CHK(con.execute(Commit) == 0);
+ unsigned i2 = (unsigned)-1;
+ CHK(set2.getkey(par, &i2) == 0 && i == i2);
+ CHK(set2.putval(i, false) == 0);
+ LL4("row " << set2.count() << ": " << *set2.m_row[i]);
+ con.closeTransaction();
+ }
+ if (par.m_verify)
+ CHK(set1.verify(par, set2) == 0);
+ return 0;
+}
+
+static int
+pkreadfast(Par par, unsigned count)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ const Set& set = par.set();
+ LL3("pkfast " << tab.m_name);
+ Row keyrow(tab);
+ // not batched on purpose
+ for (unsigned j = 0; j < count; j++) {
+ unsigned i = urandom(set.m_rows);
+ assert(set.exist(i));
+ CHK(con.startTransaction() == 0);
+ // define key
+ keyrow.calc(par, i);
+ CHK(keyrow.selrow(par) == 0);
+ NdbRecAttr* rec;
+ // get 1st column
+ CHK(con.getValue((Uint32)0, rec) == 0);
+ CHK(con.execute(Commit) == 0);
+ con.closeTransaction();
+ }
+ return 0;
+}
+
+// hash index operations
+
+static int
+hashindexupdate(Par par, const ITab& itab)
+{
+ Con& con = par.con();
+ Set& set = par.set();
+ LL3("hashindexupdate " << itab.m_name);
+ CHK(con.startTransaction() == 0);
+ Lst lst;
+ bool deadlock = false;
+ for (unsigned j = 0; j < par.m_rows; j++) {
+ unsigned j2 = ! par.m_randomkey ? j : urandom(par.m_rows);
+ unsigned i = thrrow(par, j2);
+ set.lock();
+ if (! set.exist(i) || set.pending(i, Row::AnyOp)) {
+ set.unlock();
+ continue;
+ }
+ set.dbsave(i);
+ // index key columns are not re-calculated
+ set.calc(par, i, itab.m_colmask);
+ CHK(set.updrow(par, itab, i) == 0);
+ set.unlock();
+ LL4("hashindexupdate " << i << ": " << *set.m_row[i]);
+ lst.push(i);
+ if (lst.cnt() == par.m_batch) {
+ deadlock = par.m_deadlock;
+ CHK(con.execute(Commit, deadlock) == 0);
+ if (deadlock) {
+ LL1("hashindexupdate: stop on deadlock [at 1]");
+ break;
+ }
+ con.closeTransaction();
+ set.lock();
+ set.notpending(lst);
+ set.dbdiscard(lst);
+ set.unlock();
+ lst.reset();
+ CHK(con.startTransaction() == 0);
+ }
+ }
+ if (! deadlock && lst.cnt() != 0) {
+ deadlock = par.m_deadlock;
+ CHK(con.execute(Commit, deadlock) == 0);
+ if (deadlock) {
+ LL1("hashindexupdate: stop on deadlock [at 1]");
+ } else {
+ set.lock();
+ set.notpending(lst);
+ set.dbdiscard(lst);
+ set.unlock();
+ }
+ }
+ con.closeTransaction();
+ return 0;
+}
+
+static int
+hashindexdelete(Par par, const ITab& itab)
+{
+ Con& con = par.con();
+ Set& set = par.set();
+ LL3("hashindexdelete " << itab.m_name);
+ CHK(con.startTransaction() == 0);
+ Lst lst;
+ bool deadlock = false;
+ for (unsigned j = 0; j < par.m_rows; j++) {
+ unsigned j2 = ! par.m_randomkey ? j : urandom(par.m_rows);
+ unsigned i = thrrow(par, j2);
+ set.lock();
+ if (! set.exist(i) || set.pending(i, Row::AnyOp)) {
+ set.unlock();
+ continue;
+ }
+ CHK(set.delrow(par, itab, i) == 0);
+ set.unlock();
+ LL4("hashindexdelete " << i << ": " << *set.m_row[i]);
+ lst.push(i);
+ if (lst.cnt() == par.m_batch) {
+ deadlock = par.m_deadlock;
+ CHK(con.execute(Commit, deadlock) == 0);
+ if (deadlock) {
+ LL1("hashindexdelete: stop on deadlock [at 1]");
+ break;
+ }
+ con.closeTransaction();
+ set.lock();
+ set.notpending(lst);
+ set.unlock();
+ lst.reset();
+ CHK(con.startTransaction() == 0);
+ }
+ }
+ if (! deadlock && lst.cnt() != 0) {
+ deadlock = par.m_deadlock;
+ CHK(con.execute(Commit, deadlock) == 0);
+ if (deadlock) {
+ LL1("hashindexdelete: stop on deadlock [at 2]");
+ } else {
+ set.lock();
+ set.notpending(lst);
+ set.unlock();
+ }
+ }
+ con.closeTransaction();
+ return 0;
+}
+
+static int
+hashindexread(Par par, const ITab& itab)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ Set& set = par.set();
+ LL3("hashindexread " << itab.m_name << " verify=" << par.m_verify);
+ // expected
+ const Set& set1 = set;
+ Set set2(tab, set.m_rows);
+ for (unsigned i = 0; i < set.m_rows; i++) {
+ set.lock();
+ if (! set.exist(i)) {
+ set.unlock();
+ continue;
+ }
+ set.unlock();
+ CHK(con.startTransaction() == 0);
+ CHK(set2.selrow(par, itab, *set1.m_row[i]) == 0);
+ CHK(con.execute(Commit) == 0);
+ unsigned i2 = (unsigned)-1;
+ CHK(set2.getkey(par, &i2) == 0 && i == i2);
+ CHK(set2.putval(i, false) == 0);
+ LL4("row " << set2.count() << ": " << *set2.m_row[i]);
+ con.closeTransaction();
+ }
+ if (par.m_verify)
+ CHK(set1.verify(par, set2) == 0);
+ return 0;
+}
+
+// scan read
+
+static int
+scanreadtable(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ const Set& set = par.set();
+ // expected
+ const Set& set1 = set;
+ LL3("scanread " << tab.m_name << " lockmode=" << par.m_lockmode << " expect=" << set1.count() << " verify=" << par.m_verify);
+ Set set2(tab, set.m_rows);
+ CHK(con.startTransaction() == 0);
+ CHK(con.getNdbScanOperation(tab) == 0);
+ CHK(con.readTuples(par) == 0);
+ set2.getval(par);
+ CHK(con.executeScan() == 0);
+ unsigned n = 0;
+ bool deadlock = false;
+ while (1) {
+ int ret;
+ deadlock = par.m_deadlock;
+ CHK((ret = con.nextScanResult(true, deadlock)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ if (deadlock) {
+ LL1("scanreadtable: stop on deadlock");
+ break;
+ }
+ unsigned i = (unsigned)-1;
+ CHK(set2.getkey(par, &i) == 0);
+ CHK(set2.putval(i, false, n) == 0);
+ LL4("row " << n << ": " << *set2.m_row[i]);
+ n++;
+ }
+ con.closeTransaction();
+ if (par.m_verify)
+ CHK(set1.verify(par, set2) == 0);
+ LL3("scanread " << tab.m_name << " done rows=" << n);
+ return 0;
+}
+
+static int
+scanreadtablefast(Par par, unsigned countcheck)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ const Set& set = par.set();
+ LL3("scanfast " << tab.m_name);
+ CHK(con.startTransaction() == 0);
+ CHK(con.getNdbScanOperation(tab) == 0);
+ CHK(con.readTuples(par) == 0);
+ // get 1st column
+ NdbRecAttr* rec;
+ CHK(con.getValue((Uint32)0, rec) == 0);
+ CHK(con.executeScan() == 0);
+ unsigned count = 0;
+ while (1) {
+ int ret;
+ CHK((ret = con.nextScanResult(true)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ count++;
+ }
+ con.closeTransaction();
+ CHK(count == countcheck);
+ return 0;
+}
+
+static int
+scanreadindex(Par par, const ITab& itab, BSet& bset, bool calc)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ const Set& set = par.set();
+ Set set1(tab, set.m_rows);
+ if (calc) {
+ while (true) {
+ bset.calc(par);
+ bset.filter(par, set, set1);
+ unsigned n = set1.count();
+ // prefer proper subset
+ if (0 < n && n < set.m_rows)
+ break;
+ if (urandom(3) == 0)
+ break;
+ set1.reset();
+ }
+ } else {
+ bset.filter(par, set, set1);
+ }
+ LL3("scanread " << itab.m_name << " " << bset << " lockmode=" << par.m_lockmode << " expect=" << set1.count() << " verify=" << par.m_verify << " ordered=" << par.m_ordered << " descending=" << par.m_descending);
+ Set set2(tab, set.m_rows);
+ CHK(con.startTransaction() == 0);
+ CHK(con.getNdbIndexScanOperation(itab, tab) == 0);
+ CHK(con.readIndexTuples(par) == 0);
+ CHK(bset.setbnd(par) == 0);
+ set2.getval(par);
+ CHK(con.executeScan() == 0);
+ unsigned n = 0;
+ bool deadlock = false;
+ while (1) {
+ int ret;
+ deadlock = par.m_deadlock;
+ CHK((ret = con.nextScanResult(true, deadlock)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ if (deadlock) {
+ LL1("scanreadindex: stop on deadlock");
+ break;
+ }
+ unsigned i = (unsigned)-1;
+ CHK(set2.getkey(par, &i) == 0);
+ CHK(set2.putval(i, par.m_dups, n) == 0);
+ LL4("key " << i << " row " << n << ": " << *set2.m_row[i]);
+ n++;
+ }
+ con.closeTransaction();
+ if (par.m_verify) {
+ CHK(set1.verify(par, set2) == 0);
+ if (par.m_ordered)
+ CHK(set2.verifyorder(par, itab, par.m_descending) == 0);
+ }
+ LL3("scanread " << itab.m_name << " done rows=" << n);
+ return 0;
+}
+
+static int
+scanreadindexfast(Par par, const ITab& itab, const BSet& bset, unsigned countcheck)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ const Set& set = par.set();
+ LL3("scanfast " << itab.m_name << " " << bset);
+ LL4(bset);
+ CHK(con.startTransaction() == 0);
+ CHK(con.getNdbIndexScanOperation(itab, tab) == 0);
+ CHK(con.readIndexTuples(par) == 0);
+ CHK(bset.setbnd(par) == 0);
+ // get 1st column
+ NdbRecAttr* rec;
+ CHK(con.getValue((Uint32)0, rec) == 0);
+ CHK(con.executeScan() == 0);
+ unsigned count = 0;
+ while (1) {
+ int ret;
+ CHK((ret = con.nextScanResult(true)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ count++;
+ }
+ con.closeTransaction();
+ CHK(count == countcheck);
+ return 0;
+}
+
+static int
+scanreadfilter(Par par, const ITab& itab, BSet& bset, bool calc)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ const Set& set = par.set();
+ Set set1(tab, set.m_rows);
+ if (calc) {
+ while (true) {
+ bset.calc(par);
+ bset.filter(par, set, set1);
+ unsigned n = set1.count();
+ // prefer proper subset
+ if (0 < n && n < set.m_rows)
+ break;
+ if (urandom(3) == 0)
+ break;
+ set1.reset();
+ }
+ } else {
+ bset.filter(par, set, set1);
+ }
+ LL3("scanfilter " << itab.m_name << " " << bset << " lockmode=" << par.m_lockmode << " expect=" << set1.count() << " verify=" << par.m_verify);
+ Set set2(tab, set.m_rows);
+ CHK(con.startTransaction() == 0);
+ CHK(con.getNdbScanOperation(tab) == 0);
+ CHK(con.readTuples(par) == 0);
+ CHK(bset.setflt(par) == 0);
+ set2.getval(par);
+ CHK(con.executeScan() == 0);
+ unsigned n = 0;
+ bool deadlock = false;
+ while (1) {
+ int ret;
+ deadlock = par.m_deadlock;
+ CHK((ret = con.nextScanResult(true, deadlock)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ if (deadlock) {
+ LL1("scanfilter: stop on deadlock");
+ break;
+ }
+ unsigned i = (unsigned)-1;
+ CHK(set2.getkey(par, &i) == 0);
+ CHK(set2.putval(i, par.m_dups, n) == 0);
+ LL4("key " << i << " row " << n << ": " << *set2.m_row[i]);
+ n++;
+ }
+ con.closeTransaction();
+ if (par.m_verify) {
+ CHK(set1.verify(par, set2) == 0);
+ }
+ LL3("scanfilter " << itab.m_name << " done rows=" << n);
+ return 0;
+}
+
+static int
+scanreadindex(Par par, const ITab& itab)
+{
+ const Tab& tab = par.tab();
+ for (unsigned i = 0; i < par.m_subsubloop; i++) {
+ if (itab.m_type == ITab::OrderedIndex) {
+ BSet bset(tab, itab, par.m_rows);
+ CHK(scanreadfilter(par, itab, bset, true) == 0);
+ CHK(scanreadindex(par, itab, bset, true) == 0);
+ }
+ }
+ return 0;
+}
+
+static int
+scanreadindex(Par par)
+{
+ const Tab& tab = par.tab();
+ for (unsigned i = 0; i < tab.m_itabs; i++) {
+ if (tab.m_itab[i] == 0)
+ continue;
+ const ITab& itab = *tab.m_itab[i];
+ if (itab.m_type == ITab::OrderedIndex) {
+ CHK(scanreadindex(par, itab) == 0);
+ } else {
+ CHK(hashindexread(par, itab) == 0);
+ }
+ }
+ return 0;
+}
+
+static int
+scanreadall(Par par)
+{
+ CHK(scanreadtable(par) == 0);
+ CHK(scanreadindex(par) == 0);
+ return 0;
+}
+
+// timing scans
+
+static int
+timescantable(Par par)
+{
+ par.tmr().on();
+ CHK(scanreadtablefast(par, par.m_totrows) == 0);
+ par.tmr().off(par.set().m_rows);
+ return 0;
+}
+
+static int
+timescanpkindex(Par par)
+{
+ const Tab& tab = par.tab();
+ const ITab& itab = *tab.m_itab[0]; // 1st index is on PK
+ BSet bset(tab, itab, par.m_rows);
+ par.tmr().on();
+ CHK(scanreadindexfast(par, itab, bset, par.m_totrows) == 0);
+ par.tmr().off(par.set().m_rows);
+ return 0;
+}
+
+static int
+timepkreadtable(Par par)
+{
+ par.tmr().on();
+ unsigned count = par.m_samples;
+ if (count == 0)
+ count = par.m_totrows;
+ CHK(pkreadfast(par, count) == 0);
+ par.tmr().off(count);
+ return 0;
+}
+
+static int
+timepkreadindex(Par par)
+{
+ const Tab& tab = par.tab();
+ const ITab& itab = *tab.m_itab[0]; // 1st index is on PK
+ BSet bset(tab, itab, par.m_rows);
+ unsigned count = par.m_samples;
+ if (count == 0)
+ count = par.m_totrows;
+ par.tmr().on();
+ for (unsigned j = 0; j < count; j++) {
+ unsigned i = urandom(par.m_totrows);
+ bset.calcpk(par, i);
+ CHK(scanreadindexfast(par, itab, bset, 1) == 0);
+ }
+ par.tmr().off(count);
+ return 0;
+}
+
+// scan update
+
+static int
+scanupdatetable(Par par)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ Set& set = par.set();
+ LL3("scan update " << tab.m_name);
+ Set set2(tab, set.m_rows);
+ par.m_lockmode = NdbOperation::LM_Exclusive;
+ CHK(con.startTransaction() == 0);
+ CHK(con.getNdbScanOperation(tab) == 0);
+ CHK(con.readTuples(par) == 0);
+ set2.getval(par);
+ CHK(con.executeScan() == 0);
+ unsigned count = 0;
+ // updating trans
+ Con con2;
+ con2.connect(con);
+ CHK(con2.startTransaction() == 0);
+ Lst lst;
+ bool deadlock = false;
+ while (1) {
+ int ret;
+ deadlock = par.m_deadlock;
+ CHK((ret = con.nextScanResult(true, deadlock)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ if (deadlock) {
+ LL1("scanupdatetable: stop on deadlock [at 1]");
+ break;
+ }
+ if (par.m_scanstop != 0 && urandom(par.m_scanstop) == 0) {
+ con.closeScan();
+ break;
+ }
+ do {
+ unsigned i = (unsigned)-1;
+ CHK(set2.getkey(par, &i) == 0);
+ const Row& row = *set.m_row[i];
+ set.lock();
+ if (! set.exist(i) || set.pending(i, Row::AnyOp)) {
+ LL4("scan update " << tab.m_name << ": skip: " << row);
+ } else {
+ CHKTRY(set2.putval(i, false) == 0, set.unlock());
+ CHKTRY(con.updateScanTuple(con2) == 0, set.unlock());
+ Par par2 = par;
+ par2.m_con = &con2;
+ set.dbsave(i);
+ set.calc(par, i);
+ CHKTRY(set.setrow(par2, i) == 0, set.unlock());
+ LL4("scan update " << tab.m_name << ": " << row);
+ lst.push(i);
+ }
+ set.unlock();
+ if (lst.cnt() == par.m_batch) {
+ deadlock = par.m_deadlock;
+ CHK(con2.execute(Commit, deadlock) == 0);
+ if (deadlock) {
+ LL1("scanupdatetable: stop on deadlock [at 2]");
+ goto out;
+ }
+ con2.closeTransaction();
+ set.lock();
+ set.notpending(lst);
+ set.dbdiscard(lst);
+ set.unlock();
+ count += lst.cnt();
+ lst.reset();
+ CHK(con2.startTransaction() == 0);
+ }
+ CHK((ret = con.nextScanResult(false)) == 0 || ret == 1 || ret == 2);
+ if (ret == 2 && lst.cnt() != 0) {
+ deadlock = par.m_deadlock;
+ CHK(con2.execute(Commit, deadlock) == 0);
+ if (deadlock) {
+ LL1("scanupdatetable: stop on deadlock [at 3]");
+ goto out;
+ }
+ con2.closeTransaction();
+ set.lock();
+ set.notpending(lst);
+ set.dbdiscard(lst);
+ set.unlock();
+ count += lst.cnt();
+ lst.reset();
+ CHK(con2.startTransaction() == 0);
+ }
+ } while (ret == 0);
+ if (ret == 1)
+ break;
+ }
+out:
+ con2.closeTransaction();
+ LL3("scan update " << tab.m_name << " rows updated=" << count);
+ con.closeTransaction();
+ return 0;
+}
+
+static int
+scanupdateindex(Par par, const ITab& itab, const BSet& bset)
+{
+ Con& con = par.con();
+ const Tab& tab = par.tab();
+ Set& set = par.set();
+ LL3("scan update " << itab.m_name);
+ Set set2(tab, set.m_rows);
+ par.m_lockmode = NdbOperation::LM_Exclusive;
+ CHK(con.startTransaction() == 0);
+ CHK(con.getNdbIndexScanOperation(itab, tab) == 0);
+ CHK(con.readTuples(par) == 0);
+ CHK(bset.setbnd(par) == 0);
+ set2.getval(par);
+ CHK(con.executeScan() == 0);
+ unsigned count = 0;
+ // updating trans
+ Con con2;
+ con2.connect(con);
+ CHK(con2.startTransaction() == 0);
+ Lst lst;
+ bool deadlock = false;
+ while (1) {
+ int ret;
+ deadlock = par.m_deadlock;
+ CHK((ret = con.nextScanResult(true, deadlock)) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ if (deadlock) {
+ LL1("scanupdateindex: stop on deadlock [at 1]");
+ break;
+ }
+ if (par.m_scanstop != 0 && urandom(par.m_scanstop) == 0) {
+ con.closeScan();
+ break;
+ }
+ do {
+ unsigned i = (unsigned)-1;
+ CHK(set2.getkey(par, &i) == 0);
+ const Row& row = *set.m_row[i];
+ set.lock();
+ if (! set.exist(i) || set.pending(i, Row::AnyOp)) {
+ LL4("scan update " << itab.m_name << ": skip: " << row);
+ } else {
+ CHKTRY(set2.putval(i, par.m_dups) == 0, set.unlock());
+ CHKTRY(con.updateScanTuple(con2) == 0, set.unlock());
+ Par par2 = par;
+ par2.m_con = &con2;
+ set.dbsave(i);
+ set.calc(par, i);
+ CHKTRY(set.setrow(par2, i) == 0, set.unlock());
+ LL4("scan update " << itab.m_name << ": " << row);
+ lst.push(i);
+ }
+ set.unlock();
+ if (lst.cnt() == par.m_batch) {
+ deadlock = par.m_deadlock;
+ CHK(con2.execute(Commit, deadlock) == 0);
+ if (deadlock) {
+ LL1("scanupdateindex: stop on deadlock [at 2]");
+ goto out;
+ }
+ con2.closeTransaction();
+ set.lock();
+ set.notpending(lst);
+ set.dbdiscard(lst);
+ set.unlock();
+ count += lst.cnt();
+ lst.reset();
+ CHK(con2.startTransaction() == 0);
+ }
+ CHK((ret = con.nextScanResult(false)) == 0 || ret == 1 || ret == 2);
+ if (ret == 2 && lst.cnt() != 0) {
+ deadlock = par.m_deadlock;
+ CHK(con2.execute(Commit, deadlock) == 0);
+ if (deadlock) {
+ LL1("scanupdateindex: stop on deadlock [at 3]");
+ goto out;
+ }
+ con2.closeTransaction();
+ set.lock();
+ set.notpending(lst);
+ set.dbdiscard(lst);
+ set.unlock();
+ count += lst.cnt();
+ lst.reset();
+ CHK(con2.startTransaction() == 0);
+ }
+ } while (ret == 0);
+ }
+out:
+ con2.closeTransaction();
+ LL3("scan update " << itab.m_name << " rows updated=" << count);
+ con.closeTransaction();
+ return 0;
+}
+
+static int
+scanupdateindex(Par par, const ITab& itab)
+{
+ const Tab& tab = par.tab();
+ for (unsigned i = 0; i < par.m_subsubloop; i++) {
+ if (itab.m_type == ITab::OrderedIndex) {
+ BSet bset(tab, itab, par.m_rows);
+ bset.calc(par);
+ CHK(scanupdateindex(par, itab, bset) == 0);
+ } else {
+ CHK(hashindexupdate(par, itab) == 0);
+ }
+ }
+ return 0;
+}
+
+static int
+scanupdateindex(Par par)
+{
+ const Tab& tab = par.tab();
+ for (unsigned i = 0; i < tab.m_itabs; i++) {
+ if (tab.m_itab[i] == 0)
+ continue;
+ const ITab& itab = *tab.m_itab[i];
+ CHK(scanupdateindex(par, itab) == 0);
+ }
+ return 0;
+}
+
+static int
+scanupdateall(Par par)
+{
+ CHK(scanupdatetable(par) == 0);
+ CHK(scanupdateindex(par) == 0);
+ return 0;
+}
+
+// medium level routines
+
+static int
+readverify(Par par)
+{
+ if (par.m_noverify)
+ return 0;
+ par.m_verify = true;
+ par.m_lockmode = NdbOperation::LM_CommittedRead;
+ CHK(pkread(par) == 0);
+ CHK(scanreadall(par) == 0);
+ return 0;
+}
+
+static int
+readverifyfull(Par par)
+{
+ if (par.m_noverify)
+ return 0;
+ par.m_verify = true;
+ par.m_lockmode = NdbOperation::LM_CommittedRead;
+ const Tab& tab = par.tab();
+ if (par.m_no == 0) {
+ // thread 0 scans table
+ CHK(scanreadtable(par) == 0);
+ }
+ // each thread scans different indexes
+ for (unsigned i = 0; i < tab.m_itabs; i++) {
+ if (i % par.m_threads != par.m_no)
+ continue;
+ if (tab.m_itab[i] == 0)
+ continue;
+ const ITab& itab = *tab.m_itab[i];
+ if (itab.m_type == ITab::OrderedIndex) {
+ BSet bset(tab, itab, par.m_rows);
+ CHK(scanreadindex(par, itab, bset, false) == 0);
+ } else {
+ CHK(hashindexread(par, itab) == 0);
+ }
+ }
+ return 0;
+}
+
+static int
+readverifyindex(Par par)
+{
+ if (par.m_noverify)
+ return 0;
+ par.m_verify = true;
+ par.m_lockmode = NdbOperation::LM_CommittedRead;
+ unsigned sel = urandom(10);
+ if (sel < 9) {
+ par.m_ordered = true;
+ par.m_descending = (sel < 5);
+ }
+ CHK(scanreadindex(par) == 0);
+ return 0;
+}
+
+static int
+pkops(Par par)
+{
+ const Tab& tab = par.tab();
+ par.m_randomkey = true;
+ for (unsigned i = 0; i < par.m_subsubloop; i++) {
+ unsigned j = 0;
+ while (j < tab.m_itabs) {
+ if (tab.m_itab[j] != 0) {
+ const ITab& itab = *tab.m_itab[j];
+ if (itab.m_type == ITab::UniqueHashIndex && urandom(5) == 0)
+ break;
+ }
+ j++;
+ }
+ unsigned sel = urandom(10);
+ if (par.m_slno % 2 == 0) {
+ // favor insert
+ if (sel < 8) {
+ CHK(pkinsert(par) == 0);
+ } else if (sel < 9) {
+ if (j == tab.m_itabs)
+ CHK(pkupdate(par) == 0);
+ else {
+ const ITab& itab = *tab.m_itab[j];
+ CHK(hashindexupdate(par, itab) == 0);
+ }
+ } else {
+ if (j == tab.m_itabs)
+ CHK(pkdelete(par) == 0);
+ else {
+ const ITab& itab = *tab.m_itab[j];
+ CHK(hashindexdelete(par, itab) == 0);
+ }
+ }
+ } else {
+ // favor delete
+ if (sel < 1) {
+ CHK(pkinsert(par) == 0);
+ } else if (sel < 2) {
+ if (j == tab.m_itabs)
+ CHK(pkupdate(par) == 0);
+ else {
+ const ITab& itab = *tab.m_itab[j];
+ CHK(hashindexupdate(par, itab) == 0);
+ }
+ } else {
+ if (j == tab.m_itabs)
+ CHK(pkdelete(par) == 0);
+ else {
+ const ITab& itab = *tab.m_itab[j];
+ CHK(hashindexdelete(par, itab) == 0);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+pkupdatescanread(Par par)
+{
+ par.m_dups = true;
+ par.m_deadlock = true;
+ unsigned sel = urandom(10);
+ if (sel < 5) {
+ CHK(pkupdate(par) == 0);
+ } else if (sel < 6) {
+ par.m_verify = false;
+ CHK(scanreadtable(par) == 0);
+ } else {
+ par.m_verify = false;
+ if (sel < 8) {
+ par.m_ordered = true;
+ par.m_descending = (sel < 7);
+ }
+ CHK(scanreadindex(par) == 0);
+ }
+ return 0;
+}
+
+static int
+mixedoperations(Par par)
+{
+ par.m_dups = true;
+ par.m_deadlock = true;
+ par.m_scanstop = par.m_totrows; // randomly close scans
+ unsigned sel = urandom(10);
+ if (sel < 2) {
+ CHK(pkdelete(par) == 0);
+ } else if (sel < 4) {
+ CHK(pkupdate(par) == 0);
+ } else if (sel < 6) {
+ CHK(scanupdatetable(par) == 0);
+ } else {
+ if (sel < 8) {
+ par.m_ordered = true;
+ par.m_descending = (sel < 7);
+ }
+ CHK(scanupdateindex(par) == 0);
+ }
+ return 0;
+}
+
+static int
+pkupdateindexbuild(Par par)
+{
+ if (par.m_no == 0) {
+ CHK(createindex(par) == 0);
+ } else {
+ par.m_randomkey = true;
+ CHK(pkupdate(par) == 0);
+ }
+ return 0;
+}
+
+// threads
+
+typedef int (*TFunc)(Par par);
+enum TMode { ST = 1, MT = 2 };
+
+extern "C" { static void* runthread(void* arg); }
+
+struct Thr {
+ enum State { Wait, Start, Stop, Stopped, Exit };
+ State m_state;
+ Par m_par;
+ Uint64 m_id;
+ NdbThread* m_thread;
+ NdbMutex* m_mutex;
+ NdbCondition* m_cond;
+ TFunc m_func;
+ int m_ret;
+ void* m_status;
+ Thr(Par par, unsigned n);
+ ~Thr();
+ int run();
+ void start();
+ void stop();
+ void stopped();
+ void exit();
+ //
+ void lock() {
+ NdbMutex_Lock(m_mutex);
+ }
+ void unlock() {
+ NdbMutex_Unlock(m_mutex);
+ }
+ void wait() {
+ NdbCondition_Wait(m_cond, m_mutex);
+ }
+ void signal() {
+ NdbCondition_Signal(m_cond);
+ }
+ void join() {
+ NdbThread_WaitFor(m_thread, &m_status);
+ m_thread = 0;
+ }
+};
+
+Thr::Thr(Par par, unsigned n) :
+ m_state(Wait),
+ m_par(par),
+ m_id(0),
+ m_thread(0),
+ m_mutex(0),
+ m_cond(0),
+ m_func(0),
+ m_ret(0),
+ m_status(0)
+{
+ m_par.m_no = n;
+ char buf[10];
+ sprintf(buf, "thr%03u", par.m_no);
+ const char* name = strcpy(new char[10], buf);
+ // mutex
+ m_mutex = NdbMutex_Create();
+ m_cond = NdbCondition_Create();
+ assert(m_mutex != 0 && m_cond != 0);
+ // run
+ const unsigned stacksize = 256 * 1024;
+ const NDB_THREAD_PRIO prio = NDB_THREAD_PRIO_LOW;
+ m_thread = NdbThread_Create(runthread, (void**)this, stacksize, name, prio);
+}
+
+Thr::~Thr()
+{
+ if (m_thread != 0) {
+ NdbThread_Destroy(&m_thread);
+ m_thread = 0;
+ }
+ if (m_cond != 0) {
+ NdbCondition_Destroy(m_cond);
+ m_cond = 0;
+ }
+ if (m_mutex != 0) {
+ NdbMutex_Destroy(m_mutex);
+ m_mutex = 0;
+ }
+}
+
+static void*
+runthread(void* arg)
+{
+ Thr& thr = *(Thr*)arg;
+ thr.m_id = (Uint64)pthread_self();
+ if (thr.run() < 0) {
+ LL1("exit on error");
+ } else {
+ LL4("exit ok");
+ }
+ return 0;
+}
+
+int
+Thr::run()
+{
+ LL4("run");
+ Con con;
+ CHK(con.connect() == 0);
+ m_par.m_con = &con;
+ LL4("connected");
+ while (1) {
+ lock();
+ while (m_state != Start && m_state != Exit) {
+ LL4("wait");
+ wait();
+ }
+ if (m_state == Exit) {
+ LL4("exit");
+ unlock();
+ break;
+ }
+ LL4("start");
+ assert(m_state == Start);
+ m_ret = (*m_func)(m_par);
+ m_state = Stopped;
+ LL4("stop");
+ signal();
+ unlock();
+ CHK(m_ret == 0);
+ }
+ con.disconnect();
+ return 0;
+}
+
+void
+Thr::start()
+{
+ lock();
+ m_state = Start;
+ signal();
+ unlock();
+}
+
+void
+Thr::stop()
+{
+ lock();
+ m_state = Stop;
+ signal();
+ unlock();
+}
+
+void
+Thr::stopped()
+{
+ lock();
+ while (m_state != Stopped)
+ wait();
+ m_state = Wait;
+ unlock();
+}
+
+void
+Thr::exit()
+{
+ lock();
+ m_state = Exit;
+ signal();
+ unlock();
+}
+
+// test run
+
+static Thr** g_thrlist = 0;
+
+static unsigned
+getthrno()
+{
+ if (g_thrlist != 0) {
+ Uint64 id = (Uint64)pthread_self();
+ for (unsigned n = 0; n < g_opt.m_threads; n++) {
+ if (g_thrlist[n] != 0) {
+ const Thr& thr = *g_thrlist[n];
+ if (thr.m_id == id)
+ return thr.m_par.m_no;
+ }
+ }
+ }
+ return (unsigned)-1;
+}
+
+static int
+runstep(Par par, const char* fname, TFunc func, unsigned mode)
+{
+ LL2(fname);
+ const int threads = (mode & ST ? 1 : par.m_threads);
+ int n;
+ for (n = 0; n < threads; n++) {
+ LL4("start " << n);
+ Thr& thr = *g_thrlist[n];
+ thr.m_par.m_tab = par.m_tab;
+ thr.m_par.m_set = par.m_set;
+ thr.m_par.m_tmr = par.m_tmr;
+ thr.m_par.m_lno = par.m_lno;
+ thr.m_par.m_slno = par.m_slno;
+ thr.m_func = func;
+ thr.start();
+ }
+ unsigned errs = 0;
+ for (n = threads - 1; n >= 0; n--) {
+ LL4("stop " << n);
+ Thr& thr = *g_thrlist[n];
+ thr.stopped();
+ if (thr.m_ret != 0)
+ errs++;
+ }
+ CHK(errs == 0);
+ return 0;
+}
+
+#define RUNSTEP(par, func, mode) CHK(runstep(par, #func, func, mode) == 0)
+
+static int
+tbuild(Par par)
+{
+ RUNSTEP(par, droptable, ST);
+ RUNSTEP(par, createtable, ST);
+ RUNSTEP(par, invalidatetable, MT);
+ for (par.m_slno = 0; par.m_slno < par.m_subloop; par.m_slno++) {
+ if (par.m_slno % 2 == 0) {
+ RUNSTEP(par, createindex, ST);
+ RUNSTEP(par, invalidateindex, MT);
+ RUNSTEP(par, pkinsert, MT);
+ } else {
+ RUNSTEP(par, pkinsert, MT);
+ RUNSTEP(par, createindex, ST);
+ RUNSTEP(par, invalidateindex, MT);
+ }
+ RUNSTEP(par, pkupdate, MT);
+ RUNSTEP(par, readverifyfull, MT);
+ RUNSTEP(par, pkdelete, MT);
+ RUNSTEP(par, readverifyfull, MT);
+ RUNSTEP(par, dropindex, ST);
+ }
+ return 0;
+}
+
+static int
+tindexscan(Par par)
+{
+ RUNSTEP(par, droptable, ST);
+ RUNSTEP(par, createtable, ST);
+ RUNSTEP(par, invalidatetable, MT);
+ RUNSTEP(par, createindex, ST);
+ RUNSTEP(par, invalidateindex, MT);
+ RUNSTEP(par, pkinsert, MT);
+ RUNSTEP(par, readverifyfull, MT);
+ for (par.m_slno = 0; par.m_slno < par.m_subloop; par.m_slno++) {
+ LL4("subloop " << par.m_slno);
+ RUNSTEP(par, readverifyindex, MT);
+ }
+ return 0;
+}
+
+
+static int
+tpkops(Par par)
+{
+ RUNSTEP(par, droptable, ST);
+ RUNSTEP(par, createtable, ST);
+ RUNSTEP(par, invalidatetable, MT);
+ RUNSTEP(par, createindex, ST);
+ RUNSTEP(par, invalidateindex, MT);
+ for (par.m_slno = 0; par.m_slno < par.m_subloop; par.m_slno++) {
+ RUNSTEP(par, pkops, MT);
+ LL2("rows=" << par.set().count());
+ RUNSTEP(par, readverifyfull, MT);
+ }
+ return 0;
+}
+
+static int
+tpkopsread(Par par)
+{
+ RUNSTEP(par, droptable, ST);
+ RUNSTEP(par, createtable, ST);
+ RUNSTEP(par, invalidatetable, MT);
+ RUNSTEP(par, pkinsert, MT);
+ RUNSTEP(par, createindex, ST);
+ RUNSTEP(par, invalidateindex, MT);
+ RUNSTEP(par, readverify, ST);
+ for (par.m_slno = 0; par.m_slno < par.m_subloop; par.m_slno++) {
+ RUNSTEP(par, pkupdatescanread, MT);
+ RUNSTEP(par, readverify, ST);
+ }
+ RUNSTEP(par, pkdelete, MT);
+ RUNSTEP(par, readverify, ST);
+ return 0;
+}
+
+static int
+tmixedops(Par par)
+{
+ RUNSTEP(par, droptable, ST);
+ RUNSTEP(par, createtable, ST);
+ RUNSTEP(par, invalidatetable, MT);
+ RUNSTEP(par, pkinsert, MT);
+ RUNSTEP(par, createindex, ST);
+ RUNSTEP(par, invalidateindex, MT);
+ RUNSTEP(par, readverify, ST);
+ for (par.m_slno = 0; par.m_slno < par.m_subloop; par.m_slno++) {
+ RUNSTEP(par, mixedoperations, MT);
+ RUNSTEP(par, readverify, ST);
+ }
+ return 0;
+}
+
+static int
+tbusybuild(Par par)
+{
+ RUNSTEP(par, droptable, ST);
+ RUNSTEP(par, createtable, ST);
+ RUNSTEP(par, invalidatetable, MT);
+ RUNSTEP(par, pkinsert, MT);
+ for (par.m_slno = 0; par.m_slno < par.m_subloop; par.m_slno++) {
+ RUNSTEP(par, pkupdateindexbuild, MT);
+ RUNSTEP(par, invalidateindex, MT);
+ RUNSTEP(par, readverify, ST);
+ RUNSTEP(par, dropindex, ST);
+ }
+ return 0;
+}
+
+static int
+ttimebuild(Par par)
+{
+ Tmr t1;
+ RUNSTEP(par, droptable, ST);
+ RUNSTEP(par, createtable, ST);
+ RUNSTEP(par, invalidatetable, MT);
+ for (par.m_slno = 0; par.m_slno < par.m_subloop; par.m_slno++) {
+ RUNSTEP(par, pkinsert, MT);
+ t1.on();
+ RUNSTEP(par, createindex, ST);
+ t1.off(par.m_totrows);
+ RUNSTEP(par, invalidateindex, MT);
+ RUNSTEP(par, dropindex, ST);
+ }
+ LL1("build index - " << t1.time());
+ return 0;
+}
+
+static int
+ttimemaint(Par par)
+{
+ Tmr t1, t2;
+ RUNSTEP(par, droptable, ST);
+ RUNSTEP(par, createtable, ST);
+ RUNSTEP(par, invalidatetable, MT);
+ for (par.m_slno = 0; par.m_slno < par.m_subloop; par.m_slno++) {
+ RUNSTEP(par, pkinsert, MT);
+ t1.on();
+ RUNSTEP(par, pkupdate, MT);
+ t1.off(par.m_totrows);
+ RUNSTEP(par, createindex, ST);
+ RUNSTEP(par, invalidateindex, MT);
+ t2.on();
+ RUNSTEP(par, pkupdate, MT);
+ t2.off(par.m_totrows);
+ RUNSTEP(par, dropindex, ST);
+ }
+ LL1("update - " << t1.time());
+ LL1("update indexed - " << t2.time());
+ LL1("overhead - " << t2.over(t1));
+ return 0;
+}
+
+static int
+ttimescan(Par par)
+{
+ if (par.tab().m_itab[0] == 0) {
+ LL1("ttimescan - no index 0, skipped");
+ return 0;
+ }
+ Tmr t1, t2;
+ RUNSTEP(par, droptable, ST);
+ RUNSTEP(par, createtable, ST);
+ RUNSTEP(par, invalidatetable, MT);
+ for (par.m_slno = 0; par.m_slno < par.m_subloop; par.m_slno++) {
+ RUNSTEP(par, pkinsert, MT);
+ RUNSTEP(par, createindex, ST);
+ par.m_tmr = &t1;
+ RUNSTEP(par, timescantable, ST);
+ par.m_tmr = &t2;
+ RUNSTEP(par, timescanpkindex, ST);
+ RUNSTEP(par, dropindex, ST);
+ }
+ LL1("full scan table - " << t1.time());
+ LL1("full scan PK index - " << t2.time());
+ LL1("overhead - " << t2.over(t1));
+ return 0;
+}
+
+static int
+ttimepkread(Par par)
+{
+ if (par.tab().m_itab[0] == 0) {
+ LL1("ttimescan - no index 0, skipped");
+ return 0;
+ }
+ Tmr t1, t2;
+ RUNSTEP(par, droptable, ST);
+ RUNSTEP(par, createtable, ST);
+ RUNSTEP(par, invalidatetable, MT);
+ for (par.m_slno = 0; par.m_slno < par.m_subloop; par.m_slno++) {
+ RUNSTEP(par, pkinsert, MT);
+ RUNSTEP(par, createindex, ST);
+ par.m_tmr = &t1;
+ RUNSTEP(par, timepkreadtable, ST);
+ par.m_tmr = &t2;
+ RUNSTEP(par, timepkreadindex, ST);
+ RUNSTEP(par, dropindex, ST);
+ }
+ LL1("pk read table - " << t1.time());
+ LL1("pk read PK index - " << t2.time());
+ LL1("overhead - " << t2.over(t1));
+ return 0;
+}
+
+static int
+tdrop(Par par)
+{
+ RUNSTEP(par, droptable, ST);
+ return 0;
+}
+
+struct TCase {
+ const char* m_name;
+ TFunc m_func;
+ const char* m_desc;
+ TCase(const char* name, TFunc func, const char* desc) :
+ m_name(name),
+ m_func(func),
+ m_desc(desc) {
+ }
+};
+
+static const TCase
+tcaselist[] = {
+ TCase("a", tbuild, "index build"),
+ TCase("b", tindexscan, "index scans"),
+ TCase("c", tpkops, "pk operations"),
+ TCase("d", tpkopsread, "pk operations and scan reads"),
+ TCase("e", tmixedops, "pk operations and scan operations"),
+ TCase("f", tbusybuild, "pk operations and index build"),
+ TCase("t", ttimebuild, "time index build"),
+ TCase("u", ttimemaint, "time index maintenance"),
+ TCase("v", ttimescan, "time full scan table vs index on pk"),
+ TCase("w", ttimepkread, "time pk read table vs index on pk"),
+ TCase("z", tdrop, "drop test tables")
+};
+
+static const unsigned
+tcasecount = sizeof(tcaselist) / sizeof(tcaselist[0]);
+
+static void
+printcases()
+{
+ ndbout << "test cases:" << endl;
+ for (unsigned i = 0; i < tcasecount; i++) {
+ const TCase& tcase = tcaselist[i];
+ ndbout << " " << tcase.m_name << " - " << tcase.m_desc << endl;
+ }
+}
+
+static void
+printtables()
+{
+ Par par(g_opt);
+ makebuiltintables(par);
+ ndbout << "tables and indexes (x=ordered z=hash x0=on pk):" << endl;
+ for (unsigned j = 0; j < tabcount; j++) {
+ if (tablist[j] == 0)
+ continue;
+ const Tab& tab = *tablist[j];
+ const char* tname = tab.m_name;
+ ndbout << " " << tname;
+ for (unsigned i = 0; i < tab.m_itabs; i++) {
+ if (tab.m_itab[i] == 0)
+ continue;
+ const ITab& itab = *tab.m_itab[i];
+ const char* iname = itab.m_name;
+ if (strncmp(tname, iname, strlen(tname)) == 0)
+ iname += strlen(tname);
+ ndbout << " " << iname;
+ ndbout << "(";
+ for (unsigned k = 0; k < itab.m_icols; k++) {
+ if (k != 0)
+ ndbout << ",";
+ const ICol& icol = *itab.m_icol[k];
+ const Col& col = icol.m_col;
+ ndbout << col.m_name;
+ }
+ ndbout << ")";
+ }
+ ndbout << endl;
+ }
+}
+
+static int
+runtest(Par par)
+{
+ LL1("start");
+ if (par.m_seed == -1) {
+ // good enough for daily run
+ unsigned short seed = (getpid() ^ time(0));
+ LL1("random seed: " << seed);
+ srandom((unsigned)seed);
+ } else if (par.m_seed != 0) {
+ LL1("random seed: " << par.m_seed);
+ srandom(par.m_seed);
+ } else {
+ LL1("random seed: loop number");
+ }
+ // cs
+ assert(par.m_csname != 0);
+ if (strcmp(par.m_csname, "random") != 0) {
+ CHARSET_INFO* cs;
+ CHK((cs = get_charset_by_name(par.m_csname, MYF(0))) != 0 || (cs = get_charset_by_csname(par.m_csname, MY_CS_PRIMARY, MYF(0))) != 0);
+ par.m_cs = cs;
+ }
+ // con
+ Con con;
+ CHK(con.connect() == 0);
+ par.m_con = &con;
+ // threads
+ g_thrlist = new Thr* [par.m_threads];
+ unsigned n;
+ for (n = 0; n < par.m_threads; n++) {
+ g_thrlist[n] = 0;
+ }
+ for (n = 0; n < par.m_threads; n++) {
+ g_thrlist[n] = new Thr(par, n);
+ Thr& thr = *g_thrlist[n];
+ assert(thr.m_thread != 0);
+ }
+ for (par.m_lno = 0; par.m_loop == 0 || par.m_lno < par.m_loop; par.m_lno++) {
+ LL1("loop " << par.m_lno);
+ if (par.m_seed == 0)
+ srandom(par.m_lno);
+ for (unsigned i = 0; i < tcasecount; i++) {
+ const TCase& tcase = tcaselist[i];
+ if (par.m_case != 0 && strchr(par.m_case, tcase.m_name[0]) == 0)
+ continue;
+ makebuiltintables(par);
+ LL1("case " << tcase.m_name << " - " << tcase.m_desc);
+ for (unsigned j = 0; j < tabcount; j++) {
+ if (tablist[j] == 0)
+ continue;
+ const Tab& tab = *tablist[j];
+ par.m_tab = &tab;
+ par.m_set = new Set(tab, par.m_totrows);
+ LL1("table " << tab.m_name);
+ CHK(tcase.m_func(par) == 0);
+ delete par.m_set;
+ par.m_set = 0;
+ }
+ }
+ }
+ for (n = 0; n < par.m_threads; n++) {
+ Thr& thr = *g_thrlist[n];
+ thr.exit();
+ }
+ for (n = 0; n < par.m_threads; n++) {
+ Thr& thr = *g_thrlist[n];
+ thr.join();
+ delete &thr;
+ }
+ delete [] g_thrlist;
+ g_thrlist = 0;
+ con.disconnect();
+ LL1("done");
+ return 0;
+}
+
+NDB_COMMAND(testOIBasic, "testOIBasic", "testOIBasic", "testOIBasic", 65535)
+{
+ ndb_init();
+ if (ndbout_mutex == NULL)
+ ndbout_mutex = NdbMutex_Create();
+ while (++argv, --argc > 0) {
+ const char* arg = argv[0];
+ if (*arg != '-') {
+ ndbout << "testOIBasic: unknown argument " << arg;
+ goto usage;
+ }
+ if (strcmp(arg, "-batch") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_batch = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-bound") == 0) {
+ if (++argv, --argc > 0) {
+ const char* p = argv[0];
+ if (strlen(p) != 0 && strlen(p) == strspn(p, "01234")) {
+ g_opt.m_bound = strdup(p);
+ continue;
+ }
+ }
+ }
+ if (strcmp(arg, "-case") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_case = strdup(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-collsp") == 0) {
+ g_opt.m_collsp = true;
+ continue;
+ }
+ if (strcmp(arg, "-core") == 0) {
+ g_opt.m_core = true;
+ continue;
+ }
+ if (strcmp(arg, "-csname") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_csname = strdup(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-die") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_die = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-dups") == 0) {
+ g_opt.m_dups = true;
+ continue;
+ }
+ if (strcmp(arg, "-fragtype") == 0) {
+ if (++argv, --argc > 0) {
+ if (strcmp(argv[0], "single") == 0) {
+ g_opt.m_fragtype = NdbDictionary::Object::FragSingle;
+ continue;
+ }
+ if (strcmp(argv[0], "small") == 0) {
+ g_opt.m_fragtype = NdbDictionary::Object::FragAllSmall;
+ continue;
+ }
+ if (strcmp(argv[0], "medium") == 0) {
+ g_opt.m_fragtype = NdbDictionary::Object::FragAllMedium;
+ continue;
+ }
+ if (strcmp(argv[0], "large") == 0) {
+ g_opt.m_fragtype = NdbDictionary::Object::FragAllLarge;
+ continue;
+ }
+ }
+ }
+ if (strcmp(arg, "-index") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_index = strdup(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-loop") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_loop = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-nologging") == 0) {
+ g_opt.m_nologging = true;
+ continue;
+ }
+ if (strcmp(arg, "-noverify") == 0) {
+ g_opt.m_noverify = true;
+ continue;
+ }
+ if (strcmp(arg, "-pctnull") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_pctnull = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-rows") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_rows = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-samples") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_samples = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-scanbat") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_scanbat = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-scanpar") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_scanpar = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-seed") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_seed = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-subloop") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_subloop = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-table") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_table = strdup(argv[0]);
+ continue;
+ }
+ }
+ if (strcmp(arg, "-threads") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_threads = atoi(argv[0]);
+ if (1 <= g_opt.m_threads)
+ continue;
+ }
+ }
+ if (strcmp(arg, "-v") == 0) {
+ if (++argv, --argc > 0) {
+ g_opt.m_v = atoi(argv[0]);
+ continue;
+ }
+ }
+ if (strncmp(arg, "-v", 2) == 0 && isdigit(arg[2])) {
+ g_opt.m_v = atoi(&arg[2]);
+ continue;
+ }
+ if (strcmp(arg, "-h") == 0 || strcmp(arg, "-help") == 0) {
+ printhelp();
+ goto wrongargs;
+ }
+ ndbout << "testOIBasic: bad or unknown option " << arg;
+ goto usage;
+ }
+ {
+ Par par(g_opt);
+ g_ncc = new Ndb_cluster_connection();
+ if (g_ncc->connect(30) != 0 || runtest(par) < 0)
+ goto failed;
+ delete g_ncc;
+ g_ncc = 0;
+ }
+ok:
+ return NDBT_ProgramExit(NDBT_OK);
+failed:
+ return NDBT_ProgramExit(NDBT_FAILED);
+usage:
+ ndbout << " (use -h for help)" << endl;
+wrongargs:
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+}
+
+// vim: set sw=2 et:
diff --git a/storage/ndb/test/ndbapi/testOperations.cpp b/storage/ndb/test/ndbapi/testOperations.cpp
new file mode 100644
index 00000000000..e254aff58dc
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testOperations.cpp
@@ -0,0 +1,641 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "NDBT_Test.hpp"
+#include "NDBT_ReturnCodes.h"
+#include "HugoTransactions.hpp"
+#include "UtilTransactions.hpp"
+
+struct OperationTestCase {
+ const char * name;
+ bool preCond; // start transaction | insert | commit
+
+ // start transaction
+ const char * op1;
+ const int res1;
+ const int val1;
+
+ // no commit
+
+ const char * op2;
+ const int res2;
+ const int val2;
+ // Commit
+
+ // start transaction
+ // op3 = READ
+ const int res3;
+ const int val3;
+ // commit transaction
+};
+
+OperationTestCase matrix[] = {
+ { "ReadRead", true, "READ", 0, 0, "READ", 0, 0, 0, 0 },
+ { "ReadReadEx", true, "READ", 0, 0, "READ-EX", 0, 0, 0, 0 },
+ { "ReadSimpleRead", true, "READ", 0, 0, "S-READ", 0, 0, 0, 0 },
+ { "ReadDirtyRead", true, "READ", 0, 0, "D-READ", 0, 0, 0, 0 },
+ { "ReadInsert", true, "READ", 0, 0, "INSERT", 630, 1, 0, 0 },
+ { "ReadUpdate", true, "READ", 0, 0, "UPDATE", 0, 1, 0, 1 },
+ { "ReadDelete", true, "READ", 0, 0, "DELETE", 0, 0, 626, 0 },
+
+ { "FReadRead", false, "READ", 626, 0, "READ", 626, 0, 626, 0 },
+ { "FReadReadEx", false, "READ", 626, 0, "READ-EX", 626, 0, 626, 0 },
+ { "FReadSimpleRead", false, "READ", 626, 0, "S-READ", 626, 0, 626, 0 },
+ { "FReadDirtyRead", false, "READ", 626, 0, "D-READ", 626, 0, 626, 0 },
+ { "FReadInsert", false, "READ", 626, 0, "INSERT", 0, 1, 0, 1 },
+ { "FReadUpdate", false, "READ", 626, 0, "UPDATE", 626, 0, 626, 0 },
+ { "FReadDelete", false, "READ", 626, 0, "DELETE", 626, 0, 626, 0 },
+
+ { "ReadExRead", true, "READ-EX", 0, 0, "READ", 0, 0, 0, 0 },
+ { "ReadExReadEx", true, "READ-EX", 0, 0, "READ-EX", 0, 0, 0, 0 },
+ { "ReadExSimpleRead", true, "READ-EX", 0, 0, "S-READ", 0, 0, 0, 0 },
+ { "ReadExDirtyRead", true, "READ-EX", 0, 0, "D-READ", 0, 0, 0, 0 },
+ { "ReadExInsert", true, "READ-EX", 0, 0, "INSERT", 630, 1, 0, 0 },
+ { "ReadExUpdate", true, "READ-EX", 0, 0, "UPDATE", 0, 1, 0, 1 },
+ { "ReadExDelete", true, "READ-EX", 0, 0, "DELETE", 0, 0, 626, 0 },
+
+ { "InsertRead", false, "INSERT", 0, 0, "READ", 0, 0, 0, 0 },
+ { "InsertReadEx", false, "INSERT", 0, 0, "READ-EX", 0, 0, 0, 0 },
+ { "InsertSimpleRead",false, "INSERT", 0, 0, "S-READ", 0, 0, 0, 0 },
+ { "InsertDirtyRead", false, "INSERT", 0, 0, "D-READ", 0, 0, 0, 0 },
+ { "InsertInsert", false, "INSERT", 0, 0, "INSERT", 630, 0, 626, 0 },
+ { "InsertUpdate", false, "INSERT", 0, 0, "UPDATE", 0, 1, 0, 1 },
+ { "InsertDelete", false, "INSERT", 0, 0, "DELETE", 0, 0, 626, 0 },
+
+ { "UpdateRead", true, "UPDATE", 0, 1, "READ", 0, 1, 0, 1 },
+ { "UpdateReadEx", true, "UPDATE", 0, 1, "READ-EX", 0, 1, 0, 1 },
+ { "UpdateSimpleRead", true, "UPDATE", 0, 1, "S-READ", 0, 1, 0, 1 },
+ { "UpdateDirtyRead", true, "UPDATE", 0, 1, "D-READ", 0, 1, 0, 1 },
+ { "UpdateInsert", true, "UPDATE", 0, 1, "INSERT", 630, 0, 0, 0 },
+ { "UpdateUpdate", true, "UPDATE", 0, 1, "UPDATE", 0, 2, 0, 2 },
+ { "UpdateDelete", true, "UPDATE", 0, 1, "DELETE", 0, 0, 626, 0 },
+
+ { "DeleteRead", true, "DELETE", 0, 0, "READ", 626, 0, 0, 0 },
+ { "DeleteReadEx", true, "DELETE", 0, 0, "READ-EX", 626, 0, 0, 0 },
+ { "DeleteSimpleRead", true, "DELETE", 0, 0, "S-READ", 626, 0, 0, 0 },
+ { "DeleteDirtyRead", true, "DELETE", 0, 0, "D-READ", 626, 0, 626, 0 },
+ { "DeleteInsert", true, "DELETE", 0, 0, "INSERT", 0, 1, 0, 1 },
+ { "DeleteUpdate", true, "DELETE", 0, 0, "UPDATE", 626, 1, 0, 0 },
+ { "DeleteDelete", true, "DELETE", 0, 0, "DELETE", 626, 0, 0, 0 }
+};
+
+#define CHECK(b) if (!(b)) { \
+ g_err << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ break; }
+
+#define C3(b) if (!(b)) { \
+ g_err << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ abort(); return NDBT_FAILED; }
+
+#define C3(b) if (!(b)) { \
+ g_err << "ERR: failed on line " << __LINE__ << endl; \
+ return NDBT_FAILED; }
+
+int
+runOp(HugoOperations & hugoOps,
+ Ndb * pNdb,
+ const char * op,
+ int value){
+
+#define C2(x, y) { int r = (x); int s = (y); if(r != s) {\
+ g_err << "ERR: failed on line " << __LINE__ << ": " \
+ << r << " != " << s << endl; \
+ return NDBT_FAILED; }}
+
+ if(strcmp(op, "READ") == 0){
+ C2(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Read), 0);
+ } else if(strcmp(op, "READ-EX") == 0){
+ C2(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Exclusive), 0);
+ } else if(strcmp(op, "S-READ") == 0){
+ C2(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Read), 0);
+ } else if(strcmp(op, "D-READ") == 0){
+ C2(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_CommittedRead), 0);
+ } else if(strcmp(op, "INSERT") == 0){
+ C2(hugoOps.pkInsertRecord(pNdb, 1, 1, value), 0);
+ } else if(strcmp(op, "UPDATE") == 0){
+ C2(hugoOps.pkUpdateRecord(pNdb, 1, 1, value), 0);
+ } else if(strcmp(op, "DELETE") == 0){
+ C2(hugoOps.pkDeleteRecord(pNdb, 1, 1), 0);
+ } else {
+ g_err << __FILE__ << " - " << __LINE__
+ << ": Unknown operation" << op << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int
+checkVal(HugoOperations & hugoOps,
+ const char * op,
+ int value,
+ int result){
+ if(result != 0)
+ return NDBT_OK;
+
+ if(strcmp(op, "READ") == 0){
+ } else if(strcmp(op, "READ-EX") == 0){
+ } else if(strcmp(op, "S-READ") == 0){
+ } else if(strcmp(op, "D-READ") == 0){
+ } else {
+ return NDBT_OK;
+ }
+
+ return hugoOps.verifyUpdatesValue(value);
+}
+
+int
+runTwoOperations(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ const char * op1 = ctx->getProperty("op1", "NONE");
+ const int val1 = ctx->getProperty("val1", ~0);
+ const int res1 = ctx->getProperty("res1", ~0);
+ const char * op2 = ctx->getProperty("op2", "NONE");
+ const int res2 = ctx->getProperty("res2", ~0);
+ const int val2 = ctx->getProperty("val2", ~0);
+
+ const int res3 = ctx->getProperty("res3", ~0);
+ const int val3 = ctx->getProperty("val3", ~0);
+
+ do {
+ // Insert, read
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(runOp(hugoOps, pNdb, op1, val1) == 0);
+ AbortOption oa = (res1 == 0) ? AbortOnError : AO_IgnoreError;
+ CHECK(hugoOps.execute_NoCommit(pNdb, oa) == res1);
+ CHECK(checkVal(hugoOps, op1, val1, res1) == 0);
+
+ ndbout_c("-- running op 2");
+
+ CHECK(runOp(hugoOps, pNdb, op2, val2) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == res2);
+ CHECK(checkVal(hugoOps, op2, val2, res2) == 0);
+
+ } while(false);
+ hugoOps.closeTransaction(pNdb);
+
+ if(result != NDBT_OK)
+ return result;
+
+ do {
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(runOp(hugoOps, pNdb, "READ", 0) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == res3);
+ CHECK(checkVal(hugoOps, "READ", val3, res3) == 0);
+ } while(false);
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int
+runInsertRecord(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Insert, insert
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkInsertRecord(pNdb, 1) == 0);
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+
+ } while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int
+runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable2(GETNDB(step), records, 240) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+enum OPS { o_DONE= 0, o_INS= 1, o_UPD= 2, o_DEL= 3 };
+typedef Vector<OPS> Sequence;
+
+static
+bool
+valid(const Sequence& s)
+{
+ if(s.size() == 0)
+ return false;
+
+ for(size_t i = 1; i<s.size(); i++)
+ {
+ switch(s[i]){
+ case o_INS:
+ if(s[i-1] != o_DEL)
+ return false;
+ break;
+ case o_UPD:
+ case o_DEL:
+ if(s[i-1] == o_DEL)
+ return false;
+ break;
+ case o_DONE:
+ return true;
+ }
+ }
+ return true;
+}
+
+static
+NdbOut& operator<<(NdbOut& out, const Sequence& s)
+{
+ out << "[ ";
+ for(size_t i = 0; i<s.size(); i++)
+ {
+ switch(s[i]){
+ case o_INS:
+ out << "INS ";
+ break;
+ case o_DEL:
+ out << "DEL ";
+ break;
+ case o_UPD:
+ out << "UPD ";
+ break;
+ case o_DONE:
+ abort();
+ }
+ }
+ out << "]";
+ return out;
+}
+
+static
+void
+generate(Sequence& out, int no)
+{
+ while(no & 3)
+ {
+ out.push_back((OPS)(no & 3));
+ no >>= 2;
+ }
+}
+
+static
+void
+generate(Vector<int>& out, size_t len)
+{
+ int max= 1;
+ while(len)
+ {
+ max <<= 2;
+ len--;
+ }
+
+ len= 1;
+ for(int i = 0; i<max; i++)
+ {
+ Sequence tmp;
+ generate(tmp, i);
+
+ if(tmp.size() >= len && valid(tmp))
+ {
+ out.push_back(i);
+ len= tmp.size();
+ }
+ else
+ {
+ //ndbout << "DISCARD: " << tmp << endl;
+ }
+ }
+}
+
+static const Uint32 DUMMY = 0;
+static const Uint32 ROW = 1;
+
+int
+verify_other(NDBT_Context* ctx,
+ Ndb* pNdb, int seq, OPS latest, bool initial_row, bool commit)
+{
+ Uint32 no_wait = NdbOperation::LM_CommittedRead*
+ ctx->getProperty("NoWait", (Uint32)1);
+
+ for(size_t j = no_wait; j<3; j++)
+ {
+ HugoOperations other(*ctx->getTab());
+ C3(other.startTransaction(pNdb) == 0);
+ C3(other.pkReadRecord(pNdb, ROW, 1, (NdbOperation::LockMode)j) == 0);
+ int tmp= other.execute_Commit(pNdb);
+ if(seq == 0){
+ if(j == NdbOperation::LM_CommittedRead)
+ {
+ C3(initial_row? tmp==0 && other.verifyUpdatesValue(0) == 0 : tmp==626);
+ }
+ else
+ {
+ C3(tmp == 266);
+ }
+ }
+ else if(commit)
+ {
+ switch(latest){
+ case o_INS:
+ case o_UPD:
+ C3(tmp == 0 && other.verifyUpdatesValue(seq) == 0);
+ break;
+ case o_DEL:
+ C3(tmp == 626);
+ break;
+ case o_DONE:
+ abort();
+ }
+ }
+ else
+ {
+ // rollback
+ C3(initial_row? tmp==0 && other.verifyUpdatesValue(0) == 0 : tmp==626);
+ }
+ }
+
+ return NDBT_OK;
+}
+
+int
+verify_savepoint(NDBT_Context* ctx,
+ Ndb* pNdb, int seq, OPS latest,
+ Uint64 transactionId)
+{
+ bool initial_row= (seq == 0) && latest == o_INS;
+
+ for(size_t j = 0; j<3; j++)
+ {
+ const NdbOperation::LockMode lm= (NdbOperation::LockMode)j;
+
+ HugoOperations same(*ctx->getTab());
+ C3(same.startTransaction(pNdb) == 0);
+ same.setTransactionId(transactionId); // Cheat
+
+ /**
+ * Increase savepoint to <em>k</em>
+ */
+ for(size_t l = 1; l<=seq; l++)
+ {
+ C3(same.pkReadRecord(pNdb, DUMMY, 1, lm) == 0); // Read dummy row
+ C3(same.execute_NoCommit(pNdb) == 0);
+ g_info << "savepoint: " << l << endl;
+ }
+
+ g_info << "op(" << seq << "): "
+ << " lock mode " << lm << endl;
+
+ C3(same.pkReadRecord(pNdb, ROW, 1, lm) == 0); // Read real row
+ int tmp= same.execute_Commit(pNdb);
+ if(seq == 0)
+ {
+ if(initial_row)
+ {
+ C3(tmp == 0 && same.verifyUpdatesValue(0) == 0);
+ } else
+ {
+ C3(tmp == 626);
+ }
+ }
+ else
+ {
+ switch(latest){
+ case o_INS:
+ case o_UPD:
+ C3(tmp == 0 && same.verifyUpdatesValue(seq) == 0);
+ break;
+ case o_DEL:
+ C3(tmp == 626);
+ break;
+ case o_DONE:
+ abort();
+ }
+ }
+ }
+ return NDBT_OK;
+}
+
+int
+runOperations(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int tmp;
+ Ndb* pNdb = GETNDB(step);
+
+ Uint32 seqNo = ctx->getProperty("Sequence", (Uint32)0);
+ Uint32 commit= ctx->getProperty("Commit", (Uint32)1);
+
+ if(seqNo == 0)
+ {
+ return NDBT_FAILED;
+ }
+
+ Sequence seq;
+ generate(seq, seqNo);
+
+ {
+ // Dummy row
+ HugoOperations hugoOps(*ctx->getTab());
+ C3(hugoOps.startTransaction(pNdb) == 0);
+ C3(hugoOps.pkInsertRecord(pNdb, DUMMY, 1, 0) == 0);
+ C3(hugoOps.execute_Commit(pNdb) == 0);
+ }
+
+ const bool initial_row= (seq[0] != o_INS);
+ if(initial_row)
+ {
+ HugoOperations hugoOps(*ctx->getTab());
+ C3(hugoOps.startTransaction(pNdb) == 0);
+ C3(hugoOps.pkInsertRecord(pNdb, ROW, 1, 0) == 0);
+ C3(hugoOps.execute_Commit(pNdb) == 0);
+ }
+
+ HugoOperations trans1(*ctx->getTab());
+ C3(trans1.startTransaction(pNdb) == 0);
+ for(size_t i = 0; i<seq.size(); i++)
+ {
+ /**
+ * Perform operation
+ */
+ switch(seq[i]){
+ case o_INS:
+ C3(trans1.pkInsertRecord(pNdb, ROW, 1, i+1) == 0);
+ break;
+ case o_UPD:
+ C3(trans1.pkUpdateRecord(pNdb, ROW, 1, i+1) == 0);
+ break;
+ case o_DEL:
+ C3(trans1.pkDeleteRecord(pNdb, ROW, 1) == 0);
+ break;
+ case o_DONE:
+ abort();
+ }
+ C3(trans1.execute_NoCommit(pNdb) == 0);
+
+ /**
+ * Verify other transaction
+ */
+ if(verify_other(ctx, pNdb, 0, seq[0], initial_row, commit) != NDBT_OK)
+ return NDBT_FAILED;
+
+ /**
+ * Verify savepoint read
+ */
+ Uint64 transactionId= trans1.getTransaction()->getTransactionId();
+
+ for(size_t k=0; k<=i+1; k++)
+ {
+ if(verify_savepoint(ctx, pNdb, k,
+ k>0 ? seq[k-1] : initial_row ? o_INS : o_DONE,
+ transactionId) != NDBT_OK)
+ return NDBT_FAILED;
+ }
+ }
+
+ if(commit)
+ {
+ C3(trans1.execute_Commit(pNdb) == 0);
+ }
+ else
+ {
+ C3(trans1.execute_Rollback(pNdb) == 0);
+ }
+
+ if(verify_other(ctx, pNdb, seq.size(), seq.back(),
+ initial_row, commit) != NDBT_OK)
+ return NDBT_FAILED;
+
+ return NDBT_OK;
+}
+
+int
+main(int argc, const char** argv){
+ ndb_init();
+
+ Vector<int> tmp;
+ generate(tmp, 5);
+
+ NDBT_TestSuite ts("testOperations");
+ for(size_t i = 0; i<tmp.size(); i++)
+ {
+ BaseString name;
+ Sequence s;
+ generate(s, tmp[i]);
+ for(size_t j = 0; j<s.size(); j++){
+ switch(s[j]){
+ case o_INS:
+ name.append("_INS");
+ break;
+ case o_DEL:
+ name.append("_DEL");
+ break;
+ case o_UPD:
+ name.append("_UPD");
+ break;
+ case o_DONE:
+ abort();
+ }
+ }
+
+ BaseString n1;
+ n1.append(name);
+ n1.append("_COMMIT");
+
+ NDBT_TestCaseImpl1 *pt = new NDBT_TestCaseImpl1(&ts,
+ n1.c_str()+1, "");
+
+ pt->setProperty("Sequence", tmp[i]);
+ pt->addInitializer(new NDBT_Initializer(pt,
+ "runClearTable",
+ runClearTable));
+
+ pt->addStep(new NDBT_ParallelStep(pt,
+ "run",
+ runOperations));
+
+ pt->addFinalizer(new NDBT_Finalizer(pt,
+ "runClearTable",
+ runClearTable));
+
+ ts.addTest(pt);
+
+ name.append("_ABORT");
+ pt = new NDBT_TestCaseImpl1(&ts, name.c_str()+1, "");
+ pt->setProperty("Sequence", tmp[i]);
+ pt->setProperty("Commit", (Uint32)0);
+ pt->addInitializer(new NDBT_Initializer(pt,
+ "runClearTable",
+ runClearTable));
+
+ pt->addStep(new NDBT_ParallelStep(pt,
+ "run",
+ runOperations));
+
+ pt->addFinalizer(new NDBT_Finalizer(pt,
+ "runClearTable",
+ runClearTable));
+
+ ts.addTest(pt);
+ }
+
+ for(Uint32 i = 0; i<sizeof(matrix)/sizeof(matrix[0]); i++){
+ NDBT_TestCaseImpl1 *pt = new NDBT_TestCaseImpl1(&ts, matrix[i].name, "");
+
+ pt->addInitializer(new NDBT_Initializer(pt,
+ "runClearTable",
+ runClearTable));
+
+ if(matrix[i].preCond){
+ pt->addInitializer(new NDBT_Initializer(pt,
+ "runInsertRecord",
+ runInsertRecord));
+ }
+
+ pt->setProperty("op1", matrix[i].op1);
+ pt->setProperty("res1", matrix[i].res1);
+ pt->setProperty("val1", matrix[i].val1);
+
+ pt->setProperty("op2", matrix[i].op2);
+ pt->setProperty("res2", matrix[i].res2);
+ pt->setProperty("val2", matrix[i].val2);
+
+ pt->setProperty("res3", matrix[i].res3);
+ pt->setProperty("val3", matrix[i].val3);
+
+ pt->addStep(new NDBT_ParallelStep(pt,
+ matrix[i].name,
+ runTwoOperations));
+ pt->addFinalizer(new NDBT_Finalizer(pt,
+ "runClearTable",
+ runClearTable));
+
+ ts.addTest(pt);
+ }
+
+ return ts.execute(argc, argv);
+}
+
+template class Vector<OPS>;
+template class Vector<Sequence>;
diff --git a/storage/ndb/test/ndbapi/testOrderedIndex.cpp b/storage/ndb/test/ndbapi/testOrderedIndex.cpp
new file mode 100644
index 00000000000..b3a75410646
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testOrderedIndex.cpp
@@ -0,0 +1,225 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <Vector.hpp>
+#include <ndbapi_limits.h>
+
+const unsigned MaxTableAttrs = NDB_MAX_ATTRIBUTES_IN_TABLE;
+const unsigned MaxIndexAttrs = NDB_MAX_ATTRIBUTES_IN_INDEX;
+const unsigned MaxIndexes = 20;
+
+static unsigned
+urandom(unsigned n)
+{
+ unsigned i = random();
+ return i % n;
+}
+
+static int
+runDropIndex(NDBT_Context* ctx, NDBT_Step* step)
+{
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+ NdbDictionary::Dictionary* pDic = pNdb->getDictionary();
+ NdbDictionary::Dictionary::List list;
+ if (pDic->listIndexes(list, pTab->getName()) != 0) {
+ g_err << pTab->getName() << ": listIndexes failed" << endl;
+ ERR(pDic->getNdbError());
+ return NDBT_FAILED;
+ }
+ for (unsigned i = 0; i < list.count; i++) {
+ NDBT_Index* pInd = new NDBT_Index(list.elements[i].name);
+ pInd->setTable(pTab->getName());
+ g_info << "Drop index:" << endl << *pInd;
+ if (pInd->dropIndexInDb(pNdb) != 0) {
+ return NDBT_FAILED;
+ }
+ }
+ return NDBT_OK;
+}
+
+static Uint32 workaround[1000];
+
+static void
+setTableProperty(NDBT_Context* ctx, NDBT_Table* pTab, const char* name, Uint32 num)
+{
+ char key[200];
+ sprintf(key, "%s-%s", name, pTab->getName());
+ //ctx->setProperty(key, num);
+ workaround[pTab->getTableId()] = num;
+}
+
+static Uint32
+getTableProperty(NDBT_Context* ctx, NDBT_Table* pTab, const char* name)
+{
+ char key[200];
+ sprintf(key, "%s-%s", name, pTab->getName());
+ //Uint32 num = ctx->getProperty(key, (Uint32)-1);
+ Uint32 num = workaround[pTab->getTableId()];
+ assert(num != (Uint32)-1);
+ return num;
+}
+
+static int
+runCreateIndex(NDBT_Context* ctx, NDBT_Step* step)
+{
+ srandom(1);
+ NDBT_Table* pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+ unsigned numTabAttrs = pTab->getNumAttributes();
+ unsigned numIndex = 0;
+ while (numIndex < MaxIndexes) {
+ if (numIndex != 0 && urandom(10) == 0)
+ break;
+ char buf[200];
+ sprintf(buf, "%s_X%03d", pTab->getName(), numIndex);
+ NDBT_Index* pInd = new NDBT_Index(buf);
+ pInd->setTable(pTab->getName());
+ pInd->setType(NdbDictionary::Index::OrderedIndex);
+ pInd->setLogging(false);
+ unsigned numAttrs = 0;
+ while (numAttrs < MaxIndexAttrs) {
+ if (numAttrs != 0 && urandom(5) == 0)
+ break;
+ unsigned i = urandom(numTabAttrs);
+ const NDBT_Attribute* pAttr = pTab->getAttribute(i);
+ bool found = false;
+ for (unsigned j = 0; j < numAttrs; j++) {
+ if (strcmp(pAttr->getName(), pInd->getAttribute(j)->getName()) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ continue;
+ pInd->addAttribute(*pAttr);
+ numAttrs++;
+ }
+ g_info << "Create index:" << endl << *pInd;
+ if (pInd->createIndexInDb(pNdb, false) != 0)
+ continue;
+ numIndex++;
+ }
+ setTableProperty(ctx, pTab, "numIndex", numIndex);
+ g_info << "Created " << numIndex << " indexes on " << pTab->getName() << endl;
+ return NDBT_OK;
+}
+
+static int
+runInsertUpdate(NDBT_Context* ctx, NDBT_Step* step)
+{
+ NDBT_Table* pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+ int ret;
+ g_info << "Insert: " << pTab->getName() << endl;
+ HugoTransactions hugoTrans(*pTab);
+ ret = hugoTrans.loadTable(pNdb, ctx->getNumRecords(), 100);
+ if (ret != 0) {
+ g_err << "ERR: " << step->getName() << "failed" << endl;
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+static int
+runFullScan(NDBT_Context* ctx, NDBT_Step* step)
+{
+ NDBT_Table* pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+ unsigned cntIndex = getTableProperty(ctx, pTab, "numIndex");
+ for (unsigned numIndex = 0; numIndex < cntIndex; numIndex++) {
+ char buf[200];
+ sprintf(buf, "%s_X%03d", pTab->getName(), numIndex);
+ NDBT_Index* pInd = NDBT_Index::discoverIndexFromDb(pNdb, buf, pTab->getName());
+ assert(pInd != 0);
+ g_info << "Scan index:" << pInd->getName() << endl << *pInd;
+ NdbConnection* pCon = pNdb->startTransaction();
+ if (pCon == 0) {
+ ERR(pNdb->getNdbError());
+ return NDBT_FAILED;
+ }
+ NdbOperation* pOp = pCon->getNdbOperation(pInd->getName(),
+ pTab->getName());
+ if (pOp == 0) {
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ return NDBT_FAILED;
+ }
+ if (pOp->openScanRead() != 0) {
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ return NDBT_FAILED;
+ }
+ if (pCon->executeScan() != 0) {
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ return NDBT_FAILED;
+ }
+ unsigned rows = 0;
+ while (1) {
+ int ret = pCon->nextScanResult();
+ if (ret == 0) {
+ rows++;
+ } else if (ret == 1) {
+ break;
+ } else {
+ ERR(pCon->getNdbError());
+ pNdb->closeTransaction(pCon);
+ return NDBT_FAILED;
+ }
+ }
+ pNdb->closeTransaction(pCon);
+ g_info << "Scanned " << rows << " rows" << endl;
+ }
+ return NDBT_OK;
+}
+
+NDBT_TESTSUITE(testOrderedIndex);
+TESTCASE(
+ "DropIndex",
+ "Drop any old indexes") {
+ INITIALIZER(runDropIndex);
+}
+TESTCASE(
+ "CreateIndex",
+ "Create ordered indexes") {
+ INITIALIZER(runCreateIndex);
+}
+TESTCASE(
+ "InsertUpdate",
+ "Run inserts and updates") {
+ INITIALIZER(runInsertUpdate);
+}
+TESTCASE(
+ "FullScan",
+ "Full scan on each ordered index") {
+ INITIALIZER(runFullScan);
+}
+NDBT_TESTSUITE_END(testOrderedIndex);
+
+int
+main(int argc, const char** argv)
+{
+ ndb_init();
+ return testOrderedIndex.execute(argc, argv);
+}
+
+// vim: set sw=2:
diff --git a/storage/ndb/test/ndbapi/testPartitioning.cpp b/storage/ndb/test/ndbapi/testPartitioning.cpp
new file mode 100644
index 00000000000..9d67c27354b
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testPartitioning.cpp
@@ -0,0 +1,430 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT_Test.hpp>
+#include <NDBT_ReturnCodes.h>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+
+#define GETNDB(ps) ((NDBT_NdbApiStep*)ps)->getNdb()
+
+static Uint32 max_dks = 0;
+
+static
+int
+run_drop_table(NDBT_Context* ctx, NDBT_Step* step)
+{
+ NdbDictionary::Dictionary* dict = GETNDB(step)->getDictionary();
+ dict->dropTable(ctx->getTab()->getName());
+ return 0;
+}
+
+static
+int
+add_distribution_key(Ndb*, NdbDictionary::Table& tab, int when)
+{
+ switch(when){
+ case 0: // Before
+ break;
+ case 1: // After
+ return 0;
+ default:
+ return 0;
+ }
+
+ int keys = tab.getNoOfPrimaryKeys();
+ int dks = (2 * keys + 2) / 3; dks = (dks > max_dks ? max_dks : dks);
+ int cnt = 0;
+
+ for(unsigned i = 0; i<tab.getNoOfColumns(); i++)
+ if(tab.getColumn(i)->getPrimaryKey() &&
+ tab.getColumn(i)->getCharset() != 0)
+ keys--;
+
+ Uint32 max = NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY - tab.getNoOfPrimaryKeys();
+
+ if(max_dks < max)
+ max = max_dks;
+
+ if(keys <= 1 && max > 0)
+ {
+ dks = 1 + (rand() % max);
+ ndbout_c("%s pks: %d dks: %d", tab.getName(), keys, dks);
+ while(dks--)
+ {
+ NdbDictionary::Column col;
+ BaseString name;
+ name.assfmt("PK_DK_%d", dks);
+ col.setName(name.c_str());
+ col.setType(NdbDictionary::Column::Unsigned);
+ col.setLength(1);
+ col.setNullable(false);
+ col.setPrimaryKey(true);
+ col.setDistributionKey(true);
+ tab.addColumn(col);
+ }
+ }
+ else
+ {
+ for(unsigned i = 0; i<tab.getNoOfColumns(); i++)
+ {
+ NdbDictionary::Column* col = tab.getColumn(i);
+ if(col->getPrimaryKey() && col->getCharset() == 0)
+ {
+ if(dks >= keys || (rand() % 100) > 50)
+ {
+ col->setDistributionKey(true);
+ dks--;
+ }
+ keys--;
+ }
+ }
+ }
+ ndbout << (NDBT_Table&)tab << endl;
+
+ return 0;
+}
+
+static int
+run_create_table(NDBT_Context* ctx, NDBT_Step* step)
+{
+ max_dks = ctx->getProperty("distributionkey", (unsigned)0);
+
+ if(NDBT_Tables::createTable(GETNDB(step),
+ ctx->getTab()->getName(),
+ false, false,
+ max_dks?add_distribution_key:0) == NDBT_OK)
+ {
+ return NDBT_OK;
+ }
+
+ if(GETNDB(step)->getDictionary()->getNdbError().code == 745)
+ return NDBT_OK;
+
+ return NDBT_FAILED;
+}
+
+static int
+run_create_pk_index(NDBT_Context* ctx, NDBT_Step* step){
+ bool orderedIndex = ctx->getProperty("OrderedIndex", (unsigned)0);
+
+ Ndb* pNdb = GETNDB(step);
+ const NdbDictionary::Table *pTab =
+ pNdb->getDictionary()->getTable(ctx->getTab()->getName());
+
+ if(!pTab)
+ return NDBT_OK;
+
+ bool logged = ctx->getProperty("LoggedIndexes", orderedIndex ? 0 : 1);
+
+ BaseString name;
+ name.assfmt("IND_%s_PK_%c", pTab->getName(), orderedIndex ? 'O' : 'U');
+
+ // Create index
+ if (orderedIndex)
+ ndbout << "Creating " << ((logged)?"logged ": "temporary ") << "ordered index "
+ << name.c_str() << " (";
+ else
+ ndbout << "Creating " << ((logged)?"logged ": "temporary ") << "unique index "
+ << name.c_str() << " (";
+
+ NdbDictionary::Index pIdx(name.c_str());
+ pIdx.setTable(pTab->getName());
+ if (orderedIndex)
+ pIdx.setType(NdbDictionary::Index::OrderedIndex);
+ else
+ pIdx.setType(NdbDictionary::Index::UniqueHashIndex);
+ for (int c = 0; c< pTab->getNoOfColumns(); c++){
+ const NdbDictionary::Column * col = pTab->getColumn(c);
+ if(col->getPrimaryKey()){
+ pIdx.addIndexColumn(col->getName());
+ ndbout << col->getName() <<" ";
+ }
+ }
+
+ pIdx.setStoredIndex(logged);
+ ndbout << ") ";
+ if (pNdb->getDictionary()->createIndex(pIdx) != 0){
+ ndbout << "FAILED!" << endl;
+ const NdbError err = pNdb->getDictionary()->getNdbError();
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ ndbout << "OK!" << endl;
+ return NDBT_OK;
+}
+
+static int run_create_pk_index_drop(NDBT_Context* ctx, NDBT_Step* step){
+ bool orderedIndex = ctx->getProperty("OrderedIndex", (unsigned)0);
+
+ Ndb* pNdb = GETNDB(step);
+ const NdbDictionary::Table *pTab =
+ pNdb->getDictionary()->getTable(ctx->getTab()->getName());
+
+ if(!pTab)
+ return NDBT_OK;
+
+ BaseString name;
+ name.assfmt("IND_%s_PK_%c", pTab->getName(), orderedIndex ? 'O' : 'U');
+
+ ndbout << "Dropping index " << name.c_str() << " ";
+ if (pNdb->getDictionary()->dropIndex(name.c_str(), pTab->getName()) != 0){
+ ndbout << "FAILED!" << endl;
+ ERR(pNdb->getDictionary()->getNdbError());
+ return NDBT_FAILED;
+ } else {
+ ndbout << "OK!" << endl;
+ }
+
+ return NDBT_OK;
+}
+
+static int
+run_tests(Ndb* p_ndb, HugoTransactions& hugoTrans, int records)
+{
+ if (hugoTrans.loadTable(p_ndb, records) != 0)
+ {
+ return NDBT_FAILED;
+ }
+
+ if(hugoTrans.pkReadRecords(p_ndb, records) != 0)
+ {
+ return NDBT_FAILED;
+ }
+
+ if(hugoTrans.pkUpdateRecords(p_ndb, records) != 0)
+ {
+ return NDBT_FAILED;
+ }
+
+ if(hugoTrans.pkDelRecords(p_ndb, records) != 0)
+ {
+ return NDBT_FAILED;
+ }
+
+ if (hugoTrans.loadTable(p_ndb, records) != 0)
+ {
+ return NDBT_FAILED;
+ }
+
+ if(hugoTrans.scanUpdateRecords(p_ndb, records) != 0)
+ {
+ return NDBT_FAILED;
+ }
+
+ Uint32 abort = 23;
+ for(Uint32 j = 0; j<5; j++){
+ Uint32 parallelism = (j == 1 ? 1 : j * 3);
+ ndbout_c("parallelism: %d", parallelism);
+ if (hugoTrans.scanReadRecords(p_ndb, records, abort, parallelism,
+ NdbOperation::LM_Read) != 0)
+ {
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.scanReadRecords(p_ndb, records, abort, parallelism,
+ NdbOperation::LM_Exclusive) != 0)
+ {
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.scanReadRecords(p_ndb, records, abort, parallelism,
+ NdbOperation::LM_CommittedRead) != 0)
+ {
+ return NDBT_FAILED;
+ }
+ }
+
+ if(hugoTrans.clearTable(p_ndb, records) != 0)
+ {
+ return NDBT_FAILED;
+ }
+
+ return 0;
+}
+
+static int
+run_pk_dk(NDBT_Context* ctx, NDBT_Step* step)
+{
+ Ndb* p_ndb = GETNDB(step);
+ int records = ctx->getNumRecords();
+ const NdbDictionary::Table *tab =
+ p_ndb->getDictionary()->getTable(ctx->getTab()->getName());
+
+ if(!tab)
+ return NDBT_OK;
+
+ HugoTransactions hugoTrans(*tab);
+
+ return run_tests(p_ndb, hugoTrans, records);
+}
+
+int
+run_index_dk(NDBT_Context* ctx, NDBT_Step* step)
+{
+ Ndb* p_ndb = GETNDB(step);
+ int records = ctx->getNumRecords();
+ const NdbDictionary::Table *pTab =
+ p_ndb->getDictionary()->getTable(ctx->getTab()->getName());
+
+ if(!pTab)
+ return NDBT_OK;
+
+ bool orderedIndex = ctx->getProperty("OrderedIndex", (unsigned)0);
+
+ BaseString name;
+ name.assfmt("IND_%s_PK_%c", pTab->getName(), orderedIndex ? 'O' : 'U');
+
+ const NdbDictionary::Index * idx =
+ p_ndb->getDictionary()->getIndex(name.c_str(), pTab->getName());
+
+ if(!idx)
+ {
+ ndbout << "Failed to retreive index: " << name.c_str() << endl;
+ return NDBT_FAILED;
+ }
+
+ HugoTransactions hugoTrans(*pTab, idx);
+
+ return run_tests(p_ndb, hugoTrans, records);
+}
+
+static int
+run_startHint(NDBT_Context* ctx, NDBT_Step* step)
+{
+ Ndb* p_ndb = GETNDB(step);
+ int records = ctx->getNumRecords();
+ const NdbDictionary::Table *tab =
+ p_ndb->getDictionary()->getTable(ctx->getTab()->getName());
+
+ if(!tab)
+ return NDBT_OK;
+
+ HugoTransactions hugoTrans(*tab);
+ if (hugoTrans.loadTable(p_ndb, records) != 0)
+ {
+ return NDBT_FAILED;
+ }
+
+ NdbRestarter restarter;
+ if(restarter.insertErrorInAllNodes(8050) != 0)
+ return NDBT_FAILED;
+
+ HugoCalculator dummy(*tab);
+ int result = NDBT_OK;
+ for(int i = 0; i<records && result == NDBT_OK; i++)
+ {
+ char buffer[8000];
+ char* start= buffer + (rand() & 7);
+ char* pos= start;
+
+ for(int j = 0; j<tab->getNoOfColumns(); j++)
+ {
+ if(tab->getColumn(j)->getPartitionKey())
+ {
+ ndbout_c(tab->getColumn(j)->getName());
+ int sz = tab->getColumn(j)->getSizeInBytes();
+ int aligned_size = 4 * ((sz + 3) >> 2);
+ memset(pos, 0, aligned_size);
+ dummy.calcValue(i, j, 0, pos, sz);
+ pos += aligned_size;
+ }
+ }
+ // Now we have the pk
+ NdbTransaction* pTrans= p_ndb->startTransaction(tab, start,(pos - start));
+ HugoOperations ops(*tab);
+ ops.setTransaction(pTrans);
+ if(ops.pkReadRecord(p_ndb, i, 1) != NDBT_OK)
+ {
+ result = NDBT_FAILED;
+ break;
+ }
+
+ if(ops.execute_Commit(p_ndb) != 0)
+ {
+ result = NDBT_FAILED;
+ break;
+ }
+
+ ops.closeTransaction(p_ndb);
+ }
+ restarter.insertErrorInAllNodes(0);
+ return result;
+}
+
+
+NDBT_TESTSUITE(testPartitioning);
+TESTCASE("pk_dk",
+ "Primary key operations with distribution key")
+{
+ TC_PROPERTY("distributionkey", ~0);
+ INITIALIZER(run_drop_table);
+ INITIALIZER(run_create_table);
+ INITIALIZER(run_pk_dk);
+ INITIALIZER(run_drop_table);
+}
+TESTCASE("hash_index_dk",
+ "Unique index operatations with distribution key")
+{
+ TC_PROPERTY("distributionkey", ~0);
+ TC_PROPERTY("OrderedIndex", (unsigned)0);
+ INITIALIZER(run_drop_table);
+ INITIALIZER(run_create_table);
+ INITIALIZER(run_create_pk_index);
+ INITIALIZER(run_index_dk);
+ INITIALIZER(run_create_pk_index_drop);
+ INITIALIZER(run_drop_table);
+}
+TESTCASE("ordered_index_dk",
+ "Ordered index operatations with distribution key")
+{
+ TC_PROPERTY("distributionkey", (unsigned)1);
+ TC_PROPERTY("OrderedIndex", (unsigned)1);
+ INITIALIZER(run_drop_table);
+ INITIALIZER(run_create_table);
+ INITIALIZER(run_create_pk_index);
+ INITIALIZER(run_index_dk);
+ INITIALIZER(run_create_pk_index_drop);
+ INITIALIZER(run_drop_table);
+}
+TESTCASE("startTransactionHint",
+ "Test startTransactionHint wo/ distribution key")
+{
+ TC_PROPERTY("distributionkey", (unsigned)0);
+ INITIALIZER(run_drop_table);
+ INITIALIZER(run_create_table);
+ INITIALIZER(run_startHint);
+ INITIALIZER(run_drop_table);
+}
+TESTCASE("startTransactionHint_dk",
+ "Test startTransactionHint with distribution key")
+{
+ TC_PROPERTY("distributionkey", (unsigned)~0);
+ INITIALIZER(run_drop_table);
+ INITIALIZER(run_create_table);
+ INITIALIZER(run_startHint);
+ INITIALIZER(run_drop_table);
+}
+NDBT_TESTSUITE_END(testPartitioning);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ testPartitioning.setCreateTable(false);
+ return testPartitioning.execute(argc, argv);
+}
+
+
+
diff --git a/storage/ndb/test/ndbapi/testReadPerf.cpp b/storage/ndb/test/ndbapi/testReadPerf.cpp
new file mode 100644
index 00000000000..ba5f3c4232d
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testReadPerf.cpp
@@ -0,0 +1,409 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <random.h>
+#include <getarg.h>
+
+struct Parameter {
+ const char * name;
+ unsigned value;
+ unsigned min;
+ unsigned max;
+};
+
+#define P_OPER 0
+#define P_RANGE 1
+#define P_ROWS 2
+#define P_LOOPS 3
+#define P_CREATE 4
+#define P_LOAD 5
+
+#define P_MAX 6
+
+/**
+ * operation
+ * 0 - serial pk
+ * 1 - batch pk
+ * 2 - serial uniq
+ * 3 - batch uniq
+ * 4 - index eq
+ * 5 - range scan
+ * 6 - ordered range scan
+ * 7 - interpreted scan
+ */
+static const char * g_ops[] = {
+ "serial pk",
+ "batch pk",
+ "serial uniq index access",
+ "batch uniq index access",
+ "index eq-bound",
+ "index range",
+ "index ordered",
+ "interpreted scan"
+};
+
+#define P_OP_TYPES 8
+static Uint64 g_times[P_OP_TYPES];
+
+static
+Parameter
+g_paramters[] = {
+ { "operation", 0, 0, 6 }, // 0
+ { "range", 1000, 1, ~0 },// 1 no of rows to read
+ { "size", 1000000, 1, ~0 },// 2 rows in tables
+ { "iterations", 3, 1, ~0 },// 3
+ { "create_drop", 0, 0, 1 }, // 4
+ { "data", 0, 0, 1 } // 5
+};
+
+static Ndb* g_ndb = 0;
+static const NdbDictionary::Table * g_tab;
+static const NdbDictionary::Index * g_i_unique;
+static const NdbDictionary::Index * g_i_ordered;
+static char g_table[256];
+static char g_unique[256];
+static char g_ordered[256];
+static char g_buffer[2*1024*1024];
+
+int create_table();
+int load_table();
+int run_read();
+int clear_table();
+int drop_table();
+void print_result();
+
+int
+main(int argc, const char** argv){
+ ndb_init();
+ int verbose = 1;
+ int optind = 0;
+
+ struct getargs args[1+P_MAX] = {
+ { "verbose", 'v', arg_flag, &verbose, "Print verbose status", "verbose" }
+ };
+ const int num_args = 1 + P_MAX;
+ int i;
+ for(i = 0; i<P_MAX; i++){
+ args[i+1].long_name = g_paramters[i].name;
+ args[i+1].short_name = * g_paramters[i].name;
+ args[i+1].type = arg_integer;
+ args[i+1].value = &g_paramters[i].value;
+ BaseString tmp;
+ tmp.assfmt("min: %d max: %d", g_paramters[i].min, g_paramters[i].max);
+ args[i+1].help = strdup(tmp.c_str());
+ args[i+1].arg_help = 0;
+ }
+
+ if(getarg(args, num_args, argc, argv, &optind)) {
+ arg_printusage(args, num_args, argv[0], "tabname1 tabname2 ...");
+ return NDBT_WRONGARGS;
+ }
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ memset(g_times, 0, sizeof(g_times));
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1))
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ g_ndb = new Ndb(&con, "TEST_DB");
+ if(g_ndb->init() != 0){
+ g_err << "init() failed" << endl;
+ goto error;
+ }
+ if(g_ndb->waitUntilReady() != 0){
+ g_err << "Wait until ready failed" << endl;
+ goto error;
+ }
+ for(i = optind; i<argc; i++){
+ const char * T = argv[i];
+ g_info << "Testing " << T << endl;
+ BaseString::snprintf(g_table, sizeof(g_table), T);
+ BaseString::snprintf(g_ordered, sizeof(g_ordered), "IDX_O_%s", T);
+ BaseString::snprintf(g_unique, sizeof(g_unique), "IDX_U_%s", T);
+ if(create_table())
+ goto error;
+ if(load_table())
+ goto error;
+ for(int l = 0; l<g_paramters[P_LOOPS].value; l++){
+ for(int j = 0; j<P_OP_TYPES; j++){
+ g_paramters[P_OPER].value = j;
+ if(run_read())
+ goto error;
+ }
+ }
+ print_result();
+ }
+
+ if(g_ndb) delete g_ndb;
+ return NDBT_OK;
+error:
+ if(g_ndb) delete g_ndb;
+ return NDBT_FAILED;
+}
+
+int
+create_table(){
+ NdbDictionary::Dictionary* dict = g_ndb->getDictionary();
+ assert(dict);
+ if(g_paramters[P_CREATE].value){
+ const NdbDictionary::Table * pTab = NDBT_Tables::getTable(g_table);
+ assert(pTab);
+ NdbDictionary::Table copy = * pTab;
+ copy.setLogging(false);
+ if(dict->createTable(copy) != 0){
+ g_err << "Failed to create table: " << g_table << endl;
+ return -1;
+ }
+
+ NdbDictionary::Index x(g_ordered);
+ x.setTable(g_table);
+ x.setType(NdbDictionary::Index::OrderedIndex);
+ x.setLogging(false);
+ for (unsigned k = 0; k < copy.getNoOfColumns(); k++){
+ if(copy.getColumn(k)->getPrimaryKey()){
+ x.addColumn(copy.getColumn(k)->getName());
+ }
+ }
+
+ if(dict->createIndex(x) != 0){
+ g_err << "Failed to create index: " << endl;
+ return -1;
+ }
+
+ x.setName(g_unique);
+ x.setType(NdbDictionary::Index::UniqueHashIndex);
+ if(dict->createIndex(x) != 0){
+ g_err << "Failed to create index: " << endl;
+ return -1;
+ }
+ }
+ g_tab = dict->getTable(g_table);
+ g_i_unique = dict->getIndex(g_unique, g_table);
+ g_i_ordered = dict->getIndex(g_ordered, g_table);
+ assert(g_tab);
+ assert(g_i_unique);
+ assert(g_i_ordered);
+ return 0;
+}
+
+int
+drop_table(){
+ if(!g_paramters[P_CREATE].value)
+ return 0;
+ if(g_ndb->getDictionary()->dropTable(g_tab->getName()) != 0){
+ g_err << "Failed to drop table: " << g_tab->getName() << endl;
+ return -1;
+ }
+ g_tab = 0;
+ return 0;
+}
+
+int
+load_table(){
+ if(!g_paramters[P_LOAD].value)
+ return 0;
+
+ int rows = g_paramters[P_ROWS].value;
+ HugoTransactions hugoTrans(* g_tab);
+ if (hugoTrans.loadTable(g_ndb, rows)){
+ g_err.println("Failed to load %s with %d rows", g_tab->getName(), rows);
+ return -1;
+ }
+ return 0;
+}
+
+int
+clear_table(){
+ if(!g_paramters[P_LOAD].value)
+ return 0;
+ int rows = g_paramters[P_ROWS].value;
+
+ UtilTransactions utilTrans(* g_tab);
+ if (utilTrans.clearTable(g_ndb, rows) != 0){
+ g_err.println("Failed to clear table %s", g_tab->getName());
+ return -1;
+ }
+ return 0;
+}
+
+inline
+void err(NdbError e){
+ ndbout << e << endl;
+}
+
+int
+run_read(){
+ int iter = g_paramters[P_LOOPS].value;
+ NDB_TICKS start1, stop;
+ int sum_time= 0;
+
+ const Uint32 rows = g_paramters[P_ROWS].value;
+ const Uint32 range = g_paramters[P_RANGE].value;
+
+ start1 = NdbTick_CurrentMillisecond();
+ NdbConnection * pTrans = g_ndb->startTransaction();
+ if(!pTrans){
+ g_err << "Failed to start transaction" << endl;
+ err(g_ndb->getNdbError());
+ return -1;
+ }
+
+ NdbOperation * pOp;
+ NdbScanOperation * pSp;
+ NdbIndexOperation * pUp;
+ NdbIndexScanOperation * pIp;
+
+ Uint32 start_row = rand() % (rows - range);
+ Uint32 stop_row = start_row + range;
+
+ /**
+ * 0 - serial pk
+ * 1 - batch pk
+ * 2 - serial uniq
+ * 3 - batch uniq
+ * 4 - index eq
+ * 5 - range scan
+ * 6 - interpreted scan
+ */
+ int check = 0;
+ void* res = (void*)~0;
+ const Uint32 pk = 0;
+ Uint32 cnt = 0;
+ for(; start_row < stop_row; start_row++){
+ switch(g_paramters[P_OPER].value){
+ case 0:
+ pOp = pTrans->getNdbOperation(g_table);
+ check = pOp->readTuple();
+ check = pOp->equal(pk, start_row);
+ break;
+ case 1:
+ for(; start_row<stop_row; start_row++){
+ pOp = pTrans->getNdbOperation(g_table);
+ check = pOp->readTuple();
+ check = pOp->equal(pk, start_row);
+ for(int j = 0; j<g_tab->getNoOfColumns(); j++){
+ res = pOp->getValue(j);
+ assert(res);
+ }
+ }
+ break;
+ case 2:
+ pOp = pTrans->getNdbIndexOperation(g_unique, g_table);
+ check = pOp->readTuple();
+ check = pOp->equal(pk, start_row);
+ break;
+ case 3:
+ for(; start_row<stop_row; start_row++){
+ pOp = pTrans->getNdbIndexOperation(g_unique, g_table);
+ check = pOp->readTuple();
+ check = pOp->equal(pk, start_row);
+ for(int j = 0; j<g_tab->getNoOfColumns(); j++){
+ res = pOp->getValue(j);
+ assert(res);
+ }
+ }
+ break;
+ case 4:
+ pOp = pSp = pIp = pTrans->getNdbIndexScanOperation(g_ordered,g_table);
+ pIp->readTuples(NdbScanOperation::LM_CommittedRead, 0, 0);
+ check = pIp->setBound(pk, NdbIndexScanOperation::BoundEQ, &start_row);
+ break;
+ case 5:
+ pOp = pSp = pIp = pTrans->getNdbIndexScanOperation(g_ordered,g_table);
+ pIp->readTuples(NdbScanOperation::LM_CommittedRead, 0, 0);
+ check = pIp->setBound(pk, NdbIndexScanOperation::BoundLE, &start_row);
+ check = pIp->setBound(pk, NdbIndexScanOperation::BoundGT, &stop_row);
+ start_row = stop_row;
+ break;
+ case 6:
+ pOp = pSp = pIp = pTrans->getNdbIndexScanOperation(g_ordered,g_table);
+ pIp->readTuples(NdbScanOperation::LM_CommittedRead, 0, 0, true);
+ check = pIp->setBound(pk, NdbIndexScanOperation::BoundLE, &start_row);
+ check = pIp->setBound(pk, NdbIndexScanOperation::BoundGT, &stop_row);
+ start_row = stop_row;
+ break;
+ case 7:
+ pOp = pSp = pTrans->getNdbScanOperation(g_table);
+ pSp->readTuples(NdbScanOperation::LM_CommittedRead, 0, 0);
+ NdbScanFilter filter(pOp) ;
+ filter.begin(NdbScanFilter::AND);
+ filter.ge(pk, start_row);
+ filter.lt(pk, stop_row);
+ filter.end();
+ start_row = stop_row;
+ break;
+ }
+
+ assert(res);
+ if(check != 0){
+ ndbout << pOp->getNdbError() << endl;
+ ndbout << pTrans->getNdbError() << endl;
+ }
+ assert(check == 0);
+
+ for(int j = 0; j<g_tab->getNoOfColumns(); j++){
+ res = pOp->getValue(j);
+ assert(res);
+ }
+
+ check = pTrans->execute(NoCommit);
+ if(check != 0){
+ ndbout << pTrans->getNdbError() << endl;
+ }
+ assert(check == 0);
+ if(g_paramters[P_OPER].value >= 4){
+ while((check = pSp->nextResult(true)) == 0){
+ cnt++;
+ }
+
+ if(check == -1){
+ err(pTrans->getNdbError());
+ return -1;
+ }
+ assert(check == 1);
+ pSp->close();
+ }
+ }
+ assert(g_paramters[P_OPER].value < 4 || (cnt == range));
+
+ pTrans->close();
+
+ stop = NdbTick_CurrentMillisecond();
+ g_times[g_paramters[P_OPER].value] += (stop - start1);
+ return 0;
+}
+
+void
+print_result(){
+ int tmp = 1;
+ tmp *= g_paramters[P_RANGE].value;
+ tmp *= g_paramters[P_LOOPS].value;
+
+ int t, t2;
+ for(int i = 0; i<P_OP_TYPES; i++){
+ g_err << g_ops[i] << " avg: "
+ << (int)((1000*g_times[i])/tmp)
+ << " us/row ("
+ << (1000 * tmp)/g_times[i] << " rows / sec)" << endl;
+ }
+}
diff --git a/storage/ndb/test/ndbapi/testRestartGci.cpp b/storage/ndb/test/ndbapi/testRestartGci.cpp
new file mode 100644
index 00000000000..4e541d1f38f
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testRestartGci.cpp
@@ -0,0 +1,222 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "NDBT_Test.hpp"
+#include "NDBT_ReturnCodes.h"
+#include "HugoTransactions.hpp"
+#include "UtilTransactions.hpp"
+#include "NdbRestarter.hpp"
+
+
+/**
+ * Global vector to keep track of
+ * records stored in db
+ */
+
+struct SavedRecord {
+ int m_gci;
+ BaseString m_str;
+ SavedRecord(int _gci, BaseString _str){
+ m_gci = _gci;
+ m_str.assign(_str);
+ }
+ SavedRecord(){
+ m_gci = 0;
+ m_str = "";
+ };
+};
+Vector<SavedRecord> savedRecords;
+
+
+#define CHECK(b) if (!(b)) { \
+ ndbout << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ break; }
+
+int runInsertRememberGci(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int records = ctx->getNumRecords();
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+ int i = 0;
+
+ while(ctx->isTestStopped() == false && i < records){
+ // Insert record and read it in same transaction
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkInsertRecord(pNdb, i) == 0);
+ if (hugoOps.execute_NoCommit(pNdb) != 0){
+ ndbout << "Could not insert record " << i << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+ CHECK(hugoOps.pkReadRecord(pNdb, i) == 0);
+ if (hugoOps.execute_Commit(pNdb) != 0){
+ ndbout << "Did not find record in DB " << i << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+ savedRecords.push_back(SavedRecord(hugoOps.getRecordGci(0),
+ hugoOps.getRecordStr(0)));
+
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+ i++;
+ };
+
+ return result;
+}
+
+int runRestartGciControl(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ Ndb* pNdb = GETNDB(step);
+ UtilTransactions utilTrans(*ctx->getTab());
+ NdbRestarter restarter;
+
+ // Wait until we have enough records in db
+ int count = 0;
+ while (count < records){
+ if (utilTrans.selectCount(pNdb, 64, &count) != 0){
+ ctx->stopTest();
+ return NDBT_FAILED;
+ }
+ }
+
+ // Restart cluster with abort
+ if (restarter.restartAll(false, false, true) != 0){
+ ctx->stopTest();
+ return NDBT_FAILED;
+ }
+
+ // Stop the other thread
+ ctx->stopTest();
+
+ if (restarter.waitClusterStarted(300) != 0){
+ return NDBT_FAILED;
+ }
+
+ if (pNdb->waitUntilReady() != 0){
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int runVerifyInserts(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ Ndb* pNdb = GETNDB(step);
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoOperations hugoOps(*ctx->getTab());
+ NdbRestarter restarter;
+
+ int restartGCI = pNdb->NdbTamper(Ndb::ReadRestartGCI, 0);
+
+ ndbout << "restartGCI = " << restartGCI << endl;
+ int count = 0;
+ if (utilTrans.selectCount(pNdb, 64, &count) != 0){
+ return NDBT_FAILED;
+ }
+
+ // RULE1: The vector with saved records should have exactly as many
+ // records with lower or same gci as there are in DB
+ int recordsWithLowerOrSameGci = 0;
+ unsigned i;
+ for (i = 0; i < savedRecords.size(); i++){
+ if (savedRecords[i].m_gci <= restartGCI)
+ recordsWithLowerOrSameGci++;
+ }
+ if (recordsWithLowerOrSameGci != count){
+ ndbout << "ERR: Wrong number of expected records" << endl;
+ result = NDBT_FAILED;
+ }
+
+
+ // RULE2: The records found in db should have same or lower
+ // gci as in the vector
+ for (i = 0; i < savedRecords.size(); i++){
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, i) == 0);
+ if (hugoOps.execute_Commit(pNdb) != 0){
+ // Record was not found in db'
+
+ // Check record gci
+ if (savedRecords[i].m_gci <= restartGCI){
+ ndbout << "ERR: Record "<<i<<" should have existed" << endl;
+ result = NDBT_FAILED;
+ }
+ } else {
+ // Record was found in db
+ BaseString str = hugoOps.getRecordStr(0);
+ // Check record string
+ if (!(savedRecords[i].m_str == str)){
+ ndbout << "ERR: Record "<<i<<" str did not match "<< endl;
+ result = NDBT_FAILED;
+ }
+ // Check record gci
+ if (savedRecords[i].m_gci > restartGCI){
+ ndbout << "ERR: Record "<<i<<" should not have existed" << endl;
+ result = NDBT_FAILED;
+ }
+ }
+
+ CHECK(hugoOps.closeTransaction(pNdb) == 0);
+ }
+
+
+ ndbout << "There are " << count << " records in db" << endl;
+ ndbout << "There are " << savedRecords.size()
+ << " records in vector" << endl;
+
+ ndbout << "There are " << recordsWithLowerOrSameGci
+ << " records with lower or same gci than " << restartGCI << endl;
+
+ return result;
+}
+
+int runClearGlobals(NDBT_Context* ctx, NDBT_Step* step){
+ savedRecords.clear();
+ return NDBT_OK;
+}
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable2(GETNDB(step), records, 240) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+
+NDBT_TESTSUITE(testRestartGci);
+TESTCASE("InsertRestartGci",
+ "Verify that only expected records are still in NDB\n"
+ "after a restart" ){
+ INITIALIZER(runClearTable);
+ INITIALIZER(runClearGlobals);
+ STEP(runInsertRememberGci);
+ STEP(runRestartGciControl);
+ VERIFIER(runVerifyInserts);
+ FINALIZER(runClearTable);
+}
+NDBT_TESTSUITE_END(testRestartGci);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ return testRestartGci.execute(argc, argv);
+}
+
+template class Vector<SavedRecord>;
diff --git a/storage/ndb/test/ndbapi/testScan.cpp b/storage/ndb/test/ndbapi/testScan.cpp
new file mode 100644
index 00000000000..3b52778a013
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testScan.cpp
@@ -0,0 +1,1594 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <Vector.hpp>
+#include "ScanFunctions.hpp"
+#include <random.h>
+
+const NdbDictionary::Table *
+getTable(Ndb* pNdb, int i){
+ const NdbDictionary::Table* t = NDBT_Tables::getTable(i);
+ if (t == NULL){
+ return 0;
+ }
+ return pNdb->getDictionary()->getTable(t->getName());
+}
+
+
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getProperty("Rows", ctx->getNumRecords());
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+
+int runCreateAllTables(NDBT_Context* ctx, NDBT_Step* step){
+
+ int a = NDBT_Tables::createAllTables(GETNDB(step), false, true);
+ return a;
+}
+
+int runDropAllTablesExceptTestTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ for (int i=0; i < NDBT_Tables::getNumTables(); i++){
+
+ const NdbDictionary::Table* tab = NDBT_Tables::getTable(i);
+ if (tab == NULL){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ // Don't drop test table
+ if (strcmp(tab->getName(), ctx->getTab()->getName()) == 0){
+ continue;
+ }
+
+ int res = GETNDB(step)->getDictionary()->dropTable(tab->getName());
+ if(res == -1){
+ return NDBT_FAILED;
+ }
+ }
+ return NDBT_OK;
+}
+
+
+int runLoadAllTables(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ for (int i=0; i < NDBT_Tables::getNumTables(); i++){
+
+ const NdbDictionary::Table* tab = getTable(GETNDB(step), i);
+ if (tab == NULL){
+ return NDBT_FAILED;
+ }
+ HugoTransactions hugoTrans(*tab);
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ }
+ return NDBT_OK;
+}
+
+char orderedPkIdxName[255];
+
+int createOrderedPkIndex(NDBT_Context* ctx, NDBT_Step* step){
+
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+
+ // Create index
+ BaseString::snprintf(orderedPkIdxName, sizeof(orderedPkIdxName),
+ "IDC_O_PK_%s", pTab->getName());
+ NdbDictionary::Index pIdx(orderedPkIdxName);
+ pIdx.setTable(pTab->getName());
+ pIdx.setType(NdbDictionary::Index::OrderedIndex);
+ pIdx.setLogging(false);
+
+ for (int c = 0; c< pTab->getNoOfColumns(); c++){
+ const NdbDictionary::Column * col = pTab->getColumn(c);
+ if(col->getPrimaryKey()){
+ pIdx.addIndexColumn(col->getName());
+ }
+ }
+
+ if (pNdb->getDictionary()->createIndex(pIdx) != 0){
+ ndbout << "FAILED! to create index" << endl;
+ const NdbError err = pNdb->getDictionary()->getNdbError();
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int createOrderedPkIndex_Drop(NDBT_Context* ctx, NDBT_Step* step){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ Ndb* pNdb = GETNDB(step);
+
+ // Drop index
+ if (pNdb->getDictionary()->dropIndex(orderedPkIdxName,
+ pTab->getName()) != 0){
+ ndbout << "FAILED! to drop index" << endl;
+ ERR(pNdb->getDictionary()->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+
+int runScanReadRandomTable(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int parallelism = ctx->getProperty("Parallelism", 240);
+ int abort = ctx->getProperty("AbortProb", 5);
+
+ int i = 0;
+ while (i<loops) {
+
+ int tabNum = myRandom48(NDBT_Tables::getNumTables());
+ const NdbDictionary::Table* tab = getTable(GETNDB(step), tabNum);
+ if (tab == NULL){
+ g_info << "tab == NULL" << endl;
+ return NDBT_FAILED;
+ }
+
+ g_info << "Scan reading from table " << tab->getName() << endl;
+ HugoTransactions hugoTrans(*tab);
+
+ g_info << i << ": ";
+ if (hugoTrans.scanReadRecords(GETNDB(step), records, abort, parallelism) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runInsertUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ g_info << i << ": ";
+ if (hugoTrans.loadTable(GETNDB(step), records, 1) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runInsertDelete(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int records = ctx->getNumRecords();
+ int loops = ctx->getNumLoops();
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ UtilTransactions utilTrans(*ctx->getTab());
+ while (i<loops) {
+ g_info << i << ": ";
+ if (hugoTrans.loadTable(GETNDB(step), records, 1) != 0){
+ result = NDBT_FAILED;
+ break;
+ }
+ if (utilTrans.clearTable(GETNDB(step), records) != 0){
+ result = NDBT_FAILED;
+ break;
+ }
+ i++;
+ }
+
+ ctx->stopTest();
+
+ return result;
+}
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable2(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runScanDelete(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+
+ int i = 0;
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ g_info << i << ": ";
+ if (utilTrans.clearTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ // Load table, don't allow any primary key violations
+ if (hugoTrans.loadTable(GETNDB(step), records, 512, false) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runScanDelete2(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+
+ int i = 0;
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ while (i<loops) {
+ g_info << i << ": ";
+ if (utilTrans.clearTable2(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ // Load table, don't allow any primary key violations
+ if (hugoTrans.loadTable(GETNDB(step), records, 512, false) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runVerifyTable(NDBT_Context* ctx, NDBT_Step* step){
+ return NDBT_OK;
+}
+
+int runScanRead(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getProperty("Rows", ctx->getNumRecords());
+ int parallelism = ctx->getProperty("Parallelism", 240);
+ int abort = ctx->getProperty("AbortProb", 5);
+
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops && !ctx->isTestStopped()) {
+ g_info << i << ": ";
+ if (hugoTrans.scanReadRecords(GETNDB(step), records, abort, parallelism) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runRandScanRead(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int parallelism = ctx->getProperty("Parallelism", 240);
+ int abort = ctx->getProperty("AbortProb", 5);
+
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops && !ctx->isTestStopped()) {
+ g_info << i << ": ";
+ NdbOperation::LockMode lm = (NdbOperation::LockMode)(rand() % 3);
+ if (hugoTrans.scanReadRecords(GETNDB(step),
+ records, abort, parallelism,
+ lm) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runScanReadIndex(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int parallelism = ctx->getProperty("Parallelism", 240);
+ int abort = ctx->getProperty("AbortProb", 5);
+ const NdbDictionary::Index * pIdx =
+ GETNDB(step)->getDictionary()->getIndex(orderedPkIdxName,
+ ctx->getTab()->getName());
+
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (pIdx && i<loops && !ctx->isTestStopped()) {
+ g_info << i << ": ";
+ bool sort = (rand() % 100) > 50 ? true : false;
+ NdbOperation::LockMode lm = (NdbOperation::LockMode)(rand() % 3);
+ if (hugoTrans.scanReadRecords(GETNDB(step), pIdx,
+ records, abort, parallelism,
+ lm,
+ sort) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runScanReadCommitted(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int parallelism = ctx->getProperty("Parallelism", 240);
+ int abort = ctx->getProperty("AbortProb", 5);
+
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops && !ctx->isTestStopped()) {
+ g_info << i << ": ";
+ if (hugoTrans.scanReadRecords(GETNDB(step), records,
+ abort, parallelism,
+ NdbOperation::LM_CommittedRead) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runScanReadError(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int parallelism = 240; // Max parallelism
+ int error = ctx->getProperty("ErrorCode");
+ NdbRestarter restarter;
+
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops && !ctx->isTestStopped()) {
+ g_info << i << ": ";
+
+ ndbout << "insertErrorInAllNodes("<<error<<")"<<endl;
+ if (restarter.insertErrorInAllNodes(error) != 0){
+ ndbout << "Could not insert error in all nodes "<<endl;
+ return NDBT_FAILED;
+ }
+
+ if (hugoTrans.scanReadRecords(GETNDB(step), records, 0, parallelism) != 0){
+ result = NDBT_FAILED;
+ }
+ i++;
+ }
+
+ restarter.insertErrorInAllNodes(0);
+ return result;
+}
+
+int
+runInsertError(NDBT_Context* ctx, NDBT_Step* step){
+ int error = ctx->getProperty("ErrorCode");
+ NdbRestarter restarter;
+
+ ctx->setProperty("ErrorCode", (Uint32)0);
+ if (restarter.insertErrorInAllNodes(error) != 0){
+ ndbout << "Could not insert error in all nodes "<<endl;
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runScanReadErrorOneNode(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int parallelism = 240; // Max parallelism
+ int error = ctx->getProperty("ErrorCode");
+ NdbRestarter restarter;
+ int lastId = 0;
+
+ if (restarter.getNumDbNodes() < 2){
+ ctx->stopTest();
+ return NDBT_OK;
+ }
+
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops && result == NDBT_OK) {
+ g_info << i << ": ";
+
+ int nodeId = restarter.getDbNodeId(lastId);
+ lastId = (lastId + 1) % restarter.getNumDbNodes();
+ ndbout << "insertErrorInNode("<<nodeId<<", "<<error<<")"<<endl;
+ if (restarter.insertErrorInNode(nodeId, error) != 0){
+ ndbout << "Could not insert error in node="<<nodeId<<endl;
+ return NDBT_FAILED;
+ }
+
+ for (int j=0; j<10; j++){
+ if (hugoTrans.scanReadRecords(GETNDB(step),
+ records, 0, parallelism) != 0)
+ result = NDBT_FAILED;
+ }
+
+
+ if(restarter.waitClusterStarted(120) != 0){
+ g_err << "Cluster failed to restart" << endl;
+ result = NDBT_FAILED;
+ }
+ restarter.insertErrorInAllNodes(0);
+
+ i++;
+ }
+ restarter.insertErrorInAllNodes(0);
+ return result;
+}
+
+int runRestartAll(NDBT_Context* ctx, NDBT_Step* step){
+
+ NdbRestarter restarter;
+
+ if (restarter.restartAll() != 0){
+ ndbout << "Could not restart all nodes"<<endl;
+ return NDBT_FAILED;
+ }
+
+ if (restarter.waitClusterStarted(120) != 0){
+ ndbout << "Could not restarted" << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+static int RANDOM_PARALLELISM = 9999;
+
+int runScanReadUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int i = 0;
+
+ int parallelism = ctx->getProperty("Parallelism", 240);
+ int para = parallelism;
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ if (parallelism == RANDOM_PARALLELISM)
+ para = myRandom48(239)+1;
+
+ g_info << i << ": ";
+ if (hugoTrans.scanReadRecords(GETNDB(step), records, 0, para) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runScanReadUntilStoppedNoCount(NDBT_Context* ctx, NDBT_Step* step){
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ g_info << i << ": ";
+ if (hugoTrans.scanReadRecords(GETNDB(step), 0) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runScanReadUntilStoppedPrintTime(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int i = 0;
+ int parallelism = ctx->getProperty("Parallelism", 240);
+ NdbTimer timer;
+ Ndb* ndb = GETNDB(step);
+
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ timer.doReset();
+ timer.doStart();
+ g_info << i << ": ";
+ if (ndb->waitUntilReady() != 0)
+ return NDBT_FAILED;
+ if (hugoTrans.scanReadRecords(GETNDB(step), records, 0, parallelism) != 0)
+ return NDBT_FAILED;
+ timer.doStop();
+ if ((timer.elapsedTime()/1000) > 1)
+ timer.printTotalTime();
+ i++;
+ }
+ return NDBT_OK;
+}
+
+
+int runPkRead(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ g_info << i << ": ";
+ if (hugoTrans.pkReadRecords(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runScanUpdate(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int parallelism = ctx->getProperty("Parallelism", 1);
+ int abort = ctx->getProperty("AbortProb", 5);
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ g_info << i << ": ";
+
+ if (hugoTrans.scanUpdateRecords(GETNDB(step), records, abort, parallelism) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runScanUpdateUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int i = 0;
+
+ int parallelism = ctx->getProperty("Parallelism", 240);
+ int para = parallelism;
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (ctx->isTestStopped() == false) {
+ if (parallelism == RANDOM_PARALLELISM)
+ para = myRandom48(239)+1;
+
+ g_info << i << ": ";
+ if (hugoTrans.scanUpdateRecords(GETNDB(step), records, 0, para) == NDBT_FAILED){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+
+int runScanUpdate2(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int parallelism = ctx->getProperty("Parallelism", 240);
+ int abort = ctx->getProperty("AbortProb", 5);
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ g_info << i << ": ";
+ if (hugoTrans.scanUpdateRecords2(GETNDB(step), records, abort, parallelism) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runLocker(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ if (hugoTrans.lockRecords(GETNDB(step), records, 5, 500) != 0){
+ result = NDBT_FAILED;
+ }
+ ctx->stopTest();
+
+ return result;
+}
+
+int runRestarter(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ NdbRestarter restarter;
+ int i = 0;
+ int lastId = 0;
+ int timeout = 240;
+
+ if (restarter.getNumDbNodes() < 2){
+ ctx->stopTest();
+ return NDBT_OK;
+ }
+ while(i<loops && result != NDBT_FAILED){
+ if(restarter.waitClusterStarted(timeout) != 0){
+ g_err << "Cluster failed to start 1" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+ NdbSleep_SecSleep(10);
+
+ int nodeId = restarter.getDbNodeId(lastId);
+ lastId = (lastId + 1) % restarter.getNumDbNodes();
+ if(restarter.restartOneDbNode(nodeId) != 0){
+ g_err << "Failed to restartNextDbNode" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+ i++;
+ }
+ if(restarter.waitClusterStarted(timeout) != 0){
+ g_err << "Cluster failed to start 2" << endl;
+ result = NDBT_FAILED;
+ }
+
+ ctx->stopTest();
+
+ return result;
+}
+
+
+int runStopAndStartNode(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ NdbRestarter restarter;
+ int i = 0;
+ int lastId = 0;
+ int timeout = 240;
+
+ if (restarter.getNumDbNodes() < 2){
+ ctx->stopTest();
+ return NDBT_OK;
+ }
+ while(i<loops && result != NDBT_FAILED){
+ if(restarter.waitClusterStarted(timeout) != 0){
+ g_err << "Cluster failed to start 1" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+ NdbSleep_SecSleep(1);
+ int nodeId = restarter.getDbNodeId(lastId);
+ lastId = (lastId + 1) % restarter.getNumDbNodes();
+ g_err << "Stopping node " << nodeId << endl;
+
+ if(restarter.restartOneDbNode(nodeId, false, true) != 0){
+ g_err << "Failed to restartOneDbNode" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+
+ if(restarter.waitNodesNoStart(&nodeId, 1, timeout) != 0){
+ g_err << "Node failed to reach NoStart" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+
+ g_info << "Sleeping for 10 secs" << endl;
+ NdbSleep_SecSleep(10);
+
+ g_err << "Starting node " << nodeId << endl;
+ if(restarter.startNodes(&nodeId, 1) != 0){
+ g_err << "Failed to start the node" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+
+ i++;
+ }
+ if(restarter.waitClusterStarted(timeout) != 0){
+ g_err << "Cluster failed to start 2" << endl;
+ result = NDBT_FAILED;
+ }
+
+ ctx->stopTest();
+
+ return result;
+}
+
+int runRestarter9999(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ NdbRestarter restarter;
+ int i = 0;
+ int lastId = 0;
+
+ if (restarter.getNumDbNodes() < 2){
+ ctx->stopTest();
+ return NDBT_OK;
+ }
+ while(i<loops && result != NDBT_FAILED){
+ if(restarter.waitClusterStarted(120) != 0){
+ g_err << "Cluster failed to start" << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+ NdbSleep_SecSleep(10);
+
+ int nodeId = restarter.getDbNodeId(lastId);
+ lastId = (lastId + 1) % restarter.getNumDbNodes();
+ if(restarter.insertErrorInNode(nodeId, 9999) != 0){
+ g_err << "Failed to insertErrorInNode="<<nodeId << endl;
+ result = NDBT_FAILED;
+ break;
+ }
+ NdbSleep_SecSleep(10);
+ i++;
+ }
+ if(restarter.waitClusterStarted(120) != 0){
+ g_err << "Cluster failed to start" << endl;
+ result = NDBT_FAILED;
+ }
+
+ ctx->stopTest();
+
+ return result;
+}
+
+
+int runCheckGetValue(NDBT_Context* ctx, NDBT_Step* step){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ int parallelism = ctx->getProperty("Parallelism", 1);
+ int records = ctx->getNumRecords();
+ int numFailed = 0;
+ AttribList alist;
+ alist.buildAttribList(pTab);
+ UtilTransactions utilTrans(*pTab);
+ for(size_t i = 0; i < alist.attriblist.size(); i++){
+ g_info << (unsigned)i << endl;
+ if(utilTrans.scanReadRecords(GETNDB(step),
+ parallelism,
+ NdbOperation::LM_Read,
+ records,
+ alist.attriblist[i]->numAttribs,
+ alist.attriblist[i]->attribs) != 0){
+ numFailed++;
+ }
+ if(utilTrans.scanReadRecords(GETNDB(step),
+ parallelism,
+ NdbOperation::LM_Read,
+ records,
+ alist.attriblist[i]->numAttribs,
+ alist.attriblist[i]->attribs) != 0){
+ numFailed++;
+ }
+ }
+
+ if(numFailed > 0)
+ return NDBT_FAILED;
+ else
+ return NDBT_OK;
+}
+
+int runCloseWithoutStop(NDBT_Context* ctx, NDBT_Step* step){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ int records = ctx->getNumRecords();
+ int numFailed = 0;
+ ScanFunctions scanF(*pTab);
+ // Iterate over all possible parallelism valuse
+ for (int p = 1; p<240; p++){
+ g_info << p << " CloseWithoutStop openScan" << endl;
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ p,
+ ScanFunctions::CloseWithoutStop,
+ false) != 0){
+ numFailed++;
+ }
+ g_info << p << " CloseWithoutStop openScanExclusive" << endl;
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ p,
+ ScanFunctions::CloseWithoutStop,
+ true) != 0){
+ numFailed++;
+ }
+ }
+
+ if(numFailed > 0)
+ return NDBT_FAILED;
+ else
+ return NDBT_OK;
+}
+
+int runNextScanWhenNoMore(NDBT_Context* ctx, NDBT_Step* step){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ int records = ctx->getNumRecords();
+ int numFailed = 0;
+ ScanFunctions scanF(*pTab);
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ 6,
+ ScanFunctions::NextScanWhenNoMore,
+ false) != 0){
+ numFailed++;
+ }
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ 6,
+ ScanFunctions::NextScanWhenNoMore,
+ true) != 0){
+ numFailed++;
+ }
+
+
+ if(numFailed > 0)
+ return NDBT_FAILED;
+ else
+ return NDBT_OK;
+}
+
+int runEqualAfterOpenScan(NDBT_Context* ctx, NDBT_Step* step){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ int records = ctx->getNumRecords();
+ int numFailed = 0;
+ ScanFunctions scanF(*pTab);
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ 6,
+ ScanFunctions::EqualAfterOpenScan,
+ false) == NDBT_OK){
+ numFailed++;
+ }
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ 6,
+ ScanFunctions::EqualAfterOpenScan,
+ true) == NDBT_OK){
+ numFailed++;
+ }
+
+
+ if(numFailed > 0)
+ return NDBT_FAILED;
+ else
+ return NDBT_OK;
+}
+
+int runOnlyOpenScanOnce(NDBT_Context* ctx, NDBT_Step* step){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ int records = ctx->getNumRecords();
+ int numFailed = 0;
+ ScanFunctions scanF(*pTab);
+ g_info << "OnlyOpenScanOnce openScanRead" << endl;
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ 6,
+ ScanFunctions::OnlyOpenScanOnce,
+ false) == 0){
+ numFailed++;
+ }
+ g_info << "OnlyOpenScanOnce openScanExclusive" << endl;
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ 6,
+ ScanFunctions::OnlyOpenScanOnce,
+ true) == 0){
+ numFailed++;
+ }
+
+
+ if(numFailed > 0)
+ return NDBT_FAILED;
+ else
+ return NDBT_OK;
+}
+
+int runOnlyOneOpInScanTrans(NDBT_Context* ctx, NDBT_Step* step){
+ return NDBT_OK;
+}
+
+int runExecuteScanWithoutOpenScan(NDBT_Context* ctx, NDBT_Step* step){
+ return NDBT_OK;
+}
+
+int runOnlyOneOpBeforeOpenScan(NDBT_Context* ctx, NDBT_Step* step){
+ return NDBT_OK;
+}
+
+int runOnlyOneScanPerTrans(NDBT_Context* ctx, NDBT_Step* step){
+ return NDBT_OK;
+}
+
+int runNoCloseTransaction(NDBT_Context* ctx, NDBT_Step* step){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int numFailed = 0;
+
+ ScanFunctions scanF(*pTab);
+ int l = 0;
+ while(l < loops){
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ 6,
+ ScanFunctions::NoCloseTransaction,
+ false) != 0){
+ numFailed++;
+ }
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ 6,
+ ScanFunctions::NoCloseTransaction,
+ true) != 0){
+ numFailed++;
+ }
+ l++;
+ }
+
+
+ if(numFailed > 0)
+ return NDBT_FAILED;
+ else
+ return NDBT_OK;
+
+}
+
+int runCheckInactivityTimeOut(NDBT_Context* ctx, NDBT_Step* step){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ int records = ctx->getNumRecords();
+ int numFailed = 0;
+
+ ScanFunctions scanF(*pTab);
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ 1,
+ ScanFunctions::CheckInactivityTimeOut,
+ false) != NDBT_OK){
+ numFailed++;
+ }
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ 240,
+ ScanFunctions::CheckInactivityTimeOut,
+ true) != NDBT_OK){
+ numFailed++;
+ }
+
+ if(numFailed > 0)
+ return NDBT_FAILED;
+ else
+ return NDBT_OK;
+
+}
+
+int runCheckInactivityBeforeClose(NDBT_Context* ctx, NDBT_Step* step){
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ int records = ctx->getNumRecords();
+ int numFailed = 0;
+
+ ScanFunctions scanF(*pTab);
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ 16,
+ ScanFunctions::CheckInactivityBeforeClose,
+ false) != 0){
+ numFailed++;
+ }
+ if (scanF.scanReadFunctions(GETNDB(step),
+ records,
+ 240,
+ ScanFunctions::CheckInactivityBeforeClose,
+ true) != 0){
+ numFailed++;
+ }
+
+ if(numFailed > 0)
+ return NDBT_FAILED;
+ else
+ return NDBT_OK;
+
+}
+
+int runScanRestart(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ Ndb * pNdb = GETNDB(step);
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ HugoCalculator calc(* pTab);
+ NDBT_ResultRow tmpRow(* pTab);
+
+ int i = 0;
+ while (i<loops && !ctx->isTestStopped()) {
+ g_info << i++ << ": ";
+ const int record = (rand() % records);
+ g_info << " row=" << record;
+
+ NdbConnection* pCon = pNdb->startTransaction();
+ NdbScanOperation* pOp = pCon->getNdbScanOperation(pTab->getName());
+ if (pOp == NULL) {
+ ERR(pCon->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples() ) {
+ ERR(pCon->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ int check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pCon->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Define attributes to read
+ for(int a = 0; a<pTab->getNoOfColumns(); a++){
+ if((tmpRow.attributeStore(a) =
+ pOp->getValue(pTab->getColumn(a)->getName())) == 0) {
+ ERR(pCon->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+
+ check = pCon->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pCon->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ int res;
+ int row = 0;
+ while(row < record && (res = pOp->nextResult()) == 0) {
+ if(calc.verifyRowValues(&tmpRow) != 0){
+ abort();
+ return NDBT_FAILED;
+ }
+ row++;
+ }
+ if(row != record){
+ ERR(pCon->getNdbError());
+ abort();
+ return NDBT_FAILED;
+ }
+ g_info << " restarting" << endl;
+ if((res = pOp->restart()) != 0){
+ ERR(pCon->getNdbError());
+ abort();
+ return NDBT_FAILED;
+ }
+
+ row = 0;
+ while((res = pOp->nextResult()) == 0) {
+ if(calc.verifyRowValues(&tmpRow) != 0){
+ abort();
+ return NDBT_FAILED;
+ }
+ row++;
+ }
+ if(res != 1 || row != records){
+ ERR(pCon->getNdbError());
+ abort();
+ return NDBT_FAILED;
+ }
+ pCon->close();
+ }
+ return NDBT_OK;
+}
+
+
+int
+runScanParallelism(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops() + 3;
+ int records = ctx->getNumRecords();
+ int abort = ctx->getProperty("AbortProb", 15);
+
+ Uint32 fib[] = { 1, 2 };
+ Uint32 parallelism = 0; // start with 0
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops && !ctx->isTestStopped()) {
+ g_info << i << ": ";
+
+ if (hugoTrans.scanReadRecords(GETNDB(step), records, abort, parallelism,
+ NdbOperation::LM_Read) != 0){
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.scanReadRecords(GETNDB(step), records, abort, parallelism,
+ NdbOperation::LM_Exclusive) != 0){
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.scanReadRecords(GETNDB(step), records, abort, parallelism,
+ NdbOperation::LM_CommittedRead) != 0){
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.scanUpdateRecords(GETNDB(step), records, abort, parallelism)
+ != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ parallelism = fib[0];
+ Uint32 next = fib[0] + fib[1];
+ fib[0] = fib[1];
+ fib[1] = next;
+ }
+ return NDBT_OK;
+}
+
+NDBT_TESTSUITE(testScan);
+TESTCASE("ScanRead",
+ "Verify scan requirement: It should be possible "\
+ "to read all records in a table without knowing their "\
+ "primary key."){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 1);
+ STEP(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanRead16",
+ "Verify scan requirement: It should be possible to scan read "\
+ "with parallelism, test with parallelism 16"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 16);
+ STEP(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanRead240",
+ "Verify scan requirement: It should be possible to scan read with "\
+ "parallelism, test with parallelism 240(240 would automatically be "\
+ "downgraded to the maximum parallelism value for the current config)"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 240);
+ STEP(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadCommitted240",
+ "Verify scan requirement: It should be possible to scan read committed with "\
+ "parallelism, test with parallelism 240(240 would automatically be "\
+ "downgraded to the maximum parallelism value for the current config)"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 240);
+ STEP(runScanReadCommitted);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanUpdate",
+ "Verify scan requirement: It should be possible "\
+ "to update all records in a table without knowing their"\
+ " primary key."){
+ INITIALIZER(runLoadTable);
+ STEP(runScanUpdate);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanUpdate2",
+ "Verify scan requirement: It should be possible "\
+ "to update all records in a table without knowing their"\
+ " primary key. Do this efficently by calling nextScanResult(false) "\
+ "in order to update the records already fetched to the api in one batch."){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 240);
+ STEP(runScanUpdate2);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanDelete",
+ "Verify scan requirement: It should be possible "\
+ "to delete all records in a table without knowing their"\
+ " primary key."){
+ INITIALIZER(runLoadTable);
+ STEP(runScanDelete);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanDelete2",
+ "Verify scan requirement: It should be possible "\
+ "to delete all records in a table without knowing their"\
+ " primary key. Do this efficently by calling nextScanResult(false) "\
+ "in order to delete the records already fetched to the api in one batch."){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 240);
+ STEP(runScanDelete2);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanUpdateAndScanRead",
+ "Verify scan requirement: It should be possible to run "\
+ "scan read and scan update at the same time"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 16);
+ STEP(runScanRead);
+ STEP(runScanUpdate);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadAndLocker",
+ "Verify scan requirement: The locks are not kept throughout "\
+ "the entire scan operation. This means that a scan does not "\
+ "lock the entire table, only the records it's currently "\
+ "operating on. This will test how scan performs when there are "\
+ " a number of 1 second locks in the table"){
+ INITIALIZER(runLoadTable);
+ STEP(runScanReadUntilStopped);
+ STEP(runLocker);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadAndPkRead",
+ "Verify scan requirement: The locks are not kept throughout "\
+ "the entire scan operation. This means that a scan does not "\
+ "lock the entire table, only the records it's currently "\
+ "operating on. This will test how scan performs when there are "\
+ " a pk reads "){
+ INITIALIZER(runLoadTable);
+ STEPS(runScanRead, 2);
+ STEPS(runPkRead, 2);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanRead488",
+ "Verify scan requirement: It's only possible to have 11 concurrent "\
+ "scans per fragment running in Ndb kernel at the same time. "\
+ "When this limit is exceeded the scan will be aborted with errorcode "\
+ "488."){
+ INITIALIZER(runLoadTable);
+ STEPS(runRandScanRead, 70);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanRead488O",
+ "Verify scan requirement: It's only possible to have 11 concurrent "\
+ "scans per fragment running in Ndb kernel at the same time. "\
+ "When this limit is exceeded the scan will be aborted with errorcode "\
+ "488."){
+ INITIALIZER(createOrderedPkIndex);
+ INITIALIZER(runLoadTable);
+ STEPS(runScanReadIndex, 70);
+ FINALIZER(createOrderedPkIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanRead488_Mixed",
+ "Verify scan requirement: It's only possible to have 11 concurrent "\
+ "scans per fragment running in Ndb kernel at the same time. "\
+ "When this limit is exceeded the scan will be aborted with errorcode "\
+ "488."){
+ INITIALIZER(createOrderedPkIndex);
+ INITIALIZER(runLoadTable);
+ STEPS(runRandScanRead, 50);
+ STEPS(runScanReadIndex, 50);
+ FINALIZER(createOrderedPkIndex_Drop);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanRead488Timeout",
+ ""){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("ErrorCode", 5034);
+ STEPS(runScanRead, 30);
+ STEP(runScanReadError);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanRead40",
+ "Verify scan requirement: Scan with 40 simultaneous threads"){
+ INITIALIZER(runLoadTable);
+ STEPS(runScanRead, 40);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanRead100",
+ "Verify scan requirement: Scan with 100 simultaneous threads"){
+ INITIALIZER(runLoadTable);
+ STEPS(runScanRead, 100);
+ FINALIZER(runClearTable);
+}
+TESTCASE("Scan-bug8262",
+ ""){
+ TC_PROPERTY("Rows", 1);
+ TC_PROPERTY("ErrorCode", 8035);
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runInsertError); // Will reset error code
+ STEPS(runScanRead, 25);
+ FINALIZER(runInsertError);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanRead40RandomTable",
+ "Verify scan requirement: Scan with 40 simultaneous threads. "\
+ "Use random table for the scan"){
+ INITIALIZER(runCreateAllTables);
+ INITIALIZER(runLoadAllTables);
+ STEPS(runScanReadRandomTable, 40);
+ FINALIZER(runDropAllTablesExceptTestTable);
+}
+TESTCASE("ScanRead100RandomTable",
+ "Verify scan requirement: Scan with 100 simultaneous threads. "\
+ "Use random table for the scan"){
+ INITIALIZER(runCreateAllTables);
+ INITIALIZER(runLoadAllTables);
+ STEPS(runScanReadRandomTable, 100);
+ FINALIZER(runDropAllTablesExceptTestTable);
+}
+TESTCASE("ScanReadRandomPrepare",
+ "Create and load tables for ScanRead40RandomNoTableCreate."){
+ INITIALIZER(runCreateAllTables);
+ INITIALIZER(runLoadAllTables);
+}
+TESTCASE("ScanRead40RandomNoTableCreate",
+ "Verify scan requirement: Scan with 40 simultaneous threads. "\
+ "Use random table for the scan. Dont create or load the tables."){
+ STEPS(runScanReadRandomTable, 40);
+}
+TESTCASE("ScanRead100RandomNoTableCreate",
+ "Verify scan requirement: Scan with 100 simultaneous threads. "\
+ "Use random table for the scan. Dont create or load the tables."){
+ STEPS(runScanReadRandomTable, 100);
+}
+TESTCASE("ScanWithLocksAndInserts",
+ "TR457: This test is added to verify that an insert of a records "\
+ "that is already in the database does not delete the record"){
+ INITIALIZER(runLoadTable);
+ STEPS(runScanReadUntilStopped, 2);
+ STEP(runLocker);
+ STEP(runInsertUntilStopped);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadAbort",
+ "Scan requirement: A scan may be aborted by the application "\
+ "at any time. This can be performed even if there are more "\
+ "tuples to scan."){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("AbortProb", 90);
+ STEPS(runScanRead, 3);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadAbort15",
+ "Scan requirement: A scan may be aborted by the application "\
+ "at any time. This can be performed even if there are more "\
+ "tuples to scan. Use parallelism 15"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 15);
+ TC_PROPERTY("AbortProb", 90);
+ STEPS(runScanRead, 3);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadAbort240",
+ "Scan requirement: A scan may be aborted by the application "\
+ "at any time. This can be performed even if there are more "\
+ "tuples to scan. Use parallelism 240(it will be downgraded to max para for this config)"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 240);
+ TC_PROPERTY("AbortProb", 90);
+ STEPS(runScanRead, 3);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanUpdateAbort16",
+ "Scan requirement: A scan may be aborted by the application "\
+ "at any time. This can be performed even if there are more "\
+ "tuples to scan. Use parallelism 16"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 16);
+ TC_PROPERTY("AbortProb", 90);
+ STEPS(runScanUpdate, 3);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanUpdateAbort240",
+ "Scan requirement: A scan may be aborted by the application "\
+ "at any time. This can be performed even if there are more "\
+ "tuples to scan. Use parallelism 240(it will be downgraded to max para for this config)"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 240);
+ TC_PROPERTY("AbortProb", 90);
+ STEPS(runScanUpdate, 3);
+ FINALIZER(runClearTable);
+}
+TESTCASE("CheckGetValue",
+ "Check that we can call getValue to read attributes"\
+ "Especially interesting to see if we can read only the"\
+ " first, last or any two attributes from the table"){
+ INITIALIZER(runLoadTable);
+ STEP(runCheckGetValue);
+ VERIFIER(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("CloseWithoutStop",
+ "Check that we can close the scanning transaction without calling "\
+ "stopScan"){
+ INITIALIZER(runLoadTable);
+ STEP(runCloseWithoutStop);
+ VERIFIER(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NextScanWhenNoMore",
+ "Check that we can call nextScanResult when there are no more "\
+ "records, and that it returns a valid value"){
+ INITIALIZER(runLoadTable);
+ STEP(runNextScanWhenNoMore);
+ VERIFIER(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("EqualAfterOpenScan",
+ "Check that we can't call equal after openScan"){
+ STEP(runEqualAfterOpenScan);
+}
+TESTCASE("ExecuteScanWithoutOpenScan",
+ "Check that we can't call executeScan without defining a scan "\
+ "with openScan"){
+ INITIALIZER(runLoadTable);
+ STEP(runExecuteScanWithoutOpenScan);
+ VERIFIER(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("OnlyOpenScanOnce",
+ "Check that we may only call openScan once in the same trans"){
+ INITIALIZER(runLoadTable);
+ STEP(runOnlyOpenScanOnce);
+ VERIFIER(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("OnlyOneOpInScanTrans",
+ "Check that we can have only one operation in a scan trans"){
+ INITIALIZER(runLoadTable);
+ STEP(runOnlyOneOpInScanTrans);
+ VERIFIER(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("OnlyOneOpBeforeOpenScan",
+ "Check that we can have only one operation in a trans defined "\
+ "when calling openScan "){
+ INITIALIZER(runLoadTable);
+ STEP(runOnlyOneOpBeforeOpenScan);
+ VERIFIER(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("OnlyOneScanPerTrans",
+ "Check that we can have only one scan operation in a trans"){
+ INITIALIZER(runLoadTable);
+ STEP(runOnlyOneScanPerTrans);
+ VERIFIER(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("NoCloseTransaction",
+ "Check behaviour when close transaction is not called "){
+ INITIALIZER(runLoadTable);
+ STEP(runNoCloseTransaction);
+ VERIFIER(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("CheckInactivityTimeOut",
+ "Check behaviour when the api sleeps for a long time before continuing scan "){
+ INITIALIZER(runLoadTable);
+ STEP(runCheckInactivityTimeOut);
+ VERIFIER(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("CheckInactivityBeforeClose",
+ "Check behaviour when the api sleeps for a long time before calling close scan "){
+ INITIALIZER(runLoadTable);
+ STEP(runCheckInactivityBeforeClose);
+ VERIFIER(runScanRead);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadError5021",
+ "Scan and insert error 5021, one node is expected to crash"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("ErrorCode", 5021);
+ STEP(runScanReadErrorOneNode);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadError5022",
+ "Scan and insert error 5022, one node is expected to crash"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("ErrorCode", 5022);
+ TC_PROPERTY("NodeNumber", 2);
+ STEP(runScanReadErrorOneNode);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadError5023",
+ "Scan and insert error 5023"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("ErrorCode", 5023);
+ STEP(runScanReadError);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadError5024",
+ "Scan and insert error 5024"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("ErrorCode", 5024);
+ STEP(runScanReadError);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadError5025",
+ "Scan and insert error 5025"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("ErrorCode", 5025);
+ STEP(runScanReadError);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadError5030",
+ "Scan and insert error 5030."\
+ "Drop all SCAN_NEXTREQ signals in LQH until the node is "\
+ "shutdown with SYSTEM_ERROR because of scan fragment timeout"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("ErrorCode", 5030);
+ STEP(runScanReadErrorOneNode);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadRestart",
+ "Scan requirement:A scan should be able to start and "\
+ "complete during node recovery and when one or more nodes "\
+ "in the cluster is down.Use random parallelism "){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", RANDOM_PARALLELISM); // Random
+ STEP(runScanReadUntilStopped);
+ STEP(runRestarter);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanUpdateRestart",
+ "Scan requirement:A scan should be able to start and "\
+ "complete during node recovery and when one or more nodes "\
+ "in the cluster is down. Use random parallelism"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", RANDOM_PARALLELISM); // Random
+ STEP(runScanUpdateUntilStopped);
+ STEP(runRestarter);
+ FINALIZER(runClearTable);
+}
+#if 0
+TESTCASE("ScanReadRestart9999",
+ "Scan requirement:A scan should be able to start and "\
+ "complete during node recovery and when one or more nodes "\
+ "in the cluster is down. Use parallelism 240."\
+ "Restart using error insert 9999"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 240);
+ STEP(runScanReadUntilStopped);
+ STEP(runRestarter9999);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanUpdateRestart9999",
+ "Scan requirement:A scan should be able to start and "\
+ "complete during node recovery and when one or more nodes "\
+ "in the cluster is down. Use parallelism 240."\
+ "Restart using error insert 9999"){
+ INITIALIZER(runLoadTable);
+ TC_PROPERTY("Parallelism", 240);
+ STEP(runScanReadUntilStopped);
+ STEP(runScanUpdateUntilStopped);
+ STEP(runRestarter9999);
+ FINALIZER(runClearTable);
+}
+#endif
+TESTCASE("InsertDelete",
+ "Load and delete all while scan updating and scan reading\n"\
+ "Alexander Lukas special"){
+ INITIALIZER(runClearTable);
+ STEP(runScanReadUntilStoppedNoCount);
+ STEP(runScanUpdateUntilStopped);
+ STEP(runInsertDelete);
+ FINALIZER(runClearTable);
+}
+TESTCASE("CheckAfterTerror",
+ "Check that we can still scan read after this terror of NdbApi"){
+ INITIALIZER(runLoadTable);
+ STEPS(runScanRead, 5);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanReadWhileNodeIsDown",
+ "Scan requirement:A scan should be able to run as fast when "\
+ "one or more nodes in the cluster is down."){
+ INITIALIZER(runLoadTable);
+ STEP(runScanReadUntilStoppedPrintTime);
+ STEP(runStopAndStartNode);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanRestart",
+ "Verify restart functionallity"){
+ INITIALIZER(runLoadTable);
+ STEP(runScanRestart);
+ FINALIZER(runClearTable);
+}
+TESTCASE("ScanParallelism",
+ "Test scan with different parallelism"){
+ INITIALIZER(runLoadTable);
+ STEP(runScanParallelism);
+ FINALIZER(runClearTable);
+}
+NDBT_TESTSUITE_END(testScan);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ return testScan.execute(argc, argv);
+}
+
+template class Vector<Attrib*>;
diff --git a/storage/ndb/test/ndbapi/testScanInterpreter.cpp b/storage/ndb/test/ndbapi/testScanInterpreter.cpp
new file mode 100644
index 00000000000..5a7ca30cd2a
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testScanInterpreter.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 "NDBT_Test.hpp"
+#include "NDBT_ReturnCodes.h"
+#include "HugoTransactions.hpp"
+#include "UtilTransactions.hpp"
+#include "NdbRestarter.hpp"
+#include <Vector.hpp>
+#include "ScanFilter.hpp"
+#include "ScanInterpretTest.hpp"
+
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable2(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runClearResTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ const NdbDictionary::Table* pResTab =
+ GETNDB(step)->getDictionary()->getTable(ctx->getProperty("ResultTabName", "NULL"));
+
+ UtilTransactions utilTrans(*pResTab);
+ if (utilTrans.clearTable2(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runScanRead(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int parallelism = ctx->getProperty("Parallelism", 1);
+
+ int i = 0;
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while (i<loops) {
+ g_info << i << ": ";
+ if (hugoTrans.scanReadRecords(GETNDB(step), records, 0, parallelism) != 0){
+ return NDBT_FAILED;
+ }
+ i++;
+ }
+ return NDBT_OK;
+}
+
+int runScanReadResTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ int parallelism = ctx->getProperty("Parallelism", 1);
+ const NdbDictionary::Table* pResTab =
+ NDBT_Table::discoverTableFromDb(GETNDB(step),
+ ctx->getProperty("ResultTabName", "NULL"));
+
+ HugoTransactions hugoTrans(*pResTab);
+ if (hugoTrans.scanReadRecords(GETNDB(step), records, 0, parallelism) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runCreateResultTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ char newTabName[256];
+ BaseString::snprintf(newTabName, 256, "%s_RES", pTab->getName());
+ ctx->setProperty("ResultTabName", newTabName);
+
+ NdbDictionary::Table resTab(* pTab);
+ resTab.setName(newTabName);
+
+ if (GETNDB(step)->getDictionary()->createTable(resTab) != 0){
+ g_err << newTabName << " creation failed!"<< endl;
+ return NDBT_FAILED;
+ }else{
+ g_info << newTabName << " created!"<< endl;
+ return NDBT_OK;
+ }
+}
+
+int scanWithFilter(NDBT_Context* ctx, NDBT_Step* step, ScanFilter& filt){
+ int records = ctx->getNumRecords();
+ const char* resTabName = ctx->getProperty("ResultTabName", "NULL");
+ if (strcmp(resTabName, "NULL") == 0)
+ return NDBT_FAILED;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ const NdbDictionary::Table* pResTab = NDBT_Table::discoverTableFromDb(GETNDB(step), resTabName);
+ if (pResTab == NULL)
+ return NDBT_FAILED;
+
+ ScanInterpretTest interpretTest(*pTab, *pResTab);
+ if (interpretTest.scanRead(GETNDB(step),
+ records,
+ 16,
+ filt) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+int runScanLessThan(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ LessThanFilter filt(records);
+ return scanWithFilter(ctx, step, filt);
+}
+int runScanEqual(NDBT_Context* ctx, NDBT_Step* step){
+ EqualFilter filt;
+ return scanWithFilter(ctx, step, filt);
+}
+
+int scanVerifyWithFilter(NDBT_Context* ctx, NDBT_Step* step, ScanFilter& filt){
+ int records = ctx->getNumRecords();
+ const char* resTabName = ctx->getProperty("ResultTabName", "NULL");
+ if (strcmp(resTabName, "NULL") == 0)
+ return NDBT_FAILED;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+ const NdbDictionary::Table* pResTab = NDBT_Table::discoverTableFromDb(GETNDB(step), resTabName);
+ if (pResTab == NULL)
+ return NDBT_FAILED;
+
+ ScanInterpretTest interpretTest(*pTab, *pResTab);
+ if (interpretTest.scanReadVerify(GETNDB(step),
+ records,
+ 16,
+ filt) != NDBT_OK){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+int runScanLessThanVerify(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+ LessThanFilter filt(records);
+ return scanVerifyWithFilter(ctx, step, filt);
+}
+int runScanEqualVerify(NDBT_Context* ctx, NDBT_Step* step){
+ EqualFilter filt;
+ return scanVerifyWithFilter(ctx, step, filt);
+}
+
+int runScanEqualLoop(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int l = 0;
+ EqualFilter filt;
+ while(l < loops){
+ if (scanWithFilter(ctx, step, filt) != NDBT_OK)
+ return NDBT_FAILED;
+ if (runClearResTable(ctx, step) != NDBT_OK)
+ return NDBT_FAILED;
+ l++;
+ }
+ return NDBT_OK;
+}
+
+
+int runScanEqualVerifyLoop(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int l = 0;
+ EqualFilter filt;
+ while(l < loops){
+ if (scanWithFilter(ctx, step, filt) != NDBT_OK)
+ return NDBT_FAILED;
+ if (scanVerifyWithFilter(ctx, step, filt) != NDBT_OK)
+ return NDBT_FAILED;
+ if (runClearResTable(ctx, step) != NDBT_OK)
+ return NDBT_FAILED;
+ l++;
+ }
+ return NDBT_OK;
+}
+
+int runScanLessThanLoop(NDBT_Context* ctx, NDBT_Step* step){
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int l = 0;
+ LessThanFilter filt(records);
+ while(l < loops){
+ if (scanWithFilter(ctx, step, filt) != NDBT_OK)
+ return NDBT_FAILED;
+ if (runClearResTable(ctx, step) != NDBT_OK)
+ return NDBT_FAILED;
+ l++;
+ }
+ return NDBT_OK;
+}
+
+NDBT_TESTSUITE(testScanInterpreter);
+TESTCASE("ScanLessThan",
+ "Read all records in table TX with attrX less "\
+ "than a value and store the resultset in TX_RES."\
+ "Then compare records in TX_RES with records in TX."){
+ // TABLE("T1");
+ // TABLE("T2");
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCreateResultTable);
+ STEP(runScanLessThan);
+ VERIFIER(runScanLessThanVerify);
+ FINALIZER(runClearTable);
+ FINALIZER(runClearResTable);
+}
+TESTCASE("ScanEqual",
+ "Read all records in table TX with attrX equal "\
+ "to a value and store the resultset in TX_RES."\
+ "Then compare records in TX_RES with records in TX."){
+ // TABLE("T1");
+ // TABLE("T2");
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCreateResultTable);
+ STEP(runScanEqual);
+ VERIFIER(runScanEqualVerify);
+ FINALIZER(runClearTable);
+ FINALIZER(runClearResTable);
+}
+TESTCASE("ScanEqualLoop",
+ "Scan all records in TX equal to a value."\
+ "Do this loop number of times"){
+ // TABLE("T1");
+ // TABLE("T2");
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCreateResultTable);
+ STEP(runScanEqualLoop);
+ FINALIZER(runClearTable);
+ FINALIZER(runClearResTable);
+}
+TESTCASE("ScanEqualVerifyLoop",
+ "Scan all records in TX equal to a value."\
+ "Verify record in TX_RES table"\
+ "Do this loop number of times"){
+ // TABLE("T1");
+ // TABLE("T2");
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCreateResultTable);
+ STEP(runScanEqualVerifyLoop);
+ FINALIZER(runClearTable);
+ FINALIZER(runClearResTable);
+}
+TESTCASE("ScanLessThanLoop",
+ "Scan all records in TX less than a value."\
+ "Do this loop number of times"){
+ // TABLE("T1");
+ // TABLE("T2");
+ INITIALIZER(runLoadTable);
+ INITIALIZER(runCreateResultTable);
+ STEP(runScanLessThanLoop);
+ FINALIZER(runClearTable);
+ FINALIZER(runClearResTable);
+}
+NDBT_TESTSUITE_END(testScanInterpreter);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ return testScanInterpreter.execute(argc, argv);
+}
+
+
+
diff --git a/storage/ndb/test/ndbapi/testScanPerf.cpp b/storage/ndb/test/ndbapi/testScanPerf.cpp
new file mode 100644
index 00000000000..8ac81297ac3
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testScanPerf.cpp
@@ -0,0 +1,372 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <random.h>
+#include <getarg.h>
+
+struct Parameter {
+ char * name;
+ unsigned value;
+ unsigned min;
+ unsigned max;
+};
+
+#define P_BATCH 0
+#define P_PARRA 1
+#define P_LOCK 2
+#define P_FILT 3
+#define P_BOUND 4
+#define P_ACCESS 5
+#define P_FETCH 6
+#define P_ROWS 7
+#define P_LOOPS 8
+#define P_CREATE 9
+#define P_RESET 11
+#define P_MULTI 12
+
+#define P_MAX 13
+
+static
+Parameter
+g_paramters[] = {
+ { "batch", 0, 0, 1 }, // 0, 15
+ { "parallelism", 0, 0, 1 }, // 0, 1
+ { "lock", 0, 0, 2 }, // read, exclusive, dirty
+ { "filter", 0, 0, 3 }, // all, none, 1, 100
+ { "range", 0, 0, 3 }, // all, none, 1, 100
+ { "access", 0, 0, 2 }, // scan, idx, idx sorted
+ { "fetch", 0, 0, 1 }, // No, yes
+ { "size", 1000000, 1, ~0 },
+ { "iterations", 3, 1, ~0 },
+ { "create_drop", 1, 0, 1 },
+ { "data", 1, 0, 1 },
+ { "q-reset bounds", 0, 1, 0 },
+ { "multi read range", 1000, 1, ~0 }
+};
+
+static Ndb* g_ndb = 0;
+static const NdbDictionary::Table * g_table;
+static const NdbDictionary::Index * g_index;
+static char g_tablename[256];
+static char g_indexname[256];
+
+int create_table();
+int run_scan();
+
+int
+main(int argc, const char** argv){
+ ndb_init();
+ int verbose = 1;
+ int optind = 0;
+
+ struct getargs args[1+P_MAX] = {
+ { "verbose", 'v', arg_flag, &verbose, "Print verbose status", "verbose" }
+ };
+ const int num_args = 1 + P_MAX;
+ int i;
+ for(i = 0; i<P_MAX; i++){
+ args[i+1].long_name = g_paramters[i].name;
+ args[i+1].short_name = * g_paramters[i].name;
+ args[i+1].type = arg_integer;
+ args[i+1].value = &g_paramters[i].value;
+ BaseString tmp;
+ tmp.assfmt("min: %d max: %d", g_paramters[i].min, g_paramters[i].max);
+ args[i+1].help = strdup(tmp.c_str());
+ args[i+1].arg_help = 0;
+ }
+
+ if(getarg(args, num_args, argc, argv, &optind)) {
+ arg_printusage(args, num_args, argv[0], "tabname1 tabname2 ...");
+ return NDBT_WRONGARGS;
+ }
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1))
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ g_ndb = new Ndb(&con, "TEST_DB");
+ if(g_ndb->init() != 0){
+ g_err << "init() failed" << endl;
+ goto error;
+ }
+ if(g_ndb->waitUntilReady() != 0){
+ g_err << "Wait until ready failed" << endl;
+ goto error;
+ }
+ for(i = optind; i<argc; i++){
+ const char * T = argv[i];
+ g_info << "Testing " << T << endl;
+ BaseString::snprintf(g_tablename, sizeof(g_tablename), T);
+ BaseString::snprintf(g_indexname, sizeof(g_indexname), "IDX_%s", T);
+ if(create_table())
+ goto error;
+ if(run_scan())
+ goto error;
+ }
+
+ if(g_ndb) delete g_ndb;
+ return NDBT_OK;
+ error:
+ if(g_ndb) delete g_ndb;
+ return NDBT_FAILED;
+}
+
+int
+create_table(){
+ NdbDictionary::Dictionary* dict = g_ndb->getDictionary();
+ assert(dict);
+ if(g_paramters[P_CREATE].value){
+ g_ndb->getDictionary()->dropTable(g_tablename);
+ const NdbDictionary::Table * pTab = NDBT_Tables::getTable(g_tablename);
+ assert(pTab);
+ NdbDictionary::Table copy = * pTab;
+ copy.setLogging(false);
+ if(dict->createTable(copy) != 0){
+ g_err << "Failed to create table: " << g_tablename << endl;
+ return -1;
+ }
+
+ NdbDictionary::Index x(g_indexname);
+ x.setTable(g_tablename);
+ x.setType(NdbDictionary::Index::OrderedIndex);
+ x.setLogging(false);
+ for (unsigned k = 0; k < copy.getNoOfColumns(); k++){
+ if(copy.getColumn(k)->getPrimaryKey()){
+ x.addColumnName(copy.getColumn(k)->getName());
+ }
+ }
+
+ if(dict->createIndex(x) != 0){
+ g_err << "Failed to create index: " << endl;
+ return -1;
+ }
+ }
+ g_table = dict->getTable(g_tablename);
+ g_index = dict->getIndex(g_indexname, g_tablename);
+ assert(g_table);
+ assert(g_index);
+
+ if(g_paramters[P_CREATE].value)
+ {
+ int rows = g_paramters[P_ROWS].value;
+ HugoTransactions hugoTrans(* g_table);
+ if (hugoTrans.loadTable(g_ndb, rows)){
+ g_err.println("Failed to load %s with %d rows",
+ g_table->getName(), rows);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+inline
+void err(NdbError e){
+ ndbout << e << endl;
+}
+
+int
+run_scan(){
+ int iter = g_paramters[P_LOOPS].value;
+ NDB_TICKS start1, stop;
+ int sum_time= 0;
+
+ int sample_rows = 0;
+ int tot_rows = 0;
+ NDB_TICKS sample_start = NdbTick_CurrentMillisecond();
+
+ Uint32 tot = g_paramters[P_ROWS].value;
+
+ if(g_paramters[P_BOUND].value >= 2 || g_paramters[P_FILT].value == 2)
+ iter *= g_paramters[P_ROWS].value;
+
+ NdbScanOperation * pOp = 0;
+ NdbIndexScanOperation * pIOp = 0;
+ NdbConnection * pTrans = 0;
+ int check = 0;
+
+ for(int i = 0; i<iter; i++){
+ start1 = NdbTick_CurrentMillisecond();
+ pTrans = pTrans ? pTrans : g_ndb->startTransaction();
+ if(!pTrans){
+ g_err << "Failed to start transaction" << endl;
+ err(g_ndb->getNdbError());
+ return -1;
+ }
+
+ int par = g_paramters[P_PARRA].value;
+ int bat = g_paramters[P_BATCH].value;
+ NdbScanOperation::LockMode lm;
+ switch(g_paramters[P_LOCK].value){
+ case 0:
+ lm = NdbScanOperation::LM_CommittedRead;
+ break;
+ case 1:
+ lm = NdbScanOperation::LM_Read;
+ break;
+ case 2:
+ lm = NdbScanOperation::LM_Exclusive;
+ break;
+ default:
+ abort();
+ }
+
+ if(g_paramters[P_ACCESS].value == 0){
+ pOp = pTrans->getNdbScanOperation(g_tablename);
+ assert(pOp);
+ pOp->readTuples(lm, bat, par);
+ } else {
+ if(g_paramters[P_RESET].value == 0 || pIOp == 0)
+ {
+ pOp= pIOp= pTrans->getNdbIndexScanOperation(g_indexname, g_tablename);
+ bool ord = g_paramters[P_ACCESS].value == 2;
+ pIOp->readTuples(lm, bat, par, ord);
+ }
+ else
+ {
+ pIOp->reset_bounds();
+ }
+
+ switch(g_paramters[P_BOUND].value){
+ case 0: // All
+ break;
+ case 1: // None
+ pIOp->setBound((Uint32)0, NdbIndexScanOperation::BoundEQ, 0);
+ break;
+ case 2: { // 1 row
+ default:
+ assert(g_table->getNoOfPrimaryKeys() == 1); // only impl. so far
+ int tot = g_paramters[P_ROWS].value;
+ int row = rand() % tot;
+#if 0
+ fix_eq_bound(pIOp, row);
+#else
+ pIOp->setBound((Uint32)0, NdbIndexScanOperation::BoundEQ, &row);
+#endif
+ if(g_paramters[P_RESET].value == 2)
+ goto execute;
+ break;
+ }
+ case 3: { // read multi
+ int multi = g_paramters[P_MULTI].value;
+ int tot = g_paramters[P_ROWS].value;
+ for(; multi > 0 && i < iter; --multi, i++)
+ {
+ int row = rand() % tot;
+ pIOp->setBound((Uint32)0, NdbIndexScanOperation::BoundEQ, &row);
+ pIOp->end_of_bound(i);
+ }
+ if(g_paramters[P_RESET].value == 2)
+ goto execute;
+ break;
+ }
+ }
+ }
+ assert(pOp);
+
+ switch(g_paramters[P_FILT].value){
+ case 0: // All
+ check = pOp->interpret_exit_ok();
+ break;
+ case 1: // None
+ check = pOp->interpret_exit_nok();
+ break;
+ case 2: { // 1 row
+ default:
+ assert(g_table->getNoOfPrimaryKeys() == 1); // only impl. so far
+ abort();
+#if 0
+ int tot = g_paramters[P_ROWS].value;
+ int row = rand() % tot;
+ NdbScanFilter filter(pOp) ;
+ filter.begin(NdbScanFilter::AND);
+ fix_eq(filter, pOp, row);
+ filter.end();
+ break;
+#endif
+ }
+ }
+ if(check != 0){
+ err(pOp->getNdbError());
+ return -1;
+ }
+ assert(check == 0);
+
+ if(g_paramters[P_RESET].value == 1)
+ g_paramters[P_RESET].value = 2;
+
+ for(int i = 0; i<g_table->getNoOfColumns(); i++){
+ pOp->getValue(i);
+ }
+
+ if(g_paramters[P_RESET].value == 1)
+ g_paramters[P_RESET].value = 2;
+execute:
+ int rows = 0;
+ check = pTrans->execute(NoCommit);
+ assert(check == 0);
+ int fetch = g_paramters[P_FETCH].value;
+ while((check = pOp->nextResult(true)) == 0){
+ do {
+ rows++;
+ } while(!fetch && ((check = pOp->nextResult(false)) == 0));
+ if(check == -1){
+ err(pTrans->getNdbError());
+ return -1;
+ }
+ assert(check == 2);
+ }
+
+ if(check == -1){
+ err(pTrans->getNdbError());
+ return -1;
+ }
+ assert(check == 1);
+ if(g_paramters[P_RESET].value == 0)
+ {
+ pTrans->close();
+ pTrans = 0;
+ }
+ stop = NdbTick_CurrentMillisecond();
+
+ int time_passed= (int)(stop - start1);
+ sample_rows += rows;
+ sum_time+= time_passed;
+ tot_rows+= rows;
+
+ if(sample_rows >= tot)
+ {
+ int sample_time = (int)(stop - sample_start);
+ g_info << "Found " << sample_rows << " rows" << endl;
+ g_err.println("Time: %d ms = %u rows/sec", sample_time,
+ (1000*sample_rows)/sample_time);
+ sample_rows = 0;
+ sample_start = stop;
+ }
+ }
+
+ g_err.println("Avg time: %d ms = %u rows/sec", sum_time/tot_rows,
+ (1000*tot_rows)/sum_time);
+ return 0;
+}
diff --git a/storage/ndb/test/ndbapi/testSystemRestart.cpp b/storage/ndb/test/ndbapi/testSystemRestart.cpp
new file mode 100644
index 00000000000..35016896495
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testSystemRestart.cpp
@@ -0,0 +1,1244 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <Vector.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+#define CHECK(b) if (!(b)) { \
+ g_err << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ continue; }
+
+int runSystemRestart1(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int timeout = 300;
+ Uint32 loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int count;
+ NdbRestarter restarter;
+ Uint32 i = 1;
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while(i<=loops && result != NDBT_FAILED){
+
+ ndbout << "Loop " << i << "/"<< loops <<" started" << endl;
+ /*
+ 1. Load data
+ 2. Restart cluster and verify records
+ 3. Update records
+ 4. Restart cluster and verify records
+ 5. Delete half of the records
+ 6. Restart cluster and verify records
+ 7. Delete all records
+ 8. Restart cluster and verify records
+ 9. Insert, update, delete records
+ 10. Restart cluster and verify records
+ 11. Insert, update, delete records
+ 12. Restart cluster with error insert 5020 and verify records
+ */
+ ndbout << "Loading records..." << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+
+ ndbout << "Restarting cluster" << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(hugoTrans.pkReadRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+
+ ndbout << "Updating records..." << endl;
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(hugoTrans.pkReadRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+
+ ndbout << "Deleting 50% of records..." << endl;
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(hugoTrans.scanReadRecords(pNdb, records/2, 0, 64) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == (records/2));
+
+ ndbout << "Deleting all records..." << endl;
+ CHECK(utilTrans.clearTable(pNdb, records/2) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == 0);
+
+ ndbout << "Doing it all..." << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == 0);
+
+ ndbout << "Doing it all..." << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+
+ ndbout << "Restarting cluster with error insert 5020..." << endl;
+ CHECK(restarter.restartAll(false, true) == 0);
+ CHECK(restarter.waitClusterNoStart(timeout) == 0);
+ CHECK(restarter.insertErrorInAllNodes(5020) == 0);
+ CHECK(restarter.startAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ i++;
+ }
+
+ ndbout << "runSystemRestart1 finished" << endl;
+
+ return result;
+}
+
+int runSystemRestart2(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+/// int timeout = 300;
+ int timeout = 120;
+ Uint32 loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int count;
+ NdbRestarter restarter;
+ Uint32 i = 1;
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while(i<=loops && result != NDBT_FAILED && !ctx->isTestStopped()){
+
+ ndbout << "Loop " << i << "/"<< loops <<" started" << endl;
+ /* Use error 7070 to set time between LCP to it's min value
+ 1. Load data
+ 2. Restart cluster and verify records
+ 3. Update records
+ 4. Restart cluster and verify records
+ 5. Delete half of the records
+ 6. Restart cluster and verify records
+ 7. Delete all records
+ 8. Restart cluster and verify records
+ 9. Insert, update, delete records
+ 10. Restart cluster and verify records
+ */
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+
+ ndbout << "Loading records..." << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+
+ ndbout << "Restarting cluster" << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+ }
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(hugoTrans.pkReadRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+
+ ndbout << "Updating records..." << endl;
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+ }
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(hugoTrans.pkReadRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+
+ ndbout << "Deleting 50% of records..." << endl;
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+ }
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(hugoTrans.scanReadRecords(pNdb, records/2, 0, 64) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == (records/2));
+
+ ndbout << "Deleting all records..." << endl;
+ CHECK(utilTrans.clearTable(pNdb, records/2) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+ }
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == 0);
+
+ ndbout << "Doing it all..." << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+ }
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == 0);
+
+ i++;
+ }
+
+ ndbout << "runSystemRestart2 finished" << endl;
+
+ return result;
+}
+
+int runSystemRestartTestUndoLog(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int timeout = 300;
+ Uint32 loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int count;
+ NdbRestarter restarter;
+ Uint32 i = 1;
+
+ int dump7080[2];
+ dump7080[0] = 7080;
+ dump7080[1] = ctx->getTab()->getTableId();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while(i<=loops && result != NDBT_FAILED){
+
+ ndbout << "Loop " << i << "/"<< loops <<" started" << endl;
+ /*
+ 1. Start LCP, turn on undologging but delay write of datapages.
+ 2. Insert, update, delete records
+ 3. Complete writing of data pages and finish LCP.
+ 4. Restart cluster and verify records
+ */
+ // Use dump state 7080 to delay writing of datapages
+ // for the current table
+ ndbout << "Dump state: "<<dump7080[0]<<", "<<dump7080[1]<<endl;
+ CHECK(restarter.dumpStateAllNodes(dump7080, 2) == 0);
+ NdbSleep_SecSleep(10);
+
+ ndbout << "Doing it all..." << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+ CHECK(hugoTrans.scanUpdateRecords(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+
+ // Reset error and let LCP continue
+ CHECK(restarter.insertErrorInAllNodes(0) == 0);
+ NdbSleep_SecSleep(60);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == 0);
+
+ // Use dump state 7080 to delay writing of datapages
+ // for the current table
+ ndbout << "Dump state: "<<dump7080[0]<<", "<<dump7080[1]<<endl;
+ CHECK(restarter.dumpStateAllNodes(dump7080, 2) == 0);
+ NdbSleep_SecSleep(10);
+
+ ndbout << "Doing it all, delete 50%..." << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+
+ // Reset error and let LCP continue
+ CHECK(restarter.insertErrorInAllNodes(0) == 0);
+ NdbSleep_SecSleep(20);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(hugoTrans.scanReadRecords(pNdb, records/2, 0, 64) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == (records/2));
+ CHECK(utilTrans.clearTable(pNdb, records) == 0);
+
+ i++;
+ }
+
+ ndbout << "runSystemRestartTestUndoLog finished" << endl;
+
+ return result;
+}
+
+int runSystemRestartTestFullDb(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int timeout = 300;
+ Uint32 loops = ctx->getNumLoops();
+ int count1, count2;
+ NdbRestarter restarter;
+ Uint32 i = 1;
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+ while(i<=loops && result != NDBT_FAILED){
+
+ ndbout << "Loop " << i << "/"<< loops <<" started" << endl;
+ /*
+ 1. Load data until db reports it's full
+ 2. Restart cluster and verify records
+ */
+ ndbout << "Filling up table..." << endl;
+ CHECK(hugoTrans.fillTable(pNdb) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count1) == 0);
+ ndbout << "Db is full. Table has "<<count1 <<" records."<< endl;
+
+ ndbout << "Restarting cluster" << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(hugoTrans.scanReadRecords(pNdb, count1) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count2) == 0);
+ CHECK(count1 == count2);
+
+ ndbout << "Deleting all records..." << endl;
+ CHECK(utilTrans.clearTable2(pNdb, count1) == 0);
+
+ ndbout << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count1) == 0);
+ CHECK(count1 == 0);
+
+ i++;
+ }
+
+ ndbout << "runSystemRestartTestFullDb finished" << endl;
+
+ return result;
+}
+
+int runSystemRestart3(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int timeout = 300;
+ Uint32 loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int count;
+ NdbRestarter restarter;
+ Uint32 i = 1;
+
+ const Uint32 nodeCount = restarter.getNumDbNodes();
+ if(nodeCount < 2){
+ g_info << "SR3 - Needs atleast 2 nodes to test" << endl;
+ return NDBT_OK;
+ }
+
+ Vector<int> nodeIds;
+ for(i = 0; i<nodeCount; i++)
+ nodeIds.push_back(restarter.getDbNodeId(i));
+
+ Uint32 currentRestartNodeIndex = 0;
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ while(i<=loops && result != NDBT_FAILED){
+
+ g_info << "Loop " << i << "/"<< loops <<" started" << endl;
+ /**
+ * 1. Load data
+ * 2. Restart 1 node -nostart
+ * 3. Update records
+ * 4. Restart cluster and verify records
+ * 5. Restart 1 node -nostart
+ * 6. Delete half of the records
+ * 7. Restart cluster and verify records
+ * 8. Restart 1 node -nostart
+ * 9. Delete all records
+ * 10. Restart cluster and verify records
+ */
+ g_info << "Loading records..." << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+
+ /*** 1 ***/
+ g_info << "1 - Stopping one node" << endl;
+ CHECK(restarter.restartOneDbNode(nodeIds[currentRestartNodeIndex],
+ false,
+ true,
+ false) == 0);
+ currentRestartNodeIndex = (currentRestartNodeIndex + 1 ) % nodeCount;
+
+ g_info << "Updating records..." << endl;
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+
+ g_info << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ g_info << "Verifying records..." << endl;
+ CHECK(hugoTrans.pkReadRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+
+ g_info << "2 - Stopping one node" << endl;
+ CHECK(restarter.restartOneDbNode(nodeIds[currentRestartNodeIndex],
+ false,
+ true,
+ false) == 0);
+ currentRestartNodeIndex = (currentRestartNodeIndex + 1 ) % nodeCount;
+
+ g_info << "Deleting 50% of records..." << endl;
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+
+ g_info << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ g_info << "Verifying records..." << endl;
+ CHECK(hugoTrans.scanReadRecords(pNdb, records/2, 0, 64) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == (records/2));
+
+ g_info << "3 - Stopping one node" << endl;
+ CHECK(restarter.restartOneDbNode(nodeIds[currentRestartNodeIndex],
+ false,
+ true,
+ false) == 0);
+ currentRestartNodeIndex = (currentRestartNodeIndex + 1 ) % nodeCount;
+ g_info << "Deleting all records..." << endl;
+ CHECK(utilTrans.clearTable(pNdb, records/2) == 0);
+
+ g_info << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == 0);
+
+ i++;
+ }
+
+ g_info << "runSystemRestart3 finished" << endl;
+
+ return result;
+}
+
+int runSystemRestart4(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int timeout = 300;
+ Uint32 loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int count;
+ NdbRestarter restarter;
+ Uint32 i = 1;
+
+ const Uint32 nodeCount = restarter.getNumDbNodes();
+ if(nodeCount < 2){
+ g_info << "SR4 - Needs atleast 2 nodes to test" << endl;
+ return NDBT_OK;
+ }
+
+ Vector<int> nodeIds;
+ for(i = 0; i<nodeCount; i++)
+ nodeIds.push_back(restarter.getDbNodeId(i));
+
+ Uint32 currentRestartNodeIndex = 0;
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ if(restarter.dumpStateAllNodes(&val, 1) != 0){
+ g_err << "ERR: "<< step->getName()
+ << " failed on line " << __LINE__ << endl;
+ return NDBT_FAILED;
+ }
+ }
+
+ while(i<=loops && result != NDBT_FAILED){
+
+ g_info << "Loop " << i << "/"<< loops <<" started" << endl;
+ /**
+ * 1. Load data
+ * 2. Restart 1 node -nostart
+ * 3. Update records
+ * 4. Restart cluster and verify records
+ * 5. Restart 1 node -nostart
+ * 6. Delete half of the records
+ * 7. Restart cluster and verify records
+ * 8. Restart 1 node -nostart
+ * 9. Delete all records
+ * 10. Restart cluster and verify records
+ */
+ g_info << "Loading records..." << endl;
+ CHECK(hugoTrans.loadTable(pNdb, records) == 0);
+
+ /*** 1 ***/
+ g_info << "1 - Stopping one node" << endl;
+ CHECK(restarter.restartOneDbNode(nodeIds[currentRestartNodeIndex],
+ false,
+ true,
+ false) == 0);
+ currentRestartNodeIndex = (currentRestartNodeIndex + 1 ) % nodeCount;
+
+ g_info << "Updating records..." << endl;
+ CHECK(hugoTrans.pkUpdateRecords(pNdb, records) == 0);
+
+ g_info << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+ }
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ g_info << "Verifying records..." << endl;
+ CHECK(hugoTrans.pkReadRecords(pNdb, records) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+
+ g_info << "2 - Stopping one node" << endl;
+ CHECK(restarter.restartOneDbNode(nodeIds[currentRestartNodeIndex],
+ false,
+ true,
+ false) == 0);
+ currentRestartNodeIndex = (currentRestartNodeIndex + 1 ) % nodeCount;
+
+ g_info << "Deleting 50% of records..." << endl;
+ CHECK(hugoTrans.pkDelRecords(pNdb, records/2) == 0);
+
+ g_info << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+ }
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ g_info << "Verifying records..." << endl;
+ CHECK(hugoTrans.scanReadRecords(pNdb, records/2, 0, 64) == 0);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == (records/2));
+
+ g_info << "3 - Stopping one node" << endl;
+ CHECK(restarter.restartOneDbNode(nodeIds[currentRestartNodeIndex],
+ false,
+ true,
+ false) == 0);
+ currentRestartNodeIndex = (currentRestartNodeIndex + 1 ) % nodeCount;
+ g_info << "Deleting all records..." << endl;
+ CHECK(utilTrans.clearTable(pNdb, records/2) == 0);
+
+ g_info << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+ }
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == 0);
+
+ i++;
+ }
+
+ g_info << "runSystemRestart4 finished" << endl;
+
+ return result;
+}
+
+int runSystemRestart5(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int timeout = 300;
+ Uint32 loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int count;
+ NdbRestarter restarter;
+ Uint32 i = 1;
+
+ const Uint32 nodeCount = restarter.getNumDbNodes();
+ if(nodeCount < 2){
+ g_info << "SR5 - Needs atleast 2 nodes to test" << endl;
+ return NDBT_OK;
+ }
+
+ Vector<int> nodeIds;
+ for(i = 0; i<nodeCount; i++)
+ nodeIds.push_back(restarter.getDbNodeId(i));
+
+ Uint32 currentRestartNodeIndex = 0;
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ if(restarter.dumpStateAllNodes(&val, 1) != 0){
+ g_err << "ERR: "<< step->getName()
+ << " failed on line " << __LINE__ << endl;
+ return NDBT_FAILED;
+ }
+ }
+
+ while(i<=loops && result != NDBT_FAILED){
+
+ g_info << "Loop " << i << "/"<< loops <<" started" << endl;
+ /**
+ * 1. Load data
+ * 2. Restart 1 node -nostart
+ * 3. Update records
+ * 4. Restart cluster and verify records
+ * 5. Restart 1 node -nostart
+ * 6. Delete half of the records
+ * 7. Restart cluster and verify records
+ * 8. Restart 1 node -nostart
+ * 9. Delete all records
+ * 10. Restart cluster and verify records
+ */
+ g_info << "Loading records..." << endl;
+ hugoTrans.loadTable(pNdb, records);
+
+ /*** 1 ***/
+ g_info << "1 - Stopping one node" << endl;
+ CHECK(restarter.restartOneDbNode(nodeIds[currentRestartNodeIndex],
+ false,
+ true,
+ false) == 0);
+ currentRestartNodeIndex = (currentRestartNodeIndex + 1 ) % nodeCount;
+
+ g_info << "Updating records..." << endl;
+ hugoTrans.pkUpdateRecords(pNdb, records);
+
+ g_info << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll(false, false, true) == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+ }
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ g_info << "Verifying records..." << endl;
+ hugoTrans.pkReadRecords(pNdb, records);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ //CHECK(count == records);
+
+ g_info << "2 - Stopping one node" << endl;
+ CHECK(restarter.restartOneDbNode(nodeIds[currentRestartNodeIndex],
+ false,
+ true,
+ false) == 0);
+ currentRestartNodeIndex = (currentRestartNodeIndex + 1 ) % nodeCount;
+
+ g_info << "Deleting 50% of records..." << endl;
+ hugoTrans.pkDelRecords(pNdb, records/2);
+
+ g_info << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll(false, false, true) == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+ }
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ g_info << "Verifying records..." << endl;
+ hugoTrans.scanReadRecords(pNdb, records/2, 0, 64);
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ //CHECK(count == (records/2));
+
+ g_info << "3 - Stopping one node" << endl;
+ CHECK(restarter.restartOneDbNode(nodeIds[currentRestartNodeIndex],
+ false,
+ true,
+ false) == 0);
+ currentRestartNodeIndex = (currentRestartNodeIndex + 1 ) % nodeCount;
+ g_info << "Deleting all records..." << endl;
+ utilTrans.clearTable(pNdb, records/2);
+
+ g_info << "Restarting cluster..." << endl;
+ CHECK(restarter.restartAll(false, false, true) == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(restarter.dumpStateAllNodes(&val, 1) == 0);
+ }
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+
+ ndbout << "Verifying records..." << endl;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ //CHECK(count == 0);
+
+ CHECK(utilTrans.clearTable(pNdb) == 0);
+ i++;
+ }
+
+ g_info << "runSystemRestart5 finished" << endl;
+
+ return result;
+}
+
+int runSystemRestart6(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int timeout = 300;
+ Uint32 loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ NdbRestarter restarter;
+ Uint32 i = 1;
+
+ const Uint32 nodeCount = restarter.getNumDbNodes();
+ if(nodeCount < 2){
+ g_info << "SR6 - Needs atleast 2 nodes to test" << endl;
+ return NDBT_OK;
+ }
+
+ Vector<int> nodeIds;
+ for(i = 0; i<nodeCount; i++)
+ nodeIds.push_back(restarter.getDbNodeId(i));
+
+ Uint32 currentRestartNodeIndex = 0;
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ while(i<=loops && result != NDBT_FAILED){
+
+ g_info << "Loop " << i << "/"<< loops <<" started" << endl;
+ /**
+ * 1. Load data
+ * 2. Restart all node -nostart
+ * 3. Restart some nodes -i -nostart
+ * 4. Start all nodes verify records
+ */
+ g_info << "Loading records..." << endl;
+ hugoTrans.loadTable(pNdb, records);
+
+ CHECK(restarter.restartAll(false, true, false) == 0);
+
+ Uint32 nodeId = nodeIds[currentRestartNodeIndex];
+ currentRestartNodeIndex = (currentRestartNodeIndex + 1 ) % nodeCount;
+
+ CHECK(restarter.restartOneDbNode(nodeId, true, true,false) == 0);
+ CHECK(restarter.waitClusterNoStart(timeout) == 0);
+ CHECK(restarter.startAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+ int count = records - 1;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+ CHECK(utilTrans.clearTable(pNdb) == 0);
+ i++;
+ }
+
+ g_info << "runSystemRestart6 finished" << endl;
+
+ return result;
+}
+
+int runSystemRestart7(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ Uint32 loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ NdbRestarter restarter;
+ Uint32 i = 1;
+
+ const Uint32 nodeCount = restarter.getNumDbNodes();
+ if(nodeCount < 2){
+ g_info << "SR7 - Needs atleast 2 nodes to test" << endl;
+ return NDBT_OK;
+ }
+
+ Vector<int> nodeIds;
+ for(i = 0; i<nodeCount; i++)
+ nodeIds.push_back(restarter.getDbNodeId(i));
+
+ int a_nodeIds[64];
+ if(nodeCount > 64)
+ abort();
+
+ Uint32 currentRestartNodeIndex = 1;
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ while(i<=loops && result != NDBT_FAILED){
+
+ g_info << "Loop " << i << "/"<< loops <<" started" << endl;
+ /**
+ * 1. Load data
+ * 2. Restart all node -nostart
+ * 3. Start all but one node
+ * 4. Wait for startphase >= 2
+ * 5. Start last node
+ * 6. Verify records
+ */
+ g_info << "Loading records..." << endl;
+ hugoTrans.loadTable(pNdb, records);
+
+ CHECK(restarter.restartAll(false, true, false) == 0);
+
+ int nodeId = nodeIds[currentRestartNodeIndex];
+ currentRestartNodeIndex = (currentRestartNodeIndex + 1 ) % nodeCount;
+
+ Uint32 j = 0;
+ for(Uint32 k = 0; k<nodeCount; k++){
+ if(nodeIds[k] != nodeId){
+ a_nodeIds[j++] = nodeIds[k];
+ }
+ }
+
+ CHECK(restarter.startNodes(a_nodeIds, nodeCount - 1) == 0);
+ CHECK(restarter.waitNodesStarted(a_nodeIds, nodeCount - 1, 120) == 0);
+ CHECK(pNdb->waitUntilReady(5) == 0);
+ int count = records - 1;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+
+ CHECK(restarter.startNodes(&nodeId, 1) == 0);
+ CHECK(restarter.waitNodesStarted(&nodeId, 1, 120) == 0);
+
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+ CHECK(utilTrans.clearTable(pNdb) == 0);
+
+ i++;
+ }
+
+ g_info << "runSystemRestart7 finished" << endl;
+
+ return result;
+}
+
+int runSystemRestart8(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int timeout = 300;
+ Uint32 loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ NdbRestarter restarter;
+ Uint32 i = 1;
+
+ const Uint32 nodeCount = restarter.getNumDbNodes();
+ if(nodeCount < 2){
+ g_info << "SR8 - Needs atleast 2 nodes to test" << endl;
+ return NDBT_OK;
+ }
+
+ Vector<int> nodeIds;
+ for(i = 0; i<nodeCount; i++)
+ nodeIds.push_back(restarter.getDbNodeId(i));
+
+ int a_nodeIds[64];
+ if(nodeCount > 64)
+ abort();
+
+ Uint32 currentRestartNodeIndex = 1;
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ while(i<=loops && result != NDBT_FAILED){
+
+ g_info << "Loop " << i << "/"<< loops <<" started" << endl;
+ /**
+ * 1. Load data
+ * 2. Restart all node -nostart
+ * 3. Start all but one node
+ * 4. Verify records
+ * 5. Start last node
+ * 6. Verify records
+ */
+ g_info << "Loading records..." << endl;
+ hugoTrans.loadTable(pNdb, records);
+
+ CHECK(restarter.restartAll(false, true, false) == 0);
+
+ int nodeId = nodeIds[currentRestartNodeIndex];
+ currentRestartNodeIndex = (currentRestartNodeIndex + 1 ) % nodeCount;
+
+ Uint32 j = 0;
+ for(Uint32 k = 0; k<nodeCount; k++){
+ if(nodeIds[k] != nodeId){
+ a_nodeIds[j++] = nodeIds[k];
+ }
+ }
+
+ CHECK(restarter.startNodes(a_nodeIds, nodeCount-1) == 0);
+ CHECK(restarter.waitNodesStartPhase(a_nodeIds, nodeCount-1, 3, 120) == 0);
+ CHECK(restarter.startNodes(&nodeId, 1) == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+
+ int count = records - 1;
+ CHECK(utilTrans.selectCount(pNdb, 64, &count) == 0);
+ CHECK(count == records);
+ CHECK(utilTrans.clearTable(pNdb) == 0);
+ i++;
+ }
+
+ g_info << "runSystemRestart8 finished" << endl;
+
+ return result;
+}
+
+int runSystemRestart9(NDBT_Context* ctx, NDBT_Step* step){
+ Ndb* pNdb = GETNDB(step);
+ int result = NDBT_OK;
+ int timeout = 300;
+ Uint32 loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ NdbRestarter restarter;
+ Uint32 i = 1;
+
+ Uint32 currentRestartNodeIndex = 1;
+ UtilTransactions utilTrans(*ctx->getTab());
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ int args[] = { DumpStateOrd::DihMaxTimeBetweenLCP };
+ int dump[] = { DumpStateOrd::DihStartLcpImmediately };
+
+ do {
+ CHECK(restarter.dumpStateAllNodes(args, 1) == 0);
+
+ HugoOperations ops(* ctx->getTab());
+ CHECK(ops.startTransaction(pNdb) == 0);
+ for(i = 0; i<10; i++){
+ CHECK(ops.pkInsertRecord(pNdb, i, 1, 1) == 0);
+ CHECK(ops.execute_NoCommit(pNdb) == 0);
+ }
+ for(i = 0; i<10; i++){
+ CHECK(ops.pkUpdateRecord(pNdb, i, 1) == 0);
+ CHECK(ops.execute_NoCommit(pNdb) == 0);
+ }
+ NdbSleep_SecSleep(10);
+ CHECK(restarter.dumpStateAllNodes(dump, 1) == 0);
+ NdbSleep_SecSleep(10);
+ CHECK(ops.execute_Commit(pNdb) == 0);
+
+ CHECK(restarter.restartAll() == 0);
+ CHECK(restarter.waitClusterStarted(timeout) == 0);
+ CHECK(pNdb->waitUntilReady(timeout) == 0);
+ ops.closeTransaction(pNdb);
+ } while(0);
+
+ g_info << "runSystemRestart9 finished" << endl;
+
+ return result;
+}
+
+int runWaitStarted(NDBT_Context* ctx, NDBT_Step* step){
+
+ NdbRestarter restarter;
+ restarter.waitClusterStarted(300);
+
+ NdbSleep_SecSleep(3);
+ return NDBT_OK;
+}
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ Ndb* pNdb = GETNDB(step);
+ if(pNdb->waitUntilReady(5) != 0){
+ return NDBT_FAILED;
+ }
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable2(pNdb, records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+
+NDBT_TESTSUITE(testSystemRestart);
+TESTCASE("SR1",
+ "Basic system restart test. Focus on testing restart from REDO log.\n"
+ "NOTE! Time between lcp's and gcp's should be left at default, \n"
+ "so that Ndb uses the Redo log when restarting\n"
+ "1. Load records\n"
+ "2. Restart cluster and verify records \n"
+ "3. Update records\n"
+ "4. Restart cluster and verify records \n"
+ "5. Delete half of the records \n"
+ "6. Restart cluster and verify records \n"
+ "7. Delete all records \n"
+ "8. Restart cluster and verify records \n"
+ "9. Insert, update, delete records \n"
+ "10. Restart cluster and verify records\n"
+ "11. Insert, update, delete records \n"
+ "12. Restart cluster with error insert 5020 and verify records\n"){
+ INITIALIZER(runWaitStarted);
+ STEP(runSystemRestart1);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR2",
+ "Basic system restart test. Focus on testing restart from LCP\n"
+ "NOTE! Time between lcp's is automatically set to it's min value\n"
+ "so that Ndb uses LCP's when restarting.\n"
+ "1. Load records\n"
+ "2. Restart cluster and verify records \n"
+ "3. Update records\n"
+ "4. Restart cluster and verify records \n"
+ "5. Delete half of the records \n"
+ "6. Restart cluster and verify records \n"
+ "7. Delete all records \n"
+ "8. Restart cluster and verify records \n"
+ "9. Insert, update, delete records \n"
+ "10. Restart cluster and verify records\n"){
+ INITIALIZER(runWaitStarted);
+ STEP(runSystemRestart2);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR_UNDO",
+ "System restart test. Focus on testing of undologging\n"
+ "in DBACC and DBTUP.\n"
+ "This is done by starting a LCP, turn on undologging \n"
+ "but don't start writing the datapages. This will force all\n"
+ "operations to be written into the undolog.\n"
+ "Then write datapages and complete LCP.\n"
+ "Restart the system\n"){
+ INITIALIZER(runWaitStarted);
+ STEP(runSystemRestartTestUndoLog);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR_FULLDB",
+ "System restart test. Test to restart when DB is full.\n"){
+ INITIALIZER(runWaitStarted);
+ STEP(runSystemRestartTestFullDb);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR3",
+ "System restart test. Focus on testing restart from with\n"
+ "not all nodes alive when system went down\n"
+ "* 1. Load data\n"
+ "* 2. Restart 1 node -nostart\n"
+ "* 3. Update records\n"
+ "* 4. Restart cluster and verify records\n"
+ "* 5. Restart 1 node -nostart\n"
+ "* 6. Delete half of the records\n"
+ "* 7. Restart cluster and verify records\n"
+ "* 8. Restart 1 node -nostart\n"
+ "* 9. Delete all records\n"
+ "* 10. Restart cluster and verify records\n"){
+ INITIALIZER(runWaitStarted);
+ STEP(runSystemRestart3);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR4",
+ "System restart test. Focus on testing restart from with\n"
+ "not all nodes alive when system went down but running LCP at\n"
+ "high speed so that sometimes a TO is required to start cluster\n"
+ "* 1. Load data\n"
+ "* 2. Restart 1 node -nostart\n"
+ "* 3. Update records\n"
+ "* 4. Restart cluster and verify records\n"
+ "* 5. Restart 1 node -nostart\n"
+ "* 6. Delete half of the records\n"
+ "* 7. Restart cluster and verify records\n"
+ "* 8. Restart 1 node -nostart\n"
+ "* 9. Delete all records\n"
+ "* 10. Restart cluster and verify records\n"){
+ INITIALIZER(runWaitStarted);
+ STEP(runSystemRestart4);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR5",
+ "As SR4 but making restart aborts\n"
+ "* 1. Load data\n"
+ "* 2. Restart 1 node -nostart\n"
+ "* 3. Update records\n"
+ "* 4. Restart cluster and verify records\n"
+ "* 5. Restart 1 node -nostart\n"
+ "* 6. Delete half of the records\n"
+ "* 7. Restart cluster and verify records\n"
+ "* 8. Restart 1 node -nostart\n"
+ "* 9. Delete all records\n"
+ "* 10. Restart cluster and verify records\n"){
+ INITIALIZER(runWaitStarted);
+ STEP(runSystemRestart5);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR6",
+ "Perform system restart with some nodes having FS others wo/\n"
+ "* 1. Load data\n"
+ "* 2. Restart all node -nostart\n"
+ "* 3. Restart some nodes -i -nostart\n"
+ "* 4. Start all nodes verify records\n"){
+ INITIALIZER(runWaitStarted);
+ INITIALIZER(runClearTable);
+ STEP(runSystemRestart6);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR7",
+ "Perform partition win system restart\n"
+ "* 1. Load data\n"
+ "* 2. Restart all node -nostart\n"
+ "* 3. Start all but one node\n"
+ "* 4. Verify records\n"
+ "* 5. Start last node\n"
+ "* 6. Verify records\n"){
+ INITIALIZER(runWaitStarted);
+ INITIALIZER(runClearTable);
+ STEP(runSystemRestart7);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR8",
+ "Perform partition win system restart with other nodes delayed\n"
+ "* 1. Load data\n"
+ "* 2. Restart all node -nostart\n"
+ "* 3. Start all but one node\n"
+ "* 4. Wait for startphase >= 2\n"
+ "* 5. Start last node\n"
+ "* 6. Verify records\n"){
+ INITIALIZER(runWaitStarted);
+ INITIALIZER(runClearTable);
+ STEP(runSystemRestart8);
+ FINALIZER(runClearTable);
+}
+TESTCASE("SR9",
+ "Perform partition win system restart with other nodes delayed\n"
+ "* 1. Start transaction\n"
+ "* 2. insert (1,1)\n"
+ "* 3. update (1,2)\n"
+ "* 4. start lcp\n"
+ "* 5. commit\n"
+ "* 6. restart\n"){
+ INITIALIZER(runWaitStarted);
+ INITIALIZER(runClearTable);
+ STEP(runSystemRestart9);
+ FINALIZER(runClearTable);
+}
+NDBT_TESTSUITE_END(testSystemRestart);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ return testSystemRestart.execute(argc, argv);
+}
+
+
diff --git a/storage/ndb/test/ndbapi/testTimeout.cpp b/storage/ndb/test/ndbapi/testTimeout.cpp
new file mode 100644
index 00000000000..ac4f257f12c
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testTimeout.cpp
@@ -0,0 +1,353 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Test.hpp>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <random.h>
+#include <NdbConfig.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+
+#define TIMEOUT 3000
+
+Uint32 g_org_timeout = 3000;
+
+int
+setTransactionTimeout(NDBT_Context* ctx, NDBT_Step* step){
+ NdbRestarter restarter;
+
+ NdbConfig conf(GETNDB(step)->getNodeId()+1);
+ unsigned int nodeId = conf.getMasterNodeId();
+ if (!conf.getProperty(nodeId,
+ NODE_TYPE_DB,
+ CFG_DB_TRANSACTION_INACTIVE_TIMEOUT,
+ &g_org_timeout)){
+ return NDBT_FAILED;
+ }
+
+ int val[] = { DumpStateOrd::TcSetApplTransactionTimeout, TIMEOUT };
+ if(restarter.dumpStateAllNodes(val, 2) != 0){
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int
+resetTransactionTimeout(NDBT_Context* ctx, NDBT_Step* step){
+ NdbRestarter restarter;
+
+ int val[] = { DumpStateOrd::TcSetApplTransactionTimeout, g_org_timeout };
+ if(restarter.dumpStateAllNodes(val, 2) != 0){
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+
+int runLoadTable(NDBT_Context* ctx, NDBT_Step* step){
+
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable2(GETNDB(step), records) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+
+#define CHECK(b) if (!(b)) { \
+ ndbout << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl; \
+ result = NDBT_FAILED; \
+ break; }
+
+int runTimeoutTrans2(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int stepNo = step->getStepNo();
+ int mul1 = ctx->getProperty("Op1", (Uint32)0);
+ int mul2 = ctx->getProperty("Op2", (Uint32)0);
+ int records = ctx->getNumRecords();
+
+ int minSleep = (int)(TIMEOUT * 1.5);
+ int maxSleep = TIMEOUT * 2;
+
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ for (int l = 0; l<loops && !ctx->isTestStopped() && result == NDBT_OK; l++){
+
+ int op1 = 0 + (l + stepNo) * mul1;
+ int op2 = 0 + (l + stepNo) * mul2;
+
+ op1 = (op1 % 5);
+ op2 = (op2 % 5);
+
+ ndbout << stepNo << ": TransactionInactiveTimeout="<< TIMEOUT
+ << ", minSleep="<<minSleep
+ << ", maxSleep="<<maxSleep
+ << ", op1=" << op1
+ << ", op2=" << op2 << endl;;
+
+ do{
+ // Commit transaction
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+
+ switch(op1){
+ case 0:
+ break;
+ case 1:
+ if(hugoOps.pkReadRecord(pNdb, stepNo) != 0){
+ g_err << stepNo << ": Fail" << __LINE__ << endl;
+ result = NDBT_FAILED; break;
+ }
+ break;
+ case 2:
+ if(hugoOps.pkUpdateRecord(pNdb, stepNo) != 0){
+ g_err << stepNo << ": Fail" << __LINE__ << endl;
+ result = NDBT_FAILED; break;
+ }
+ break;
+ case 3:
+ if(hugoOps.pkDeleteRecord(pNdb, stepNo) != 0){
+ g_err << stepNo << ": Fail" << __LINE__ << endl;
+ result = NDBT_FAILED; break;
+ }
+ break;
+ case 4:
+ if(hugoOps.pkInsertRecord(pNdb, stepNo+records+l) != 0){
+ g_err << stepNo << ": Fail" << __LINE__ << endl;
+ result = NDBT_FAILED; break;
+ }
+ break;
+ }
+
+ if(result != NDBT_OK)
+ break;
+
+ int res = hugoOps.execute_NoCommit(pNdb);
+ if(res != 0){
+ g_err << stepNo << ": Fail" << __LINE__ << endl;
+ result = NDBT_FAILED; break;
+ }
+
+ int sleep = minSleep + myRandom48(maxSleep-minSleep);
+ ndbout << stepNo << ": Sleeping for "<< sleep << " milliseconds" << endl;
+ NdbSleep_MilliSleep(sleep);
+
+ switch(op2){
+ case 0:
+ break;
+ case 1:
+ if(hugoOps.pkReadRecord(pNdb, stepNo) != 0){
+ g_err << stepNo << ": Fail" << __LINE__ << endl;
+ result = NDBT_FAILED; break;
+ }
+ break;
+ case 2:
+ if(hugoOps.pkUpdateRecord(pNdb, stepNo) != 0){
+ g_err << stepNo << ": Fail" << __LINE__ << endl;
+ result = NDBT_FAILED; break;
+ }
+ break;
+ case 3:
+ if(hugoOps.pkDeleteRecord(pNdb, stepNo) != 0){
+ g_err << stepNo << ": Fail" << __LINE__ << endl;
+ result = NDBT_FAILED; break;
+ }
+ break;
+ case 4:
+ if(hugoOps.pkInsertRecord(pNdb, stepNo+2*records+l) != 0){
+ g_err << stepNo << ": Fail" << __LINE__ << endl;
+ result = NDBT_FAILED; break;
+ }
+ break;
+ }
+
+ // Expect that transaction has timed-out
+ res = hugoOps.execute_Commit(pNdb);
+ if(op1 != 0 && res != 266){
+ g_err << stepNo << ": Fail: " << res << "!= 237, op1="
+ << op1 << ", op2=" << op2 << endl;
+ result = NDBT_FAILED; break;
+ }
+
+ } while(false);
+
+ hugoOps.closeTransaction(pNdb);
+ }
+
+ return result;
+}
+
+int runDontTimeoutTrans(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int stepNo = step->getStepNo();
+
+ int maxSleep = (int)(TIMEOUT * 0.5);
+ ndbout << "TransactionInactiveTimeout="<< TIMEOUT
+ << ", maxSleep="<<maxSleep<<endl;
+
+
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ for (int l = 0; l < loops && result == NDBT_OK; l++){
+
+ do{
+ // Commit transaction
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ CHECK(hugoOps.pkReadRecord(pNdb, stepNo) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ int sleep = myRandom48(maxSleep);
+ ndbout << "Sleeping for " << sleep << " milliseconds" << endl;
+ NdbSleep_MilliSleep(sleep);
+
+ // Expect that transaction has NOT timed-out
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+
+ } while(false);
+
+ hugoOps.closeTransaction(pNdb);
+ }
+
+ return result;
+}
+
+int runBuddyTransNoTimeout(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ int stepNo = step->getStepNo();
+ int maxSleep = (int)(TIMEOUT * 0.3);
+ ndbout << "TransactionInactiveTimeout="<< TIMEOUT
+ << ", maxSleep="<<maxSleep<<endl;
+
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ for (int l = 1; l < loops && result == NDBT_OK; l++){
+
+ do{
+ // Start an insert trans
+ CHECK(hugoOps.startTransaction(pNdb) == 0);
+ int recordNo = records + (stepNo*loops) + l;
+ CHECK(hugoOps.pkInsertRecord(pNdb, recordNo) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ for (int i = 0; i < 3; i++){
+ // Perform buddy scan reads
+ CHECK((hugoOps.scanReadRecords(pNdb)) == 0);
+ CHECK(hugoOps.execute_NoCommit(pNdb) == 0);
+
+ int sleep = myRandom48(maxSleep);
+ ndbout << "Sleeping for " << sleep << " milliseconds" << endl;
+ NdbSleep_MilliSleep(sleep);
+ }
+
+ // Expect that transaction has NOT timed-out
+ CHECK(hugoOps.execute_Commit(pNdb) == 0);
+
+ } while(false);
+
+ hugoOps.closeTransaction(pNdb);
+ }
+
+ return result;
+}
+
+NDBT_TESTSUITE(testTimeout);
+TESTCASE("DontTimeoutTransaction",
+ "Test that the transaction does not timeout "\
+ "if we sleep during the transaction. Use a sleep "\
+ "value which is smaller than TransactionInactiveTimeout"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(setTransactionTimeout);
+ STEPS(runDontTimeoutTrans, 1);
+ FINALIZER(resetTransactionTimeout);
+ FINALIZER(runClearTable);
+}
+TESTCASE("DontTimeoutTransaction5",
+ "Test that the transaction does not timeout "\
+ "if we sleep during the transaction. Use a sleep "\
+ "value which is smaller than TransactionInactiveTimeout" \
+ "Five simultaneous threads"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(setTransactionTimeout);
+ STEPS(runDontTimeoutTrans, 5);
+ FINALIZER(resetTransactionTimeout);
+ FINALIZER(runClearTable);
+}
+TESTCASE("TimeoutRandTransaction",
+ "Test that the transaction does timeout "\
+ "if we sleep during the transaction. Use a sleep "\
+ "value which is larger than TransactionInactiveTimeout"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(setTransactionTimeout);
+ TC_PROPERTY("Op1", 7);
+ TC_PROPERTY("Op2", 11);
+ STEPS(runTimeoutTrans2, 5);
+ FINALIZER(resetTransactionTimeout);
+ FINALIZER(runClearTable);
+}
+TESTCASE("BuddyTransNoTimeout",
+ "Start a transaction and perform an insert with NoCommit. " \
+ "Start a buddy transaction wich performs long running scans " \
+ "and sleeps. " \
+ "The total sleep time is longer than TransactionInactiveTimeout" \
+ "Commit the first transaction, it should not have timed out."){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(setTransactionTimeout);
+ STEPS(runBuddyTransNoTimeout, 1);
+ FINALIZER(resetTransactionTimeout);
+ FINALIZER(runClearTable);
+}
+TESTCASE("BuddyTransNoTimeout5",
+ "Start a transaction and perform an insert with NoCommit. " \
+ "Start a buddy transaction wich performs long running scans " \
+ "and sleeps. " \
+ "The total sleep time is longer than TransactionInactiveTimeout" \
+ "Commit the first transaction, it should not have timed out." \
+ "Five simultaneous threads"){
+ INITIALIZER(runLoadTable);
+ INITIALIZER(setTransactionTimeout);
+ STEPS(runBuddyTransNoTimeout, 5);
+ FINALIZER(resetTransactionTimeout);
+ FINALIZER(runClearTable);
+}
+NDBT_TESTSUITE_END(testTimeout);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ return testTimeout.execute(argc, argv);
+}
+
diff --git a/storage/ndb/test/ndbapi/testTransactions.cpp b/storage/ndb/test/ndbapi/testTransactions.cpp
new file mode 100644
index 00000000000..46be808d8a5
--- /dev/null
+++ b/storage/ndb/test/ndbapi/testTransactions.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 <NDBT_Test.hpp>
+#include <NDBT_ReturnCodes.h>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+
+struct OperationTestCase {
+ const char * name;
+ bool preCond; // start transaction | insert | commit
+
+ // start transaction 1
+ const char * op1;
+ const int val1;
+
+ // no commit
+
+ // start transaction 2
+ const char * op2;
+ const int res2;
+ const int val2;
+ // no commit
+
+ // commit transaction 1
+ // commit transaction 2
+
+ // start transaction
+ // op3 = READ
+ const int res3;
+ const int val3;
+ // commit transaction
+};
+
+#define X -1
+
+OperationTestCase matrix[] = {
+ { "ReadRead", true, "READ", 1, "READ", 0, 1, 0, 1 },
+ { "ReadReadEx", true, "READ", 1, "READ-EX", 266, X, 0, 1 },
+ { "ReadSimpleRead", true, "READ", 1, "S-READ", 0, 1, 0, 1 },
+ { "ReadDirtyRead", true, "READ", 1, "D-READ", 0, 1, 0, 1 },
+ { "ReadInsert", true, "READ", 1, "INSERT", 266, X, 0, 1 },
+ { "ReadUpdate", true, "READ", 1, "UPDATE", 266, X, 0, 1 },
+ { "ReadDelete", true, "READ", 1, "DELETE", 266, X, 0, 1 },
+ { "ReadScan", true, "READ", 1, "SCAN", 0, 1, 0, 1 },
+ { "ReadScanHl", true, "READ", 1, "SCAN-HL", 0, 1, 0, 1 },
+ { "ReadScanEx", true, "READ", 1, "SCAN-EX", 274, X, 0, 1 },
+#if 0
+ { "ReadScanUp", true, "READ", 1, "SCAN-UP", 266, X, 0, 1 },
+ { "ReadScanDe", true, "READ", 1, "SCAN-DE", 266, X, 0, 1 },
+#endif
+
+ { "ScanRead", true, "SCAN", 1, "READ", 0, 1, 0, 1 },
+ { "ScanReadEx", true, "SCAN", 1, "READ-EX", 0, 1, 0, 1 },
+ { "ScanSimpleRead", true, "SCAN", 1, "S-READ", 0, 1, 0, 1 },
+ { "ScanDirtyRead", true, "SCAN", 1, "D-READ", 0, 1, 0, 1 },
+ { "ScanInsert", true, "SCAN", 1, "INSERT", 630, X, 0, 1 },
+ { "ScanUpdate", true, "SCAN", 1, "UPDATE", 0, 2, 0, 2 },
+ { "ScanDelete", true, "SCAN", 1, "DELETE", 0, X, 626, X },
+ { "ScanScan", true, "SCAN", 1, "SCAN", 0, 1, 0, 1 },
+ { "ScanScanHl", true, "SCAN", 1, "SCAN-HL", 0, 1, 0, 1 },
+ { "ScanScanEx", true, "SCAN", 1, "SCAN-EX", 0, 1, 0, 1 },
+#if 0
+ { "ScanScanUp", true, "SCAN", 1, "SCAN-UP", 266, X, 0, 1 },
+ { "ScanScanDe", true, "SCAN", 1, "SCAN-DE", 266, X, 0, 1 },
+#endif
+
+ { "ScanHlRead", true, "SCAN-HL",1, "READ", 0, 1, 0, 1 },
+ { "ScanHlReadEx", true, "SCAN-HL",1, "READ-EX", 266, 1, 0, 1 },
+ { "ScanHlSimpleRead", true, "SCAN-HL",1, "S-READ", 0, 1, 0, 1 },
+ { "ScanHlDirtyRead", true, "SCAN-HL",1, "D-READ", 0, 1, 0, 1 },
+ { "ScanHlInsert", true, "SCAN-HL",1, "INSERT", 266, X, 0, 1 },
+ { "ScanHlUpdate", true, "SCAN-HL",1, "UPDATE", 266, 2, 0, 1 },
+ { "ScanHlDelete", true, "SCAN-HL",1, "DELETE", 266, X, 0, 1 },
+ { "ScanHlScan", true, "SCAN-HL",1, "SCAN", 0, 1, 0, 1 },
+ { "ScanHlScanHl", true, "SCAN-HL",1, "SCAN-HL", 0, 1, 0, 1 },
+ { "ScanHlScanEx", true, "SCAN-HL",1, "SCAN-EX", 274, X, 0, 1 },
+#if 0
+ { "ScanHlScanUp", true, "SCAN-HL",1, "SCAN-UP", 266, X, 0, 1 },
+ { "ScanHlScanDe", true, "SCAN-HL",1, "SCAN-DE", 266, X, 0, 1 },
+#endif
+
+ { "ScanExRead", true, "SCAN-EX",1, "READ", 266, 1, 0, 1 },
+ { "ScanExReadEx", true, "SCAN-EX",1, "READ-EX", 266, 1, 0, 1 },
+ { "ScanExSimpleRead", true, "SCAN-EX",1, "S-READ", 266, 1, 0, 1 },
+ { "ScanExDirtyRead", true, "SCAN-EX",1, "D-READ", 0, 1, 0, 1 },
+ { "ScanExInsert", true, "SCAN-EX",1, "INSERT", 266, X, 0, 1 },
+ { "ScanExUpdate", true, "SCAN-EX",1, "UPDATE", 266, 2, 0, 1 },
+ { "ScanExDelete", true, "SCAN-EX",1, "DELETE", 266, X, 0, 1 },
+ { "ScanExScan", true, "SCAN-EX",1, "SCAN", 0, 1, 0, 1 },
+ { "ScanExScanHl", true, "SCAN-EX",1, "SCAN-HL", 274, X, 0, 1 },
+ { "ScanExScanEx", true, "SCAN-EX",1, "SCAN-EX", 274, X, 0, 1 },
+#if 0
+ { "ScanExScanUp", true, "SCAN-EX",1, "SCAN-UP", 266, X, 0, 1 },
+ { "ScanExScanDe", true, "SCAN-EX",1, "SCAN-DE", 266, X, 0, 1 },
+#endif
+
+ { "ReadExRead", true, "READ-EX",1, "READ", 266, X, 0, 1 },
+ { "ReadExReadEx", true, "READ-EX",1, "READ-EX", 266, X, 0, 1 },
+ { "ReadExSimpleRead", true, "READ-EX",1, "S-READ", 266, X, 0, 1 },
+ { "ReadExDirtyRead", true, "READ-EX",1, "D-READ", 0, 1, 0, 1 },
+ { "ReadExInsert", true, "READ-EX",1, "INSERT", 266, X, 0, 1 },
+ { "ReadExUpdate", true, "READ-EX",1, "UPDATE", 266, X, 0, 1 },
+ { "ReadExDelete", true, "READ-EX",1, "DELETE", 266, X, 0, 1 },
+ { "ReadExScan", true, "READ-EX",1, "SCAN", 0, 1, 0, 1 },
+ { "ReadExScanHl", true, "READ-EX",1, "SCAN-HL", 274, X, 0, 1 },
+ { "ReadExScanEx", true, "READ-EX",1, "SCAN-EX", 274, X, 0, 1 },
+#if 0
+ { "ReadExScanUp", true, "READ-EX",1, "SCAN-UP", 266, X, 0, 1 },
+ { "ReadExScanDe", true, "READ-EX",1, "SCAN-DE", 266, X, 0, 1 },
+#endif
+
+ { "InsertRead", false, "INSERT", 1, "READ", 266, X, 0, 1 },
+ { "InsertReadEx", false, "INSERT", 1, "READ-EX", 266, X, 0, 1 },
+ { "InsertSimpleRead",false, "INSERT", 1, "S-READ", 266, X, 0, 1 },
+ { "InsertDirtyRead", false, "INSERT", 1, "D-READ", 626, X, 0, 1 },
+ { "InsertInsert", false, "INSERT", 1, "INSERT", 266, X, 0, 1 },
+ { "InsertUpdate", false, "INSERT", 1, "UPDATE", 266, X, 0, 1 },
+ { "InsertDelete", false, "INSERT", 1, "DELETE", 266, X, 0, 1 },
+ { "InsertScan", false, "INSERT", 1, "SCAN", 626, X, 0, 1 },
+ { "InsertScanHl", false, "INSERT", 1, "SCAN-HL", 274, X, 0, 1 },
+ { "InsertScanEx", false, "INSERT", 1, "SCAN-EX", 274, X, 0, 1 },
+#if 0
+ { "InsertScanUp", false, "INSERT", 1, "SCAN-UP", 266, X, 0, 1 },
+ { "InsertScanDe", false, "INSERT", 1, "SCAN-DE", 266, X, 0, 1 },
+#endif
+
+ { "UpdateRead", true, "UPDATE", 2, "READ", 266, X, 0, 2 },
+ { "UpdateReadEx", true, "UPDATE", 2, "READ-EX", 266, X, 0, 2 },
+ { "UpdateSimpleRead", true, "UPDATE", 2, "S-READ", 266, X, 0, 2 },
+ { "UpdateDirtyRead", true, "UPDATE", 2, "D-READ", 0, 1, 0, 2 },
+ { "UpdateInsert", true, "UPDATE", 2, "INSERT", 266, X, 0, 2 },
+ { "UpdateUpdate", true, "UPDATE", 2, "UPDATE", 266, X, 0, 2 },
+ { "UpdateDelete", true, "UPDATE", 2, "DELETE", 266, X, 0, 2 },
+ { "UpdateScan", true, "UPDATE", 2, "SCAN", 0, 1, 0, 2 },
+ { "UpdateScanHl", true, "UPDATE", 2, "SCAN-HL", 274, X, 0, 2 },
+ { "UpdateScanEx", true, "UPDATE", 2, "SCAN-EX", 274, X, 0, 2 },
+#if 0
+ { "UpdateScanUp", true, "UPDATE", 2, "SCAN-UP", 266, X, 0, 2 },
+ { "UpdateScanDe", true, "UPDATE", 2, "SCAN-DE", 266, X, 0, 2 },
+#endif
+
+ { "DeleteRead", true, "DELETE", X, "READ", 266, X, 626, X },
+ { "DeleteReadEx", true, "DELETE", X, "READ-EX", 266, X, 626, X },
+ { "DeleteSimpleRead", true, "DELETE", X, "S-READ", 266, X, 626, X },
+ { "DeleteDirtyRead", true, "DELETE", X, "D-READ", 0, 1, 626, X },
+ { "DeleteInsert", true, "DELETE", X, "INSERT", 266, X, 626, X },
+ { "DeleteUpdate", true, "DELETE", X, "UPDATE", 266, X, 626, X },
+ { "DeleteDelete", true, "DELETE", X, "DELETE", 266, X, 626, X },
+ { "DeleteScan", true, "DELETE", X, "SCAN", 0, 1, 626, X },
+ { "DeleteScanHl", true, "DELETE", X, "SCAN-HL", 274, X, 626, X },
+ { "DeleteScanEx", true, "DELETE", X, "SCAN-EX", 274, X, 626, X },
+#if 0
+ { "DeleteScanUp", true, "DELETE", X, "SCAN-UP", 266, X, 626, X },
+ { "DeleteScanDe", true, "DELETE", X, "SCAN-DE", 266, X, 626, X }
+#endif
+
+};
+
+#define CHECK(a, b) { int x = a; int y = b; if (x != y) { \
+ g_err << "ERR: "<< step->getName() \
+ << " failed on line " << __LINE__ << endl << " " \
+ << x << " != " << y << endl;\
+ result = NDBT_FAILED; \
+ break; } }
+
+int
+runOp(HugoOperations & hugoOps,
+ Ndb * pNdb,
+ const char * op,
+ int value){
+
+#define C2(x) if(!(x)) {\
+ g_err << "ERR: failed on line " << __LINE__ << endl; \
+ return NDBT_FAILED; }
+
+ if(strcmp(op, "READ") == 0){
+ C2(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Read) == 0);
+ } else if(strcmp(op, "READ-EX") == 0){
+ C2(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Exclusive) == 0);
+ } else if(strcmp(op, "S-READ") == 0){
+ C2(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Read) == 0);
+ } else if(strcmp(op, "D-READ") == 0){
+ C2(hugoOps.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_CommittedRead) == 0);
+ } else if(strcmp(op, "INSERT") == 0){
+ C2(hugoOps.pkInsertRecord(pNdb, 1, 1, value) == 0);
+ } else if(strcmp(op, "UPDATE") == 0){
+ C2(hugoOps.pkUpdateRecord(pNdb, 1, 1, value) == 0);
+ } else if(strcmp(op, "DELETE") == 0){
+ C2(hugoOps.pkDeleteRecord(pNdb, 1, 1) == 0);
+ } else if(strcmp(op, "SCAN") == 0){
+ C2(hugoOps.scanReadRecords(pNdb) == 0);
+ } else if(strcmp(op, "SCAN-HL") == 0){
+ C2(hugoOps.scanReadRecords(pNdb, NdbScanOperation::LM_Read)== 0);
+ } else if(strcmp(op, "SCAN-EX") == 0){
+ C2(hugoOps.scanReadRecords(pNdb, NdbScanOperation::LM_Exclusive)== 0);
+ } else {
+ g_err << __FILE__ << " - " << __LINE__
+ << ": Unknown operation" << op << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int
+checkVal(HugoOperations & hugoOps,
+ const char * op,
+ int value,
+ int result){
+
+ if(result != 0)
+ return NDBT_OK;
+
+ if(strcmp(op, "READ") == 0){
+ } else if(strcmp(op, "READ-EX") == 0){
+ } else if(strcmp(op, "S-READ") == 0){
+ } else if(strcmp(op, "D-READ") == 0){
+ } else if(strcmp(op, "SCAN") == 0){
+ } else if(strcmp(op, "SCAN-HL") == 0){
+ } else if(strcmp(op, "SCAN-EX") == 0){
+ } else {
+ return NDBT_OK;
+ }
+
+ return hugoOps.verifyUpdatesValue(value);
+}
+
+#define TIMEOUT 100
+
+int
+setTransactionTimeout(NDBT_Context* ctx, NDBT_Step* step){
+ NdbRestarter restarter;
+
+ int val[] =
+ { DumpStateOrd::TcSetTransactionTimeout, TIMEOUT };
+ if(restarter.dumpStateAllNodes(val, 2) != 0){
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int
+runTwoTrans1(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations T1(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ const char * op1 = ctx->getProperty("op1", "NONE");
+ int val1 = ctx->getProperty("val1", ~0);
+
+ do {
+ // Insert, read
+ CHECK(T1.startTransaction(pNdb), 0);
+ CHECK(runOp(T1, pNdb, op1, val1), 0);
+ CHECK(T1.execute_NoCommit(pNdb), 0);
+ CHECK(checkVal(T1, op1, val1, 0), 0);
+
+ ctx->setProperty("T1-1-Complete", 1);
+ while(ctx->getProperty("T2-Complete", (Uint32)0) == 0){
+ T1.refresh();
+ NdbSleep_MilliSleep(10);
+ }
+
+ CHECK(T1.execute_Commit(pNdb), 0);
+
+ } while(false);
+ T1.closeTransaction(pNdb);
+
+ if(result != NDBT_OK)
+ return result;
+
+ const int res3 = ctx->getProperty("res3", ~0);
+ const int val3 = ctx->getProperty("val3", ~0);
+
+ do {
+ CHECK(T1.startTransaction(pNdb), 0);
+ CHECK(runOp(T1, pNdb, "READ", 0), 0);
+ CHECK(T1.execute_Commit(pNdb), res3);
+ CHECK(checkVal(T1, "READ", val3, res3), 0);
+ } while(false);
+ T1.closeTransaction(pNdb);
+
+ return result;
+}
+
+int
+runTwoTrans2(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations T2(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ const char * op2 = ctx->getProperty("op2", "NONE");
+ const int res2 = ctx->getProperty("res2", ~0);
+ const int val2 = ctx->getProperty("val2", ~0);
+
+ while(ctx->getProperty("T1-1-Complete", (Uint32)0) == 0 &&
+ !ctx->isTestStopped()){
+ NdbSleep_MilliSleep(100);
+ }
+
+ if(!ctx->isTestStopped()){
+ do {
+ CHECK(T2.startTransaction(pNdb), 0);
+ CHECK(runOp(T2, pNdb, op2, val2), 0);
+ CHECK(T2.execute_NoCommit(pNdb), res2);
+ CHECK(checkVal(T2, op2, val2, res2), 0);
+ if(res2 == 0){
+ CHECK(T2.execute_Commit(pNdb), res2);
+ }
+ } while(false);
+ T2.closeTransaction(pNdb);
+ }
+
+ ctx->setProperty("T2-Complete", 1);
+
+ return result;
+}
+
+int
+runInsertRecord(NDBT_Context* ctx, NDBT_Step* step){
+ int result = NDBT_OK;
+ HugoOperations hugoOps(*ctx->getTab());
+ Ndb* pNdb = GETNDB(step);
+
+ do{
+ // Insert, insert
+ CHECK(hugoOps.startTransaction(pNdb), 0);
+ CHECK(hugoOps.pkInsertRecord(pNdb, 1, 1, 1), 0);
+ CHECK(hugoOps.execute_Commit(pNdb), 0);
+ } while(false);
+
+ hugoOps.closeTransaction(pNdb);
+
+ return result;
+}
+
+int
+runClearTable(NDBT_Context* ctx, NDBT_Step* step){
+ int records = ctx->getNumRecords();
+
+ UtilTransactions utilTrans(*ctx->getTab());
+ if (utilTrans.clearTable2(GETNDB(step), records, 240) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int
+main(int argc, const char** argv){
+ ndb_init();
+
+ NDBT_TestSuite ts("testOperations");
+ for(Uint32 i = 0; i<sizeof(matrix)/sizeof(matrix[0]); i++){
+ NDBT_TestCaseImpl1 *pt = new NDBT_TestCaseImpl1(&ts, matrix[i].name, "");
+
+ pt->addInitializer(new NDBT_Initializer(pt,
+ "runClearTable",
+ runClearTable));
+
+ if(matrix[i].preCond){
+ pt->addInitializer(new NDBT_Initializer(pt,
+ "runInsertRecord",
+ runInsertRecord));
+ }
+
+ pt->addInitializer(new NDBT_Initializer(pt,
+ "setTransactionTimeout",
+ setTransactionTimeout));
+
+ pt->setProperty("op1", matrix[i].op1);
+ pt->setProperty("val1", matrix[i].val1);
+
+ pt->setProperty("op2", matrix[i].op2);
+ pt->setProperty("res2", matrix[i].res2);
+ pt->setProperty("val2", matrix[i].val2);
+
+ pt->setProperty("res3", matrix[i].res3);
+ pt->setProperty("val3", matrix[i].val3);
+
+ pt->addStep(new NDBT_ParallelStep(pt,
+ matrix[i].name,
+ runTwoTrans1));
+ pt->addStep(new NDBT_ParallelStep(pt,
+ matrix[i].name,
+ runTwoTrans2));
+ pt->addFinalizer(new NDBT_Finalizer(pt,
+ "runClearTable",
+ runClearTable));
+
+ ts.addTest(pt);
+ }
+
+ return ts.execute(argc, argv);
+}
+
diff --git a/storage/ndb/test/ndbapi/test_event.cpp b/storage/ndb/test/ndbapi/test_event.cpp
new file mode 100644
index 00000000000..2df50f21e43
--- /dev/null
+++ b/storage/ndb/test/ndbapi/test_event.cpp
@@ -0,0 +1,489 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT_Test.hpp>
+#include <NDBT_ReturnCodes.h>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <TestNdbEventOperation.hpp>
+
+#define GETNDB(ps) ((NDBT_NdbApiStep*)ps)->getNdb()
+
+int runCreateEvent(NDBT_Context* ctx, NDBT_Step* step)
+{
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ if (hugoTrans.createEvent(GETNDB(step)) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runCreateShadowTable(NDBT_Context* ctx, NDBT_Step* step)
+{
+ const NdbDictionary::Table *table= ctx->getTab();
+ char buf[1024];
+ sprintf(buf, "%s_SHADOW", table->getName());
+
+ GETNDB(step)->getDictionary()->dropTable(buf);
+ if (GETNDB(step)->getDictionary()->getTable(buf))
+ {
+ g_err << "unsucessful drop of " << buf << endl;
+ return NDBT_FAILED;
+ }
+
+ NdbDictionary::Table table_shadow(*table);
+ table_shadow.setName(buf);
+ GETNDB(step)->getDictionary()->createTable(table_shadow);
+ if (GETNDB(step)->getDictionary()->getTable(buf))
+ return NDBT_OK;
+
+ g_err << "unsucessful create of " << buf << endl;
+ return NDBT_FAILED;
+}
+
+int runCreateDropEventOperation(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+ EventOperationStats stats;
+
+ Ndb *pNdb=GETNDB(step);
+ const NdbDictionary::Table& tab= *ctx->getTab();
+ NdbEventOperation *pOp;
+ char eventName[1024];
+ sprintf(eventName,"%s_EVENT",tab.getName());
+ int noEventColumnName = tab.getNoOfColumns();
+
+ for (int i= 0; i < loops; i++)
+ {
+#if 1
+ if (hugoTrans.eventOperation(GETNDB(step), (void*)&stats, 0) != 0){
+ return NDBT_FAILED;
+ }
+#else
+ g_info << "create EventOperation\n";
+ pOp = pNdb->createEventOperation(eventName, 100);
+ if ( pOp == NULL ) {
+ g_err << "Event operation creation failed\n";
+ return NDBT_FAILED;
+ }
+
+ g_info << "dropping event operation" << endl;
+ int res = pNdb->dropEventOperation(pOp);
+ if (res != 0) {
+ g_err << "operation execution failed\n";
+ return NDBT_FAILED;
+ }
+#endif
+ }
+
+ return NDBT_OK;
+}
+
+int theThreadIdCounter = 0;
+
+int runEventOperation(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int tId = theThreadIdCounter++;
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ EventOperationStats stats;
+
+ g_info << "***** start Id " << tId << endl;
+
+ // sleep(tId);
+
+ if (hugoTrans.eventOperation(GETNDB(step), (void*)&stats, 3*records) != 0){
+ return NDBT_FAILED;
+ }
+
+ int ret;
+ if (stats.n_inserts == records &&
+ stats.n_deletes == records &&
+ stats.n_updates == records &&
+ stats.n_consecutive == 3 &&
+ stats.n_duplicates == 0)
+ ret = NDBT_OK;
+ else
+ ret = NDBT_FAILED;
+
+ if (ret == NDBT_FAILED) {
+ g_info << "***** end Id " << tId << endl;
+ ndbout_c("n_inserts = %d (%d)", stats.n_inserts, records);
+ ndbout_c("n_deletes = %d (%d)", stats.n_deletes, records);
+ ndbout_c("n_updates = %d (%d)", stats.n_updates, records);
+ ndbout_c("n_consecutive = %d (%d)", stats.n_consecutive, 3);
+ ndbout_c("n_duplicates = %d (%d)", stats.n_duplicates, 0);
+ ndbout_c("n_inconsistent_gcis = %d (%d)", stats.n_inconsistent_gcis, 0);
+ }
+
+ return ret;
+}
+
+int runEventLoad(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ sleep(5);
+ sleep(theThreadIdCounter);
+
+ if (hugoTrans.loadTable(GETNDB(step), records, 1, true, loops) != 0){
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.pkUpdateRecords(GETNDB(step), records, 1, loops) != 0){
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.pkDelRecords(GETNDB(step), records, 1, true, loops) != 0){
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int runEventMixedLoad(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ sleep(5);
+
+ if (hugoTrans.loadTable(GETNDB(step), 3*records, 1, true, 1) != 0){
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.pkDelRecords(GETNDB(step), 3*records, 1, true, 1) != 0){
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.loadTable(GETNDB(step), records, 1, true, 1) != 0){
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.pkUpdateRecords(GETNDB(step), records, 1, 1) != 0){
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.pkUpdateRecords(GETNDB(step), records, 1, 1) != 0){
+ return NDBT_FAILED;
+ }
+ if (hugoTrans.pkUpdateRecords(GETNDB(step), records, 1, 1) != 0){
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int runDropEvent(NDBT_Context* ctx, NDBT_Step* step)
+{
+ HugoTransactions hugoTrans(*ctx->getTab());
+
+ theThreadIdCounter = 0;
+ // if (hugoTrans.createEvent(GETNDB(step)) != 0){
+ // return NDBT_FAILED;
+ // }
+ return NDBT_OK;
+}
+
+int runVerify(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int records = ctx->getNumRecords();
+ const NdbDictionary::Table * table= ctx->getTab();
+ char buf[1024];
+
+ sprintf(buf, "%s_SHADOW", table->getName());
+
+ HugoTransactions hugoTrans(*table);
+ if (hugoTrans.compare(GETNDB(step), buf, 0))
+ {
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+int runEventApplier(NDBT_Context* ctx, NDBT_Step* step)
+{
+ DBUG_ENTER("runEventApplier");
+
+ int records = ctx->getNumRecords();
+ int loops = ctx->getNumLoops();
+ const NdbDictionary::Table * table= ctx->getTab();
+ char buf[1024];
+
+ sprintf(buf, "%s_SHADOW", table->getName());
+ const NdbDictionary::Table * table_shadow;
+ if ((table_shadow = GETNDB(step)->getDictionary()->getTable(buf)) == 0)
+ {
+ g_err << "Unable to get table " << buf << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ sprintf(buf, "%s_EVENT", table->getName());
+ NdbEventOperation *pOp;
+ pOp = GETNDB(step)->createEventOperation(buf, 10*records);
+ if ( pOp == NULL ) {
+ g_err << "Event operation creation failed on %s" << buf << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ int i;
+ int n_columns= table->getNoOfColumns();
+ NdbRecAttr* recAttr[1024];
+ NdbRecAttr* recAttrPre[1024];
+ for (i = 0; i < n_columns; i++) {
+ recAttr[i] = pOp->getValue(table->getColumn(i)->getName());
+ recAttrPre[i] = pOp->getPreValue(table->getColumn(i)->getName());
+ }
+
+ if (pOp->execute()) { // This starts changes to "start flowing"
+ g_err << "execute operation execution failed: \n";
+ g_err << pOp->getNdbError().code << " "
+ << pOp->getNdbError().message << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ int r= 0;
+ int res;
+ while (r < 10*records){
+ //printf("now waiting for event...\n");
+ res= GETNDB(step)->pollEvents(1000); // wait for event or 1000 ms
+ if (res <= 0)
+ {
+ ndbout_c("********************");
+ continue;
+ }
+
+ //printf("got data! %d\n", r);
+ int overrun= 0;
+ while (pOp->next(&overrun) > 0)
+ {
+ if (overrun)
+ {
+ g_err << "buffer overrun\n";
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ r++;
+
+ Uint32 gci= pOp->getGCI();
+
+ if (!pOp->isConsistent()) {
+ g_err << "A node failure has occured and events might be missing\n";
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ int noRetries= 0;
+ do
+ {
+ NdbTransaction *trans= GETNDB(step)->startTransaction();
+ if (trans == 0)
+ {
+ g_err << "startTransaction failed "
+ << GETNDB(step)->getNdbError().code << " "
+ << GETNDB(step)->getNdbError().message << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ NdbOperation *op= trans->getNdbOperation(table_shadow);
+ if (op == 0)
+ {
+ g_err << "getNdbOperation failed "
+ << trans->getNdbError().code << " "
+ << trans->getNdbError().message << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ switch (pOp->getEventType()) {
+ case NdbDictionary::Event::TE_INSERT:
+ if (op->insertTuple())
+ {
+ g_err << "insertTuple "
+ << op->getNdbError().code << " "
+ << op->getNdbError().message << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ break;
+ case NdbDictionary::Event::TE_DELETE:
+ if (op->deleteTuple())
+ {
+ g_err << "deleteTuple "
+ << op->getNdbError().code << " "
+ << op->getNdbError().message << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ break;
+ case NdbDictionary::Event::TE_UPDATE:
+ if (op->updateTuple())
+ {
+ g_err << "updateTuple "
+ << op->getNdbError().code << " "
+ << op->getNdbError().message << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ break;
+ default:
+ abort();
+ }
+
+ for (i= 0; i < n_columns; i++)
+ {
+ if (recAttr[i]->isNULL())
+ {
+ if (table->getColumn(i)->getPrimaryKey())
+ {
+ g_err << "internal error: primary key isNull()="
+ << recAttr[i]->isNULL() << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ switch (pOp->getEventType()) {
+ case NdbDictionary::Event::TE_INSERT:
+ if (recAttr[i]->isNULL() < 0)
+ {
+ g_err << "internal error: missing value for insert\n";
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ break;
+ case NdbDictionary::Event::TE_DELETE:
+ break;
+ case NdbDictionary::Event::TE_UPDATE:
+ break;
+ default:
+ abort();
+ }
+ }
+ if (table->getColumn(i)->getPrimaryKey() &&
+ op->equal(i,recAttr[i]->aRef()))
+ {
+ g_err << "equal " << i << " "
+ << op->getNdbError().code << " "
+ << op->getNdbError().message << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ }
+
+ switch (pOp->getEventType()) {
+ case NdbDictionary::Event::TE_INSERT:
+ for (i= 0; i < n_columns; i++)
+ {
+ if (!table->getColumn(i)->getPrimaryKey() &&
+ op->setValue(i,recAttr[i]->isNULL() ? 0:recAttr[i]->aRef()))
+ {
+ g_err << "setValue(insert) " << i << " "
+ << op->getNdbError().code << " "
+ << op->getNdbError().message << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ }
+ break;
+ case NdbDictionary::Event::TE_DELETE:
+ break;
+ case NdbDictionary::Event::TE_UPDATE:
+ for (i= 0; i < n_columns; i++)
+ {
+ if (!table->getColumn(i)->getPrimaryKey() &&
+ recAttr[i]->isNULL() >= 0 &&
+ op->setValue(i,recAttr[i]->isNULL() ? 0:recAttr[i]->aRef()))
+ {
+ g_err << "setValue(update) " << i << " "
+ << op->getNdbError().code << " "
+ << op->getNdbError().message << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ }
+ break;
+ case NdbDictionary::Event::TE_ALL:
+ abort();
+ }
+ if (trans->execute(Commit) == 0)
+ {
+ trans->close();
+ // everything ok
+ break;
+ }
+ if (noRetries++ == 10 ||
+ trans->getNdbError().status != NdbError::TemporaryError)
+ {
+ g_err << "execute " << r << " failed "
+ << trans->getNdbError().code << " "
+ << trans->getNdbError().message << endl;
+ trans->close();
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ trans->close();
+ NdbSleep_MilliSleep(100); // sleep before retying
+ } while(1);
+ }
+ }
+
+ if (GETNDB(step)->dropEventOperation(pOp)) {
+ g_err << "dropEventOperation execution failed "
+ << GETNDB(step)->getNdbError().code << " "
+ << GETNDB(step)->getNdbError().message << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+
+ DBUG_RETURN(NDBT_OK);
+}
+
+// INITIALIZER(runInsert);
+// STEP(runPkRead);
+// VERIFIER(runVerifyInsert);
+// FINALIZER(runClearTable);
+
+NDBT_TESTSUITE(test_event);
+TESTCASE("BasicEventOperation",
+ "Verify that we can listen to Events"
+ "NOTE! No errors are allowed!" ){
+ INITIALIZER(runCreateEvent);
+ STEP(runEventOperation);
+ STEP(runEventLoad);
+ FINALIZER(runDropEvent);
+}
+TESTCASE("CreateDropEventOperation",
+ "Verify that we can Create and Drop many times"
+ "NOTE! No errors are allowed!" ){
+ INITIALIZER(runCreateEvent);
+ STEP(runCreateDropEventOperation);
+ FINALIZER(runDropEvent);
+}
+TESTCASE("ParallellEventOperation",
+ "Verify that we can listen to Events in parallell"
+ "NOTE! No errors are allowed!" ){
+ INITIALIZER(runCreateEvent);
+ STEP(runEventOperation);
+ STEP(runEventOperation);
+ STEP(runEventLoad);
+ FINALIZER(runDropEvent);
+}
+TESTCASE("EventOperationApplier",
+ "Verify that if we apply the data we get from event "
+ "operation is the same as the original table"
+ "NOTE! No errors are allowed!" ){
+ INITIALIZER(runCreateEvent);
+ INITIALIZER(runCreateShadowTable);
+ STEP(runEventApplier);
+ STEP(runEventMixedLoad);
+ FINALIZER(runDropEvent);
+ FINALIZER(runVerify);
+}
+NDBT_TESTSUITE_END(test_event);
+
+int main(int argc, const char** argv){
+ ndb_init();
+ return test_event.execute(argc, argv);
+}
+
diff --git a/storage/ndb/test/ndbapi/test_event_multi_table.cpp b/storage/ndb/test/ndbapi/test_event_multi_table.cpp
new file mode 100644
index 00000000000..f16504029fa
--- /dev/null
+++ b/storage/ndb/test/ndbapi/test_event_multi_table.cpp
@@ -0,0 +1,487 @@
+/* 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 */
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+#include <NDBT_Test.hpp>
+#include <NDBT_ReturnCodes.h>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <TestNdbEventOperation.hpp>
+
+static void usage()
+{
+ ndb_std_print_version();
+}
+
+static int start_transaction(Ndb *ndb, Vector<HugoOperations*> &ops)
+{
+ if (ops[0]->startTransaction(ndb) != NDBT_OK)
+ return -1;
+ NdbTransaction * t= ops[0]->getTransaction();
+ for (int i= ops.size()-1; i > 0; i--)
+ {
+ ops[i]->setTransaction(t);
+ }
+ return 0;
+}
+
+static int close_transaction(Ndb *ndb, Vector<HugoOperations*> &ops)
+{
+ if (ops[0]->closeTransaction(ndb) != NDBT_OK)
+ return -1;
+ for (int i= ops.size()-1; i > 0; i--)
+ {
+ ops[i]->setTransaction(NULL);
+ }
+ return 0;
+}
+
+static int execute_commit(Ndb *ndb, Vector<HugoOperations*> &ops)
+{
+ if (ops[0]->execute_Commit(ndb) != NDBT_OK)
+ return -1;
+ return 0;
+}
+
+static int copy_events(Ndb *ndb,
+ Vector<NdbEventOperation *> &ops,
+ Vector<const NdbDictionary::Table *> &tabs,
+ Vector<Vector<NdbRecAttr *> > &values)
+{
+ DBUG_ENTER("copy_events");
+ int r= 0;
+ while (1)
+ {
+ int res= ndb->pollEvents(1000); // wait for event or 1000 ms
+ DBUG_PRINT("info", ("pollEvents res=%d", r));
+ if (res <= 0)
+ {
+ break;
+ }
+ for (unsigned i_ops= 0; i_ops < ops.size(); i_ops++)
+ {
+ NdbEventOperation *pOp= ops[i_ops];
+ const NdbDictionary::Table *table= tabs[i_ops];
+ Vector<NdbRecAttr *> &recAttr= values[i_ops];
+
+ int overrun= 0;
+ unsigned i;
+ unsigned n_columns= table->getNoOfColumns();
+ while (pOp->next(&overrun) > 0)
+ {
+ if (overrun)
+ {
+ g_err << "buffer overrun\n";
+ DBUG_RETURN(-1);
+ }
+ r++;
+
+ Uint32 gci= pOp->getGCI();
+
+ if (!pOp->isConsistent()) {
+ g_err << "A node failure has occured and events might be missing\n";
+ DBUG_RETURN(-1);
+ }
+
+ int noRetries= 0;
+ do
+ {
+ NdbTransaction *trans= ndb->startTransaction();
+ if (trans == 0)
+ {
+ g_err << "startTransaction failed "
+ << ndb->getNdbError().code << " "
+ << ndb->getNdbError().message << endl;
+ DBUG_RETURN(-1);
+ }
+
+ NdbOperation *op= trans->getNdbOperation(table);
+ if (op == 0)
+ {
+ g_err << "getNdbOperation failed "
+ << trans->getNdbError().code << " "
+ << trans->getNdbError().message << endl;
+ DBUG_RETURN(-1);
+ }
+
+ switch (pOp->getEventType()) {
+ case NdbDictionary::Event::TE_INSERT:
+ if (op->insertTuple())
+ {
+ g_err << "insertTuple "
+ << op->getNdbError().code << " "
+ << op->getNdbError().message << endl;
+ DBUG_RETURN(-1);
+ }
+ break;
+ case NdbDictionary::Event::TE_DELETE:
+ if (op->deleteTuple())
+ {
+ g_err << "deleteTuple "
+ << op->getNdbError().code << " "
+ << op->getNdbError().message << endl;
+ DBUG_RETURN(-1);
+ }
+ break;
+ case NdbDictionary::Event::TE_UPDATE:
+ if (op->updateTuple())
+ {
+ g_err << "updateTuple "
+ << op->getNdbError().code << " "
+ << op->getNdbError().message << endl;
+ DBUG_RETURN(-1);
+ }
+ break;
+ default:
+ abort();
+ }
+
+ for (i= 0; i < n_columns; i++)
+ {
+ if (recAttr[i]->isNULL())
+ {
+ if (table->getColumn(i)->getPrimaryKey())
+ {
+ g_err << "internal error: primary key isNull()="
+ << recAttr[i]->isNULL() << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ switch (pOp->getEventType()) {
+ case NdbDictionary::Event::TE_INSERT:
+ if (recAttr[i]->isNULL() < 0)
+ {
+ g_err << "internal error: missing value for insert\n";
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ break;
+ case NdbDictionary::Event::TE_DELETE:
+ break;
+ case NdbDictionary::Event::TE_UPDATE:
+ break;
+ default:
+ abort();
+ }
+ }
+ if (table->getColumn(i)->getPrimaryKey() &&
+ op->equal(i,recAttr[i]->aRef()))
+ {
+ g_err << "equal " << i << " "
+ << op->getNdbError().code << " "
+ << op->getNdbError().message << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ }
+
+ switch (pOp->getEventType()) {
+ case NdbDictionary::Event::TE_INSERT:
+ for (i= 0; i < n_columns; i++)
+ {
+ if (!table->getColumn(i)->getPrimaryKey() &&
+ op->setValue(i,recAttr[i]->isNULL() ? 0:recAttr[i]->aRef()))
+ {
+ g_err << "setValue(insert) " << i << " "
+ << op->getNdbError().code << " "
+ << op->getNdbError().message << endl;
+ DBUG_RETURN(-1);
+ }
+ }
+ break;
+ case NdbDictionary::Event::TE_DELETE:
+ break;
+ case NdbDictionary::Event::TE_UPDATE:
+ for (i= 0; i < n_columns; i++)
+ {
+ if (!table->getColumn(i)->getPrimaryKey() &&
+ recAttr[i]->isNULL() >= 0 &&
+ op->setValue(i,recAttr[i]->isNULL() ? 0:recAttr[i]->aRef()))
+ {
+ g_err << "setValue(update) " << i << " "
+ << op->getNdbError().code << " "
+ << op->getNdbError().message << endl;
+ DBUG_RETURN(NDBT_FAILED);
+ }
+ }
+ break;
+ case NdbDictionary::Event::TE_ALL:
+ abort();
+ }
+ if (trans->execute(Commit) == 0)
+ {
+ trans->close();
+ // everything ok
+ break;
+ }
+ if (noRetries++ == 10 ||
+ trans->getNdbError().status != NdbError::TemporaryError)
+ {
+ g_err << "execute " << r << " failed "
+ << trans->getNdbError().code << " "
+ << trans->getNdbError().message << endl;
+ trans->close();
+ DBUG_RETURN(-1);
+ }
+ trans->close();
+ NdbSleep_MilliSleep(100); // sleep before retying
+ } while(1);
+ }
+ }
+ }
+ DBUG_RETURN(r);
+}
+
+static int verify_copy(Ndb *ndb,
+ Vector<const NdbDictionary::Table *> &tabs1,
+ Vector<const NdbDictionary::Table *> &tabs2)
+{
+ for (unsigned i= 0; i < tabs1.size(); i++)
+ if (tabs1[i])
+ {
+ HugoTransactions hugoTrans(*tabs1[i]);
+ if (hugoTrans.compare(ndb, tabs2[i]->getName(), 0))
+ return -1;
+ }
+ return 0;
+}
+
+NDB_STD_OPTS_VARS;
+
+static const char* _dbname = "TEST_DB";
+static struct my_option my_long_options[] =
+{
+ NDB_STD_OPTS(""),
+ { "database", 'd', "Name of database table is in",
+ (gptr*) &_dbname, (gptr*) &_dbname, 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}
+};
+
+int
+main(int argc, char** argv)
+{
+ NDB_INIT(argv[0]);
+ const char *load_default_groups[]= { "mysql_cluster",0 };
+ load_defaults("my",load_default_groups,&argc,&argv);
+
+ int ho_error;
+#ifndef DBUG_OFF
+ opt_debug= "d:t:F:L";
+#endif
+ if ((ho_error=handle_options(&argc, &argv, my_long_options,
+ ndb_std_get_one_option)))
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+
+ DBUG_ENTER("main");
+ Ndb_cluster_connection con(opt_connect_str);
+ if(con.connect(12, 5, 1))
+ {
+ DBUG_RETURN(NDBT_ProgramExit(NDBT_FAILED));
+ }
+
+
+ Ndb ndb(&con,_dbname);
+ ndb.init();
+ while (ndb.waitUntilReady() != 0);
+
+ NdbDictionary::Dictionary * dict = ndb.getDictionary();
+ int no_error= 1;
+ int i;
+
+ // create all tables
+ Vector<const NdbDictionary::Table*> pTabs;
+ for (i= 0; no_error && argc; argc--, i++)
+ {
+ dict->dropTable(argv[i]);
+ NDBT_Tables::createTable(&ndb, argv[i]);
+ const NdbDictionary::Table *pTab= dict->getTable(argv[i]);
+ if (pTab == 0)
+ {
+ ndbout << "Failed to create table" << endl;
+ ndbout << dict->getNdbError() << endl;
+ no_error= 0;
+ break;
+ }
+ pTabs.push_back(pTab);
+ }
+ pTabs.push_back(NULL);
+
+ // create an event for each table
+ for (i= 0; no_error && pTabs[i]; i++)
+ {
+ HugoTransactions ht(*pTabs[i]);
+ if (ht.createEvent(&ndb)){
+ no_error= 0;
+ break;
+ }
+ }
+
+ // create an event operation for each event
+ Vector<NdbEventOperation *> pOps;
+ for (i= 0; no_error && pTabs[i]; i++)
+ {
+ char buf[1024];
+ sprintf(buf, "%s_EVENT", pTabs[i]->getName());
+ NdbEventOperation *pOp= ndb.createEventOperation(buf, 1000);
+ if ( pOp == NULL )
+ {
+ no_error= 0;
+ break;
+ }
+ pOps.push_back(pOp);
+ }
+
+ // get storage for each event operation
+ Vector<Vector<NdbRecAttr*> > values;
+ Vector<Vector<NdbRecAttr*> > pre_values;
+ for (i= 0; no_error && pTabs[i]; i++)
+ {
+ int n_columns= pTabs[i]->getNoOfColumns();
+ Vector<NdbRecAttr*> tmp_a;
+ Vector<NdbRecAttr*> tmp_b;
+ for (int j = 0; j < n_columns; j++) {
+ tmp_a.push_back(pOps[i]->getValue(pTabs[i]->getColumn(j)->getName()));
+ tmp_b.push_back(pOps[i]->getPreValue(pTabs[i]->getColumn(j)->getName()));
+ }
+ values.push_back(tmp_a);
+ pre_values.push_back(tmp_b);
+ }
+
+ // start receiving events
+ for (i= 0; no_error && pTabs[i]; i++)
+ {
+ if ( pOps[i]->execute() )
+ {
+ no_error= 0;
+ break;
+ }
+ }
+
+ // create a "shadow" table for each table
+ Vector<const NdbDictionary::Table*> pShadowTabs;
+ for (i= 0; no_error && pTabs[i]; i++)
+ {
+ char buf[1024];
+ sprintf(buf, "%s_SHADOW", pTabs[i]->getName());
+
+ dict->dropTable(buf);
+ if (dict->getTable(buf))
+ {
+ no_error= 0;
+ break;
+ }
+
+ NdbDictionary::Table table_shadow(*pTabs[i]);
+ table_shadow.setName(buf);
+ dict->createTable(table_shadow);
+ pShadowTabs.push_back(dict->getTable(buf));
+ if (!pShadowTabs[i])
+ {
+ no_error= 0;
+ break;
+ }
+ }
+
+ // create a hugo operation per table
+ Vector<HugoOperations *> hugo_ops;
+ for (i= 0; no_error && pTabs[i]; i++)
+ {
+ hugo_ops.push_back(new HugoOperations(*pTabs[i]));
+ }
+
+ sleep(5);
+
+ // insert 3 records per table
+ do {
+ if (start_transaction(&ndb, hugo_ops))
+ {
+ no_error= 0;
+ break;
+ }
+ for (i= 0; no_error && pTabs[i]; i++)
+ {
+ hugo_ops[i]->pkInsertRecord(&ndb, 0, 3);
+ }
+ if (execute_commit(&ndb, hugo_ops))
+ {
+ no_error= 0;
+ break;
+ }
+ if(close_transaction(&ndb, hugo_ops))
+ {
+ no_error= 0;
+ break;
+ }
+ } while(0);
+
+ // copy events and verify
+ do {
+ if (copy_events(&ndb, pOps, pShadowTabs, values) < 0)
+ {
+ no_error= 0;
+ break;
+ }
+ if (verify_copy(&ndb, pTabs, pShadowTabs))
+ {
+ no_error= 0;
+ break;
+ }
+ } while (0);
+
+ // update 2 records in first table
+ do {
+ if (start_transaction(&ndb, hugo_ops))
+ {
+ no_error= 0;
+ break;
+ }
+
+ hugo_ops[0]->pkUpdateRecord(&ndb, 2);
+
+ if (execute_commit(&ndb, hugo_ops))
+ {
+ no_error= 0;
+ break;
+ }
+ if(close_transaction(&ndb, hugo_ops))
+ {
+ no_error= 0;
+ break;
+ }
+ } while(0);
+
+ // copy events and verify
+ do {
+ if (copy_events(&ndb, pOps, pShadowTabs, values) < 0)
+ {
+ no_error= 0;
+ break;
+ }
+ if (verify_copy(&ndb, pTabs, pShadowTabs))
+ {
+ no_error= 0;
+ break;
+ }
+ } while (0);
+
+ if (no_error)
+ DBUG_RETURN(NDBT_ProgramExit(NDBT_OK));
+ DBUG_RETURN(NDBT_ProgramExit(NDBT_FAILED));
+}
+
+template class Vector<HugoOperations *>;
+template class Vector<NdbEventOperation *>;
+template class Vector<NdbRecAttr*>;
+template class Vector<Vector<NdbRecAttr*> >;
diff --git a/storage/ndb/test/ndbapi/userInterface.cpp b/storage/ndb/test/ndbapi/userInterface.cpp
new file mode 100644
index 00000000000..2f77c0f4857
--- /dev/null
+++ b/storage/ndb/test/ndbapi/userInterface.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 */
+
+/***************************************************************
+* I N C L U D E D F I L E S *
+***************************************************************/
+
+#include <ndb_global.h>
+#include <time.h>
+
+#include "ndb_schema.hpp"
+#include "ndb_error.hpp"
+#include "userInterface.h"
+#include <NdbMutex.h>
+#include <NdbThread.h>
+#include <NdbTick.h>
+#include <NdbApi.hpp>
+#include <NdbOut.hpp>
+
+/***************************************************************
+* 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 *
+***************************************************************/
+
+/***************************************************************
+* L O C A L F U N C T I O N S *
+***************************************************************/
+
+#ifndef NDB_WIN32
+#include <unistd.h>
+#endif
+
+
+static NdbMutex* startupMutex = NdbMutex_Create();
+
+Ndb*
+asyncDbConnect(int parallellism){
+ NdbMutex_Lock(startupMutex);
+ Ndb * pNDB = new Ndb("");
+
+ pNDB->init(parallellism + 1);
+
+ while(pNDB->waitUntilReady() != 0){
+ }
+
+ NdbMutex_Unlock(startupMutex);
+
+ return pNDB;
+}
+
+void
+asyncDbDisconnect(Ndb* pNDB)
+{
+ delete pNDB;
+}
+
+double
+userGetTime(void)
+{
+ static bool initialized = false;
+ static NDB_TICKS initSecs = 0;
+ static Uint32 initMicros = 0;
+ double timeValue = 0;
+
+ if ( !initialized ) {
+ initialized = true;
+ NdbTick_CurrentMicrosecond(&initSecs, &initMicros);
+ timeValue = 0.0;
+ } else {
+ NDB_TICKS secs = 0;
+ Uint32 micros = 0;
+
+ NdbTick_CurrentMicrosecond(&secs, &micros);
+ double s = (double)secs - (double)initSecs;
+ double us = (double)micros - (double)initMicros;
+
+ timeValue = s + (us / 1000000.0);
+ }
+ return timeValue;
+}
+
+void showTime()
+{
+ char buf[128];
+ struct tm* tm_now;
+ time_t now;
+ now = ::time((time_t*)NULL);
+ tm_now = ::gmtime(&now);
+
+ BaseString::snprintf(buf, 128,
+ "%d-%.2d-%.2d %.2d:%.2d:%.2d",
+ tm_now->tm_year + 1900,
+ tm_now->tm_mon,
+ tm_now->tm_mday,
+ tm_now->tm_hour,
+ tm_now->tm_min,
+ tm_now->tm_sec);
+
+ ndbout_c("Time: %s", buf);
+}
+
diff --git a/storage/ndb/test/ndbnet/test.run b/storage/ndb/test/ndbnet/test.run
new file mode 100644
index 00000000000..30042488c92
--- /dev/null
+++ b/storage/ndb/test/ndbnet/test.run
@@ -0,0 +1,3 @@
+#
+
+system("printenv|sort");
diff --git a/storage/ndb/test/ndbnet/testError.run b/storage/ndb/test/ndbnet/testError.run
new file mode 100644
index 00000000000..3cce489a3da
--- /dev/null
+++ b/storage/ndb/test/ndbnet/testError.run
@@ -0,0 +1,266 @@
+#
+# file : test/ndbnet/testError.run
+# usage: perl testError.run
+#
+# you need to have $NDB_TOP/lib/perl5 on search path $PERL5LIB
+# or else write perl -I$NDB_TOP/lib/perl5 test1.run
+#
+# The database is specified by the $NDB_DATABASE environment variable
+#
+# method names and argument style will change slightly.
+#
+#
+
+use strict;
+use NDB::Run;
+
+my $env = NDB::Run->getenv;
+my $log = $env->getlog;
+$log->setpart(time => 1, line => 0);
+$log->setprio("info");
+
+my $database = $ENV{NDB_DATABASE};
+my $api_cmd = $ENV{API_CMD};
+$log->put("start test database=$database");
+$env->init or $log->push("init failed")->fatal;
+
+my $db = $env->getdb($database) or $log->push->fatal;
+my $mgm = $db->getnode(1) or $log->push->fatal;
+my $api = $db->getnode(4) or $log->push->fatal;
+
+my @dbnode = (); # array of db nodes indexed 2..3
+for my $i (2..3) {
+ $dbnode[$i] = $db->getnode($i) or $log->push->fatal;
+}
+
+# list of db nodes and errors to insert
+my @errors = ( # array of array refs
+ [ 2, 9998 ],
+ [ 2, 37017 ],
+ [ 2, 37018 ],
+ [ 2, 37017 ],
+ [ 2, 37018 ],
+ [ 2, 37017 ],
+ [ 2, 37018 ],
+ [ 2, 37017 ],
+ [ 2, 37018 ],
+ [ 2, 37017 ],
+ [ 2, 37018 ],
+ [ 2, 37017 ],
+ [ 2, 9998 ],
+ [ 3, 9998 ],
+ [ 3, 9998 ],
+ [ 2, 9998 ],
+ [ 3, 9998 ],
+ [ 3, 9998 ],
+ [ 2, 38002 ],
+ [ 2, 38002 ],
+ [ 2, 8002 ],
+ [ 3, 8029 ],
+ [ 2, 8030 ],
+ [ 2, 8031 ],
+ [ 3, 8020 ],
+ [ 2, 8021 ],
+ [ 3, 8022 ],
+ [ 2, 8023 ],
+ [ 3, 8025 ],
+ [ 2, 8027 ],
+ [ 2, 38002 ],
+ [ 3, 38029 ],
+ [ 2, 38030 ],
+ [ 2, 38031 ],
+ [ 3, 38020 ],
+ [ 2, 38021 ],
+ [ 3, 38022 ],
+ [ 2, 38023 ],
+ [ 3, 38025 ],
+ [ 2, 38027 ],
+ [ 2, 48002 ],
+ [ 3, 48029 ],
+ [ 2, 48030 ],
+ [ 2, 48031 ],
+ [ 3, 48020 ],
+ [ 2, 48021 ],
+ [ 3, 48022 ],
+ [ 2, 48023 ],
+ [ 3, 48025 ],
+ [ 2, 48027 ],
+ [ 2, 9999 ],
+ [ 3, 9999 ],
+ [ 3, 9999 ],
+ [ 2, 9998 ],
+ [ 3, 9998 ],
+ [ 3, 9998 ],
+ [ 2, 9998 ],
+ [ 3, 9998 ],
+ [ 3, 9998 ],
+ [ 3, 37000 ],
+ [ 2, 37001 ],
+ [ 2, 37002 ],
+ [ 2, 37003 ],
+ [ 2, 47005 ],
+ [ 2, 47006 ],
+ [ 2, 47007 ],
+ [ 2, 47008 ],
+ [ 2, 45000 ],
+ [ 2, 37005 ],
+ [ 2, 37006 ],
+ [ 2, 37007 ],
+ [ 2, 37008 ],
+ [ 2, 35000 ],
+ [ 2, 37009 ],
+ [ 2, 37010 ],
+ [ 2, 37013 ],
+ [ 2, 37014 ],
+ [ 2, 37015 ],
+ [ 2, 37016 ],
+ [ 2, 37017 ],
+ [ 2, 37018 ],
+ [ 2, 37019 ],
+ [ 2, 47020 ],
+ [ 2, 37020 ],
+ [ 2, 48000 ],
+ [ 2, 38000 ],
+ [ 2, 48001 ],
+ [ 2, 38001 ],
+ [ 2, 45001 ],
+ [ 2, 35001 ],
+);
+
+$db->kill;
+$db->start({init_rm=>1}) or $log->push->fatal;
+sleep 10;
+
+# should be option (or default) in $db->start
+sub wait_until_started {
+ my $local_cnt = 60;
+ while (--$local_cnt > 0) {
+ sleep 2;
+ my $ret = $mgm->write("all status", { wait => 3 });
+ $ret or $log->fatal;
+ my $output = $ret->{output};
+ if ($output =~ /\bstarted\b(.|\n)*started\b/i) {
+ $log->put("*** db is started ***")->info;
+ return;
+ }
+ if ($output =~ /\bno.contact\b(.|\n)*no.contact\b/i) {
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $log->put("*** db is dead ***")->fatal;
+ }
+ }
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $log->put("*** node recovery failed ***")->fatal;
+}
+
+sub getdbstatus {
+ my $ret = $mgm->write("all status", { wait => 3 });
+ $ret or return undef;
+ my $output = $ret->{output};
+ my @status = (); # indexed 2..3
+ for my $i (2..3) {
+ my $s = "Unknown";
+ if ($output =~ /Node\s*$i\s*:\s*(\w+)/i) {
+ $s = $1;
+ }#if
+ $status[$i] = lc $s;
+ }#for
+ return \@status;
+}
+
+# count elapsed time
+my $lasttime;
+sub settime { $lasttime = time };
+sub gettime { return time - $lasttime };
+
+wait_until_started();
+$api->start({run=>$api_cmd});
+sleep 10;
+
+# loop over error inserts
+for my $e (@errors) {
+for my $loop (1..7) {
+ my $i = $e->[0]; # db node number 2..3
+ my $c = $e->[1]; # error code
+ my $dead_node_id = $i;
+ my $dbnode = $dbnode[$i];
+ my $two = 2;
+ my $three = 3;
+ my $kill_no = 9998;
+
+ $log->put("insert error $c")->push($dbnode)->notice;
+
+ # insert error
+ if ($c eq $kill_no) {
+ $dbnode->kill
+ or $log->put("Kill 1 failed")->fatal;
+ } else {
+ $mgm->write("$i error $c")
+ or $log->put("insert error fault")->fatal;
+ }#if
+
+ # after a few seconds check that node is dead
+ settime();
+ loop: {
+ gettime() <= 300
+ or $log->put("db node $i refuses to die")->fatal;
+ my $status = getdbstatus()
+ or $log->put("getdbstatus error")->fatal;
+
+ if (($status->[$two] eq 'no') && ($status->[$three] eq 'no')) {
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $log->put("*** db is dead ***")->fatal;
+ }#if
+ if ($c < 10000) {
+ if ($status->[$i] ne 'no') { # ...contact
+ sleep 2;
+ redo loop;
+ }#if
+ $dead_node_id = $i;
+ } else {
+ if (($status->[$two] eq 'no') &&
+ ($status->[$three] eq 'started')) {
+ $dead_node_id = 2;
+ } else {
+ if (($status->[$three] eq 'no') &&
+ ($status->[$two] eq 'started')) {
+ $dead_node_id = 3;
+ } else {
+ sleep 2;
+ redo loop;
+ }#if
+ }#if
+ }#if
+ }#loop
+
+ my $dead_node = $dbnode[$dead_node_id];
+ # have to even check the process is gone
+ sleep 5;
+ if ($dead_node->stat ne "down") {
+ $log->put("ndb did not die, kill it")->push($dead_node)->warn;
+ $dead_node->kill
+ or $log->put("Kill 2 failed")->fatal;
+ }#if
+
+ $log->put("node $dead_node_id is dead")->notice;
+
+ # start the failed node
+ $dead_node->start
+ or $log->put("Start ndb node failed")->fatal;
+
+ wait_until_started();
+ $log->put("node $dead_node_id is up again")->notice;
+
+ # check test pgm is running
+ my $stat = $api->stat
+ or $log->put("api->stat failed")->fatal;
+ if ($stat ne "up") {
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $db->kill;
+ $log->put("flexBench has crashed")->fatal;
+ }#if
+}#for
+}#for
+print "NDBT_ProgramExit: 0 - test ready\n";
+$db->kill;
+
+# vim: set sw=4:
diff --git a/storage/ndb/test/ndbnet/testMNF.run b/storage/ndb/test/ndbnet/testMNF.run
new file mode 100644
index 00000000000..df226cd3359
--- /dev/null
+++ b/storage/ndb/test/ndbnet/testMNF.run
@@ -0,0 +1,277 @@
+#
+# file : test/ndbnet/testError.run
+# usage: perl testError.run
+#
+# you need to have $NDB_TOP/lib/perl5 on search path $PERL5LIB
+# or else write perl -I$NDB_TOP/lib/perl5 test1.run
+#
+# The database is specified by the $NDB_DATABASE environment variable
+#
+# method names and argument style will change slightly.
+#
+#
+
+use strict;
+use NDB::Run;
+
+my $env = NDB::Run->getenv;
+my $log = $env->getlog;
+$log->setpart(time => 1, line => 0);
+$log->setprio("info");
+
+my $database = $ENV{NDB_DATABASE};
+my $api_cmd = $ENV{API_CMD};
+my $api_cmd2 = $ENV{API_CMD2};
+$log->put("start test database=$database");
+$env->init or $log->push("init failed")->fatal;
+
+my $db = $env->getdb($database) or $log->push->fatal;
+my $mgm = $db->getnode(1) or $log->push->fatal;
+my $api = $db->getnode(6) or $log->push->fatal;
+my $api2 = $db->getnode(7) or $log->push->fatal;
+
+my @dbnode = (); # array of db nodes indexed 2..5
+for my $i (2..5) {
+ $dbnode[$i] = $db->getnode($i) or $log->push->fatal;
+}
+
+# list of db nodes and errors to insert
+my @errors = ( # array of array refs
+ [ 3, 4, 8030, 8031 ],
+ [ 2, 5, 8027, 8030 ],
+ [ 2, 4, 5000, 5000 ],
+ [ 2, 5, 5000, 5000 ],
+ [ 3, 4, 5000, 7008 ],
+ [ 3, 5, 5000, 7008 ],
+ [ 2, 4, 5000, 7007 ],
+ [ 3, 4, 5000, 7007 ],
+ [ 3, 5, 5000, 7006 ],
+ [ 2, 5, 5000, 7006 ],
+ [ 2, 4, 5000, 7005 ],
+ [ 3, 5, 5000, 7005 ],
+ [ 3, 4, 7005, 7005 ],
+ [ 2, 5, 7005, 7005 ],
+ [ 3, 4, 7005, 7007 ],
+ [ 3, 5, 7005, 7007 ],
+ [ 2, 5, 7005, 7008 ],
+ [ 2, 4, 7005, 7008 ],
+ [ 3, 5, 7006, 7006 ],
+ [ 3, 4, 7006, 7006 ],
+ [ 2, 4, 7006, 7007 ],
+ [ 2, 5, 7006, 7007 ],
+ [ 2, 5, 7006, 7008 ],
+ [ 2, 4, 7006, 7008 ],
+ [ 3, 4, 7007, 7007 ],
+ [ 3, 5, 7007, 7007 ],
+ [ 3, 5, 7007, 7008 ],
+ [ 3, 4, 7007, 7008 ],
+ [ 2, 4, 7008, 7008 ],
+ [ 2, 5, 7008, 7008 ],
+ [ 2, 5, 7008, 7008 ],
+ [ 2, 5, 7008, 7008 ],
+ [ 2, 5, 8000, 8000 ],
+ [ 2, 4, 8000, 8000 ],
+ [ 3, 5, 8000, 8001 ],
+ [ 3, 4, 8000, 8001 ],
+ [ 2, 5, 8000, 5001 ],
+ [ 3, 5, 8000, 5001 ],
+ [ 3, 4, 8001, 5001 ],
+ [ 2, 4, 8001, 5001 ],
+ [ 2, 5, 8002, 8029 ],
+ [ 3, 4, 8030, 8031 ],
+ [ 3, 5, 8020, 8021 ],
+ [ 2, 4, 8022, 9999 ],
+ [ 2, 4, 8023, 8025 ],
+ [ 2, 5, 8027, 8030 ],
+ [ 3, 4, 8002, 8002 ],
+ [ 3, 5, 8029, 8030 ],
+ [ 3, 5, 8031, 8031 ],
+ [ 3, 4, 8020, 8020 ],
+ [ 2, 4, 8021, 8021 ],
+ [ 2, 5, 8022, 8022 ],
+ [ 3, 4, 8023, 8023 ],
+ [ 3, 5, 8025, 8025 ],
+ [ 2, 4, 8027, 8027 ],
+ [ 2, 5, 8027, 8027 ],
+ [ 3, 4, 8023, 9999 ],
+ [ 3, 5, 8025, 9998 ],
+ [ 2, 4, 8027, 9999 ],
+ [ 2, 5, 8027, 9998 ],
+ [ 3, 4, 8000, 9999 ],
+ [ 3, 5, 8001, 9998 ],
+ [ 2, 4, 5001, 9999 ],
+ [ 2, 5, 5000, 9999 ],
+ [ 3, 4, 7005, 9999 ],
+ [ 3, 5, 7006, 9999 ],
+ [ 2, 4, 7007, 9999 ],
+ [ 2, 5, 7008, 9999 ],
+ [ 2, 4, 9998, 9998 ],
+ [ 3, 5, 9998, 9998 ],
+ [ 2, 4, 9998, 9998 ],
+ [ 2, 4, 9998, 9998 ],
+ [ 3, 5, 9998, 9998 ],
+ [ 2, 4, 9999, 9999 ],
+ [ 2, 4, 9998, 9998 ],
+ [ 3, 5, 9999, 9999 ],
+ [ 2, 4, 9999, 9999 ],
+ [ 2, 4, 9999, 9999 ],
+ [ 3, 5, 9999, 9999 ],
+);
+
+$db->kill;
+$db->start({init_rm=>1}) or $log->push->fatal;
+sleep 10;
+
+# should be option (or default) in $db->start
+sub wait_until_started {
+ my $local_cnt = 60;
+ while (--$local_cnt > 0) {
+ sleep 5;
+ my $ret = $mgm->write("all status", { wait => 3 });
+ $ret or $log->fatal;
+ my $output = $ret->{output};
+ if ($output =~ /((.|\n)*\bstarted\b(.|\n)*){4}/i) {
+ $log->put("*** db is started ***")->info;
+ return;
+ }
+ if ($output =~ /((.|\n)*\bno.contact\b(.|\n)*){4}/i) {
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $log->put("*** db is dead ***")->fatal;
+ }
+ }
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $log->put("*** node recovery failed ***")->fatal;
+}
+
+sub getdbstatus {
+ my $ret = $mgm->write("all status", { wait => 3 });
+ $ret or return undef;
+ my $output = $ret->{output};
+ my @status = (); # indexed 2..5
+ for my $i (2..5) {
+ my $s = "Unknown";
+ if ($output =~ /Node\s*$i\s*:\s*(\w+)/i) {
+ $s = $1;
+ }#if
+ $status[$i] = lc $s;
+ }#for
+ return \@status;
+}
+
+# count elapsed time
+my $lasttime;
+sub settime { $lasttime = time };
+sub gettime { return time - $lasttime };
+
+wait_until_started();
+$api->start({run=>$api_cmd});
+sleep 15;
+$api2->start({run=>$api_cmd2});
+sleep 15;
+
+# loop over error inserts
+for my $x (0..1) {
+for my $e (@errors) {
+for my $z (0..3) {
+ my $i1 = $e->[0]; # db node number 2..5
+ my $i2 = $e->[1]; # db node number 2..5
+ my $c1 = $e->[2]; # error code
+ my $c2 = $e->[3]; # error code
+ my $dbnode1 = $dbnode[$i1];
+ my $dbnode2 = $dbnode[$i2];
+ my $kill_no = 9998;
+ my @survivor = ();
+ my $survivor_count = 0;
+
+ $log->put("insert error $c1 on node $i1")->push($dbnode1)->notice;
+ $log->put("and insert error $c2 on node $i2")->push($dbnode2)->notice;
+
+ for my $node (2..5) {
+ if (($node ne $i1) && ($node ne $i2)) {
+ $survivor[$survivor_count] = $node;
+ $survivor_count++;
+ }#if
+ }#for
+ # insert error
+ if ($c1 eq $kill_no) {
+ $dbnode1->kill
+ or $log->put("Kill 1 failed")->fatal;
+ } else {
+ $mgm->write("$i1 error $c1")
+ or $log->put("insert error 1 fault")->fatal;
+ }#if
+ if ($c2 eq $kill_no) {
+ $dbnode2->kill
+ or $log->put("Kill 2 failed")->fatal;
+ } else {
+ $mgm->write("$i2 error $c2")
+ or $log->put("insert error 2 fault")->fatal;
+ }#if
+
+ # after a few seconds check that node is dead
+ settime();
+ loop: {
+ gettime() <= 300
+ or $log->put("db node $i1 or $i2 refuses to die")->fatal;
+ my $status = getdbstatus()
+ or $log->put("getdbstatus error")->fatal;
+
+ if (($status->[$survivor[0]] eq 'no') ||
+ ($status->[$survivor[1]] eq 'no')) {
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $db->kill;
+ $log->put("*** db is dead ***")->fatal;
+ }#if
+ if (($status->[$i1] ne 'no') || ($status->[$i2] ne 'no')) { # ...contact
+ sleep 2;
+ redo loop;
+ }#if
+ }#loop
+
+ # have to even check the process is gone
+ sleep 5;
+ if ($dbnode1->stat ne "down") {
+ $log->put("ndb did not die, kill it")->push($dbnode1)->warn;
+ $dbnode1->kill
+ or $log->put("Kill 3 failed")->fatal;
+ }#if
+ if ($dbnode2->stat ne "down") {
+ $log->put("ndb did not die, kill it")->push($dbnode2)->warn;
+ $dbnode2->kill
+ or $log->put("Kill 4 failed")->fatal;
+ }#if
+
+ $log->put("node $i1 and node $i2 is dead")->notice;
+
+ # start the failed nodes
+ $dbnode1->start
+ or $log->put("Start ndb node 1 failed")->fatal;
+ $dbnode2->start
+ or $log->put("Start ndb node 2 failed")->fatal;
+ wait_until_started();
+ $log->put("node $i1 and node $i2 is up again")->notice;
+
+ # check test pgm is running
+ my $stat = $api->stat
+ or $log->put("api->stat failed")->fatal;
+ if ($stat ne "up") {
+ print "NDBT_ProgramExit: 1 - test error\n";
+ sleep 15;
+ $db->kill;
+ $log->put("flexBench has crashed")->fatal;
+ }#if
+ my $stat = $api2->stat
+ or $log->put("api2->stat failed")->fatal;
+ if ($stat ne "up") {
+ print "NDBT_ProgramExit: 1 - test error\n";
+ sleep 15;
+ $db->kill;
+ $log->put("flexBench2 has crashed")->fatal;
+ }#if
+}#for
+}#for
+}#for
+print "NDBT_ProgramExit: 0 - test ready\n";
+$db->kill;
+
+# vim: set sw=4:
diff --git a/storage/ndb/test/ndbnet/testNR.run b/storage/ndb/test/ndbnet/testNR.run
new file mode 100644
index 00000000000..01a3d76266d
--- /dev/null
+++ b/storage/ndb/test/ndbnet/testNR.run
@@ -0,0 +1,60 @@
+#
+
+use strict;
+use NDB::Run;
+
+my $env = NDB::Run->getenv;
+my $log = $env->getlog;
+$log->setpart(time => 1, line => 0);
+$log->setprio("info");
+
+my $database = $ENV{NDB_DATABASE};
+$log->put("start test database=$database");
+$env->init or $log->push("init failed")->fatal;
+
+my $db = $env->getdb($database) or $log->push->fatal;
+my $mgm = $db->getnode(1) or $log->push->fatal;
+my $db1 = $db->getnode(2) or $log->push->fatal;
+my $db2 = $db->getnode(3) or $log->push->fatal;
+my $api = $db->getnode(4) or $log->push->fatal;
+
+$db->kill;
+$db->start({init_rm=>1}) or $db->kill, $log->push->fatal;
+sleep 10;
+
+# should be option (or default) in $db->start
+sub wait_until_started {
+ my $local_cnt = 100;
+ while (--$local_cnt > 0) {
+ sleep 2;
+ my $ret = $mgm->write("all status", { wait => 2 });
+ $ret or $db->kill, $log->fatal;
+ my $output = $ret->{output};
+ if ($output =~ /\bstarted\b(.|\n)*started\b/i) {
+ $log->put("*** db is started ***")->info;
+ return;
+ }
+ if ($output =~ /\bno.contact\b(.|\n)*no.contact\b/i) {
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $db->kill, $log->put("*** db is dead ***")->fatal;
+ }
+ }
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $db->kill, $log->put("*** node recovery failed ***")->fatal;
+}
+
+my $cnt = 0;
+wait_until_started();
+$api->start({run=>"flexBench -t 32 -l 800"});
+while (1) {
+ wait_until_started();
+ sleep 2;
+ my $dbx = (++$cnt % 2 == 1 ? $db1 : $db2);
+ $dbx->kill or $db->kill, $log->fatal;
+ sleep 2;
+ $dbx->start; # start the node
+}
+
+$db->kill;
+
+# vim: set sw=4:
diff --git a/storage/ndb/test/ndbnet/testNR1.run b/storage/ndb/test/ndbnet/testNR1.run
new file mode 100644
index 00000000000..8819a92c8ca
--- /dev/null
+++ b/storage/ndb/test/ndbnet/testNR1.run
@@ -0,0 +1,61 @@
+# Node recovery killing 1 node out of 4 at the time and waiting for recover
+
+use strict;
+use NDB::Run;
+
+my $env = NDB::Run->getenv;
+my $log = $env->getlog;
+$log->setpart(time => 1, line => 0);
+$log->setprio("info");
+
+my $database = $ENV{NDB_DATABASE};
+$log->put("start test database=$database");
+$env->init or $log->push("init failed")->fatal;
+
+my $db = $env->getdb($database) or $log->push->fatal;
+my $mgm = $db->getnode(1) or $log->push->fatal;
+my $db1 = $db->getnode(2) or $log->push->fatal;
+my $api = $db->getnode(6) or $log->push->fatal;
+
+$db->kill;
+$db->start({init_rm=>1}) or $log->push->fatal;
+sleep 10;
+
+# should be option (or default) in $db->start
+sub wait_until_started {
+ my $local_cnt = 100;
+ while (--$local_cnt > 0) {
+ sleep 2;
+ my $ret = $mgm->write("all status", { wait => 2 });
+ $ret or $log->fatal;
+ my $output = $ret->{output};
+ if ($output =~ /((.|\n)*\bstarted\b(.|\n)*){1}/i) {
+# if all 4 nodes started
+ $log->put("*** db is started ***")->info;
+ return;
+ }
+ if ($output =~ /((.|\n)*\bno.contact\b(.|\n)*){1}/i) {
+#if all 4 nodes no contact
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $log->put("*** db is dead ***")->fatal;
+ }
+ }
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $log->put("*** node recovery failed ***")->fatal;
+}
+
+my $cnt = 0;
+wait_until_started();
+$api->start({run=>"flexBench -t 32 -l 800"});
+while (1) {
+ wait_until_started();
+ sleep 10;
+ $db1->kill or $log->fatal;
+ sleep 10;
+ $db1->start; # start the node
+ sleep 10;
+}
+
+$db->kill;
+
+# vim: set sw=4:
diff --git a/storage/ndb/test/ndbnet/testNR4.run b/storage/ndb/test/ndbnet/testNR4.run
new file mode 100644
index 00000000000..f7a5eef3494
--- /dev/null
+++ b/storage/ndb/test/ndbnet/testNR4.run
@@ -0,0 +1,77 @@
+# Node recovery killing 1 node out of 4 at the time and waiting for recover
+
+use strict;
+use NDB::Run;
+
+my $env = NDB::Run->getenv;
+my $log = $env->getlog;
+$log->setpart(time => 1, line => 0);
+$log->setprio("info");
+
+my $database = $ENV{NDB_DATABASE};
+$log->put("start test database=$database");
+$env->init or $log->push("init failed")->fatal;
+
+my $db = $env->getdb($database) or $log->push->fatal;
+my $mgm = $db->getnode(1) or $log->push->fatal;
+my $db1 = $db->getnode(2) or $log->push->fatal;
+my $db2 = $db->getnode(3) or $log->push->fatal;
+my $db3 = $db->getnode(4) or $log->push->fatal;
+my $db4 = $db->getnode(5) or $log->push->fatal;
+my $api = $db->getnode(6) or $log->push->fatal;
+
+$db->kill;
+$db->start({init_rm=>1}) or $log->push->fatal;
+sleep 10;
+
+# should be option (or default) in $db->start
+sub wait_until_started {
+ my $local_cnt = 100;
+ while (--$local_cnt > 0) {
+ sleep 2;
+ my $ret = $mgm->write("all status", { wait => 2 });
+ $ret or $log->fatal;
+ my $output = $ret->{output};
+ if ($output =~ /((.|\n)*\bstarted\b(.|\n)*){4}/i) {
+# if all 4 nodes started
+ $log->put("*** db is started ***")->info;
+ return;
+ }
+ if ($output =~ /((.|\n)*\bno.contact\b(.|\n)*){4}/i) {
+#if all 4 nodes no contact
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $log->put("*** db is dead ***")->fatal;
+ }
+ }
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $log->put("*** node recovery failed ***")->fatal;
+}
+
+my $cnt = 0;
+wait_until_started();
+$api->start({run=>"flexBench -t 32 -l 800"});
+while (1) {
+ wait_until_started();
+ sleep 10;
+ my $id = (++$cnt % 4);
+ my $dbx = 0;
+ if ($id eq 0) {
+ $dbx = $db1;
+ }
+ elsif ($id eq 1) {
+ $dbx = $db2;
+ }
+ elsif ($id eq 2) {
+ $dbx = $db3;
+ }
+ else {
+ $dbx = $db4;
+ }
+ $dbx->kill or $log->fatal;
+ sleep 10;
+ $dbx->start; # start the node
+}
+
+$db->kill;
+
+# vim: set sw=4:
diff --git a/storage/ndb/test/ndbnet/testSRhang.run b/storage/ndb/test/ndbnet/testSRhang.run
new file mode 100644
index 00000000000..8cb65a75ded
--- /dev/null
+++ b/storage/ndb/test/ndbnet/testSRhang.run
@@ -0,0 +1,50 @@
+#
+
+use strict;
+use NDB::Run;
+
+my $env = NDB::Run->getenv;
+my $log = $env->getlog;
+$log->setpart(time => 1, line => 0);
+$log->setprio("info");
+
+my $database = $ENV{NDB_DATABASE};
+$log->put("start test database=$database");
+$env->init or $log->push("init failed")->fatal;
+
+my $db = $env->getdb($database) or $log->push->fatal;
+my $mgm = $db->getnode(1) or $log->push->fatal;
+
+# should be option (or default) in $db->start
+sub wait_until_started {
+ my $local_cnt = 200;
+ while (--$local_cnt > 0) {
+ sleep 2;
+ if ($local_cnt < 150) {
+ my $x = $mgm->write("all dump 1", { wait => 2 });
+ }#if
+ my $ret = $mgm->write("all status", { wait => 2 });
+ $ret or $log->fatal;
+ my $output = $ret->{output};
+ if ($output =~ /\bstarted\b(.|\n)*started\b/i) {
+ $log->put("*** db is started ***")->info;
+ return;
+ }#if
+ if ($output =~ /\bno.contact\b(.|\n)*no.contact\b/i) {
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $db->kill;
+ $log->put("*** db is dead ***")->fatal;
+ }#if
+ }#while
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $db->kill;
+ $log->put("*** initial start failed ***")->fatal;
+}#sub wait_until_started
+while (1) {
+ $db->kill;
+ $db->start({init_rm=>1}) or $log->push->fatal;
+ sleep 10;
+ wait_until_started();
+}#while
+
+# vim: set sw=4:
diff --git a/storage/ndb/test/ndbnet/testTR295.run b/storage/ndb/test/ndbnet/testTR295.run
new file mode 100644
index 00000000000..ce4250b60ae
--- /dev/null
+++ b/storage/ndb/test/ndbnet/testTR295.run
@@ -0,0 +1,75 @@
+# testing TR295, kill non-master when recovering in phase 4
+
+use strict;
+use NDB::Run;
+
+my $env = NDB::Run->getenv;
+my $log = $env->getlog;
+$log->setpart(time => 1, line => 0);
+$log->setprio("info");
+
+my $database = $ENV{NDB_DATABASE};
+$log->put("start test database=$database");
+$env->init or $log->push("init failed")->fatal;
+
+my $db = $env->getdb($database) or $log->push->fatal;
+my $mgm = $db->getnode(1) or $log->push->fatal;
+my $db1 = $db->getnode(2) or $log->push->fatal;
+my $db2 = $db->getnode(3) or $log->push->fatal;
+
+$db->kill;
+$db->start({init_rm=>1}) or $log->push->fatal;
+sleep 10;
+
+# should be option (or default) in $db->start
+sub wait_until_started {
+ my $local_cnt = 100;
+ while (--$local_cnt > 0) {
+ sleep 2;
+ my $ret = $mgm->write("all status", { wait => 2 });
+ $ret or $log->fatal;
+ my $output = $ret->{output};
+ if ($output =~ /\bstarted\b(.|\n)*started\b/i) {
+ $log->put("*** db is started ***")->info;
+ return;
+ }
+ if ($output =~ /\bno.contact\b(.|\n)*no.contact\b/i) {
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $log->put("*** db is dead ***")->fatal;
+ # Maybe try a system restart?
+ }
+ }
+ print "NDBT_ProgramExit: 1 - test error\n";
+ $log->put("*** node recovery failed ***")->fatal;
+}
+sub wait_until_phase4_and_kill {
+# kill db2 as quick as possible when it has reached phase4
+my $cnt = 1000;
+ while (--$cnt > 0) {
+ my $ret = $mgm->write("3 status", {wait => 0});
+ #$ret or log->fatal;
+ my $output = $ret->{output};
+ if ($output =~ /\bphase 4\b/i) {
+ $db2->kill or log->fatal;
+ return;
+ }
+ }
+ print "NDBT_ProgramExit: 1 -test error\n";
+ $log->put("*** node restart failed after 1000 loops ***")->fatal;
+}
+
+my $cnt = 0;
+wait_until_started();
+while (1) {
+ wait_until_started();
+ sleep 2;
+ $db2->kill or $log->fatal;
+ $db2->start; # start the node
+ wait_until_phase4_and_kill();
+ sleep 10;
+ $db2->start;
+}
+
+$db->kill;
+
+# vim: set sw=4:
diff --git a/storage/ndb/test/newtonapi/basic_test/Makefile b/storage/ndb/test/newtonapi/basic_test/Makefile
new file mode 100644
index 00000000000..d7eaf984b12
--- /dev/null
+++ b/storage/ndb/test/newtonapi/basic_test/Makefile
@@ -0,0 +1,25 @@
+include .defs.mk
+
+TYPE := util
+
+PIC_ARCHIVE := Y
+ARCHIVE_TARGET := newtonbasictestcommon
+
+A_LIB := Y
+SO_LIB := Y
+PIC_LIB := Y
+
+LIB_TARGET := NEWTON_BASICTEST_COMMON
+LIB_TARGET_ARCHIVES := $(ARCHIVE_TARGET) NEWTON_API
+
+
+SOURCES = common.cpp
+
+DIRS := basic \
+ ptr_binding \
+ bulk_read
+
+CCFLAGS_LOC := -I$(call fixpath,$(NDB_TOP)/include/util) -I$(call fixpath,$(NDB_TOP)/include/newtonapi)
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/newtonapi/basic_test/basic/Makefile b/storage/ndb/test/newtonapi/basic_test/basic/Makefile
new file mode 100644
index 00000000000..7e2945d2e5f
--- /dev/null
+++ b/storage/ndb/test/newtonapi/basic_test/basic/Makefile
@@ -0,0 +1,14 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := newton_basic
+BIN_TARGET_LIBS :=
+BIN_TARGET_ARCHIVES := NEWTON_BASICTEST_COMMON NEWTON_API
+SOURCES := basic.cpp
+
+CCFLAGS_LOC := -I.. -I$(call fixpath,$(NDB_TOP)/include/util) -I$(call fixpath,$(NDB_TOP)/include/newtonapi) -I$(call fixpath,$(NDB_TOP)/include/portlib)
+
+include $(NDB_TOP)/Epilogue.mk
+
+
diff --git a/storage/ndb/test/newtonapi/basic_test/basic/basic.cpp b/storage/ndb/test/newtonapi/basic_test/basic/basic.cpp
new file mode 100644
index 00000000000..bc33400078d
--- /dev/null
+++ b/storage/ndb/test/newtonapi/basic_test/basic/basic.cpp
@@ -0,0 +1,321 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+
+/****** THIS LINE IS 80 CHARACTERS WIDE - DO *NOT* EXCEED 80 CHARACTERS! ****/
+extern "C" {
+#include <dba.h>
+}
+
+#include "common.hpp"
+
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NdbMain.h>
+
+static const
+DBA_ColumnDesc_t EmpColDesc[] = {
+ { "emp_no", DBA_INT, PCN_SIZE_OF( Employee, EmpNo ), PCN_TRUE },
+ { "first_name", DBA_CHAR, PCN_SIZE_OF( Employee, FirstName ), PCN_FALSE },
+ { "last_name", DBA_CHAR, PCN_SIZE_OF( Employee, LastName ), PCN_FALSE }
+};
+
+static const
+DBA_ColumnDesc_t AddColDesc[] = {
+ { "emp_no", DBA_INT, PCN_SIZE_OF( Address, EmpNo ), PCN_TRUE },
+ { "street_name", DBA_CHAR, PCN_SIZE_OF( Address, StreetName ), PCN_FALSE},
+ { "street_no", DBA_INT, PCN_SIZE_OF( Address, StreetNo ), PCN_FALSE},
+ { "city", DBA_CHAR, PCN_SIZE_OF( Address, City ), PCN_FALSE}
+} ;
+
+static const
+DBA_ColumnBinding_t EmpBindings[] = {
+ DBA_BINDING( "emp_no", DBA_INT, Employee, EmpNo ),
+ DBA_BINDING( "last_name", DBA_CHAR, Employee, LastName ),
+ DBA_BINDING( "first_name", DBA_CHAR, Employee, FirstName)
+};
+
+static const
+DBA_ColumnBinding_t AddBindings[] = {
+ DBA_BINDING( "emp_no", DBA_INT, Address, EmpNo ),
+ DBA_BINDING( "street_name", DBA_CHAR, Address, StreetName ),
+ DBA_BINDING( "street_no", DBA_INT, Address, StreetNo ),
+ DBA_BINDING( "city", DBA_CHAR, Address, City )
+};
+
+static DBA_Binding_t * EmpB;
+static DBA_Binding_t * AddB;
+
+static const int Rows = 6;
+
+static
+Employee_t EMP_TABLE_DATA[] = {
+ { 1242, "Joe", "Dalton" },
+ { 123, "Lucky", "Luke" },
+ { 456, "Averell", "Dalton" },
+ { 8976, "Gaston", "Lagaffe" },
+ { 1122, "Jolly", "Jumper" },
+ { 3211, "Leffe", "Pagrotsky" }
+};
+
+static
+Employee_t EMP_TABLE_DATA_READ[] = {
+ { 1242, "", "" },
+ { 123, "", "" },
+ { 456, "", "" },
+ { 8976, "", "" },
+ { 1122, "", "" },
+ { 3211, "", "" }
+};
+
+static
+Address_t ADD_TABLE_DATA[] = {
+ { 1242, "Lonesome Street", 12, "Crime Town" },
+ { 123, "Pistol Road", 13, "Fort Mount" },
+ { 456, "Banking Blv.", 43, "Las Vegas" },
+ { 8976, "ChancylleZee", 54, "Paris" },
+ { 1122, "Lucky", 111, "Wild West" },
+ { 3211, "Parlament St.", 11, "Stockholm" }
+};
+
+static
+Address_t ADD_TABLE_DATA_READ[] = {
+ { 1242, "", 0, "" },
+ { 123, "", 0, "" },
+ { 456, "", 0, "" },
+ { 8976, "", 0, "" },
+ { 1122, "", 0, "" },
+ { 3211, "", 0, "" }
+};
+
+static const char EMP_TABLE[] = "employees";
+static const char ADD_TABLE[] = "addresses";
+
+static const int EmpNbCol = 3;
+static const int AddNbCol = 4;
+
+static
+void
+DbCreate(void){
+
+ ndbout << "Opening database" << endl;
+ require( DBA_Open() == DBA_NO_ERROR );
+
+ ndbout << "Creating tables" << endl;
+ require( DBA_CreateTable( EMP_TABLE, EmpNbCol, EmpColDesc ) == DBA_NO_ERROR );
+ require( DBA_CreateTable( ADD_TABLE, AddNbCol, AddColDesc ) == DBA_NO_ERROR );
+
+ ndbout << "Checking for table existance" << endl;
+ require( DBA_TableExists( EMP_TABLE ) );
+ require( DBA_TableExists( ADD_TABLE ) );
+}
+
+static
+void
+CreateBindings(void){
+ ndbout << "Creating bindings" << endl;
+
+ EmpB = DBA_CreateBinding(EMP_TABLE,
+ EmpNbCol,
+ EmpBindings,
+ sizeof(Employee_t) );
+ require(EmpB != 0);
+
+ AddB = DBA_CreateBinding(ADD_TABLE,
+ AddNbCol,
+ AddBindings,
+ sizeof(Address_t) );
+ require(AddB != 0);
+}
+
+extern "C" {
+ static void insertCallback( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+ static void deleteCallback( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+ static void updateCallback( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+ static void readCallback ( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+ static void writeCallback ( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+}
+
+static
+void BasicArray(){
+ ndbout << "Testing basic array operations" << endl;
+
+ // Basic insert
+ DBA_ArrayInsertRows(EmpB, EMP_TABLE_DATA, Rows-2, insertCallback);
+ NdbSleep_SecSleep(1);
+ DBA_ArrayReadRows(EmpB, EMP_TABLE_DATA_READ, Rows-2, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows-2, EMP_TABLE_DATA_READ);
+
+ // Basic update
+ AlterRows(EMP_TABLE_DATA, Rows-2);
+ DBA_ArrayUpdateRows(EmpB, EMP_TABLE_DATA, Rows-2, updateCallback);
+ NdbSleep_SecSleep(1);
+ DBA_ArrayReadRows(EmpB, EMP_TABLE_DATA_READ, Rows-2, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows-2, EMP_TABLE_DATA_READ);
+
+ // Basic write
+ AlterRows(EMP_TABLE_DATA, Rows);
+ DBA_ArrayWriteRows(EmpB, EMP_TABLE_DATA, Rows, writeCallback);
+ NdbSleep_SecSleep(1);
+ DBA_ArrayReadRows(EmpB, EMP_TABLE_DATA_READ, Rows, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows, EMP_TABLE_DATA_READ);
+
+ // Basic delete
+ DBA_ArrayDeleteRows(EmpB, EMP_TABLE_DATA, Rows, deleteCallback);
+ NdbSleep_SecSleep(1);
+}
+
+static
+void Multi(){
+ ndbout << "Testing multi operations" << endl;
+
+ const int R2 = Rows + Rows;
+
+ DBA_Binding_t * Bindings[R2];
+ void * DATA[R2];
+ void * DATA_READ[R2];
+ for(int i = 0; i<Rows; i++){
+ Bindings[2*i] = EmpB;
+ Bindings[2*i+1] = AddB;
+
+ DATA[2*i] = &EMP_TABLE_DATA[i];
+ DATA[2*i+1] = &ADD_TABLE_DATA[i];
+
+ DATA_READ[2*i] = &EMP_TABLE_DATA_READ[i];
+ DATA_READ[2*i+1] = &ADD_TABLE_DATA_READ[i];
+ }
+
+ // Basic insert
+ DBA_MultiInsertRow(Bindings, DATA, R2-4, insertCallback);
+ NdbSleep_SecSleep(1);
+ DBA_MultiReadRow (Bindings, DATA_READ, R2-4, readCallback);
+ NdbSleep_SecSleep(1);
+
+ CompareRows(EMP_TABLE_DATA, Rows-2, EMP_TABLE_DATA_READ);
+ CompareRows(ADD_TABLE_DATA, Rows-2, ADD_TABLE_DATA_READ);
+
+ // Basic update
+ AlterRows(EMP_TABLE_DATA, Rows-2);
+ AlterRows(ADD_TABLE_DATA, Rows-2);
+ DBA_MultiUpdateRow(Bindings, DATA, R2-4, updateCallback);
+ NdbSleep_SecSleep(1);
+ DBA_MultiReadRow (Bindings, DATA_READ, R2-4, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows-2, EMP_TABLE_DATA_READ);
+ CompareRows(ADD_TABLE_DATA, Rows-2, ADD_TABLE_DATA_READ);
+
+ // Basic write
+ AlterRows(EMP_TABLE_DATA, Rows);
+ AlterRows(ADD_TABLE_DATA, Rows);
+ DBA_MultiWriteRow(Bindings, DATA, R2, writeCallback);
+ NdbSleep_SecSleep(1);
+ DBA_MultiReadRow (Bindings, DATA_READ, R2, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows, EMP_TABLE_DATA_READ);
+ CompareRows(ADD_TABLE_DATA, Rows, ADD_TABLE_DATA_READ);
+
+ // Basic delete
+ DBA_MultiDeleteRow(Bindings, DATA, R2, deleteCallback);
+ NdbSleep_SecSleep(1);
+}
+
+static
+void BasicPtr(){
+ ndbout << "Testing array of pointer operations" << endl;
+ Employee_t * EmpData[Rows];
+ Employee_t * EmpDataRead[Rows];
+ for(int i = 0; i<Rows; i++){
+ EmpData[i] = &EMP_TABLE_DATA[i];
+ EmpDataRead[i] = &EMP_TABLE_DATA_READ[i];
+ }
+
+ void * const * EMP_TABLE_DATA2 = (void * const *)EmpData;
+ void * const * EMP_TABLE_DATA_READ2 = (void * const *)EmpDataRead;
+
+ // Basic insert
+ DBA_InsertRows(EmpB, EMP_TABLE_DATA2, Rows-2, insertCallback);
+ NdbSleep_SecSleep(1);
+ DBA_ReadRows (EmpB, EMP_TABLE_DATA_READ2, Rows-2, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows-2, EMP_TABLE_DATA_READ);
+
+ // Basic update
+ AlterRows(EMP_TABLE_DATA, Rows-2);
+ DBA_UpdateRows(EmpB, EMP_TABLE_DATA2, Rows-2, updateCallback);
+ NdbSleep_SecSleep(1);
+ DBA_ReadRows (EmpB, EMP_TABLE_DATA_READ2, Rows-2, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows-2, EMP_TABLE_DATA_READ);
+
+ // Basic write
+ AlterRows (EMP_TABLE_DATA, Rows);
+ DBA_WriteRows(EmpB, EMP_TABLE_DATA2, Rows, writeCallback);
+ NdbSleep_SecSleep(1);
+ DBA_ReadRows (EmpB, EMP_TABLE_DATA_READ2, Rows, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows, EMP_TABLE_DATA_READ);
+
+ // Basic delete
+ DBA_DeleteRows(EmpB, EMP_TABLE_DATA2, Rows, deleteCallback);
+ NdbSleep_SecSleep(1);
+}
+
+/*---------------------------------------------------------------------------*/
+NDB_COMMAND(newton_basic, "newton_basic",
+ "newton_basic", "newton_basic", 65535){
+
+ DbCreate();
+ CreateBindings();
+
+ BasicArray();
+ BasicPtr();
+ Multi();
+
+ DBA_Close();
+
+ return 0;
+}
+
+
+
+/**
+ * callbackStatusCheck checks whether or not the operation succeeded
+ */
+void
+callbackStatusCheck( DBA_Error_t status, const char* operation) {
+ ndbout_c("%s: %d", operation, status);
+}
+
+void insertCallback( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "insert");
+}
+void deleteCallback( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "delete");
+}
+void updateCallback( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "update");
+}
+void readCallback ( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "read");
+}
+void writeCallback ( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "write");
+}
+
diff --git a/storage/ndb/test/newtonapi/basic_test/bulk_read/Makefile b/storage/ndb/test/newtonapi/basic_test/bulk_read/Makefile
new file mode 100644
index 00000000000..c45bbad7957
--- /dev/null
+++ b/storage/ndb/test/newtonapi/basic_test/bulk_read/Makefile
@@ -0,0 +1,14 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := newton_br
+BIN_TARGET_LIBS :=
+BIN_TARGET_ARCHIVES := NEWTON_BASICTEST_COMMON NEWTON_API
+SOURCES := br_test.cpp
+
+CCFLAGS_LOC := -I.. -I$(call fixpath,$(NDB_TOP)/include/util) -I$(call fixpath,$(NDB_TOP)/include/newtonapi) -I$(call fixpath,$(NDB_TOP)/include/portlib)
+
+include $(NDB_TOP)/Epilogue.mk
+
+
diff --git a/storage/ndb/test/newtonapi/basic_test/bulk_read/br_test.cpp b/storage/ndb/test/newtonapi/basic_test/bulk_read/br_test.cpp
new file mode 100644
index 00000000000..4120cfba864
--- /dev/null
+++ b/storage/ndb/test/newtonapi/basic_test/bulk_read/br_test.cpp
@@ -0,0 +1,262 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+
+/****** THIS LINE IS 80 CHARACTERS WIDE - DO *NOT* EXCEED 80 CHARACTERS! ****/
+extern "C" {
+#include <dba.h>
+}
+
+#include "common.hpp"
+
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NdbMain.h>
+
+static const
+DBA_ColumnDesc_t EmpColDesc[] = {
+ { "emp_no", DBA_INT, PCN_SIZE_OF( Employee, EmpNo ), PCN_TRUE },
+ { "first_name", DBA_CHAR, PCN_SIZE_OF( Employee, FirstName ), PCN_FALSE },
+ { "last_name", DBA_CHAR, PCN_SIZE_OF( Employee, LastName ), PCN_FALSE }
+};
+
+static const
+DBA_ColumnDesc_t AddColDesc[] = {
+ { "emp_no", DBA_INT, PCN_SIZE_OF( Address, EmpNo ), PCN_TRUE },
+ { "street_name", DBA_CHAR, PCN_SIZE_OF( Address, StreetName ), PCN_FALSE},
+ { "street_no", DBA_INT, PCN_SIZE_OF( Address, StreetNo ), PCN_FALSE},
+ { "city", DBA_CHAR, PCN_SIZE_OF( Address, City ), PCN_FALSE}
+} ;
+
+static const
+DBA_ColumnBinding_t EmpBindings[] = {
+ DBA_BINDING( "emp_no", DBA_INT, Employee, EmpNo ),
+ DBA_BINDING( "last_name", DBA_CHAR, Employee, LastName ),
+ DBA_BINDING( "first_name", DBA_CHAR, Employee, FirstName)
+};
+
+static const
+DBA_ColumnBinding_t AddBindings[] = {
+ DBA_BINDING( "emp_no", DBA_INT, Address, EmpNo ),
+ DBA_BINDING( "street_name", DBA_CHAR, Address, StreetName ),
+ DBA_BINDING( "street_no", DBA_INT, Address, StreetNo ),
+ DBA_BINDING( "city", DBA_CHAR, Address, City )
+};
+
+static DBA_Binding_t * EmpB;
+static DBA_Binding_t * AddB;
+
+static const int Rows = 6;
+
+static
+Employee_t EMP_TABLE_DATA[] = {
+ { 1242, "Joe", "Dalton" },
+ { 123, "Lucky", "Luke" },
+ { 456, "Averell", "Dalton" },
+ { 8976, "Gaston", "Lagaffe" },
+ { 1122, "Jolly", "Jumper" },
+ { 3211, "Leffe", "Pagrotsky" }
+};
+
+static
+Employee_t EMP_TABLE_DATA_READ[] = {
+ { 1242, "", "" },
+ { 123, "", "" },
+ { 456, "", "" },
+ { 8976, "", "" },
+ { 1122, "", "" },
+ { 3211, "", "" }
+};
+
+static
+Address_t ADD_TABLE_DATA[] = {
+ { 1242, "Lonesome Street", 12, "Crime Town" },
+ { 123, "Pistol Road", 13, "Fort Mount" },
+ { 456, "Banking Blv.", 43, "Las Vegas" },
+ { 8976, "ChancylleZee", 54, "Paris" },
+ { 1122, "Lucky", 111, "Wild West" },
+ { 3211, "Parlament St.", 11, "Stockholm" }
+};
+
+static
+Address_t ADD_TABLE_DATA_READ[] = {
+ { 1242, "", 0, "" },
+ { 123, "", 0, "" },
+ { 456, "", 0, "" },
+ { 8976, "", 0, "" },
+ { 1122, "", 0, "" },
+ { 3211, "", 0, "" }
+};
+
+static const char EMP_TABLE[] = "employees";
+static const char ADD_TABLE[] = "addresses";
+
+static const int EmpNbCol = 3;
+static const int AddNbCol = 4;
+
+static
+void
+DbCreate(void){
+
+ ndbout << "Opening database" << endl;
+ require( DBA_Open() == DBA_NO_ERROR );
+
+ ndbout << "Creating tables" << endl;
+ require( DBA_CreateTable( EMP_TABLE, EmpNbCol, EmpColDesc ) == DBA_NO_ERROR );
+ require( DBA_CreateTable( ADD_TABLE, AddNbCol, AddColDesc ) == DBA_NO_ERROR );
+
+ ndbout << "Checking for table existance" << endl;
+ require( DBA_TableExists( EMP_TABLE ) );
+ require( DBA_TableExists( ADD_TABLE ) );
+}
+
+static
+void
+CreateBindings(void){
+ ndbout << "Creating bindings" << endl;
+
+ EmpB = DBA_CreateBinding(EMP_TABLE,
+ EmpNbCol,
+ EmpBindings,
+ sizeof(Employee_t) );
+ require(EmpB != 0);
+
+ AddB = DBA_CreateBinding(ADD_TABLE,
+ AddNbCol,
+ AddBindings,
+ sizeof(Address_t) );
+ require(AddB != 0);
+}
+
+int
+CountRows(DBA_BulkReadResultSet_t * rs, int count){
+ int res = 0;
+ for(int i = 0; i<count; i++)
+ if(rs[i].RowFoundIndicator)
+ res++;
+ return res;
+}
+
+extern "C" {
+ static void insertCallback( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+ static void deleteCallback( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+ static void updateCallback( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+ static void readCallback ( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+ static void writeCallback ( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+}
+
+static
+void Multi(){
+ ndbout << "Testing multi operations" << endl;
+
+ DBA_ArrayInsertRows(EmpB, EMP_TABLE_DATA, Rows-2, insertCallback);
+ DBA_ArrayInsertRows(AddB, ADD_TABLE_DATA, Rows-2, insertCallback);
+ NdbSleep_SecSleep(1);
+
+ const int R2 = Rows + Rows;
+
+ DBA_Binding_t * Bindings[2];
+ DBA_BulkReadResultSet_t DataRead[R2];
+
+ Bindings[0] = EmpB;
+ Bindings[1] = AddB;
+
+ for(int i = 0; i<Rows; i++)
+ DataRead[i].DataPtr = &EMP_TABLE_DATA_READ[i];
+
+ for(int i = 0; i<Rows; i++)
+ DataRead[i+Rows].DataPtr = &ADD_TABLE_DATA_READ[i];
+
+ NdbSleep_SecSleep(1);
+
+ DBA_BulkMultiReadRows(Bindings, DataRead, 2, Rows, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows-2, EMP_TABLE_DATA_READ);
+ CompareRows(ADD_TABLE_DATA, Rows-2, ADD_TABLE_DATA_READ);
+
+ require(CountRows(DataRead, R2) == (R2-4));
+
+ // Basic delete
+ DBA_ArrayDeleteRows(EmpB, EMP_TABLE_DATA, Rows-2, deleteCallback);
+ DBA_ArrayDeleteRows(AddB, ADD_TABLE_DATA, Rows-2, deleteCallback);
+ NdbSleep_SecSleep(1);
+}
+
+static
+void BasicPtr(){
+ ndbout << "Testing array of pointer operations" << endl;
+
+ // Basic insert
+ DBA_ArrayInsertRows(EmpB, EMP_TABLE_DATA, Rows-2, insertCallback);
+ NdbSleep_SecSleep(1);
+
+ DBA_BulkReadResultSet_t EmpDataRead[Rows];
+ for(int i = 0; i<Rows; i++){
+ EmpDataRead[i].DataPtr = &EMP_TABLE_DATA_READ[i];
+ }
+
+ DBA_BulkReadRows(EmpB, EmpDataRead, Rows, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows-2, EMP_TABLE_DATA_READ);
+ require(CountRows(EmpDataRead, Rows) == (Rows-2));
+
+ // Basic delete
+ DBA_ArrayDeleteRows(EmpB, EMP_TABLE_DATA, Rows-2, deleteCallback);
+ NdbSleep_SecSleep(1);
+}
+
+/*---------------------------------------------------------------------------*/
+NDB_COMMAND(newton_br, "newton_br",
+ "newton_br", "newton_br", 65535){
+
+ DbCreate();
+ CreateBindings();
+
+ BasicPtr();
+ Multi();
+
+ DBA_Close();
+
+ return 0;
+}
+
+
+
+/**
+ * callbackStatusCheck checks whether or not the operation succeeded
+ */
+void
+callbackStatusCheck( DBA_Error_t status, const char* operation) {
+ ndbout_c("%s: %d", operation, status);
+}
+
+void insertCallback( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "insert");
+}
+void deleteCallback( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "delete");
+}
+void updateCallback( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "update");
+}
+void readCallback ( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "read");
+}
+void writeCallback ( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "write");
+}
+
diff --git a/storage/ndb/test/newtonapi/basic_test/common.cpp b/storage/ndb/test/newtonapi/basic_test/common.cpp
new file mode 100644
index 00000000000..d4c4e6a74a7
--- /dev/null
+++ b/storage/ndb/test/newtonapi/basic_test/common.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 "common.hpp"
+
+NdbOut &
+operator << (NdbOut & out, const Employee_t & emp){
+ out << emp.EmpNo << " \"" << emp.FirstName << "\" \""
+ << emp.LastName << "\"";
+ return out;
+}
+
+bool
+operator==(const Employee_t & e1, const Employee_t & e2){
+ if(e1.EmpNo != e2.EmpNo)
+ return false;
+ if(strcmp(e1.FirstName, e2.FirstName) != 0)
+ return false;
+ return strcmp(e1.LastName, e2.LastName) == 0;
+}
+
+void
+Alter(Employee_t & emp){
+ static int updown = 0;
+ if(updown == 0){
+ for(int i = 0; i<strlen(emp.FirstName); i++)
+ toupper(emp.FirstName[i]);
+
+ for(int i = 0; i<strlen(emp.LastName); i++)
+ toupper(emp.LastName[i]);
+ } else {
+ for(int i = 0; i<strlen(emp.FirstName); i++)
+ tolower(emp.FirstName[i]);
+
+ for(int i = 0; i<strlen(emp.LastName); i++)
+ tolower(emp.LastName[i]);
+ }
+ updown = 1 - updown;
+}
+
+void
+CompareRows(Employee_t * data1,
+ int rows,
+ Employee_t * data2){
+ for(int i = 0; i<rows; i++){
+ if(!(data1[i] == data2[i])){
+ ndbout << data1[i] << endl
+ << data2[i] << endl;
+ }
+ }
+}
+
+void
+AlterRows(Employee_t * data1, int rows){
+ for(int i = 0; i<rows; i++){
+ Alter(data1[i]);
+ }
+}
+
+inline
+NdbOut &
+operator << (NdbOut & out, const Address_t & adr){
+ out << adr.EmpNo << " \"" << adr.StreetName << "\" "
+ << adr.StreetNo << " \"" << adr.City << "\"";
+ return out;
+}
+
+inline
+bool
+operator==(const Address_t & a1, const Address_t & a2){
+ if(a1.EmpNo != a2.EmpNo)
+ return false;
+ if(a1.StreetNo != a2.StreetNo)
+ return false;
+ if(strcmp(a1.StreetName, a2.StreetName) != 0)
+ return false;
+ return strcmp(a1.City, a2.City) == 0;
+}
+
+inline
+void
+Alter(Address_t & emp){
+ static int updown = 0;
+ if(updown == 0){
+ for(int i = 0; i<strlen(emp.StreetName); i++)
+ toupper(emp.StreetName[i]);
+
+ for(int i = 0; i<strlen(emp.City); i++)
+ toupper(emp.City[i]);
+ } else {
+ for(int i = 0; i<strlen(emp.StreetName); i++)
+ tolower(emp.StreetName[i]);
+
+ for(int i = 0; i<strlen(emp.City); i++)
+ tolower(emp.City[i]);
+ }
+ emp.StreetNo *= emp.EmpNo;
+ updown = 1 - updown;
+}
+
+void
+CompareRows(Address_t * data1,
+ int rows,
+ Address_t * data2){
+ for(int i = 0; i<rows; i++){
+ if(!(data1[i] == data2[i])){
+ ndbout << data1[i] << endl
+ << data2[i] << endl;
+ }
+ }
+}
+
+void
+AlterRows(Address_t * data1, int rows){
+ for(int i = 0; i<rows; i++){
+ Alter(data1[i]);
+ }
+}
+
diff --git a/storage/ndb/test/newtonapi/basic_test/common.hpp b/storage/ndb/test/newtonapi/basic_test/common.hpp
new file mode 100644
index 00000000000..0df8f7e078d
--- /dev/null
+++ b/storage/ndb/test/newtonapi/basic_test/common.hpp
@@ -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 */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <ndb_global.h>
+
+extern "C" {
+#include <dba.h>
+}
+
+#include <NdbOut.hpp>
+
+typedef struct Employee {
+ UInt32_t EmpNo;
+ char FirstName[24];
+ char LastName[24];
+
+ struct Address * EmployeeAddress;
+} Employee_t;
+
+typedef struct Address {
+ UInt32_t EmpNo;
+ char StreetName[24];
+ UInt32_t StreetNo;
+ char City[12];
+} Address_t;
+
+/**
+ * Employee functions
+ */
+NdbOut & operator << (NdbOut & out, const Employee_t & emp);
+bool operator==(const Employee_t & e1, const Employee_t & e2);
+void Alter(Employee_t & emp);
+void CompareRows(Employee_t * data1, int rows, Employee_t * data2);
+void AlterRows(Employee_t * data1, int rows);
+
+/**
+ * Address functions
+ */
+NdbOut & operator << (NdbOut & out, const Address_t & adr);
+bool operator==(const Address_t & a1, const Address_t & a2);
+void Alter(Address_t & emp);
+void CompareRows(Address_t * data1, int rows, Address_t * data2);
+void AlterRows(Address_t * data1, int rows);
+
+inline void require(bool test){
+ if(!test)
+ abort();
+}
+
+#endif
diff --git a/storage/ndb/test/newtonapi/basic_test/ptr_binding/Makefile b/storage/ndb/test/newtonapi/basic_test/ptr_binding/Makefile
new file mode 100644
index 00000000000..95e87d47e62
--- /dev/null
+++ b/storage/ndb/test/newtonapi/basic_test/ptr_binding/Makefile
@@ -0,0 +1,14 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := newton_pb
+BIN_TARGET_LIBS :=
+BIN_TARGET_ARCHIVES := NEWTON_BASICTEST_COMMON NEWTON_API
+SOURCES := ptr_binding_test.cpp
+
+CCFLAGS_LOC := -I.. -I$(call fixpath,$(NDB_TOP)/include/util) -I$(call fixpath,$(NDB_TOP)/include/newtonapi) -I$(call fixpath,$(NDB_TOP)/include/portlib)
+
+include $(NDB_TOP)/Epilogue.mk
+
+
diff --git a/storage/ndb/test/newtonapi/basic_test/ptr_binding/ptr_binding_test.cpp b/storage/ndb/test/newtonapi/basic_test/ptr_binding/ptr_binding_test.cpp
new file mode 100644
index 00000000000..2c9cee5be87
--- /dev/null
+++ b/storage/ndb/test/newtonapi/basic_test/ptr_binding/ptr_binding_test.cpp
@@ -0,0 +1,264 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+
+/****** THIS LINE IS 80 CHARACTERS WIDE - DO *NOT* EXCEED 80 CHARACTERS! ****/
+extern "C" {
+#include <dba.h>
+}
+
+#include "common.hpp"
+
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NdbMain.h>
+
+static const
+DBA_ColumnDesc_t ColDesc[] = {
+ { "emp_no", DBA_INT, PCN_SIZE_OF( Employee, EmpNo ), PCN_TRUE },
+ { "first_name", DBA_CHAR, PCN_SIZE_OF( Employee, FirstName ), PCN_FALSE },
+ { "last_name", DBA_CHAR, PCN_SIZE_OF( Employee, LastName ), PCN_FALSE },
+ { "street_name",DBA_CHAR, PCN_SIZE_OF( Address, StreetName ), PCN_FALSE },
+ { "street_no", DBA_INT, PCN_SIZE_OF( Address, StreetNo ), PCN_FALSE },
+ { "city", DBA_CHAR, PCN_SIZE_OF( Address, City ), PCN_FALSE }
+};
+static const int NbCol = 6;
+
+static const
+DBA_ColumnBinding_t AddBindings[] = {
+ //DBA_BINDING( "emp_no", DBA_INT, Address, EmpNo ),
+ DBA_BINDING( "street_name", DBA_CHAR, Address, StreetName ),
+ DBA_BINDING( "street_no", DBA_INT, Address, StreetNo ),
+ DBA_BINDING( "city", DBA_CHAR, Address, City )
+};
+
+static const
+int AddBindingRows = sizeof(AddBindings)/sizeof(DBA_ColumnBinding_t);
+
+static const
+DBA_ColumnBinding_t EmpBindings[] = {
+ DBA_BINDING( "emp_no", DBA_INT, Employee, EmpNo ),
+ DBA_BINDING( "last_name", DBA_CHAR, Employee, LastName ),
+ DBA_BINDING( "first_name", DBA_CHAR, Employee, FirstName),
+ DBA_BINDING_PTR(Employee, EmployeeAddress, AddBindings, AddBindingRows)
+};
+static const
+int EmpBindingRows = sizeof(EmpBindings)/sizeof(DBA_ColumnBinding_t);
+
+static DBA_Binding_t * Bind;
+
+static const int Rows = 6;
+
+static
+Address_t ADD_TABLE_DATA[] = {
+ { 1242, "Lonesome Street", 12, "Crime Town" },
+ { 123, "Pistol Road", 13, "Fort Mount" },
+ { 456, "Banking Blv.", 43, "Las Vegas" },
+ { 8976, "ChancylleZee", 54, "Paris" },
+ { 1122, "Lucky", 111, "Wild West" },
+ { 3211, "Parlament St.", 11, "Stockholm" }
+};
+
+static
+Address_t ADD_TABLE_DATA_READ[] = {
+ { 1242, "", 0, "" },
+ { 123, "", 0, "" },
+ { 456, "", 0, "" },
+ { 8976, "", 0, "" },
+ { 1122, "", 0, "" },
+ { 3211, "", 0, "" }
+};
+
+static
+Employee_t EMP_TABLE_DATA[] = {
+ { 1242, "Joe", "Dalton" , &ADD_TABLE_DATA[0] },
+ { 123, "Lucky", "Luke" , &ADD_TABLE_DATA[1] },
+ { 456, "Averell", "Dalton" , &ADD_TABLE_DATA[2] },
+ { 8976, "Gaston", "Lagaffe", &ADD_TABLE_DATA[3] },
+ { 1122, "Jolly", "Jumper" , &ADD_TABLE_DATA[4] },
+ { 3211, "Leffe", "Pagrotsky", &ADD_TABLE_DATA[5] },
+};
+
+static
+Employee_t EMP_TABLE_DATA_READ[] = {
+ { 1242, "", "", &ADD_TABLE_DATA_READ[0] },
+ { 123, "", "", &ADD_TABLE_DATA_READ[1] },
+ { 456, "", "", &ADD_TABLE_DATA_READ[2] },
+ { 8976, "", "", &ADD_TABLE_DATA_READ[3] },
+ { 1122, "", "", &ADD_TABLE_DATA_READ[4] },
+ { 3211, "", "", &ADD_TABLE_DATA_READ[5] }
+};
+
+static const char TABLE[] = "employee_address";
+
+static
+void
+DbCreate(void){
+
+ ndbout << "Opening database" << endl;
+ require( DBA_Open() == DBA_NO_ERROR );
+
+ ndbout << "Creating tables" << endl;
+ require( DBA_CreateTable( TABLE, NbCol, ColDesc ) == DBA_NO_ERROR );
+
+ ndbout << "Checking for table existance" << endl;
+ require( DBA_TableExists( TABLE ) );
+}
+
+static
+void
+CreateBindings(void){
+ ndbout << "Creating bindings" << endl;
+
+ Bind = DBA_CreateBinding(TABLE,
+ EmpBindingRows,
+ EmpBindings,
+ sizeof(Employee_t) );
+
+ require(Bind != 0);
+}
+
+extern "C" {
+ static void insertCallback( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+ static void deleteCallback( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+ static void updateCallback( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+ static void readCallback ( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+ static void writeCallback ( DBA_ReqId_t, DBA_Error_t, DBA_ErrorCode_t );
+}
+
+static
+void BasicArray(){
+ ndbout << "Testing basic array operations" << endl;
+
+ // Basic insert
+ DBA_ArrayInsertRows(Bind, EMP_TABLE_DATA, Rows-2, insertCallback);
+ NdbSleep_SecSleep(1);
+ DBA_ArrayReadRows (Bind, EMP_TABLE_DATA_READ, Rows-2, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows-2, EMP_TABLE_DATA_READ);
+ CompareRows(ADD_TABLE_DATA, Rows-2, ADD_TABLE_DATA_READ);
+
+ // Basic update
+ AlterRows (EMP_TABLE_DATA, Rows-2);
+ AlterRows (ADD_TABLE_DATA, Rows-2);
+ DBA_ArrayUpdateRows(Bind, EMP_TABLE_DATA, Rows-2, updateCallback);
+ NdbSleep_SecSleep(1);
+ DBA_ArrayReadRows (Bind, EMP_TABLE_DATA_READ, Rows-2, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows-2, EMP_TABLE_DATA_READ);
+ CompareRows(ADD_TABLE_DATA, Rows-2, ADD_TABLE_DATA_READ);
+
+ // Basic write
+ AlterRows (EMP_TABLE_DATA, Rows);
+ AlterRows (ADD_TABLE_DATA, Rows);
+ DBA_ArrayWriteRows(Bind, EMP_TABLE_DATA, Rows, writeCallback);
+ NdbSleep_SecSleep(1);
+ DBA_ArrayReadRows (Bind, EMP_TABLE_DATA_READ, Rows, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows, EMP_TABLE_DATA_READ);
+ CompareRows(ADD_TABLE_DATA, Rows, ADD_TABLE_DATA_READ);
+
+ // Basic delete
+ DBA_ArrayDeleteRows(Bind, EMP_TABLE_DATA, Rows, deleteCallback);
+ NdbSleep_SecSleep(1);
+}
+
+static
+void BasicPtr(){
+ ndbout << "Testing array of pointer operations" << endl;
+ Employee_t * EmpData[Rows];
+ Employee_t * EmpDataRead[Rows];
+ for(int i = 0; i<Rows; i++){
+ EmpData[i] = &EMP_TABLE_DATA[i];
+ EmpDataRead[i] = &EMP_TABLE_DATA_READ[i];
+ }
+
+ void * const * EMP_TABLE_DATA2 = (void * const *)EmpData;
+ void * const * EMP_TABLE_DATA_READ2 = (void * const *)EmpDataRead;
+
+ // Basic insert
+ DBA_InsertRows(Bind, EMP_TABLE_DATA2, Rows-2, insertCallback);
+ NdbSleep_SecSleep(1);
+ DBA_ReadRows (Bind, EMP_TABLE_DATA_READ2, Rows-2, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows-2, EMP_TABLE_DATA_READ);
+ CompareRows(ADD_TABLE_DATA, Rows-2, ADD_TABLE_DATA_READ);
+
+ // Basic update
+ AlterRows (ADD_TABLE_DATA, Rows-2);
+ AlterRows (EMP_TABLE_DATA, Rows-2);
+ DBA_UpdateRows(Bind, EMP_TABLE_DATA2, Rows-2, updateCallback);
+ NdbSleep_SecSleep(1);
+ DBA_ReadRows (Bind, EMP_TABLE_DATA_READ2, Rows-2, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows-2, EMP_TABLE_DATA_READ);
+ CompareRows(ADD_TABLE_DATA, Rows-2, ADD_TABLE_DATA_READ);
+
+ // Basic write
+ AlterRows (ADD_TABLE_DATA, Rows);
+ AlterRows (EMP_TABLE_DATA, Rows);
+ DBA_WriteRows(Bind, EMP_TABLE_DATA2, Rows, writeCallback);
+ NdbSleep_SecSleep(1);
+ DBA_ReadRows (Bind, EMP_TABLE_DATA_READ2, Rows, readCallback);
+ NdbSleep_SecSleep(1);
+ CompareRows(EMP_TABLE_DATA, Rows, EMP_TABLE_DATA_READ);
+ CompareRows(ADD_TABLE_DATA, Rows, ADD_TABLE_DATA_READ);
+
+ // Basic delete
+ DBA_DeleteRows(Bind, EMP_TABLE_DATA2, Rows, deleteCallback);
+ NdbSleep_SecSleep(1);
+}
+
+/*---------------------------------------------------------------------------*/
+NDB_COMMAND(newton_pb, "newton_pb",
+ "newton_pb", "newton_pb", 65535){
+
+ DbCreate();
+ CreateBindings();
+
+ BasicArray();
+ BasicPtr();
+
+ DBA_Close();
+
+ return 0;
+}
+
+/**
+ * callbackStatusCheck checks whether or not the operation succeeded
+ */
+void
+callbackStatusCheck( DBA_Error_t status, const char* operation) {
+ ndbout_c("%s: %d", operation, status);
+}
+
+void insertCallback( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "insert");
+}
+void deleteCallback( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "delete");
+}
+void updateCallback( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "update");
+}
+void readCallback ( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "read");
+}
+void writeCallback ( DBA_ReqId_t, DBA_Error_t s, DBA_ErrorCode_t ){
+ callbackStatusCheck(s, "write");
+}
+
diff --git a/storage/ndb/test/newtonapi/basic_test/too_basic.cpp b/storage/ndb/test/newtonapi/basic_test/too_basic.cpp
new file mode 100644
index 00000000000..883aacf8841
--- /dev/null
+++ b/storage/ndb/test/newtonapi/basic_test/too_basic.cpp
@@ -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 */
+
+
+
+/****** THIS LINE IS 80 CHARACTERS WIDE - DO *NOT* EXCEED 80 CHARACTERS! ****/
+
+#include <ndb_global.h>
+#include <NdbOut.hpp>
+
+//#include <cfg/cfg_db.h>
+//#include <init/init_start_restart.h>
+//#include "pcn_types.h"
+//#include <testing/testing.h>
+
+extern "C" {
+#include <cfg_db.h>
+}
+
+typedef struct Employee {
+
+ UInt32_t EmpNo;
+ char FirstName[22];
+ char LastName[22];
+
+} Employee_t;
+#define CHECK_DB_CALL( Call ) \
+ CheckDbCall( Call, #Call, __FILE__, __LINE__ )
+
+
+
+/* --- Exported functions --- */
+
+/*---------------------------------------------------------------------------*/
+int main() {
+
+
+ char EMP_TABLE_NAME[] = "employees";
+
+ Employee_t t;
+
+ CFG_DbColumnDesc_t EmpColDesc[] = {
+ { "first_name", CFG_DB_CHAR, PCN_SIZE_OF( Employee, FirstName ),
+ PCN_FALSE },
+ { "emp_no", CFG_DB_INT, PCN_SIZE_OF( Employee, EmpNo ), PCN_TRUE },
+ { "last_name", CFG_DB_CHAR, PCN_SIZE_OF( Employee, LastName ),
+ PCN_FALSE },
+ };
+
+ int EmpNbCol = 3;
+
+
+
+ CFG_DbColumnBinding_t ColBindings[] = {
+ CFG_DB_BINDING( "last_name", CFG_DB_CHAR, Employee, LastName ),
+ CFG_DB_BINDING( "emp_no", CFG_DB_INT, Employee, EmpNo ),
+ CFG_DB_BINDING( "first_name", CFG_DB_CHAR, Employee, FirstName)
+ };
+
+
+ Employee_t EMP_TABLE_DATA[] = {
+ { 1242, "Joe", "Dalton" },
+ { 123, "Lucky", "Luke" },
+ { 456, "Averell", "Dalton" },
+ { 8976, "Gaston", "Lagaffe" }
+ };
+
+
+ char* DbName;
+
+ DbName = NULL;
+
+
+ // On Linux: will destroy the table to start from a clean slate.
+
+ CFG_DbDestroy();
+ CFG_DbOpen( &DbName ) ;
+ CFG_DbCreateTable( EMP_TABLE_NAME,
+ EmpNbCol, EmpColDesc );
+
+ CFG_DbTableExists( EMP_TABLE_NAME );
+
+ //#ifndef CELLO_PLATFORM
+ //CHECK_DB_CALL( CFG_DbDumpSchema( stdout ) );
+ //#endif
+
+ CFG_DbClose();
+ // INIT_StopSystem();
+
+}
+
+
diff --git a/storage/ndb/test/newtonapi/perf_test/Makefile b/storage/ndb/test/newtonapi/perf_test/Makefile
new file mode 100644
index 00000000000..2be004d4277
--- /dev/null
+++ b/storage/ndb/test/newtonapi/perf_test/Makefile
@@ -0,0 +1,14 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := newton_perf
+BIN_TARGET_LIBS :=
+BIN_TARGET_ARCHIVES := NEWTON_API
+SOURCES := perf.cpp
+
+CCFLAGS_LOC := -I$(call fixpath,$(NDB_TOP)/include/util) -I$(call fixpath,$(NDB_TOP)/include/newtonapi) -I$(call fixpath,$(NDB_TOP)/include/portlib)
+
+include $(NDB_TOP)/Epilogue.mk
+
+
diff --git a/storage/ndb/test/newtonapi/perf_test/perf.cpp b/storage/ndb/test/newtonapi/perf_test/perf.cpp
new file mode 100644
index 00000000000..7b818e93a2a
--- /dev/null
+++ b/storage/ndb/test/newtonapi/perf_test/perf.cpp
@@ -0,0 +1,647 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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>
+
+extern "C" {
+#include <dba.h>
+}
+
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NdbTimer.hpp>
+#include <NDBT_Stats.hpp>
+#include <NDBT_ReturnCodes.h>
+#include <NdbMain.h>
+#include <time.h>
+
+#undef min
+#undef max
+
+static const int NP_Insert = 0;
+static const int NP_Update = 1;
+static const int NP_WriteUpdate = 2;
+static const int NP_WriteInsert = 3;
+static const int NP_Delete = 4;
+static const int NP_BulkRead = 5;
+static const int NP_MAX = 5;
+
+static const char * Operations[] = {
+ "Insert ",
+ "Update ",
+ "WriteUpd",
+ "WriteIns",
+ "Delete ",
+ "BulkRead"
+};
+
+/**
+ * Configuration variables
+ */
+static int NoOfTransactions = 10000;
+static int ParallellTransactions = 1000;
+static int OperationsPerTransaction = 10;
+static int NoOfColumns = 20;
+static int BytesPerInsert = 300;
+static int BytesPerUpdate = 200;
+static int LoopCount = 10;
+
+/**
+ * Global variables
+ */
+static char TableName[255];
+static DBA_ColumnDesc_t * ColumnDescriptions;
+static DBA_ColumnBinding_t * InsertBindings;
+static DBA_ColumnBinding_t * UpdateBindings; static int UpdateBindingColumns;
+static DBA_ColumnBinding_t * DeleteBindings;
+
+static char * TestData;
+static DBA_Binding_t * InsertB;
+static DBA_Binding_t * UpdateB;
+static DBA_Binding_t * DeleteB;
+
+/**
+ * Function prototypes
+ */
+static void sequence(int loops);
+
+inline void * getPtr(int rowNo) { return TestData+rowNo*BytesPerInsert;}
+inline void setPK(int rowNo, int pk){ * (int *)getPtr(rowNo) = pk; }
+
+static void SetupTestData();
+static void CleanupTestData();
+
+static bool CreateTable();
+static bool CleanTable();
+static bool CreateBindings();
+
+static void usage();
+
+static
+void
+usage(){
+ int ForceSend, Interval;
+ DBA_GetParameter(0, &Interval);
+ DBA_GetParameter(3, &ForceSend);
+
+ ndbout << "newtonPerf" << endl
+ << " -n Transactions per loop and operation ("
+ << NoOfTransactions << ")" << endl
+ << " -p parallell transactions (" << ParallellTransactions << ")"
+ << endl
+ << " -o operations per transaction (" << OperationsPerTransaction
+ << ")" << endl
+ << " -a no of columns (" << NoOfColumns << ")" << endl
+ << " -b Table size in bytes (" << BytesPerInsert << ")" << endl
+ << " -u Bytes per update (" << BytesPerUpdate << ")" << endl
+ << " -l Loop count (" << LoopCount << ")" << endl
+ << " -i Interval (" << Interval << "ms)" << endl
+ << " -f Force send algorithm (" << ForceSend << ")" << endl
+ << " -h Help" << endl;
+
+}
+
+static
+bool
+parseArgs(int argc, const char **argv){
+ bool a = false, b = false, u = false;
+
+ for(int i = 1; i<argc; i++){
+ if(argv[i][0] != '-'){
+ ndbout << "Invalid argument: " << argv[i] << endl;
+ return false;
+ }
+
+ if(argv[i][1] == 'h')
+ return false;
+
+ if(i == argc-1){
+ ndbout << "Expecting argument to " << argv[i] << endl;
+ return false;
+ }
+
+ switch(argv[i][1]){
+ case 'n':
+ NoOfTransactions = atoi(argv[i+1]);
+ break;
+ case 'p':
+ ParallellTransactions = atoi(argv[i+1]);
+ break;
+ case 'o':
+ OperationsPerTransaction = atoi(argv[i+1]);
+ break;
+ case 'a':
+ NoOfColumns = atoi(argv[i+1]);
+ a = true;
+ break;
+ case 'b':
+ BytesPerInsert = atoi(argv[i+1]);
+ b = true;
+ break;
+ case 'u':
+ BytesPerUpdate = atoi(argv[i+1]);
+ u = true;
+ break;
+ case 'l':
+ LoopCount = atoi(argv[i+1]);
+ break;
+ case 'f':
+ {
+ const int val = atoi(argv[i+1]);
+ if(DBA_SetParameter(3, val) != DBA_NO_ERROR){
+ ndbout << "Invalid force send algorithm: "
+ << DBA_GetLatestErrorMsg()
+ << "(" << DBA_GetLatestError() << ")" << endl;
+ return false;
+ }
+ }
+ break;
+ case 'i':
+ {
+ const int val = atoi(argv[i+1]);
+ if(DBA_SetParameter(0, val) != DBA_NO_ERROR){
+ ndbout << "Invalid NBP interval: "
+ << DBA_GetLatestErrorMsg()
+ << "(" << DBA_GetLatestError() << ")" << endl;
+ return false;
+ }
+ }
+ break;
+ default:
+ ndbout << "Invalid option: " << argv[i] << endl;
+ return false;
+ }
+ i++;
+ }
+ if(a && !b) BytesPerInsert = 15 * NoOfColumns;
+ if(!a && b) NoOfColumns = ((BytesPerInsert + 14) / 15)+1;
+
+ if(!u)
+ BytesPerUpdate = (2 * BytesPerInsert) / 3;
+
+ bool t = true;
+ if(NoOfColumns < 2) t = false;
+ if(BytesPerInsert < 8) t = false;
+ if(BytesPerUpdate < 8) t = false;
+
+ if(!t){
+ ndbout << "Invalid arguments combination of -a -b -u not working out"
+ << endl;
+ return false;
+ }
+ return true;
+}
+
+NDB_COMMAND(newton_perf, "newton_perf",
+ "newton_perf", "newton_perf", 65535){
+
+ if(!parseArgs(argc, argv)){
+ usage();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ ndbout << "-----------" << endl;
+ usage();
+ ndbout << endl;
+
+ SetupTestData();
+
+ DBA_Open();
+
+ if(!CreateTable()){
+ DBA_Close();
+ CleanupTestData();
+ return 0;
+ }
+
+ if(!CreateBindings()){
+ DBA_Close();
+ CleanupTestData();
+ return 0;
+ }
+
+ CleanTable();
+
+ sequence(LoopCount);
+
+ DBA_Close();
+ CleanupTestData();
+
+ DBA_DestroyBinding(InsertB);
+ DBA_DestroyBinding(UpdateB);
+ DBA_DestroyBinding(DeleteB);
+}
+
+static
+void
+ErrorMsg(const char * s){
+ ndbout << s
+ << ": " << DBA_GetLatestError() << "-" << DBA_GetLatestErrorMsg()
+ << ", " << DBA_GetLatestNdbError()
+ << endl;
+}
+
+static
+int
+m4(int i){
+ const int j = i - (i & 3);
+ return j;
+}
+
+static
+void
+SetupTestData(){
+ ndbout << "Creating testdata" << endl;
+
+ ColumnDescriptions = new DBA_ColumnDesc_t[NoOfColumns];
+ InsertBindings = new DBA_ColumnBinding_t[NoOfColumns];
+
+ const int sz = m4((BytesPerInsert - ((NoOfColumns+1)/2)*4)/(NoOfColumns/2));
+ int sum = 0;
+ UpdateBindingColumns = 0;
+ for(int i = 0; i<NoOfColumns; i++){
+ char tmp[16];
+ if((i % 2) == 0){
+ sprintf(tmp, "I%d", i);
+ ColumnDescriptions[i].DataType = DBA_INT;
+ ColumnDescriptions[i].Size = 4;
+ sum += 4;
+ } else {
+ sprintf(tmp, "S%d", i);
+ ColumnDescriptions[i].DataType = DBA_CHAR;
+ ColumnDescriptions[i].Size = sz;
+ sum += sz;
+ }
+ ColumnDescriptions[i].IsKey = 0;
+ ColumnDescriptions[i].Name = strdup(tmp);
+
+ InsertBindings[i].Name = strdup(tmp);
+ InsertBindings[i].DataType = ColumnDescriptions[i].DataType;
+ InsertBindings[i].Size = ColumnDescriptions[i].Size;
+ InsertBindings[i].Offset = sum - ColumnDescriptions[i].Size;
+ InsertBindings[i].Ptr = 0;
+
+ if(sum <= BytesPerUpdate)
+ UpdateBindingColumns++;
+ }
+ if(UpdateBindingColumns == 1)
+ UpdateBindingColumns++;
+
+ ColumnDescriptions[0].IsKey = 1;
+
+ assert(sum <= BytesPerInsert);
+ sprintf(TableName, "NEWTON_%d_%d", sum, NoOfColumns);
+
+ UpdateBindings = new DBA_ColumnBinding_t[UpdateBindingColumns];
+ memcpy(UpdateBindings, InsertBindings,
+ UpdateBindingColumns*sizeof(DBA_ColumnBinding_t));
+
+ DeleteBindings = new DBA_ColumnBinding_t[1];
+ memcpy(DeleteBindings, InsertBindings,
+ 1*sizeof(DBA_ColumnBinding_t));
+
+ TestData = (char *)malloc(NoOfTransactions *
+ OperationsPerTransaction * BytesPerInsert);
+
+ assert(TestData != 0);
+ for(int i = 0; i<NoOfTransactions; i++)
+ for(int j = 0; j<OperationsPerTransaction; j++){
+ const int pk = i * OperationsPerTransaction + j;
+ setPK(pk, pk);
+ }
+}
+
+static
+void
+CleanupTestData(){
+ free(TestData);
+ for(int i = 0; i<NoOfColumns; i++){
+ free((char*)ColumnDescriptions[i].Name);
+ free((char*)InsertBindings[i].Name);
+ }
+ delete [] ColumnDescriptions;
+ delete [] InsertBindings;
+ delete [] UpdateBindings;
+ delete [] DeleteBindings;
+}
+
+
+static bool CleanReturnValue = true;
+static int CleanCallbacks = 0;
+static int CleanRows = 0;
+
+extern "C"
+void
+CleanCallback(DBA_ReqId_t reqId, DBA_Error_t error, DBA_ErrorCode_t ec){
+ CleanCallbacks++;
+ if(error == DBA_NO_ERROR)
+ CleanRows++;
+}
+
+static
+bool
+CleanTable(){
+ ndbout << "Cleaning table..." << flush;
+ CleanReturnValue = true;
+ CleanCallbacks = 0;
+ CleanRows = 0;
+ for(int i = 0; i<NoOfTransactions * OperationsPerTransaction; i++){
+ DBA_ArrayDeleteRows(DeleteB,
+ getPtr(i), 1,
+ CleanCallback);
+ while((i-CleanCallbacks)>ParallellTransactions)
+ NdbSleep_MilliSleep(100);
+ }
+ while(CleanCallbacks != (NoOfTransactions * OperationsPerTransaction))
+ NdbSleep_SecSleep(1);
+
+ ndbout << CleanRows << " rows deleted" << endl;
+
+ return CleanReturnValue;
+}
+
+static
+bool
+CreateBindings(){
+ ndbout << "Creating bindings" << endl;
+ InsertB = UpdateB = DeleteB = 0;
+
+ InsertB = DBA_CreateBinding(TableName, NoOfColumns,
+ InsertBindings, BytesPerInsert);
+ if(InsertB == 0){
+ ErrorMsg("Failed to create insert bindings");
+ return false;
+ }
+
+ UpdateB = DBA_CreateBinding(TableName, UpdateBindingColumns,
+ UpdateBindings, BytesPerInsert);
+ if(UpdateB == 0){
+ ErrorMsg("Failed to create update bindings");
+ DBA_DestroyBinding(InsertB);
+ return false;
+ }
+
+ DeleteB = DBA_CreateBinding(TableName, 1,
+ DeleteBindings, BytesPerInsert);
+ if(DeleteB == 0){
+ ErrorMsg("Failed to create delete bindings");
+ DBA_DestroyBinding(InsertB);
+ DBA_DestroyBinding(UpdateB);
+ return false;
+ }
+ return true;
+}
+
+static
+bool
+CreateTable(){
+ ndbout << "Creating " << TableName << endl;
+ return DBA_CreateTable( TableName,
+ NoOfColumns,
+ ColumnDescriptions ) == DBA_NO_ERROR;
+}
+
+/**
+ *
+ */
+static NdbTimer SequenceTimer;
+
+static int CurrentOp = NP_Insert;
+static int SequenceSent = 0;
+static int SequenceRecv = 0;
+static NDBT_Stats SequenceStats[NP_MAX][4];
+static NDBT_Stats SequenceLatency[NP_MAX];
+
+static int HashMax;
+static DBA_ReqId_t * ReqHash; // ReqId - Latency/Row
+static int * ReqHashPos; // (row in StartTime)
+
+static int SequenceLatencyPos;
+static NDB_TICKS * StartTime;
+
+static
+inline
+int
+computeHashMax(int elements){
+ HashMax = 1;
+ while(HashMax < elements)
+ HashMax *= 2;
+
+ if(HashMax < 1024)
+ HashMax = 1024;
+
+ return HashMax;
+}
+
+static
+inline
+int
+hash(DBA_ReqId_t request){
+ int r = (request >> 2) & (HashMax-1);
+ return r;
+}
+
+static
+inline
+void
+addRequest(DBA_ReqId_t request, int pos){
+
+ int i = hash(request);
+
+ while(ReqHash[i] != 0)
+ i = ((i + 1) & (HashMax-1));
+
+ ReqHash[i] = request;
+ ReqHashPos[i] = pos;
+}
+
+static
+inline
+int
+getRequest(DBA_ReqId_t request){
+
+ int i = hash(request);
+
+ while(ReqHash[i] != request)
+ i = ((i + 1) & (HashMax-1));
+
+ ReqHash[i] = 0;
+
+ return ReqHashPos[i];
+}
+
+extern "C"
+void
+SequenceCallback(DBA_ReqId_t reqId, DBA_Error_t error, DBA_ErrorCode_t ec){
+ int p = getRequest(reqId) - 1;
+
+ if(error != DBA_NO_ERROR){
+ ndbout << "p = " << p << endl;
+ ndbout << "DBA_GetErrorMsg(" << error << ") = "
+ << DBA_GetErrorMsg(error) << endl;
+ ndbout << "DBA_GetNdbErrorMsg(" << ec << ") = "
+ << DBA_GetNdbErrorMsg(ec) << endl;
+
+ assert(error == DBA_NO_ERROR);
+ }
+
+ SequenceRecv++;
+ if(SequenceRecv == NoOfTransactions){
+ SequenceTimer.doStop();
+ }
+
+ if((p & 127) == 127){
+ NDB_TICKS t = NdbTick_CurrentMillisecond() - StartTime[p];
+ SequenceLatency[CurrentOp].addObservation(t);
+ }
+}
+
+typedef DBA_ReqId_t (* DBA_ArrayFunction)( const DBA_Binding_t* pBindings,
+ const void * pData,
+ int NbRows,
+ DBA_AsyncCallbackFn_t CbFunc );
+
+inline
+int
+min(int a, int b){
+ return a > b ? b : a;
+}
+
+static
+void
+SequenceOp(DBA_ArrayFunction func, const DBA_Binding_t* pBindings, int op){
+ SequenceSent = 0;
+ SequenceRecv = 0;
+ SequenceLatencyPos = 1;
+ CurrentOp = op;
+
+ SequenceTimer.doStart();
+ for(int i = 0; i<NoOfTransactions; ){
+ const int l1 = ParallellTransactions - (SequenceSent - SequenceRecv);
+ const int l2 = min(NoOfTransactions - i, l1);
+ for(int j = 0; j<l2; j++){
+ const DBA_ReqId_t r = func(pBindings,
+ getPtr(i*OperationsPerTransaction),
+ OperationsPerTransaction,
+ SequenceCallback);
+ assert(r != 0);
+ SequenceSent++;
+ addRequest(r, i + 1);
+ i++;
+
+ if((SequenceSent & 127) == 127){
+ NDB_TICKS t = NdbTick_CurrentMillisecond();
+ StartTime[i] = t;
+ }
+ }
+ if(l2 == 0)
+ NdbSleep_MilliSleep(10);
+ }
+
+ while(SequenceRecv != SequenceSent)
+ NdbSleep_SecSleep(1);
+
+ ndbout << "Performed " << NoOfTransactions << " " << Operations[op]
+ << " in ";
+
+ double p = NoOfTransactions * 1000;
+ double t = SequenceTimer.elapsedTime();
+ double o = p * OperationsPerTransaction;
+
+ p /= t;
+ o /= t;
+
+ int _p = p;
+ int _o = o;
+
+ double b = 0;
+
+ switch(op){
+ case NP_Insert:
+ case NP_WriteInsert:
+ case NP_WriteUpdate:
+ case NP_BulkRead:
+ b = BytesPerInsert;
+ break;
+ case NP_Update:
+ b = BytesPerUpdate;
+ break;
+ case NP_Delete:
+ b = 4;
+ break;
+ default:
+ b = 0;
+ }
+ b *= NoOfTransactions * OperationsPerTransaction;
+ b /= t;
+ int _b = b;
+
+ SequenceStats[op][0].addObservation(t);
+ SequenceStats[op][1].addObservation(p);
+ SequenceStats[op][2].addObservation(o);
+ SequenceStats[op][3].addObservation(b);
+
+ int t2 = SequenceStats[op][0].getMean();
+ int p2 = SequenceStats[op][1].getMean();
+ int o2 = SequenceStats[op][2].getMean();
+ int b2 = SequenceStats[op][3].getMean();
+
+ ndbout << SequenceTimer.elapsedTime() << "(" << t2 << ")ms";
+ ndbout << " -> " << _p << "(" << p2 << ") T/s - " << _o
+ << "(" << o2 << ") O/s - " << _b << "(" << b2 << ") Kb/s" << endl;
+
+ ndbout << " Latency (ms) Avg: " << (int)SequenceLatency[op].getMean()
+ << " min: " << (int)SequenceLatency[op].getMin()
+ << " max: " << (int)SequenceLatency[op].getMax()
+ << " stddev: " << (int)SequenceLatency[op].getStddev()
+ << " n: " << SequenceLatency[op].getCount() << endl;
+}
+
+/**
+ * Sequence
+ */
+static
+void
+sequence(int loops){
+ computeHashMax(ParallellTransactions);
+ ReqHash = new DBA_ReqId_t[HashMax];
+ ReqHashPos = new int[HashMax];
+ StartTime = new NDB_TICKS[NoOfTransactions];
+
+ for(int i = 0; i<NP_MAX; i++){
+ SequenceLatency[i].reset();
+ for(int j = 0; j<4; j++)
+ SequenceStats[i][j].reset();
+ }
+ for(int i = 0; i<loops; i++){
+ ndbout << "Loop #" << (i+1) << endl;
+ SequenceOp(DBA_ArrayInsertRows, InsertB, NP_Insert);
+
+ // BulkRead
+
+ SequenceOp(DBA_ArrayUpdateRows, UpdateB, NP_Update);
+ SequenceOp(DBA_ArrayWriteRows, InsertB, NP_WriteUpdate);
+ SequenceOp(DBA_ArrayDeleteRows, DeleteB, NP_Delete);
+ SequenceOp(DBA_ArrayWriteRows, InsertB, NP_WriteInsert);
+ SequenceOp(DBA_ArrayDeleteRows, DeleteB, NP_Delete);
+ ndbout << "-------------------" << endl << endl;
+ }
+
+ delete [] ReqHash;
+ delete [] ReqHashPos;
+ delete [] StartTime;
+}
diff --git a/storage/ndb/test/odbc/SQL99_test/Makefile b/storage/ndb/test/odbc/SQL99_test/Makefile
new file mode 100644
index 00000000000..3ac06016670
--- /dev/null
+++ b/storage/ndb/test/odbc/SQL99_test/Makefile
@@ -0,0 +1,26 @@
+include .defs.mk
+
+TYPE = odbcdriver
+
+BIN_TARGET = SQL99_test
+
+SOURCES = SQL99_test.cpp
+
+CCFLAGS_LOC += -I/usr/local/include \
+ -I$(NDB_TOP)/test/include \
+ -I$(NDB_TOP)/include \
+ -I$(NDB_TOP)/src/client/odbc/common
+
+
+CCFLAGS_WARNINGS += -Wno-unused
+
+LIBS_SPEC += \
+ -lNDBT \
+ -lodbc \
+ -lodbcinst \
+ -lportlib
+
+
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/odbc/SQL99_test/SQL99_test.cpp b/storage/ndb/test/odbc/SQL99_test/SQL99_test.cpp
new file mode 100644
index 00000000000..eda9ff33834
--- /dev/null
+++ b/storage/ndb/test/odbc/SQL99_test/SQL99_test.cpp
@@ -0,0 +1,2138 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+// ODBC.cpp : Defines the entry point for the console application.
+//
+
+#include "SQL99_test.h"
+#include <iostream> // Loose later
+
+using namespace std; //
+
+#define MAXCOL 64
+#define DEFCOL 4
+
+#define MAXROW 64
+#define DEFROW 8
+
+#define MAXTHREADS 24
+#define DEFTHREADS 2
+
+#define MAXTABLES 16
+#define DEFTABLES 2
+
+#define MAXLOOPS 100000
+#define DEFLOOPS 4
+
+#define UPDATE_VALUE 7
+
+#define PKSIZE 2
+
+
+static int nNoOfThreads = 1 ;
+static int nNoOfCol = 4 ;
+static int nNoOfRows = 2 ;
+static int nNoOfLoops = 0 ;
+static int nNoOfTables = 2 ;
+static int nAPI = 0 ;
+static int tAttributeSize = sizeof(char) ;
+static attr_type AttributeType = T_CHAR ;
+static int nAggregate = 0 ;
+static int nArithmetic = 0 ;
+static int nPrint = 0 ;
+static int nColList = 0 ;
+static char szColNames[MAXCOL*MAX_COL_NAME] = { 0 } ;
+int createTables(char* szTableName, int nTables) ;
+
+
+/*************************************************
+Function: main - the entry point
+*************************************************/
+int main(int argc, char* argv[]){
+
+ int nRetrunValue = NDBT_FAILED ;
+ SQLRETURN rc = SQL_ERROR ;
+ double dResultA = 0 ;
+ double dResultB = 0 ;
+ double dInput = 0 ;
+ int x = 0, y = 0 ;
+ int* pIntRefBuffer = NULL ;
+ float* pFloatRefBuffer = NULL ;
+ double* pDoubleRefBuffer = NULL ;
+ char* pCharRefBuffer = NULL ;
+ char szColBuffer[MAX_COL_NAME] = { 0 } ;
+
+
+ ParseArguments(argc, (const char**)argv) ;
+
+ PARAMS* pparams = (PARAMS*)malloc(sizeof(PARAMS)*nNoOfThreads) ;
+ memset(pparams, 0, (sizeof(PARAMS)*nNoOfThreads)) ;
+
+ char* szTableNames = (char*)malloc(sizeof(char)*nNoOfTables*MAX_TABLE_NAME) ;
+ memset(szTableNames, 0, sizeof(char)*nNoOfTables*MAX_TABLE_NAME) ;
+
+ UintPtr pThreadHandles[MAXTHREADS] = { NULL } ;
+
+ AssignTableNames(szTableNames, nNoOfTables) ;
+
+ if(nAPI){
+ if(0 != createTables(szTableNames, nNoOfTables)){
+ printf("Failed to create tables through NDB API; quitting...\n") ;
+ NDBT_ProgramExit(NDBT_FAILED) ;
+ return NDBT_FAILED ;
+ }
+ }else{
+
+ //CreateDemoTables(szTableNames, nNoOfTables, DROP) ;
+ rc = CreateDemoTables(szTableNames, nNoOfTables, CREATE) ;
+ if(!(SQL_SUCCESS == rc || SQL_SUCCESS_WITH_INFO == rc)){
+ printf("Failed to create tables, quiting now.\n") ;
+ NDBT_ProgramExit(NDBT_FAILED) ;
+ return NDBT_FAILED ;
+ }
+ }
+
+ // Store column names in the buffer for use in some stmts
+ int k = 0 ;
+ for(;;){
+ memset((char*)szColBuffer, 0, strlen(szColBuffer)) ;
+ sprintf((char*)szColBuffer, "COL%d", k) ;
+ strcat((char*)szColNames, (char*)szColBuffer) ;
+ ++k ;
+ if( k == nNoOfCol ){
+ break ;
+ }
+ strcat((char*)szColNames, ", ") ;
+ } // for
+
+
+ switch(AttributeType){
+ case T_INT:
+ pIntRefBuffer = (int*)malloc(sizeof(int)*nNoOfRows*nNoOfCol*nNoOfThreads) ;
+ memset(pIntRefBuffer, 0, sizeof(int)*nNoOfRows*nNoOfCol*nNoOfThreads) ;
+ AssignRefNumValues(pIntRefBuffer, T_INT, nPrint) ;
+ StartThreads(pparams, (void*)pIntRefBuffer, nNoOfTables, szTableNames, AttributeType, pThreadHandles) ;
+ break ;
+ case T_FLOAT:
+ pFloatRefBuffer = (float*)malloc(sizeof(float)*nNoOfRows*nNoOfCol*nNoOfThreads) ;
+ memset(pFloatRefBuffer, 0, sizeof(float)*nNoOfRows*nNoOfCol*nNoOfThreads) ;
+ AssignRefNumValues(pFloatRefBuffer, T_FLOAT, nPrint) ;
+ StartThreads(pparams, (void*)pFloatRefBuffer, nNoOfTables, szTableNames, AttributeType, pThreadHandles) ;
+ break ;
+/* case T_DOUBLE:
+ pDoubleRefBuffer = (double*)malloc(sizeof(double)*nNoOfRows*nNoOfCol*nNoOfThreads) ;
+ memset(pDoubleRefBuffer, 0, sizeof(double)*nNoOfRows*nNoOfCol*nNoOfThreads) ;
+ AssignRefNumValues(pDoubleRefBuffer, T_DOUBLE, 0) ;
+ StartThreads(pparams, (void*)pDoubleRefBuffer, nNoOfTables, szTableNames, AttributeType, pThreadHandles) ;
+ break ;
+*/
+ case T_CHAR:
+ pCharRefBuffer = (char*)malloc(sizeof(char)*nNoOfRows*nNoOfCol*nNoOfThreads*MAX_CHAR_ATTR_LEN) ;
+ memset(pCharRefBuffer, 0, sizeof(char)*nNoOfRows*nNoOfCol*nNoOfThreads*MAX_CHAR_ATTR_LEN) ;
+ AssignRefCharValues(pCharRefBuffer, nPrint ) ;
+ StartThreads(pparams, (void*)pCharRefBuffer, nNoOfTables, szTableNames, AttributeType, pThreadHandles) ;
+ break ;
+ default:
+ break ;
+ }
+
+ NdbThread_SetConcurrencyLevel(nNoOfThreads + 2) ;
+
+
+ printf("\nPerforming inserts...") ;
+ SetThreadOperationType(pparams, T_INSERT) ;
+ if(0 < WaitForThreads(pparams)){
+ printf("\t\t%d thread(s) failed\n") ;
+ }else{
+ printf("\t\tdone\n") ;
+ }
+ printf("----------------------\n\n") ;
+ PrintAll(szTableNames, nNoOfTables, AttributeType) ;
+
+ printf("\nVerifying inserts...") ;
+ SetThreadOperationType(pparams, T_READ_VERIFY) ;
+ if(0 < WaitForThreads(pparams)){
+ printf("\t\t%d thread(s) failed\n") ;
+ }else{
+ printf("\t\tdone\n") ;
+ }
+ printf("----------------------\n\n") ;
+
+ printf("\nPerforming updates...") ;
+ SetThreadOperationType(pparams, T_UPDATE) ;
+ if(0 < WaitForThreads(pparams)){
+ printf("\t\t%d thread(s) failed\n") ;
+ }else{
+ printf("\t\tdone\n") ;
+ }
+ printf("----------------------\n\n") ;
+ //PrintAll(szTableNames, nNoOfTables, AttributeType) ;
+
+ printf("\nVerifying updates...") ;
+ SetThreadOperationType(pparams, T_READ_VERIFY) ;
+ if(0 < WaitForThreads(pparams)){
+ printf("\t\t%d thread(s) failed\n") ;
+ }else{
+ printf("\t\tdone\n") ;
+ }
+ printf("----------------------\n\n") ;
+
+ printf("\nPerforming reads...") ;
+ SetThreadOperationType(pparams, T_READ) ;
+ if(0 < WaitForThreads(pparams)){
+ printf("\t\t%d thread(s) failed\n") ;
+ }else{
+ printf("\t\tdone\n") ;
+ }
+ printf("----------------------\n\n") ;
+ PrintAll(szTableNames, nNoOfTables, AttributeType) ;
+
+
+ if(T_CHAR != AttributeType && nAggregate){
+ printf("\nTesting aggregate functions for each table\n\n") ;
+ printf("FN\tCOLUMN\tVALUE\t\t\tTOTAL ROWS WHERE\n\t\t\t\t\tVALUE(S) > VALUE\n--------------------------------------------------------\n\n") ;
+
+ for(y = 0 ; y < nNoOfTables ; ++y){
+ for(x = 0; x < nNoOfCol ; ++x){
+ dResultA = dResultB = 0 ;
+ AggregateFn(FN_MIN, (char*)(szTableNames + MAX_TABLE_NAME*y), x, NULL, &dResultA, AttributeType) ;
+ AggregateFn(FN_COUNT, (char*)(szTableNames + MAX_TABLE_NAME*y) , x, &dResultA, &dResultB, AttributeType) ;
+ ATTR_TYPE_SWITCH_AGR("MIN", x, dResultA, dResultB, AttributeType) ;
+ }
+ }
+
+ for(y = 0; y < nNoOfTables ; ++y){
+ for(x = 0; x < nNoOfCol ; ++x){
+ dResultA = dResultB = 0 ;
+ AggregateFn(FN_MAX, (char*)(szTableNames + MAX_TABLE_NAME*y), x, NULL, &dResultA, AttributeType) ;
+ AggregateFn(FN_COUNT, (char*)(szTableNames + MAX_TABLE_NAME*y), x, &dResultA, &dResultB, AttributeType) ;
+ ATTR_TYPE_SWITCH_AGR("MAX", x, dResultA, dResultB, AttributeType) ;
+ }
+ }
+
+ for(y = 0 ; y < nNoOfTables ; ++y){
+ for(x = 0; x < nNoOfCol ; ++x){
+ dResultA = dResultB = 0 ;
+ AggregateFn(FN_AVG, (char*)(szTableNames + MAX_TABLE_NAME*y), x, NULL, &dResultA, AttributeType) ;
+ AggregateFn(FN_COUNT, (char*)(szTableNames + MAX_TABLE_NAME*y), x, &dResultA, &dResultB, AttributeType) ;
+ ATTR_TYPE_SWITCH_AGR("AVG", x, dResultA, dResultB, AttributeType)
+ }
+ }
+
+ printf("--------------------------------------------------------\n\n") ;
+ }
+
+ if(T_CHAR != AttributeType && nArithmetic){
+
+ float nVal = (rand() % 10) /1.82342 ;
+
+ for(int h = 0 ; h < nNoOfTables ; ++h){
+
+ printf("\nTesting arithmetic operators\nfor each column in %s:\n----------------------\n", (char*)(szTableNames + MAX_TABLE_NAME*sizeof(char)*h) ) ;
+
+ printf("\nOperator [ * ]... \t\t") ;
+ ArithOp((char*)(szTableNames + MAX_TABLE_NAME*sizeof(char)*h), nNoOfCol, &nVal, AttributeType, MULTI) ;
+ printf("done\n") ;
+
+ printf("\nOperator [ / ]... \t\t") ;
+ ArithOp((char*)(szTableNames + MAX_TABLE_NAME*sizeof(char)*h), nNoOfCol, &nVal, AttributeType, DIVIDE) ;
+ printf("done\n") ;
+
+ printf("\nOperator [ + ]... \t\t") ;
+ ArithOp((char*)(szTableNames + MAX_TABLE_NAME*sizeof(char)*h), nNoOfCol, &nVal, AttributeType, PLUS) ;
+ printf("done\n") ;
+
+ printf("\nOperator [ - ]... \t\t") ;
+ ArithOp((char*)(szTableNames + MAX_TABLE_NAME*sizeof(char)*h), nNoOfCol, &nVal, AttributeType, MINUS) ;
+ printf("done\n\n") ;
+ /*
+ printf("\nOperator [ % ]... \t\t") ;
+ ArithOp((char*)szTableNames, nNoOfCol, &nVal, AttributeType, MODULO) ;
+ printf("done\n\n") ;
+ */
+ }
+ }
+/*
+ printf("\nPerforming deletes...") ;
+ SetThreadOperationType(pparams, T_DELETE) ;
+ if(0 < WaitForThreads(pparams)){
+ printf("\t\t%d thread(s) failed\n") ;
+ }else{
+ printf("\t\tdone\n") ;
+ }
+ printf("----------------------\n\n") ;
+
+ printf("\nVerifying deletes...") ;
+ SetThreadOperationType(pparams, T_DELETE_VERIFY) ;
+ if(0 < WaitForThreads(pparams)){
+ printf("\t\t%d thread(s) failed\n") ;
+ }else{
+ printf("\t\tdone\n") ;
+ }
+ printf("----------------------\n\n") ;
+*/
+ StopThreads(pparams, pThreadHandles) ;
+
+ //PrintAll(szTableNames, nNoOfTables, AttributeType) ;
+
+ //CreateDemoTables(szTableNames, nNoOfTables, DROP) ;
+
+ free((void*)szTableNames) ;
+ free((void*)pparams) ;
+ free((void*)pIntRefBuffer) ;
+ free((void*)pFloatRefBuffer) ;
+ free((void*)pDoubleRefBuffer) ;
+ free((void*)pCharRefBuffer) ;
+
+ return 0;
+}
+
+
+
+/**************************************************
+Function: ParseArguments
+***************************************************/
+void ParseArguments(int argc, const char** argv){
+
+ int i = 1;
+
+ while (argc > 1){
+
+ if (strcmp(argv[i], "-t") == 0)
+ {
+ nNoOfThreads = atoi(argv[i+1]);
+ if ((nNoOfThreads < 1) || (nNoOfThreads > MAXTHREADS))
+ nNoOfThreads = DEFTHREADS ;
+ }
+ else if (strcmp(argv[i], "-c") == 0)
+ {
+ nNoOfCol = atoi(argv[i+1]);
+ if ((nNoOfCol < 2) || (nNoOfCol > MAXCOL))
+ nNoOfCol = DEFCOL ;
+ }
+ else if (strcmp(argv[i], "-l") == 0)
+ {
+ nNoOfLoops = atoi(argv[i+1]);
+ if ((nNoOfLoops < 0) || (nNoOfLoops > MAXLOOPS))
+ nNoOfLoops = DEFLOOPS ;
+ }
+ else if (strcmp(argv[i], "-r") == 0)
+ {
+ nNoOfRows = atoi(argv[i+1]);;
+ if ((nNoOfRows < 0) || (nNoOfRows > MAXROW))
+ nNoOfRows = DEFROW ;
+ }
+ else if (strcmp(argv[i], "-m") == 0)
+ {
+ nArithmetic = 1 ;
+ argc++ ;
+ i-- ;
+ }
+ else if (strcmp(argv[i], "-g") == 0)
+ {
+ nAggregate = 1 ;
+ argc++ ;
+ i-- ;
+ }
+ else if (strcmp(argv[i], "-n") == 0)
+ {
+ nAPI = 1 ;
+ argc++ ;
+ i-- ;
+ }
+ else if (strcmp(argv[i], "-v") == 0)
+ {
+ nPrint = 1 ;
+ argc++ ;
+ i-- ;
+ }
+ else if (strcmp(argv[i], "-a") == 0)
+ {
+ if(strcmp(argv[i+1], "int") == 0){
+ AttributeType = T_INT ;
+ tAttributeSize = 32 ;
+ }else if(strcmp(argv[i+1], "float") == 0){
+ AttributeType = T_FLOAT ;
+ tAttributeSize = 64 ;
+ }else if(strcmp(argv[i+1], "char") == 0){
+ AttributeType = T_CHAR ;
+ }
+ }
+ else
+ {
+ cout << "Arguments:\n";
+ cout << "-n Create tables using NDB API (vs ODBC by default)" << endl;
+ cout << "-t Number of threads; maximum 24, default 2\n" << endl;
+ cout << "-c Number of columns per table; maximum 64, default 4\n" << endl;
+ cout << "-r Number of rows; maximum 64, default 8\n" << endl;
+ cout << "-a Type of attribute to use: int, double or char; default int " << endl;
+ cout << "-g Test aggregate functions" << endl;
+ cout << "-m Test arithmetic operators" << endl;
+ cout << "-v Print executed statements" << endl;
+ exit(-1);
+ }
+
+ argc -= 2 ;
+ i = i + 2 ;
+ }
+
+char *szAttrType[MAX_STR_LEN] = { 0 } ;
+switch(AttributeType){
+ case T_INT:
+ strcpy((char*)szAttrType, "Integer") ;
+ break ;
+ case T_FLOAT:
+ strcpy((char*)szAttrType, "Float") ;
+ break ;
+/* case T_DOUBLE:
+ strcpy((char*)szAttrType, "Double") ;
+ break ;
+*/
+ case T_CHAR:
+ strcpy((char*)szAttrType, "Character") ;
+ break ;
+ default:
+ strcpy((char*)szAttrType, "Not defined") ;
+ break ;
+ }
+
+
+printf("\n\nCurrent parameters: %d thread(s), %d tables, %d rows, %d colums, attribute type: %s\n\n", nNoOfThreads, nNoOfTables, nNoOfRows, nNoOfCol, szAttrType) ;
+ }
+
+
+
+
+/*************************************************
+Function: ThreadFnInt - thread function
+for int attributes
+*************************************************/
+void* ThreadFnInt(void* pparams){
+
+ SQLRETURN retcode = SQL_ERROR ;
+ SQLCHAR szStmtBuffer[MAX_SQL_STMT] = { 0 } ;
+ SQLCHAR szValueBuffer[MAX_VALUE_LEN] = { 0 } ;
+ SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
+ SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
+ SQLINTEGER cbInt = 0 ;
+ ODBC_HANDLES stHandles ;
+ memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
+
+ int r = 0, j = 0 ;
+ //Get thread parameters
+ PARAMS* p = (PARAMS*)pparams ;
+ int* pRef = (int*)p->pThreadRef ;
+
+ int* pBindBuffer = (int*)malloc(sizeof(int)*nNoOfCol) ;
+
+ //printf("Thread #%d\n", p->nThreadID) ;
+
+ retcode = GetHandles(&stHandles, GET, 0) ;
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ) {
+ p->report_status = S_STARTED ;
+ }else{
+ printf("Thread #%d failed to allocate handles, exiting now.\n", p->nThreadID) ;
+ free((void*)pBindBuffer) ;
+ p->nError = 1 ;
+ p->report_status = S_EXIT ;
+ return 0 ;
+ }
+
+ //p->report_status = S_STARTED ;
+
+ //Main thread loop
+ for(;;){
+
+ while(S_IDLE == p->thread_status){
+ NdbSleep_MilliSleep(1) ;
+ }
+
+ if(S_STOP == p->thread_status) {
+ break ;
+ }else{
+ p->thread_status = S_BUSY ;
+ }
+
+ switch(p->op_type){
+
+
+ /************************************** T_INSERT case **************************************/
+ case T_INSERT:
+
+ for(r = 0 ; r < nNoOfRows ; ++r){
+
+ if(!nColList){
+ sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ;
+ }else{
+ sprintf((char*)szStmtBuffer, "INSERT INTO %s (%s) VALUES(", p->szTableName, szColNames) ;
+ }
+
+ //sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ;
+
+ for(j = 0 ;;){
+ sprintf((char*)szValueBuffer,"%d", pRef[nNoOfCol*r + j]) ;
+ strncat((char*)szStmtBuffer, (char*)szValueBuffer, strlen((char*)szValueBuffer)) ;
+ ++j ;
+ if(nNoOfCol == j) break ;
+ strcat((char*)szStmtBuffer, ", ") ;
+ }
+ strcat((char*)szStmtBuffer, ")") ;
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
+ if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ }else{
+ p->nError = 1 ;
+ printf("INSERT in thread #%d failed\n", p->nThreadID) ;
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+ }
+ break ;
+
+
+ /************************************** T_READ case **************************************/
+ case T_READ:
+
+ for(r = 0 ; r < nNoOfRows ; r++){
+
+ sprintf((char*)szStmtBuffer, "SELECT * FROM %s WHERE COL0 = %d", p->szTableName, pRef[nNoOfCol*r]) ;
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ ODBC_FN(SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmtBuffer, SQL_NTS), retcode) ;
+
+ for(j = 0 ; j < nNoOfCol ; ++j){
+ ODBC_FN(SQLBindCol(stHandles.hstmt, (j+1), SQL_C_SLONG, (void*)&pBindBuffer[j], sizeof(SQLINTEGER), &cbInt), retcode) ;
+ }
+
+ for (;;) {
+ retcode = SQLFetch(stHandles.hstmt);
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ for(int k = 0 ; k < nNoOfCol ; ++k){
+ if(p->nVerifyFlag){
+ if(pBindBuffer[k] != pRef[nNoOfCol*r + k])
+ printf("Expected: %d Actual: %d\n", pBindBuffer[k], pRef[nNoOfCol*r + k]) ;
+ }
+ }
+ }else if(SQL_NO_DATA == retcode){
+ break ;
+ }else{
+ p->nError = 1 ;
+ printf("READ in thread #%d failed\n", p->nThreadID) ;
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+ //printf("\n") ;
+ }
+ SQLCloseCursor(stHandles.hstmt) ;
+ }
+ break ;
+
+
+ /************************************** T_UPDATE case **************************************/
+ case T_UPDATE:
+ for(r = 0 ; r < nNoOfRows ; ++r){
+
+ sprintf((char*)szStmtBuffer, "UPDATE %s SET ", p->szTableName) ;
+ for(j = 1 ;;){
+ pRef[nNoOfCol*r + j] = pRef[nNoOfCol*r + j] + UPDATE_VALUE ;
+ sprintf((char*)szColBuffer,"COL%d = %d", j, pRef[nNoOfCol*r + j]) ;
+ strncat((char*)szStmtBuffer, (char*)szColBuffer, strlen((char*)szColBuffer)) ;
+ ++j ;
+ if(nNoOfCol == j) break ;
+ strcat((char*)szStmtBuffer, ", ") ;
+ }
+ sprintf((char*)szAuxBuffer, " WHERE COL0 = %d ;", pRef[nNoOfCol*r]) ;
+ strcat((char*)szStmtBuffer, (char*)szAuxBuffer);
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
+
+ if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ }else{
+ p->nError = 1 ;
+ printf("UPDATE in thread %d failed\n", p->nThreadID) ;
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+ }
+ break ;
+
+
+ /************************************** T_DELETE case **************************************/
+ case T_DELETE:
+ for(r = 0 ; r < nNoOfRows ; ++r){
+ sprintf((char*)szStmtBuffer, "DELETE * FROM %s WHERE COL0 = %d", p->szTableName, pRef[nNoOfCol*r]) ;
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
+ if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ }else if( 1 == p->nVerifyFlag && SQL_NO_DATA != retcode){
+ p->nError = 1 ;
+ printf("\nVerification failed: the row found\n") ;
+ }else{
+ p->nError = 1 ;
+ printf("INSERT in thread %d failed\n", p->nThreadID) ;
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+
+ }
+ break ;
+
+
+ /************************************** default case **************************************/
+ default:
+ break ;
+ }//switch
+p->thread_status = S_IDLE ;
+ } //for
+
+free((void*)pBindBuffer) ;
+GetHandles(&stHandles, FREE, 0) ;
+p->thread_status = S_EXIT ;
+return 0 ;
+ };
+
+
+
+/*************************************************
+Function: ThreadFnFloat - thread function
+for float attributes
+*************************************************/
+void* ThreadFnFloat(void* pparams){
+
+ SQLRETURN retcode = SQL_ERROR ;
+ SQLCHAR szStmtBuffer[MAX_SQL_STMT] = { 0 } ;
+ SQLCHAR szValueBuffer[MAX_VALUE_LEN] = { 0 } ;
+ SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
+ SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
+ SQLINTEGER cbFloat = 0 ;
+ ODBC_HANDLES stHandles ;
+ memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
+
+ int r = 0, j = 0 ;
+ //Get thread parameters
+ PARAMS* p = (PARAMS*)pparams ;
+
+ float* pRef = (float*)p->pThreadRef ;
+ float* pBindBuffer = (float*)malloc(sizeof(float)*nNoOfCol) ;
+
+ //printf("Thread #%d\n", p->nThreadID) ;
+
+ retcode = GetHandles(&stHandles, GET, 0) ;
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ) {
+ p->report_status = S_STARTED ;
+ }else{
+ printf("Thread #%d failed to allocate handles, exiting now.\n", p->nThreadID) ;
+ free((void*)pBindBuffer) ;
+ p->nError = 1 ;
+ p->report_status = S_EXIT ;
+ return 0 ;
+ }
+
+ //p->report_status = S_STARTED ;
+
+ //Main thread loop
+ for(;;){
+
+ while(S_IDLE == p->thread_status){
+ NdbSleep_MilliSleep(1) ;
+ }
+
+ if(S_STOP == p->thread_status) {
+ break ;
+ }else{
+ p->thread_status = S_BUSY ;
+ }
+
+ switch(p->op_type){
+
+
+ /************************************** T_INSERT case **************************************/
+ case T_INSERT:
+
+ for(r = 0 ; r < nNoOfRows ; ++r){
+
+ if(!nColList){
+ sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ;
+ }else{
+ sprintf((char*)szStmtBuffer, "INSERT INTO %s (%s) VALUES(", p->szTableName, szColNames) ;
+ }
+
+ //sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ;
+
+ for(j = 0 ;;){
+ sprintf((char*)szValueBuffer,"%f", pRef[nNoOfCol*r + j]) ;
+ strncat((char*)szStmtBuffer, (char*)szValueBuffer, strlen((const char*)szValueBuffer)) ;
+ ++j ;
+ if(nNoOfCol == j) break ;
+ strcat((char*)szStmtBuffer, ", ") ;
+ }
+ strcat((char*)szStmtBuffer, ")") ;
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
+ if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ }else{
+ p->nError = 1 ;
+ printf("INSERT in thread #%d failed\n", p->nThreadID) ;
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+ }
+ break ;
+
+
+ /************************************** T_READ case **************************************/
+ case T_READ:
+
+ for(r = 0 ; r < nNoOfRows ; ++r){
+
+ sprintf((char*)szStmtBuffer, "SELECT * FROM %s WHERE COL0 = %f", p->szTableName, pRef[nNoOfCol*r]) ;
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ ODBC_FN(SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmtBuffer, SQL_NTS), retcode) ;
+
+ for(j = 0 ; j < nNoOfCol ; ++j){
+ ODBC_FN(SQLBindCol(stHandles.hstmt, (j+1), SQL_C_FLOAT, (void*)&pBindBuffer[j], sizeof(SQLFLOAT), &cbFloat), retcode) ;
+ }
+
+ for (;;) {
+ retcode = SQLFetch(stHandles.hstmt);
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ for(int k = 0 ; k < nNoOfCol ; ++k){
+ if(p->nVerifyFlag){
+ if(abs(pBindBuffer[k] - pRef[nNoOfCol*r + k]) > FLTDEV )
+ printf("Expected: %f Actual: %f\n", pBindBuffer[k], pRef[nNoOfCol*r + k]) ;
+ }
+ }
+ }else if(SQL_NO_DATA == retcode){
+ break ;
+ }else{
+ p->nError = 1 ;
+ printf("READ in thread #%d failed\n", p->nThreadID) ;
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+ //printf("\n") ;
+ }
+ SQLCloseCursor(stHandles.hstmt) ;
+ }
+ break ;
+
+
+ /************************************** T_UPDATE case **************************************/
+ case T_UPDATE:
+ for(r = 0 ; r < nNoOfRows ; ++r){
+
+ sprintf((char*)szStmtBuffer, "UPDATE %s SET ", p->szTableName) ;
+ for(j = 1 ;;){
+ pRef[nNoOfCol*r + j] = pRef[nNoOfCol*r + j] + UPDATE_VALUE ;
+ sprintf((char*)szColBuffer,"COL%d = %f", j, pRef[nNoOfCol*r + j]) ;
+ strncat((char*)szStmtBuffer, (char*)szColBuffer, strlen((char*)szColBuffer)) ;
+ ++j ;
+ if(nNoOfCol == j) break ;
+ strcat((char*)szStmtBuffer, ", ") ;
+ }
+ sprintf((char*)szAuxBuffer, " WHERE COL0 = %f ;", pRef[nNoOfCol*r]) ;
+ strcat((char*)szStmtBuffer, (char*)szAuxBuffer);
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
+ if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ }else{
+ p->nError = 1 ;
+ printf("UPDATE in thread #%d failed\n", p->nThreadID) ;
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+ }
+ break ;
+
+
+ /************************************** T_DELETE case **************************************/
+ case T_DELETE:
+ for(r = 0 ; r < nNoOfRows ; ++r){
+ sprintf((char*)szStmtBuffer, "DELETE * FROM %s WHERE COL0 = %f", p->szTableName, pRef[nNoOfCol*r]) ;
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ retcode = SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmtBuffer, SQL_NTS) ;
+ if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ }else if( 1 == p->nVerifyFlag && SQL_NO_DATA != retcode){
+ p->nError = 1 ;
+ printf("\nVerification failed: still row exists\n") ;
+ }else{
+ p->nError = 1 ;
+ printf("DELETE in thread #%d failed\n", p->nThreadID) ;
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+ }
+ break ;
+
+
+ /************************************** default case **************************************/
+ default:
+ break ;
+ }//switch
+p->thread_status = S_IDLE ;
+ } //for
+
+free((void*)pBindBuffer) ;
+GetHandles(&stHandles, FREE, 0) ;
+p->thread_status = S_EXIT ;
+return 0 ;
+ };
+
+
+
+/*************************************************
+Function: ThreadFnDouble - thread function
+for double attributes
+*************************************************/
+/*
+void* ThreadFnDouble(void* pparams){
+
+ SQLRETURN retcode = SQL_ERROR ;
+ SQLCHAR szStmtBuffer[MAX_SQL_STMT] = { 0 } ;
+ SQLCHAR szValueBuffer[MAX_VALUE_LEN] = { 0 } ;
+ SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
+ SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
+ SQLINTEGER cbDouble = 0 ;
+ ODBC_HANDLES stHandles ;
+ memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
+ int r = 0, j = 0 ;
+
+ //Get thread parameters
+ PARAMS* p = (PARAMS*)pparams ;
+ double* pRef = (double*)p->pThreadRef ;
+
+ double* pBindBuffer = (double*)malloc(sizeof(double)*nNoOfCol) ;
+
+ //printf("Thread #%d\n", p->nThreadID) ;
+
+ retcode = GetHandles(&stHandles, GET, 0) ;
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ) {
+ p->report_status = S_STARTED ;
+ }else{
+ printf("Thread #%d failed to allocate handles, exiting now.\n", p->nThreadID) ;
+ free((void*)pBindBuffer) ;
+ p->report_status = S_EXIT ;
+ return 0 ;
+ }
+ //p->report_status = S_STARTED ;
+
+ //Main thread loop
+ for(;;){
+
+ while(S_IDLE == p->thread_status){
+ NdbSleep_MilliSleep(1) ;
+ }
+
+ if(S_STOP == p->thread_status) {
+ break ;
+ }else{
+ p->thread_status = S_BUSY ;
+ }
+
+ switch(p->op_type){
+
+
+ /************************************** T_INSERT case **************************************/
+ /* case T_INSERT:
+
+ for(r = 0 ; r < nNoOfRows ; ++r){
+ sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ;
+ for(j = 0 ;;){
+ sprintf((char*)szValueBuffer,"%.9f", pRef[nNoOfCol*r + j]) ;
+ strncat((char*)szStmtBuffer, (char*)szValueBuffer, strlen((const char*)szValueBuffer)) ;
+ ++j ;
+ if(nNoOfCol == j) break ;
+ strcat((char*)szStmtBuffer, ", ") ;
+ }
+ strcat((char*)szStmtBuffer, ")") ;
+ ODBC_FN(SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS), retcode) ;
+ }
+
+ break ;
+
+
+ /************************************** T_READ case **************************************/
+ /* case T_READ:
+
+ for(r = 0 ; r < nNoOfRows ; ++r){
+ sprintf((char*)szStmtBuffer, "SELECT * FROM %s WHERE COL0 = %.9f", p->szTableName, pRef[nNoOfCol*r]) ;
+ ODBC_FN(SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmtBuffer, SQL_NTS), retcode) ;
+ for(j = 0 ; j < nNoOfCol ; ++j){
+ ODBC_FN(SQLBindCol(stHandles.hstmt, (j+1), SQL_C_DOUBLE, (void*)&pBindBuffer[j], sizeof(SQLDOUBLE), &cbDouble), retcode) ;
+ }
+ for (;;) {
+ retcode = SQLFetch(stHandles.hstmt);
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ for(int k = 0 ; k < nNoOfCol ; ++k){
+ if(p->nVerifyFlag){
+ if(abs(pBindBuffer[k] - pRef[nNoOfCol*r + k]) > DBLDEV)
+ printf("Expected: %.9f Actual: %.9f\n", pBindBuffer[k], pRef[nNoOfCol*r + k]) ;
+ }
+ }
+ }else if(SQL_NO_DATA == retcode){
+ break ;
+ }else{
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+ //printf("\n") ;
+ }
+ SQLCloseCursor(stHandles.hstmt) ;
+ }
+ break ;
+
+
+ /************************************** T_UPDATE case **************************************/
+ /* case T_UPDATE:
+ for(r = 0 ; r < nNoOfRows ; ++r){
+
+ sprintf((char*)szStmtBuffer, "UPDATE %s SET ", p->szTableName) ;
+ for(j = 1 ;;){
+ pRef[nNoOfCol*r + j] = pRef[nNoOfCol*r + j] + UPDATE_VALUE ;
+ sprintf((char*)szColBuffer,"COL%d = %.9f", j, pRef[nNoOfCol*r + j]) ;
+ strncat((char*)szStmtBuffer, (char*)szColBuffer, strlen((char*)szColBuffer)) ;
+ ++j ;
+ if(nNoOfCol == j) break ;
+ strcat((char*)szStmtBuffer, ", ") ;
+ }
+ sprintf((char*)szAuxBuffer, " WHERE COL0 = %.9f ;", pRef[nNoOfCol*r]) ;
+ strcat((char*)szStmtBuffer, (char*)szAuxBuffer);
+ ODBC_FN(SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS), retcode) ;
+ }
+ break ;
+
+
+ /************************************** T_DELETE case **************************************/
+ /* case T_DELETE:
+ for(r = 0 ; r < nNoOfRows ; ++r){
+ sprintf((char*)szStmtBuffer, "DELETE FROM %s WHERE COL0 = %.9f", p->szTableName, pRef[nNoOfCol*r]) ;
+ retcode = SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmtBuffer, SQL_NTS) ;
+ if( 1 == p->nVerifyFlag && SQL_NO_DATA != retcode ){
+ printf("\nVerification failed: still row exists\n") ;
+ }
+ }
+ break ;
+
+
+ /************************************** default case **************************************/
+ /* default:
+ break ;
+ }//switch
+p->thread_status = S_IDLE ;
+ } //for
+
+free((void*)pBindBuffer) ;
+GetHandles(&stHandles, FREE, 0) ;
+p->thread_status = S_EXIT ;
+return 0 ;
+ };
+
+
+
+/*************************************************
+Function: ThreadFnChar - thread function
+for character attributes
+*************************************************/
+void* ThreadFnChar(void* pparams){
+
+ SQLRETURN retcode = SQL_ERROR ;
+ SQLCHAR szStmtBuffer[MAX_SQL_STMT] = { 0 } ;
+ SQLCHAR szValueBuffer[MAX_VALUE_LEN] = { 0 } ;
+ SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
+ SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
+ SQLINTEGER cbChar = 0 ;
+ ODBC_HANDLES stHandles ;
+ memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
+ int r = 0, j = 0 ;
+
+ //Get thread parameters
+ PARAMS* p = (PARAMS*)pparams ;
+ char* pRef = (char*)p->pThreadRef ;
+ char* pBindBuffer = (char*)malloc(sizeof(char)*nNoOfCol*MAX_CHAR_ATTR_LEN) ;
+
+ //printf("Thread #%d\n", p->nThreadID) ;
+
+ retcode = GetHandles(&stHandles, GET, 0) ;
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ) {
+ p->report_status = S_STARTED ;
+ }else{
+ printf("Thread #%d failed to allocate handles, retcode = %d, exiting now.\n", p->nThreadID, retcode) ;
+ p->nError = 1 ;
+ free((void*)pBindBuffer) ;
+ p->report_status = S_EXIT ;
+ return 0 ;
+ }
+
+ //Main thread loop
+ for(;;){
+
+ while(S_IDLE == p->thread_status){
+ NdbSleep_MilliSleep(1) ;
+ }
+
+ if(S_STOP == p->thread_status) {
+ break ;
+ }else{
+ p->thread_status = S_BUSY ;
+ }
+
+ switch(p->op_type){
+
+
+ /************************************** T_INSERT case **************************************/
+ case T_INSERT:
+
+ for(r = 0 ; r < nNoOfRows ; ++r){
+ memset(szStmtBuffer, 0, strlen(szStmtBuffer)) ;
+ if(!nColList){
+ sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ;
+ }else{
+ sprintf((char*)szStmtBuffer, "INSERT INTO %s (%s) VALUES(", p->szTableName, szColNames) ;
+ }
+
+ for(j = 0 ;;){
+ sprintf((char*)szValueBuffer,"'%s'", (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char) + j*MAX_CHAR_ATTR_LEN*sizeof(char))) ;
+ strncat((char*)szStmtBuffer, (char*)szValueBuffer, strlen((const char*)szValueBuffer)) ;
+ ++j ;
+ if(nNoOfCol == j) break ;
+ strcat((char*)szStmtBuffer, ", ") ;
+ }
+ strcat((char*)szStmtBuffer, ")") ;
+
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
+ if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ }else{
+ p->nError = 1 ;
+ printf("INSERT in thread #%d failed\n", p->nThreadID) ;
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+ }
+
+ break ;
+
+
+ /************************************** T_READ case **************************************/
+ case T_READ:
+
+ for(r = 0 ; r < nNoOfRows ; ++r){
+ sprintf((char*)szStmtBuffer, "SELECT * FROM %s WHERE COL0 = '%s'", p->szTableName, (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char))) ;
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ ODBC_FN(SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmtBuffer, SQL_NTS), retcode) ;
+ for(j = 0 ; j < nNoOfCol ; ++j){
+ ODBC_FN(SQLBindCol(stHandles.hstmt, (j+1), SQL_C_CHAR, (void*)(pBindBuffer+j*MAX_CHAR_ATTR_LEN*sizeof(char)), MAX_CHAR_ATTR_LEN, &cbChar), retcode) ;
+ }
+ for (;;) {
+ retcode = SQLFetch(stHandles.hstmt);
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ for(int k = 0 ; k < nNoOfCol ; ++k){
+ if(p->nVerifyFlag){
+ if(!strcmp((char*)(pBindBuffer + k*MAX_CHAR_ATTR_LEN*sizeof(char)), (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char) + k*MAX_CHAR_ATTR_LEN*sizeof(char))))
+ printf("Expected: %s Actual: %s\n", (char*)(pBindBuffer + k*MAX_CHAR_ATTR_LEN*sizeof(char)), (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char) + k*MAX_CHAR_ATTR_LEN*sizeof(char))) ;
+ }
+ }
+ }else if(SQL_NO_DATA == retcode){
+ break ;
+ }else{
+ p->nError = 1 ;
+ printf("READ in thread #%d failed\n", p->nThreadID) ;
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+ //printf("\n") ;
+ }
+ SQLCloseCursor(stHandles.hstmt) ;
+ }
+ break ;
+
+
+ /************************************** T_UPDATE case **************************************/
+ case T_UPDATE:
+ for(r = 0 ; r < nNoOfRows ; ++r){
+ sprintf((char*)szStmtBuffer, "UPDATE %s SET ", p->szTableName) ;
+ for(j = 1 ;;){
+ swab((char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char) + j*MAX_CHAR_ATTR_LEN*sizeof(char)), (char*)szColBuffer, MAX_CHAR_ATTR_LEN*sizeof(char)) ;
+ memcpy((void*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char) + j*MAX_CHAR_ATTR_LEN*sizeof(char)), (void*)szColBuffer, MAX_CHAR_ATTR_LEN*sizeof(char)) ;
+ sprintf((char*)szColBuffer,"COL%d = '%s'", j, (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char) + j*MAX_CHAR_ATTR_LEN*sizeof(char))) ;
+ strncat((char*)szStmtBuffer, (char*)szColBuffer, strlen((char*)szColBuffer)) ;
+ ++j ;
+ if(nNoOfCol == j) break ;
+ strcat((char*)szStmtBuffer, ", ") ;
+ }
+ sprintf( (char*)szAuxBuffer, " WHERE COL0 = '%s';", (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char)) ) ;
+ strcat((char*)szStmtBuffer, (char*)szAuxBuffer) ;
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
+ if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ }else{
+ p->nError = 1 ;
+ printf("UPDATE in thread #%d failed\n", p->nThreadID) ;
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+ }
+ break ;
+
+
+ /************************************** T_DELETE case **************************************/
+ case T_DELETE:
+ for(r = 0 ; r < nNoOfRows ; ++r){
+ sprintf((char*)szStmtBuffer, "DELETE FROM %s WHERE COL0 = '%s\'", p->szTableName, (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char))) ;
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
+ if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ }else if(1 == p->nVerifyFlag && SQL_NO_DATA != retcode){
+ p->nError = 1 ;
+ printf("\nVerification failed: still row exists\n") ;
+ }else{
+ p->nError = 1 ;
+ printf("INSERT in thread #%d failed\n", p->nThreadID) ;
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+ }
+ break ;
+
+
+ /************************************** default case **************************************/
+ default:
+ break ;
+ }//switch
+ p->thread_status = S_IDLE ;
+ } //for
+
+ free((void*)pBindBuffer) ;
+ GetHandles(&stHandles, FREE, 0) ;
+ p->thread_status = S_EXIT ;
+ return 0 ;
+};
+
+
+
+/*************************************************
+Function: CreateDemoTable
+*************************************************/
+SQLRETURN CreateDemoTables(char* szTableName, int nTables, table_opt op){
+
+ SQLRETURN retcode = SQL_ERROR ;
+ SQLCHAR szStmtBuffer[MAX_SQL_STMT] = { 0 } ;
+ SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
+ SQLCHAR szAuxBuffer[32] = { 0 } ;
+ ODBC_HANDLES stHandles ;
+ memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
+ int c = 0 ;
+
+ GetHandles(&stHandles, GET, 0) ;
+
+ if(CREATE == op){
+
+ for(c = 0; c < nTables ; ++c){
+ sprintf((char*)szStmtBuffer, "CREATE TABLE %s (", (char*)(szTableName+MAX_TABLE_NAME*c)) ;
+ int j = 0 ;
+ for(;;){
+ sprintf((char*)szColBuffer, "COL%d ", j) ;
+ strcat((char*)szStmtBuffer, (char*)szColBuffer) ;
+ ++j ;
+
+ switch(AttributeType){
+ case T_INT:
+ strcat((char*)szStmtBuffer, "INTEGER") ;
+ break ;
+ case T_FLOAT:
+ strcat((char*)szStmtBuffer, "FLOAT") ;
+ break ;
+
+/* case T_DOUBLE:
+ strcat((char*)szStmtBuffer, "DOUBLE") ;
+ break ;
+*/
+ case T_CHAR:
+ sprintf((char*)szAuxBuffer, "CHAR(%d)", MAX_CHAR_ATTR_LEN) ;
+ strcat((char*)szStmtBuffer, (char*)szAuxBuffer) ;
+ break ;
+ default:
+ break ;
+ }
+
+ if(nNoOfCol <= j){
+ strcat((char*)szStmtBuffer, ")") ;
+ break ;
+ }
+ strcat((char*)szStmtBuffer, ", ") ;
+ } //for(;;)
+
+
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ ODBC_FN(SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS), retcode) ;
+ if(SQL_SUCCESS != retcode) HandleError(stHandles.hstmt , SQL_HANDLE_STMT) ;
+
+
+ }// for()
+
+ }else{
+
+ for(c = 0 ; c < nTables ; ++c){
+ sprintf((char*)szStmtBuffer, "DROP TABLE %s ", (char*)(szTableName + MAX_TABLE_NAME*c)) ;
+ //ODBC_FN(SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS), retcode) ;
+ if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
+ retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
+ }
+
+ }
+
+ GetHandles(&stHandles, FREE, 0) ;
+
+ return retcode ;
+}
+
+
+
+/*************************************************
+Function: AssignTableNames()
+*************************************************/
+inline void AssignTableNames(char* szBuffer, int nTables){
+ for(int c = 0 ; c < nTables ; ++c){
+ sprintf((char*)(szBuffer + MAX_TABLE_NAME*sizeof(char)*c), "TAB%d", c) ;
+ }
+return ;
+ }
+
+
+
+
+/*************************************************
+Function: StartThreads()
+*************************************************/
+
+inline void StartThreads(PARAMS* p, void* pRef, int nTables, char* szTables, attr_type attrType, UintPtr* pHandles) {
+
+ int* pInt = NULL ;
+ float* pFloat = NULL ;
+ double* pDouble = NULL ;
+ char* pChar = NULL ;
+ UintPtr pTmpThread = NULL ;
+
+ bool bFlap = 1 ;
+ for(int f = 0 ; f < nNoOfThreads ; ++f){
+ p[f].nThreadID = f ;
+ p[f].nError = 0 ;
+ p[f].thread_status = S_IDLE ;
+ p[f].op_type = T_WAIT ;
+ if(bFlap){
+ strncpy((char*)p[f].szTableName, (char*)szTables, MAX_TABLE_NAME) ;
+ }else{
+ strncpy((char*)p[f].szTableName, (char*)(szTables + MAX_TABLE_NAME*sizeof(char)), MAX_TABLE_NAME) ;
+ }
+ bFlap = !bFlap ;
+ //pTmpThread = pHandles[ ;
+
+ switch(attrType){
+ case T_INT:
+ pInt = (int*)pRef ;
+ p[f].pThreadRef = (void*)&pInt[nNoOfRows*nNoOfCol*f] ;
+ pHandles[f] = (UintPtr)NdbThread_Create(ThreadFnInt, (void**)&p[f], 32768, "SQL99_test", NDB_THREAD_PRIO_MEAN) ;
+ break ;
+ case T_FLOAT:
+ pFloat = (float*)pRef ;
+ p[f].pThreadRef = (void*)&pFloat[nNoOfRows*nNoOfCol*f] ;
+ pHandles[f] = (UintPtr)NdbThread_Create(ThreadFnFloat, (void**)&p[f], 32768, "SQL99_test", NDB_THREAD_PRIO_MEAN) ;
+ break ;
+ /*
+ case T_DOUBLE:
+ pDouble = (double*)pRef ;
+ p[f].pThreadRef = (void*)&pDouble[nNoOfRows*nNoOfCol*f] ;
+ pHandles[f] = (UintPtr)NdbThread_Create(ThreadFnDouble, (void**)&p[f], 32768, "SQL99_test", NDB_THREAD_PRIO_MEAN) ;
+ break ;
+ */
+ case T_CHAR:
+ pChar = (char*)pRef ;
+ p[f].pThreadRef = (void*)&pChar[nNoOfRows*nNoOfCol*f*MAX_CHAR_ATTR_LEN] ;
+ pHandles[f] = (UintPtr)NdbThread_Create(ThreadFnChar, (void**)&p[f], 32768, "SQL99_test", NDB_THREAD_PRIO_MEAN) ;
+ default:
+ break ;
+ }
+ while(!(S_STARTED != p[f].report_status || S_EXIT != p[f].report_status)){
+ NdbSleep_MilliSleep(1) ;
+ }
+ }
+ return ;
+}
+
+
+
+/*************************************************
+Function: SetThreadOperationType()
+*************************************************/
+inline void SetThreadOperationType(PARAMS* p, type op){
+
+ for(int e = 0 ; e < nNoOfThreads ; ++e){
+ p[e].nVerifyFlag = 0 ;
+ if(T_READ_VERIFY == op){
+ p[e].nVerifyFlag = 1 ;
+ p[e].op_type = T_READ ;
+ }else if(T_DELETE_VERIFY == op){
+ p[e].nVerifyFlag = 1 ;
+ p[e].op_type = T_DELETE ;
+ }else{
+ p[e].op_type = op ;
+ }
+ p[e].thread_status = S_GET_BUSY ;
+ }
+return ;
+ }
+
+
+
+/*************************************************
+Function: WaitForThreads()
+*************************************************/
+inline int WaitForThreads(PARAMS* p) {
+
+ int ret_value = 0 ;
+ for(int w = 0 ; w < nNoOfThreads ; ++w){
+ while(!(S_IDLE != p[w].thread_status || S_EXIT != p[w].report_status)) {
+ NdbSleep_MilliSleep(1) ;
+ }
+ ret_value += p[w].nError ;
+ }
+ return ret_value ;
+}
+
+
+
+/*************************************************
+Function: StopThreads()
+*************************************************/
+inline void StopThreads(PARAMS* p, UintPtr* pHandles) {
+
+ for(int k = 0 ; k < nNoOfThreads ; ++k){
+ while(!(S_IDLE != p[k].thread_status || S_EXIT != p[k].report_status)){
+ NdbSleep_MilliSleep(1) ;
+ }
+ p[k].thread_status = S_STOP ;
+ while(!(S_EXIT != p[k].thread_status || S_EXIT != p[k].report_status)){
+ NdbSleep_MilliSleep(1) ;
+ }
+ NdbThread_Destroy((NdbThread**)&pHandles[k]) ;
+ }
+
+ return ;
+}
+
+
+
+/*************************************************
+Function: PrintAll()
+*************************************************/
+inline void PrintAll(char* szTableName, int nTables, attr_type attrType){
+
+ SQLRETURN retcode = SQL_ERROR ;
+ SQLCHAR* szStmt[MAX_SQL_STMT] = { 0 } ;
+ ODBC_HANDLES stHandles ;
+ memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
+ double* pDoubleBuffer = NULL ;
+ char* pCharBuffer = NULL ;
+
+ if(T_CHAR != attrType){
+ pDoubleBuffer = (double*)malloc(sizeof(double)*nNoOfCol) ;
+ }else{
+ pCharBuffer = (char*)malloc(sizeof(char)*nNoOfCol*MAX_CHAR_ATTR_LEN) ;
+ }
+
+ SQLINTEGER cbLen = 0 ;
+
+ GetHandles(&stHandles, GET, 0) ;
+
+ for(int c = 0 ; c < nTables ; ++c){
+
+ int nCol = 0, nRows = 0 ;
+
+ printf("Table: \"%s\":\n------------------\n", (char*)(szTableName + MAX_TABLE_NAME*c*sizeof(char))) ;
+
+ sprintf((char*)szStmt, "SELECT * FROM %s", (char*)(szTableName + MAX_TABLE_NAME*c*sizeof(char))) ;
+ if(nPrint) printf("\n> %s\n", szStmt) ;
+ ODBC_FN(SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmt, SQL_NTS), retcode) ;
+
+ for(int i = 0 ; i < nNoOfCol ; ++i){
+
+ if(T_CHAR != attrType){
+ ODBC_FN(SQLBindCol(stHandles.hstmt, (i+1), SQL_C_DOUBLE, (void*)&pDoubleBuffer[i], sizeof(SQLDOUBLE), &cbLen), retcode) ;
+ }else{
+ ODBC_FN(SQLBindCol(stHandles.hstmt, (i+1), SQL_C_CHAR, (void*)(pCharBuffer + i*MAX_CHAR_ATTR_LEN*sizeof(char)), MAX_CHAR_ATTR_LEN*sizeof(char), &cbLen), retcode) ;
+ }
+ nCol++ ;
+
+ }
+
+ int k = 0 ; //out of the <for> loop
+ for (;;) {
+
+ retcode = SQLFetch(stHandles.hstmt);
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ){
+ for(k = 0 ; k < nNoOfCol ; ++k){
+ if(T_CHAR != attrType){
+ ATTR_TYPE_SWITCH_T(pDoubleBuffer[k], AttributeType) ;
+ }else{
+ printf("%s\t", (char*)(pCharBuffer + k*MAX_CHAR_ATTR_LEN)) ;
+ }
+ }
+ }else if(SQL_NO_DATA == retcode){
+ if(0 == k){
+ printf("<empty>\n") ;
+ break ;
+ }else{
+ break ;
+ }
+ }else{
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+
+ ++nRows ;
+ printf("\n") ;
+ }
+
+ SQLCloseCursor(stHandles.hstmt) ;
+
+ printf("------------------\n") ;
+ printf("Rows: %d Columns: %d\n\n", nRows, nCol) ;
+
+ }
+
+ free((void*)pDoubleBuffer) ;
+ free((void*)pCharBuffer) ;
+ GetHandles(&stHandles, FREE, 0) ;
+
+ return ;
+}
+
+
+
+/*************************************************
+Function: AssignRefCharValues()
+*************************************************/
+void AssignRefCharValues(char* pRef, bool bVerbose) {
+
+ int count = 0, rows = 0, nThreadOffset = 0, nRowOffset = 0 ;
+ char szStrBuffer[MAX_CHAR_ATTR_LEN] = { 0 } ;
+ int char_count = sizeof(szANSI)/sizeof(char) ;
+
+ for(int c = 0 ; c < nNoOfThreads ; ++c){
+ nThreadOffset = nNoOfRows*nNoOfCol*c*MAX_CHAR_ATTR_LEN*sizeof(char) ;
+ for(int d = 0 ; d < nNoOfRows ; ++d){
+ nRowOffset = nNoOfCol*d*MAX_CHAR_ATTR_LEN*sizeof(char) ; ++rows ;
+ for(int i = 0 ; i < nNoOfCol ; ++i){
+ for(int j = 0 ; j < (MAX_CHAR_ATTR_LEN - 2) ; ++j){
+ int h = (char)(rand() % (char_count-1)) ;
+ szStrBuffer[j] = szANSI[h] ;
+ }
+ szStrBuffer[MAX_CHAR_ATTR_LEN - 1] = '\0' ;
+
+ strcpy((char*)(pRef + nThreadOffset + nRowOffset + i*MAX_CHAR_ATTR_LEN*sizeof(char)), (char*)szStrBuffer) ;
+ count++ ;
+ if(bVerbose){
+ printf(" %s ", (char*)(pRef + nThreadOffset + nRowOffset + i*MAX_CHAR_ATTR_LEN*sizeof(char))) ;
+ }
+ }
+ if(bVerbose) {
+ printf("\n") ;
+ NdbSleep_MilliSleep(10) ;
+ }
+ }
+ }
+
+if(bVerbose){
+ printf("_____________________") ;
+ printf("\nRows: %d Values: %d\n\n", rows, count) ;
+ }
+
+return ;
+ }
+
+
+/*
+
+
+sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ;
+for(j = 0 ;;){
+strcat((char*)szStmtBuffer, "?") ;
+++j ;
+if(nNoOfCol == j) break ;
+strcat((char*)szStmtBuffer, ", ") ;
+}
+strcat((char*)szStmtBuffer, ")") ;
+
+ODBC_FN(SQLPrepare(stHandles.hstmt, szStmtBuffer, SQL_NTS), retcode) ;
+
+for(j = 0 ; j < nNoOfCol ; ++j){
+ODBC_FN(SQLBindParameter(stHandles.hstmt, (j+1), SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, 0, 0, (void*)&pBindBuffer[j], 0, &cbFloat), retcode) ;
+HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+}
+
+for(r = 0 ; r < nNoOfRows ; ++r){
+for(j = 0 ; j < nNoOfCol ; ++j){
+pBindBuffer[j] = pRef[nNoOfCol*r + j] ;
+}
+ODBC_FN(SQLExecute(stHandles.hstmt), retcode) ;
+HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+}
+
+*/
+
+
+
+
+/*************************************************
+Function: HandleError
+*************************************************/
+
+void HandleError(void* handle, SQLSMALLINT HandleType){
+
+ SQLCHAR szError[MAX_STR_LEN], szSqlState[32] ;
+ SQLINTEGER nError = 0 ;
+ SQLSMALLINT nHandleType = HandleType ;
+ SQLSMALLINT nLength = 0 ;
+ SQLHANDLE SQLHandle = handle ;
+ SQLGetDiagRec(nHandleType, SQLHandle, 1, szSqlState, &nError, szError, 128, &nLength) ;
+ printf("Error: %s\nSqlState: %s\n", szError, szSqlState) ;
+
+ return ;
+ }
+
+
+
+/*************************************************
+Function: ReportError
+*************************************************/
+
+void ReportError(char* szFn, char* szBuffer, char* szFile, int iLine){
+
+ printf("%s %s\nFile: %s\nLine: %d\n", szFn, szBuffer, szFile, iLine) ;
+
+ return ;
+}
+
+
+
+/*************************************************
+Function: GetHandles()
+*************************************************/
+
+SQLRETURN GetHandles(ODBC_HANDLES* pHandles, handle_op op, bool bDriverInfo){
+
+ SQLRETURN retcode = SQL_ERROR ;
+
+ if(GET == op){
+
+ retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &pHandles->henv);
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ) {
+ retcode = SQLSetEnvAttr(pHandles->henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC2, 0);
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode) {
+ retcode = SQLAllocHandle(SQL_HANDLE_DBC, pHandles->henv, &pHandles->hdbc);
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode) {
+
+ //SQLSetConnectAttr(pHandles->hdbc, SQL_LOGIN_TIMEOUT, (void*)5, 0);
+
+ retcode = SQLConnect(pHandles->hdbc, (SQLCHAR*)"", SQL_NTS, (SQLCHAR*)"", SQL_NTS, (SQLCHAR*)"", SQL_NTS ) ;
+
+ SQL_SUCCESS == SQLSetConnectAttr(pHandles->hdbc, SQL_ATTR_AUTOCOMMIT, (void*)SQL_AUTOCOMMIT_ON, 0) ;
+ //printf("AUTOCOMMIT is on\n") ;
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode) {
+ // retcode still holds the value returned by SQLConnect
+ retcode = SQLAllocHandle(SQL_HANDLE_STMT, pHandles->hdbc, &pHandles->hstmt) ;
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode) {
+ if(bDriverInfo) GetDriverAndSourceInfo(pHandles->hdbc) ;
+ // printf("All handles allocated OK\n", retcode);
+ }else{ // SQLAllocHandle()
+ REPORTERROR((char*)"SQLAllocHandle()", (char*)"failed") ;
+ HandleError(pHandles->hdbc, SQL_HANDLE_DBC) ;
+ ODBC_FN(SQLDisconnect(pHandles->hdbc), retcode) ;
+ ODBC_FN(SQLFreeHandle(SQL_HANDLE_DBC, pHandles->hdbc), retcode) ;
+ ODBC_FN(SQLFreeHandle(SQL_HANDLE_ENV, pHandles->henv), retcode) ;
+ retcode = SQL_ERROR ;
+ }
+ }else{ // SQLConnect()
+ REPORTERROR((char*)"SQLConnect()", (char*)"failed" ) ;
+ HandleError(pHandles->hdbc, SQL_HANDLE_DBC) ;
+ ODBC_FN(SQLFreeHandle(SQL_HANDLE_DBC, pHandles->hdbc), retcode) ;
+ ODBC_FN(SQLFreeHandle(SQL_HANDLE_ENV, pHandles->henv), retcode) ;
+ retcode = SQL_ERROR ;
+ }
+ }else{ // SQLAllocHandle()
+ REPORTERROR((char*)"SQLAllocHandle()", "failed" ) ;
+ HandleError(pHandles->hdbc, SQL_HANDLE_DBC) ;
+ ODBC_FN(SQLFreeHandle(SQL_HANDLE_ENV, pHandles->henv), retcode) ;
+ retcode = SQL_ERROR ;
+ }
+ }else{ // SQLSetEnvAttr()
+ REPORTERROR((char*)"SQLSetEnvAttr()", "failed" ) ;
+ HandleError(pHandles->henv, SQL_HANDLE_ENV) ;
+ ODBC_FN(SQLFreeHandle(SQL_HANDLE_ENV, pHandles->henv), retcode) ;
+ retcode = SQL_ERROR ;
+ }
+ }else{ // SQLAllocHandle()
+ REPORTERROR((char*)"SQLAllocHandle()", "failed" ) ;
+ HandleError(pHandles->henv, SQL_HANDLE_ENV) ;
+ retcode = SQL_ERROR ;
+ }
+ }else{
+ ODBC_FN(SQLFreeHandle(SQL_HANDLE_STMT, pHandles->hstmt), retcode) ;
+ ODBC_FN(SQLDisconnect(pHandles->hdbc), retcode) ;
+ ODBC_FN(SQLFreeHandle(SQL_HANDLE_DBC, pHandles->hdbc), retcode) ;
+ ODBC_FN(SQLFreeHandle(SQL_HANDLE_ENV, pHandles->henv), retcode) ;
+ }
+
+ return retcode ;
+}
+
+
+
+/*************************************************
+Function: AggretateFn():
+<aggr_fn fn> - name of the aggregate function to use
+<char* szTableName> - name of the table
+<int nCol> - number of the column
+<double* pdIn> - pointer to double containing the value to be used in a call to COUNT; used only by this function
+<double* pdOut> - pointer to double that will recieve the result
+<attr_type attrType> - type of the attribute
+*************************************************/
+SQLRETURN AggregateFn(aggr_fn fn, char* szTableName, int nCol, double* pdIn, double* pdOut, attr_type attrType){
+
+ SQLRETURN retcode = SQL_ERROR ;
+ SQLCHAR* szStmt[MAX_SQL_STMT] = { 0 } ;
+ SQLCHAR szValueBuffer[MAX_VALUE_LEN] = { 0 } ;
+ SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
+ SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
+ ODBC_HANDLES stHandles ;
+ memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
+ SQLINTEGER cbDouble = 0 ;
+
+ GetHandles(&stHandles, GET, 0) ;
+
+ switch(fn){
+ case FN_COUNT:
+ switch(attrType){
+ case T_INT:
+ sprintf((char*)szStmt, "SELECT COUNT(*) FROM %s WHERE COL%d > %d", szTableName, nCol, (int)*pdIn) ;
+ break ;
+ case T_FLOAT:
+ sprintf((char*)szStmt, "SELECT COUNT(*) FROM %s WHERE COL%d > %f", szTableName, nCol, (float)*pdIn) ;
+ break ;
+/* case T_DOUBLE:
+ sprintf((char*)szStmt, "SELECT COUNT(*) FROM %s WHERE COL%d > %.15f", szTableName, nCol, *pdIn) ;
+ break ;
+*/
+ default:
+ break ;
+ }
+ break ;
+ case FN_SUM:
+ sprintf((char*)szStmt, "SELECT SUM(COL%d) FROM %s", nCol, szTableName) ;
+ break ;
+ case FN_AVG:
+ sprintf((char*)szStmt, "SELECT AVG(COL%d) FROM %s", nCol, szTableName) ;
+ break ;
+ case FN_MAX:
+ sprintf((char*)szStmt, "SELECT MAX(COL%d) FROM %s", nCol, szTableName) ;
+ break ;
+ case FN_MIN:
+ sprintf((char*)szStmt, "SELECT MIN(COL%d) FROM %s", nCol, szTableName) ;
+ break ;
+ case FN_VARIANCE: // not implemented
+ //sprintf((char*)szStmt, "SELECT VARIANCE(COL%d) FROM %s;", nCol, szTableName) ;
+ break ;
+ case FN_STDDEV: // not implemented
+ //sprintf((char*)szStmt, "SELECT STDDEV(COL%d) FROM %s;", nCol, szTableName) ;
+ break ;
+ default:
+ break ;
+ }
+//printf("%s\n", szStmt) ;
+
+retcode = SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmt, SQL_NTS) ;
+if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ){
+ retcode = SQLBindCol(stHandles.hstmt, 1, SQL_C_DOUBLE, (void*)pdOut, sizeof(SQLDOUBLE), &cbDouble) ;
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ){
+ retcode = SQLFetch(stHandles.hstmt) ;
+ }
+ }
+
+if(SQL_SUCCESS != retcode && SQL_SUCCESS_WITH_INFO != retcode){
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+
+SQLCloseCursor(stHandles.hstmt) ;
+
+GetHandles(&stHandles, FREE, 0) ;
+
+return retcode ;
+
+ };
+
+
+
+/*************************************************
+Function: GetDriverAndSourceInfo()
+*************************************************/
+SQLRETURN GetDriverAndSourceInfo(SQLHDBC hdbc){
+
+ SQLRETURN retcode = SQL_ERROR ;
+
+ SQLCHAR buffer[255] ;
+ SQLUSMALLINT snValue = 0 ;
+ SQLSMALLINT outlen = 0 ;
+
+ printf( "-------------------------------------------\n" ) ;
+
+ retcode = SQLGetInfo( hdbc, SQL_DATA_SOURCE_NAME, buffer, 255, &outlen ) ;
+
+ printf( "Connected to Server: %s\n", buffer ) ;
+
+ retcode = SQLGetInfo( hdbc, SQL_DATABASE_NAME, buffer, 255, &outlen ) ;
+ printf( " Database name: %s\n", buffer ) ;
+
+ retcode = SQLGetInfo( hdbc, SQL_SERVER_NAME, buffer, 255, &outlen ) ;
+ printf( " Instance name: %s\n", buffer ) ;
+
+ retcode = SQLGetInfo( hdbc, SQL_DBMS_NAME, buffer, 255, &outlen ) ;
+ printf( " DBMS name: %s\n", buffer ) ;
+
+ retcode = SQLGetInfo( hdbc, SQL_DBMS_VER, buffer, 255, &outlen ) ;
+ printf( " DBMS version: %s\n", buffer ) ;
+
+ retcode = SQLGetInfo( hdbc, SQL_ODBC_VER, buffer, 255, &outlen ) ;
+ printf( " ODBC version: %s\n", buffer ) ;
+
+ retcode = SQLGetInfo( hdbc, SQL_DRIVER_NAME, buffer, 255, &outlen ) ;
+ printf( " Driver name: %s\n", buffer ) ;
+
+ retcode = SQLGetInfo( hdbc, SQL_DRIVER_VER, buffer, 255, &outlen ) ;
+ printf( " Driver version: %s\n", buffer ) ;
+
+ retcode = SQLGetInfo( hdbc, SQL_MAX_DRIVER_CONNECTIONS, &snValue, sizeof(SQLSMALLINT), &outlen ) ;
+ printf( " Max connections: %d\n", snValue ) ;
+
+ retcode = SQLGetInfo( hdbc, SQL_CURSOR_COMMIT_BEHAVIOR, &snValue, sizeof(SQLSMALLINT), &outlen ) ;
+ printf( "Autocommit behavior:") ;
+
+ switch(snValue){
+ case SQL_CB_DELETE:
+ printf(" SQL_CB_DELETE\n") ;
+ break ;
+ case SQL_CB_CLOSE:
+ printf(" SQL_CB_CLOSE\n") ;
+ break ;
+ case SQL_CB_PRESERVE:
+ printf(" SQL_CB_PRESERVE\n") ;
+ break ;
+ default:
+ printf(" undefined\n") ;
+ break ;
+ }
+
+ printf( "-------------------------------------------\n" ) ;
+
+ return retcode ;
+
+}
+
+
+
+/*************************************************
+Function: ArithOp()
+*************************************************/
+
+int ArithOp(char* szTable, int nTotalCols, float* pValue, attr_type attrType, arth_op op){
+
+ SQLRETURN retcode = SQL_ERROR ;
+ int nVerRet = -1 ;
+ SQLCHAR szStmt[MAX_SQL_STMT] = { 0 } ;
+ SQLCHAR szEndBuffer[MAX_STR_LEN] = { 0 } ;
+ SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
+ SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
+ ODBC_HANDLES stHandles ;
+ memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
+
+ void* pBuffer = NULL ;
+ SQLINTEGER BindInt = 0, IntResult = 0, RefIntResult = 0 ;
+ SQLFLOAT BindFloat = 0, FloatResult = 0, RefFloatResult = 0 ;
+ SQLDOUBLE BindDouble = 0, DoubleResult = 0, RefDoubleResult = 0 ;
+ SQLINTEGER cbSize = 0 ;
+ SQLINTEGER cbLen = 0 ;
+ SQLSMALLINT cbTarget = 0 ;
+
+ GetHandles(&stHandles, GET, 0) ;
+
+ for(int c = 0 ; c < nTotalCols ; ++c){
+
+ sprintf((char*)szStmt, "SELECT COL%d, (COL%d", c, c) ;
+ switch(op){
+ case MINUS:
+ strcat((char*)szStmt, " - ") ;
+ break ;
+ case PLUS:
+ strcat((char*)szStmt, " + ") ;
+ break ;
+ case MULTI:
+ strcat((char*)szStmt, " * ") ;
+ break ;
+ case DIVIDE:
+ strcat((char*)szStmt, " / ") ;
+ break ;
+ case MODULO:
+ //strcat((char*)szStmt, " % ") ; Not implemented
+ GetHandles(&stHandles, FREE, 0) ;
+ return -1 ; //Close handles and return
+ break ;
+ default:
+ break ;
+ }
+
+ sprintf((char*)(szAuxBuffer),"%.9f) ", *((float*)(pValue))) ;
+ strcat((char*)szStmt, (char*)szAuxBuffer) ;
+ sprintf((char*)szEndBuffer, "FROM %s", szTable) ;
+ strcat((char*)szStmt, (char*)szEndBuffer) ;
+
+ ODBC_FN(SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmt, SQL_NTS), retcode) ;
+ if(retcode == SQL_ERROR){
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ printf("\n%s\n", szStmt) ;
+ }
+
+ SQLSMALLINT cbNameLen = 0, cbSqlType = 0, cbNullable = 0, cbColScale = 0 ;
+ SQLINTEGER cbColSize = 0 ;
+ SQLDescribeCol(stHandles.hstmt, 2, szColBuffer, MAX_COL_NAME-1, &cbNameLen, &cbSqlType, (unsigned long*)&cbColSize, &cbColScale, &cbNullable) ;
+
+ switch(cbSqlType){
+ case SQL_NUMERIC:
+ pBuffer = &IntResult ;
+ cbSize = sizeof(SQLINTEGER) ;
+ cbTarget = SQL_C_ULONG ;
+ case SQL_INTEGER:
+ pBuffer = &IntResult ;
+ cbSize = sizeof(SQLINTEGER) ;
+ cbTarget = SQL_C_LONG ;
+ break ;
+ case SQL_FLOAT:
+ pBuffer = &FloatResult ;
+ cbSize = sizeof(SQLFLOAT) ;
+ cbTarget = SQL_C_FLOAT ;
+ break ;
+ case SQL_DOUBLE:
+ pBuffer = &DoubleResult ;
+ cbSize = sizeof(SQLDOUBLE) ;
+ cbTarget = SQL_C_DOUBLE ;
+ break ;
+ default:
+ printf("\nUndefined result type: %d\n", cbSqlType) ;
+ break ;
+ }
+
+ switch(attrType){
+ case T_INT:
+ ODBC_FN(SQLBindCol(stHandles.hstmt, 1, SQL_C_SLONG, (void*)&BindInt, sizeof(SQLINTEGER), &cbLen), retcode) ;
+ break ;
+ case T_FLOAT:
+ ODBC_FN(SQLBindCol(stHandles.hstmt, 1, SQL_C_FLOAT, (void*)&BindFloat, sizeof(SQLFLOAT), &cbLen), retcode) ;
+ break ;
+ /* case T_DOUBLE:
+ ODBC_FN(SQLBindCol(stHandles.hstmt, 1, SQL_C_DOUBLE, (void*)&BindDouble, sizeof(SQLDOUBLE), &cbLen), retcode) ;
+ break ;
+ */
+ default:
+ break ;
+ }
+
+ ODBC_FN(SQLBindCol(stHandles.hstmt, 2, cbTarget, pBuffer, cbSize, &cbLen), retcode) ;
+
+ retcode = SQLFetch(stHandles.hstmt) ;
+
+ if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
+ switch(attrType){
+ case T_INT:
+ switch(cbSqlType){
+ case SQL_INTEGER:
+ nVerRet = VerifyArthOp((int*)&BindInt, pValue, (int*)pBuffer, op) ;
+ break ;
+ case SQL_FLOAT:
+ nVerRet = VerifyArthOp((int*)&BindInt, pValue, (float*)pBuffer, op) ;
+ break ;
+ case SQL_DOUBLE:
+ nVerRet = VerifyArthOp((int*)&BindInt, pValue, (double*)pBuffer, op) ;
+ break ;
+ case SQL_NUMERIC:
+ nVerRet = VerifyArthOp((int*)&BindInt, pValue, (int*)pBuffer, op) ;
+ break ;
+ default:
+ break ;
+ }
+ break ;
+
+ case T_FLOAT:
+ switch(cbSqlType){
+ case SQL_INTEGER:
+ nVerRet = VerifyArthOp((float*)&BindFloat, pValue, (int*)pBuffer, op) ;
+ break ;
+ case SQL_FLOAT:
+ nVerRet = VerifyArthOp((float*)&BindFloat, pValue, (float*)pBuffer, op) ;
+ break ;
+ case SQL_DOUBLE:
+ nVerRet = VerifyArthOp((float*)&BindFloat, pValue, (double*)pBuffer, op) ;
+ break ;
+ default:
+ break ;
+ }
+ break ;
+ /* case T_DOUBLE:
+ switch(cbSqlType){
+ case SQL_INTEGER:
+ nVerRet = VerifyArthOp((double*)&BindDouble, pValue, (int*)pBuffer, op) ;
+ break ;
+ case SQL_FLOAT:
+ nVerRet = VerifyArthOp((double*)&BindDouble, pValue, (float*)pBuffer, op) ;
+ break ;
+ case SQL_DOUBLE:
+ nVerRet = VerifyArthOp((double*)&BindDouble, pValue, (double*)pBuffer, op) ;
+ break ;
+ default:
+ break ;
+ }
+ break ;
+ */
+ default:
+ break ;
+ }
+ if(-1 == nVerRet){
+ printf("\nVerification failed.\n") ;
+ return nVerRet ;
+ }else if(SQL_NO_DATA == retcode){
+ break ;
+ }
+ }else{
+
+ HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
+ }
+
+ SQLCloseCursor(stHandles.hstmt) ;
+ }
+
+ GetHandles(&stHandles, FREE, 0) ;
+
+ return nVerRet ;
+}
+
+
+
+
+/*************************************************
+Function: Join()
+*************************************************/
+SQLRETURN Join(char* szTable, int nTables, int nCol, join_type joinType){
+
+ SQLRETURN retcode = SQL_ERROR ;
+ SQLCHAR szStmt[MAX_SQL_STMT] = { 0 } ;
+ SQLCHAR szEndBuffer[MAX_STR_LEN] = { 0 } ;
+ SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
+ SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
+
+ ODBC_HANDLES stHandles ;
+ memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
+
+ int c = 0, t = 0 ;
+
+ GetHandles(&stHandles, GET, 0) ;
+
+ for(c = 0 ; c < nCol ; ++c) {
+
+ switch(joinType){
+ case ITSELF:
+ sprintf((char*)szStmt, "SELECT * FROM %s, %s", (char*)szTable, (char*)szTable) ;
+ break ;
+ case EQUI:
+ break ;
+ case NON_EQUI:
+ break ;
+ case INNER:
+ break ;
+ case OUTTER:
+ break ;
+ default:
+ break ;
+ }
+ }
+
+GetHandles(&stHandles, FREE, 0) ;
+
+return retcode ;
+
+}
+
+
+
+SQLRETURN GetResults(SQLHSTMT){
+
+ SQLRETURN retcode = SQL_ERROR ;
+
+ return retcode ;
+}
+
+/*
+
+int createTables(char* szTableName, int nTables){
+
+ for (int i = 0; i < nNoOfCol; i++){
+ snprintf(attrName[i], MAXSTRLEN, "COL%d", i) ;
+ }
+
+ for (int i = 0; i < nTables; i++){
+ snprintf(tableName[i], MAXSTRLEN, "TAB%d", i) ;
+ }
+
+ for(unsigned i = 0; i < nTables; i++){
+
+ ndbout << "Creating " << szTableName[i] << "... " ;
+
+ NDBT_Table tmpTable(szTableName[i]) ;
+
+ tmpTable.setStoredTable(!theTempTable) ;
+
+ tmpTable.addAttribute(NDBT_Attribute(attrName[0],
+ UnSigned,
+ 4, // 4 Bytes
+ TupleKey));
+ }
+
+
+ for (int j = 1 ; j < nNoOfCol ; j++)
+ tmpTable.addAttribute(NDBT_Attribute(attrName[j], UnSigned, 4*tAttributeSize)) ;
+
+ if(tmpTable.createTableInDb(pMyNdb) == -1){
+ return -1 ;
+ }
+
+ ndbout << "done" << endl ;
+
+ return 0;
+}
+*/
+
+/*************************************************
+Function: createTables()
+Uses NDB API to create tables for the tests
+*************************************************/
+
+int createTables(char* szTableName, int nTables){
+
+ Ndb * pNdb = new Ndb("TEST_DB") ;
+ pNdb->init();
+
+ ndbout << "Waiting for ndb to become ready..." <<endl;
+ if (pNdb->waitUntilReady(10000) != 0){
+ ndbout << "NDB is not ready" << endl;
+ ndbout << "Benchmark failed!" << endl;
+ delete pNdb ;
+ return -1 ;
+ }
+
+ NdbSchemaCon *MySchemaTransaction = NULL ;
+ NdbSchemaOp *MySchemaOp = NULL ;
+ int check = -1 ;
+ char szColNameBuffer[MAX_COL_NAME] = { 0 } ;
+ int tLoadFactor = 80 ;
+
+ for(int i=0 ; i < nTables ; ++i) {
+
+ ndbout << "Creating " << (char*)(szTableName+MAX_TABLE_NAME*i) << "..." << endl ;
+
+ MySchemaTransaction = pNdb->startSchemaTransaction() ;
+ //printf("MySchemaTransaction - OK\n") ;
+ if(MySchemaTransaction == NULL){
+ printf("MySchemaTransaction is NULL\n") ;
+ delete pNdb ;
+ return -1 ;
+ }
+
+ MySchemaOp = MySchemaTransaction->getNdbSchemaOp();
+ //printf("MySchemaTransaction->getNdb... - OK\n") ;
+ if(MySchemaOp == NULL){
+ printf("MySchemaOp is NULL\n") ;
+ delete pNdb ;
+ return -1 ;
+ }
+
+ check = MySchemaOp->createTable( (const char*)(szTableName+MAX_TABLE_NAME*i)
+ ,8 // Table Size
+ ,TupleKey // Key Type
+ ,40 // Nr of Pages
+ ,All
+ ,6
+ ,(tLoadFactor - 5)
+ ,(tLoadFactor)
+ ,1
+ ,0
+ );
+
+ if (check == -1){
+ printf("MySchemaOp->createTable failed\n") ;
+ delete pNdb ;
+ return -1 ;
+ }
+
+ snprintf(szColNameBuffer, MAX_COL_NAME, "COL%d", 0) ;
+ check = MySchemaOp->createAttribute( szColNameBuffer,
+ TupleKey,
+ 32,
+ PKSIZE,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if (check == -1){
+ printf("MySchemaOp->createAttribute() #1 failed\n") ;
+ delete pNdb ;
+ return -1 ;
+ }
+
+ for (int j = 1; j < nNoOfCol ; j++){
+ snprintf(szColNameBuffer, MAX_COL_NAME, "COL%d", j) ;
+ check = MySchemaOp->createAttribute(szColNameBuffer,
+ NoKey,
+ 32,
+ tAttributeSize,
+ UnSigned,
+ MMBased,
+ NotNullAttribute );
+
+ if (check == -1){
+ printf("MySchemaOp->createAttribute() #2 failed\n") ;
+ delete pNdb ;
+ return -1;
+ }
+ }
+
+ if (MySchemaTransaction->execute() == -1){
+ printf("MySchemaTransaction->execute() failed\n") ;
+ printf("%s\n", MySchemaTransaction->getNdbError().message) ;
+ return -1 ;
+ delete pNdb ;
+ }
+
+ pNdb->closeSchemaTransaction(MySchemaTransaction);
+ }
+
+ return 0;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/SQL99_test/SQL99_test.h b/storage/ndb/test/odbc/SQL99_test/SQL99_test.h
new file mode 100644
index 00000000000..1c49f4a9a51
--- /dev/null
+++ b/storage/ndb/test/odbc/SQL99_test/SQL99_test.h
@@ -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 */
+
+
+#include <ndb_types.h>
+#include <NdbThread.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NDBT.hpp>
+#include <sqlext.h>
+#include <stdio.h>
+//#include <stdlib.h>
+#include <unistd.h>
+//#include <windows.h>
+//#include <process.h>
+
+#define MAX_STR_LEN 128
+#define MAX_TABLE_NAME 32
+#define MAX_COL_NAME 32
+#define MAX_SQL_STMT 2048
+#define MAX_VALUE_LEN 32
+#define MAX_CHAR_ATTR_LEN 24
+#define NUM_COL_ARITHM 2
+#define FLTDEV 0.0001
+//#define DBLDEV 0.000000001
+
+#define REPORTERROR(fn, str) ReportError(fn, str, __FILE__, __LINE__)
+#define REPORT(str) printf((str))
+
+#define ATTR_TYPE_SWITCH(buffer, ptr, attr) switch(attr){ \
+ case T_INT:\
+ sprintf((char*)(buffer),"%d", (int)(ptr)) ;\
+ break ;\
+ case T_FLOAT:\
+ sprintf((char*)(buffer),"%f", (float)(ptr)) ;\
+ break ;\
+ default:\
+ break ;\
+ }
+
+#define ATTR_TYPE_SWITCH_T(value, attr) switch(attr){ \
+ case T_INT:\
+ printf("%d \t", (int)(value)) ;\
+ break ;\
+ case T_FLOAT:\
+ printf("%f \t", (float)(value)) ;\
+ break ;\
+ default:\
+ break ;\
+ }
+
+#define ATTR_TYPE_SWITCH_AGR(str, value_A, value_B, value_C, attr) switch(attr){ \
+ case T_INT:\
+ printf("%s\t%d %d\t\t\t%d\n\n", str, value_A, (int)value_B, (int)value_C) ; break ;\
+ case T_FLOAT:\
+ printf("%s\t%d %f\t\t\t%d\n\n", str, value_A, value_B, (int)value_C) ; break ;\
+ default:\
+ break ;\
+ }
+
+
+#define ODBC_FN(fn, rc) rc = ((((fn)))) ; if(SQL_SUCCESS == rc || SQL_SUCCESS_WITH_INFO == rc){;}else ReportError("ODBC function", "failed in ", __FILE__, __LINE__)
+
+
+typedef enum attr_type_tag {
+ T_INT,
+ T_FLOAT,
+// T_DOUBLE,
+ T_CHAR
+} attr_type ;
+
+typedef enum aggr_fn_tag {
+ FN_COUNT,
+ FN_SUM,
+ FN_AVG,
+ FN_MAX,
+ FN_MIN,
+ FN_VARIANCE,
+ FN_STDDEV
+} aggr_fn ;
+
+typedef enum join_type_tag {
+ ITSELF,
+ EQUI,
+ NON_EQUI,
+ INNER,
+ OUTTER
+} join_type ;
+
+typedef enum arth_op_tag {
+ MINUS,
+ PLUS,
+ MULTI,
+ DIVIDE,
+ MODULO
+} arth_op ;
+
+typedef struct ODBC_HANDLES_tag{
+ SQLHENV henv ;
+ SQLHDBC hdbc ;
+ SQLHSTMT hstmt ;
+} ODBC_HANDLES ;
+
+typedef enum handle_op_tag{
+ GET,
+ FREE
+} handle_op ;
+
+typedef enum test_case_tag {
+ NUMERIC_DATA_TYPES,
+ CHAR_DATA_TYPES,
+ IDENTIFIERS,
+ BASIC_QUERY,
+ PREDICATE_SEARCH,
+ DATA_MANIPULATION,
+ NULL_SUPPORT,
+ BASIC_CONSTRAINTS,
+ TRANSACTION,
+ SET_FUNCTIONS,
+ BASIC_SCHEMA,
+ JOINED_TABLE,
+ ALL
+} test_case ;
+
+typedef enum status_tag{
+ S_STOP,
+ S_IDLE,
+ S_STARTED,
+ S_GET_BUSY,
+ S_BUSY,
+ S_EXIT
+} status ;
+
+typedef enum type_tag {
+ T_INSERT,
+ T_READ,
+ T_UPDATE,
+ T_DELETE,
+ T_READ_VERIFY,
+ T_DELETE_VERIFY,
+ T_WAIT
+} type ;
+
+typedef struct PARAMS_tag {
+ int nThreadID ;
+ int nError ;
+ int nVerifyFlag ;
+ status thread_status ;
+ status report_status ;
+ type op_type ;
+ void* pThreadRef ;
+ char szTableName[MAX_TABLE_NAME] ;
+} PARAMS ;
+
+typedef enum table_opt_tag {
+ CREATE,
+ DROP
+} table_opt ;
+
+static char szANSI[] ="0123456789ABCEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ;
+
+void ReportError(char* szFn, char* szBuffer, char* szFile, int iLine) ;
+void HandleError(void*, SQLSMALLINT) ;
+SQLRETURN GetHandles(ODBC_HANDLES*, handle_op, bool) ;
+SQLRETURN AggregateFn(aggr_fn, char*, int, double*, double*, attr_type) ;
+SQLRETURN GetDriverAndSourceInfo(SQLHDBC) ;
+SQLRETURN Join(char*, join_type) ;
+SQLRETURN GetResults(SQLHSTMT) ;
+int ArithOp(char*, int, float*, attr_type, arth_op) ;
+void ParseArguments(int argc, const char** argv) ;
+void* ThreadFnInt(void*) ;
+void* ThreadFnFloat(void*) ;
+//void* ThreadFnDouble(void*) ;
+void* ThreadFnChar(void*) ;
+inline void AssignTableNames(char* szBuffer, int nTables) ;
+SQLRETURN CreateDemoTables(char*, int, table_opt) ;
+inline void StartThreads(PARAMS*, void*, int, char*, attr_type, UintPtr*) ;
+inline void SetThreadOperationType(PARAMS*, type) ;
+inline int WaitForThreads(PARAMS*) ;
+inline void StopThreads(PARAMS*, UintPtr*) ;
+inline void PrintAll(char* szTableName, int, attr_type) ;
+void AssignRefCharValues(char*, bool) ;
+
+template <class T, class V>
+int VerifyArthOp(V* tValue, float* tOperand, T* tRes, arth_op op){
+
+ int nResult = 0 ;
+ int nValue = 0, nOperand = 0 ;
+
+ switch(op){
+ case MINUS:
+ if(FLTDEV < abs((*tValue - *tOperand) - *tRes))
+ nResult = -1 ;
+ break ;
+ case PLUS:
+ if(FLTDEV < abs((*tValue + *tOperand) - *tRes))
+ nResult = -1 ;
+ break ;
+ case MULTI:
+ if(FLTDEV < abs((*tValue * *tOperand) - *tRes))
+ nResult = -1 ;
+ break ;
+ case DIVIDE:
+ if(FLTDEV < abs((*tValue / *tOperand) - *tRes))
+ nResult = -1 ;
+ break ;
+ case MODULO:
+ nValue = *tValue ;
+ nOperand = *tOperand ;
+ if(*tRes != (nValue % nOperand))
+ nResult = -1 ;
+ break ;
+ }
+
+ return nResult ;
+}
+
+template <class P> void AssignRefNumValues(P* pRef, attr_type attrType, bool bVerbose) {
+
+ int count = 0, rows = 0, nThreadOffset = 0, nRowOffset = 0 ;
+ P* p = (P*)pRef ;
+
+ float fRandomBase = (rand()*rand()) % 100;
+ for(int c = 0 ; c < nNoOfThreads ; ++c){
+ nThreadOffset = nNoOfRows*nNoOfCol*c ;
+ for(int d = 0 ; d < nNoOfRows ; ++d){
+ nRowOffset = nNoOfCol*d ; ++rows ;
+ for(int i = 0 ; i < nNoOfCol ; ++i){
+ (p[nThreadOffset + nRowOffset + i]) = (fRandomBase*(c+1) + (d+3)*7 + i)/1.1034093201 ;
+ ++count ;
+ if(bVerbose){
+ ATTR_TYPE_SWITCH_T(p[nThreadOffset + nRowOffset + i], AttributeType) ;
+ }
+ }
+ if(bVerbose) { printf("\n") ; NdbSleep_MilliSleep(10) ;
+ }
+ }
+ }
+
+ if(bVerbose){
+ printf("_____________________") ;
+ printf("\nRows: %d Values: %d\n\n", rows, count) ;
+ }
+
+ return ;
+}
+
+
diff --git a/storage/ndb/test/odbc/client/Makefile b/storage/ndb/test/odbc/client/Makefile
new file mode 100644
index 00000000000..4b962f5b65a
--- /dev/null
+++ b/storage/ndb/test/odbc/client/Makefile
@@ -0,0 +1,95 @@
+include .defs.mk
+
+TYPE := odbcclient
+#TYPE := odbcdriver
+
+BIN_TARGET := testOdbcClient
+#BIN_TARGET := testodbc2
+
+
+# Source files of non-templated classes (.C files)
+SOURCES = main.cpp \
+ SQLFetchTest.cpp \
+ SQLDisconnectTest.cpp \
+ SQLTablesTest.cpp \
+ SQLGetInfoTest.cpp \
+ SQLGetTypeInfoTest.cpp \
+ SQLGetFunctionsTest.cpp \
+ SQLGetDataTest.cpp \
+ SQLCancelTest.cpp \
+ SQLTransactTest.cpp \
+ SQLGetCursorNameTest.cpp \
+ SQLSetCursorNameTest.cpp \
+ SQLRowCountTest.cpp \
+ SQLNumResultColsTest.cpp \
+ SQLDescribeColTest.cpp \
+ SQLExecDirectTest.cpp \
+ SQLColAttributeTest.cpp \
+ SQLColAttributeTest1.cpp \
+ SQLColAttributeTest2.cpp \
+ SQLColAttributeTest3.cpp \
+ SQLBindColTest.cpp \
+ SQLDriverConnectTest.cpp \
+ SQLPrepareTest.cpp \
+ SQLGetDiagRecSimpleTest.cpp \
+ SQLConnectTest.cpp
+
+XSOURCES = testodbc2.cpp
+XSOURCES = \
+ main.cpp \
+ SQLDriverConnectTest.cpp \
+ SQLPrepareTest.cpp \
+ SQLMoreResultsTest.cpp \
+ SQLGetStmtAttrTest.cpp \
+ SQLGetEnvAttrTest.cpp \
+ SQLGetConnectAttrTest.cpp \
+ SQLExecuteTest.cpp \
+ SQLExecDirectTest.cpp \
+ SQLDisconnectTest.cpp \
+ SQLCloseCursorTest.cpp \
+ SQLCancelTest.cpp \
+ SQLBindColTest.cpp \
+ SQLDescribeColTest.cpp \
+ SQLGetTypeInfoTest.cpp \
+ SQLGetFunctionsTest.cpp \
+ SQLNumResultColsTest.cpp \
+ SQLSetDescFieldTest.cpp \
+ SQLGetDescRecTest.cpp \
+ SQLEndTranTest.cpp \
+ SQLGetInfoTest.cpp \
+ SQLConnectTest.cpp \
+ SQLAllocHandleTest.cpp \
+ SQLAllocEnvTest.cpp \
+ SQLRowCountTest.cpp \
+ SQLFetchScrollTest.cpp \
+ SQLFetchTest.cpp \
+ SQLGetDescFieldTest.cpp \
+ SQLSetDescRecTest.cpp \
+ SQLFreeHandleTest.cpp
+
+ifeq ($(TYPE),odbcdriver)
+LIBS_SPEC += \
+ -lodbcdriver_pic \
+ -lodbchandles_pic \
+ -lodbccodegen_pic \
+ -lodbccompiler_pic \
+ -lodbcexecutor_pic \
+ -lodbccommon_pic \
+ -lodbcdictionary_pic \
+ -lNDBT \
+ -lportlib
+endif
+
+ifeq ($(TYPE),odbcclient)
+LIBS_SPEC += \
+ -lportlib \
+ -lNDBT
+endif
+
+CCFLAGS_LOC += -I/usr/local/include \
+ -I$(NDB_TOP)/include/ndbapi \
+ -I$(NDB_TOP)/test/include
+
+include $(NDB_TOP)/Epilogue.mk
+#LIBS_LOC += -L/usr/local/opt/iODBC/lib
+#LIBS_SPEC = -liodbc -lNDBT -lportlib
diff --git a/storage/ndb/test/odbc/client/NDBT_ALLOCHANDLE.cpp b/storage/ndb/test/odbc/client/NDBT_ALLOCHANDLE.cpp
new file mode 100644
index 00000000000..336f4a46554
--- /dev/null
+++ b/storage/ndb/test/odbc/client/NDBT_ALLOCHANDLE.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 "common.h"
+#include <NdbTest.hpp>
+#include <NdbMain.h>
+
+SQLRETURN SQLHENV_check, SQLHENV_FREE_check;
+
+int NDBT_ALLOCHANDLE()
+{
+ /*****************************HENV Handle*****************************/
+
+ SQLHENV henv;
+ SQLHENV_check = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
+
+ if (SQLHENV_check == -1) {
+ return(-1);
+ //return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ if (SQLHENV_check == 0) {
+ return 0;
+ }
+
+ SQLHENV_FREE_check = SQLFreeHandle(SQL_HANDLE_ENV, henv);
+
+ if (SQLHENV_FREE_check == -1) {
+ // Deallocate any allocated memory, if it exists
+ return(-1);
+ //return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ if (SQLHENV_FREE_check == 0) {
+ return 0;
+ }
+
+ return 0;
+}
+
diff --git a/storage/ndb/test/odbc/client/NDBT_ALLOCHANDLE_HDBC.cpp b/storage/ndb/test/odbc/client/NDBT_ALLOCHANDLE_HDBC.cpp
new file mode 100644
index 00000000000..8477a71edbf
--- /dev/null
+++ b/storage/ndb/test/odbc/client/NDBT_ALLOCHANDLE_HDBC.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 "common.h"
+#include <NdbTest.hpp>
+#include <NdbMain.h>
+
+SQLRETURN SQLHENVFREE_check, SQLHDBC_check;
+
+
+// NDB_COMMAND(SQLTest1, ......., 65535)
+int NDBT_ALLOCHANDLE_HDBC()
+{
+
+ SQLHENV henv;
+ SQLHDBC hdbc;
+
+ /*****************************HDBC Handle*****************************/
+ SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
+
+ SQLHDBC_check = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
+
+ if (SQLHDBC_check == -1) {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ if (SQLHDBC_check == 0) {
+ return 0;
+ }
+
+ SQLHENVFREE_check = SQLFreeHandle(SQL_HANDLE_ENV, henv);
+
+ if (SQLHENVFREE_check == -1) {
+ // Deallocate any allocated memory, if it exists
+ return(-1);
+ //return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ if (SQLHENVFREE_check == 0) {
+ return 0;
+ }
+}
+
+
+
+
diff --git a/storage/ndb/test/odbc/client/NDBT_SQLConnect.cpp b/storage/ndb/test/odbc/client/NDBT_SQLConnect.cpp
new file mode 100644
index 00000000000..da97ffebea4
--- /dev/null
+++ b/storage/ndb/test/odbc/client/NDBT_SQLConnect.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 "common.h"
+#include <NdbTest.hpp>
+#include <NdbMain.h>
+
+SQLRETURN retcode, SQLSTATEs;
+SQLHENV henv;
+SQLHDBC hdbc;
+
+void NDBT_Connect_DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+
+int NDBT_SQLConnect()
+{
+
+ /*****************************SQLConnect AutoTest*****************************/
+
+ // Allocate An Environment Handle
+ SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
+
+ // This part does not include in sqlcli.h, it is only in ODBC
+ // Set the ODBC application Version to 3.x
+ // SQLSetEnvattr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTERGER);
+
+ // Allocate A Connection Handle
+ SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
+
+ // Connect to NDB
+ retcode = SQLConnect(hdbc,
+ (SQLCHAR*) "Sales",
+ 5,
+ (SQLCHAR*) "JohnS",
+ 5,
+ (SQLCHAR*) "Sesame",
+ 6);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+ else
+ { if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ NDBT_Connect_DisplayError(SQL_HANDLE_DBC, hdbc);}
+
+ // Free the Connection Handle
+ SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
+
+ // Free the Environment Handle
+ SQLFreeHandle(SQL_HANDLE_ENV, henv);
+
+ return 0;
+}
+
+
+void NDBT_Connect_DisplayError(SQLSMALLINT HandleType, SQLHDBC InputHandle)
+{
+ SQLRETURN Sqlstate;
+ int i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
diff --git a/storage/ndb/test/odbc/client/NDBT_SQLPrepare.cpp b/storage/ndb/test/odbc/client/NDBT_SQLPrepare.cpp
new file mode 100644
index 00000000000..4aaff6a7df9
--- /dev/null
+++ b/storage/ndb/test/odbc/client/NDBT_SQLPrepare.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 <NdbOut.hpp>
+#include <sqlext.h>
+#include <stdio.h>
+
+#include <NdbTest.hpp>
+#include <NdbMain.h>
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN SQLPrepare_retcode, SQLAllocHandl_retcode, SQLSTATEs;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+void NDBT_SQLPrepare_DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+
+ // Execute a statement to retrieve rows from the Customers table. We can
+ // create the table and inside rows into NDB by invoking SQLExecute() or
+ // another program called TestDirectSQL
+
+int NDBT_SQLPrepare()
+{
+ // Allocate An Environment Handle
+ SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
+
+ // Allocate A Connection Handle
+ SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
+
+ // Allocate A Connection Handle
+ SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
+
+ // Connecte to database
+ SQLConnect(hdbc, (SQLCHAR*) "Sales", 5, (SQLCHAR*) "JohnS", 5, (SQLCHAR*) "Sesame", 6);
+
+ // Allocate A Statement Handle
+ SQLAllocHandl_retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
+
+ /* We can change the SQL statement in the SQLPrepare() function according to the requirement of Johnny. */
+ /* The order of the SQL statement could be CREATE, INSERT, UPDATE, SELECT, DELETE or another special SQL */
+
+ if (SQLAllocHandl_retcode == SQL_SUCCESS){
+ SQLPrepare_retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", 56);
+
+ if (SQLPrepare_retcode == SQL_INVALID_HANDLE)
+ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE and SQL_SUCCESS still appeared. Please check programm" << endl;
+
+ if (SQLPrepare_retcode == SQL_ERROR || SQLPrepare_retcode == SQL_SUCCESS_WITH_INFO)
+ NDBT_SQLPrepare_DisplayError(SQL_HANDLE_STMT, hstmt);
+
+ SQLExecute(hstmt);
+
+ SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
+ }
+
+ // Disconnect from the database before free Connection Handle and Environment Handle
+ SQLDisconnect(hdbc);
+
+ // Free the Connection Handle
+ SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
+
+ // Free the Environment Handle
+ SQLFreeHandle(SQL_HANDLE_ENV, henv);
+
+ return 0;
+
+ }
+
+
+void NDBT_SQLPrepare_DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLAllocEnvTest.cpp b/storage/ndb/test/odbc/client/SQLAllocEnvTest.cpp
new file mode 100644
index 00000000000..ce50c4b7ccd
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLAllocEnvTest.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 "common.h"
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHENV henv;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+
+void sqlallocenv_deal_with_HENV(SQLSMALLINT HandleType, SQLHENV InputHandle);
+void sqlallocenv_deal_with_HDBC(SQLSMALLINT HandleType, SQLHDBC InputHandle);
+
+void DisplayError_HDBC_free(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDBC InputHandle);
+void DisplayError_HENV_free(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHENV InputHandle);
+
+
+int SQLAllocEnvTest()
+{
+
+/* Environment test for SQLAllocEnv() */
+ndbout << "Environment test for SQLAllocEnv()" << endl;
+//SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
+
+sqlallocenv_deal_with_HENV(SQL_HANDLE_ENV, henv);
+
+//SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
+//SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
+sqlallocenv_deal_with_HDBC(SQL_HANDLE_DBC, hdbc);
+
+return 0;
+
+}
+
+void sqlallocenv_deal_with_HDBC(SQLSMALLINT HandleType, SQLHDBC InputHandle)
+{
+ SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
+ retcode = SQLAllocHandle(HandleType, henv, &InputHandle);
+
+ ndbout << "the HandleType is : " << HandleType << endl;
+ ndbout << "the InputHandle is SQLHDBC:" << InputHandle << endl;
+ ndbout << "retcode = " << retcode << endl;
+
+ /* ***
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+ DisplayError_HDBC_free(Sqlstate, HandleType, InputHandle);
+
+ i ++;
+ }
+ }
+ *** */
+}
+
+void sqlallocenv_deal_with_HENV(SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ retcode = SQLAllocEnv(&InputHandle);
+
+ ndbout << "the HandleType is : " << HandleType << endl;
+ ndbout << "the InputHandle is SQLHENV:" << InputHandle << endl;
+ ndbout << "retcode = " << retcode << endl;
+ /*
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ DisplayError_HENV_free(Sqlstate, HandleType, InputHandle);
+
+ i ++;
+ }
+ }
+ */
+ }
+
+
+void DisplayError_HENV_free(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
+void DisplayError_HDBC_free(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDBC InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
diff --git a/storage/ndb/test/odbc/client/SQLAllocHandleTest.cpp b/storage/ndb/test/odbc/client/SQLAllocHandleTest.cpp
new file mode 100644
index 00000000000..0c51e2e46b7
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLAllocHandleTest.cpp
@@ -0,0 +1,314 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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.h"
+
+using namespace std;
+
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+long strangehandle;
+
+void handle_deal_with_HSTMT(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+void handle_deal_with_HENV(SQLSMALLINT HandleType, SQLHENV InputHandle);
+void handle_deal_with_HDESC(SQLSMALLINT HandleType, SQLHDESC InputHandle);
+void handle_deal_with_HDBC(SQLSMALLINT HandleType, SQLHDBC InputHandle);
+//void handle_deal_with_int(SQLSMALLINT HandleType, long InputHandle);
+
+void DisplayError_HDBC(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDBC InputHandle);
+void DisplayError_HSTMT(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+void DisplayError_HENV(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHENV InputHandle);
+void DisplayError_HDESC(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDESC InputHandle);
+//void DisplayError_int(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, int InputHandle);
+
+int SQLAllocHandleTest()
+{
+
+strangehandle = 6;
+
+/*Allocate environment handle */
+
+//retcode = SQLFreeHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE);
+
+/* ENV */
+ndbout << endl;
+ndbout << "The HandleType: Allocate Environment handle" << endl;
+ndbout << endl;
+
+handle_deal_with_HENV(SQL_HANDLE_ENV, SQL_NULL_HANDLE);
+
+handle_deal_with_HENV(SQL_HANDLE_ENV, henv);
+
+handle_deal_with_HDBC(SQL_HANDLE_ENV, hdbc);
+
+handle_deal_with_HSTMT(SQL_HANDLE_ENV, hstmt);
+
+handle_deal_with_HDESC(SQL_HANDLE_ENV, hdesc);
+
+//handle_deal_with_int(SQL_HANDLE_ENV, strangehandle);
+
+/* DBC */
+ndbout << endl;
+ndbout << "The HandleType: Allocate Connection handle" << endl;
+ndbout << endl;
+
+handle_deal_with_HDBC(SQL_HANDLE_DBC, SQL_NULL_HANDLE);
+
+handle_deal_with_HENV(SQL_HANDLE_DBC, henv);
+
+handle_deal_with_HDBC(SQL_HANDLE_DBC, hdbc);
+
+handle_deal_with_HSTMT(SQL_HANDLE_DBC, hstmt);
+
+handle_deal_with_HDESC(SQL_HANDLE_DBC, hdesc);
+
+//handle_deal_with_int(SQL_HANDLE_DBC, strangehandle);
+
+/* STMT */
+ndbout << endl;
+ndbout << "The HandleType: Allocate Statement handle" << endl;
+ndbout << endl;
+
+handle_deal_with_HSTMT(SQL_HANDLE_STMT, SQL_NULL_HANDLE);
+
+handle_deal_with_HENV(SQL_HANDLE_STMT, henv);
+
+handle_deal_with_HDBC(SQL_HANDLE_STMT, hdbc);
+
+handle_deal_with_HSTMT(SQL_HANDLE_STMT, hstmt);
+
+handle_deal_with_HDESC(SQL_HANDLE_STMT, hdesc);
+
+//handle_deal_with_int(SQL_HANDLE_STMT, strangehandle);
+
+
+/* DESC */
+ndbout << endl;
+ndbout << "The HandType: Allocate Descriptor handle" << endl;
+ndbout << endl;
+
+handle_deal_with_HDESC(SQL_HANDLE_DESC, SQL_NULL_HANDLE);
+
+handle_deal_with_HENV(SQL_HANDLE_DESC, henv);
+
+handle_deal_with_HDBC(SQL_HANDLE_DESC, hdbc);
+
+handle_deal_with_HSTMT(SQL_HANDLE_DESC, hstmt);
+
+handle_deal_with_HDESC(SQL_HANDLE_DESC, hdesc);
+
+//handle_deal_with_int(SQL_HANDLE_DESC, strangehandle);
+
+
+/* strangehandle */
+ndbout << endl;
+ndbout << "The HandType: strangehandle" << endl;
+ndbout << endl;
+
+//handle_deal_with_int(strangehandle, SQL_NULL_HANDLE);
+
+handle_deal_with_HENV(strangehandle, henv);
+
+handle_deal_with_HDBC(strangehandle, hdbc);
+
+handle_deal_with_HSTMT(strangehandle, hstmt);
+
+handle_deal_with_HDESC(strangehandle, hdesc);
+
+// handle_deal_with_int(strangehandle, strangehandle);
+
+return 0;
+
+}
+
+void handle_deal_with_HDBC(SQLSMALLINT HandleType, SQLHDBC InputHandle)
+{
+ SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+ retcode = SQLAllocHandle(HandleType, InputHandle, &hdbc);
+
+ ndbout << "the HandleType is : " << HandleType << endl;
+
+ ndbout << "the InputHandle is SQLHDBC:" << InputHandle << endl;
+
+ ndbout << "return &hdbc: " << (long)&hdbc << endl;
+ ndbout << "the retcode state is:" << retcode << endl;
+ ndbout << endl;
+
+ /*
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+ DisplayError_HDBC(Sqlstate, HandleType, InputHandle);
+ i ++;
+ }
+ }
+ */
+ }
+
+
+void handle_deal_with_HSTMT(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+ retcode = SQLAllocHandle(HandleType, InputHandle, &hstmt);
+
+ ndbout << "the HandleType is : " << HandleType << endl;
+
+ ndbout << "the InputHandle is SQLHSTMT:" << InputHandle << endl;
+
+ ndbout << "return &hstmt: " << (long)&hstmt << endl;
+ ndbout << "the output retcode is:" << retcode << endl;
+ ndbout << endl;
+ /*
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+ DisplayError_HSTMT(Sqlstate, HandleType, InputHandle);
+ i ++;
+ }
+ }
+ */
+ }
+
+void handle_deal_with_HENV(SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+ retcode = SQLAllocHandle(HandleType, InputHandle, &henv);
+
+ ndbout << "the HandleType is : " << HandleType << endl;
+
+ ndbout << "the InputHandle is SQLHENV:" << InputHandle << endl;
+
+ ndbout << "return &henv: " << (long)&henv << endl;
+ ndbout << "the output retcode is:" << retcode << endl;
+ ndbout << endl;
+ /*
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ DisplayError_HENV(Sqlstate, HandleType, InputHandle);
+ i ++;
+ }
+ }
+ */
+ }
+
+void handle_deal_with_HDESC(SQLSMALLINT HandleType, SQLHDESC InputHandle)
+{
+ SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+ retcode = SQLAllocHandle(HandleType, InputHandle, &hdesc);
+
+ ndbout << "the HandleType is : " << HandleType << endl;
+
+ ndbout << "the InputHandle is SQLHDESC:" << InputHandle << endl;
+
+ ndbout << "return &hdesc: " << (long)&hdesc << endl;
+ ndbout << "the output retcode is:" << retcode << endl;
+ ndbout << endl;
+ /*
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ DisplayError_HDESC(Sqlstate, HandleType, InputHandle);
+ i ++;
+ }
+ }
+ */
+ }
+
+
+//void handle_deal_with_int(SQLSMALLINT HandleType, long InputHandle)
+//{
+// SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+// retcode = SQLAllocHandle(HandleType, InputHandle, &InputHandle);
+//
+// ndbout << "the HandleType is: " << HandleType << endl;
+//
+// ndbout << "the InputHandle is stranghandle:" << InputHandle << endl;
+//
+// ndbout << "return &InputHandle: " << (long)&InputHandle << endl;
+// ndbout << "the output retcode is:" << retcode << endl;
+// ndbout << endl;
+// /*
+// if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+// i = 1;
+// while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+// Sqlstate, &NativeError, Msg, sizeof(Msg),
+// &MsgLen)) != SQL_NO_DATA) {
+//
+// DisplayError_int(Sqlstate, HandleType, InputHandle);
+//
+// i ++;
+// }
+// }
+// */
+// }
+
+
+void DisplayError_HENV(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
+
+void DisplayError_HDBC(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDBC InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
+void DisplayError_HSTMT(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
+void DisplayError_HDESC(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDESC InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
+//void DisplayError_int(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, int InputHandle)
+//{
+// ndbout << "the HandleType is:" << HandleType << endl;
+// ndbout << "the InputHandle is :" << InputHandle << endl;
+// ndbout << "the output state is:" << (char *)Sqlstate << endl;
+//}
diff --git a/storage/ndb/test/odbc/client/SQLAllocHandleTest_bf.cpp b/storage/ndb/test/odbc/client/SQLAllocHandleTest_bf.cpp
new file mode 100644
index 00000000000..7786675243a
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLAllocHandleTest_bf.cpp
@@ -0,0 +1,259 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include sqlcli.h;
+#include stdio.h;
+
+#define SQL_MAX_MESSAGE_LENGTH 200;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLCHAR SqlState[6], SQLStmt[100], Msg[SQL_MAX_MESSAGE_LENGTH];
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+
+struct handle_set
+{
+SQLHDBC hdbc_varible;
+SQLHSTMT hstmt_varible;
+SQLHENV henv_varible;
+SQLHDESC hdesc_varible;
+INTEGER strangehandle;
+}
+
+static int
+check(
+ SQLSMALLINT HandleType,
+ SQLHANDLE inputhandle,
+ SQLHANDLE *outputhandle,
+ SQLRETURN wantret,
+ char *wantSqlstate)
+{
+ SQLRETURN ret;
+ SQLCHAR Sqlstate[20];
+
+ ret = SQLAllocHandle(handletype, inputhandle, outputhandle);
+ if (ret != wantret) {
+ // report error
+ return -1;
+ }
+ if (ret == SQL_INVALID_HANDLE) {
+ // cannot get diag
+ return 0;
+ }
+ // TODO
+ ret = SQLGetDiagRec(HandleType, InputHandle, 1, Sqlstate, &NativeError, Msg, sizeof(Msg), &MsgLen);
+ if (strcmp(Sqlstate, wantSqlstate) != 0) {
+ // report error;
+ return -1;
+ }
+ return 0;
+}
+
+int
+Test_SQLAllocHandle()
+{
+ SQLRETURN ret;
+ SQLHENV henv;
+ SQLDBC dbc;
+ int i;
+
+ // env
+ check(SQL_HANDLE_ENV, SQL_NULL_HANDLE, 0, SQL_ERROR, "HY009");
+ for (i = 0; i < 1000; i++) {
+ if (i != SQL_NULL_HANDLE)
+ check(SQL_HANDLE_ENV, i, &henv, SQL_INVALID_HANDLE, 0);
+ }
+ if (check(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv, SQL_SUCCESS, "00000") < 0)
+ return -1;
+
+ // dbc
+ check(SQL_HANDLE_DBC, henv, 0, SQL_ERROR, "HY009");
+ for (i = 0; i < 1000; i++) {
+ if (i != henv)
+ check(SQL_HANDLE_DBC, i, &dbc, SQL_INVALID_HANDLE, 0);
+ }
+ if (check(SQL_HANDLE_DBC, henv, &dbc, SQL_SUCCESS, "00000") < 0)
+ return -1;
+
+ //??
+ check(SQL_HANDLE_ENV, dbc, 0, SQL_ERROR, "HY092");
+
+ // TODO
+ // stmt
+
+ return 0;
+}
+
+
+handle_set handlevalue;
+
+handlevalue.hdbc_varible = hdbc;
+handlevalue.hstmt_varible = hstmt;
+handlevalue.henv_varible = henv;
+handlevalue.hdesc_varible = hdesc;
+handlevalue.stranghandle = 67;
+
+ /*Allocate environment handle */
+//retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
+
+while (int j = 0; j++; j < 6) {
+ if ( j = 0 )
+ handle_deal_with(SQL_HANDLE_ENV, SQL_NULL_HANDLE, );
+
+ else if ( j = 1 )
+ handle_deal_with(SQL_HANDLE_ENV, handlevalue.henv_varible, );
+
+ else if ( j = 2 )
+ handle_deal_with(SQL_HANDLE_ENV, handlevalue.hdbc_varible, );
+
+ else if ( j = 3 )
+ handle_deal_with(SQL_HANDLE_ENV, handlevalue.hstmt_varible, );
+
+ else if ( j = 4 )
+ handle_deal_with(SQL_HANDLE_ENV, handlevalue.hdesc_varible, );
+
+ else
+ handle_deal_with(SQL_HANDLE_ENV, handlevalue.stranghandle, );
+
+ }
+
+ while (int j = 0; j++; j < 6) {
+ if ( j = 0 )
+ handle_deal_with(SQL_HANDLE_DBC, SQL_NULL_HANDLE, );
+
+ else if ( j = 1 )
+ handle_deal_with(SQL_HANDLE_DBC, handlevalue.henv_varible, );
+
+ else if ( j = 2 )
+ handle_deal_with(SQL_HANDLE_DBC, handlevalue.hdbc_varible, );
+
+ else if ( j = 3 )
+ handle_deal_with(SQL_HANDLE_DBC, handlevalue.hstmt_varible, );
+
+ else if ( j = 4 )
+ handle_deal_with(SQL_HANDLE_DBC, handlevalue.hdesc_varible, );
+
+ else
+ handle_deal_with(SQL_HANDLE_DBC, handlevalue.stranghandle, );
+
+ }
+
+
+ while (int j = 0; j++; j < 6) {
+ if ( j = 0 )
+ handle_deal_with(SQL_HANDLE_STMT, SQL_NULL_HANDLE, );
+
+ else if ( j = 1 )
+ handle_deal_with(SQL_HANDLE_STMT, handlevalue.henv_varible, );
+
+ else if ( j = 2 )
+ handle_deal_with(SQL_HANDLE_STMT, handlevalue.hdbc_varible, );
+
+ else if ( j = 3 )
+ handle_deal_with(SQL_HANDLE_STMT, handlevalue.hstmt_varible, );
+
+ else if ( j = 4 )
+ handle_deal_with(SQL_HANDLE_STMT, handlevalue.hdesc_varible, );
+
+ else
+ handle_deal_with(SQL_HANDLE_STMT, handlevalue.stranghandle, );
+
+ }
+
+
+
+ while (int j = 0; j++; j < 6) {
+ if ( j = 0 )
+ handle_deal_with(SQL_HANDLE_DESC, SQL_NULL_HANDLE, );
+
+ else if ( j = 1 )
+ handle_deal_with(SQL_HANDLE_DESC, handlevalue.henv_varible, );
+
+ else if ( j = 2 )
+ handle_deal_with(SQL_HANDLE_DESC, handlevalue.hdbc_varible, );
+
+ else if ( j = 3 )
+ handle_deal_with(SQL_HANDLE_DESC, handlevalue.hstmt_varible, );
+
+ else if ( j = 4 )
+ handle_deal_with(SQL_HANDLE_DESC, handlevalue.hdesc_varible, );
+
+ else
+ handle_deal_with(SQL_HANDLE_DESC, handlevalue.stranghandle, );
+
+ }
+
+ while (int j = 0; j++; j < 6) {
+ if ( j = 0 )
+ handle_deal_with(handlevalue.stranghandle, SQL_NULL_HANDLE, );
+
+ else if ( j = 1 )
+ handle_deal_with(handlevalue.stranghandle, handlevalue.henv_varible, );
+
+ else if ( j = 2 )
+ handle_deal_with(handlevalue.stranghandle, handlevalue.hdbc_varible, );
+
+ else if ( j = 3 )
+ handle_deal_with(handlevalue.stranghandle, handlevalue.hstmt_varible, );
+
+ else if ( j = 4 )
+ handle_deal_with(handlevalue.stranghandle handlevalue.hdesc_varible, );
+
+ else
+ handle_deal_with(handlevalue.stranghandle, handlevalue.stranghandle, );
+
+ }
+
+
+}
+
+
+void DisplayError(SQLCHAR SqlState[6], string SQLSTATE, string flag, SQLSMALLINT HandleType, SQLHANDLE InputHandle)
+{
+cout << "the operation is: " << flag << endl;
+cout << "the HandleType is:" << HandleType << endl;
+cout << "the InputHandle is :"<< InputHandle <<endl;
+cout << "the correct state is:" << SQLSTATE << endl;
+cout << "the output state is:" << Sqlstate << endl;
+}
+
+}
+
+
+void handle_deal_with(SQLSMALLINT HandleType, SQLHANDLE InputHandle, string SQLSTATE)
+{
+ retcode = SQLAllocHandle(HandleType, InputHandle, OutputHandlePtr);
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATE) {
+
+ if (SQLSTATE = Sqlstate )
+ DisplayError(SqlState, SQLSTATE, 'OK');
+
+ else
+ DisplayError(SqlState, SQLSTATE, 'failure');
+
+ i ++;
+ }
+ }
+ }
diff --git a/storage/ndb/test/odbc/client/SQLBindColTest.cpp b/storage/ndb/test/odbc/client/SQLBindColTest.cpp
new file mode 100644
index 00000000000..e2cd4ce73d1
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLBindColTest.cpp
@@ -0,0 +1,537 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+ /**
+ * @file SQLBindColTest.cpp
+ */
+#include <common.hpp>
+using namespace std;
+
+#define BindCol_NAME_LEN 10
+#define BindCol_PHONE_LEN 10
+#define BindCol_ADDRESS_LEN 10
+#define BindCol_Price_LEN 10
+#define BindCol_Weight_LEN 10
+#define BindCol_Tax_LEN 10
+
+#define BindCol_SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+//SQLHDBC BindCol_hdbc;
+//SQLHSTMT BindCol_hstmt;
+//SQLHENV BindCol_henv;
+//SQLHDESC BindCol_hdesc;
+//SQLRETURN BCret;
+
+//SQLCHAR BindCol_Name[BindCol_NAME_LEN], BindCol_Phone[BindCol_PHONE_LEN];
+//SQLCHAR BindCol_Address[BindCol_ADDRESS_LEN];
+//SQLINTEGER NativeError;
+//unsigned long BindCol_CustID;
+
+void BindCol_DisplayError(SQLSMALLINT BindCol_HandleType,
+ SQLHSTMT BindCol_InputHandle);
+
+/**
+ * Test setting column to bind
+ * for a column in a result
+ *
+ * -# Bind columns 1
+ * -# Bind columns 2
+ * -# Bind columns 3
+ * -# Bind columns 4
+ * -# Bind columns 5
+ * -# Bind columns 6
+ * -# Bind columns 7
+ * @return Zero, if test succeeded
+ */
+
+int SQLBindColTest()
+{
+
+ SQLHDBC BindCol_hdbc;
+ SQLHSTMT BindCol_hstmt;
+ SQLHENV BindCol_henv;
+ SQLHDESC BindCol_hdesc;
+
+ SQLCHAR SQLStmt1 [240];
+ SQLCHAR SQLStmt2 [240];
+ SQLCHAR SQLStmt3 [120];
+
+ SQLRETURN BCret;
+
+ unsigned long BindCol_CustID;
+ SQLCHAR BindCol_Name[BindCol_NAME_LEN];
+ short BindCol_Account;
+ unsigned short BindCol_Phone;
+ long BindCol_Price;
+ float BindCol_Weight;
+ double BindCol_Tax;
+
+ ndbout << endl << "Start SQLBindCol Testing" << endl;
+
+ //*******************************************************************
+ //** hstmt
+ //** Execute a statement to retrieve rows from the Customers table **
+ //** We can create the table and insert rows into Customers **
+ //*******************************************************************
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ BCret = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &BindCol_henv);
+
+if (BCret == SQL_SUCCESS || BCret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ BCret = SQLSetEnvAttr(BindCol_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+if (BCret == SQL_SUCCESS || BCret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+//**********************************
+//** Allocate A Connection Handle **
+//**********************************
+
+ BCret = SQLAllocHandle(SQL_HANDLE_DBC,
+ BindCol_henv,
+ &BindCol_hdbc);
+
+ if (BCret == SQL_SUCCESS || BCret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ BCret = SQLConnect(BindCol_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (BCret == SQL_SUCCESS || BCret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ BCret = SQLAllocHandle(SQL_HANDLE_STMT,
+ BindCol_hdbc,
+ &BindCol_hstmt);
+ if(BCret == SQL_SUCCESS || BCret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //************************
+ //** Define a statement **
+ //************************
+
+ /* Primary key is Integer and Char */
+ strcpy((char *) SQLStmt1, "CREATE TABLE Customer1(CustID Integer, Name Char(12), Account Char(12), Phone Char(12), Price Char(6), Weight Char(6), Tax Char(6), Primary Key(CustID, Name))");
+
+ strcpy((char *) SQLStmt2, "INSERT INTO Customer1 (CustID, Name, Account, Phone, Price, Weight, Tax) VALUES(588, 'peter','6808','7190890', '5.68', '1.58', '0.88')");
+
+ strcpy((char *) SQLStmt3, "SELECT * FROM Customer1");
+
+ //************************************************
+ //** Prepare and Execute CREATE TABLE statement **
+ //************************************************
+ ndbout << endl << "Prepare and Execute CREATE TABLE statement ......" << endl;
+ ndbout << ">>>>" << (char*)SQLStmt1 << "<<<<" << endl;
+ BCret = SQLExecDirect(BindCol_hstmt,
+ SQLStmt1,
+ SQL_NTS);
+ if (BCret == SQL_SUCCESS)
+ ndbout << "Prepare and Execute CREATE TABLE statement OK!"
+ << endl<< endl;
+
+ if (BCret == SQL_ERROR || BCret == SQL_SUCCESS_WITH_INFO)
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+
+ if (BCret == -2)
+ {
+ ndbout << "BCret = SQLExexDirect()=" << BCret << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ }
+
+ //*******************************************************
+ //** Prepare and Execute INSERT statement with prepare **
+ //*******************************************************
+ ndbout << "Prepare and Execute INSERT statement ......" << endl;
+ ndbout << ">>>>" << (char*)SQLStmt2 << "<<<<" << endl;
+ BCret = SQLExecDirect(BindCol_hstmt,
+ SQLStmt2,
+ SQL_NTS);
+
+ if (BCret == SQL_SUCCESS)
+ ndbout << "Prepare and Execute INSERT statement OK!"
+ << endl << endl;
+
+ if (BCret == SQL_ERROR || BCret == SQL_SUCCESS_WITH_INFO)
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+
+ if (BCret == -2)
+ {
+ ndbout << "BCret = SQLExexDirect()=" << BCret << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ }
+
+ //******************************************
+ //** Prepare and EXECUTE SELECT statement **
+ //******************************************
+ ndbout << "Prepare and Execute SELECT statement ......" << endl;
+ ndbout << ">>>>" << (char*)SQLStmt3 << "<<<<" << endl;
+ BCret = SQLExecDirect(BindCol_hstmt,
+ SQLStmt3,
+ SQL_NTS);
+
+ if (BCret == SQL_SUCCESS)
+ ndbout << "Prepare and Execute SELECT statement OK!"
+ << endl << endl;
+
+ if (BCret == SQL_ERROR || BCret == SQL_SUCCESS_WITH_INFO)
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+
+ if (BCret == -2)
+ {
+ ndbout << "BCret = SQLExexDirect()=" << BCret << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ }
+
+ //*******************************
+ //** Execute SELECT statement **
+ //*******************************
+ // BCret = SQLExecute(BindCol_hstmt);
+ // if (BCret == SQL_ERROR || BCret == SQL_SUCCESS_WITH_INFO)
+ // {
+ // ndbout << "BCret = " << BCret << endl;
+ // BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ // }
+ // else
+ // {
+
+ if (BCret == SQL_SUCCESS)
+ ndbout << "Execute INSERT statement OK!" << endl;
+
+ //*********************
+ //** Test1 **
+ //** Bind columns 1 **
+ //*********************
+
+ BCret =SQLBindCol(BindCol_hstmt,
+ 1,
+ SQL_C_ULONG,
+ &BindCol_CustID,
+ sizeof(BindCol_CustID),
+ NULL);
+
+ if (BCret == SQL_SUCCESS)
+ {
+ ndbout << endl << "Bind col 1 OK!" << endl;
+ }
+ else if (BCret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Bind Col 1 OK but with INFO" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ }
+ else if (BCret == SQL_ERROR)
+ {
+ ndbout << "Bind Col 1 Failed!" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ return NDBT_FAILED;
+ }
+ else
+ ndbout << endl;
+
+ //*********************
+ //** Test2 **
+ //** Bind columns 2 **
+ //*********************
+
+ BCret =SQLBindCol(BindCol_hstmt,
+ 2,
+ SQL_C_CHAR,
+ &BindCol_Name,
+ BindCol_NAME_LEN,
+ NULL);
+
+ if (BCret == SQL_SUCCESS)
+ {
+ ndbout << "Bind col 2 OK!" << endl;
+ }
+ else if (BCret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Bind Col 2 OK but with INFO" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ }
+ else if (BCret == SQL_ERROR)
+ {
+ ndbout << "Bind Col 2 Failed!" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ return NDBT_FAILED;
+ }
+ else
+ ndbout << endl;
+
+ //*********************
+ //** Test3 **
+ //** Bind columns 3 **
+ //*********************
+
+ BCret = SQLBindCol(BindCol_hstmt,
+ 3,
+ SQL_C_USHORT,
+ &BindCol_Account,
+ sizeof(BindCol_Account),
+ NULL);
+
+ if (BCret == SQL_ERROR)
+ {
+ ndbout << "Bind Col 3 Failed!" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ return NDBT_FAILED;
+ }
+ else if (BCret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Bind Col 3 OK but with INFO" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ }
+ else if (BCret == SQL_SUCCESS)
+ {
+ ndbout << "Bind col 3 OK!" << endl;
+ }
+ else
+ ndbout << endl;
+
+ //*********************
+ //** Test4 **
+ //** Bind columns 4 **
+ //*********************
+
+ BCret = SQLBindCol(BindCol_hstmt,
+ 4,
+ SQL_C_USHORT,
+ &BindCol_Phone,
+ sizeof(BindCol_Phone),
+ NULL);
+
+ if (BCret == SQL_ERROR)
+ {
+ ndbout << "Bind Col 4 Failed!" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ return NDBT_FAILED;
+ }
+ else if (BCret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Bind Col 4 OK but with INFO" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ }
+ else if (BCret == SQL_SUCCESS)
+ {
+ ndbout << "Bind col 4 OK!" << endl;
+ }
+ else
+ ndbout << endl;
+
+ //*********************
+ //** Test5 **
+ //** Bind columns 5 **
+ //*********************
+
+ BCret = SQLBindCol(BindCol_hstmt,
+ 5,
+ SQL_C_SLONG,
+ &BindCol_Price,
+ sizeof(BindCol_Price),
+ NULL);
+
+ if (BCret == SQL_ERROR)
+ {
+ ndbout << "Bind Col 5 Failed!" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ return NDBT_FAILED;
+ }
+ else if (BCret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Bind Col 5 OK but with INFO" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ }
+ else if (BCret == SQL_SUCCESS)
+ {
+ ndbout << "Bind col 5 OK!" << endl;
+ }
+ else
+ ndbout << endl;
+
+ //*********************
+ //** Test6 **
+ //** Bind columns 6 **
+ //*********************
+
+ BCret = SQLBindCol(BindCol_hstmt,
+ 6,
+ SQL_C_FLOAT,
+ &BindCol_Weight,
+ sizeof(BindCol_Weight),
+ NULL);
+
+ if (BCret == SQL_ERROR)
+ {
+ ndbout << "Bind Col 6 Failed!" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ return NDBT_FAILED;
+ }
+ else if (BCret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Bind Col 6 OK but with INFO" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ }
+ else if (BCret == SQL_SUCCESS)
+ {
+ ndbout << "Bind col 6 OK!" << endl;
+ }
+ else
+ ndbout << endl;
+
+ //*********************
+ //** Test7 **
+ //** Bind columns 7 **
+ //*********************
+
+ BCret = SQLBindCol(BindCol_hstmt,
+ 7,
+ SQL_C_DOUBLE,
+ &BindCol_Tax,
+ sizeof(BindCol_Tax),
+ NULL);
+
+ if (BCret == SQL_ERROR)
+ {
+ ndbout << "Bind Col 7 Failed!" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ return NDBT_FAILED;
+ }
+ else if (BCret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Bind Col 7 OK but with INFO" << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ }
+ else if (BCret == SQL_SUCCESS)
+ {
+ ndbout << "Bind col 7 OK!" << endl;
+ }
+ else
+ ndbout << endl;
+
+ //}
+
+//*****************************************
+//* Fetch and print each row of data. On **
+//* an error, display a message and exit **
+//*****************************************
+
+BCret = SQLFetch(BindCol_hstmt);
+
+ ndbout << endl << "BCret = SQLFetch(BindCol_hstmt) = "
+ << BCret << endl;
+
+if (BCret == SQL_ERROR)
+{
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+ return NDBT_FAILED;
+}
+else if (BCret == SQL_SUCCESS_WITH_INFO)
+{
+ ndbout << "CustID = " << (int)BindCol_CustID << endl;
+ ndbout << "Name = " << (char *)BindCol_Name << endl;
+ ndbout << "Account = " << (int)BindCol_Account << endl;
+ ndbout << "Phone = " << (int)BindCol_Phone << endl;
+ ndbout << "Price = " << (int)BindCol_Price << endl;
+ ndbout << "Weight = " << (int)BindCol_Weight << endl;
+ ndbout << "Tax = " << (int)BindCol_Tax << endl;
+ BindCol_DisplayError(SQL_HANDLE_STMT, BindCol_hstmt);
+}
+else
+{
+ ndbout << "CustID = " << (int)BindCol_CustID << endl;
+ ndbout << "Name = " << (char *)BindCol_Name << endl;
+ ndbout << "Account = " << (int)BindCol_Account << endl;
+ ndbout << "Phone = " << (int)BindCol_Phone << endl;
+ ndbout << "Price = " << (int)BindCol_Price << endl;
+ ndbout << "Weight = " << (int)BindCol_Weight << endl;
+ ndbout << "Tax = " << (int)BindCol_Tax << endl;
+}
+
+// *********************************
+// ** Disconnect and Free Handles **
+// *********************************
+SQLDisconnect(BindCol_hdbc);
+SQLFreeHandle(SQL_HANDLE_STMT, BindCol_hstmt);
+SQLFreeHandle(SQL_HANDLE_DBC, BindCol_hdbc);
+SQLFreeHandle(SQL_HANDLE_ENV, BindCol_henv);
+
+return NDBT_OK;
+
+}
+
+void BindCol_DisplayError(SQLSMALLINT BindCol_HandleType,
+ SQLHSTMT BindCol_InputHandle)
+{
+ SQLSMALLINT BindCol_i = 1;
+ SQLRETURN BindCol__SQLSTATEs;
+ SQLCHAR BindCol_Sqlstate[5];
+ SQLCHAR BindCol_Msg[BindCol_SQL_MAXIMUM_MESSAGE_LENGTH];
+ SQLSMALLINT BindCol_MsgLen;
+ SQLINTEGER NativeError;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((BindCol__SQLSTATEs = SQLGetDiagRec(BindCol_HandleType,
+ BindCol_InputHandle,
+ BindCol_i,
+ BindCol_Sqlstate,
+ &NativeError,
+ BindCol_Msg,
+ sizeof(BindCol_Msg),
+ &BindCol_MsgLen)
+ ) != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << BindCol_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)BindCol_InputHandle << endl;
+ ndbout << "the BindCol_Msg is: " << (char *) BindCol_Msg << endl;
+ ndbout << "the output state is:" << (char *)BindCol_Sqlstate << endl;
+
+ BindCol_i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
diff --git a/storage/ndb/test/odbc/client/SQLBindParameterTest.cpp b/storage/ndb/test/odbc/client/SQLBindParameterTest.cpp
new file mode 100644
index 00000000000..2ffd2892064
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLBindParameterTest.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 <NdbOut.hpp>
+#include <sqlcli.h>
+#include <stdio.h>
+
+using namespace std;
+
+#define NAME_LEN 50
+#define PHONE_LEN 10
+#define SALES_PERSON_LEN 10
+#define STATUS_LEN 6
+
+SQLHSTMT hstmt;
+
+SQLSMALLINT sOrderID;
+SQLSMALLINT sCustID;
+DATE_STRUCT dsOpenDate;
+SQLCHAR szSalesPerson[SALES_PERSON_LEN];
+
+SQLCHAR szStatus[STATUS_LEN],Sqlstate[5], Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+SQLINTEGER cbOrderID = 0, cbCustID = 0, cbOpenDate = 0, cbSalesPerson = SQL_NTS, cbStatus = SQL_NTS, NativeError;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLSMALLINT i, MsgLen;
+
+void DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+
+int SQLBindParameterTest ()
+{
+
+ /* hstmt */
+ //** Execute a statement to retrieve rows from the Customers table.
+ //** We can create the table and inside rows in
+ //** NDB by another program TestDirectSQL.
+ //** In this test program(SQLBindParameterTest),we only have three rows in
+ //** table ORDERS
+
+ //************************
+ //** Define a statement **
+ //************************
+ strcpy( (char *) SQLStmt,
+ "INSERT INTO Customers (CUSTID, Name, Address, Phone) VALUES (2, 'paul, 'Alzato', '468719989');
+
+/* Prepare the SQL statement with parameter markers. */
+retcode = SQLPrepare(hstmt, SQLStmt, SQL_NTS);
+
+/* Specify data types and buffers for OrderID, CustID, OpenDate, SalesPerson, */
+/* Status parameter data. */
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ /* ParameterNumber is less than 1 */
+retcode = SQLBindParameter(hstmt,
+ 0,
+ SQL_PARAM_INPUT,
+ SQL_C_SSHORT,
+ SQL_INTEGER,
+ 0,
+ 0,
+ &sOrderID,
+ 0,
+ &cbOrderID);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ DisplayError(SQL_HANDLE_STMT, hstmt);
+
+ /* InputOutputMode is not one of the code values in Table 11 */
+retcode = SQLBindParameter(hstmt,
+ 1,
+ 3,
+ SQL_C_SSHORT,
+ SQL_INTEGER,
+ 0,
+ 0,
+ &sOrderID,
+ 0,
+ &cbOrderID);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ DisplayError(SQL_HANDLE_STMT, hstmt);
+
+ /* ParameterType is not one of the code values in Table 37 */
+retcode = SQLBindParameter(hstmt,
+ 1,
+ 3,
+ SQL_C_SSHORT,
+ 114,
+ 0,
+ 0,
+ &sOrderID,
+ 0,
+ &cbOrderID);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ DisplayError(SQL_HANDLE_STMT, hstmt);
+
+SQLBindParameter(hstmt,
+ 1,
+ SQL_PARAM_INPUT,
+ SQL_C_SSHORT,
+ SQL_INTEGER,
+ 0,
+ 0,
+ &sOrderID,
+ 0,
+ &cbOrderID);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ DisplayError(SQL_HANDLE_STMT, hstmt);
+
+
+SQLBindParameter(hstmt,
+ 2,
+ SQL_PARAM_INPUT,
+ SQL_C_SSHORT,
+ SQL_INTEGER,
+ 0,
+ 0,
+ &sCustID,
+ 0,
+ &cbCustID);
+
+SQLBindParameter(hstmt,
+ 3,
+ SQL_PARAM_INPUT,
+ SQL_C_TYPE_DATE,
+ SQL_TYPE_DATE,
+ 0,
+ 0,
+ &dsOpenDate,
+ 0,
+ &cbOpenDate);
+
+SQLBindParameter(hstmt,
+ 4,
+ SQL_PARAM_INPUT,
+ SQL_C_CHAR,
+ SQL_CHAR,
+ SALES_PERSON_LEN,
+ 0,
+ szSalesPerson,
+ 0,
+ &cbSalesPerson);
+
+SQLBindParameter(hstmt,
+ 5,
+ SQL_PARAM_INPUT,
+ SQL_C_CHAR,
+ SQL_CHAR,
+ STATUS_LEN,
+ 0,
+ szStatus,
+ 0,
+ &cbStatus);
+
+/*
+
+/* Specify first row of parameter data. */
+sOrderID = 1001;
+sCustID = 298;
+dsOpenDate.year = 1996;
+dsOpenDate.month = 3;
+dsOpenDate.day = 8;
+strcpy(szSalesPerson, "Johnson");
+strcpy(szStatus, "Closed");
+
+/* Execute statement with first row. */
+retcode = SQLExecute(hstmt);
+
+/* Specify second row of parameter data. */
+sOrderID = 1002;
+sCustID = 501;
+dsOpenDate.year = 1996;
+dsOpenDate.month = 3;
+dsOpenDate.day = 9;
+strcpy(szSalesPerson, "Bailey");
+strcpy(szStatus, "Open");
+
+/* Execute statement with second row. */
+retcode = SQLExecute(hstmt);
+
+*/
+
+}
+
+ return 0;
+
+ }
+
+
+void DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLCancelTest.cpp b/storage/ndb/test/odbc/client/SQLCancelTest.cpp
new file mode 100644
index 00000000000..904ffab6979
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLCancelTest.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 */
+
+ /**
+ * @file SQLCancelTest.cpp
+ */
+
+#include <common.hpp>
+#define Cancel_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC CC_hdbc;
+SQLHSTMT CC_hstmt;
+SQLHENV CC_henv;
+SQLHDESC CC_hdesc;
+
+void Cancel_DisplayError(SQLSMALLINT Cancel_HandleType,
+ SQLHSTMT Cancel_InputHandle);
+/**
+ * Test to terminate SQL statement precessing
+ *
+ * Tests:
+ * -# normal case test with correct hstmt handle
+ * -# SQL_STILL_EXECUTING case test with hstmt handle
+ * -# abnormal case test with incorrect hdbc, henv and hdesc handle
+ * @return Zero, if test succeeded
+ */
+
+int SQLCancelTest()
+{
+
+ SQLRETURN retcode;
+ SQLCHAR SQLStmt [120];
+
+ ndbout << endl << "Start SQLCancel Testing" << endl;
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &CC_henv);
+
+ if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ retcode = SQLSetEnvAttr(CC_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+ retcode = SQLAllocHandle(SQL_HANDLE_DBC, CC_henv, &CC_hdbc);
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ retcode = SQLConnect(CC_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ ndbout << "Failure to Connect DB!" << endl;
+
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_STMT, CC_hdbc, &CC_hstmt);
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //************************
+ //** Define a statement **
+ //************************
+
+ strcpy((char *) SQLStmt,
+ "select * FROM Customers");
+
+ //*************************
+ //** Prepare a statement **
+ //*************************
+
+ retcode = SQLPrepare(CC_hstmt,
+ SQLStmt,
+ SQL_NTS);
+
+ //***********************
+ //** Execute statement **
+ //***********************
+
+ retcode = SQLExecute(CC_hstmt);
+
+ //************************************************
+ //** Test 1 **
+ //** Input correct hstmt handle for normal test **
+ //************************************************
+
+ retcode = SQLCancel(CC_hstmt);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ {
+ ndbout << "Test 1" << endl;
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but SQL_INVALID_HANDLE"
+ << "still appeared. Please check program" << endl;
+ }
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Test 1" << endl;
+ Cancel_DisplayError(SQL_HANDLE_STMT, CC_hstmt);
+ }
+ //************************************************
+ //** Test 2 **
+ //** SQL_STILL_EXECUTING is not defined **
+ //************************************************
+
+ if (retcode == SQL_STILL_EXECUTING)
+ {
+ ndbout << "Test 2" << endl;
+ ndbout << "The function is still processing." << endl;
+ }
+
+ if (retcode == SQL_ERROR)
+ {
+ ndbout << "Test 2" << endl;
+ ndbout << "The Asynchronous processing was successfully canceled!"
+ << endl;
+ }
+ //*********************************
+ //** Test 3 **
+ //** Input incorrect henv handle **
+ //*********************************
+
+ retcode = SQLCancel(CC_henv);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS)
+ {
+ ndbout << "Test 3" << endl;
+ ndbout << "Handle Type is SQL_HANDLE_ENV, but SQL_SUCCESS_WITH_INFO"
+ << " still appeared. Please check program" << endl;
+ Cancel_DisplayError(SQL_HANDLE_ENV, CC_henv);
+ }
+
+ //*********************************
+ //** Test 4 **
+ //** Input incorrect hdbc handle **
+ //*********************************
+
+ retcode = SQLCancel(CC_hdbc);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS)
+ {
+ ndbout << "Test 4" << endl;
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but SQL_SUCCESS_WITH_INFO"
+ << "still appeared. Please check programm" << endl;
+ Cancel_DisplayError(SQL_HANDLE_DBC, CC_hdbc);
+ }
+
+ //**********************************
+ //** Test 5 **
+ //** Input incorrect handle hdesc **
+ //**********************************
+
+ retcode = SQLCancel(CC_hdesc);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS)
+ {
+ ndbout << endl
+ << "Handle Type is SQL_HANDLE_DESC, but SQL_SUCCESS_WITH_INFO"
+ << "still appeared. Please check program" << endl;
+ ndbout << "Test 5" << endl;
+ Cancel_DisplayError(SQL_HANDLE_DESC, CC_hdesc);
+ }
+
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(CC_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, CC_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, CC_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, CC_henv);
+
+ return NDBT_OK;
+
+ }
+
+void Cancel_DisplayError(SQLSMALLINT Cancel_HandleType,
+ SQLHSTMT Cancel_InputHandle)
+{
+ SQLCHAR Sqlstate[5];
+ SQLRETURN SQLSTATEs;
+ SQLINTEGER NativeError;
+ SQLSMALLINT i, MsgLen;
+ SQLCHAR Msg[Cancel_MESSAGE_LENGTH];
+ i = 1;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((SQLSTATEs = SQLGetDiagRec(Cancel_HandleType,
+ Cancel_InputHandle,
+ i,
+ Sqlstate,
+ &NativeError,
+ Msg,
+ sizeof(Msg),
+ &MsgLen))
+ != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << Cancel_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)Cancel_InputHandle << endl;
+ ndbout << "the Msg is: " << (char *) Msg << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLCloseCursorTest.cpp b/storage/ndb/test/odbc/client/SQLCloseCursorTest.cpp
new file mode 100644
index 00000000000..35f125df59d
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLCloseCursorTest.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 <common.h>
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+void CloseCursor_DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+
+int SQLCloseCursorTest()
+{
+ /* "If there is no open cursor associated with S, then an exception is raised: invalid cursor state" How to test this case */
+
+ /* hstmt */
+ retcode = SQLCloseCursor(hstmt);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ CloseCursor_DisplayError(SQL_HANDLE_STMT, hstmt);
+
+ /* henv */
+ retcode = SQLCloseCursor(henv);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS)
+ ndbout << "Handle Type is SQL_HANDLE_ENV, but string SQL_SUCCESS_WITH_INFO still appeared. Please check programm" << endl;
+ // CloseCursor_DisplayError(SQL_HANDLE_ENV, henv);
+
+ /* hdbc */
+ retcode = SQLCloseCursor(hdbc);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_SUCCESS_WITH_INFO still appeared. Please check programm" << endl;
+ // CloseCursor_DisplayError(SQL_HANDLE_DBC, hdbc);
+
+ /* hdesc */
+ retcode = SQLCloseCursor(hdesc);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS)
+ ndbout << "Handle Type is SQL_HANDLE_DESC, but string SQL_SUCCESS_WITH_INFO still appeared. Please check programm" << endl;
+ // CloseCursor_DisplayError(SQL_HANDLE_DESC, hdesc);
+
+ return 0;
+
+ }
+
+
+void CloseCursor_DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLColAttributeTest.cpp b/storage/ndb/test/odbc/client/SQLColAttributeTest.cpp
new file mode 100644
index 00000000000..4c067c21d7d
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLColAttributeTest.cpp
@@ -0,0 +1,328 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ * @file SQLColAttributeTest.cpp
+ */
+
+#include <common.hpp>
+using namespace std;
+
+#define MAXIMUM_MESSAGE_LENGTH_Test 200
+#define BufferLengthTest 156
+
+SQLHSTMT ColAtt_hstmt;
+SQLHSTMT ColAtt_hdbc;
+SQLHENV ColAtt_henv;
+SQLHDESC ColAtt_hdesc;
+
+SQLCHAR CharacterAttributePtr;
+SQLINTEGER NumericAttributePtr;
+SQLSMALLINT StringLengthPtr;
+
+SQLRETURN ColAtt_ret;
+
+void ColAtt_DisplayError(SQLSMALLINT ColAtt_HandleType,
+ SQLHSTMT ColAtt_InputHandle);
+
+/**
+ * Test returning descriptor information
+ *
+ * Tests:
+ * -# Call SQLColAttribute, without preceeding SQLPrepare
+ * -# ???
+ *
+ * @return Zero, if test succeeded
+ */
+int SQLColAttributeTest()
+{
+ ndbout << endl << "Start SQLColAttribute Testing" << endl;
+
+ SQLCHAR SQLStmt [120];
+
+ /********************************************************************
+ ** Test 1: **
+ ** **
+ ** Checks to execute SQLColAttribute, when there is no **
+ ** prepared or executed statement associated with StatementHandle **
+ ** **
+ ** Intended result: SQL_ERROR ??? **
+ ********************************************************************/
+ ColAtt_ret = SQLColAttribute(ColAtt_hstmt,
+ 1,
+ SQL_DESC_AUTO_UNIQUE_VALUE,
+ &CharacterAttributePtr,
+ BufferLengthTest,
+ &StringLengthPtr,
+ &NumericAttributePtr);
+
+ if (ColAtt_ret == SQL_ERROR)
+ {
+ ndbout << "ColAtt_ret = " << ColAtt_ret << endl;
+ ndbout << endl << "There is no prepared or executed" << endl
+ << " statement associated with StatementHandle" << endl;
+ ColAtt_DisplayError(SQL_HANDLE_STMT, ColAtt_hstmt);
+ }
+ else if (ColAtt_ret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "ColAtt_ret = " << ColAtt_ret << endl;
+ ndbout << endl << "There is no prepared or executed" << endl
+ << " statement associated with StatementHandle" << endl;
+ ColAtt_DisplayError(SQL_HANDLE_STMT, ColAtt_hstmt);
+ }
+ else if (ColAtt_ret == SQL_SUCCESS)
+ {
+ ndbout << "ColAtt_ret = " << ColAtt_ret << endl;
+ ndbout << endl << "There is no prepared or executed" << endl
+ << " statement associated with StatementHandle" << endl;
+ ColAtt_DisplayError(SQL_HANDLE_STMT, ColAtt_hstmt);
+ }
+ else if (ColAtt_ret == -2)
+ {
+ ndbout << "ColAtt_ret = " << ColAtt_ret << endl;
+ ndbout << endl << "There is no prepared or executed" << endl
+ << " statement associated with StatementHandle" << endl;
+ ColAtt_DisplayError(SQL_HANDLE_STMT, ColAtt_hstmt);
+ }
+ else
+ {
+ ndbout << "ColAtt_ret = " << ColAtt_ret << endl;
+ ndbout << endl << "There is no prepared or executed" << endl
+ << " statement associated with StatementHandle" << endl;
+ ColAtt_DisplayError(SQL_HANDLE_STMT, ColAtt_hstmt);
+ }
+
+ //*******************************************************************
+ //** Test 2: **
+ //** **
+ //** hstmt **
+ //** Execute a statement to retrieve rows from the Customers table **
+ //** We can create the table and insert rows into Mysql **
+ //** **
+ //** Intended result: ??? **
+ //*******************************************************************
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ ColAtt_ret = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &ColAtt_henv);
+
+ if (ColAtt_ret == SQL_SUCCESS || ColAtt_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 2.x **
+ //*********************************************
+ ColAtt_ret = SQLSetEnvAttr(ColAtt_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC2,
+ SQL_IS_UINTEGER);
+
+ if (ColAtt_ret == SQL_SUCCESS || ColAtt_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 2.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ ColAtt_ret = SQLAllocHandle(SQL_HANDLE_DBC,
+ ColAtt_henv,
+ &ColAtt_hdbc);
+
+ if (ColAtt_ret == SQL_SUCCESS || ColAtt_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ ColAtt_ret = SQLConnect(ColAtt_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (ColAtt_ret == SQL_SUCCESS || ColAtt_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ ColAtt_ret = SQLAllocHandle(SQL_HANDLE_STMT,
+ ColAtt_hdbc,
+ &ColAtt_hstmt);
+ if(ColAtt_ret == SQL_SUCCESS || ColAtt_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //************************
+ //** Define a statement **
+ //************************
+
+ /*
+ strcpy((char *) SQLStmt,
+ "DELETE FROM Customers WHERE CustID = 6");
+ */
+
+ strcpy((char *) SQLStmt,
+ "INSERT INTO Customers (CustID, Name, Address, Phone) VALUES (6, 'Jan', 'LM vag 8', '969696')");
+
+ /*
+ strcpy((char *) SQLStmt,
+ "INSERT INTO Customers (CustID, Name, Address, Phone) VALUES (?, ?, ?, ?)");
+ */
+
+ //********************************
+ //** Prepare SQL statement **
+ //********************************
+ ColAtt_ret = SQLPrepare(ColAtt_hstmt,
+ SQLStmt,
+ SQL_NTS);
+
+ if (ColAtt_ret == SQL_SUCCESS || ColAtt_ret == SQL_SUCCESS_WITH_INFO)
+ {
+ //**************************************************************
+ //** FieldIdentifer is not one of the code valuess in Table 20,
+ //** "Codes used for descriptor fields"
+ //**************************************************************
+ ColAtt_ret = SQLColAttribute(ColAtt_hstmt,
+ 2,
+ 9999,
+ &CharacterAttributePtr,
+ BufferLengthTest,
+ &StringLengthPtr,
+ &NumericAttributePtr);
+
+ if (ColAtt_ret == SQL_ERROR || ColAtt_ret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << endl << "FieldIdentifer is not one of the" << endl;
+ ndbout << "code valuess in Table 20, Codes used for" << endl;
+ ndbout << "descriptor fields" <<endl;
+ ColAtt_DisplayError(SQL_HANDLE_STMT, ColAtt_hstmt);
+ }
+
+ //****************************************************************
+ //** Let TYPE is 'ITEM' in Table 20, ColumnNumber is less than one
+ //****************************************************************
+ ColAtt_ret = SQLColAttribute(ColAtt_hstmt,
+ -1,
+ SQL_DESC_BASE_COLUMN_NAME,
+ &CharacterAttributePtr,
+ BufferLengthTest,
+ &StringLengthPtr,
+ &NumericAttributePtr);
+
+ if (ColAtt_ret == SQL_ERROR || ColAtt_ret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Let TYPE is 'ITEM' in Table 20,ColumnNumber"
+ << "is less than one" << endl;
+ ColAtt_DisplayError(SQL_HANDLE_STMT, ColAtt_hstmt);
+ }
+
+ //*********************************************************
+ //** Let TYPE is 'ITEM' in Table 20, FieldIdentifer is zero
+ //*********************************************************
+ ColAtt_ret = SQLColAttribute(ColAtt_hstmt,
+ 1018,
+ 0,
+ &CharacterAttributePtr,
+ BufferLengthTest,
+ &StringLengthPtr,
+ &NumericAttributePtr);
+
+ if (ColAtt_ret == SQL_ERROR || ColAtt_ret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Let TYPE is 'ITEM' in Table 20, FieldIdentifer"
+ << " is zero" <<endl;
+ ColAtt_DisplayError(SQL_HANDLE_STMT, ColAtt_hstmt);
+ }
+
+ //**********************************************************
+ //** Let TYPE is 'ITEM' in Table 20, ColumnNumber is greater
+ //** than TOP_LEVEL_COUNT(1044)
+ //*********************************************************
+ ColAtt_ret = SQLColAttribute(ColAtt_hstmt,
+ 1045,
+ SQL_DESC_BASE_COLUMN_NAME,
+ &CharacterAttributePtr,
+ BufferLengthTest,
+ &StringLengthPtr,
+ &NumericAttributePtr);
+
+ if (ColAtt_ret == SQL_ERROR || ColAtt_ret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Let TYPE is 'ITEM' in Table 20, ColumnNumber" << endl
+ << "is greater than TOP_LEVEL_COUNT(1044)" << endl;
+ ColAtt_DisplayError(SQL_HANDLE_STMT, ColAtt_hstmt);
+ }
+
+ }
+
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(ColAtt_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, ColAtt_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, ColAtt_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, ColAtt_henv);
+
+ return NDBT_OK;
+}
+
+void ColAtt_DisplayError(SQLSMALLINT ColAtt_HandleType,
+ SQLHSTMT ColAtt_InputHandle)
+{
+ SQLSMALLINT ColAtt_i = 1;
+ SQLRETURN ColAtt_SQLSTATEs;
+ SQLCHAR ColAtt_Sqlstate[5];
+ SQLCHAR ColAtt_Msg[MAXIMUM_MESSAGE_LENGTH_Test];
+ SQLSMALLINT ColAtt_MsgLen;
+ SQLINTEGER ColAtt_NativeError;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((ColAtt_SQLSTATEs = SQLGetDiagRec(ColAtt_HandleType,
+ ColAtt_InputHandle,
+ ColAtt_i,
+ ColAtt_Sqlstate,
+ &ColAtt_NativeError,
+ ColAtt_Msg,
+ sizeof(ColAtt_Msg),
+ &ColAtt_MsgLen))
+ != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << ColAtt_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)ColAtt_InputHandle << endl;
+ ndbout << "the ColAtt_Msg is: " << (char *) ColAtt_Msg << endl;
+ ndbout << "the output state is:" << (char *)ColAtt_Sqlstate << endl;
+
+ ColAtt_i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLColAttributeTest1.cpp b/storage/ndb/test/odbc/client/SQLColAttributeTest1.cpp
new file mode 100644
index 00000000000..322a21eefc1
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLColAttributeTest1.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 */
+
+/**
+ * @file SQLColAttributeTest1.cpp
+ */
+
+#include <common.hpp>
+using namespace std;
+
+#define MAXIMUM_MESSAGE_LENGTH_Test1 200
+#define BufferLenghTest1 156
+
+SQLHSTMT ColAtt_hstmtTest1;
+SQLHSTMT ColAtt_hdbcTest1;
+SQLHENV ColAtt_henvTest1;
+SQLHDESC ColAtt_hdescTest1;
+
+SQLCHAR CharacterAttributePtrTest1;
+SQLINTEGER NumericAttributePtrTest1;
+SQLSMALLINT StringLengthPtrTest1;
+
+SQLRETURN ColAtt_retTest1;
+
+void ColAtt_DisplayErrorTest1(SQLSMALLINT ColAtt_HandleType,
+ SQLHSTMT ColAtt_InputHandle);
+
+/**
+ * Test returning descriptor information
+ *
+ * Tests:
+ * -# Execute SQLColAttribute without prepared or executed statement
+ *
+ * @return Zero, if test succeeded
+ */
+int SQLColAttributeTest1()
+{
+ ndbout << endl << "Start SQLColAttribute Testing1" << endl;
+ /********************************************************************
+ ** Test : **
+ ** **
+ ** Checks to execute SQLColAttribute, when there is no **
+ ** prepared or executed statement associated with StatementHandle **
+ ** **
+ ** Intended result:CLI-specific condition-function sequence error **
+ ********************************************************************/
+ ColAtt_retTest1 = SQLColAttribute(ColAtt_hstmtTest1,
+ 1,
+ SQL_DESC_AUTO_UNIQUE_VALUE,
+ &CharacterAttributePtrTest1,
+ BufferLenghTest1,
+ &StringLengthPtrTest1,
+ &NumericAttributePtrTest1);
+
+ if (ColAtt_retTest1 == SQL_ERROR)
+ {
+ ndbout << "ColAtt_ret = " << ColAtt_retTest1 << endl;
+ ndbout << endl << "There is no prepared or executed" << endl
+ << " statement associated with StatementHandle" << endl;
+ ColAtt_DisplayErrorTest1(SQL_HANDLE_STMT, ColAtt_hstmtTest1);
+ }
+ else if (ColAtt_retTest1 == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "ColAtt_ret = " << ColAtt_retTest1 << endl;
+ ndbout << endl << "There is no prepared or executed" << endl
+ << " statement associated with StatementHandle" << endl;
+ ColAtt_DisplayErrorTest1(SQL_HANDLE_STMT, ColAtt_hstmtTest1);
+ }
+ else if (ColAtt_retTest1 == SQL_SUCCESS)
+ {
+ ndbout << "ColAtt_ret = " << ColAtt_retTest1 << endl;
+ ndbout << endl << "There is no prepared or executed" << endl
+ << " statement associated with StatementHandle" << endl;
+ ColAtt_DisplayErrorTest1(SQL_HANDLE_STMT, ColAtt_hstmtTest1);
+ }
+ else if (ColAtt_retTest1 == -2)
+ {
+ ndbout << "ColAtt_ret = " << ColAtt_retTest1 << endl;
+ ndbout << endl << "There is no prepared or executed" << endl
+ << " statement associated with StatementHandle" << endl;
+ ColAtt_DisplayErrorTest1(SQL_HANDLE_STMT, ColAtt_hstmtTest1);
+ }
+ else
+ {
+ ndbout << "ColAtt_ret = " << ColAtt_retTest1 << endl;
+ ndbout << endl << "There is no prepared or executed" << endl
+ << " statement associated with StatementHandle" << endl;
+ ColAtt_DisplayErrorTest1(SQL_HANDLE_STMT, ColAtt_hstmtTest1);
+ }
+
+ return NDBT_OK;
+}
+
+void ColAtt_DisplayErrorTest1(SQLSMALLINT ColAtt_HandleType,
+ SQLHSTMT ColAtt_InputHandle)
+{
+ SQLSMALLINT ColAtt_i = 1;
+ SQLRETURN ColAtt_SQLSTATEs;
+ SQLCHAR ColAtt_Sqlstate[5];
+ SQLCHAR ColAtt_Msg[MAXIMUM_MESSAGE_LENGTH_Test1];
+ SQLSMALLINT ColAtt_MsgLen;
+ SQLINTEGER ColAtt_NativeError;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((ColAtt_SQLSTATEs = SQLGetDiagRec(ColAtt_HandleType,
+ ColAtt_InputHandle,
+ ColAtt_i,
+ ColAtt_Sqlstate,
+ &ColAtt_NativeError,
+ ColAtt_Msg,
+ sizeof(ColAtt_Msg),
+ &ColAtt_MsgLen))
+ != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << ColAtt_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)ColAtt_InputHandle << endl;
+ ndbout << "the ColAtt_Msg is: " << (char *) ColAtt_Msg << endl;
+ ndbout << "the output state is:" << (char *)ColAtt_Sqlstate << endl;
+
+ ColAtt_i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLColAttributeTest2.cpp b/storage/ndb/test/odbc/client/SQLColAttributeTest2.cpp
new file mode 100644
index 00000000000..18cffae76c1
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLColAttributeTest2.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 */
+
+ /**
+ * @file SQLColAttributeTest2.cpp
+ */
+
+#include <common.hpp>
+using namespace std;
+
+#define MAXIMUM_MESSAGE_LENGTH_Test2 200
+#define BufferLengthTest2 156
+
+SQLHSTMT ColAtt_hstmtTest2;
+SQLHSTMT ColAtt_hdbcTest2;
+SQLHENV ColAtt_henvTest2;
+SQLHDESC ColAtt_hdescTest2;
+
+SQLCHAR CharacterAttributePtrTest2;
+SQLINTEGER NumericAttributePtrTest2;
+SQLSMALLINT StringLengthPtrTest2;
+
+SQLRETURN ColAtt_retTest2;
+
+void ColAtt_DisplayErrorTest2(SQLSMALLINT ColAttTest2_HandleType,
+ SQLHSTMT ColAttTest2_InputHandle);
+
+/**
+ * Test returning descriptor information
+ *
+ * Test:
+ * -# Call SQLColAttribute without preceeding SQLExecute
+ * -# Let TYPE is 'ITEM' in Table 20, FieldIdentifer is zero
+ * -# Let TYPE is 'ITEM' in Table 20, ColumnNumber is less than one
+ * -# FieldIdentifer is not one of the code valuess in Table 20
+ * -# Let TYPE is 'ITEM' in Table 20, ColumnNumber is greater than 1044
+ *
+ * @return Zero, if test succeeded
+ */
+int SQLColAttributeTest2()
+{
+ ndbout << endl << "Start SQLColAttribute Testing2" << endl;
+
+ SQLCHAR SQLStmt [120];
+
+ //*******************************************************************
+ //** Test **
+ //** **
+ //** hstmt **
+ //** Prepare a statement without executing the statement **
+ //** **
+ //** Intended result: table Customer should not have new row **
+ //*******************************************************************
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ ColAtt_retTest2 = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &ColAtt_henvTest2);
+
+ if (ColAtt_retTest2 == SQL_SUCCESS || ColAtt_retTest2 == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ ColAtt_retTest2 = SQLSetEnvAttr(ColAtt_henvTest2,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (ColAtt_retTest2 == SQL_SUCCESS || ColAtt_retTest2 == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 2.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ ColAtt_retTest2 = SQLAllocHandle(SQL_HANDLE_DBC,
+ ColAtt_henvTest2,
+ &ColAtt_hdbcTest2);
+
+ if (ColAtt_retTest2 == SQL_SUCCESS || ColAtt_retTest2 == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ ColAtt_retTest2 = SQLConnect(ColAtt_hdbcTest2,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (ColAtt_retTest2 == SQL_SUCCESS || ColAtt_retTest2 == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ ColAtt_retTest2 = SQLAllocHandle(SQL_HANDLE_STMT,
+ ColAtt_hdbcTest2,
+ &ColAtt_hstmtTest2);
+ if(ColAtt_retTest2 == SQL_SUCCESS || ColAtt_retTest2 == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //************************
+ //** Define a statement **
+ //************************
+
+ /*
+ strcpy((char *) SQLStmt,
+ "DELETE FROM Customers WHERE CustID = 6");
+ */
+
+ strcpy((char *) SQLStmt,
+ "INSERT INTO Customers (CustID, Name, Address, Phone) VALUES (6, 'Jan', 'LM vag 8', '969696')");
+
+ /*
+ strcpy((char *) SQLStmt,
+ "INSERT INTO Customers (CustID, Name, Address, Phone) VALUES (?, ?, ?, ?)");
+ */
+
+ //********************************
+ //** Prepare SQL statement **
+ //********************************
+ ColAtt_retTest2 = SQLPrepare(ColAtt_hstmtTest2,
+ SQLStmt,
+ SQL_NTS);
+
+ if (ColAtt_retTest2 == SQL_SUCCESS || ColAtt_retTest2 == SQL_SUCCESS_WITH_INFO)
+ {
+ //**************************************************************
+ //** FieldIdentifer is not one of the code valuess in Table 20,
+ //** "Codes used for descriptor fields"
+ //**************************************************************
+ ColAtt_retTest2 = SQLColAttribute(ColAtt_hstmtTest2,
+ 2,
+ 9999,
+ &CharacterAttributePtrTest2,
+ BufferLengthTest2,
+ &StringLengthPtrTest2,
+ &NumericAttributePtrTest2);
+
+ if (ColAtt_retTest2 == SQL_ERROR || ColAtt_retTest2 == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << endl << "FieldIdentifer is not one of the" << endl;
+ ndbout << "code valuess in Table 20, Codes used for" << endl;
+ ndbout << "descriptor fields" <<endl;
+ ColAtt_DisplayErrorTest2(SQL_HANDLE_STMT, ColAtt_hstmtTest2);
+ }
+
+ //****************************************************************
+ //** Let TYPE is 'ITEM' in Table 20, ColumnNumber is less than one
+ //****************************************************************
+ ColAtt_retTest2 = SQLColAttribute(ColAtt_hstmtTest2,
+ -1,
+ SQL_DESC_BASE_COLUMN_NAME,
+ &CharacterAttributePtrTest2,
+ BufferLengthTest2,
+ &StringLengthPtrTest2,
+ &NumericAttributePtrTest2);
+
+ if (ColAtt_retTest2 == SQL_ERROR || ColAtt_retTest2 == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Let TYPE is 'ITEM' in Table 20,ColumnNumber"
+ << "is less than one" << endl;
+ ColAtt_DisplayErrorTest2(SQL_HANDLE_STMT, ColAtt_hstmtTest2);
+ }
+
+ //*********************************************************
+ //** Let TYPE is 'ITEM' in Table 20, FieldIdentifer is zero
+ //*********************************************************
+ ColAtt_retTest2 = SQLColAttribute(ColAtt_hstmtTest2,
+ 1018,
+ 0,
+ &CharacterAttributePtrTest2,
+ BufferLengthTest2,
+ &StringLengthPtrTest2,
+ &NumericAttributePtrTest2);
+
+ if (ColAtt_retTest2 == SQL_ERROR || ColAtt_retTest2 == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Let TYPE is 'ITEM' in Table 20, FieldIdentifer"
+ << " is zero" <<endl;
+ ColAtt_DisplayErrorTest2(SQL_HANDLE_STMT, ColAtt_hstmtTest2);
+ }
+
+ //**********************************************************
+ //** Let TYPE is 'ITEM' in Table 20, ColumnNumber is greater
+ //** than TOP_LEVEL_COUNT(1044)
+ //*********************************************************
+ ColAtt_retTest2 = SQLColAttribute(ColAtt_hstmtTest2,
+ 1045,
+ SQL_DESC_BASE_COLUMN_NAME,
+ &CharacterAttributePtrTest2,
+ BufferLengthTest2,
+ &StringLengthPtrTest2,
+ &NumericAttributePtrTest2);
+
+ if (ColAtt_retTest2 == SQL_ERROR || ColAtt_retTest2 == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Let TYPE is 'ITEM' in Table 20, ColumnNumber" << endl
+ << "is greater than TOP_LEVEL_COUNT(1044)" << endl;
+ ColAtt_DisplayErrorTest2(SQL_HANDLE_STMT, ColAtt_hstmtTest2);
+ }
+
+ }
+
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(ColAtt_hdbcTest2);
+ SQLFreeHandle(SQL_HANDLE_STMT, ColAtt_hstmtTest2);
+ SQLFreeHandle(SQL_HANDLE_DBC, ColAtt_hdbcTest2);
+ SQLFreeHandle(SQL_HANDLE_ENV, ColAtt_henvTest2);
+
+ return NDBT_OK;
+}
+
+
+void ColAtt_DisplayErrorTest2(SQLSMALLINT ColAttTest2_HandleType,
+ SQLHSTMT ColAttTest2_InputHandle)
+{
+ SQLSMALLINT ColAtt_i = 1;
+ SQLRETURN ColAtt_SQLSTATEs;
+ SQLCHAR ColAtt_Sqlstate[5];
+ SQLCHAR ColAtt_Msg[MAXIMUM_MESSAGE_LENGTH_Test2];
+ SQLSMALLINT ColAtt_MsgLen;
+ SQLINTEGER ColAtt_NativeError;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((ColAtt_SQLSTATEs = SQLGetDiagRec(ColAttTest2_HandleType,
+ ColAttTest2_InputHandle,
+ ColAtt_i,
+ ColAtt_Sqlstate,
+ &ColAtt_NativeError,
+ ColAtt_Msg,
+ sizeof(ColAtt_Msg),
+ &ColAtt_MsgLen))
+ != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << ColAttTest2_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)ColAttTest2_InputHandle << endl;
+ ndbout << "the ColAtt_Msg is: " << (char *) ColAtt_Msg << endl;
+ ndbout << "the output state is:" << (char *)ColAtt_Sqlstate << endl;
+
+ ColAtt_i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
diff --git a/storage/ndb/test/odbc/client/SQLColAttributeTest3.cpp b/storage/ndb/test/odbc/client/SQLColAttributeTest3.cpp
new file mode 100644
index 00000000000..f8817565711
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLColAttributeTest3.cpp
@@ -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 */
+
+ /**
+ * @file SQLColAttributeTest3.cpp
+ */
+
+#include <common.hpp>
+using namespace std;
+
+#define MAXIMUM_MESSAGE_LENGTH_Test3 200
+#define BufferLengthTest3 156
+
+SQLHSTMT ColAtt_hstmtTest3;
+SQLHSTMT ColAtt_hdbcTest3;
+SQLHENV ColAtt_henvTest3;
+SQLHDESC ColAtt_hdescTest3;
+
+SQLCHAR TypeName[18];
+SQLSMALLINT TypeNameLen;
+
+SQLRETURN ColAtt_retTest3;
+
+void ColAtt_DisplayErrorTest3(SQLSMALLINT ColAttTest3_HandleType,
+ SQLHSTMT ColAttTest3_InputHandle);
+
+/**
+ * Test returning descriptor information
+ *
+ * Test:
+ * -# Print out column name without executing statement
+ *
+ * @return Zero, if test succeeded
+ */
+
+int SQLColAttributeTest3()
+{
+ ndbout << endl << "Start SQLColAttribute Testing3" << endl;
+
+ SQLCHAR SQLStmt [120];
+
+ //********************************************************************
+ //** Test 3: **
+ //** **
+ //** Prepare a statement without executing the statement **
+ //** We want to print out the Type Name of each column in the table **
+ //** Customers **
+ //** **
+ //** Intended result: Only display column name, but there is no new **
+ //** row in table Customers **
+ //********************************************************************
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ ColAtt_retTest3 = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &ColAtt_henvTest3);
+
+ if (ColAtt_retTest3 == SQL_SUCCESS || ColAtt_retTest3 == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ ColAtt_retTest3 = SQLSetEnvAttr(ColAtt_henvTest3,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (ColAtt_retTest3 == SQL_SUCCESS || ColAtt_retTest3 == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ ColAtt_retTest3 = SQLAllocHandle(SQL_HANDLE_DBC,
+ ColAtt_henvTest3,
+ &ColAtt_hdbcTest3);
+
+ if (ColAtt_retTest3 == SQL_SUCCESS || ColAtt_retTest3 == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ ColAtt_retTest3 = SQLConnect(ColAtt_hdbcTest3,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (ColAtt_retTest3 == SQL_SUCCESS || ColAtt_retTest3 == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ ColAtt_retTest3 = SQLAllocHandle(SQL_HANDLE_STMT,
+ ColAtt_hdbcTest3,
+ &ColAtt_hstmtTest3);
+ if(ColAtt_retTest3 == SQL_SUCCESS || ColAtt_retTest3 == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //************************
+ //** Define a statement **
+ //************************
+
+ /*
+ strcpy((char *) SQLStmt,
+ "DELETE FROM Customers WHERE CustID = 6");
+ */
+
+ strcpy((char *) SQLStmt,
+ "INSERT INTO Customers (CustID, Name, Address, Phone) VALUES (6, 'Jan', 'LM vag 8', '969696')");
+
+ /*
+ strcpy((char *) SQLStmt,
+ "INSERT INTO Customers (CustID, Name, Address, Phone) VALUES (?, ?, ?, ?)");
+ */
+
+ //*****************************
+ //** Prepare SQL statement **
+ //*****************************
+ ColAtt_retTest3 = SQLPrepare(ColAtt_hstmtTest3,
+ SQLStmt,
+ SQL_NTS);
+
+ if (ColAtt_retTest3 == SQL_SUCCESS || ColAtt_retTest3 == SQL_SUCCESS_WITH_INFO)
+ {
+ //************************************
+ //** Display the name of column one **
+ //************************************
+ ColAtt_retTest3 = SQLColAttribute(ColAtt_hstmtTest3,
+ 1,
+ SQL_COLUMN_TYPE_NAME,
+ TypeName,
+ sizeof(TypeName),
+ &TypeNameLen,
+ NULL);
+
+ if (ColAtt_retTest3 == SQL_ERROR || ColAtt_retTest3 == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << endl << "ColAtt_retTest3 = " << ColAtt_retTest3 << endl;
+ ndbout << endl << "Name of column 1 is:"
+ << (char *)TypeName <<endl;
+ ColAtt_DisplayErrorTest3(SQL_HANDLE_STMT, ColAtt_hstmtTest3);
+ }
+
+ //************************************
+ //** Display the name of column two **
+ //************************************
+ ColAtt_retTest3 = SQLColAttribute(ColAtt_hstmtTest3,
+ 2,
+ SQL_DESC_BASE_COLUMN_NAME,
+ TypeName,
+ sizeof(TypeName),
+ &TypeNameLen,
+ NULL);
+
+ if (ColAtt_retTest3 == SQL_ERROR || ColAtt_retTest3 == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << endl << "ColAtt_retTest3 = " << ColAtt_retTest3 << endl;
+ ndbout << endl << "Name of column 2 is:"
+ << (char *)TypeName <<endl;
+ ColAtt_DisplayErrorTest3(SQL_HANDLE_STMT, ColAtt_hstmtTest3);
+ }
+
+ //***************************************
+ //** Display the name of column three **
+ //***************************************
+ ColAtt_retTest3 = SQLColAttribute(ColAtt_hstmtTest3,
+ 3,
+ SQL_DESC_BASE_COLUMN_NAME,
+ TypeName,
+ sizeof(TypeName),
+ &TypeNameLen,
+ NULL);
+
+ if (ColAtt_retTest3 == SQL_ERROR || ColAtt_retTest3 == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "ColAtt_retTest3 = " << ColAtt_retTest3 << endl;
+ ndbout << endl << "Name of column 3 is:"
+ << (char *)TypeName <<endl;
+ ColAtt_DisplayErrorTest3(SQL_HANDLE_STMT, ColAtt_hstmtTest3);
+ }
+
+ //**************************************
+ //** Display the name of column four **
+ //**************************************
+ ColAtt_retTest3 = SQLColAttribute(ColAtt_hstmtTest3,
+ 4,
+ SQL_DESC_BASE_COLUMN_NAME,
+ TypeName,
+ sizeof(TypeName),
+ &TypeNameLen,
+ NULL);
+
+ if (ColAtt_retTest3 == SQL_ERROR || ColAtt_retTest3 == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "ColAtt_retTest3 = " << ColAtt_retTest3 << endl;
+ ndbout << endl << "Name of column 4 is:"
+ << (char *)TypeName <<endl;
+ ColAtt_DisplayErrorTest3(SQL_HANDLE_STMT, ColAtt_hstmtTest3);
+ }
+
+ }
+
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(ColAtt_hdbcTest3);
+ SQLFreeHandle(SQL_HANDLE_STMT, ColAtt_hstmtTest3);
+ SQLFreeHandle(SQL_HANDLE_DBC, ColAtt_hdbcTest3);
+ SQLFreeHandle(SQL_HANDLE_ENV, ColAtt_henvTest3);
+
+ return NDBT_OK;
+}
+
+void ColAtt_DisplayErrorTest3(SQLSMALLINT ColAttTest3_HandleType,
+ SQLHSTMT ColAttTest3_InputHandle)
+{
+ SQLSMALLINT ColAtt_i = 1;
+ SQLRETURN ColAtt_SQLSTATEs;
+ SQLCHAR ColAtt_Sqlstate[5];
+ SQLCHAR ColAtt_Msg[MAXIMUM_MESSAGE_LENGTH_Test3];
+ SQLSMALLINT ColAtt_MsgLen;
+ SQLINTEGER ColAtt_NativeError;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((ColAtt_SQLSTATEs = SQLGetDiagRec(ColAttTest3_HandleType,
+ ColAttTest3_InputHandle,
+ ColAtt_i,
+ ColAtt_Sqlstate,
+ &ColAtt_NativeError,
+ ColAtt_Msg,
+ sizeof(ColAtt_Msg),
+ &ColAtt_MsgLen))
+ != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << ColAttTest3_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)ColAttTest3_InputHandle << endl;
+ ndbout << "the ColAtt_Msg is: " << (char *) ColAtt_Msg << endl;
+ ndbout << "the output state is:" << (char *)ColAtt_Sqlstate << endl;
+
+ ColAtt_i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
diff --git a/storage/ndb/test/odbc/client/SQLConnectTest.cpp b/storage/ndb/test/odbc/client/SQLConnectTest.cpp
new file mode 100644
index 00000000000..552fc8640fe
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLConnectTest.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 */
+
+ /**
+ * @file SQLConnectTest.cpp
+ */
+#include <common.hpp>
+using namespace std;
+
+SQLHDBC conn_hdbc;
+SQLHSTMT conn_hstmt;
+SQLHENV conn_henv;
+SQLHDESC conn_hdesc;
+SQLRETURN conn_retcode;
+
+#define conn_SQL_MAXIMUM_MESSAGE_LENGTH 200
+SQLCHAR conn_Sqlstate[5];
+
+SQLINTEGER conn_NativeError;
+SQLSMALLINT conn_MsgLen;
+SQLCHAR conn_Msg[conn_SQL_MAXIMUM_MESSAGE_LENGTH];
+
+void SQLConnectTest_DisplayError_HDBC(SQLSMALLINT conn_HandleType,
+ SQLHDBC conn_InputHandle);
+
+/**
+ * -# Test to make a connection to an ODBC data source
+ *
+ * @return Zero, if test succeeded
+ */
+int SQLConnectTest()
+{
+ ndbout << endl << "Start SQLConnect Testing" << endl;
+
+ // ************************************
+ // ** Allocate an environment handle **
+ // ************************************
+ conn_retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &conn_henv);
+ //conn_retcode = SQLAllocEnv(&conn_henv);
+ if(conn_retcode == SQL_SUCCESS || conn_retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Allocated an environment handle!" << endl;
+ }
+ else
+ {
+ ndbout << "Failed to allocate environment handle!" << endl;
+ return NDBT_FAILED;
+ }
+
+ // *********************************************
+ // ** Set the ODBC application Version to 3.x **
+ // *********************************************
+ conn_retcode = SQLSetEnvAttr(conn_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+ if (conn_retcode == SQL_SUCCESS || conn_retcode == SQL_SUCCESS_WITH_INFO) {
+ ndbout << "Set ODBC application version to 3.x" << endl;
+ } else {
+ ndbout << "Failed to set application version!" << endl;
+ return NDBT_FAILED;
+ }
+
+ // **********************************
+ // ** Allocate a connection handle **
+ // **********************************
+ conn_retcode = SQLAllocHandle(SQL_HANDLE_DBC, conn_henv, &conn_hdbc);
+ // retcode = SQLAllocConnect(conn_henv, &conn_hdbc);
+ if (conn_retcode == SQL_SUCCESS || conn_retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Allocated a connection handle!" << endl;
+ }
+ else
+ {
+ ndbout << "Failed to allocate connection handle!" << endl;
+ return NDBT_FAILED;
+ }
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ conn_retcode = SQLConnect(conn_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+ ndbout << "conn_retcode = " << conn_retcode << endl;
+ if (conn_retcode == SQL_SUCCESS)
+ {
+ ndbout << "Connected to DB!" << endl;
+ }
+ else if (conn_retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Connected to DB, but SQL_SUCCESS_WITH_INFO!" << endl;
+ SQLConnectTest_DisplayError_HDBC(SQL_HANDLE_DBC, conn_hdbc);
+ }
+ else if (conn_retcode == SQL_INVALID_HANDLE)
+ {
+ ndbout << "SQL_INVALID_HANDLE appeared. Please check program." << endl;
+ return NDBT_FAILED;
+ }
+ else if (conn_retcode == SQL_ERROR)
+ {
+ ndbout << "Failed to connect!" << endl;
+ SQLConnectTest_DisplayError_HDBC(SQL_HANDLE_DBC, conn_hdbc);
+ return NDBT_FAILED;
+ }
+ else
+ ;
+
+ // ******************
+ // ** Free Handles **
+ // ******************
+ SQLDisconnect(conn_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, conn_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, conn_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, conn_henv);
+ return NDBT_OK;
+}
+
+
+void SQLConnectTest_DisplayError_HDBC(SQLSMALLINT conn_HandleType,
+ SQLHDBC conn_InputHandle) {
+ SQLSMALLINT conn_i = 1;
+ SQLRETURN conn_SQLSTATE;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((conn_SQLSTATE = SQLGetDiagRec(conn_HandleType,
+ conn_InputHandle,
+ conn_i,
+ conn_Sqlstate,
+ &conn_NativeError,
+ conn_Msg,
+ sizeof(conn_Msg),
+ &conn_MsgLen)
+ ) != SQL_NO_DATA)
+ {
+ ndbout << "SQLSTATE = " << conn_SQLSTATE << endl;
+ ndbout << "the HandleType is: " << conn_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)conn_InputHandle << endl;
+ ndbout << "the conn_Msg is: " << (char *) conn_Msg << endl;
+ ndbout << "the output state is:" << (char *)conn_Sqlstate << endl;
+
+ conn_i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
diff --git a/storage/ndb/test/odbc/client/SQLCopyDescTest.cpp b/storage/ndb/test/odbc/client/SQLCopyDescTest.cpp
new file mode 100644
index 00000000000..4a3742f97ae
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLCopyDescTest.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 <NdbOut.hpp>
+#include <sqlcli.h>
+#include <stdio.h>
+
+using namespace std;
+
+#define NAME_LEN 50
+#define PHONE_LEN 10
+#define SALES_PERSON_LEN 10
+#define STATUS_LEN 6
+
+#define ROWS 100
+#define DESC_LEN 50
+
+
+// Template for a row
+typedef struct {
+ SQLINTEGER sPartID;
+ SQLINTEGER cbPartID;
+ SQLUCHAR szDescription[DESC_LENGTH];
+ SQLINTEGER cbDescription;
+ REAL sPrice;
+ SQLINTEGER cbPrice;
+} PartsSource;
+
+PartsSource rget[ROWS]; // rowset buffer
+SQLUSMALLINT sts_ptr[ROWS]; // status pointer
+SQLHSTMT hstmt0, hstmt1;
+SQLHDESC hArd0, hIrd0, hApd1, hIpd1;
+
+
+SQLHSTMT hstmt;
+SQLHDESC hdesc;
+
+SQLSMALLINT RecNumber;
+SQLCHAR szSalesPerson[SALES_PERSON_LEN];
+
+SQLCHAR Sqlstate[5], Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+SQLINTEGER NativeError;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLINTEGER ValuePtr1;
+SQLCHAR ValuePtr2;
+SQLSMALLINT ValuePtr3;
+
+
+SQLINTEGER StringLengthPtr;
+
+SQLSMALLINT i, MsgLen;
+
+void DisplayError(SQLSMALLINT HandleType, SQLHDESC InputHandle);
+
+int SQLCopyDescTest ()
+{
+
+
+ // We can create the table and insert rows in NDB by program TestDirectSQL.
+ // In this test program(SQLGetCopyRecTest),we only have three rows in table ORDERS
+
+
+// ARD and IRD of hstmt0
+SQLGetStmtAttr(hstmt0, SQL_ATTR_APP_ROW_DESC, &hArd0, 0, NULL);
+SQLGetStmtAttr(hstmt0, SQL_ATTR_IMP_ROW_DESC, &hIrd0, 0, NULL);
+
+// APD and IPD of hstmt1
+SQLGetStmtAttr(hstmt1, SQL_ATTR_APP_PARAM_DESC, &hApd1, 0, NULL);
+SQLGetStmtAttr(hstmt1, SQL_ATTR_IMP_PARAM_DESC, &hIpd1, 0, NULL);
+
+// Use row-wise binding on hstmt0 to fetch rows
+SQLSetStmtAttr(hstmt0, SQL_ATTR_ROW_BIND_TYPE, (SQLPOINTER) sizeof(PartsSource), 0);
+
+// Set rowset size for hstmt0
+SQLSetStmtAttr(hstmt0, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) ROWS, 0);
+
+// Execute a select statement
+SQLExecDirect(hstmt0, "SELECT PARTID, DESCRIPTION, PRICE FROM PARTS ORDER BY 3, 1, 2"",
+ SQL_NTS);
+
+// Bind
+SQLBindCol(hstmt0, 1, SQL_C_SLONG, rget[0].sPartID, 0,
+ &rget[0].cbPartID);
+SQLBindCol(hstmt0, 2, SQL_C_CHAR, &rget[0].szDescription, DESC_LEN,
+ &rget[0].cbDescription);
+SQLBindCol(hstmt0, 3, SQL_C_FLOAT, rget[0].sPrice,
+ 0, &rget[0].cbPrice);
+
+ // Perform parameter bindings on hstmt1.
+ /* If SourceDeschandle does not identify an allocated CLI descriptor area */
+ retcode1 = SQLCopyDesc(hArd0, hApd1);
+ retcode2 = SQLCopyDesc(hIrd0, hIpd1);
+
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ DisplayError(SQL_HANDLE_DESC, hdesc);
+
+
+ /* If TargetDeschandle does not identify an allocated CLI descriptor area */
+ retcode = SQLCopyDesc(hdesc, );
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ DisplayError(SQL_HANDLE_DESC, hdesc);
+
+
+ return 0;
+
+ }
+
+
+void DisplayError(SQLSMALLINT HandleType, SQLHDESC InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLDescribeColTest.cpp b/storage/ndb/test/odbc/client/SQLDescribeColTest.cpp
new file mode 100644
index 00000000000..9f55c6a1cfe
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLDescribeColTest.cpp
@@ -0,0 +1,260 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+ /**
+ * @file SQLDescribeColTest.cpp
+ */
+#include <common.hpp>
+
+using namespace std;
+
+#define DC_Column_NAME_LEN 50
+#define DC_MESSAGE_LENGTH 200
+
+SQLHSTMT DC_hstmt;
+SQLHDBC DC_hdbc;
+SQLHENV DC_henv;
+SQLHDESC DC_hdesc;
+
+void DescribeCol_DisplayError(SQLSMALLINT DC_HandleType,
+ SQLHSTMT DC_InputHandle);
+
+/**
+ * Test to retrieve basic result data set metadata information
+ * (specifically, column name, SQL data type, column size, decimal
+ * precision, and nullability) for a specified column in a result
+ * data set
+ * -# No prepared or executed statement when executing
+ * -# ColumnNumber is less than 1
+ * -# ColumnNumber is greater than the value of the TOP_LEVEL_COUNT field of IRD
+ * @return Zero, if test succeeded
+ */
+
+int SQLDescribeColTest()
+{
+ SQLCHAR SQLStmt [120];
+ SQLRETURN retcode;
+ SQLCHAR ColumnName[DC_Column_NAME_LEN];
+ SQLSMALLINT NameLength, DataTypePtr, DecimalDigitsPtr, NullablePtr;
+ SQLUINTEGER ColumnSizePtr;
+
+ ndbout << "Start SQLDescribeCol Test " << endl;
+ //******************************************************************
+ //** Test1 **
+ //** There is no prepared or executed statement associated with **
+ //** StatementHandle **
+ //******************************************************************
+
+ retcode = SQLDescribeCol(DC_hstmt,
+ (SQLUSMALLINT)1,
+ ColumnName,
+ sizeof(ColumnName),
+ &NameLength,
+ &DataTypePtr,
+ &ColumnSizePtr,
+ &DecimalDigitsPtr,
+ &NullablePtr);
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ DescribeCol_DisplayError(SQL_HANDLE_STMT, DC_hstmt);
+
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &DC_henv);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ retcode = SQLSetEnvAttr(DC_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_DBC,
+ DC_henv,
+ &DC_hdbc);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ retcode = SQLConnect(DC_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_STMT,
+ DC_hdbc,
+ &DC_hstmt);
+ if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //************************
+ //** Define a statement **
+ //************************
+
+ strcpy((char *) SQLStmt,
+ "SELECT * FROM Customers");
+
+ //***********************************************
+ //** Prepare and Execute the SQL statement **
+ //***********************************************
+
+ retcode = SQLExecDirect(DC_hstmt,
+ SQLStmt,
+ SQL_NTS);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ //*********************************
+ //** ColumnNumber is from 1 to 4 **
+ //*********************************
+ ndbout << endl << "ColumnNumber is from 1 to 4" << endl;
+
+ for (int ii = 1; ii <= 4; ii++)
+ {
+ retcode = SQLDescribeCol(DC_hstmt,
+ ii,
+ ColumnName,
+ sizeof(ColumnName),
+ &NameLength,
+ &DataTypePtr,
+ &ColumnSizePtr,
+ &DecimalDigitsPtr,
+ &NullablePtr);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Column Name = " << (char *)ColumnName << endl;
+
+ }
+
+ //*********************************
+ //** Test2 **
+ //** ColumnNumber is less than 1 **
+ //*********************************
+
+ retcode = SQLDescribeCol(DC_hstmt,
+ (SQLUSMALLINT)-1,
+ ColumnName,
+ sizeof(ColumnName),
+ &NameLength,
+ &DataTypePtr,
+ &ColumnSizePtr,
+ &DecimalDigitsPtr,
+ &NullablePtr);
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << endl << "ColumnNumber is less than 1" << endl;
+ DescribeCol_DisplayError(SQL_HANDLE_STMT, DC_hstmt);
+
+ //*********************************************************************
+ //** Test3 **
+ //** ColumnNumber is greater than N(the value of the TOP_LEVEL_COUNT **
+ //** field of IRD) **
+ //*********************************************************************
+
+ ndbout << endl <<"ColumnNumber is greater than N(the value"
+ << "of the TOP_LEVEL_COUNTfield of IRD)" << endl;
+
+ retcode = SQLDescribeCol(DC_hstmt,
+ (SQLUSMALLINT)1045,
+ ColumnName,
+ sizeof(ColumnName),
+ &NameLength,
+ &DataTypePtr,
+ &ColumnSizePtr,
+ &DecimalDigitsPtr,
+ &NullablePtr);
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ DescribeCol_DisplayError(SQL_HANDLE_STMT, DC_hstmt);
+
+}
+
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(DC_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, DC_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, DC_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, DC_henv);
+
+ return NDBT_OK;
+}
+
+void DescribeCol_DisplayError(SQLSMALLINT DC_HandleType,
+ SQLHSTMT DC_InputHandle)
+{
+ SQLCHAR Sqlstate[5], Msg[DC_MESSAGE_LENGTH];
+ SQLINTEGER NativeError;
+ SQLSMALLINT DC_i, MsgLen;
+ SQLRETURN SQLSTATEs;
+
+ DC_i = 1;
+
+ while ((SQLSTATEs = SQLGetDiagRec(DC_HandleType,
+ DC_InputHandle,
+ DC_i,
+ Sqlstate,
+ &NativeError,
+ Msg,
+ sizeof(Msg),
+ &MsgLen))
+ != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << DC_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)DC_InputHandle << endl;
+ ndbout << "the return message is:" << (char *)Msg << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ DC_i ++;
+ break;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLDisconnectTest.cpp b/storage/ndb/test/odbc/client/SQLDisconnectTest.cpp
new file mode 100644
index 00000000000..823b446ab84
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLDisconnectTest.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 */
+
+ /**
+ * @file SQLDisconnectTest.cpp
+ */
+
+#include <common.hpp>
+#define disc_SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC disc_hdbc;
+SQLHSTMT disc_hstmt;
+SQLHENV disc_henv;
+SQLHDESC disc_hdesc;
+
+void Disconnect_DisplayError_HDBC(SQLSMALLINT disc_HandleType,
+ SQLHDBC disc_InputHandle);
+/**
+ * Test to close the data source connection associated with
+ * a specific connection handle
+ *
+ * -# Normal case testing
+ * @return Zero, if test succeeded
+ */
+
+int SQLDisconnectTest()
+{
+ SQLRETURN disc_retcode;
+ ndbout << endl << "Start SQLDisconnect Testing" << endl;
+
+ // ************************************
+ // ** Allocate an environment handle **
+ // ************************************
+ disc_retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &disc_henv);
+
+ if(disc_retcode == SQL_SUCCESS || disc_retcode == SQL_SUCCESS_WITH_INFO) {
+ ndbout << "Allocated an environment handle!" << endl;
+ } else {
+ ndbout << "Failed to allocate environment handle!" << endl;
+ return NDBT_FAILED;
+ }
+
+ // *********************************************
+ // ** Set the ODBC application Version to 3.x **
+ // *********************************************
+ disc_retcode = SQLSetEnvAttr(disc_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+ if (disc_retcode == SQL_SUCCESS || disc_retcode == SQL_SUCCESS_WITH_INFO) {
+ ndbout << "Set ODBC application version to 3.x" << endl;
+ } else {
+ ndbout << "Failed to set application version!" << endl;
+ return NDBT_FAILED;
+ }
+
+ // **********************************
+ // ** Allocate a connection handle **
+ // **********************************
+ disc_retcode = SQLAllocHandle(SQL_HANDLE_DBC, disc_henv, &disc_hdbc);
+
+ if (disc_retcode == SQL_SUCCESS || disc_retcode == SQL_SUCCESS_WITH_INFO) {
+ ndbout << "Allocated a connection handle!" << endl;
+ } else {
+ ndbout << "Failed to allocate connection handle!" << endl;
+ return NDBT_FAILED;
+ }
+
+ // *******************
+ // ** connect to DB **
+ // *******************
+ disc_retcode = SQLConnect(disc_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ // **********************
+ // ** Disconnect to DB **
+ // **********************
+ disc_retcode = SQLDisconnect(disc_hdbc);
+
+ if (disc_retcode == SQL_INVALID_HANDLE)
+{
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE"
+ << " still appeared. Please check program" << endl;
+ Disconnect_DisplayError_HDBC(SQL_HANDLE_DBC, disc_hdbc);
+}
+
+ if (disc_retcode == SQL_ERROR || disc_retcode == SQL_SUCCESS_WITH_INFO)
+{
+ ndbout << "disconnect retcode = " << disc_retcode << endl;
+ Disconnect_DisplayError_HDBC(SQL_HANDLE_DBC, disc_hdbc);
+}
+ // ******************
+ // ** Free Handles **
+ // ******************
+ SQLFreeHandle(SQL_HANDLE_STMT, disc_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, disc_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, disc_henv);
+
+ return NDBT_OK;
+
+ }
+
+void Disconnect_DisplayError_HDBC(SQLSMALLINT disc_HandleType,
+ SQLHDBC disc_InputHandle)
+{
+ SQLCHAR disc_Msg[disc_SQL_MAXIMUM_MESSAGE_LENGTH];
+ SQLSMALLINT disc_i, disc_MsgLen;
+ SQLINTEGER disc_NativeError;
+ SQLRETURN disc_SQLSTATEs;
+ disc_i = 1;
+ SQLCHAR disc_Sqlstate[5];
+
+ while ((disc_SQLSTATEs = SQLGetDiagRec(disc_HandleType,
+ disc_InputHandle,
+ disc_i,
+ disc_Sqlstate,
+ &disc_NativeError,
+ disc_Msg,
+ sizeof(disc_Msg),
+ &disc_MsgLen))
+ != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << disc_HandleType << endl;
+ ndbout << "the InputHandle is :" <<(long)disc_InputHandle << endl;
+ ndbout << "the output state is:" << (char *)disc_Sqlstate << endl;
+
+ disc_i ++;
+ break;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLDriverConnectTest.cpp b/storage/ndb/test/odbc/client/SQLDriverConnectTest.cpp
new file mode 100644
index 00000000000..fc3b1d10f91
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLDriverConnectTest.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.hpp>
+#include <string.h>
+
+using namespace std;
+
+SQLHDBC driconn_hdbc;
+SQLHSTMT driconn_hstmt;
+SQLHENV driconn_henv;
+SQLHDESC driconn_hdesc;
+SQLRETURN driconn_retcode, driconn_SQLSTATEs;
+
+#define driconn_SQL_MAXIMUM_MESSAGE_LENGTH 200
+SQLCHAR driconn_Sqlstate[5];
+
+SQLINTEGER driconn_NativeError;
+SQLSMALLINT driconn_i, driconn_MsgLen;
+SQLCHAR driconn_Msg[driconn_SQL_MAXIMUM_MESSAGE_LENGTH], driconn_ConnectIn[30];
+
+void SQLDriverConnectTest_DisplayError_HDBC(SQLSMALLINT driconn_HandleType, SQLHDBC driconn_InputHandle);
+
+int SQLDriverConnectTest()
+{
+ ndbout << endl << "Start SQLDriverConnect Testing" << endl;
+ // Allocate An Environment Handle
+ driconn_retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &driconn_henv);
+
+ // Set the ODBC application Version to 3.x
+ driconn_retcode = SQLSetEnvAttr(driconn_henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
+
+ if (driconn_retcode == SQL_SUCCESS || driconn_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x" << endl;
+
+ // Allocate A Connection Handle
+ driconn_retcode = SQLAllocHandle(SQL_HANDLE_DBC, driconn_henv, &driconn_hdbc);
+
+ if (driconn_retcode == SQL_SUCCESS || driconn_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocation A Connection Handle" << endl;
+
+ // Build A Connection String
+ strcpy((char*) driconn_ConnectIn, "DSN=ndb;UID=x;PWD=y");
+
+ // Connect to NDB
+ driconn_retcode = SQLDriverConnect(driconn_hdbc, NULL, driconn_ConnectIn, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
+ ndbout << "retcode = " << driconn_retcode << endl;
+ ndbout << "Before pringing out information about connection, we print out retcode = " << driconn_retcode << endl;
+
+ if (driconn_retcode == SQL_SUCCESS || driconn_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to NDB" << endl;
+
+ if (driconn_retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ else
+ { if (driconn_retcode == SQL_ERROR || driconn_retcode == SQL_SUCCESS_WITH_INFO)
+ SQLDriverConnectTest_DisplayError_HDBC(SQL_HANDLE_DBC, driconn_hdbc);}
+
+ // Free the Connection Handle
+ SQLFreeHandle(SQL_HANDLE_DBC, driconn_hdbc);
+
+ // Free the Environment Handle
+ SQLFreeHandle(SQL_HANDLE_ENV, driconn_henv);
+
+ return 0;
+ }
+
+void SQLDriverConnectTest_DisplayError_HDBC(SQLSMALLINT driconn_HandleType, SQLHDBC driconn_InputHandle)
+{
+ driconn_i = 1;
+ while ((driconn_SQLSTATEs = SQLGetDiagRec(driconn_HandleType, driconn_InputHandle, driconn_i,
+ driconn_Sqlstate, &driconn_NativeError, driconn_Msg, sizeof(driconn_Msg),
+ &driconn_MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << driconn_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)driconn_InputHandle << endl;
+ ndbout << "the output state is:" << (char *)driconn_Sqlstate << endl;
+
+ driconn_i ++;
+ }
+
+}
diff --git a/storage/ndb/test/odbc/client/SQLEndTranTest.cpp b/storage/ndb/test/odbc/client/SQLEndTranTest.cpp
new file mode 100644
index 00000000000..06c497954fd
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLEndTranTest.cpp
@@ -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 */
+
+#include <common.h>
+
+using namespace std;
+
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLINTEGER strangehandle;
+SQLRETURN retcode, retcodeprepare, SQLSTATEs;
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+SQLSMALLINT Not_In_Table13;
+
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+
+void SQLEndTran_DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+
+int SQLEndTranTest()
+{
+
+ strangehandle = 67;
+ /* hstmt */
+ // Execute a statement to retrieve rows from the Customers table. We can create the table and
+ // inside rows into NDB by program TestDirectSQL
+ // retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", 56);
+
+ retcodeprepare = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcodeprepare == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+ retcode = SQLExecute(hstmt);
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+
+ /* HandleType is not in Table 13 */
+ Not_In_Table13 = 67;
+ SQLSTATEs = SQLEndTran(Not_In_Table13, (void*)strangehandle , SQL_COMMIT);
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(67, 67, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:67" << endl;
+ ndbout << "the InputHandle is :67" << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+ }
+
+ /* HandleType is STATEMENT HANDLE, if the value of Handle does not identity an allocated SQL_statement */
+ SQLSTATEs = SQLEndTran(SQL_HANDLE_STMT, hdbc, SQL_COMMIT);
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLEndTran_DisplayError(SQL_HANDLE_STMT, hstmt);
+
+ /* The value of CompletionType is not in Table 14 */
+ SQLSTATEs = SQLEndTran(SQL_HANDLE_STMT, hstmt, 8888);
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLEndTran_DisplayError(SQL_HANDLE_STMT, hstmt);
+
+ }
+
+ }
+ return 0;
+
+ }
+
+
+void SQLEndTran_DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLErrorTest.cpp b/storage/ndb/test/odbc/client/SQLErrorTest.cpp
new file mode 100644
index 00000000000..5220e7b5eed
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLErrorTest.cpp
@@ -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 */
+
+#if 0
+
+#include <NdbOut.hpp>
+#include <sqlcli.h>
+#include <stdio.h>
+
+using namespace std;
+
+#define NAME_LEN 50
+#define PHONE_LEN 10
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLCHAR Sqlstate[5];
+
+SQLSMALLINT i, MsgLen;
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+SQLCHAR szName[NAME_LEN], szPhone[PHONE_LEN];
+SQLINTEGER sCustID, cbName, cbCustID, cbPhone, NativeError;
+
+void DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+
+int SQLBindColTest ()
+{
+
+ /* hstmt */
+ // Execute a statement to retrieve rows from the Customers table. We can create the table and inside rows in
+ // NDB by another program TestDirectSQL. In this test program(SQLBindColTest),we only have three rows in
+ // table CUSTOMERS
+
+retcode = SQLExecDirect(hstmt, (SQLCHAR*)"SELECT CUSTID, NAME, PHONE FROM CUSTOMERS ORDER BY 2, 1, 3", SQL_NTS);
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+
+SQLBindCol(hstmt, 0, SQL_C_ULONG, &sCustID, 0, &cbCustID);
+while (TRUE) {
+retcode = SQLFetch(hstmt);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ DisplayError(SQL_HANDLE_STMT, hstmt);
+
+ }
+
+
+SQLBindCol(hstmt, 4, SQL_C_ULONG, &sCustID, 0, &cbCustID);
+while (TRUE) {
+retcode = SQLFetch(hstmt);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ DisplayError(SQL_HANDLE_STMT, hstmt);
+
+ }
+
+/* Bind columns 1, 2, and 3 */
+SQLBindCol(hstmt, 1, SQL_C_ULONG, &sCustID, 0, &cbCustID);
+SQLBindCol(hstmt, 2, SQL_C_CHAR, szName, NAME_LEN, &cbName);
+SQLBindCol(hstmt, 3, SQL_C_CHAR, szPhone, PHONE_LEN, &cbPhone);
+/* Fetch and print each row of data. On */
+/* an error, display a message and exit. */
+while (TRUE) {
+retcode = SQLFetch(hstmt);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ DisplayError(SQL_HANDLE_STMT, hstmt);
+
+ }
+ }
+
+ return 0;
+
+ }
+
+
+void DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+#endif
diff --git a/storage/ndb/test/odbc/client/SQLExecDirectTest.cpp b/storage/ndb/test/odbc/client/SQLExecDirectTest.cpp
new file mode 100644
index 00000000000..b9b4e770412
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLExecDirectTest.cpp
@@ -0,0 +1,353 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+ /**
+ * @file SQLExecDirectTest.cpp
+ */
+#include <common.hpp>
+#define EXD_MESSAGE_LENGTH 200
+#define EXD_NAME_LEN 10
+#define EXD_PHONE_LEN 10
+#define EXD_ADDRESS_LEN 10
+using namespace std;
+
+SQLHDBC EXD_hdbc;
+SQLHSTMT EXD_hstmt;
+SQLHENV EXD_henv;
+SQLHDESC EXD_hdesc;
+SQLRETURN EXD_ret, SQLSTATEs;
+
+void ExecDirect_DisplayError(SQLSMALLINT EXD_HandleType,
+ SQLHSTMT EXD_InputHandle);
+
+int EXD_Display_Result(SQLHSTMT EXDR_InputHandle);
+
+/**
+ * Test to execute a prepared ststement
+ *
+ * -# Normal case: Prepare and Execute a prepared statement
+ * -# Prepare and Execute an empty statement
+ * -# Prepare and Execute a statement with wrong henv handle
+ * -# Prepare and Execute a statement with wrong hdbc handle
+ * -# Prepare and Execute a statement with wrong hdesc handle
+ * @return Zero, if test succeeded
+ */
+
+int SQLExecDirectTest()
+{
+ ndbout << endl << "Start ExecDirect Testing" << endl;
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ EXD_ret = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &EXD_henv);
+
+if (EXD_ret == SQL_SUCCESS || EXD_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ EXD_ret = SQLSetEnvAttr(EXD_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+if (EXD_ret == SQL_SUCCESS || EXD_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ EXD_ret = SQLAllocHandle(SQL_HANDLE_DBC,
+ EXD_henv,
+ &EXD_hdbc);
+
+if (EXD_ret == SQL_SUCCESS || EXD_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ EXD_ret = SQLConnect(EXD_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+if (EXD_ret == SQL_SUCCESS || EXD_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ EXD_ret = SQLAllocHandle(SQL_HANDLE_STMT,
+ EXD_hdbc,
+ &EXD_hstmt);
+if(EXD_ret == SQL_SUCCESS || EXD_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //**********************************************
+ //** Test1 **
+ //** Prepare and Execute a prepared statement **
+ //**********************************************
+ EXD_ret = SQLExecDirect(EXD_hstmt,
+ (SQLCHAR*)"SELECT * FROM Customers",
+ SQL_NTS);
+
+ if (EXD_ret == SQL_INVALID_HANDLE)
+ {
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but SQL_INVALID_HANDLE" << endl;
+ ndbout << "still appeared. Please check program" << endl;
+ }
+
+ if (EXD_ret == SQL_ERROR || EXD_ret == SQL_SUCCESS_WITH_INFO)
+ ExecDirect_DisplayError(SQL_HANDLE_STMT, EXD_hstmt);
+
+ //*************************
+ //** Display the results **
+ //*************************
+
+ EXD_Display_Result(EXD_hstmt);
+
+ //*******************************************
+ //** Test2 **
+ //** Prepare and Execute an empty statement**
+ //** in order to see what will happen **
+ //*******************************************
+ EXD_ret = SQLExecDirect(EXD_hstmt,
+ (SQLCHAR*)" ",
+ SQL_NTS);
+
+ if (EXD_ret == SQL_ERROR || EXD_ret == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Prepare and Execute an empty statement," << endl;
+ ndbout << "The following case happened!" << endl;
+ ExecDirect_DisplayError(SQL_HANDLE_STMT, EXD_hstmt);
+ }
+
+ //***************************************************************
+ //** Test3 **
+ //** Prepare and Execute a statement with wrong henv handle **
+ //** in order to see what will happen **
+ //***************************************************************
+ EXD_ret = SQLExecDirect(EXD_henv,
+ (SQLCHAR*)"SELECT * FROM Customers",
+ SQL_NTS);
+
+ if (EXD_ret == SQL_SUCCESS_WITH_INFO || EXD_ret == SQL_SUCCESS)
+ { ndbout << "Handle Type is SQL_HANDLE_HENV, but SQL_INVALID_HANDLE" << endl;
+ ndbout << "still appeared. Please check programm" << endl;
+ ExecDirect_DisplayError(SQL_HANDLE_ENV, EXD_henv);
+ }
+
+ //******************************************************************
+ //** Test4 **
+ //** Prepare and Execute a statement with wrong hdbc handle **
+ //** in order to see what will happen **
+ //******************************************************************
+
+ EXD_ret = SQLExecDirect(EXD_hdbc,
+ (SQLCHAR*)"SELECT * FROM Customers",
+ SQL_NTS);
+
+ if (EXD_ret == SQL_SUCCESS_WITH_INFO || EXD_ret == SQL_SUCCESS)
+ ExecDirect_DisplayError(SQL_HANDLE_DBC, EXD_hdbc);
+
+ //*******************************************************************
+ //** Test5 **
+ //** Prepare and Execute a statement with wrong hdesc handle **
+ //** in order to see what will happen **
+ //*******************************************************************
+
+ EXD_ret = SQLExecDirect(EXD_hdesc,
+ (SQLCHAR*)"SELECT * FROM Customers",
+ SQL_NTS);
+
+ if (EXD_ret == SQL_SUCCESS_WITH_INFO || EXD_ret == SQL_SUCCESS)
+ {
+ ndbout << "Handle Type is SQL_HANDLE_DESC, but SQL_SUCCESS_WITH_INFO" <<endl;
+ ndbout << "appeared. Please check program" << endl;
+ ExecDirect_DisplayError(SQL_HANDLE_DESC, EXD_hdesc);
+ }
+
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(EXD_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, EXD_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, EXD_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, EXD_henv);
+
+ return NDBT_OK;
+
+ }
+
+
+void ExecDirect_DisplayError(SQLSMALLINT EXD_HandleType,
+ SQLHSTMT EXD_InputHandle)
+{
+ SQLCHAR EXD_Sqlstate[5];
+ SQLINTEGER EXD_NativeError;
+ SQLSMALLINT EXD_i, EXD_MsgLen;
+ SQLCHAR EXD_Msg[EXD_MESSAGE_LENGTH];
+ SQLRETURN SQLSTATEs;
+ EXD_i = 1;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((SQLSTATEs = SQLGetDiagRec(EXD_HandleType,
+ EXD_InputHandle,
+ EXD_i,
+ EXD_Sqlstate,
+ &EXD_NativeError,
+ EXD_Msg,
+ sizeof(EXD_Msg),
+ &EXD_MsgLen))
+ != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << EXD_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)EXD_InputHandle << endl;
+ ndbout << "the ColAtt_Msg is: " << (char *) EXD_Msg << endl;
+ ndbout << "the output state is:" << (char *)EXD_Sqlstate << endl;
+
+ EXD_i ++;
+ // break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+int EXD_Display_Result(SQLHSTMT EXDR_InputHandle)
+{
+ SQLRETURN EXD_retcode;
+ unsigned long EXD_CustID;
+ SQLCHAR EXD_Name[EXD_NAME_LEN], EXD_Phone[EXD_PHONE_LEN];
+ SQLCHAR EXD_Address[EXD_ADDRESS_LEN];
+
+ //*********************
+ //** Bind columns 1 **
+ //*********************
+ EXD_retcode =SQLBindCol(EXDR_InputHandle,
+ 1,
+ SQL_C_ULONG,
+ &EXD_CustID,
+ sizeof(EXD_CustID),
+ NULL);
+ if (EXD_retcode == SQL_ERROR)
+ {
+ ndbout << "Executing SQLBindCol, SQL_ERROR happened!" << endl;
+ ExecDirect_DisplayError(SQL_HANDLE_STMT, EXDR_InputHandle);
+ return NDBT_FAILED;
+ }
+ //*********************
+ //** Bind columns 2 **
+ //*********************
+
+ EXD_retcode =SQLBindCol(EXDR_InputHandle,
+ 2,
+ SQL_C_CHAR,
+ &EXD_Name,
+ EXD_NAME_LEN,
+ NULL);
+ if (EXD_retcode == SQL_ERROR)
+ {
+ ndbout << "Executing SQLBindCol, SQL_ERROR happened!" << endl;
+ ExecDirect_DisplayError(SQL_HANDLE_STMT, EXDR_InputHandle);
+ return NDBT_FAILED;
+ }
+
+ //*********************
+ //** Bind columns 3 **
+ //*********************
+
+ EXD_retcode = SQLBindCol(EXDR_InputHandle,
+ 3,
+ SQL_C_CHAR,
+ &EXD_Address,
+ EXD_ADDRESS_LEN,
+ NULL);
+
+ if (EXD_retcode == SQL_ERROR)
+ {
+ ndbout << "Executing SQLBindCol, SQL_ERROR happened!" << endl;
+ ExecDirect_DisplayError(SQL_HANDLE_STMT, EXDR_InputHandle);
+ return NDBT_FAILED;
+ }
+
+ //*********************
+ //** Bind columns 4 **
+ //*********************
+
+ EXD_retcode = SQLBindCol(EXDR_InputHandle,
+ 4,
+ SQL_C_CHAR,
+ &EXD_Phone,
+ EXD_PHONE_LEN,
+ NULL);
+
+ if (EXD_retcode == SQL_ERROR)
+ {
+ ndbout << "Executing SQLBindCol, SQL_ERROR happened!" << endl;
+ ExecDirect_DisplayError(SQL_HANDLE_STMT, EXDR_InputHandle);
+ return NDBT_FAILED;
+ }
+
+ //*****************************************
+ //* Fetch and print each row of data. On **
+ //* an error, display a message and exit **
+ //*****************************************
+
+ if (EXD_retcode != SQL_ERROR)
+ EXD_retcode = SQLFetch(EXDR_InputHandle);
+
+ ndbout << endl << "EXD_retcode = SQLFetch(EXDR_InputHandle) = "
+ << EXD_retcode << endl;
+
+ if (EXD_retcode == SQL_ERROR)
+ {
+ ndbout << "Executing SQLFetch, SQL_ERROR happened!" << endl;
+ ExecDirect_DisplayError(SQL_HANDLE_STMT, EXDR_InputHandle);
+ return NDBT_FAILED;
+ }
+ else if (EXD_retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "CustID = " << (int)EXD_CustID << endl;
+ ndbout << "Name = " << (char *)EXD_Name << endl;
+ ndbout << "Address = " << (char *)EXD_Address << endl;
+ ndbout << "Phone = " << (char *)EXD_Phone << endl;
+ ExecDirect_DisplayError(SQL_HANDLE_STMT, EXDR_InputHandle);
+ }
+ else
+ {
+ ndbout << "CustID = " << (int)EXD_CustID << endl;
+ ndbout << "Name = " << (char *)EXD_Name << endl;
+ ndbout << "Address = " << (char *)EXD_Address << endl;
+ ndbout << "Phone = " << (char *)EXD_Phone << endl;
+ }
+ return 0;
+}
diff --git a/storage/ndb/test/odbc/client/SQLExecuteTest.cpp b/storage/ndb/test/odbc/client/SQLExecuteTest.cpp
new file mode 100644
index 00000000000..5f6bdb5d4bf
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLExecuteTest.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 */
+
+ /**
+ * @file SQLExecuteTest.cpp
+ */
+
+#include <common.hpp>
+#define ESQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC Ehdbc;
+SQLHSTMT Ehstmt;
+SQLHENV Ehenv;
+SQLHDESC Ehdesc;
+
+void Execute_DisplayError(SQLSMALLINT EHandleType,
+ SQLHSTMT EInputHandle);
+
+/**
+ * Test to execute a SQL statement in a data result set
+ *
+ * Tests:
+ * -# Test1 There is no executed statement
+ * @return Zero, if test succeeded
+ */
+int SQLExecuteTest()
+{
+
+ SQLRETURN retcode;
+ /* hstmt */
+ retcode = SQLExecute(Ehstmt);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but SQL_INVALID_HANDLE" << endl;
+ ndbout << "still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ Execute_DisplayError(SQL_HANDLE_STMT, Ehstmt);
+
+ /* henv */
+ retcode = SQLExecute(Ehenv);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS)
+ ndbout << "Handle Type is SQL_HANDLE_ENV, but SQL_SUCCESS_WITH_INFO"
+ << "still appeared. Please check programm" << endl;
+ // Execute_DisplayError(SQL_HANDLE_ENV, Ehenv);
+
+ /* hdbc */
+ retcode = SQLExecute(Ehdbc);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but SQL_SUCCESS_WITH_INFO"
+ <<"still appeared. Please check programm" << endl;
+ // Execute_DisplayError(SQL_HANDLE_DBC, Ehdbc);
+
+ /* hdesc */
+ retcode = SQLExecute(Ehdesc);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS)
+ ndbout << "Handle Type is SQL_HANDLE_DESC, but SQL_SUCCESS_WITH_INFO"
+ << "still appeared. Please check programm" << endl;
+ // Execute_DisplayError(SQL_HANDLE_DESC, Ehdesc);
+
+ return NDBT_OK;
+
+ }
+
+
+void Execute_DisplayError(SQLSMALLINT EHandleType,
+ SQLHSTMT EInputHandle)
+{
+ SQLCHAR Sqlstate[5];
+
+ SQLINTEGER NativeError;
+ SQLSMALLINT i, MsgLen;
+ SQLCHAR Msg[ESQL_MAXIMUM_MESSAGE_LENGTH];
+ SQLRETURN SQLSTATEs;
+ i = 1;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((SQLSTATEs = SQLGetDiagRec(EHandleType,
+ EInputHandle,
+ i,
+ Sqlstate,
+ &NativeError,
+ Msg,
+ sizeof(Msg),
+ &MsgLen))
+ != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << EHandleType << endl;
+ ndbout << "the InputHandle is :" << EInputHandle << endl;
+ ndbout << "the Msg is :" << (char *)Msg << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ break;
+
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLFetchScrollTest.cpp b/storage/ndb/test/odbc/client/SQLFetchScrollTest.cpp
new file mode 100644
index 00000000000..4a11ccd143e
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLFetchScrollTest.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 <NdbOut.hpp>
+#include <stdio.h>
+#include <sqlext.h>
+
+using namespace std;
+
+#define NAME_LEN 50
+#define PHONE_LEN 10
+#define SALES_PERSON_LEN 10
+#define STATUS_LEN 6
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+SQLHSTMT hstmt;
+SQLSMALLINT RecNumber;
+SQLCHAR szSalesPerson[SALES_PERSON_LEN];
+
+SQLCHAR Sqlstate[5], Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+SQLINTEGER NativeError;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLINTEGER ValuePtr1;
+SQLCHAR ValuePtr2;
+SQLSMALLINT ValuePtr3;
+SQLSMALLINT i, MsgLen;
+
+void SFCT_DisplayError(SQLSMALLINT HandleType, SQLHDESC InputHandle);
+
+int SQLFetchScrollTest ()
+{
+
+ // FetchScroll a statement to retrieve rows from the Customers table. We can
+ // create the table and insert rows in NDB by program TestDirectSQL
+
+ /* There is no executed statement associated with the allocated SQL-statement identified by StatementHandle */
+retcode = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 1);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SFCT_DisplayError(SQL_HANDLE_DESC, hstmt);
+
+ /* FetchOrientation is not one of the code values in Table24 */
+retcode = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 8);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SFCT_DisplayError(SQL_HANDLE_DESC, hstmt);
+
+ return 0;
+
+ }
+
+
+void SFCT_DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLFetchTest.cpp b/storage/ndb/test/odbc/client/SQLFetchTest.cpp
new file mode 100644
index 00000000000..bd62fcb2f04
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLFetchTest.cpp
@@ -0,0 +1,438 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+ /**
+ * @file SQLFetchTest.cpp
+ */
+
+#include <common.hpp>
+#define F_MESSAGE_LENGTH 200
+using namespace std;
+
+#define F_NAME_LEN 20
+#define F_PHONE_LEN 20
+#define F_ADDRESS_LEN 20
+
+SQLHSTMT F_hstmt;
+SQLHDESC F_hdbc;
+SQLHENV F_henv;
+SQLHDESC F_hdesc;
+
+void SQLFetchTest_DisplayError(SQLSMALLINT F_HandleType,
+ SQLHDESC F_InputHandle);
+
+/**
+ * Test to advance a cursor to the next row of data in a data result set
+ * and to retrieve data from any bound columns that exist for that row
+ * into their associated application variables
+ *
+ * Tests:
+ * _# Test1 Execute statements and display the results
+ * -# Test2 There is no executed statement
+ * @return Zero, if test succeeded
+ */
+int SQLFetchTest()
+{
+ SQLRETURN retcode;
+ SQLCHAR SQLStmt[120];
+ SQLCHAR SQLStmt1[120];
+ SQLCHAR SQLStmt2[120];
+ SQLCHAR SQLStmt3[120];
+ SQLCHAR SQLStmt4[120];
+
+ SQLCHAR F_CustID[20];
+ SQLCHAR F_Name[F_NAME_LEN], F_Phone[F_PHONE_LEN];
+ SQLCHAR F_Address[F_ADDRESS_LEN];
+
+ ndbout << "Start SQLFetch Testing!" << endl;
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &F_henv);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ retcode = SQLSetEnvAttr(F_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_DBC,
+ F_henv,
+ &F_hdbc);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ retcode = SQLConnect(F_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_STMT,
+ F_hdbc,
+ &F_hstmt);
+if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //************************
+ //** Define a statement **
+ //************************
+
+ /* *** CustID is Integer *** */
+ strcpy((char *) SQLStmt1, "CREATE TABLE Customers (CustID Integer, Name Char(12), Address Char(12), Phone Char(12), Primary Key(CustID, Name))");
+
+ /* *** the primary key is alone *** */
+// strcpy((char *) SQLStmt1, "CREATE TABLE Customers (CustID Integer, Name Char(12), Address Char(12), Phone Char(12), Primary Key(CustID))");
+
+ strcpy((char *) SQLStmt2, "INSERT INTO Customers (CustID, Name, Address,Phone) VALUES(188, 'peter','LM Vag8','7190890')");
+
+ /* *** CustID is Float *** */
+// strcpy((char *) SQLStmt1,
+// "CREATE TABLE Customers (CustID float, Name Char(12), Address Char(12), Phone Char(12), Primary Key(CustID))");
+// strcpy((char *) SQLStmt2, "INSERT INTO Customers (CustID, Name, Address,Phone) VALUES(1.1516, 'peter','LM Vag8','7190890')");
+
+ /* *** CustID is Char *** */
+ // strcpy((char *) SQLStmt1, "CREATE TABLE Customers (CustID char(6), Name Char(12), Address Char(12), Phone Char(12), Primary Key(CustID))");
+
+ // strcpy((char *) SQLStmt2, "INSERT INTO Customers (CustID, Name, Address,Phone) VALUES('000001', 'peter','LM Vag8','7190890')");
+
+ /* The UPDATE statements */
+ // strcpy((char *) SQLStmt3, "UPDATE Customers SET Phone = '98998' WHERE CustID = 1.1516");
+
+ // strcpy((char *) SQLStmt3, "UPDATE Customers SET Phone = '98998' WHERE CustID = '000001'");
+
+ // strcpy((char *) SQLStmt3, "UPDATE Customers SET Phone = '98998' WHERE CustID = 188");
+
+ strcpy((char *) SQLStmt3, "UPDATE Customers SET Phone = '98998' WHERE CustID = 188 AND Name = 'peter'");
+
+ // DELETE statements
+
+ // DELETE all records
+ // strcpy((char *) SQLStmt4, "DELETE FROM Customers");
+
+ // DELETE One record
+ // strcpy((char *) SQLStmt4, "DELETE FROM Customers WHERE CustID = 1.1516");
+ // strcpy((char *) SQLStmt4, "DELETE FROM Customers WHERE CustID = '000001'");
+ // strcpy((char *) SQLStmt4, "DELETE FROM Customers WHERE CustID = 188 AND Name = 'peter'");
+ // strcpy((char *) SQLStmt4, "DELETE FROM Customers WHERE CustID = 188");
+
+ strcpy((char *) SQLStmt4, "DELETE FROM Customers WHERE CustID = 188 AND Name = 'peter'");
+
+ //SELECT statements
+ strcpy((char *) SQLStmt, "SELECT * FROM Customers");
+
+ //********************************
+ //** Prepare CREATE statements **
+ //********************************
+
+ ndbout << ">>>>" << (char*)SQLStmt1 << "<<<<" << endl;
+ retcode = SQLPrepare(F_hstmt,
+ SQLStmt1,
+ SQL_NTS);
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLFetchTest_DisplayError(SQL_HANDLE_STMT, F_hstmt);
+
+ //******************************************************************
+ //** There is no executed statement associated with the allocated **
+ //** SQL-statement identified by StatementHandle **
+ //******************************************************************
+
+ //This function is correct after testing. We don't test again.
+ /*
+ retcode = SQLFetch(F_hstmt);
+ ndbout << endl << "retcode = SQLFetch(F_hstmt) = " << retcode << endl;
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "There is no executed statement associated with" << endl;
+ ndbout << "the allocated SQL-statement" << endl;
+ SQLFetchTest_DisplayError(SQL_HANDLE_DESC, F_hstmt);
+
+ }
+ */
+
+ //*******************************
+ //** Execute CREATE statement **
+ //*******************************
+
+ retcode = SQLExecute(F_hstmt);
+
+ if (retcode == 0)
+ ndbout << endl << "Execute CREATE TABLE Statement OK!" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLFetchTest_DisplayError(SQL_HANDLE_STMT, F_hstmt);
+
+ //********************************
+ //** Prepare INSERT statements **
+ //********************************
+
+ ndbout << ">>>>" << (char*)SQLStmt2 << "<<<<" << endl;
+ retcode = SQLPrepare(F_hstmt,
+ SQLStmt2,
+ SQL_NTS);
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLFetchTest_DisplayError(SQL_HANDLE_STMT, F_hstmt);
+
+
+ //******************************
+ //** Execute INSERT statement **
+ //******************************
+ retcode = SQLExecute(F_hstmt);
+
+ if (retcode == 0)
+ ndbout << endl <<"Execute INSERT Statement OK!" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLFetchTest_DisplayError(SQL_HANDLE_STMT, F_hstmt);
+
+ //********************************
+ //** Prepare UPDATE statements **
+ //********************************
+
+ ndbout << ">>>>" << (char*)SQLStmt3 << "<<<<" << endl;
+ retcode = SQLPrepare(F_hstmt,
+ SQLStmt3,
+ SQL_NTS);
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLFetchTest_DisplayError(SQL_HANDLE_STMT, F_hstmt);
+
+ //******************************
+ //** Execute UPDATE statement **
+ //******************************
+ retcode = SQLExecute(F_hstmt);
+
+ if (retcode == 0)
+ ndbout << endl <<"Execute UPDATE Statement OK!" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLFetchTest_DisplayError(SQL_HANDLE_STMT, F_hstmt);
+
+ //********************************
+ //** Prepare DELETE statements **
+ //********************************
+ ndbout << ">>>>" << (char*)SQLStmt4 << "<<<<" << endl;
+ retcode = SQLPrepare(F_hstmt,
+ SQLStmt4,
+ SQL_NTS);
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << endl << "Preparing DELETE Statement failure!" << endl;
+ SQLFetchTest_DisplayError(SQL_HANDLE_STMT, F_hstmt);
+ }
+
+ //******************************
+ //** Execute DELETE statement **
+ //******************************
+
+ retcode = SQLExecute(F_hstmt);
+
+ if (retcode == 0)
+ ndbout << endl <<"Execute DELETE Statement OK!" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "DELETE Statement executing failure!" << endl;
+ SQLFetchTest_DisplayError(SQL_HANDLE_STMT, F_hstmt);
+ }
+ //********************************
+ //** Prepare SELECT statements **
+ //********************************
+ ndbout << ">>>>" << (char*)SQLStmt << "<<<<" << endl;
+ retcode = SQLPrepare(F_hstmt,
+ SQLStmt,
+ SQL_NTS);
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLFetchTest_DisplayError(SQL_HANDLE_STMT, F_hstmt);
+
+ /*
+ //******************************
+ //** Execute SELECT statement **
+ //******************************
+
+ retcode = SQLExecute(F_hstmt);
+
+ if (retcode == 0)
+ ndbout << endl <<"Execute SELECT Statement OK!" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLFetchTest_DisplayError(SQL_HANDLE_STMT, F_hstmt);
+ */
+
+ //********************
+ //** Bind columns **
+ //********************
+
+ retcode =SQLBindCol(F_hstmt,
+ 1,
+ SQL_C_CHAR,
+ F_CustID,
+ sizeof(F_CustID),
+ NULL);
+ ndbout << endl << "Bind Col1 retcode = " << retcode << " OK!" << endl;
+
+ retcode =SQLBindCol(F_hstmt,
+ 2,
+ SQL_C_CHAR,
+ F_Name,
+ F_NAME_LEN,
+ NULL);
+
+ ndbout << "Bind Col2 retcode = " << retcode << " OK!" << endl;
+
+ retcode = SQLBindCol(F_hstmt,
+ 3,
+ SQL_C_CHAR,
+ F_Address,
+ F_ADDRESS_LEN,
+ NULL);
+
+ ndbout << "Bind Col3 retcode = " << retcode << " OK!" << endl;
+
+ retcode = SQLBindCol(F_hstmt,
+ 4,
+ SQL_C_CHAR,
+ F_Phone,
+ F_PHONE_LEN,
+ NULL);
+
+ ndbout << "Bind Col4 retcode = " << retcode << " OK!" << endl;
+
+ //******************************
+ //** Execute SELECT statement **
+ //******************************
+
+ retcode = SQLExecute(F_hstmt);
+
+ if (retcode == 0)
+ ndbout << endl <<"Execute SELECT Statement OK!" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLFetchTest_DisplayError(SQL_HANDLE_STMT, F_hstmt);
+
+ //***************
+ //* Fetch data **
+ //***************
+ ndbout << endl <<"Executing Fetch SELECT Statement ......" << endl;
+
+ retcode = SQLFetch(F_hstmt);
+
+ if (retcode == 100)
+ ndbout << endl <<"Execute Fetch SELECT Statement, But No DATA!" << endl;
+
+ if (retcode == 0)
+ ndbout << endl <<"Execute Fetch SELECT Statement OK!" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLFetchTest_DisplayError(SQL_HANDLE_STMT, F_hstmt);
+
+ //*******************
+ //* Display result **
+ //*******************
+ ndbout << endl << "The results is : " << endl;
+ ndbout << "CustID = " << (char *)F_CustID << endl;
+ ndbout << "Name = " << (char *)F_Name << endl;
+ ndbout << "Address = " << (char *)F_Address << endl;
+ ndbout << "Phone = " << (char *)F_Phone << endl;
+
+
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(F_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, F_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, F_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, F_henv);
+
+ return NDBT_OK;
+
+ }
+
+
+void SQLFetchTest_DisplayError(SQLSMALLINT F_HandleType,
+ SQLHSTMT F_InputHandle)
+{
+ SQLCHAR Sqlstate[50], Msg[F_MESSAGE_LENGTH];
+ SQLRETURN SQLSTATEs;
+ SQLINTEGER NativeError;
+ SQLSMALLINT i, MsgLen;
+ Msg[0] = 0;
+ i = 1;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((SQLSTATEs = SQLGetDiagRec(F_HandleType,
+ F_InputHandle,
+ i,
+ Sqlstate,
+ &NativeError,
+ Msg,
+ sizeof(Msg),
+ &MsgLen))
+ != SQL_NO_DATA)
+{
+
+ ndbout << "the HandleType is:" << F_HandleType << endl;
+ ndbout << "the InputHandle is :" <<(long)F_InputHandle << endl;
+ ndbout << "the Msg is :" << (char *)Msg << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ break;
+}
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLFreeHandleTest.cpp b/storage/ndb/test/odbc/client/SQLFreeHandleTest.cpp
new file mode 100644
index 00000000000..3a7241dbe68
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLFreeHandleTest.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 "common.h"
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+int strangehandle;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+
+
+void freehandle_deal_with_HSTMT(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+void freehandle_deal_with_HENV(SQLSMALLINT HandleType, SQLHENV InputHandle);
+void freehandle_deal_with_HDESC(SQLSMALLINT HandleType, SQLHDESC InputHandle);
+void freehandle_deal_with_HDBC(SQLSMALLINT HandleType, SQLHDBC InputHandle);
+
+void freehandle_DisplayError_HDBC(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDBC InputHandle);
+void freehandle_DisplayError_HSTMT(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+void freehandle_DisplayError_HENV(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHENV InputHandle);
+void freehandle_DisplayError_HDESC(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDESC InputHandle);
+
+int SQLFreeHandleTest ()
+{
+
+strangehandle = 67;
+
+/* ENV */
+ndbout << "Environment Handle" << endl;
+SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
+freehandle_deal_with_HENV(SQL_HANDLE_ENV, henv);
+
+/* DBC */
+ndbout << "Connection Handle" << endl;
+SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
+SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
+freehandle_deal_with_HDBC(SQL_HANDLE_DBC, hdbc);
+
+/* STMT */
+ndbout << "Statement Handle" << endl;
+SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
+SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
+freehandle_deal_with_HSTMT(SQL_HANDLE_STMT, hstmt);
+
+/* DESC */
+ndbout << "Descriptor Handle" << endl;
+SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &hdesc);
+freehandle_deal_with_HDESC(SQL_HANDLE_DESC, hdesc);
+
+return 0;
+
+}
+
+
+void freehandle_deal_with_HDBC(SQLSMALLINT HandleType, SQLHDBC InputHandle)
+{
+ SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+ retcode = SQLFreeHandle(HandleType, InputHandle);
+
+ ndbout << "the HandleType is : " << HandleType << endl;
+ ndbout << "the InputHandle is SQLHDBC:" << InputHandle << endl;
+ ndbout << "retcode = " << retcode << endl;
+ /*
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+ DisplayError_HDBC_free(Sqlstate, HandleType, InputHandle);
+
+ i ++;
+ }
+ }
+ */
+ }
+
+
+void freehandle_deal_with_HSTMT(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+ retcode = SQLFreeHandle(HandleType, InputHandle);
+
+ ndbout << "the HandleType is : " << HandleType << endl;
+ ndbout << "the InputHandle is SQLHSTMT:" << InputHandle << endl;
+ ndbout << "retcode = " << retcode << endl;
+ /*
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+ DisplayError_HSTMT_free(Sqlstate, HandleType, InputHandle);
+
+ i ++;
+ }
+ }
+ */
+ }
+
+void freehandle_deal_with_HENV(SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+ retcode = SQLFreeHandle(HandleType, InputHandle);
+
+ ndbout << "the HandleType is : " << HandleType << endl;
+ ndbout << "the InputHandle is SQLHENV:" << InputHandle << endl;
+ ndbout << "retcode = " << retcode << endl;
+ /*
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ DisplayError_HENV_free(Sqlstate, HandleType, InputHandle);
+
+ i ++;
+ }
+ }
+ */
+ }
+
+void freehandle_deal_with_HDESC(SQLSMALLINT HandleType, SQLHDESC InputHandle)
+{
+ SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+ retcode = SQLFreeHandle(HandleType, InputHandle);
+
+ ndbout << "the HandleType is : " << HandleType << endl;
+ ndbout << "the InputHandle is SQLHDESC:" << InputHandle << endl;
+ ndbout << "retcode = " << retcode << endl;
+ /*
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ DisplayError_HDESC_free(Sqlstate, HandleType, InputHandle);
+
+ i ++;
+ }
+ }
+ */
+ }
+
+
+void freehandle_DisplayError_HENV(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
+void freehandle_DisplayError_HDBC(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDBC InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
+void freehandle_DisplayError_HSTMT(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
+void freehandle_DisplayError_HDESC(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDESC InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
diff --git a/storage/ndb/test/odbc/client/SQLFreeStmtTest.cpp b/storage/ndb/test/odbc/client/SQLFreeStmtTest.cpp
new file mode 100644
index 00000000000..e636b3063de
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLFreeStmtTest.cpp
@@ -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 */
+
+#include <common.h>
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+int strange_handle;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+
+struct handle_set
+{
+SQLHDBC hdbc_varible;
+SQLHSTMT hstmt_varible;
+SQLHENV henv_varible;
+SQLHDESC hdesc_varible;
+int strangehandle;
+};
+handle_set handlevalue;
+
+void handle_deal_with_HSTMT(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+void handle_deal_with_HENV(SQLSMALLINT HandleType, SQLHENV InputHandle);
+void handle_deal_with_HDESC(SQLSMALLINT HandleType, SQLHDESC InputHandle);
+void handle_deal_with_HDBC(SQLSMALLINT HandleType, SQLHDBC InputHandle);
+
+void DisplayError_HDBC(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDBC InputHandle);
+void DisplayError_HSTMT(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+void DisplayError_HENV(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHENV InputHandle);
+void DisplayError_HDESC(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDESC InputHandle);
+
+int SQLFreeStmtTest ()
+{
+
+handlevalue.hdbc_varible = hdbc;
+handlevalue.hstmt_varible = hstmt;
+handlevalue.henv_varible = henv;
+handlevalue.hdesc_varible = hdesc;
+handlevalue.strangehandle = 67;
+
+
+/* ENV */
+handle_deal_with_HENV(SQL_HANDLE_ENV, SQL_NULL_HANDLE);
+
+/* DBC */
+handle_deal_with_HDBC(SQL_HANDLE_DBC, SQL_NULL_HANDLE);
+
+/* STMT */
+handle_deal_with_HSTMT(SQL_HANDLE_STMT, SQL_NULL_HANDLE);
+
+/* DESC */
+handle_deal_with_HDESC(SQL_HANDLE_DESC, SQL_NULL_HANDLE);
+
+return 0;
+
+}
+
+
+void handle_deal_with_HDBC(SQLSMALLINT HandleType, SQLHDBC InputHandle)
+{
+ SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+ retcode = SQLFreeHandle(HandleType, InputHandle);
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+ DisplayError_HDBC(Sqlstate, HandleType, InputHandle);
+
+ i ++;
+ }
+ }
+ }
+
+
+void handle_deal_with_HSTMT(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+ retcode = SQLFreeHandle(HandleType, InputHandle);
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+ DisplayError_HSTMT(Sqlstate, HandleType, InputHandle);
+
+ i ++;
+ }
+ }
+ }
+
+void handle_deal_with_HENV(SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+ retcode = SQLFreeHandle(HandleType, InputHandle);
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ DisplayError_HENV(Sqlstate, HandleType, InputHandle);
+
+ i ++;
+ }
+ }
+ }
+
+void handle_deal_with_HDESC(SQLSMALLINT HandleType, SQLHDESC InputHandle)
+{
+ SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+ retcode = SQLFreeHandle(HandleType, InputHandle);
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ DisplayError_HDESC(Sqlstate, HandleType, InputHandle);
+
+ i ++;
+ }
+ }
+ }
+
+
+void DisplayError_HENV(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
+
+void DisplayError_HDBC(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDBC InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
+void DisplayError_HSTMT(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
+void DisplayError_HDESC(SQLCHAR Sqlstate[6], SQLSMALLINT HandleType, SQLHDESC InputHandle)
+{
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+}
+
+
diff --git a/storage/ndb/test/odbc/client/SQLGetConnectAttrTest.cpp b/storage/ndb/test/odbc/client/SQLGetConnectAttrTest.cpp
new file mode 100644
index 00000000000..8d5a5c0dbbb
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetConnectAttrTest.cpp
@@ -0,0 +1,131 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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.h>
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLPOINTER ValuePtr;
+SQLINTEGER GetConnectAttr_StringLengthPtr;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+void GetConnectAttr_DisplayError(SQLSMALLINT HandleType, SQLHENV InputHandle);
+
+int SQLGetConnectAttrTest()
+{
+ /* SQL/CLI attributes */
+ // char PtrValue1[2] = {'SQL_TRUE', 'SQL_FALSE'};
+ // for (i=0; i < 2; i++) {
+ retcode = SQLGetConnectAttr(hdbc, SQL_ATTR_AUTO_IPD, ValuePtr, 36, &GetConnectAttr_StringLengthPtr);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetConnectAttr_DisplayError(SQL_HANDLE_DBC, hdbc); // }
+
+ /* ODBC attributes */
+ /*
+ char PtrValue1[3] = {'SQL_MODE_READ_ONLY', 'SQL_MODE_READ_WRITE'};
+ for (i=0; i < 3; i++) {
+ retcode = SQLGetConnectAttr(hdbc, SQL_ATTR_ACCESS_MODE, (void*)PtrValue1[i], sizeof(PtrValue1[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetConnectAttr_DisplayError(SQL_HANDLE_DBC, hdbc);}
+
+
+ char PtrValue2[2] = {'SQL_ASYNC_ENABLE_OFF', 'SQL_ASYNC_ENABLE_ON'};
+ for (i=0; i < 2; i++) {
+ retcode = SQLSetConnectAttr(hdbc, SQL_ATTR_ASYNC_ENABLE, (void*)PtrValue2[i], sizeof(PtrValue2[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetConnectAttr_DisplayError(SQL_HANDLE_DBC, hdbc);}
+
+
+ char PtrValue4[2] = {'SQL_AUTOCOMMIT_OFF', 'SQL_AUTOCOMMIT_ON'};
+ for (i=0; i < 2; i++) {
+ retcode = SQLGetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (void*)PtrValue4[i], sizeof(PtrValue4[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetConnectAttr_DisplayError(SQL_HANDLE_DBC, hdbc);}
+
+ char PtrValue5[2] = {'SQL_CD_TRUE', 'SQL_CD_FALSE'};
+ for (i=0; i < 2; i++) {
+ retcode = SQLGetConnectAttr(hdbc, SQL_ATTR_CONNECTION_DEAD, (void*)PtrValue4[i], sizeof(PtrValue5[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetConnectAttr_DisplayError(SQL_HANDLE_DBC, hdbc);}
+
+
+ char PtrValue5[2] = {'SQL_CD_TRUE', 'SQL_CD_FALSE'};
+ for (i=0; i < 2; i++) {
+ retcode = SQLGetConnectAttr(hdbc, SQL_ATTR_CONNECTION_TIMEOUT, (void*)PtrValue4[i], sizeof(PtrValue5[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetConnectAttr_DisplayError(SQL_HANDLE_DBC, hdbc);}
+
+ */
+
+ return 0;
+
+ }
+
+
+void GetConnectAttr_DisplayError(SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLGetCursorNameTest.cpp b/storage/ndb/test/odbc/client/SQLGetCursorNameTest.cpp
new file mode 100644
index 00000000000..1e3ed9f557e
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetCursorNameTest.cpp
@@ -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 */
+
+ /**
+ * @file SQLGetCursorNameTest.cpp
+ */
+
+#include <common.hpp>
+using namespace std;
+
+#define GCN_MESSAGE_LENGTH 50
+
+SQLHSTMT GCN_hstmt;
+SQLHDESC GCN_hdesc;
+SQLHENV GCN_henv;
+SQLHDBC GCN_hdbc;
+
+void GCN_DisplayError(SQLSMALLINT GCN_HandleType,
+ SQLHDESC GCN_InputHandle);
+
+/**
+ * Test to assign a user-defined name to a cursor that is
+ * associated with an active SQL statement handle
+ *
+ * Tests:
+ * -# if there is no user-defined cursor name, then try to
+ * get user-definedcursor name
+ * -# get cursor name in normal case
+ *
+ * @return Zero, if test succeeded
+ */
+
+int SQLGetCursorNameTest()
+{
+ SQLRETURN retcode;
+ SQLCHAR SQLStmt [120];
+ SQLCHAR CursorName [80];
+ SQLSMALLINT CNameSize;
+
+ ndbout << endl << "Start SQLGetCursorName Testing" << endl;
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &GCN_henv);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ retcode = SQLSetEnvAttr(GCN_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_DBC,
+ GCN_henv,
+ &GCN_hdbc);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ retcode = SQLConnect(GCN_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_STMT,
+ GCN_hdbc,
+ &GCN_hstmt);
+
+ if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //************************
+ //** Define a statement **
+ //************************
+
+ strcpy((char *) SQLStmt,
+ "SELECT * FROM Customers WHERE Address = 'LM Vag 8'");
+
+ //*************************
+ //** Prepare a statement **
+ //*************************
+
+ retcode = SQLPrepare(GCN_hstmt,
+ SQLStmt,
+ SQL_NTS);
+
+ //*************************************************************************
+ //** if there is no user-defined cursor name, try to get the cursor name **
+ //*************************************************************************
+ retcode = SQLGetCursorName(GCN_hstmt,
+ CursorName,
+ sizeof(CursorName),
+ &CNameSize);
+
+ if (retcode != SQL_SUCCESS)
+ {
+ ndbout << endl << "retcode =" << retcode << endl;
+ GCN_DisplayError(SQL_HANDLE_STMT, GCN_hstmt);
+ }
+ else
+ ndbout << endl << "The cursor name is : " << (char *) CursorName << endl;
+
+ //*************************
+ //** Set the cursor name **
+ //*************************
+ retcode = SQLSetCursorName(GCN_hstmt,
+ (char *)"Customer_CURSOR",
+ SQL_NTS);
+
+ //***************************
+ //** Execute the statement **
+ //***************************
+ retcode = SQLExecute(GCN_hstmt);
+
+ //**********************************************
+ //** retrieve and display the new cursor name **
+ //**********************************************
+ retcode = SQLGetCursorName(GCN_hstmt,
+ CursorName,
+ sizeof(CursorName),
+ &CNameSize);
+
+ if (retcode != SQL_SUCCESS)
+ {
+ ndbout << endl << "retcode =" << retcode << endl;
+ GCN_DisplayError(SQL_HANDLE_STMT, GCN_hstmt);
+ }
+ else
+ ndbout << endl << "The cursor name is : " << (char *) CursorName << endl;
+
+ //****************
+ // Free Handles **
+ //****************
+ SQLDisconnect(GCN_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, GCN_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, GCN_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, GCN_henv);
+
+ return NDBT_OK;
+
+ }
+
+
+void GCN_DisplayError(SQLSMALLINT GCN_HandleType, SQLHDESC GCN_InputHandle)
+{
+
+ SQLINTEGER NativeError;
+ SQLCHAR Sqlstate[5], Msg[GCN_MESSAGE_LENGTH];
+ SQLRETURN SQLSTATEs;
+ SQLSMALLINT i, MsgLen;
+ i = 1;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((SQLSTATEs = SQLGetDiagRec(GCN_HandleType,
+ GCN_InputHandle, i,
+ Sqlstate,
+ &NativeError,
+ Msg,
+ sizeof(Msg),
+ &MsgLen))
+ != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << GCN_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)GCN_InputHandle << endl;
+ ndbout << "the Msg is: " << (char *) Msg << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLGetDataTest.cpp b/storage/ndb/test/odbc/client/SQLGetDataTest.cpp
new file mode 100644
index 00000000000..9d958c6c953
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetDataTest.cpp
@@ -0,0 +1,358 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+ /**
+ * @file SQLGetDataTest.cpp
+ */
+
+#include <common.hpp>
+using namespace std;
+
+#define GD_MESSAGE_LENGTH 200
+
+SQLHSTMT GD_hstmt;
+SQLHENV GD_henv;
+SQLHDBC GD_hdbc;
+SQLHDESC GD_hdesc;
+
+void GetData_DisplayError(SQLSMALLINT GD_HandleType, SQLHSTMT GD_InputHandle);
+
+/**
+ * Test to retrieve data for a single unbound column
+ * in the current row of a result data set
+ *
+ * Tests:
+ * -# Test1 There is no fetched rowset associated with S
+ * -# Test2 column number is less than zero
+ * -# Test3 fetched rowset is empty
+ * @return Zero, if test succeeded
+ */
+
+int SQLGetDataTest()
+{
+ SQLRETURN retcode;
+ SQLCHAR ColumnName;
+ SQLINTEGER CustID;
+ // SQLCHAR Name, Address, Phone;
+ SQLCHAR SQLStmt [120];
+ SQLCHAR SQLStmt1 [120];
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &GD_henv);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ retcode = SQLSetEnvAttr(GD_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.X!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_DBC,
+ GD_henv,
+ &GD_hdbc);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ retcode = SQLConnect(GD_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_STMT,
+ GD_hdbc,
+ &GD_hstmt);
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //*****************************
+ //** Define SELECT statement **
+ //*****************************
+
+ strcpy((char *) SQLStmt, "SELECT * FROM Customers");
+
+
+ //***********************************
+ //** Prepare SELECT SQL statement **
+ //***********************************
+
+ retcode = SQLPrepare(GD_hstmt,
+ SQLStmt,
+ SQL_NTS);
+ ndbout << endl << "Preparing SELECT, retcode = SQLprepare()= "
+ << retcode << endl;
+
+ //*********************************
+ //** Execute prepared statement **
+ //*********************************
+
+ // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ //{
+
+ retcode = SQLExecute(GD_hstmt);
+
+ ndbout << "Exexuting SELECT, retcode = SQLExecute()= "
+ << retcode << endl;
+
+ // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ // {
+
+ //*****************************************************************
+ //** Test1 **
+ //** There is no fetched rowset associated with S(SQL-statement) **
+ //*****************************************************************
+
+ retcode = SQLGetData(GD_hstmt,
+ 1,
+ SQL_C_SLONG,
+ &CustID,
+ sizeof(CustID),
+ NULL);
+ ndbout << "retcode = SQLGetData()= " << retcode << endl;
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << endl << "Test 1:" << endl;
+ ndbout << "There is no fetched rowset associated with SQL"
+ << " statement. But system reported SUCCESS or"
+ << " SUCCESS_WITH_INFO. Please check the function!" << endl;
+ GetData_DisplayError(SQL_HANDLE_STMT, GD_hstmt);
+ }
+ else if (retcode == SQL_ERROR)
+ {
+ ndbout << endl << "Test 1:" << endl;
+ ndbout << "There is no fetched rowset associated with SQL"
+ << " statement. The system reported ERROR "
+ << " The function is OK!" << endl;
+ }
+ else
+ ndbout << endl;
+
+ //*******************************
+ //** Fetch Data from database **
+ //*******************************
+
+ retcode = SQLFetch(GD_hstmt);
+
+ ndbout << endl
+ << "Fetching after Executing SELECT, retcode = SQLFetch()= "
+ << retcode << endl;
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+
+ //**************************************
+ //** Test2 **
+ //** column number is less than zero **
+ //**************************************
+
+ retcode = SQLGetData(GD_hstmt,
+ 0,
+ SQL_C_ULONG,
+ &CustID,
+ sizeof(CustID),
+ NULL);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Test 2:" <<"Column number is less than zero"
+ << " The system reported SUCCESS or SUCCESS_WITH_INFO."
+ << " Check the function, please!" <<endl;
+ GetData_DisplayError(SQL_HANDLE_STMT, GD_hstmt);
+ }
+ else if (retcode == SQL_ERROR)
+ {
+ ndbout << "Test 2:" << "Column number is less than zero."
+ << " The system reported SQL_ERROR."
+ << " The function is OK!" << endl;
+ }
+ else
+ ndbout << endl;
+ }
+ // }
+
+ // }
+
+ //*****************************
+ //** Define DELETE statement **
+ //*****************************
+
+ // strcpy((char *) SQLStmt1, "DELETE FROM Customers");
+ strcpy((char *) SQLStmt1, "DELETE FROM Customers WHERE CustID = 568 AND Name = 'Hans Peter'");
+
+ //***********************************
+ //** Prepare DELETE SQL statement **
+ //***********************************
+
+ retcode = SQLPrepare(GD_hstmt,
+ SQLStmt1,
+ SQL_NTS);
+ ndbout << endl << "Preparing DELETE, retcode = SQLPrepare()= "
+ << retcode << endl;
+
+ //****************************************
+ //** Execute prepared DELETE statement **
+ //****************************************
+
+ // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ // {
+
+ retcode = SQLExecute(GD_hstmt);
+
+ ndbout << "Executing DELETE, retcode = SQLExecute()= "
+ << retcode << endl;
+
+ // if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ // {
+
+ retcode = SQLFetch(GD_hstmt);
+
+ ndbout << "Fetching after Executing DELETE, retcode = SQLExecute()= "
+ << retcode << endl;
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+
+ //******************************************************
+ //** Test3 **
+ //** If the fetched rowset associated with **
+ //** Statement is empty, condition is raised: NO DATA **
+ //** We can delete all rows in table Customers for **
+ //** this case **
+ //******************************************************
+
+ retcode = SQLGetData(GD_hstmt,
+ 1,
+ SQL_C_ULONG,
+ &CustID,
+ sizeof(CustID),
+ NULL);
+
+ if (retcode == SQL_ERROR)
+ {
+ ndbout << "Test 3:" << endl;
+ ndbout << "The fetched rowset associated"
+ << "with Statementhandle is empty. The system"
+ << " reported SQL_ERROR. Check the function!" << endl;
+ GetData_DisplayError(SQL_HANDLE_STMT, GD_hstmt);
+ }
+ else if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Test 3:" << endl;
+ ndbout << "The fetched rowset associated"
+ << "with Statementhandle is empty. The system"
+ << " reported SUCCESS. Check the function!" << endl;
+ GetData_DisplayError(SQL_HANDLE_STMT, GD_hstmt);
+ }
+ else if (retcode == 100)
+ {
+ ndbout << "Test 3:" << endl;
+ ndbout << "The fetched rowset associated"
+ << "with Statementhandle is empty. The system"
+ << " reported SQL_NO_DATA. The function is OK!" << endl;
+ }
+ }
+ else if (retcode == SQL_ERROR)
+ {
+ ndbout << "Test 3 falied!" << endl;
+ GetData_DisplayError(SQL_HANDLE_STMT, GD_hstmt);
+ }
+ else
+ ndbout << " " << endl;
+
+ // }
+ // }
+
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(GD_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, GD_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, GD_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, GD_henv);
+
+ return NDBT_OK;
+}
+
+void GetData_DisplayError(SQLSMALLINT GD_HandleType, SQLHSTMT GD_InputHandle)
+{
+
+ SQLSMALLINT i, MsgLen;
+ SQLRETURN SQLSTATEs;
+ SQLCHAR Sqlstate[5], Msg[GD_MESSAGE_LENGTH];
+ SQLINTEGER NativeError;
+ i = 1;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((SQLSTATEs = SQLGetDiagRec(GD_HandleType,
+ GD_InputHandle,
+ i,
+ Sqlstate,
+ &NativeError,
+ Msg,
+ sizeof(Msg),
+ &MsgLen))
+ != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << GD_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)GD_InputHandle << endl;
+ ndbout << "Phone = " << (char *)Msg << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLGetDescFieldTest.cpp b/storage/ndb/test/odbc/client/SQLGetDescFieldTest.cpp
new file mode 100644
index 00000000000..b789ed75378
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetDescFieldTest.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 <NdbOut.hpp>
+#include <sqlext.h>
+#include <stdio.h>
+
+using namespace std;
+
+#define NAME_LEN 50
+#define PHONE_LEN 10
+#define SALES_PERSON_LEN 10
+#define STATUS_LEN 6
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+SQLHSTMT hstmt;
+SQLHDESC hdesc;
+
+SQLSMALLINT RecNumber;
+SQLCHAR szSalesPerson[SALES_PERSON_LEN];
+
+SQLCHAR Sqlstate[5], Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+SQLINTEGER NativeError;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLINTEGER ValuePtr1;
+SQLCHAR ValuePtr2;
+SQLSMALLINT ValuePtr3;
+
+
+
+SQLINTEGER StringLengthPtr;
+
+SQLSMALLINT i, MsgLen;
+
+void SQLGetDescFieldTest_DisplayError(SQLSMALLINT HandleType, SQLHDESC InputHandle);
+
+int SQLGetDescFieldTest()
+{
+
+ /* If MBR is 'PS' and there is no prepared or execute statement associated with S*/
+retcode = SQLGetDescField(hdesc, 1, SQL_DESC_ARRAY_SIZE, &ValuePtr1, 128, &StringLengthPtr);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLGetDescFieldTest_DisplayError(SQL_HANDLE_DESC, hdesc);
+
+ /* hstmt */
+ // SQLPrepare a statement to select rows from the ORDERS Table. We can create the
+ // table and inside rows in NDB by another program TestDirectSQL. In this test
+ // program(SQLGetDescRecTest),we only have three rows in table ORDERS
+
+/* Prepare the SQL statement with parameter markers. */
+retcode = SQLPrepare(hstmt, (SQLCHAR *)"SELECT ORDERID, CUSTID, OPENDATE, SALESPERSON, STATUS FROM ORDERS)", SQL_NTS);
+
+/* SELECT OrderID, CustID, OpenDate, SalesPerson from Table ORDERS */
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+
+
+ /* If FI(FieldIdentifer) is not one of the code values in Table 20 */
+retcode = SQLGetDescField(hdesc, 1, 9999, &ValuePtr1, 128, &StringLengthPtr);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLGetDescFieldTest_DisplayError(SQL_HANDLE_DESC, hdesc);
+
+
+ /* RecoderNumber is less than 1 */
+retcode = SQLGetDescField(hdesc, -1, SQL_DESC_ARRAY_SIZE, &ValuePtr1, 128, &StringLengthPtr);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLGetDescFieldTest_DisplayError(SQL_HANDLE_DESC, hdesc);
+
+ /* RecoderNumber is greater than N, N be the value of the COUNT field of D, D be the allocated CLI */
+ /* descriptor area identified by DescriptorHandle */
+retcode = SQLGetDescField(hdesc, 4, SQL_DESC_ARRAY_SIZE, &ValuePtr1, 128, &StringLengthPtr);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLGetDescFieldTest_DisplayError(SQL_HANDLE_DESC, hdesc);
+
+}
+
+ return 0;
+
+ }
+
+
+void SQLGetDescFieldTest_DisplayError(SQLSMALLINT HandleType, SQLHDESC InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLGetDescRecTest.cpp b/storage/ndb/test/odbc/client/SQLGetDescRecTest.cpp
new file mode 100644
index 00000000000..5944f393a71
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetDescRecTest.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 <common.h>
+
+using namespace std;
+
+#define NAME_LEN 50
+#define PHONE_LEN 10
+#define SALES_PERSON_LEN 10
+#define STATUS_LEN 6
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+SQLHSTMT hstmt;
+SQLHDESC hdesc;
+
+SQLSMALLINT RecNumber;
+SQLCHAR szSalesPerson[SALES_PERSON_LEN];
+
+SQLCHAR Sqlstate[5], Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+SQLINTEGER NativeError;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLCHAR Name;
+SQLINTEGER LengthPtr;
+SQLSMALLINT SGDR_StringLengthPtr, TypePtr, SubTypePtr, PrecisionPtr, ScalePtr, NullablePtr;
+
+SQLSMALLINT i, MsgLen;
+
+void SQLGetDescRec_DisplayError(SQLSMALLINT HandleType, SQLHDESC InputHandle);
+
+int SQLGetDescRecTest()
+{
+
+ /* hstmt */
+ // SQLPrepare a statement to select rows from the ORDERS Table. We can create the table and inside rows in
+ // NDB by another program TestDirectSQL. In this test program(SQLGetDescRecTest),we only have three rows in
+ // table ORDERS
+
+/* Prepare the SQL statement with parameter markers. */
+retcode = SQLPrepare(hstmt, (SQLCHAR *)"SELECT ORDERID, CUSTID, OPENDATE, SALESPERSON, STATUS FROM ORDERS)", SQL_NTS);
+
+/* SELECT OrderID, CustID, OpenDate, SalesPerson from Table ORDERS */
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ /* RecoderNumber is less than 1 */
+retcode = SQLGetDescRec(hdesc, -1, &Name, sizeof(Name), &SGDR_StringLengthPtr, &TypePtr, &SubTypePtr, &LengthPtr, &PrecisionPtr, &ScalePtr, &NullablePtr);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLGetDescRec_DisplayError(SQL_HANDLE_DESC, hdesc);
+
+ /* RecoderNumber is greater than N, N be the value of the COUNT field of D, D be the allocated CLI */
+ /* descriptor area identified by DescriptorHandle */
+retcode = SQLGetDescRec(hdesc, 4, &Name, sizeof(Name), &SGDR_StringLengthPtr, &TypePtr, &SubTypePtr, &LengthPtr, &PrecisionPtr, &ScalePtr, &NullablePtr);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLGetDescRec_DisplayError(SQL_HANDLE_DESC, hdesc);
+
+}
+
+ return 0;
+
+ }
+
+
+void SQLGetDescRec_DisplayError(SQLSMALLINT HandleType, SQLHDESC InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLGetDiagFieldTest.cpp b/storage/ndb/test/odbc/client/SQLGetDiagFieldTest.cpp
new file mode 100644
index 00000000000..ef9bc3eb3fc
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetDiagFieldTest.cpp
@@ -0,0 +1,236 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <sqlcli.h>
+#include <stdio.h>
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLINTEGER strangehandle;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+SQLSMALLINT StringLengthPtr;
+SQLINTEGER DiagInfoPtr1;
+SQLCHAR DiagInfoPtr2;
+SQLRETURN DiagInfoPtr3;
+
+void DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+
+int SQLGetDiagFieldTest ()
+{
+
+ strangehandle = 67;
+ /* hstmt */
+ // Execute a statement to retrieve rows from the Customers table. We can create the table and inside rows in
+ // NDB by another program TestDirectSQL
+ // const SQLCHAR *StatementText = "SELECT CustID, Name, Address, Phone FROM Customers";
+ // retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", 56);
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+ i = 1;
+while ((SQLSTATEs = SQLGetDiagField(67, 67, i, SQL_DIAG_CURSOR_ROW_COUNT, &DiagInfoPtr1, 128, &StringLengthPtr)) != SQL_NO_DATA) {
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+
+ /* HandleType indicates ENVIRNMENT HANDLE and Handle does not identify an allocated SQL_environment */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+ i = 1;
+while ((SQLSTATEs = SQLGetDiagField(SQL_HANDLE_ENV, hdbc, i, SQL_DIAG_CURSOR_ROW_COUNT, &DiagInfoPtr1, 128, &StringLengthPtr)) != SQL_NO_DATA) {
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+
+ /* HandleType indicates CONNECTION HANDLE and Handle does not identify an allocated SQL_connection */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+ i = 1;
+while ((SQLSTATEs = SQLGetDiagField(SQL_HANDLE_DBC, henv, i, SQL_DIAG_CURSOR_ROW_COUNT, &DiagInfoPtr1, 128, &StringLengthPtr)) != SQL_NO_DATA) {
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+ /* HandleType indicates STATEMENT HANDLE and Handle does not identify an allocated SQL_statement */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+ i = 1;
+while ((SQLSTATEs = SQLGetDiagField(SQL_HANDLE_STMT, hdbc, i, SQL_DIAG_CURSOR_ROW_COUNT, &DiagInfoPtr1, 128, &StringLengthPtr)) != SQL_NO_DATA) {
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+ /* HandleType indicates DESCRIPTOR HANDLE and Handle does not identify an allocated SQL_descriptor */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+ i = 1;
+while ((SQLSTATEs = SQLGetDiagField(SQL_HANDLE_DESC, hdbc, i, SQL_DIAG_CURSOR_ROW_COUNT, &DiagInfoPtr1, 128, &StringLengthPtr)) != SQL_NO_DATA) {
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+ /* DiagIdentifer is not one of the code values in Table12 */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+
+where ((SQLSTATEs = SQLGetDiagField(SQL_HANDLE_DBC, hdbc, -1, 99, &DiagInfoPtr1, 128, &StringLengthPtr)) != SQL_NO_DATA) {
+
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+ /* If TYPE is 'STATUS' and RN is greater than N */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+
+where ((SQLSTATEs = SQLGetDiagField(SQL_HANDLE_DBC, hdbc, 9999, 8, &DiagInfoPtr2, 128, &StringLengthPtr)) != SQL_NO_DATA) {
+
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+
+ /* If TYPE is 'STATUS' and RN is less than 1 */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+
+ where ((SQLSTATEs = SQLGetDiagField(SQL_HANDLE_DBC, hdbc, -1, 8, &DiagInfoPtr2, 128, &StringLengthPtr)) != SQL_NO_DATA) {
+
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+ /* If DI indicates ROW_COUNT and R is neither Execute nor ExecDirect, then an exception condition is raised */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+
+where ((SQLSTATEs = SQLGetDiagField(SQL_HANDLE_DBC, hdbc, i, SQL_DIAG_ROW_COUNT, &DiagInfoPtr1, 128, &StringLengthPtr)) != SQL_NO_DATA) {
+
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+
+ return 0;
+
+ }
+
+
+
+
+
+void DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLGetDiagRecSimpleTest.cpp b/storage/ndb/test/odbc/client/SQLGetDiagRecSimpleTest.cpp
new file mode 100644
index 00000000000..8fa4a2b3dbb
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetDiagRecSimpleTest.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 */
+
+ /**
+ * @file SQLGetDiagRecSimpleTest.cpp
+ */
+#include <common.hpp>
+#include <string.h>
+
+using namespace std;
+
+SQLHDBC GDS_hdbc;
+SQLHSTMT GDS_hstmt;
+SQLHENV GDS_henv;
+SQLHDESC GDS_hdesc;
+SQLRETURN GDS_retcode, GDS_RETURN;
+
+#define GDS_SQL_MAXIMUM_MESSAGE_LENGTH 255
+SQLCHAR GDS_Sqlstate[5];
+
+SQLINTEGER GDS_NativeError;
+SQLSMALLINT GDS_i = 1, GDS_MsgLen;
+SQLCHAR GDS_Msg[GDS_SQL_MAXIMUM_MESSAGE_LENGTH], GDS_ConnectIn[30];
+
+/**
+ * Test SQLGetDiagRec return value
+ *
+ * -#Simply test Msg when return is SQL_NO_DATA
+ * -#Simply test Msg when return is SQL_SUCCESS
+ * -#Simply test Msg when return is SQL_SUCCESS_WITH_INFO
+ * -#Simply test Msg when return is SQL_INVALID_HANDLE
+ * -#Simply test Msg when return is SQL_ERROR
+ *
+ * @return Zero, if test succeeded
+ */
+
+int SQLGetDiagRecSimpleTest()
+{
+ ndbout << endl << "Start SQLGetDiagRec Simple Testing" << endl;
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+
+ GDS_retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &GDS_henv);
+ if (GDS_retcode == SQL_SUCCESS || GDS_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated An Environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+
+ GDS_retcode = SQLSetEnvAttr(GDS_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (GDS_retcode == SQL_SUCCESS || GDS_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ GDS_retcode = SQLAllocHandle(SQL_HANDLE_DBC, GDS_henv, &GDS_hdbc);
+
+ if (GDS_retcode == SQL_SUCCESS || GDS_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated A Connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+
+ GDS_retcode = SQLConnect(GDS_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (GDS_retcode == SQL_SUCCESS || GDS_retcode == SQL_SUCCESS_WITH_INFO){
+ ndbout << "Success connection to DB!" << endl;
+ ndbout << "GDS_retcode = " << GDS_retcode << endl;
+ ndbout << "SQL_SUCCESS = " << SQL_SUCCESS << endl;
+ ndbout << "SQL_SUCCESS_WITH_INFO = " << SQL_SUCCESS_WITH_INFO << endl;}
+
+ ndbout << endl;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ if (GDS_retcode != SQL_SUCCESS || GDS_retcode != SQL_SUCCESS_WITH_INFO){
+ ndbout << "GDS_retcode = " << GDS_retcode << endl;
+ ndbout << "SQL_SUCCESS = " << SQL_SUCCESS << endl;
+ ndbout << "SQL_SUCCESS_WITH_INFO = " << SQL_SUCCESS_WITH_INFO << endl;
+
+ GDS_RETURN = SQLGetDiagRec(SQL_HANDLE_DBC,
+ GDS_hdbc,
+ GDS_i,
+ GDS_Sqlstate,
+ &GDS_NativeError,
+ GDS_Msg,
+ sizeof(GDS_Msg),
+ &GDS_MsgLen);
+
+ if (GDS_RETURN == SQL_NO_DATA){
+ ndbout << "GDS_SQLSTATES = SQL_NO_DATA" << endl;
+ ndbout << "the HandleType is:" << SQL_HANDLE_DBC << endl;
+ ndbout << "the Handle is :" << (long)GDS_hdbc << endl;
+ ndbout << "the GDS_Msg is :" << (char *)GDS_Msg << endl;
+ ndbout << "the sqlstate is:" << (char *)GDS_Sqlstate << endl;}
+
+ else if (GDS_RETURN == SQL_SUCCESS){
+ ndbout << "GDS_SQLSTATES = SQL_SUCCESS" << endl;
+ ndbout << "the HandleType is:" << SQL_HANDLE_DBC << endl;
+ ndbout << "the Handle is :" << (long)GDS_hdbc << endl;
+ ndbout << "the GDS_Msg is :" << (char *)GDS_Msg << endl;
+ ndbout << "the sqlstate is:" << (char *)GDS_Sqlstate << endl;}
+
+ else if (GDS_RETURN == SQL_SUCCESS_WITH_INFO){
+ ndbout << "GDS_SQLSTATES = SQL_SUCCESS_WITH_INFO" << endl;
+ ndbout << "the HandleType is:" << SQL_HANDLE_DBC << endl;
+ ndbout << "the Handle is :" << (long)GDS_hdbc << endl;
+ ndbout << "the GDS_Msg is :" << (char *)GDS_Msg << endl;
+ ndbout << "the sqlstate is:" << (char *)GDS_Sqlstate << endl;}
+
+ else if (GDS_RETURN == SQL_INVALID_HANDLE){
+ ndbout << "GDS_SQLSTATES = SQL_INVALID_HANDLE" << endl;
+ ndbout << "the HandleType is:" << SQL_HANDLE_DBC << endl;
+ ndbout << "the Handle is :" << (long)GDS_hdbc << endl;
+ ndbout << "the GDS_Msg is :" << (char *)GDS_Msg << endl;
+ ndbout << "the sqlstate is:" << (char *)GDS_Sqlstate << endl;}
+
+ else{
+ ndbout << "GDS_RETURN = SQL_ERROR" << endl;
+ ndbout << "the HandleType is:" << SQL_HANDLE_DBC << endl;
+ ndbout << "the Handle is :" << (long)GDS_hdbc << endl;
+ ndbout << "the GDS_Msg is :" << (char *)GDS_Msg << endl;
+ ndbout << "the sqlstate is:" << (char *)GDS_Sqlstate << endl;
+ }
+ }
+ ndbout << "-------------------------------------------------" << endl;
+
+ //******************
+ //** Free Handles **
+ //******************
+ SQLDisconnect(GDS_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, GDS_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, GDS_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, GDS_henv);
+ return NDBT_OK;
+ }
+
diff --git a/storage/ndb/test/odbc/client/SQLGetDiagRecTest.cpp b/storage/ndb/test/odbc/client/SQLGetDiagRecTest.cpp
new file mode 100644
index 00000000000..27c78edaa4d
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetDiagRecTest.cpp
@@ -0,0 +1,207 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <sqlcli.h>
+#include <stdio.h>
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLINTEGER strangehandle;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+void DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+
+int SQLGetDiagRecTest()
+{
+
+ strangehandle = 67;
+ /* hstmt */
+ // Execute a statement to retrieve rows from the Customers table. We can create the table and inside rows in
+ // NDB by another program TestDirectSQL
+ // const SQLCHAR *StatementText = "SELECT CustID, Name, Address, Phone FROM Customers";
+
+ // retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", 56);
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(67, 67, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+
+ /* HandleType indicates ENVIRNMENT HANDLE and Handle does not identify an allocated SQL_environment */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(SQL_HANDLE_ENV, hdbc, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+
+ /* HandleType indicates CONNECTION HANDLE and Handle does not identify an allocated SQL_connection */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(SQL_HANDLE_DBC, henv, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+ /* HandleType indicates STATEMENT HANDLE and Handle does not identify an allocated SQL_statement */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(SQL_HANDLE_STMT, hdbc, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+ /* HandleType indicates DESCRIPTOR HANDLE and Handle does not identify an allocated SQL_descriptor */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(SQL_HANDLE_DESC, hdbc, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+
+ /* RecordNumber is less than one */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+
+ where ((SQLSTATEs = SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, -1,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+ /* RecordNumber is greater than N */
+
+ retcode = SQLPrepare(hstmt, (SQLCHAR*)"SELECT CustID, Name, Address, Phone FROM Customers", SQL_NTS);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS) {
+
+ where ((SQLSTATEs = SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, 9999,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ if (SQLSTATEs == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+ }
+ i ++;
+ }
+ }
+
+
+ return 0;
+
+ }
+
+
+void DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLGetEnvAttrTest.cpp b/storage/ndb/test/odbc/client/SQLGetEnvAttrTest.cpp
new file mode 100644
index 00000000000..efc8117d6d2
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetEnvAttrTest.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.h>
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLPOINTER ValuePtr;
+SQLINTEGER GetEnvAttr_StringLengthPtr;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+void GetEnvAttr_DisplayError(SQLSMALLINT HandleType, SQLHENV InputHandle);
+
+int SQLGetEnvAttrTest()
+{
+ /* ODBC attributres */
+
+ /*
+ // char PtrValue1[3] = {'SQL_CP_OFF', 'SQL_CP_ONE_DRIVER', 'SQL_CP_ONE_PER_HENV'};
+ // for (i=0; i < 3; i++) {
+ retcode = SQLGetEnvAttr(henv, SQL_ATTR_CONNECTION_POOLING, ValuePtr, 36, &GetEnvAttr_StringLengthPtr);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_ENV, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetEnvAttr_DisplayError(SQL_HANDLE_ENV, henv); // }
+
+
+ // char PtrValue2[2] = {'SQL_CP_STRICT_MATCH', 'SQL_CP_RELAXED_MATCH'};
+ // for (i=0; i < 2; i++) {
+ retcode = SQLGetEnvAttr(henv, SQL_ATTR_CP_MATCH, ValuePtr, 36, &GetEnvAttr_StringLengthPtr);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_ENV, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetEnvAttr_DisplayError(SQL_HANDLE_ENV, henv); // }
+
+
+ // char PtrValue3[2] = {'SQL_OV_ODBC3', 'SQL_OV_ODBC2'};
+ // for (i=0; i < 2; i++) {
+ retcode = SQLGetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, ValuePtr, 36, &GetEnvAttr_StringLengthPtr );
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_ENV, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetEnvAttr_DisplayError(SQL_HANDLE_ENV, henv); // }
+
+ */
+
+ // char PtrValue4[2] = {'SQL_TRUE', 'SQL_FALSE'};
+ // for (i=0; i < 2; i++) {
+ retcode = SQLGetEnvAttr(henv, SQL_ATTR_OUTPUT_NTS, ValuePtr, 36, &GetEnvAttr_StringLengthPtr);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_ENV, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetEnvAttr_DisplayError(SQL_HANDLE_ENV, henv); // }
+
+ return 0;
+
+ }
+
+
+void GetEnvAttr_DisplayError(SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLGetFunctionsTest.cpp b/storage/ndb/test/odbc/client/SQLGetFunctionsTest.cpp
new file mode 100644
index 00000000000..c6feb8ec033
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetFunctionsTest.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 */
+
+ /**
+ * @file SQLGetFunctionsTest.cpp
+ */
+
+
+#include <common.hpp>
+#define GF_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC GF_hdbc;
+SQLHSTMT GF_hstmt;
+SQLHENV GF_henv;
+
+void SQLGetFunctions_DisplayError(SQLSMALLINT GF_HandleType,
+ SQLHDBC GF_InputHandle);
+
+/**
+ * Test whether a specific ODBC API function is supported by
+ * the driver an application is currently connected to.
+ *
+ * In this test program, we can change ODBC function values in order to
+ * know different which fuction is supported by ODBC drivers
+ * Tests:
+ * -# Test1 There is no established SQL-connection
+ * -# Test2 ConnectionHandle does not identify an allocated SQL-connection
+ * -# Test3 The value of FunctionId is not in table 27
+ * -# Test4 Normal case test
+ * @return Zero, if test succeeded
+ */
+
+int SQLGetFunctionsTest()
+{
+ SQLUSMALLINT TableExists, Supported;
+ SQLCHAR SQLStmt [120];
+ SQLRETURN retcode;
+
+ ndbout << endl << "Start SQLGetFunctions Testing" << endl;
+
+ //**********************************************************
+ //** Test 1 **
+ //** If there is no established SQL-connection associated **
+ //** with allocated SQL-connection **
+ //**********************************************************
+
+ retcode = SQLGetFunctions(GF_hdbc, SQL_API_SQLTABLES, &TableExists);
+ if (retcode == -2)
+{
+ ndbout << endl << "Test 1" << endl;
+ ndbout << "retcode = " << retcode << endl;
+ ndbout << endl << "There is no established SQL-connection" << endl;
+ ndbout << "associated with allocated SQL_connection" << endl;
+ SQLGetFunctions_DisplayError(SQL_HANDLE_STMT, GF_hstmt);
+}
+ else if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+{
+ ndbout << endl << "Test 1" << endl;
+ ndbout << "retcode = " << retcode << endl;
+ ndbout << endl << "There is no established SQL-connection" << endl;
+ ndbout << "associated with allocated SQL_connection" << endl;
+ SQLGetFunctions_DisplayError(SQL_HANDLE_STMT, GF_hstmt);
+}
+ else
+{
+ ndbout << endl << "Test 1" << endl;
+ ndbout << "retcode = " << retcode << endl;
+ ndbout << endl << "There is no established SQL-connection" << endl;
+ ndbout << "associated with allocated SQL_connection" << endl;
+ SQLGetFunctions_DisplayError(SQL_HANDLE_STMT, GF_hstmt);
+}
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &GF_henv);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ retcode = SQLSetEnvAttr(GF_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.X!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_DBC,
+ GF_henv,
+ &GF_hdbc);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ retcode = SQLConnect(GF_hdbc,
+ (SQLCHAR*) connectString(),
+ SQL_NTS,
+ (SQLCHAR*) "",
+ SQL_NTS,
+ (SQLCHAR*) "",
+ SQL_NTS);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+{
+
+ //*************************************************************
+ //** Test 2 **
+ //** If ConnectionHandle does not identify an allocated **
+ //** SQL-connection, then an exception condition is raised **
+ //*************************************************************
+ retcode = SQLGetFunctions(GF_hdbc, SQL_API_SQLTABLES, &TableExists);
+ if (retcode == -2)
+{
+ ndbout << endl << "Test 2" << endl;
+ ndbout << "retcode = " << retcode << endl;
+ ndbout << "If ConnectionHandle does not identify an allocated" << endl;
+ ndbout << "SQL-connection,an exception condition is raised" << endl;
+ SQLGetFunctions_DisplayError(SQL_HANDLE_STMT, GF_hstmt);
+}
+ else if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+{
+ ndbout << endl << "Test 2" << endl;
+ ndbout << "retcode = " << retcode << endl;
+ ndbout << "If ConnectionHandle does not identify an allocated" << endl;
+ ndbout << "SQL-connection,an exception condition is raised" << endl;
+ SQLGetFunctions_DisplayError(SQL_HANDLE_STMT, GF_hstmt);
+}
+ else
+{
+ ndbout << endl << "Test 2 :" << endl;
+ ndbout << "retcode = " << retcode << endl;
+ ndbout << "If ConnectionHandle does not identify an allocated" << endl;
+ ndbout << "SQL-connection,an exception condition is raised" << endl;
+ SQLGetFunctions_DisplayError(SQL_HANDLE_STMT, GF_hstmt);
+}
+
+ //*************************************************************
+ //** Test 3 **
+ //** If the value of FunctionId is not in table 27, "Codes **
+ //** used to identify SQL/CLI routines" **
+ //*************************************************************
+
+ retcode = SQLGetFunctions(GF_hdbc, 88888, &TableExists);
+ ndbout<< "TableExists = " << TableExists << endl;
+ if (retcode == -2)
+{
+ ndbout << "retcode = " << retcode << endl;
+ ndbout << "Test 3 : The value of FunctionId is not in table 27" << endl;
+ SQLGetFunctions_DisplayError(SQL_HANDLE_STMT, GF_hstmt);
+}
+ else if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+{
+ ndbout << "retcode = " << retcode << endl;
+ ndbout << "Test 3 : The value of FunctionId is not in table 27" << endl;
+ SQLGetFunctions_DisplayError(SQL_HANDLE_STMT, GF_hstmt);
+}
+ else
+{
+ ndbout << "retcode = " << retcode << endl;
+ ndbout << "Test 3 : The value of FunctionId is not in table 27" << endl;
+ SQLGetFunctions_DisplayError(SQL_HANDLE_STMT, GF_hstmt);
+}
+
+ //******************
+ //** Test 4 **
+ //** Normal case **
+ //******************
+ ndbout << "Test 4:" << endl;
+ retcode = SQLGetFunctions(GF_hdbc, SQL_API_SQLBROWSECONNECT, &Supported);
+ ndbout << "retcode = " << retcode << endl;
+ if (Supported == TRUE)
+{
+ ndbout << "Supported = " << Supported << endl;
+ ndbout << "SQLBrowseConnect is supported by the current data source"
+ << endl;
+}
+ else
+{
+ ndbout << "Supported = " << Supported << endl;
+ ndbout << "SQLBrowseConnect isn't supported by the current data source"
+ << endl;
+}
+
+
+ //******************
+ //** Test 5 **
+ //** Normal case **
+ //******************
+ ndbout << endl << "Test 5:" << endl;
+ retcode = SQLGetFunctions(GF_hdbc, SQL_API_SQLFETCH, &Supported);
+ ndbout << "retcode = " << retcode << endl;
+ if (Supported == TRUE)
+{
+ ndbout << "Supported = " << Supported << endl;
+ ndbout << "SQLFETCH is supported by the current data source" << endl;
+}
+ else
+{
+ ndbout << "Supported = " << Supported << endl;
+ ndbout << "SQLFETCH isn't supported by the current data source" << endl;
+}
+
+}
+
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(GF_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, GF_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, GF_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, GF_henv);
+
+ return NDBT_OK;
+
+}
+
+
+void SQLGetFunctions_DisplayError(SQLSMALLINT GF_HandleType,
+ SQLHDBC GF_InputHandle)
+{
+ SQLRETURN SQLSTATEs;
+ SQLCHAR Sqlstate[50];
+ SQLINTEGER NativeError;
+ SQLSMALLINT i, MsgLen;
+ SQLCHAR Msg[GF_MESSAGE_LENGTH];
+ i = 1;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ Msg[0] = 0;
+ while ((SQLSTATEs = SQLGetDiagRec(GF_HandleType,
+ GF_InputHandle,
+ i,
+ Sqlstate,
+ &NativeError,
+ Msg,
+ sizeof(Msg),
+ &MsgLen))
+ != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << GF_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)GF_InputHandle << endl;
+ ndbout << "the Msg is :" << (char *) Msg << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ Msg[0] = 0;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLGetInfoTest.cpp b/storage/ndb/test/odbc/client/SQLGetInfoTest.cpp
new file mode 100644
index 00000000000..95f7562dafe
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetInfoTest.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 */
+
+ /**
+ * @file SQLGetInfoTest.cpp
+ */
+
+#include <common.hpp>
+
+using namespace std;
+
+SQLHDBC GI_hdbc;
+SQLHSTMT GI_hstmt;
+SQLHENV GI_henv;
+
+#define GI_MESSAGE_LENGTH 200
+
+SQLCHAR Msg[GI_MESSAGE_LENGTH];
+
+void SQLGetInfoTest_DisplayError(SQLSMALLINT GI_HandleType,
+ SQLHDBC GI_InputHandle);
+
+/**
+ * Test to retrieve general information about the driver and
+ * the data source an application is currently connected to.
+ *
+ * Tests:
+ * -# Test The value of FunctionId is not in table 27
+ * @return Zero, if test succeeded
+ */
+
+int SQLGetInfoTest()
+{
+ SQLRETURN retcode;
+ SQLINTEGER InfoValuePtr;
+ SQLSMALLINT SLPStringLengthPtr;
+
+ ndbout << endl << "Start SQLGetInfo Testing" << endl;
+
+ //******************************************************
+ //** The value of FunctionId is not in Table 27, then **
+ //** an exception condition is raised **
+ //******************************************************
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &GI_henv);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ retcode = SQLSetEnvAttr(GI_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_DBC,
+ GI_henv,
+ &GI_hdbc);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ retcode = SQLConnect(GI_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+
+ // **********************
+ // ** GET INFO FROM DB **
+ // *********************
+
+ retcode = SQLGetInfo(GI_hdbc,
+ SQL_DATABASE_NAME,
+ &InfoValuePtr,
+ sizeof(InfoValuePtr),
+ &SLPStringLengthPtr);
+
+ if (retcode == SQL_SUCCESS)
+ ndbout << endl << "Database Name:" << InfoValuePtr << endl;
+ else
+ {
+ ndbout << endl << "retcode = SQLGetInfo() = " << retcode <<endl;
+ SQLGetInfoTest_DisplayError(SQL_HANDLE_STMT, GI_hstmt);
+ }
+
+ retcode = SQLGetInfo(GI_hdbc,
+ SQL_DRIVER_NAME,
+ &InfoValuePtr,
+ sizeof(InfoValuePtr),
+ &SLPStringLengthPtr);
+
+ if (retcode == SQL_SUCCESS)
+ ndbout << endl << "Driver Name:" << InfoValuePtr << endl;
+ else
+ {
+ ndbout << endl << "retcode = SQLGetInfo() = " << retcode <<endl;
+ SQLGetInfoTest_DisplayError(SQL_HANDLE_STMT, GI_hstmt);
+ }
+
+ // **************************
+ // ** INPUT WRONG InfoType **
+ // **************************
+ retcode = SQLGetInfo(GI_hdbc,
+ 8888,
+ &InfoValuePtr,
+ sizeof(InfoValuePtr),
+ &SLPStringLengthPtr);
+ if (retcode == -2)
+ {
+ ndbout << endl <<"retcode = " << retcode << endl;
+ ndbout << "System reported -2. Please check your test programme"
+ << " about the connectionhandle." << endl;
+ SQLGetInfoTest_DisplayError(SQL_HANDLE_STMT, GI_hstmt);
+ }
+ else if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << endl << "retcode = " << retcode << endl;
+ ndbout << "The information of InfoType is not in Table 28,"
+ << " but SQLGetInfo() executeed succeddfully."
+ << " Check the function!" <<endl;
+ SQLGetInfoTest_DisplayError(SQL_HANDLE_STMT, GI_hstmt);
+ }
+ else if (retcode == SQL_ERROR)
+ {
+ ndbout << endl << "retcode = " << retcode << endl;
+ ndbout << "Input a wrong InfoType. The system found the"
+ << " information of InfoType is not in Table 28."
+ << " Test successful!" << endl;
+ }
+ else
+ ndbout << endl;
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(GI_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, GI_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, GI_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, GI_henv);
+
+ return NDBT_OK;
+
+ }
+
+
+void SQLGetInfoTest_DisplayError(SQLSMALLINT GI_HandleType,
+ SQLHDBC GI_InputHandle)
+{
+ SQLRETURN SQLSTATEs;
+ SQLINTEGER NativeError;
+ SQLCHAR Sqlstate[50];
+ SQLSMALLINT i, MsgLen;
+ i = 1;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+
+ while ((SQLSTATEs = SQLGetDiagRec(GI_HandleType,
+ GI_InputHandle,
+ i,
+ Sqlstate,
+ &NativeError,
+ Msg,
+ sizeof(Msg),
+ &MsgLen))
+ != SQL_NO_DATA)
+{
+
+ ndbout << "the GI_HandleType is:" << GI_HandleType << endl;
+ ndbout << "the GI_InputHandle is :" << (long)GI_InputHandle << endl;
+ ndbout << "the Msg is :" << (char *) Msg << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLGetStmtAttrTest.cpp b/storage/ndb/test/odbc/client/SQLGetStmtAttrTest.cpp
new file mode 100644
index 00000000000..2052af60ee0
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetStmtAttrTest.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 <common.h>
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLPOINTER ValuePtr;
+SQLINTEGER GetStmtAttr_StringLengthPtr;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+void GetStmtAttr_DisplayError(SQLSMALLINT HandleType, SQLHENV InputHandle);
+
+int SQLGetStmtAttrTest()
+{
+ /* SQL/CLI attributes */
+ /* SQL_ATTR_APP_PARAM_DESC */
+ // char PtrValue1[1] = {'SQL_NULL_DESC'};
+ // for (i=0; i < 1; i++) {
+ retcode = SQLGetStmtAttr(hstmt, SQL_ATTR_APP_PARAM_DESC, ValuePtr, 36, &GetStmtAttr_StringLengthPtr);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetStmtAttr_DisplayError(SQL_HANDLE_STMT, hstmt);//}
+
+ /* SQL_ATTR_APP_ROW_DESC */
+ // char PtrValue2[1] = {'SQL_NULL_DESC'}; /* ? */
+ // for (i=0; i < 2; i++) {
+ retcode = SQLGetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, ValuePtr, 36, &GetStmtAttr_StringLengthPtr);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetStmtAttr_DisplayError(SQL_HANDLE_STMT, hstmt);//}
+
+ /* SQL_ATTR_CURSOR_SCROLLABLE */
+ // char PtrValue3[2] = {'SQL_NONSCROLLABLE', 'SQL_SCROLLABLE'}; /* ? */
+ // for (i=0; i < 2; i++) {
+ retcode = SQLGetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE, ValuePtr, 36, &GetStmtAttr_StringLengthPtr);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetStmtAttr_DisplayError(SQL_HANDLE_STMT, hstmt);//}
+
+ /* SQL_ATTR_CURSOR_SENSITIVITY */
+ // char PtrValue4[3] = {'SQL_UNSPECIFIED', 'SQL_INSENSITIVE', 'SQL_SENSITIVE'}; /* ? */
+ // for (i=0; i < 3; i++) {
+ retcode = SQLGetStmtAttr(hstmt, SQL_ATTR_CURSOR_SENSITIVITY, ValuePtr, 36, &GetStmtAttr_StringLengthPtr);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetStmtAttr_DisplayError(SQL_HANDLE_STMT, hstmt);//}
+
+ /* SQL_ATTR_METADATA_ID */
+ // char PtrValue5[2] = {'SQL_TRUE', 'SQL_FALSE'}; /* ? */
+ // for (i=0; i < 2; i++) {
+ retcode = SQLGetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, ValuePtr, 36, &GetStmtAttr_StringLengthPtr);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetStmtAttr_DisplayError(SQL_HANDLE_STMT, hstmt);//}
+
+ /* SQL_ATTR_IMP_ROW_DESC */
+ // char PtrValue6[2] = {'TRUE', 'FALSE'}; /* ? */
+ // for (i=0; i < 2; i++) {
+ retcode = SQLGetStmtAttr(hstmt, SQL_ATTR_IMP_ROW_DESC, ValuePtr, 36, &GetStmtAttr_StringLengthPtr);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetStmtAttr_DisplayError(SQL_HANDLE_STMT, hstmt);//}
+
+
+ /* SQL_ATTR_IMP_PARAM_DESC */
+ // char PtrValue6[2] = {'TRUE', 'FALSE'}; /* ? */
+ // for (i=0; i < 2; i++) {
+ retcode = SQLGetStmtAttr(hstmt, SQL_ATTR_IMP_PARAM_DESC, ValuePtr, 36, &GetStmtAttr_StringLengthPtr);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetStmtAttr_DisplayError(SQL_HANDLE_STMT, hstmt);//}
+
+
+ /* SQL_ATTR_METADATA_ID */
+ // char PtrValue6[2] = {'TRUE', 'FALSE'}; /* ? */
+ // for (i=0; i < 2; i++) {
+ retcode = SQLGetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, ValuePtr, 36, &GetStmtAttr_StringLengthPtr);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ GetStmtAttr_DisplayError(SQL_HANDLE_STMT, hstmt);//}
+
+ return 0;
+
+
+ }
+
+
+void GetStmtAttr_DisplayError(SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLGetTypeInfoTest.cpp b/storage/ndb/test/odbc/client/SQLGetTypeInfoTest.cpp
new file mode 100644
index 00000000000..5925d1cc1ae
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLGetTypeInfoTest.cpp
@@ -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 */
+
+ /**
+ * @file SQLGetTypeInfoTest.cpp
+ */
+
+#include <common.hpp>
+#define GT_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC GT_hdbc;
+SQLHSTMT GT_hstmt;
+SQLHENV GT_henv;
+
+void SQLGetTypeInfoTest_DisplayError(SQLSMALLINT GT_HandleType,
+ SQLHDBC GT_InputHandle);
+
+/**
+ * Test to retrieve general information about the data types
+ * supported by the data source an application is currently connected to.
+ *
+ * Tests:
+ * -# Test The value of FunctionId is not in table 37
+ * @return Zero, if test succeeded
+ */
+int SQLGetTypeInfoTest()
+{
+ SQLRETURN retcode;
+ SQLSMALLINT ColumnSize;
+ unsigned long TypeName;
+ // SQLCHAR TypeName[128];
+
+ ndbout << endl << "Start SQLGetTypeInfo Testing" << endl;
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &GT_henv);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ retcode = SQLSetEnvAttr(GT_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.X!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_DBC,
+ GT_henv,
+ &GT_hdbc);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ retcode = SQLConnect(GT_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_STMT,
+ GT_hdbc,
+ &GT_hstmt);
+
+
+ //***********************************************
+ //** Get DataType From the Current Application **
+ //***********************************************
+ retcode = SQLGetTypeInfo(GT_hstmt, SQL_CHAR);
+ ndbout << "retcode =SQLGetTypeInfo()= " << retcode << endl;
+ if (retcode == SQL_SUCCESS)
+ {
+ retcode =SQLBindCol(GT_hstmt,
+ 2,
+ SQL_C_ULONG,
+ TypeName,
+ sizeof(TypeName),
+ NULL);
+ ndbout << "retcode = SQLBindCol()= " << retcode << endl;
+
+ // retcode =SQLBindCol(GT_hstmt,
+ // 1,
+ // SQL_C_DEFAULT,
+ // ColumnSize,
+ // sizeof(ColumnSize),
+ // NULL);
+
+ retcode = SQLFetch(GT_hstmt);
+ ndbout << "retcode = SQLFETCH()=" << retcode << endl;
+ ndbout << "DataType = " << TypeName << endl;
+
+ }
+
+ //*******************************************************
+ //** If the Value of DataType is not in Table 37, then **
+ //** an exception condition is raised **
+ //*******************************************************
+ retcode = SQLGetTypeInfo(GT_hstmt, 8888888);
+ if (retcode == -2)
+ {
+ ndbout << "retcode = " << retcode << endl;
+ ndbout << "The value of DataType is not in table 37" << endl;
+ SQLGetTypeInfoTest_DisplayError(SQL_HANDLE_STMT, GT_hstmt);
+ }
+ else if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "retcode = " << retcode << endl;
+ ndbout << endl << "The value of DataType is not in Table 37" << endl;
+ SQLGetTypeInfoTest_DisplayError(SQL_HANDLE_STMT, GT_hstmt);
+ }
+ else
+ {
+ ndbout << "retcode = " << retcode << endl;
+ ndbout << endl << "The value of DataType is not in Table 37" << endl;
+ SQLGetTypeInfoTest_DisplayError(SQL_HANDLE_STMT, GT_hstmt);
+ }
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(GT_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, GT_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, GT_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, GT_henv);
+
+ return NDBT_OK;
+
+ }
+
+void SQLGetTypeInfoTest_DisplayError(SQLSMALLINT GT_HandleType,
+ SQLHDBC GT_InputHandle)
+{
+ SQLINTEGER NativeError;
+ SQLSMALLINT i, MsgLen;
+ SQLCHAR Msg[GT_MESSAGE_LENGTH];
+ SQLRETURN SQLSTATEs;
+ SQLCHAR Sqlstate[50];
+ i = 1;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((SQLSTATEs = SQLGetDiagRec(GT_HandleType,
+ GT_InputHandle,
+ i,
+ Sqlstate,
+ &NativeError,
+ Msg,
+ sizeof(Msg),
+ &MsgLen))
+ != SQL_NO_DATA)
+{
+
+ ndbout << "the HandleType is:" << GT_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)GT_InputHandle << endl;
+ ndbout << "the Msg is :" << (char *) Msg << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLMoreResultsTest.cpp b/storage/ndb/test/odbc/client/SQLMoreResultsTest.cpp
new file mode 100644
index 00000000000..cba8b0dc53e
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLMoreResultsTest.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 <common.h>
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+void SQLMoreResults_DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+
+int SQLMoreResultsTest()
+{
+
+ /* hstmt */
+ retcode = SQLMoreResults(hstmt);
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLMoreResults_DisplayError(SQL_HANDLE_STMT, hstmt);
+
+ /* henv */
+ retcode = SQLMoreResults(henv);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS)
+ ndbout << "Handle Type is SQL_HANDLE_ENV, but string SQL_SUCCESS_WITH_INFO still appeared. Please check programm" << endl;
+ // SQLMoreResults_DisplayError(SQL_HANDLE_ENV, henv);
+
+ /* hdbc */
+ retcode = SQLMoreResults(hdbc);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_SUCCESS_WITH_INFO still appeared. Please check programm" << endl;
+ // SQLMoreResults_DisplayError(SQL_HANDLE_DBC, hdbc);
+
+ /* hdesc */
+ retcode = SQLMoreResults(hdesc);
+
+ if (retcode == SQL_SUCCESS_WITH_INFO || retcode == SQL_SUCCESS)
+ ndbout << "Handle Type is SQL_HANDLE_DESC, but string SQL_SUCCESS_WITH_INFO still appeared. Please check programm" << endl;
+ // SQLMoreResults_DisplayError(SQL_HANDLE_DESC, hdesc);
+
+ return 0;
+
+ }
+
+
+void SQLMoreResults_DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLNumResultColsTest.cpp b/storage/ndb/test/odbc/client/SQLNumResultColsTest.cpp
new file mode 100644
index 00000000000..8f0c1dba94c
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLNumResultColsTest.cpp
@@ -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 */
+
+ /**
+ * @file SQLNumResultColsTest.cpp
+ */
+#include <common.hpp>
+using namespace std;
+
+#define NRC_MESSAGE_LENGTH 200
+
+SQLHSTMT NRC_hstmt;
+SQLHSTMT NRC_hdbc;
+SQLHENV NRC_henv;
+SQLHDESC NRC_hdesc;
+
+void SQLNumResultColsTest_DisplayError(SQLSMALLINT NRC_HandleType,
+ SQLHSTMT NRC_InputHandle);
+
+/**
+ * Test returning descriptor information
+ *
+ * Tests:
+ * -# Testing how many columns exist in the result data set
+ *
+ * @return Zero, if test succeeded
+ */
+
+int SQLNumResultColsTest()
+{
+ SQLRETURN retcode;
+ SQLSMALLINT NumColumns;
+ SQLCHAR SQLStmt[NRC_MESSAGE_LENGTH];
+
+ ndbout << endl << "Start SQLNumResultCols Testing" << endl << endl;
+
+ //**************************************************************
+ //** If there is no prepared or executed statement associated **
+ //** with SQL-statement **
+ //**************************************************************
+
+ retcode = SQLNumResultCols(NRC_hstmt, &NumColumns);
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+
+ SQLNumResultColsTest_DisplayError(SQL_HANDLE_STMT, NRC_hstmt);
+ }
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &NRC_henv);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ retcode = SQLSetEnvAttr(NRC_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_DBC,
+ NRC_henv,
+ &NRC_hdbc);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ retcode = SQLConnect(NRC_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_STMT,
+ NRC_hdbc,
+ &NRC_hstmt);
+ if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //************************
+ //** Define a statement **
+ //************************
+
+ strcpy((char *) SQLStmt, "SELECT * FROM Customers");
+
+ // strcpy((char *) SQLStmt,
+ // "INSERT INTO Customers (CustID, Name, Address, Phone) VALUES (7, 'pet', 'LM vag 8', '88888')");
+
+ //*******************************************
+ //** Prepare and Execute the SQL statement **
+ //*******************************************
+
+ retcode = SQLExecDirect(NRC_hstmt,
+ SQLStmt,
+ SQL_NTS);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ //*****************************************************
+ //** Only general error test. It is not in test rule **
+ //*****************************************************
+
+ retcode = SQLNumResultCols(NRC_hstmt, &NumColumns);
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << endl << "Number of columns in the result data set" << endl;
+ ndbout << NumColumns << endl;
+ }
+ else
+ SQLNumResultColsTest_DisplayError(SQL_HANDLE_STMT, NRC_hstmt);
+ }
+
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(NRC_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, NRC_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, NRC_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, NRC_henv);
+
+ return NDBT_OK;
+
+}
+
+void SQLNumResultColsTest_DisplayError(SQLSMALLINT NRC_HandleType,
+ SQLHSTMT NRC_InputHandle)
+{
+ SQLRETURN SQLSTATEs;
+ SQLINTEGER NativeError;
+ SQLSMALLINT i, MsgLen;
+ SQLCHAR Msg[NRC_MESSAGE_LENGTH],Sqlstate[5];
+ i = 1;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((SQLSTATEs = SQLGetDiagRec(NRC_HandleType,
+ NRC_InputHandle,
+ i,
+ Sqlstate,
+ &NativeError,
+ Msg,
+ sizeof(Msg),
+ &MsgLen))
+ != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << NRC_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)NRC_InputHandle << endl;
+ ndbout << "the Msg is: " << (char *)Msg << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLParamDataTest.cpp b/storage/ndb/test/odbc/client/SQLParamDataTest.cpp
new file mode 100644
index 00000000000..92d491dfaf5
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLParamDataTest.cpp
@@ -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 */
+
+#include <NdbOut.hpp>
+#include <sqlcli.h>
+#include <stdio.h>
+
+using namespace std;
+
+#define NAME_LEN 50
+#define PHONE_LEN 10
+#define SALES_PERSON_LEN 10
+#define STATUS_LEN 6
+
+#define NAME_LEN 50
+#define PHONE_LEN 50
+
+SQLCHAR szName[NAME_LEN], szPhone[PHONE_LEN];
+SQLINTEGER sCustID, cbName, cbAge, cbBirthday;
+
+SQLHSTMT hstmt;
+SQLHENV henv;
+
+SQLCHAR szSalesPerson[SALES_PERSON_LEN];
+
+SQLCHAR szStatus[STATUS_LEN], Sqlstate[5], Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+SQLINTEGER cbOrderID = 0, cbCustID = 0, cbOpenDate = 0, cbSalesPerson = SQL_NTS, cbStatus = SQL_NTS, NativeError;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLSMALLINT sOrderID;
+
+SQLSMALLINT i, MsgLen;
+
+SQLCHAR ColumnName;
+SQLSMALLINT TargetValuePtr;
+SQLINTEGER StrLen_or_IndPtr;
+SQLPOINTER ValuePtrPtr;
+
+void DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+
+int SQLParamDataTest()
+{
+
+ /* hstmt */
+ // We can create the table ORDERS and insert rows into ORDERS
+ // NDB by program TestDirectSQL. In this test program, We only have three rows in table ORDERS
+
+/* Prepare the SQL statement with parameter markers. */
+retcode = SQLPrepare(hstmt, (SQLCHAR *)"SELECT ORDERID, CUSTID, OPENDATE, SALESPERSON, STATUS FROM ORDERS", SQL_NTS);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+ retcode = SQLExecute(hstmt);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+ retcode = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_INTEGER, 16, 0, &sOrderID, 16, &cbOrderID);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+
+while (retcode == SQL_NEED_DATA) {
+ retcode = SQLParamData(hstmt, &ValuePtrPtr);
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
+ DisplayError(SQL_HANDLE_STMT, hstmt);
+ }
+ }
+
+
+ }
+}
+}
+ return 0;
+
+ }
+
+
+void DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLPrepareTest.cpp b/storage/ndb/test/odbc/client/SQLPrepareTest.cpp
new file mode 100644
index 00000000000..2ebbc224b85
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLPrepareTest.cpp
@@ -0,0 +1,285 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+ /**
+ * @file SQLprepareTest.cpp
+ */
+#include <common.hpp>
+#define pare_SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC pare_hdbc;
+SQLHSTMT pare_hstmt;
+SQLHENV pare_henv;
+SQLHDESC pare_hdesc;
+SQLRETURN pare_retcode, pare_SQLSTATEs;
+
+SQLCHAR pare_Sqlstate[5];
+
+SQLINTEGER pare_NativeError;
+SQLSMALLINT pare_i, pare_MsgLen;
+SQLCHAR pare_Msg[pare_SQL_MAXIMUM_MESSAGE_LENGTH];
+
+void Prepare_DisplayError(SQLSMALLINT pare_HandleType,
+ SQLHSTMT pare_InputHandle);
+
+/**
+ * Test to prepare a statement with different handles
+ *
+ * -# Input correct hstmt handle
+ * -# Input incorrect henv handle
+ * -# Input incorrect hdbc handle
+ * -# Input incorrect handle hdesc
+ *
+ * @return Zero, if test succeeded
+ */
+int SQLPrepareTest()
+{
+ SQLCHAR SQLStmt [120];
+ ndbout << endl << "Start SQLPrepare Testing" << endl;
+ ndbout << endl << "Test 1" << endl;
+ //*********************************
+ //** Test1 **
+ //** Input correct hstmt handle **
+ //*********************************
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ pare_retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &pare_henv);
+
+ if(pare_retcode == SQL_SUCCESS || pare_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ pare_retcode = SQLSetEnvAttr(pare_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (pare_retcode == SQL_SUCCESS || pare_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+ pare_retcode = SQLAllocHandle(SQL_HANDLE_DBC, pare_henv, &pare_hdbc);
+ if (pare_retcode == SQL_SUCCESS || pare_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ pare_retcode = SQLConnect(pare_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (pare_retcode == SQL_SUCCESS || pare_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ ndbout << "Failure to Connect DB!" << endl;
+
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+ pare_retcode = SQLAllocHandle(SQL_HANDLE_STMT, pare_hdbc, &pare_hstmt);
+ if (pare_retcode == SQL_SUCCESS || pare_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //************************
+ //** Define a statement **
+ //************************
+
+ strcpy( (char *) SQLStmt, "INSERT INTO Customers (CustID, Name, Address, Phone) VALUES(2, 'Hans Peter', 'LM Vag8', '468719000')");
+
+ pare_retcode = SQLPrepare(pare_hstmt,
+ SQLStmt,
+ SQL_NTS);
+
+ if (pare_retcode == SQL_INVALID_HANDLE)
+ {
+ ndbout << "pare_retcode = " << pare_retcode << endl;
+ ndbout << "HandleType is SQL_HANDLE_STMT, but SQL_INVALID_HANDLE"
+ << endl;
+ ndbout << "appeared. Please check program!" << endl;
+ }
+ else if (pare_retcode == SQL_ERROR || pare_retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ Prepare_DisplayError(SQL_HANDLE_STMT, pare_hstmt);
+ }
+ else
+ {
+ //***********************
+ //** Execute statement **
+ //***********************
+ pare_retcode = SQLExecute(pare_hstmt);
+ if (pare_retcode != SQL_SUCCESS)
+ {
+ ndbout << "pare_retcode = " << pare_retcode << endl;
+ Prepare_DisplayError(SQL_HANDLE_STMT, pare_hstmt);
+ }
+ else
+ ndbout << endl << "Test 1:Input correct HSTMT handle. OK!" << endl;
+ }
+
+ //*********************************
+ //** Test2 **
+ //** Input incorrect henv handle **
+ //*********************************
+
+ strcpy( (char *) SQLStmt, "INSERT INTO Customers (CustID, Name, Address, Phone) VALUES(3, 'Hans', 'LM8', '51888')");
+
+ pare_retcode = SQLPrepare(pare_henv,
+ SQLStmt,
+ SQL_NTS);
+
+ ndbout << endl << "Test 2" << endl;
+ if (pare_retcode == SQL_SUCCESS_WITH_INFO || pare_retcode == SQL_SUCCESS)
+ {
+ FAILURE("Wrong SQL_HANDLE_HENV, but success returned. Check it!");
+ }
+ else if (pare_retcode == SQL_INVALID_HANDLE)
+ {
+ ndbout << "Wrong SQL_HANDLE_HENV input and -2 appeared. OK!" << endl ;
+ }
+ else
+ ;
+ /*
+ {
+ ndbout << "Input wrong SQL_HANDLE_ENV, but SQL_SUCCESS_W_I" << endl;
+ ndbout << "and SQL_SUCCESS appeared. Please check program!" << endl;
+ return NDBT_FAILED;
+ }
+ */
+
+ //*********************************
+ //** Test3 **
+ //** Input incorrect hdbc handle **
+ //*********************************
+
+ strcpy( (char *) SQLStmt, "INSERT INTO Customers (CustID, Name, Address, Phone) VALUES(4, 'HP', 'VÄG8', '90888')");
+
+ pare_retcode = SQLPrepare(pare_hdbc,
+ SQLStmt,
+ SQL_NTS);
+
+ ndbout << endl << "Test 3" << endl;
+ if (pare_retcode == SQL_SUCCESS_WITH_INFO || pare_retcode == SQL_SUCCESS)
+ {
+ FAILURE("Wrong SQL_HANDLE_HDBC, but success returned. Check it!");
+ }
+ else if (pare_retcode == SQL_INVALID_HANDLE)
+ {
+ ndbout << "Wrong SQL_HANDLE_HDBC input and -2 appeared. OK!" << endl ;
+ }
+ else
+ ;
+
+ /*
+ {
+ ndbout << "Input wrong statement handle SQL_HANDLE_DBC" << endl;
+ ndbout << "but SQL_SUCCESS_WITH_INFO" << endl;
+ ndbout << "and SQL_SUCCESS still appeared. Please check program" << endl;
+ return NDBT_FAILED;
+ }
+
+ */
+ //**********************************
+ //** Test4 **
+ //** Input incorrect handle hdesc **
+ //**********************************
+
+ strcpy( (char *) SQLStmt, "INSERT INTO Customers (CustID, Name, Address, Phone) VALUES(5, 'Richard', 'VÄG8', '56888')");
+
+ pare_retcode = SQLPrepare(pare_hdesc,
+ SQLStmt,
+ SQL_NTS);
+
+ ndbout << endl << "Test 4" << endl;
+ if (pare_retcode == SQL_SUCCESS_WITH_INFO || pare_retcode == SQL_SUCCESS)
+ {
+ FAILURE("Wrong SQL_HANDLE_DESC, but success returned");
+ }
+ else if (pare_retcode == SQL_INVALID_HANDLE)
+ {
+ ndbout << "Wrong SQL_HANDLE_DESC input and -2 appeared. OK!" << endl ;
+ }
+ else
+ ndbout << endl;
+
+ /*
+ {
+ ndbout << "TEST FAILURE: Input wrong SQL_HANDLE_DESC, "
+ << "but SQL_SUCCESS_WITH_INFO or SQL_SUCCESS was returned."
+ << endl;
+ return NDBT_FAILED;
+ }
+ */
+
+ //****************
+ // Free Handles **
+ //****************
+ SQLDisconnect(pare_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, pare_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, pare_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, pare_henv);
+
+ return NDBT_OK;
+
+}
+
+void Prepare_DisplayError(SQLSMALLINT pare_HandleType,
+ SQLHSTMT pare_InputHandle)
+{
+ SQLSMALLINT pare_i = 1;
+ SQLRETURN pare_SQLSTATEs;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((pare_SQLSTATEs = SQLGetDiagRec(pare_HandleType,
+ pare_InputHandle,
+ pare_i,
+ pare_Sqlstate,
+ &pare_NativeError,
+ pare_Msg,
+ sizeof(pare_Msg),
+ &pare_MsgLen)
+ ) != SQL_NO_DATA)
+ {
+ ndbout << "SQLSTATE = " << pare_SQLSTATEs << endl;
+ ndbout << "the HandleType is:" << pare_HandleType << endl;
+ ndbout << "the Handle is :" << (long)pare_InputHandle << endl;
+ ndbout << "the conn_Msg is: " << (char *) pare_Msg << endl;
+ ndbout << "the output state is:" << (char *)pare_Sqlstate << endl;
+
+ pare_i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLPutDataTest.cpp b/storage/ndb/test/odbc/client/SQLPutDataTest.cpp
new file mode 100644
index 00000000000..38a8458fec4
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLPutDataTest.cpp
@@ -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 */
+
+#include <NdbOut.hpp>
+#include <sqlcli.h>
+#include <stdio.h>
+
+using namespace std;
+
+#define NAME_LEN 50
+#define PHONE_LEN 10
+#define SALES_PERSON_LEN 10
+#define STATUS_LEN 6
+
+#define NAME_LEN 50
+#define PHONE_LEN 50
+
+SQLCHAR szName[NAME_LEN], szPhone[PHONE_LEN];
+SQLINTEGER sCustID, cbName, cbAge, cbBirthday;
+
+SQLHSTMT hstmt;
+SQLHENV henv;
+
+SQLCHAR szSalesPerson[SALES_PERSON_LEN];
+
+SQLCHAR szStatus[STATUS_LEN], Sqlstate[5], Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+SQLINTEGER cbOrderID = 0, cbCustID = 0, cbOpenDate = 0, cbSalesPerson = SQL_NTS, cbStatus = SQL_NTS, NativeError;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLSMALLINT sOrderID;
+
+SQLSMALLINT i, MsgLen;
+
+SQLCHAR ColumnName;
+SQLSMALLINT TargetValuePtr;
+SQLINTEGER StrLen_or_Ind, DataPtr;
+SQLPOINTER ValuePtrPtr;
+
+void DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle);
+
+int SQLPutDataTest()
+{
+
+ /* hstmt */
+ // We can create the table ORDERS and insert rows into ORDERS
+ // NDB by program TestDirectSQL. In this test program, We only have three rows in table ORDERS
+
+/* Prepare the SQL statement with parameter markers. */
+retcode = SQLPrepare(hstmt, (SQLCHAR *)"SELECT ORDERID, CUSTID, OPENDATE, SALESPERSON, STATUS FROM ORDERS", SQL_NTS);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+ retcode = SQLExecute(hstmt);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+ retcode = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_INTEGER, 16, 0, &sOrderID, 16, &cbOrderID);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+
+while (retcode == SQL_NEED_DATA) {
+ retcode = SQLParamData(hstmt, &ValuePtrPtr);
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
+ retcode = SQLPutData(hstmt, &DataPtr, StrLen_or_Ind);
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
+ DisplayError(SQL_HANDLE_STMT, hstmt);
+ }
+ }
+ }
+
+
+ }
+}
+}
+ return 0;
+
+ }
+
+
+void DisplayError(SQLSMALLINT HandleType, SQLHSTMT InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLRowCountTest.cpp b/storage/ndb/test/odbc/client/SQLRowCountTest.cpp
new file mode 100644
index 00000000000..f298017c519
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLRowCountTest.cpp
@@ -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 */
+
+ /**
+ * @file SQLRowCountTest.cpp
+ */
+
+#include <common.hpp>
+using namespace std;
+
+#define NAME_LEN 50
+#define PHONE_LEN 10
+#define SALES_PERSON_LEN 10
+#define STATUS_LEN 6
+#define RC_MESSAGE_LENGTH 200
+
+SQLHSTMT RC_hstmt;
+SQLHDBC RC_hdbc;
+SQLHENV RC_henv;
+SQLHDESC RC_hdesc;
+
+void SQLRowCountTest_DisplayError(SQLSMALLINT RC_HandleType,
+ SQLHSTMT RC_InputHandle);
+
+/**
+ * Test to obtain a count of the number of rows
+ * in a table
+ *
+ * -# Call SQLRowCount without executed statement
+ * -# Call SQLRowCount with normal case
+ *
+ * @return Zero, if test succeeded
+ */
+
+int SQLRowCountTest()
+{
+ SQLRETURN retcode;
+ unsigned long RowCount;
+ SQLCHAR SQLStmt [120];
+
+ ndbout << endl << "Start SQLRowCount Testing" << endl;
+
+ //************************************************************************
+ //* If there is no executed statement, an execption condotion is raised **
+ //************************************************************************
+
+ retcode = SQLRowCount(RC_hstmt, &RowCount);
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ {
+
+ SQLRowCountTest_DisplayError(SQL_HANDLE_STMT, RC_hstmt);
+ }
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &RC_henv);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ retcode = SQLSetEnvAttr(RC_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_DBC,
+ RC_henv,
+ &RC_hdbc);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ retcode = SQLConnect(RC_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_STMT,
+ RC_hdbc,
+ &RC_hstmt);
+ if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //************************
+ //** Define a statement **
+ //************************
+ strcpy((char *) SQLStmt, "INSERT INTO Customers (CustID, Name, Address,Phone) VALUES(588, 'HeYong','LM888','919888')");
+
+ //*******************************
+ //* Prepare the SQL statement **
+ //*******************************
+
+ retcode = SQLPrepare(RC_hstmt,
+ SQLStmt,
+ SQL_NTS);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ //******************************
+ //* Execute the SQL statement **
+ //******************************
+ retcode = SQLExecute(RC_hstmt);
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ //***************
+ // Normal test **
+ //***************
+ retcode = SQLRowCount(RC_hstmt, &RowCount);
+ if (retcode == SQL_ERROR )
+ SQLRowCountTest_DisplayError(SQL_HANDLE_STMT, RC_hstmt);
+ else
+ ndbout << endl << "Number of the rows in the table Customers: "
+ << (int)RowCount << endl;
+ }
+ }
+
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(RC_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, RC_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, RC_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, RC_henv);
+
+ return NDBT_OK;
+
+}
+
+void SQLRowCountTest_DisplayError(SQLSMALLINT RC_HandleType,
+ SQLHSTMT RC_InputHandle)
+{
+ SQLRETURN SQLSTATEs;
+ SQLSMALLINT i, MsgLen;
+ SQLCHAR Sqlstate[5], Msg[RC_MESSAGE_LENGTH];
+ SQLINTEGER NativeError;
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(RC_HandleType,
+ RC_InputHandle,
+ i,
+ Sqlstate,
+ &NativeError,
+ Msg,
+ sizeof(Msg),
+ &MsgLen))
+ != SQL_NO_DATA)
+{
+
+ ndbout << "the HandleType is:" << RC_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)RC_InputHandle << endl;
+ ndbout << "the Msg:" << (char *)Msg << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+}
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLSetConnectAttrTest.cpp b/storage/ndb/test/odbc/client/SQLSetConnectAttrTest.cpp
new file mode 100644
index 00000000000..c41ef885521
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLSetConnectAttrTest.cpp
@@ -0,0 +1,131 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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.h>
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLPOINTER ValuePtr;
+//SQLINTEGER StringLength;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+void SetConnectAttr_DisplayError(SQLSMALLINT HandleType, SQLHENV InputHandle);
+
+int SQLSetConnectAttrTest ()
+{
+ /* SQL/CLI attributes */
+ char PtrValue1[2] = {'SQL_TRUE', 'SQL_FALSE'};
+ for (i=0; i < 2; i++) {
+ retcode = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTO_IPD, (void*)PtrValue1[i], sizeof(PtrValue1[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetConnectAttr_DisplayError(SQL_HANDLE_DBC, hdbc);}
+
+ /* ODBC attributes */
+ /*
+ char PtrValue1[3] = {'SQL_MODE_READ_ONLY', 'SQL_MODE_READ_WRITE'};
+ for (i=0; i < 3; i++) {
+ retcode = SQLSetConnectAttr(hdbc, SQL_ATTR_ACCESS_MODE, (void*)PtrValue1[i], sizeof(PtrValue1[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetConnectAttr_DisplayError(SQL_HANDLE_DBC, hdbc);}
+
+
+ char PtrValue2[2] = {'SQL_ASYNC_ENABLE_OFF', 'SQL_ASYNC_ENABLE_ON'};
+ for (i=0; i < 2; i++) {
+ retcode = SQLSetConnectAttr(hdbc, SQL_ATTR_ASYNC_ENABLE, (void*)PtrValue2[i], sizeof(PtrValue2[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetConnectAttr_DisplayError(SQL_HANDLE_DBC, hdbc);}
+
+
+ char PtrValue4[2] = {'SQL_AUTOCOMMIT_OFF', 'SQL_AUTOCOMMIT_ON'};
+ for (i=0; i < 2; i++) {
+ retcode = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (void*)PtrValue4[i], sizeof(PtrValue4[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetConnectAttr_DisplayError(SQL_HANDLE_DBC, hdbc);}
+
+ char PtrValue5[2] = {'SQL_CD_TRUE', 'SQL_CD_FALSE'};
+ for (i=0; i < 2; i++) {
+ retcode = SQLSetConnectAttr(hdbc, SQL_ATTR_CONNECTION_DEAD, (void*)PtrValue4[i], sizeof(PtrValue5[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetConnectAttr_DisplayError(SQL_HANDLE_DBC, hdbc);}
+
+
+ char PtrValue5[2] = {'SQL_CD_TRUE', 'SQL_CD_FALSE'};
+ for (i=0; i < 2; i++) {
+ retcode = SQLSetConnectAttr(hdbc, SQL_ATTR_CONNECTION_TIMEOUT, (void*)PtrValue4[i], sizeof(PtrValue5[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_DBC, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetConnectAttr_DisplayError(SQL_HANDLE_DBC, hdbc);}
+
+ */
+
+ return 0;
+
+ }
+
+
+void SetConnectAttr_DisplayError(SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLSetCursorNameTest.cpp b/storage/ndb/test/odbc/client/SQLSetCursorNameTest.cpp
new file mode 100644
index 00000000000..b35cf9fefc2
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLSetCursorNameTest.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 */
+
+ /**
+ * @file SQLSetCursorNameTest.cpp
+ */
+#include <common.hpp>
+using namespace std;
+
+#define SCN_MESSAGE_LENGTH 50
+
+SQLHSTMT SCN_hstmt;
+SQLHDESC SCN_hdesc;
+SQLHENV SCN_henv;
+SQLHDBC SCN_hdbc;
+
+void SCN_DisplayError(SQLSMALLINT SCN_HandleType,
+ SQLHDESC SCN_InputHandle);
+
+/**
+ * Test to assign a user-defined name to a cursor that is
+ * associated with an active SQL statement handle
+ *
+ * Tests:
+ * -# set user-defined cursor name to zero
+ * -# set user-defined cursor name in normal case
+ *
+ * @return Zero, if test succeeded
+ */
+
+int SQLSetCursorNameTest()
+{
+ SQLRETURN retcode;
+ SQLCHAR SQLStmt [120];
+ SQLCHAR CursorName [80];
+ SQLSMALLINT CNameSize;
+
+ ndbout << endl << "Start SQLSetCursorName Testing" << endl;
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &SCN_henv);
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ retcode = SQLSetEnvAttr(SCN_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_DBC,
+ SCN_henv,
+ &SCN_hdbc);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ retcode = SQLConnect(SCN_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ retcode = SQLAllocHandle(SQL_HANDLE_STMT,
+ SCN_hdbc,
+ &SCN_hstmt);
+
+ if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //************************
+ //** Define a statement **
+ //************************
+
+ strcpy((char *) SQLStmt,
+ "SELECT * FROM Customers WHERE Address = 'LM Vag 8'");
+
+ //*************************
+ //** Prepare a statement **
+ //*************************
+
+ retcode = SQLPrepare(SCN_hstmt,
+ SQLStmt,
+ SQL_NTS);
+
+ //**********************************
+ //** Set the cursor name with zero**
+ //**********************************
+ retcode = SQLSetCursorName(SCN_hstmt,
+ (char *)"",
+ SQL_NTS);
+
+ if (retcode != SQL_SUCCESS)
+ {
+ ndbout << endl << "retcode =" << retcode << endl;
+ SCN_DisplayError(SQL_HANDLE_STMT, SCN_hstmt);
+ }
+
+ //*************************
+ //** Set the cursor name **
+ //*************************
+ retcode = SQLSetCursorName(SCN_hstmt,
+ (char *)"Customer_CURSOR",
+ SQL_NTS);
+
+ if (retcode != SQL_SUCCESS)
+ {
+ ndbout << endl << "retcode =" << retcode << endl;
+ SCN_DisplayError(SQL_HANDLE_STMT, SCN_hstmt);
+ }
+ //***************************
+ //** Execute the statement **
+ //***************************
+ retcode = SQLExecute(SCN_hstmt);
+
+ //**********************************************
+ //** retrieve and display the new cursor name **
+ //**********************************************
+ retcode = SQLGetCursorName(SCN_hstmt,
+ CursorName,
+ sizeof(CursorName),
+ &CNameSize);
+
+ ndbout << endl << "The cursor name is : " << (char *) CursorName << endl;
+
+ //****************
+ // Free Handles **
+ //****************
+ SQLDisconnect(SCN_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, SCN_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, SCN_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, SCN_henv);
+
+ return NDBT_OK;
+
+ }
+
+
+void SCN_DisplayError(SQLSMALLINT SCN_HandleType, SQLHDESC SCN_InputHandle)
+{
+
+ SQLINTEGER NativeError;
+ SQLCHAR Sqlstate[5], Msg[SCN_MESSAGE_LENGTH];
+ SQLRETURN SQLSTATEs;
+ SQLSMALLINT i, MsgLen;
+ i = 1;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((SQLSTATEs = SQLGetDiagRec(SCN_HandleType,
+ SCN_InputHandle, i,
+ Sqlstate,
+ &NativeError,
+ Msg,
+ sizeof(Msg),
+ &MsgLen))
+ != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << SCN_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)SCN_InputHandle << endl;
+ ndbout << "the Msg is: " << (char *) Msg << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLSetDescFieldTest.cpp b/storage/ndb/test/odbc/client/SQLSetDescFieldTest.cpp
new file mode 100644
index 00000000000..798622e0f75
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLSetDescFieldTest.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 <common.h>
+
+using namespace std;
+
+#define NAME_LEN 50
+#define PHONE_LEN 10
+#define SALES_PERSON_LEN 10
+#define STATUS_LEN 6
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+SQLHSTMT hstmt;
+SQLHDESC hdesc;
+
+SQLSMALLINT RecNumber;
+SQLCHAR szSalesPerson[SALES_PERSON_LEN];
+
+SQLCHAR Sqlstate[5], Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+SQLINTEGER NativeError;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLINTEGER ValuePtr1;
+SQLCHAR ValuePtr2;
+SQLSMALLINT ValuePtr3;
+
+SQLSMALLINT i, MsgLen;
+
+void SQLSetDescFieldTest_DisplayError(SQLSMALLINT HandleType, SQLHDESC InputHandle);
+
+int SQLSetDescFieldTest()
+{
+
+ /* hstmt */
+ // SQLPrepare a statement to select rows from the ORDERS Table. We can create the table and inside rows in
+ // NDB by another program TestDirectSQL. In this test program(SQLGetDescRecTest),we only have three rows in
+ // table ORDERS
+
+/* Prepare the SQL statement with parameter markers. */
+retcode = SQLPrepare(hstmt, (SQLCHAR *)"SELECT ORDERID, CUSTID, OPENDATE, SALESPERSON, STATUS FROM ORDERS)", SQL_NTS);
+
+/* SELECT OrderID, CustID, OpenDate, SalesPerson from Table ORDERS */
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+ retcode = SQLExecute(hstmt);
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ /* If FI(FieldIdentifer) is not one of the code values in Table 20 */
+retcode = SQLSetDescField(hdesc, 1, 9999, &ValuePtr1, 128);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLSetDescFieldTest_DisplayError(SQL_HANDLE_DESC, hdesc);
+
+
+ /* RecoderNumber is less than 1 and the value of the Type column in the Table is ITEM */
+retcode = SQLSetDescField(hdesc, -1, SQL_DESC_LENGTH, &ValuePtr1, 128);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLSetDescFieldTest_DisplayError(SQL_HANDLE_DESC, hdesc);
+
+
+}
+
+}
+
+ return 0;
+
+ }
+
+
+void SQLSetDescFieldTest_DisplayError(SQLSMALLINT HandleType, SQLHDESC InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLSetDescRecTest.cpp b/storage/ndb/test/odbc/client/SQLSetDescRecTest.cpp
new file mode 100644
index 00000000000..d97af576cb0
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLSetDescRecTest.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 <NdbOut.hpp>
+#include <sqlext.h>
+#include <stdio.h>
+
+using namespace std;
+
+#define NAME_LEN 50
+#define PHONE_LEN 10
+#define SALES_PERSON_LEN 10
+#define STATUS_LEN 6
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+SQLHSTMT hstmt;
+SQLHDESC hdesc;
+
+SQLSMALLINT RecNumber;
+SQLCHAR szSalesPerson[SALES_PERSON_LEN];
+
+SQLCHAR Sqlstate[5], Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+SQLINTEGER NativeError;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLCHAR Name;
+SQLINTEGER LengthPtr;
+
+SQLSMALLINT i, MsgLen;
+
+SQLINTEGER StringLengthPtr, IndicatorPtr;
+SQLPOINTER DataPtr;
+
+void SQLSetDescRecTest_DisplayError(SQLSMALLINT HandleType, SQLHDESC InputHandle);
+
+int SQLSetDescRecTest()
+{
+
+ /* hstmt */
+ // SQLPrepare a statement to select rows from the ORDERS Table. We can create the table and inside rows in
+ // NDB by program TestDirectSQL. In this test program(SQLSetDescRecTest),we only have three rows in
+ // table ORDERS
+
+/* Prepare the SQL statement with parameter markers. */
+retcode = SQLPrepare(hstmt, (SQLCHAR *)"SELECT ORDERID, CUSTID, OPENDATE, SALESPERSON, STATUS FROM ORDERS)", SQL_NTS);
+
+/* SELECT OrderID, CustID, OpenDate, SalesPerson from Table ORDERS */
+
+if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
+
+ /* RecoderNumber is less than 1 */
+retcode = SQLSetDescRec(hdesc, -1, 1002, 1007, 1013, 1005, 1006, (void *)DataPtr, &StringLengthPtr, &IndicatorPtr);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLSetDescRecTest_DisplayError(SQL_HANDLE_DESC, hdesc);
+
+ /* RecoderNumber is greater than N, N be the value of the COUNT field of D, D be the allocated CLI */
+ /* descriptor area identified by DescriptorHandle */
+retcode = SQLSetDescRec(hdesc, 4, 1002, 1007, 1013, 1005, 1006, (void *)DataPtr, &StringLengthPtr, &IndicatorPtr);
+if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SQLSetDescRecTest_DisplayError(SQL_HANDLE_DESC, hdesc);
+
+}
+
+ return 0;
+
+ }
+
+
+void SQLSetDescRecTest_DisplayError(SQLSMALLINT HandleType, SQLHDESC InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLSetEnvAttrTest.cpp b/storage/ndb/test/odbc/client/SQLSetEnvAttrTest.cpp
new file mode 100644
index 00000000000..16ae5671ca3
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLSetEnvAttrTest.cpp
@@ -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 */
+
+#include <common.h>
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLPOINTER ValuePtr;
+SQLINTEGER SetEnvAttr_StringLength;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+void SetEnvAttr_DisplayError(SQLSMALLINT HandleType, SQLHENV InputHandle);
+
+int SQLSetEnvAttrTest()
+{
+ /* ODBC attributes */
+ /*
+ char PtrValue1[3] = {'SQL_CP_OFF', 'SQL_CP_ONE_DRIVER', 'SQL_CP_ONE_PER_HENV'};
+ for (i=0; i < 3; i++) {
+ retcode = SQLSetEnvAttr(henv, SQL_ATTR_CONNECTION_POOLING, (void*)PtrValue1[i], sizeof(PtrValue1[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_ENV, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetEnvAttr_DisplayError(SQL_HANDLE_ENV, henv);}
+
+
+ char PtrValue2[2] = {'SQL_CP_STRICT_MATCH', 'SQL_CP_RELAXED_MATCH'};
+ for (i=0; i < 2; i++) {
+ retcode = SQLSetEnvAttr(henv, SQL_ATTR_CP_MATCH, (void*)PtrValue2[i], sizeof(PtrValue2[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_ENV, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetEnvAttr_DisplayError(SQL_HANDLE_ENV, henv);}
+
+
+ char PtrValue3[2] = {'SQL_OV_ODBC3', 'SQL_OV_ODBC2'};
+ for (i=0; i < 2; i++) {
+ retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)PtrValue3[i], sizeof(PtrValue3[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_ENV, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetEnvAttr_DisplayError(SQL_HANDLE_ENV, henv);}
+ */
+
+ char PtrValue4[2] = {'SQL_TRUE', 'SQL_FALSE'};
+ for (i=0; i < 2; i++) {
+ retcode = SQLSetEnvAttr(henv, SQL_ATTR_OUTPUT_NTS, (void*)PtrValue4[i], sizeof(PtrValue4[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_ENV, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetEnvAttr_DisplayError(SQL_HANDLE_ENV, henv);}
+
+ return 0;
+
+ }
+
+
+void SetEnvAttr_DisplayError(SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLSetStmtAttrTest.cpp b/storage/ndb/test/odbc/client/SQLSetStmtAttrTest.cpp
new file mode 100644
index 00000000000..646f82cd306
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLSetStmtAttrTest.cpp
@@ -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 */
+
+#include <common.h>
+#define SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+using namespace std;
+
+SQLHDBC hdbc;
+SQLHSTMT hstmt;
+SQLHENV henv;
+SQLHDESC hdesc;
+SQLRETURN retcode, SQLSTATEs;
+
+SQLPOINTER ValuePtr;
+//SQLINTEGER StringLength;
+
+SQLCHAR Sqlstate[5];
+
+SQLINTEGER NativeError;
+SQLSMALLINT i, MsgLen;
+SQLCHAR Msg[SQL_MAXIMUM_MESSAGE_LENGTH];
+
+void SetStmtAttr_DisplayError(SQLSMALLINT HandleType, SQLHENV InputHandle);
+
+int SQLSetStmtAttrTest()
+{
+ /* SQL/CLI attributes */
+ /* SQL_ATTR_APP_PARAM_DESC */
+ char PtrValue1[13] = {'SQL_NULL_DESC'};
+ for (i=0; i < 1; i++) {
+ retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_APP_PARAM_DESC, (void*)PtrValue1[i], sizeof(PtrValue1[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetStmtAttr_DisplayError(SQL_HANDLE_STMT, hstmt);}
+
+ /* SQL_ATTR_APP_ROW_DESC */
+ char PtrValue2[1] = {'SQL_NULL_DESC'}; /* ? */
+ for (i=0; i < 2; i++) {
+ retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, (void*)PtrValue2[i], sizeof(PtrValue2[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetStmtAttr_DisplayError(SQL_HANDLE_STMT, hstmt);}
+
+ /* SQL_ATTR_CURSOR_SCROLLABLE */
+ char PtrValue3[2] = {'SQL_NONSCROLLABLE', 'SQL_SCROLLABLE'}; /* ? */
+ for (i=0; i < 2; i++) {
+ retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)PtrValue3[i], sizeof(PtrValue3[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetStmtAttr_DisplayError(SQL_HANDLE_STMT, hstmt);}
+
+ /* SQL_ATTR_CURSOR_SENSITIVITY */
+ char PtrValue4[3] = {'SQL_UNSPECIFIED', 'SQL_INSENSITIVE', 'SQL_SENSITIVE'}; /* ? */
+ for (i=0; i < 3; i++) {
+ retcode = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SENSITIVITY, (void*)PtrValue4[i], sizeof(PtrValue4[i]));
+
+ if (retcode == SQL_INVALID_HANDLE)
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but string SQL_INVALID_HANDLE still appeared. Please check programm" << endl;
+
+ if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
+ SetStmtAttr_DisplayError(SQL_HANDLE_STMT, hstmt);}
+
+ return 0;
+
+ }
+
+
+void SetStmtAttr_DisplayError(SQLSMALLINT HandleType, SQLHENV InputHandle)
+{
+ i = 1;
+ while ((SQLSTATEs = SQLGetDiagRec(HandleType, InputHandle, i,
+ Sqlstate, &NativeError, Msg, sizeof(Msg),
+ &MsgLen)) != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << HandleType << endl;
+ ndbout << "the InputHandle is :" << InputHandle << endl;
+ ndbout << "the output state is:" << (char *)Sqlstate << endl;
+
+ i ++;
+ }
+
+}
+
+
+
diff --git a/storage/ndb/test/odbc/client/SQLTablesTest.cpp b/storage/ndb/test/odbc/client/SQLTablesTest.cpp
new file mode 100644
index 00000000000..735efd81e9c
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLTablesTest.cpp
@@ -0,0 +1,227 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+ /**
+ * @file SQLTablesTest.cpp
+ */
+#include <common.hpp>
+using namespace std;
+
+#define Tables_NAME_LEN 12
+#define Tables_PHONE_LEN 12
+#define Tables_ADDRESS_LEN 12
+#define Tables_SQL_MAXIMUM_MESSAGE_LENGTH 200
+
+SQLHDBC Tables_hdbc;
+SQLHSTMT Tables_hstmt;
+SQLHENV Tables_henv;
+SQLHDESC Tables_hdesc;
+
+void Tables_DisplayError(SQLSMALLINT Tables_HandleType,
+ SQLHSTMT Tables_InputHandle);
+
+/**
+ * Test to retrieve a list of table names stored in aspecified
+ * data source's system
+ *
+ * -# Normal case test: print out the table name in the data result set
+ * @return Zero, if test succeeded
+ */
+
+int SQLTablesTest()
+{
+ SQLRETURN Tables_retcode;
+ SQLCHAR Tables_Name[Tables_NAME_LEN], Tables_Phone[Tables_PHONE_LEN];
+ SQLCHAR Tables_Address[Tables_ADDRESS_LEN];
+ SQLINTEGER Tables_CustID;
+
+ ndbout << endl << "Start SQLTables Testing" << endl;
+
+ //*******************************************************************
+ //** hstmt
+ //** Execute a statement to retrieve rows from the Customers table **
+ //** We can create the table and insert rows into Customers **
+ //*******************************************************************
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ Tables_retcode = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &Tables_henv);
+
+if (Tables_retcode == SQL_SUCCESS || Tables_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ Tables_retcode = SQLSetEnvAttr(Tables_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+if (Tables_retcode == SQL_SUCCESS || Tables_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ Tables_retcode = SQLAllocHandle(SQL_HANDLE_DBC,
+ Tables_henv,
+ &Tables_hdbc);
+
+if (Tables_retcode == SQL_SUCCESS || Tables_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ Tables_retcode = SQLConnect(Tables_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+if (Tables_retcode == SQL_SUCCESS || Tables_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ Tables_retcode = SQLAllocHandle(SQL_HANDLE_STMT,
+ Tables_hdbc,
+ &Tables_hstmt);
+
+if (Tables_retcode == SQL_SUCCESS || Tables_retcode == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //**************************************************************
+ //** Retrieve information about the tables in the data source **
+ //**************************************************************
+ Tables_retcode = SQLTables(Tables_hstmt,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ (SQLCHAR *)"%",
+ 128,
+ (SQLCHAR *)"TABLES",
+ 128);
+
+ ndbout <<"Tables_retcode = SQLTables() =" << Tables_retcode;
+
+ if (Tables_retcode == SQL_ERROR)
+ Tables_DisplayError(SQL_HANDLE_STMT, Tables_hstmt);
+ //*******************************************
+ //** Bind columns 3 in the result data set **
+ //*******************************************
+
+ Tables_retcode = SQLBindCol(Tables_hstmt,
+ 3,
+ SQL_C_CHAR,
+ &Tables_Name,
+ Tables_NAME_LEN,
+ NULL);
+
+ ndbout <<"Tables_retcode = SQLBindCol() =" << Tables_retcode;
+
+ //**********************************************
+ //* Fetch and print out data in the result On **
+ //* an error, display a message and exit **
+ //**********************************************
+
+ Tables_retcode = SQLFetch(Tables_hstmt);
+
+
+ ndbout <<"Tables_retcode = SQLFetch() =" << Tables_retcode;
+
+ ndbout << endl << "Tables_retcode = SQLFetch(Tables_hstmt) = "
+ << Tables_retcode << endl;
+
+ if (Tables_retcode == SQL_ERROR)
+ {
+ Tables_DisplayError(SQL_HANDLE_STMT, Tables_hstmt);
+ return NDBT_FAILED;
+ }
+ else if (Tables_retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "Table Name = " << (char *)Tables_Name << endl;
+ Tables_DisplayError(SQL_HANDLE_STMT, Tables_hstmt);
+ }
+ else if (Tables_retcode == SQL_NO_DATA)
+ Tables_DisplayError(SQL_HANDLE_STMT, Tables_hstmt);
+ else
+ {
+ ndbout << "TableName = " << (char *)Tables_Name << endl;
+ Tables_DisplayError(SQL_HANDLE_STMT, Tables_hstmt);
+ }
+
+ // *********************************
+ // ** Disconnect and Free Handles **
+ // *********************************
+ SQLDisconnect(Tables_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, Tables_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, Tables_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, Tables_henv);
+
+ return NDBT_OK;
+}
+
+void Tables_DisplayError(SQLSMALLINT Tables_HandleType,
+ SQLHSTMT Tables_InputHandle)
+{
+ SQLINTEGER NativeError;
+ SQLSMALLINT Tables_i = 1;
+ SQLRETURN Tables__SQLSTATEs;
+ SQLCHAR Tables_Sqlstate[5];
+ SQLCHAR Tables_Msg[Tables_SQL_MAXIMUM_MESSAGE_LENGTH];
+ SQLSMALLINT Tables_MsgLen;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((Tables__SQLSTATEs = SQLGetDiagRec(Tables_HandleType,
+ Tables_InputHandle,
+ Tables_i,
+ Tables_Sqlstate,
+ &NativeError,
+ Tables_Msg,
+ sizeof(Tables_Msg),
+ &Tables_MsgLen)
+ ) != SQL_NO_DATA)
+ {
+
+ ndbout << "the HandleType is:" << Tables_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)Tables_InputHandle << endl;
+ ndbout << "the Tables_Msg is: " << (char *) Tables_Msg << endl;
+ ndbout << "the output state is:" << (char *)Tables_Sqlstate << endl;
+
+ Tables_i ++;
+ break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
diff --git a/storage/ndb/test/odbc/client/SQLTransactTest.cpp b/storage/ndb/test/odbc/client/SQLTransactTest.cpp
new file mode 100644
index 00000000000..e9abe42129d
--- /dev/null
+++ b/storage/ndb/test/odbc/client/SQLTransactTest.cpp
@@ -0,0 +1,305 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+ /**
+ * @file SQLTransactTest.cpp
+ */
+#include <common.hpp>
+#define STR_MESSAGE_LENGTH 200
+#define STR_NAME_LEN 20
+#define STR_PHONE_LEN 20
+#define STR_ADDRESS_LEN 20
+using namespace std;
+
+SQLHDBC STR_hdbc;
+SQLHSTMT STR_hstmt;
+SQLHENV STR_henv;
+SQLHDESC STR_hdesc;
+
+void Transact_DisplayError(SQLSMALLINT STR_HandleType,
+ SQLHSTMT STR_InputHandle);
+
+int STR_Display_Result(SQLHSTMT EXDR_InputHandle);
+
+/**
+ * Test:
+ * -#Test to request a commit or a rollback operation for
+ * all active transactions associated with a specific
+ * environment or connection handle
+ *
+ * @return Zero, if test succeeded
+ */
+
+int SQLTransactTest()
+{
+ SQLRETURN STR_ret;
+
+ ndbout << endl << "Start SQLTransact Testing" << endl;
+
+ //************************************
+ //** Allocate An Environment Handle **
+ //************************************
+ STR_ret = SQLAllocHandle(SQL_HANDLE_ENV,
+ SQL_NULL_HANDLE,
+ &STR_henv);
+
+ if (STR_ret == SQL_SUCCESS || STR_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated an environment Handle!" << endl;
+
+ //*********************************************
+ //** Set the ODBC application Version to 3.x **
+ //*********************************************
+ STR_ret = SQLSetEnvAttr(STR_henv,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ SQL_IS_UINTEGER);
+
+ if (STR_ret == SQL_SUCCESS || STR_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Set the ODBC application Version to 3.x!" << endl;
+
+ //**********************************
+ //** Allocate A Connection Handle **
+ //**********************************
+
+ STR_ret = SQLAllocHandle(SQL_HANDLE_DBC,
+ STR_henv,
+ &STR_hdbc);
+
+ if (STR_ret == SQL_SUCCESS || STR_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a connection Handle!" << endl;
+
+ // *******************
+ // ** Connect to DB **
+ // *******************
+ STR_ret = SQLConnect(STR_hdbc,
+ (SQLCHAR *) connectString(),
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS,
+ (SQLCHAR *) "",
+ SQL_NTS);
+
+ if (STR_ret == SQL_SUCCESS || STR_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Connected to DB : OK!" << endl;
+ else
+ {
+ ndbout << "Failure to Connect DB!" << endl;
+ return NDBT_FAILED;
+ }
+
+ //*******************************
+ //** Allocate statement handle **
+ //*******************************
+
+ STR_ret = SQLAllocHandle(SQL_HANDLE_STMT,
+ STR_hdbc,
+ &STR_hstmt);
+ if(STR_ret == SQL_SUCCESS || STR_ret == SQL_SUCCESS_WITH_INFO)
+ ndbout << "Allocated a statement handle!" << endl;
+
+ //********************************
+ //** Turn Manual-Commit Mode On **
+ //********************************
+ STR_ret = SQLSetConnectOption(STR_hdbc,
+ SQL_AUTOCOMMIT,
+ (UDWORD) SQL_AUTOCOMMIT_OFF);
+
+ //**********************************************
+ //** Prepare and Execute a prepared statement **
+ //**********************************************
+ STR_ret = SQLExecDirect(STR_hstmt,
+ (SQLCHAR*)"SELECT * FROM Customers",
+ SQL_NTS);
+
+ if (STR_ret == SQL_INVALID_HANDLE)
+ {
+ ndbout << "Handle Type is SQL_HANDLE_STMT, but SQL_INVALID_HANDLE"
+ << endl;
+ ndbout << "still appeared. Please check program" << endl;
+ }
+
+ if (STR_ret == SQL_ERROR || STR_ret == SQL_SUCCESS_WITH_INFO)
+ Transact_DisplayError(SQL_HANDLE_STMT, STR_hstmt);
+
+ //*************************
+ //** Display the results **
+ //*************************
+
+ STR_Display_Result(STR_hstmt);
+
+ //****************************
+ //** Commit the transaction **
+ //****************************
+ STR_ret = SQLTransact(STR_henv,
+ STR_hdbc,
+ SQL_COMMIT);
+
+ //****************
+ // Free Handles **
+ //****************
+ SQLDisconnect(STR_hdbc);
+ SQLFreeHandle(SQL_HANDLE_STMT, STR_hstmt);
+ SQLFreeHandle(SQL_HANDLE_DBC, STR_hdbc);
+ SQLFreeHandle(SQL_HANDLE_ENV, STR_henv);
+
+ return NDBT_OK;
+
+ }
+
+void Transact_DisplayError(SQLSMALLINT STR_HandleType,
+ SQLHSTMT STR_InputHandle)
+{
+ SQLCHAR STR_Sqlstate[5];
+ SQLINTEGER STR_NativeError;
+ SQLSMALLINT STR_i, STR_MsgLen;
+ SQLCHAR STR_Msg[STR_MESSAGE_LENGTH];
+ SQLRETURN SQLSTATEs;
+ STR_i = 1;
+
+ ndbout << "-------------------------------------------------" << endl;
+ ndbout << "Error diagnostics:" << endl;
+
+ while ((SQLSTATEs = SQLGetDiagRec(STR_HandleType,
+ STR_InputHandle,
+ STR_i,
+ STR_Sqlstate,
+ &STR_NativeError,
+ STR_Msg,
+ sizeof(STR_Msg),
+ &STR_MsgLen))
+ != SQL_NO_DATA) {
+
+ ndbout << "the HandleType is:" << STR_HandleType << endl;
+ ndbout << "the InputHandle is :" << (long)STR_InputHandle << endl;
+ ndbout << "the STR_Msg is: " << (char *) STR_Msg << endl;
+ ndbout << "the output state is:" << (char *)STR_Sqlstate << endl;
+
+ STR_i ++;
+ // break;
+ }
+ ndbout << "-------------------------------------------------" << endl;
+}
+
+int STR_Display_Result(SQLHSTMT STR_InputHandle)
+{
+ SQLRETURN STR_retcode;
+ unsigned long STR_CustID;
+ SQLCHAR STR_Name[STR_NAME_LEN], STR_Phone[STR_PHONE_LEN];
+ SQLCHAR STR_Address[STR_ADDRESS_LEN];
+
+ //*********************
+ //** Bind columns 1 **
+ //*********************
+ STR_retcode =SQLBindCol(STR_InputHandle,
+ 1,
+ SQL_C_ULONG,
+ &STR_CustID,
+ sizeof(STR_CustID),
+ NULL);
+ if (STR_retcode == SQL_ERROR)
+ {
+ ndbout << "Executing SQLBindCol, SQL_ERROR happened!" << endl;
+ Transact_DisplayError(SQL_HANDLE_STMT, STR_InputHandle);
+ return NDBT_FAILED;
+ }
+
+ //*********************
+ //** Bind columns 2 **
+ //*********************
+
+ STR_retcode =SQLBindCol(STR_InputHandle,
+ 2,
+ SQL_C_CHAR,
+ &STR_Name,
+ STR_NAME_LEN,
+ NULL);
+ if (STR_retcode == SQL_ERROR)
+ {
+ ndbout << "Executing SQLBindCol, SQL_ERROR happened!" << endl;
+ Transact_DisplayError(SQL_HANDLE_STMT, STR_InputHandle);
+ return NDBT_FAILED;
+ }
+
+ //*********************
+ //** Bind columns 3 **
+ //*********************
+
+ STR_retcode = SQLBindCol(STR_InputHandle,
+ 3,
+ SQL_C_CHAR,
+ &STR_Address,
+ STR_ADDRESS_LEN,
+ NULL);
+
+ if (STR_retcode == SQL_ERROR)
+ {
+ ndbout << "Executing SQLBindCol, SQL_ERROR happened!" << endl;
+ Transact_DisplayError(SQL_HANDLE_STMT, STR_InputHandle);
+ return NDBT_FAILED;
+ }
+
+ //*********************
+ //** Bind columns 4 **
+ //*********************
+
+ STR_retcode = SQLBindCol(STR_InputHandle,
+ 4,
+ SQL_C_CHAR,
+ &STR_Phone,
+ STR_PHONE_LEN,
+ NULL);
+
+ if (STR_retcode == SQL_ERROR)
+ {
+ ndbout << "Executing SQLBindCol, SQL_ERROR happened!" << endl;
+ Transact_DisplayError(SQL_HANDLE_STMT, STR_InputHandle);
+ return NDBT_FAILED;
+ }
+
+ //*****************************************
+ //* Fetch and print each row of data. On **
+ //* an error, display a message and exit **
+ //*****************************************
+
+ if (STR_retcode != SQL_ERROR)
+ STR_retcode = SQLFetch(STR_InputHandle);
+
+ ndbout << endl << "STR_retcode = SQLFetch(STR_InputHandle) = "
+ << STR_retcode << endl;
+
+ if (STR_retcode == SQL_ERROR)
+ {
+ ndbout << "Executing SQLFetch, SQL_ERROR happened!" << endl;
+ Transact_DisplayError(SQL_HANDLE_STMT, STR_InputHandle);
+ return NDBT_FAILED;
+ }
+ else if (STR_retcode == SQL_SUCCESS_WITH_INFO)
+ {
+ ndbout << "CustID = " << (int)STR_CustID << endl;
+ ndbout << "Name = " << (char *)STR_Name << endl;
+ ndbout << "Address = " << (char *)STR_Address << endl;
+ ndbout << "Phone = " << (char *)STR_Phone << endl;
+ Transact_DisplayError(SQL_HANDLE_STMT, STR_InputHandle);
+ }
+ else
+ {
+ ndbout << "CustID = " << (int)STR_CustID << endl;
+ ndbout << "Name = " << (char *)STR_Name << endl;
+ ndbout << "Address = " << (char *)STR_Address << endl;
+ ndbout << "Phone = " << (char *)STR_Phone << endl;
+ }
+ return 0;
+}
diff --git a/storage/ndb/test/odbc/client/common.hpp b/storage/ndb/test/odbc/client/common.hpp
new file mode 100644
index 00000000000..236decf1b95
--- /dev/null
+++ b/storage/ndb/test/odbc/client/common.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 */
+
+#include <NdbOut.hpp>
+#include <NDBT.hpp>
+#include <sqlext.h>
+#include <stdio.h>
+#include <string.h>
+#define FAILURE(msg) { ndbout << "TEST FAILURE: " << msg << endl \
+ << "-- File: " << __FILE__ << ", Line: " << __LINE__ << endl; \
+ return NDBT_FAILED; }
+
+char* connectString();
+
+int SQLFetchTest();
+int SQLDisconnectTest();
+int SQLTablesTest();
+int SQLBindColTest();
+int SQLGetInfoTest();
+int SQLGetTypeInfoTest();
+int SQLGetDataTest();
+int SQLGetFunctionsTest();
+int SQLColAttributeTest();
+int SQLColAttributeTest1();
+int SQLColAttributeTest2();
+int SQLColAttributeTest3();
+int SQLGetDiagRecSimpleTest();
+int SQLDriverConnectTest();
+int SQLAllocEnvTest();
+int SQLFreeHandleTest();
+int SQLFetchScrollTest();
+int SQLFetchTest();
+int SQLGetDescRecTest();
+int SQLSetDescFieldTest();
+int SQLGetDescFieldTest();
+int SQLSetDescRecTest();
+int SQLSetCursorNameTest();
+int SQLGetCursorNameTest();
+int SQLRowCountTest();
+int SQLGetInfoTest();
+int SQLTransactTest();
+int SQLEndTranTest();
+int SQLNumResultColsTest();
+int SQLGetTypeInfoTest();
+int SQLGetFunctionsTest();
+int SQLDescribeColTest();
+int SQLAllocHandleTest();
+int SQLCancelTest();
+int SQLCloseCursorTest();
+int SQLConnectTest();
+int SQLDisconnectTest();
+int SQLExecDirectTest();
+int SQLExecuteTest();
+int SQLFreeHandleTest();
+int SQLFreeStmtTest();
+int SQLGetConnectAttrTest();
+int SQLGetEnvAttrTest();
+int SQLGetStmtAttrTest();
+int SQLMoreResultsTest();
+int SQLPrepareTest();
+int SQLSetConnectAttrTest();
+int SQLSetEnvAttrTest();
+int SQLSetStmtAttrTest();
+
+// int NDBT_ALLOCHANDLE();
+// int NDBT_ALLOCHANDLE_HDBC();
+// int NDBT_SQLPrepare();
+// int NDBT_SQLConnect();
diff --git a/storage/ndb/test/odbc/client/main.cpp b/storage/ndb/test/odbc/client/main.cpp
new file mode 100644
index 00000000000..b202b6de111
--- /dev/null
+++ b/storage/ndb/test/odbc/client/main.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 */
+
+ /**
+ * @file main.cpp
+ */
+
+#include <common.hpp>
+
+/**
+ * main ODBC Tests
+ *
+ * Tests main ODBC functions.
+ */
+
+#include <common.hpp>
+
+int check = NDBT_OK;
+static char* myConnectString;
+
+char* connectString()
+{
+ return myConnectString;
+}
+
+int main(int argc, char** argv)
+{
+
+ if (argc != 3) {
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ myConnectString = argv[1];
+
+ if ( strcmp(argv[2], "help") == 0 )
+ {
+ ndbout << "Number of Testing Program " << " Name of Testing Program" << endl;
+ ndbout << " 1 SQLGetDataTest()" << endl;
+ ndbout << " 2 SQLTablesTest()" << endl;
+ ndbout << " 3 SQLGetFunctionsTest()" << endl;
+ ndbout << " 4 SQLGetInfoTest()" << endl;
+ ndbout << " 5 SQLGetTypeInfoTest()" << endl;
+ ndbout << " 6 SQLDisconnectTest()" << endl;
+ ndbout << " 7 SQLFetchTest()" << endl;
+ ndbout << " 8 SQLRowCountTest()" << endl;
+ ndbout << " 9 SQLGetCursorNameTest()" << endl;
+ ndbout << " 10 SQLCancelTest()" << endl;
+ ndbout << " 11 SQLTransactTest()" << endl;
+ ndbout << " 12 SQLSetCursorNameTest()" << endl;
+ ndbout << " 13 SQLNumResultColsTest()" << endl;
+ ndbout << " 14 SQLDescribeColTest()" << endl;
+ ndbout << " 15 SQLExecDirectTest()" << endl;
+ ndbout << " 16 SQLColAttributeTest3()" << endl;
+ ndbout << " 17 SQLColAttributeTest2()" << endl;
+ ndbout << " 18 SQLColAttributeTest1()" << endl;
+ ndbout << " 19 SQLColAttributeTest()" << endl;
+ ndbout << " 20 SQLBindColTest()" << endl;
+ ndbout << " 21 SQLGetDiagRecSimpleTest()" << endl;
+ ndbout << " 22 SQLConnectTest()" << endl;
+ ndbout << " 23 SQLPrepareTest()" << endl;
+ }
+ else
+ {
+
+ ndbout << endl << "Executing Files Name = " << argv[0] << endl;
+ ndbout << "The Number of testing program = " << argv[2] << endl;
+
+ int i = atoi(argv[2]);
+ switch (i) {
+ case 1:
+ if (check == NDBT_OK) check = SQLGetDataTest();
+ break;
+ case 2:
+ if (check == NDBT_OK) check = SQLTablesTest();
+ break;
+ case 3:
+ if (check == NDBT_OK) check = SQLGetFunctionsTest();
+ break;
+ case 4:
+ if (check == NDBT_OK) check = SQLGetInfoTest();
+ break;
+ case 5:
+ if (check == NDBT_OK) check = SQLGetTypeInfoTest();
+ break;
+ case 6:
+ if (check == NDBT_OK) check = SQLDisconnectTest();
+ break;
+ case 7:
+ if (check == NDBT_OK) check = SQLFetchTest();
+ break;
+ case 8:
+ if (check == NDBT_OK) check = SQLRowCountTest();
+ break;
+ case 9:
+ if (check == NDBT_OK) check = SQLGetCursorNameTest();
+ break;
+ case 10:
+ if (check == NDBT_OK) check = SQLCancelTest();
+ break;
+ case 11:
+ if (check == NDBT_OK) check = SQLTransactTest();
+ break;
+ case 12:
+ if (check == NDBT_OK) check = SQLSetCursorNameTest();
+ break;
+ case 13:
+ if (check == NDBT_OK) check = SQLNumResultColsTest();
+ break;
+ case 14:
+ if (check == NDBT_OK) check = SQLDescribeColTest();
+ break;
+ case 15:
+ if (check == NDBT_OK) check = SQLExecDirectTest();
+ break;
+ case 16:
+ if (check == NDBT_OK) check = SQLColAttributeTest3();
+ break;
+ case 17:
+ if (check == NDBT_OK) check = SQLColAttributeTest2();
+ break;
+ case 18:
+ if (check == NDBT_OK) check = SQLColAttributeTest1();
+ break;
+ case 19:
+ if (check == NDBT_OK) check = SQLColAttributeTest();
+ break;
+ case 20:
+ if (check == NDBT_OK) check = SQLBindColTest();
+ break;
+ case 21:
+ if (check == NDBT_OK) check = SQLGetDiagRecSimpleTest();
+ break;
+ case 22:
+ if (check == NDBT_OK) check = SQLConnectTest();
+ break;
+ case 23:
+ if (check == NDBT_OK) check = SQLPrepareTest();
+ break;
+ }
+ }
+
+ return NDBT_ProgramExit(check);
+}
+
+
+
diff --git a/storage/ndb/test/odbc/dm-iodbc/Makefile b/storage/ndb/test/odbc/dm-iodbc/Makefile
new file mode 100644
index 00000000000..ad0f0d39f5f
--- /dev/null
+++ b/storage/ndb/test/odbc/dm-iodbc/Makefile
@@ -0,0 +1,38 @@
+include .defs.mk
+
+TYPE = *
+
+BIN_TARGET = testOdbcDMi
+
+SOURCES = testOdbcDMi.cpp
+
+CCFLAGS_LOC += \
+ -I$(NDB_TOP)/include \
+ -I$(NDB_TOP)/include/ndbapi \
+ -I$(NDB_TOP)/include/portlib \
+ -I$(NDB_TOP)/include/util \
+ -I$(NDB_TOP)/test/include
+
+CCFLAGS_WARNINGS += -Wno-unused -Wno-sign-compare
+
+CCFLAGS_TOP += -DHAVE_LONG_LONG -DiODBC
+
+BIN_TARGET_LIBS = NDBT general portlib
+
+ifeq ($(NDB_OS),SOLARIS)
+CCFLAGS_TOP += -DDMALLOC
+LIBS_LOC += -L/usr/local/opt/iODBC/lib
+LIBS_LOC += -R/usr/local/opt/iODBC/lib
+BIN_TARGET_LIBS += iodbc
+BIN_TARGET_LIBS += dmallocthcxx
+endif
+
+ifeq ($(NDB_OS),LINUX)
+BIN_TARGET_LIBS_DIRS += /usr/local/opt/iODBC/lib
+BIN_TARGET_LIBS += iodbc
+endif
+
+include $(NDB_TOP)/Epilogue.mk
+
+testOdbcDMi.cpp:
+ ln -s ../driver/testOdbcDriver.cpp $@
diff --git a/storage/ndb/test/odbc/dm-unixodbc/Makefile b/storage/ndb/test/odbc/dm-unixodbc/Makefile
new file mode 100644
index 00000000000..50d8e3b5e05
--- /dev/null
+++ b/storage/ndb/test/odbc/dm-unixodbc/Makefile
@@ -0,0 +1,39 @@
+include .defs.mk
+
+TYPE = *
+
+BIN_TARGET = testOdbcDMu
+
+SOURCES = testOdbcDMu.cpp
+
+CCFLAGS_LOC += \
+ -I$(NDB_TOP)/include \
+ -I$(NDB_TOP)/include/ndbapi \
+ -I$(NDB_TOP)/include/portlib \
+ -I$(NDB_TOP)/include/util \
+ -I$(NDB_TOP)/test/include
+
+CCFLAGS_WARNINGS += -Wno-unused -Wno-sign-compare
+
+CCFLAGS_TOP += -DHAVE_LONG_LONG -DunixODBC
+
+BIN_TARGET_LIBS = NDBT general portlib
+
+ifeq ($(NDB_OS),SOLARIS)
+CCFLAGS_TOP += -DDMALLOC
+LIBS_LOC += -L/usr/local/lib
+BIN_TARGET_LIBS += odbc odbcinst
+BIN_TARGET_LIBS += dmallocthcxx
+endif
+
+ifeq ($(NDB_OS),LINUX)
+BIN_TARGET_LIBS += odbc odbcinst
+BIN_TARGET_LIBS_DIRS += .
+dummy := $(shell [ ! -f /usr/lib/libodbc.so ] || ln -sf /usr/lib/libodbc.so.1 libodbc.so)
+dummy := $(shell [ ! -f /usr/lib/libodbcinst.so ] || ln -sf /usr/lib/libodbcinst.so.1 libodbcinst.so)
+endif
+
+include $(NDB_TOP)/Epilogue.mk
+
+testOdbcDMu.cpp:
+ ln -s ../driver/testOdbcDriver.cpp $@
diff --git a/storage/ndb/test/odbc/driver/Makefile b/storage/ndb/test/odbc/driver/Makefile
new file mode 100644
index 00000000000..5cf83d73106
--- /dev/null
+++ b/storage/ndb/test/odbc/driver/Makefile
@@ -0,0 +1,30 @@
+include .defs.mk
+
+TYPE = *
+
+BIN_TARGET = testOdbcDriver
+
+SOURCES = testOdbcDriver.cpp
+
+CCFLAGS_LOC += \
+ -I$(NDB_TOP)/include \
+ -I$(NDB_TOP)/include/ndbapi \
+ -I$(NDB_TOP)/include/portlib \
+ -I$(NDB_TOP)/include/util \
+ -I$(NDB_TOP)/test/include \
+ -I/usr/local/include
+
+CCFLAGS_WARNINGS += -Wno-unused -Wno-sign-compare -Wformat
+
+CCFLAGS_TOP += -DHAVE_LONG_LONG -DndbODBC
+
+BIN_TARGET_LIBS = NDBT NDB_ODBC
+
+ifeq ($(NDB_OS),SOLARIS)
+BIN_TARGET_LIBS += dmallocthcxx
+CCFLAGS_TOP += -DDMALLOC
+endif
+
+include $(NDB_TOP)/Epilogue.mk
+
+$(BIN_DIR)$(BIN_TARGET): Makefile
diff --git a/storage/ndb/test/odbc/driver/testOdbcDriver.cpp b/storage/ndb/test/odbc/driver/testOdbcDriver.cpp
new file mode 100644
index 00000000000..d3b3802ebe1
--- /dev/null
+++ b/storage/ndb/test/odbc/driver/testOdbcDriver.cpp
@@ -0,0 +1,4964 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ * testOdbcDriver
+ *
+ * Test of ODBC and SQL using a fixed set of tables.
+ */
+
+#include <ndb_global.h>
+#undef test
+#include <ndb_version.h>
+#include <kernel/ndb_limits.h>
+#include <Bitmask.hpp>
+#include <kernel/AttributeList.hpp>
+#ifdef ndbODBC
+#include <NdbApi.hpp>
+#endif
+#include <sqlext.h>
+
+#undef BOOL
+
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbThread.h>
+#include <NdbMutex.h>
+#include <NdbCondition.h>
+#include <NdbTick.h>
+#include <NdbSleep.h>
+
+#ifdef ndbODBC
+#include <NdbTest.hpp>
+#else
+#define NDBT_OK 0
+#define NDBT_FAILED 1
+#define NDBT_WRONGARGS 2
+static int
+NDBT_ProgramExit(int rcode)
+{
+ const char* rtext = "Unknown";
+ switch (rcode) {
+ case NDBT_OK:
+ rtext = "OK";
+ break;
+ case NDBT_FAILED:
+ rtext = "Failed";
+ break;
+ case NDBT_WRONGARGS:
+ rtext = "Wrong arguments";
+ break;
+ };
+ ndbout_c("\nNDBT_ProgramExit: %d - %s\n", rcode, rtext);
+ return rcode;
+}
+#endif
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+#define arraySize(x) (sizeof(x)/sizeof(x[0]))
+
+#define SQL_ATTR_NDB_TUPLES_FETCHED 66601
+
+// options
+
+#define MAX_THR 128 // max threads
+
+struct Opt {
+ const char* m_name[100];
+ unsigned m_namecnt;
+ bool m_core;
+ unsigned m_depth;
+ const char* m_dsn;
+ unsigned m_errs;
+ const char* m_fragtype;
+ unsigned m_frob;
+ const char* m_home;
+ unsigned m_loop;
+ bool m_nogetd;
+ bool m_noputd;
+ bool m_nosort;
+ unsigned m_scale;
+ bool m_serial;
+ const char* m_skip[100];
+ unsigned m_skipcnt;
+ unsigned m_subloop;
+ const char* m_table;
+ unsigned m_threads;
+ unsigned m_trace;
+ unsigned m_v;
+ Opt() :
+ m_namecnt(0),
+ m_core(false),
+ m_depth(5),
+ m_dsn("NDB"),
+ m_errs(0),
+ m_fragtype(0),
+ m_frob(0),
+ m_home(0),
+ m_loop(1),
+ m_nogetd(false),
+ m_noputd(false),
+ m_nosort(false),
+ m_scale(100),
+ m_serial(false),
+ m_skipcnt(0),
+ m_subloop(1),
+ m_table(0),
+ m_threads(1),
+ m_trace(0),
+ m_v(1) {
+ for (unsigned i = 0; i < arraySize(m_name); i++)
+ m_name[i] = 0;
+ for (unsigned i = 0; i < arraySize(m_skip); i++)
+ m_skip[i] = 0;
+ }
+};
+
+static Opt opt;
+
+static void listCases();
+static void listTables();
+static void printusage()
+{
+ Opt d;
+ ndbout
+ << "usage: testOdbcDriver [options]" << endl
+ << "-case name run only named tests (substring match - can be repeated)" << endl
+ << "-core dump core on failure" << endl
+ << "-depth N join depth - default " << d.m_depth << endl
+ << "-dsn string data source name - default " << d.m_dsn << endl
+ << "-errs N allow N errors before quitting - default " << d.m_errs << endl
+ << "-fragtype t fragment type single/small/medium/large" << d.m_errs << endl
+ << "-frob X case-dependent tweak (number)" << endl
+ << "-home dir set NDB_HOME (contains Ndb.cfg)" << endl
+ << "-loop N loop N times (0 = forever) - default " << d.m_loop << endl
+ << "-nogetd do not use SQLGetData - default " << d.m_nogetd << endl
+ << "-noputd do not use SQLPutData - default " << d.m_noputd << endl
+ << "-nosort no order-by in verify scan (checks non-Pk values only)" << endl
+ << "-scale N row count etc - default " << d.m_scale << endl
+ << "-serial run multi-threaded test cases one at a time" << endl
+ << "-skip name skip named tests (substring match - can be repeated)" << endl
+ << "-subloop N loop count per case (same threads) - default " << d.m_subloop << endl
+ << "-table T do only table T (table name on built-in list)" << endl
+ << "-threads N number of threads (max " << MAX_THR << ") - default " << d.m_threads << endl
+ << "-trace N trace in NDB ODBC driver - default " << d.m_trace << endl
+ << "-v N verbosity - default " << d.m_v << endl
+ ;
+ listCases();
+ listTables();
+}
+
+static void
+fatal(const char* fmt, ...)
+{
+ va_list ap;
+ char buf[200];
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ ndbout << buf << endl;
+ if (opt.m_errs != 0) {
+ opt.m_errs--;
+ return;
+ }
+ if (opt.m_core)
+ abort();
+ NDBT_ProgramExit(NDBT_FAILED);
+ exit(1);
+}
+
+static void
+cleanprint(const char* s, unsigned n)
+{
+ for (unsigned i = 0; i < n; i++) {
+ char b[10];
+ if (0x20 < s[i] && s[i] <= 0x7e)
+ sprintf(b, "%c", s[i]);
+ else
+ sprintf(b, "\\%02x", (unsigned)s[i]);
+ ndbout << b;
+ }
+}
+
+// global mutex
+static NdbMutex my_mutex = NDB_MUTEX_INITIALIZER;
+static void lock_mutex() { NdbMutex_Lock(&my_mutex); }
+static void unlock_mutex() { NdbMutex_Unlock(&my_mutex); }
+
+// semaphore zeroed before each call to a test routine
+static unsigned my_sema = 0;
+
+// print mutex
+static NdbMutex out_mutex = NDB_MUTEX_INITIALIZER;
+static NdbOut& lock(NdbOut& out) { NdbMutex_Lock(&out_mutex); return out; }
+static NdbOut& unlock(NdbOut& out) { NdbMutex_Unlock(&out_mutex); return out; }
+
+static unsigned
+urandom(unsigned n)
+{
+ assert(n != 0);
+ unsigned i = random();
+ return i % n;
+}
+
+// test cases
+
+struct Test;
+
+struct Case {
+ enum Mode {
+ Single = 1, // single thread
+ Serial = 2, // all threads but one at a time
+ Thread = 3 // all threads in parallel
+ };
+ const char* m_name;
+ void (*m_func)(Test& test);
+ Mode m_mode;
+ unsigned m_stuff;
+ const char* m_desc;
+ Case(const char* name, void (*func)(Test& test), Mode mode, unsigned stuff, const char* desc) :
+ m_name(name),
+ m_func(func),
+ m_mode(mode),
+ m_stuff(stuff),
+ m_desc(desc) {
+ }
+ const char* modename() const {
+ const char* s = "?";
+ if (m_mode == Case::Single)
+ return "Single";
+ if (m_mode == Case::Serial)
+ return "Serial";
+ if (m_mode == Case::Thread)
+ return "Thread";
+ return "?";
+ }
+ bool matchcase() const {
+ if (opt.m_namecnt == 0)
+ return ! skipcase();
+ for (unsigned i = 0; i < opt.m_namecnt; i++) {
+ if (strstr(m_name, opt.m_name[i]) != 0)
+ return ! skipcase();
+ }
+ return false;
+ }
+private:
+ bool skipcase() const {
+ for (unsigned i = 0; i < opt.m_skipcnt; i++) {
+ if (strstr(m_name, opt.m_skip[i]) != 0)
+ return true;
+ }
+ return false;
+ }
+};
+
+// calculate values
+
+struct Calc {
+ enum { m_mul = 1000000 };
+ unsigned m_no;
+ unsigned m_base;
+ unsigned m_salt; // modifies non-PK values
+ bool m_const; // base non-PK values on PK of row 0
+ Calc(unsigned no) :
+ m_no(no),
+ m_salt(0),
+ m_const(false) {
+ m_base = m_no * m_mul;
+ }
+ void calcPk(unsigned rownum, char* v, unsigned n) const {
+ char b[10];
+ sprintf(b, "%08x", m_base + rownum);
+ for (unsigned i = 0; i < n; i++) {
+ char c = i < n - 1 ? b[i % 8] : 0;
+ v[i] = c;
+ }
+ }
+ void calcPk(unsigned rownum, long* v) const {
+ *v = m_base + rownum;
+ }
+ void hashPk(unsigned* hash, const char* v, unsigned n) const {
+ for (unsigned i = 0; i < n; i++) {
+ *hash ^= (v[i] << i);
+ }
+ }
+ void hashPk(unsigned* hash, long v) const {
+ *hash ^= v;
+ }
+ void calcNk(unsigned hash, char* v, unsigned n, SQLINTEGER* ind, bool null) const {
+ unsigned m = hash % n;
+ for (unsigned i = 0; i < n; i++) {
+ char c = i < m ? 'a' + (hash + i) % ('z' - 'a' + 1) : i < n - 1 ? ' ' : 0;
+ v[i] = c;
+ }
+ *ind = null && hash % 9 == 0 ? SQL_NULL_DATA : SQL_NTS;
+ }
+ void calcNk(unsigned hash, long* v, SQLINTEGER* ind, bool null) const {
+ *v = long(hash);
+ *ind = null && hash % 7 == 0 ? SQL_NULL_DATA : 0;
+ }
+ void calcNk(unsigned hash, double* v, SQLINTEGER* ind, bool null) const {
+ *v = long(hash) / 1000.0;
+ *ind = null && hash % 5 == 0 ? SQL_NULL_DATA : 0;
+ }
+ bool verify(const char* v1, SQLINTEGER ind1, const char* v2, SQLINTEGER ind2, unsigned n) {
+ if (ind1 == SQL_NULL_DATA && ind2 == SQL_NULL_DATA)
+ return true;
+ if (ind1 != SQL_NULL_DATA && ind2 != SQL_NULL_DATA)
+ if (memcmp(v1, v2, n) == 0)
+ return true;
+ if (ind1 == SQL_NULL_DATA)
+ v1 = "NULL";
+ if (ind2 == SQL_NULL_DATA)
+ v2 = "NULL";
+ ndbout << "verify failed: got ";
+ if (ind1 == SQL_NULL_DATA)
+ ndbout << "NULL";
+ else
+ cleanprint(v1, n);
+ ndbout << " != ";
+ if (ind2 == SQL_NULL_DATA)
+ ndbout << "NULL";
+ else
+ cleanprint(v2, n);
+ ndbout << endl;
+ return false;
+ }
+ bool verify(long v1, SQLINTEGER ind1, long v2, SQLINTEGER ind2) {
+ char buf1[40], buf2[40];
+ if (ind1 == SQL_NULL_DATA && ind2 == SQL_NULL_DATA)
+ return true;
+ if (ind1 != SQL_NULL_DATA && ind2 != SQL_NULL_DATA)
+ if (v1 == v2)
+ return true;
+ if (ind1 == SQL_NULL_DATA)
+ strcpy(buf1, "NULL");
+ else
+ sprintf(buf1, "%ld", v1);
+ if (ind2 == SQL_NULL_DATA)
+ strcpy(buf2, "NULL");
+ else
+ sprintf(buf2, "%ld", v2);
+ ndbout << "verify failed: got " << buf1 << " != " << buf2 << endl;
+ return false;
+ }
+ bool verify(double v1, SQLINTEGER ind1, double v2, SQLINTEGER ind2) {
+ char buf1[40], buf2[40];
+ if (ind1 == SQL_NULL_DATA && ind2 == SQL_NULL_DATA)
+ return true;
+ if (ind1 != SQL_NULL_DATA && ind2 != SQL_NULL_DATA)
+ if (fabs(v1 - v2) < 1) // XXX
+ return true;
+ if (ind1 == SQL_NULL_DATA)
+ strcpy(buf1, "NULL");
+ else
+ sprintf(buf1, "%.10f", v1);
+ if (ind2 == SQL_NULL_DATA)
+ strcpy(buf2, "NULL");
+ else
+ sprintf(buf2, "%.10f", v2);
+ ndbout << "verify failed: got " << buf1 << " != " << buf2 << endl;
+ return false;
+ }
+};
+
+#if defined(NDB_SOLARIS) || defined(NDB_LINUX) || defined(NDB_MACOSX)
+#define HAVE_SBRK
+#else
+#undef HAVE_SBRK
+#endif
+
+struct Timer {
+ Timer() :
+ m_cnt(0),
+ m_calls(0),
+ m_on(0),
+ m_msec(0)
+#ifndef NDB_WIN32
+ ,
+ m_brk(0),
+ m_incr(0)
+#endif
+ {
+ }
+ void timerOn() {
+ m_cnt = 0;
+ m_calls = 0;
+ m_on = NdbTick_CurrentMillisecond();
+#ifdef HAVE_SBRK
+ m_brk = (int)sbrk(0);
+#endif
+ }
+ void timerOff() {
+ m_msec = NdbTick_CurrentMillisecond() - m_on;
+ if (m_msec <= 0)
+ m_msec = 1;
+#ifdef HAVE_SBRK
+ m_incr = (int)sbrk(0) - m_brk;
+ if (m_incr < 0)
+ m_incr = 0;
+#endif
+ }
+ void timerCnt(unsigned cnt) {
+ m_cnt += cnt;
+ }
+ void timerCnt(const Timer& timer) {
+ m_cnt += timer.m_cnt;
+ m_calls += timer.m_calls;
+ }
+ friend NdbOut& operator<<(NdbOut& out, const Timer& timer) {
+ out << timer.m_cnt << " ( " << 1000 * timer.m_cnt / timer.m_msec << "/sec )";
+#ifdef HAVE_SBRK
+ out << " - " << timer.m_incr << " sbrk";
+ if (opt.m_namecnt != 0) { // per case meaningless if many cases
+ if (timer.m_cnt > 0)
+ out << " ( " << timer.m_incr / timer.m_cnt << "/cnt )";
+ }
+#endif
+ out << " - " << timer.m_calls << " calls";
+ return out;
+ }
+protected:
+ unsigned m_cnt; // count rows or whatever
+ unsigned m_calls; // count ODBC function calls
+ NDB_TICKS m_on;
+ unsigned m_msec;
+#ifdef HAVE_SBRK
+ int m_brk;
+ int m_incr;
+#endif
+};
+
+#define MAX_MESSAGE 500
+#define MAX_DIAG 20
+
+struct Diag {
+ char m_state[5+1];
+ SQLINTEGER m_native;
+ char m_message[MAX_MESSAGE];
+ unsigned m_flag; // temp use
+ Diag() {
+ strcpy(m_state, "00000");
+ m_native = 0;
+ memset(m_message, 0, sizeof(m_message));
+ m_flag = 0;
+ }
+ const char* text() {
+ snprintf(m_buf, sizeof(m_buf), "%s %d '%s'", m_state, (int)m_native, m_message);
+ return m_buf;
+ }
+ void getDiag(SQLSMALLINT type, SQLHANDLE handle, unsigned k, unsigned count) {
+ int ret;
+ SQLSMALLINT length = -1;
+ memset(m_message, 0, MAX_MESSAGE);
+ ret = SQLGetDiagRec(type, handle, k, (SQLCHAR*)m_state, &m_native, (SQLCHAR*)m_message, MAX_MESSAGE, &length);
+ if (k <= count && ret != SQL_SUCCESS)
+ fatal("SQLGetDiagRec %d of %d: return %d != SQL_SUCCESS", k, count, (int)ret);
+ if (k <= count && strlen(m_message) != length)
+ fatal("SQLGetDiagRec %d of %d: message length %d != %d", k, count, strlen(m_message), length);
+ if (k > count && ret != SQL_NO_DATA)
+ fatal("SQLGetDiagRec %d of %d: return %d != SQL_NO_DATA", k, count, (int)ret);
+ m_flag = 0;
+ }
+private:
+ char m_buf[MAX_MESSAGE];
+};
+
+struct Diags {
+ Diag m_diag[MAX_DIAG];
+ SQLINTEGER m_diagCount;
+ SQLINTEGER m_rowCount;
+ SQLINTEGER m_functionCode;
+ void getDiags(SQLSMALLINT type, SQLHANDLE handle) {
+ int ret;
+ m_diagCount = -1;
+ ret = SQLGetDiagField(type, handle, 0, SQL_DIAG_NUMBER, &m_diagCount, SQL_IS_INTEGER, 0);
+ if (ret == SQL_INVALID_HANDLE)
+ return;
+ if (ret != SQL_SUCCESS)
+ fatal("SQLGetDiagField: return %d != SQL_SUCCESS", (int)ret);
+ if (m_diagCount < 0 || m_diagCount > MAX_DIAG)
+ fatal("SQLGetDiagField: count %d", (int)m_diagCount);
+ for (unsigned k = 0; k < MAX_DIAG; k++) {
+ m_diag[k].getDiag(type, handle, k + 1, m_diagCount);
+ if (k == m_diagCount + 1)
+ break;
+ }
+ m_rowCount = -1;
+ m_functionCode = SQL_DIAG_UNKNOWN_STATEMENT;
+ if (type == SQL_HANDLE_STMT) {
+ ret = SQLGetDiagField(type, handle, 0, SQL_DIAG_ROW_COUNT, &m_rowCount, SQL_IS_INTEGER, 0);
+#ifndef iODBC
+ if (ret != SQL_SUCCESS)
+ fatal("SQLGetDiagField: return %d != SQL_SUCCESS", (int)ret);
+#endif
+ ret = SQLGetDiagField(type, handle, 0, SQL_DIAG_DYNAMIC_FUNCTION_CODE, &m_functionCode, SQL_IS_INTEGER, 0);
+ }
+ }
+ void showDiags() {
+ for (unsigned k = 0; 0 <= m_diagCount && k < m_diagCount; k++) {
+ Diag& diag = m_diag[k];
+ ndbout << "diag " << k + 1;
+ ndbout << (diag.m_flag ? " [*]" : " [ ]");
+ ndbout << " " << diag.text() << endl;
+ if (k > 10)
+ abort();
+ }
+ }
+};
+
+struct Exp {
+ int m_ret;
+ const char* m_state;
+ SQLINTEGER m_native;
+ Exp() : m_ret(SQL_SUCCESS), m_state(""), m_native(0) {}
+ Exp(int ret, const char* state) : m_ret(ret), m_state(state) {}
+};
+
+struct Test : Calc, Timer, Diags {
+ Test(unsigned no, unsigned loop) :
+ Calc(no),
+ m_loop(loop),
+ m_stuff(0),
+ m_perf(false),
+ ccp(0) {
+ exp(SQL_SUCCESS, 0, 0, true);
+ }
+ unsigned m_loop; // current loop
+ Exp m_expList[20]; // expected results
+ unsigned m_expCount;
+ int m_ret; // actual return code
+ int m_stuff; // the stuff of abuse
+ bool m_perf; // check no diags on success
+ const Case* ccp; // current case
+ void exp(int ret, const char* state, SQLINTEGER native, bool reset) {
+ if (reset)
+ m_expCount = 0;
+ unsigned i = m_expCount++;
+ assert(i < arraySize(m_expList) - 1);
+ m_expList[i].m_ret = ret;
+ m_expList[i].m_state = state == 0 ? "" : state;
+ m_expList[i].m_native = native;
+ }
+ void runCase(const Case& cc) {
+ ccp = &cc;
+ if (opt.m_v >= 3)
+ ndbout << cc.m_name << ": start" << endl;
+ m_rowCount = -1;
+ NDB_TICKS m_ms1 = NdbTick_CurrentMillisecond();
+ m_salt = m_loop | (16 << cc.m_stuff);
+ m_const = cc.m_stuff == 0;
+ m_stuff = cc.m_stuff;
+ (*cc.m_func)(*this);
+ NDB_TICKS m_ms2 = NdbTick_CurrentMillisecond();
+ }
+ void run(SQLSMALLINT type, SQLHANDLE handle, int line, int ret) {
+ m_calls++;
+ m_ret = ret;
+ if (m_perf && (m_ret == SQL_SUCCESS))
+ return;
+ m_diagCount = 0;
+ if (handle != SQL_NULL_HANDLE)
+ getDiags(type, handle);
+ if (m_diagCount <= 0 && (ret != SQL_SUCCESS && ret != SQL_INVALID_HANDLE && ret != SQL_NEED_DATA && ret != SQL_NO_DATA)) {
+ fatal("%s: thr %d line %d: ret=%d but no diag records", ccp->m_name, m_no, line, ret);
+ }
+ for (unsigned k = 0; 0 <= m_diagCount && k < m_diagCount; k++) {
+ Diag& diag = m_diag[k];
+ bool match = false;
+ for (unsigned i = 0; i < m_expCount; i++) {
+ if (strcmp(diag.m_state, m_expList[i].m_state) == 0 && (diag.m_native % 10000 == m_expList[i].m_native % 10000 || m_expList[i].m_native == -1)) {
+ match = true;
+ diag.m_flag = 0;
+ continue;
+ }
+ diag.m_flag = 1; // mark unexpected
+ }
+ if (! match) {
+ showDiags();
+ fatal("%s: thr %d line %d: unexpected diag [*] ret=%d cnt=%d", ccp->m_name, m_no, line, (int)ret, (int)m_diagCount);
+ }
+ }
+ bool match = false;
+ for (unsigned i = 0; i < m_expCount; i++) {
+ if (ret == m_expList[i].m_ret) {
+ match = true;
+ break;
+ }
+ }
+ if (! match) {
+ showDiags();
+ fatal("%s: thr %d line %d: ret=%d not expected", ccp->m_name, m_no, line, ret);
+ }
+ // reset expected to success
+ exp(SQL_SUCCESS, 0, 0, true);
+ }
+ void chk(SQLSMALLINT type, SQLHANDLE handle, int line, bool match, const char* fmt, ...) {
+ if (match)
+ return;
+ va_list ap;
+ va_start(ap, fmt);
+ char buf[500];
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ fatal("%s: thr %d line %d: check failed - %s", ccp->m_name, m_no, line, buf);
+ }
+};
+
+#define HNull 0, SQL_NULL_HANDLE, __LINE__
+#define HEnv(h) SQL_HANDLE_ENV, h, __LINE__
+#define HDbc(h) SQL_HANDLE_DBC, h, __LINE__
+#define HStmt(h) SQL_HANDLE_STMT, h, __LINE__
+#define HDesc(h) SQL_HANDLE_DESC, h, __LINE__
+
+// string support
+
+#define MAX_SQL 20000
+
+static void
+scopy(char*& ptr, const char* fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(ptr, fmt, ap);
+ va_end(ap);
+ ptr += strlen(ptr);
+}
+
+static bool
+blankeq(const char* s1, const char* s2, bool caseSensitive = false)
+{
+ unsigned n1 = strlen(s1);
+ unsigned n2 = strlen(s2);
+ unsigned i = 0;
+ char c1 = 0;
+ char c2 = 0;
+ while (i < n1 || i < n2) {
+ c1 = i < n1 ? s1[i] : 0x20;
+ if (! caseSensitive && 'a' <= c1 && c1 <= 'z')
+ c1 -= 'a' - 'A';
+ c2 = i < n2 ? s2[i] : 0x20;
+ if (! caseSensitive && 'a' <= c2 && c2 <= 'z')
+ c2 -= 'a' - 'A';
+ if (c1 != c2)
+ break;
+ i++;
+ }
+ return c1 == c2;
+}
+
+// columns and tables
+
+struct Col {
+ enum Type {
+ Char = SQL_CHAR,
+ Varchar = SQL_VARCHAR,
+ Int = SQL_INTEGER,
+ Bigint = SQL_BIGINT,
+ Real = SQL_REAL,
+ Double = SQL_DOUBLE
+ };
+ enum CType {
+ CChar = SQL_C_CHAR,
+ CLong = SQL_C_SLONG,
+ CDouble = SQL_C_DOUBLE
+ };
+ enum Cons {
+ Null, // nullable
+ NotNull, // not nullable
+ Pk // part of primary key
+ };
+ const char* m_name;
+ Type m_type;
+ unsigned m_length;
+ Cons m_cons;
+ CType m_ctype;
+ Col() :
+ m_type((Type)999) {
+ }
+ Col(const char* name, Type type, unsigned length, Cons cons, CType ctype) :
+ m_name(name),
+ m_type(type),
+ m_length(length),
+ m_cons(cons),
+ m_ctype(ctype) {
+ }
+ unsigned size() const {
+ switch (m_type) {
+ case Char:
+ case Varchar:
+ return m_length;
+ case Int:
+ return 4;
+ case Bigint:
+ return 8;
+ case Real:
+ return 4;
+ case Double:
+ return 8;
+ }
+ assert(false);
+ return 0;
+ }
+ unsigned csize() const { // size as char plus terminating null
+ switch (m_ctype) {
+ case CChar:
+ return m_length + 1;
+ case CLong:
+ return 12;
+ case CDouble:
+ return 24;
+ }
+ assert(false);
+ return 0;
+ }
+ void typespec(char*& ptr) const {
+ switch (m_type) {
+ case Char:
+ scopy(ptr, "char(%d)", m_length);
+ return;
+ case Varchar:
+ scopy(ptr, "varchar(%d)", m_length);
+ return;
+ case Int:
+ scopy(ptr, "int");
+ return;
+ case Bigint:
+ scopy(ptr, "bigint");
+ return;
+ case Real:
+ scopy(ptr, "real");
+ return;
+ case Double:
+ scopy(ptr, "float");
+ return;
+ }
+ assert(false);
+ }
+ SQLSMALLINT type() const {
+ return (SQLSMALLINT)m_type;
+ }
+ SQLSMALLINT ctype() const {
+ return (SQLSMALLINT)m_ctype;
+ }
+ void create(char*& ptr, bool pk) const {
+ scopy(ptr, "%s", m_name);
+ scopy(ptr, " ");
+ typespec(ptr);
+ if (m_cons == Pk && pk) {
+ scopy(ptr, " primary key");
+ }
+ if (m_cons == NotNull) {
+ scopy(ptr, " not null");
+ }
+ }
+};
+
+static Col ColUndef;
+
+struct Tab {
+ const char* m_name;
+ const Col* m_colList;
+ unsigned m_colCount;
+ unsigned m_pkCount;
+ unsigned* m_pkIndex;
+ unsigned m_nkCount;
+ unsigned* m_nkIndex;
+ char m_upperName[20];
+ Tab(const char* name, const Col* colList, unsigned colCount) :
+ m_name(name),
+ m_colList(colList),
+ m_colCount(colCount) {
+ m_pkCount = 0;
+ m_nkCount = 0;
+ for (unsigned i = 0; i < m_colCount; i++) {
+ const Col& col = m_colList[i];
+ if (col.m_cons == Col::Pk)
+ m_pkCount++;
+ else
+ m_nkCount++;
+ }
+ m_pkIndex = new unsigned[m_pkCount];
+ m_nkIndex = new unsigned[m_nkCount];
+ unsigned pk = 0;
+ unsigned nk = 0;
+ for (unsigned i = 0; i < m_colCount; i++) {
+ const Col& col = m_colList[i];
+ if (col.m_cons == Col::Pk)
+ m_pkIndex[pk++] = i;
+ else
+ m_nkIndex[nk++] = i;
+ }
+ assert(pk == m_pkCount && nk == m_nkCount);
+ strcpy(m_upperName, m_name);
+ for (char* p = m_upperName; *p != 0; p++) {
+ if ('a' <= *p && *p <= 'z')
+ *p -= 'a' - 'A';
+ }
+ }
+ ~Tab() {
+ delete[] m_pkIndex;
+ delete[] m_nkIndex;
+ }
+ void drop(char*& ptr) const {
+ scopy(ptr, "drop table %s", m_name);
+ }
+ void create(char*& ptr) const {
+ scopy(ptr, "create table %s (", m_name);
+ for (unsigned i = 0; i < m_colCount; i++) {
+ if (i > 0)
+ scopy(ptr, ", ");
+ const Col& col = m_colList[i];
+ col.create(ptr, m_pkCount == 1);
+ }
+ if (m_pkCount != 1) {
+ scopy(ptr, ", primary key (");
+ for (unsigned i = 0; i < m_pkCount; i++) {
+ const Col& col = m_colList[m_pkIndex[i]];
+ if (i > 0)
+ scopy(ptr, ", ");
+ scopy(ptr, "%s", col.m_name);
+ }
+ scopy(ptr, ")");
+ }
+ scopy(ptr, ")");
+ }
+ void wherePk(char*& ptr) const {
+ scopy(ptr, " where");
+ for (unsigned i = 0; i < m_pkCount; i++) {
+ const Col& col = m_colList[m_pkIndex[i]];
+ if (i > 0)
+ scopy(ptr, " and");
+ scopy(ptr, " %s = ?", col.m_name);
+ }
+ }
+ void whereRange(char*& ptr) const {
+ scopy(ptr, " where");
+ for (unsigned i = 0; i < m_pkCount; i++) {
+ const Col& col = m_colList[m_pkIndex[i]];
+ if (i > 0)
+ scopy(ptr, " and");
+ scopy(ptr, " ? <= %s", col.m_name);
+ scopy(ptr, " and ");
+ scopy(ptr, "%s < ?", col.m_name);
+ }
+ }
+ void orderPk(char*& ptr) const {
+ scopy(ptr, " order by");
+ for (unsigned i = 0; i < m_pkCount; i++) {
+ const Col& col = m_colList[m_pkIndex[i]];
+ if (i > 0)
+ scopy(ptr, ", ");
+ else
+ scopy(ptr, " ");
+ scopy(ptr, "%s", col.m_name);
+ }
+ }
+ void selectPk(char*& ptr) const {
+ scopy(ptr, "select * from %s", m_name);
+ wherePk(ptr);
+ }
+ void selectAll(char*& ptr) const {
+ scopy(ptr, "select * from %s", m_name);
+ }
+ void selectRange(char*& ptr, bool sort) const {
+ selectAll(ptr);
+ whereRange(ptr);
+ if (sort)
+ orderPk(ptr);
+ }
+ void selectCount(char*& ptr) const {
+ scopy(ptr, "select count(*) from %s", m_name);
+ }
+ void insertAll(char*& ptr) const {
+ scopy(ptr, "insert into %s values (", m_name);
+ for (unsigned i = 0; i < m_colCount; i++) {
+ if (i > 0)
+ scopy(ptr, ", ");
+ scopy(ptr, "?");
+ }
+ scopy(ptr, ")");
+ }
+ void updatePk(char*& ptr) const {
+ scopy(ptr, "update %s set", m_name);
+ for (unsigned i = 0; i < m_nkCount; i++) {
+ const Col& col = m_colList[m_nkIndex[i]];
+ if (i > 0)
+ scopy(ptr, ", ");
+ else
+ scopy(ptr, " ");
+ scopy(ptr, "%s = ?", col.m_name);
+ }
+ wherePk(ptr);
+ }
+ void updateRange(char*& ptr) const {
+ scopy(ptr, "update %s set", m_name);
+ for (unsigned i = 0; i < m_nkCount; i++) {
+ const Col& col = m_colList[m_nkIndex[i]];
+ if (i > 0)
+ scopy(ptr, ", ");
+ else
+ scopy(ptr, " ");
+ scopy(ptr, "%s = ?", col.m_name); // XXX constant for now
+ }
+ whereRange(ptr);
+ }
+ void deleteAll(char*& ptr) const {
+ scopy(ptr, "delete from %s", m_name);
+ }
+ void deletePk(char*& ptr) const {
+ scopy(ptr, "delete from %s", m_name);
+ wherePk(ptr);
+ }
+ void deleteRange(char*& ptr) const {
+ scopy(ptr, "delete from %s", m_name);
+ whereRange(ptr);
+ }
+ // simple
+ void insertDirect(char*& ptr, unsigned n) const {
+ scopy(ptr, "insert into %s values (", m_name);
+ for (unsigned i = 0; i < m_colCount; i++) {
+ const Col& col = m_colList[i];
+ if (i > 0)
+ scopy(ptr, ", ");
+ if (col.m_type == Col::Char || col.m_type == Col::Varchar) {
+ scopy(ptr, "'");
+ for (unsigned i = 0; i <= n % col.m_length; i++)
+ scopy(ptr, "%c", 'a' + (n + i) % 26);
+ scopy(ptr, "'");
+ } else if (col.m_type == Col::Int || col.m_type == Col::Bigint) {
+ scopy(ptr, "%u", n);
+ } else if (col.m_type == Col::Real || col.m_type == Col::Double) {
+ scopy(ptr, "%.3f", n * 0.001);
+ } else {
+ assert(false);
+ }
+ }
+ scopy(ptr, ")");
+ }
+ void whereDirect(char*& ptr, unsigned n) const {
+ scopy(ptr, " where");
+ for (unsigned i = 0; i < m_pkCount; i++) {
+ const Col& col = m_colList[m_pkIndex[i]];
+ if (i > 0)
+ scopy(ptr, ", ");
+ else
+ scopy(ptr, " ");
+ scopy(ptr, "%s = ", col.m_name);
+ if (col.m_type == Col::Char || col.m_type == Col::Varchar) {
+ scopy(ptr, "'");
+ for (unsigned i = 0; i <= n % col.m_length; i++)
+ scopy(ptr, "%c", 'a' + (n + i) % 26);
+ scopy(ptr, "'");
+ } else if (col.m_type == Col::Int || col.m_type == Col::Bigint) {
+ scopy(ptr, "%u", n);
+ } else {
+ assert(false);
+ }
+ }
+ }
+ void countDirect(char*& ptr, unsigned n) const {
+ scopy(ptr, "select count(*) from %s", m_name);
+ whereDirect(ptr, n);
+ }
+ void deleteDirect(char*& ptr, unsigned n) const {
+ scopy(ptr, "delete from %s", m_name);
+ whereDirect(ptr, n);
+ }
+ // joins
+ void selectCart(char*& ptr, unsigned cnt) const {
+ scopy(ptr, "select count(*) from");
+ for (unsigned j = 0; j < cnt; j++) {
+ if (j > 0)
+ scopy(ptr, ",");
+ scopy(ptr, " %s", m_name);
+ scopy(ptr, " t%u", j);
+ }
+ }
+ void selectJoin(char*& ptr, unsigned cnt) const {
+ scopy(ptr, "select * from");
+ for (unsigned j = 0; j < cnt; j++) {
+ if (j > 0)
+ scopy(ptr, ",");
+ scopy(ptr, " %s", m_name);
+ scopy(ptr, " t%u", j);
+ }
+ for (unsigned i = 0; i < m_pkCount; i++) {
+ const Col& col = m_colList[m_pkIndex[i]];
+ for (unsigned j = 0; j < cnt - 1; j++) {
+ if (i == 0 && j == 0)
+ scopy(ptr, " where");
+ else
+ scopy(ptr, " and");
+ scopy(ptr, " t%u.%s = t%u.%s", j, col.m_name, j + 1, col.m_name);
+ }
+ }
+ }
+ // check if selected on command line
+ bool optok() const {
+ return opt.m_table == 0 || strcasecmp(m_name, opt.m_table) == 0;
+ }
+};
+
+// the test tables
+
+static Col col0[] = {
+ Col( "a", Col::Bigint, 0, Col::Pk, Col::CLong ),
+ Col( "b", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c", Col::Char, 4, Col::NotNull, Col::CChar ),
+ Col( "d", Col::Double, 0, Col::Null, Col::CDouble ),
+ Col( "e", Col::Char, 40, Col::Null, Col::CChar ),
+ Col( "f", Col::Char, 10, Col::Null, Col::CChar )
+};
+
+static Col col1[] = {
+ Col( "c0", Col::Int, 0, Col::Pk, Col::CLong ),
+ Col( "c1", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c2", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c3", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c4", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c5", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c6", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c7", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c8", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c9", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c10", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c11", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c12", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c13", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c14", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c15", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c16", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c17", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c18", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c19", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c20", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c21", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c22", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c23", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c24", Col::Int, 0, Col::NotNull, Col::CLong ),
+ Col( "c25", Col::Int, 0, Col::NotNull, Col::CLong )
+};
+
+static Col col2[] = {
+ Col( "a", Col::Int, 0, Col::Pk, Col::CLong ),
+ Col( "c", Col::Char, 8000, Col::NotNull, Col::CChar )
+};
+
+static Col col3[] = {
+ Col( "a", Col::Int, 0, Col::Pk, Col::CLong ),
+ Col( "c1", Col::Varchar, 1, Col::Null, Col::CChar ),
+ Col( "c2", Col::Varchar, 2, Col::Null, Col::CChar ),
+ Col( "c3", Col::Varchar, 3, Col::Null, Col::CChar ),
+ Col( "c4", Col::Varchar, 4, Col::Null, Col::CChar ),
+ Col( "c5", Col::Varchar, 10, Col::Null, Col::CChar ),
+ Col( "c6", Col::Varchar, 40, Col::Null, Col::CChar ),
+ Col( "c7", Col::Varchar, 255, Col::Null, Col::CChar ),
+ Col( "c8", Col::Varchar, 4000, Col::Null, Col::CChar )
+};
+
+static Col col4[] = {
+ Col( "a", Col::Char, 8, Col::Pk, Col::CChar ),
+ Col( "b", Col::Char, 8, Col::NotNull, Col::CChar ),
+};
+
+static Tab tabList[] = {
+#define colList(x) x, arraySize(x)
+ Tab( "tt00", colList(col0) ),
+ Tab( "tt01", colList(col1) ), // fläskbench special
+ Tab( "tt02", colList(col2) ),
+ Tab( "tt03", colList(col3) ),
+ Tab( "tt04", colList(col4) )
+#undef colList
+};
+
+static const unsigned tabCount = arraySize(tabList);
+static const unsigned maxColCount = 100; // per table - keep up to date
+
+static bool
+findTable()
+{
+ for (unsigned i = 0; i < tabCount; i++) {
+ const Tab& tab = tabList[i];
+ if (tab.optok())
+ return true;
+ }
+ return false;
+}
+
+static void
+listTables()
+{
+ ndbout << "tables:" << endl;
+ for (unsigned i = 0; i < tabCount; i++) {
+ const Tab& tab = tabList[i];
+ if (i > 0)
+ ndbout << " ";
+ ndbout << tab.m_name;
+ }
+ ndbout << endl;
+}
+
+// data fields and rows
+
+struct Fld {
+ const Col& m_col;
+ union {
+ char* m_char;
+ long m_long;
+ double m_double;
+ };
+ SQLINTEGER m_ind;
+ SQLINTEGER m_need; // constant
+ Fld() :
+ m_col(ColUndef),
+ m_need(0) {
+ }
+ Fld(const Col& col) :
+ m_col(col),
+ m_need(SQL_LEN_DATA_AT_EXEC(0)) {
+ switch (m_col.m_ctype) {
+ case Col::CChar:
+ m_char = new char[m_col.csize()];
+ memset(m_char, 0, m_col.csize());
+ break;
+ case Col::CLong:
+ m_long = 0;
+ break;
+ case Col::CDouble:
+ m_double = 0.0;
+ break;
+ }
+ m_ind = -1;
+ }
+ ~Fld() {
+ switch (m_col.m_ctype) {
+ case Col::CChar:
+ delete[] m_char;
+ break;
+ case Col::CLong:
+ break;
+ case Col::CDouble:
+ break;
+ }
+ }
+ void zero() {
+ switch (m_col.m_ctype) {
+ case Col::CChar:
+ memset(m_char, 0x1f, m_col.csize());
+ break;
+ case Col::CLong:
+ m_long = 0x1f1f1f1f;
+ break;
+ case Col::CDouble:
+ m_double = 1111111.1111111;
+ break;
+ }
+ m_ind = -1;
+ }
+ // copy values from another field
+ void copy(const Fld& fld) {
+ assert(&m_col == &fld.m_col);
+ switch (m_col.m_ctype) {
+ case Col::CChar:
+ memcpy(m_char, fld.m_char, m_col.csize());
+ break;
+ case Col::CLong:
+ m_long = fld.m_long;
+ break;
+ case Col::CDouble:
+ m_double = fld.m_double;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ m_ind = fld.m_ind;
+ }
+ SQLPOINTER caddr() {
+ switch (m_col.m_ctype) {
+ case Col::CChar:
+ return (SQLPOINTER)m_char;
+ case Col::CLong:
+ return (SQLPOINTER)&m_long;
+ case Col::CDouble:
+ return (SQLPOINTER)&m_double;
+ }
+ assert(false);
+ return 0;
+ }
+ SQLINTEGER* ind() {
+ return &m_ind;
+ }
+ SQLINTEGER* need() {
+ m_need = SQL_LEN_DATA_AT_EXEC(0);
+ return &m_need;
+ }
+ void calcPk(const Test& test, unsigned rownum) {
+ switch (m_col.m_ctype) {
+ case Col::CChar:
+ test.calcPk(rownum, m_char, m_col.csize());
+ m_ind = SQL_NTS;
+ return;
+ case Col::CLong:
+ test.calcPk(rownum, &m_long);
+ m_ind = 0;
+ return;
+ case Col::CDouble:
+ assert(false);
+ return;
+ }
+ assert(false);
+ }
+ void hashPk(const Test& test, unsigned* hash) const {
+ switch (m_col.m_ctype) {
+ case Col::CChar:
+ test.hashPk(hash, m_char, m_col.csize());
+ return;
+ case Col::CLong:
+ test.hashPk(hash, m_long);
+ return;
+ case Col::CDouble:
+ assert(false);
+ return;
+ }
+ assert(false);
+ }
+ void calcNk(const Test& test, unsigned hash) {
+ bool null = m_col.m_cons == Col::Null;
+ switch (m_col.m_ctype) {
+ case Col::CChar:
+ test.calcNk(hash, m_char, m_col.csize(), &m_ind, null);
+ return;
+ case Col::CLong:
+ test.calcNk(hash, &m_long, &m_ind, null);
+ return;
+ case Col::CDouble:
+ test.calcNk(hash, &m_double, &m_ind, null);
+ return;
+ }
+ assert(false);
+ }
+ bool verify(Test& test, const Fld& fld) {
+ assert(&m_col == &fld.m_col);
+ switch (m_col.m_ctype) {
+ case Col::CChar:
+ return test.verify(m_char, m_ind, fld.m_char, fld.m_ind, m_col.csize());
+ case Col::CLong:
+ return test.verify(m_long, m_ind, fld.m_long, fld.m_ind);
+ case Col::CDouble:
+ return test.verify(m_double, m_ind, fld.m_double, fld.m_ind);
+ }
+ assert(false);
+ return false;
+ }
+ // debug
+ void print() const {
+ if (m_ind == SQL_NULL_DATA)
+ ndbout << "NULL";
+ else {
+ switch (m_col.m_ctype) {
+ case Col::CChar:
+ ndbout << m_char;
+ break;
+ case Col::CLong:
+ ndbout << (int)m_long;
+ break;
+ case Col::CDouble:
+ ndbout << m_double;
+ break;
+ }
+ }
+ }
+};
+
+struct Row {
+ const Tab& m_tab;
+ Fld* m_fldList;
+ Row(const Tab& tab) :
+ m_tab(tab) {
+ m_fldList = new Fld[m_tab.m_colCount];
+ for (unsigned i = 0; i < m_tab.m_colCount; i++) {
+ const Col& col = m_tab.m_colList[i];
+ void* place = &m_fldList[i];
+ new (place) Fld(col);
+ }
+ }
+ ~Row() {
+ delete[] m_fldList;
+ }
+ // copy values from another row
+ void copy(const Row& row) {
+ assert(&m_tab == &row.m_tab);
+ for (unsigned i = 0; i < m_tab.m_colCount; i++) {
+ Fld& fld = m_fldList[i];
+ fld.copy(row.m_fldList[i]);
+ }
+ }
+ // primary key value is determined by row number
+ void calcPk(Test& test, unsigned rownum) {
+ for (unsigned i = 0; i < m_tab.m_pkCount; i++) {
+ Fld& fld = m_fldList[m_tab.m_pkIndex[i]];
+ fld.calcPk(test, rownum);
+ }
+ }
+ // other fields are determined by primary key value
+ void calcNk(Test& test) {
+ unsigned hash = test.m_salt;
+ for (unsigned i = 0; i < m_tab.m_pkCount; i++) {
+ Fld& fld = m_fldList[m_tab.m_pkIndex[i]];
+ fld.hashPk(test, &hash);
+ }
+ for (unsigned i = 0; i < m_tab.m_colCount; i++) {
+ const Col& col = m_tab.m_colList[i];
+ if (col.m_cons == Col::Pk)
+ continue;
+ Fld& fld = m_fldList[i];
+ fld.calcNk(test, hash);
+ }
+ }
+ // verify against another row
+ bool verifyPk(Test& test, const Row& row) const {
+ assert(&m_tab == &row.m_tab);
+ for (unsigned i = 0; i < m_tab.m_pkCount; i++) {
+ Fld& fld = m_fldList[m_tab.m_pkIndex[i]];
+ if (! fld.verify(test, row.m_fldList[m_tab.m_pkIndex[i]])) {
+ ndbout << "verify failed: tab=" << m_tab.m_name << " col=" << fld.m_col.m_name << endl;
+ return false;
+ }
+ }
+ return true;
+ }
+ bool verifyNk(Test& test, const Row& row) const {
+ assert(&m_tab == &row.m_tab);
+ for (unsigned i = 0; i < m_tab.m_nkCount; i++) {
+ Fld& fld = m_fldList[m_tab.m_nkIndex[i]];
+ if (! fld.verify(test, row.m_fldList[m_tab.m_nkIndex[i]])) {
+ ndbout << "verify failed: tab=" << m_tab.m_name << " col=" << fld.m_col.m_name << endl;
+ return false;
+ }
+ }
+ return true;
+ }
+ bool verify(Test& test, const Row& row) const {
+ return verifyPk(test, row) && verifyNk(test, row);
+ }
+ // debug
+ void print() const {
+ ndbout << "row";
+ for (unsigned i = 0; i < m_tab.m_colCount; i++) {
+ ndbout << " " << i << "=";
+ Fld& fld = m_fldList[i];
+ fld.print();
+ }
+ ndbout << endl;
+ }
+};
+
+// set ODBC version - required
+
+static void
+setVersion(Test& test, SQLHANDLE hEnv)
+{
+ test.run(HEnv(hEnv), SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER));
+}
+
+// set autocommit
+
+static void
+setAutocommit(Test& test, SQLHANDLE hDbc, bool on)
+{
+ SQLUINTEGER value = on ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
+ test.run(HDbc(hDbc), SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)value, SQL_IS_UINTEGER));
+ SQLUINTEGER value2 = (SQLUINTEGER)-1;
+ test.run(HDbc(hDbc), SQLGetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)&value2, SQL_IS_UINTEGER, 0));
+ test.chk(HDbc(hDbc), value2 == value, "got %u != %u", (unsigned)value2, (unsigned)value);
+}
+
+// subroutines - migrate simple common routines here
+
+static void
+allocEnv(Test& test, SQLHANDLE& hEnv)
+{
+ test.run(HNull, SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv));
+ setVersion(test, hEnv);
+}
+
+static void
+allocDbc(Test& test, SQLHANDLE hEnv, SQLHANDLE& hDbc)
+{
+ test.run(HEnv(hEnv), SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc));
+}
+
+static void
+allocConn(Test& test, SQLHANDLE hEnv, SQLHANDLE& hDbc)
+{
+ allocDbc(test, hEnv, hDbc);
+#ifdef unixODBC
+ test.exp(SQL_SUCCESS_WITH_INFO, "IM003", 0, false); // unicode??
+ test.exp(SQL_SUCCESS_WITH_INFO, "01000", 0, false); // version??
+#endif
+ test.run(HDbc(hDbc), SQLConnect(hDbc, (SQLCHAR*)opt.m_dsn, SQL_NTS, (SQLCHAR*)"user", SQL_NTS, (SQLCHAR*)"pass", SQL_NTS));
+}
+
+static void
+allocStmt(Test& test, SQLHANDLE hDbc, SQLHANDLE& hStmt)
+{
+ test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt));
+}
+
+static void
+allocAll(Test& test, SQLHANDLE& hEnv, SQLHANDLE& hDbc, SQLHANDLE& hStmt)
+{
+ allocEnv(test, hEnv);
+ allocConn(test, hEnv, hDbc);
+ allocStmt(test, hDbc, hStmt);
+}
+
+static void
+allocAll(Test& test, SQLHANDLE& hEnv, SQLHANDLE& hDbc, SQLHANDLE* hStmtList, unsigned nStmt)
+{
+ allocEnv(test, hEnv);
+ allocConn(test, hEnv, hDbc);
+ for (unsigned i = 0; i < nStmt; i++)
+ allocStmt(test, hDbc, hStmtList[i]);
+}
+
+static void
+freeEnv(Test& test, SQLHANDLE hEnv)
+{
+ test.run(HNull, SQLFreeHandle(SQL_HANDLE_ENV, hEnv));
+}
+
+static void
+freeDbc(Test& test, SQLHANDLE hEnv, SQLHANDLE hDbc)
+{
+ test.run(HEnv(hEnv), SQLFreeHandle(SQL_HANDLE_DBC, hDbc));
+}
+
+static void
+freeConn(Test& test, SQLHANDLE hEnv, SQLHANDLE hDbc)
+{
+ test.run(HDbc(hDbc), SQLDisconnect(hDbc));
+ test.run(HEnv(hEnv), SQLFreeHandle(SQL_HANDLE_DBC, hDbc));
+}
+
+static void
+freeStmt(Test& test, SQLHANDLE hDbc, SQLHANDLE hStmt)
+{
+ test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_STMT, hStmt));
+}
+
+static void
+freeAll(Test& test, SQLHANDLE hEnv, SQLHANDLE hDbc, SQLHANDLE hStmt)
+{
+ freeStmt(test, hDbc, hStmt);
+ freeConn(test, hEnv, hDbc);
+ freeEnv(test, hEnv);
+}
+
+static void
+freeAll(Test& test, SQLHANDLE hEnv, SQLHANDLE hDbc, SQLHANDLE* hStmtList, unsigned nStmt)
+{
+ for (unsigned i = 0; i < nStmt; i++)
+ freeStmt(test, hDbc, hStmtList[i]);
+ freeConn(test, hEnv, hDbc);
+ freeEnv(test, hEnv);
+}
+
+#define chkTuplesFetched(/*Test&*/ _test, /*SQLHANDLE*/ _hStmt, /*SQLUINTEGER*/ _countExp) \
+do { \
+ SQLUINTEGER _count = (SQLUINTEGER)-1; \
+ getTuplesFetched(_test, _hStmt, &_count); \
+ test.chk(HStmt(_hStmt), _count == _countExp, "tuples: got %ld != %ld", (long)_count, (long)_countExp); \
+} while (0)
+
+static void
+getTuplesFetched(Test& test, SQLHANDLE hStmt, SQLUINTEGER* count)
+{
+ *count = (SQLUINTEGER)-1;
+ test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_NDB_TUPLES_FETCHED, count, SQL_IS_POINTER, 0));
+}
+
+static void
+selectCount(Test& test, SQLHANDLE hStmt, const char* sql, long* count)
+{
+ if (opt.m_v >= 3)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ SQLINTEGER ind;
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_SLONG, count, 0, &ind));
+ ind = -1;
+ *count = -1;
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ unsigned k = 0;
+ while (1) {
+ if (k == 1)
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ if (k == 1)
+ break;
+ k++;
+ }
+ test.chk(HStmt(hStmt), ind == sizeof(long), "got %d != %d", (int)ind, (int)sizeof(long));
+ test.chk(HStmt(hStmt), *count >= 0, "got %ld < 0", *count);
+ chkTuplesFetched(test, hStmt, *count);
+#ifndef iODBC
+ //
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+#else
+ test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_CLOSE));
+#endif
+}
+
+static void
+selectCount(Test& test, SQLHANDLE hStmt, const Tab& tab, long* count)
+{
+ static char sql[MAX_SQL], *sqlptr; // XXX static or core
+ tab.selectCount(sqlptr = sql);
+ selectCount(test, hStmt, sql, count);
+}
+
+static void
+verifyCount(Test& test, SQLHANDLE hStmt, const Tab& tab, long countExp)
+{
+ long count = -1;
+ selectCount(test, hStmt, tab, &count);
+ test.chk(HStmt(hStmt), count == countExp, "got %ld != %ld", count, countExp);
+}
+
+#define chkRowCount(/*Test&*/ _test, /*SQLHANDLE*/ _hStmt, /*SQLINTEGER*/ _countExp) \
+do { \
+ SQLINTEGER _count = -1; \
+ getRowCount(_test, _hStmt, &_count); \
+ test.chk(HStmt(_hStmt), _count == _countExp, "rowcount: got %ld != %ld", (long)_count, (long)_countExp); \
+} while (0)
+
+static void
+getRowCount(Test& test, SQLHANDLE hStmt, SQLINTEGER* count)
+{
+ *count = -1;
+ test.run(HStmt(hStmt), SQLRowCount(hStmt, count));
+}
+
+// handle allocation
+
+static void
+testAlloc(Test& test)
+{
+ const unsigned n1 = (opt.m_scale >> 8) & 0xf; // default 500 = 0x1f4
+ const unsigned n2 = (opt.m_scale >> 4) & 0xf;
+ const unsigned n3 = (opt.m_scale >> 0) & 0xf;
+ const unsigned count = n1 + n1 * n2 + n1 * n2 * n3;
+ SQLHANDLE hEnvList[0xf];
+ SQLHANDLE hDbcList[0xf][0xf];
+ SQLHANDLE hStmtList[0xf][0xf][0xf];
+ for (unsigned i1 = 0; i1 < n1; i1++) {
+ SQLHANDLE& hEnv = hEnvList[i1];
+ test.run(HNull, SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv));
+ test.run(HEnv(hEnv), SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER));
+ for (unsigned i2 = 0; i2 < n2; i2++) {
+ SQLHANDLE& hDbc = hDbcList[i1][i2];
+ test.run(HEnv(hEnv), SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc));
+#ifdef unixODBC
+ test.exp(SQL_SUCCESS_WITH_INFO, "IM003", 0, false); // unicode??
+ test.exp(SQL_SUCCESS_WITH_INFO, "01000", 0, false); // version??
+#endif
+ test.run(HDbc(hDbc), SQLConnect(hDbc, (SQLCHAR*)opt.m_dsn, SQL_NTS, (SQLCHAR*)"user", SQL_NTS, (SQLCHAR*)"pass", SQL_NTS));
+ // some attributes
+ test.exp(SQL_ERROR, "HY092", -1, true); // read-only attribute
+ test.run(HDbc(hDbc), SQLSetConnectAttr(hDbc, SQL_ATTR_AUTO_IPD, (SQLPOINTER)SQL_TRUE, SQL_IS_UINTEGER));
+ test.exp(SQL_ERROR, "HYC00", -1, true); // not supported
+ test.run(HDbc(hDbc), SQLSetConnectAttr(hDbc, SQL_ATTR_TXN_ISOLATION, (SQLPOINTER)SQL_TXN_SERIALIZABLE, SQL_IS_UINTEGER));
+ test.run(HDbc(hDbc), SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, (SQLPOINTER)"DEFAULT", strlen("DEFAULT")));
+ for (unsigned i3 = 0; i3 < n3; i3++) {
+ SQLHANDLE& hStmt = hStmtList[i1][i2][i3];
+ test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt));
+ SQLHANDLE ipd0, ipd1;
+ SQLHANDLE ird0, ird1;
+ SQLHANDLE apd0, apd1, apd2;
+ SQLHANDLE ard0, ard1, ard2;
+ // get
+ ipd0 = ird0 = apd0 = ard0 = 0;
+ test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_IMP_PARAM_DESC, &ipd0, SQL_IS_POINTER, 0));
+ test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_IMP_ROW_DESC, &ird0, SQL_IS_POINTER, 0));
+ test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_APP_PARAM_DESC, &apd0, SQL_IS_POINTER, 0));
+ test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_APP_ROW_DESC, &ard0, SQL_IS_POINTER, 0));
+#ifndef unixODBC
+ test.chk(HStmt(hStmt), ipd0 != 0, "got 0");
+ test.chk(HStmt(hStmt), ird0 != 0, "got 0");
+ test.chk(HStmt(hStmt), apd0 != 0, "got 0");
+ test.chk(HStmt(hStmt), ard0 != 0, "got 0");
+#endif
+ // alloc
+ ipd1 = ird1 = apd1 = ard1 = 0;
+ test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_DESC, hDbc, &ipd1));
+ test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_DESC, hDbc, &ird1));
+ test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_DESC, hDbc, &apd1));
+ test.run(HDbc(hDbc), SQLAllocHandle(SQL_HANDLE_DESC, hDbc, &ard1));
+ test.chk(HDbc(hDbc), ipd1 != 0 && ird1 != 0 && apd1 != 0 && ard1 != 0, "got null");
+ // set
+ test.exp(SQL_ERROR, "HY092", -1, true); // read-only attribute
+ test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_IMP_PARAM_DESC, ipd1, SQL_IS_POINTER));
+ test.exp(SQL_ERROR, "HY092", -1, true); // read-only attribute
+ test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_IMP_ROW_DESC, ird1, SQL_IS_POINTER));
+ test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_APP_PARAM_DESC, apd1, SQL_IS_POINTER));
+ test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_APP_ROW_DESC, ard1, SQL_IS_POINTER));
+ // get
+
+ apd2 = ard2 = 0;
+ test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_APP_PARAM_DESC, &apd2, SQL_IS_POINTER, 0));
+ test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_APP_ROW_DESC, &ard2, SQL_IS_POINTER, 0));
+ test.chk(HStmt(hStmt), apd2 == apd1, "got %x != %x", (unsigned)apd2, (unsigned)apd1);
+ test.chk(HStmt(hStmt), ard2 == ard1, "got %x != %x", (unsigned)ard2, (unsigned)ard1);
+ // free
+ test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_DESC, ipd1));
+ test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_DESC, ird1));
+ test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_DESC, apd1));
+ test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_DESC, ard1));
+ }
+ }
+ }
+ test.timerCnt(count);
+ if (opt.m_v >= 3)
+ ndbout << "allocated " << count << endl;
+ for (unsigned i1 = 0; i1 < n1; i1++) {
+ SQLHANDLE& hEnv = hEnvList[i1];
+ for (unsigned i2 = 0; i2 < n2; i2++) {
+ SQLHANDLE& hDbc = hDbcList[i1][i2];
+ if (i2 % 2 == 0) {
+ for (unsigned i3 = 0; i3 < n3; i3++) {
+ SQLHANDLE& hStmt = hStmtList[i1][i2][i3];
+ test.run(HDbc(hDbc), SQLFreeHandle(SQL_HANDLE_STMT, hStmt));
+ }
+ } else {
+ // cleaned up by SQLDisconnect
+ }
+ test.run(HDbc(hDbc), SQLDisconnect(hDbc));
+ test.run(HEnv(hEnv), SQLFreeHandle(SQL_HANDLE_DBC, hDbc));
+ }
+ test.run(HNull, SQLFreeHandle(SQL_HANDLE_ENV, hEnv));
+ }
+ test.timerCnt(count);
+ if (opt.m_v >= 3)
+ ndbout << "freed " << count << endl;
+}
+
+// create tables
+
+static void
+testCreate(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned i = 0; i < tabCount; i++) {
+ Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ // drop
+ tab.drop(sqlptr = sql);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ test.exp(SQL_ERROR, "IM000", 2040709, false);
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ if (test.m_ret == SQL_SUCCESS)
+ test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_DROP_TABLE, "got %d != %d", test.m_functionCode, SQL_DIAG_DROP_TABLE);
+ if (test.m_ret == SQL_SUCCESS && opt.m_v >= 2)
+ ndbout << "table " << tab.m_name << " dropped" << endl;
+ if (test.m_ret != SQL_SUCCESS && opt.m_v >= 2)
+ ndbout << "table " << tab.m_name << " does not exist" << endl;
+ test.timerCnt(1);
+ // create
+ tab.create(sqlptr = sql);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ test.exp(SQL_ERROR, "IM000", 2040721, false);
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ if (test.m_ret == SQL_SUCCESS)
+ test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_CREATE_TABLE, "got %d != %d", test.m_functionCode, SQL_DIAG_CREATE_TABLE);
+ if (test.m_ret == SQL_SUCCESS && opt.m_v >= 2)
+ ndbout << "table " << tab.m_name << " created" << endl;
+ if (test.m_ret != SQL_SUCCESS && opt.m_v >= 2)
+ ndbout << "table " << tab.m_name << " already exists" << endl;
+ test.timerCnt(1);
+ }
+ freeAll(test, hEnv, hDbc, hStmt);
+}
+
+// prepare without execute
+
+static void
+testPrepare(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned cnt = opt.m_depth; cnt <= opt.m_depth; cnt++) {
+ for (unsigned i = 0; i < tabCount; i++) {
+ Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ tab.selectJoin(sqlptr = sql, cnt);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ SQLSMALLINT colCount = -1;
+ SQLSMALLINT colExp = cnt * tab.m_colCount;
+ test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount));
+ test.chk(HStmt(hStmt), colCount == colExp, "got %d != %d", (int)colCount, (int)colExp);
+ test.timerCnt(1);
+ }
+ }
+ freeAll(test, hEnv, hDbc, hStmt);
+}
+
+// catalog functions
+
+static void
+testCatalog(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+ odbc_typeinfo: {
+ long type[] = {
+ SQL_CHAR, SQL_VARCHAR, SQL_SMALLINT, SQL_INTEGER, SQL_BIGINT, SQL_REAL, SQL_DOUBLE
+ };
+ unsigned rows[] = {
+ 1, 1, 2, 2, 2, 1, 1 // 2 for signed and unsigned
+ };
+ for (unsigned i = 0; i < arraySize(type); i++) {
+ test.run(HStmt(hStmt), SQLGetTypeInfo(hStmt, type[i]));
+ long dataType = 0;
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 2, SQL_C_SLONG, &dataType, 0, 0));
+ unsigned k = 0;
+ while (1) {
+ if (k == rows[i])
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ if (k == rows[i])
+ break;
+ test.chk(HStmt(hStmt), dataType == type[i], "got %ld != %ld", dataType, type[i]);
+ test.timerCnt(1);
+ k++;
+ }
+#ifndef iODBC
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+#else
+ freeStmt(test, hDbc, hStmt);
+ allocStmt(test, hDbc, hStmt);
+#endif
+ }
+ if (opt.m_v >= 2)
+ ndbout << "found " << (UintPtr)arraySize(type) << " data types" << endl;
+ test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND));
+ }
+ odbc_tables: {
+ unsigned found[tabCount];
+ for (unsigned i = 0; i < tabCount; i++)
+ found[i] = 0;
+ test.run(HStmt(hStmt), SQLTables(hStmt, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0));
+ char tableName[200] = "";
+ char tableType[200] = "";
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 3, SQL_C_CHAR, tableName, sizeof(tableName), 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 4, SQL_C_CHAR, tableType, sizeof(tableType), 0));
+ unsigned cnt = 0;
+ while (1) {
+ test.exp(SQL_NO_DATA, 0, 0, false);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ if (test.m_ret == SQL_NO_DATA)
+ break;
+ test.timerCnt(1);
+ cnt++;
+ if (! blankeq(tableType, "TABLE"))
+ continue;
+ for (unsigned i = 0; i < tabCount; i++) {
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ if (! blankeq(tab.m_name, tableName))
+ continue;
+ test.chk(HStmt(hStmt), found[i] == 0, "duplicate table %s", tab.m_name);
+ found[i]++;
+ }
+ }
+#ifndef iODBC
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+#else
+ freeStmt(test, hDbc, hStmt);
+ allocStmt(test, hDbc, hStmt);
+#endif
+ for (unsigned i = 0; i < tabCount; i++) {
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ test.chk(HStmt(hStmt), found[i] == 1, "table %s not found", tab.m_name);
+ }
+ if (opt.m_v >= 2)
+ ndbout << "found " << cnt << " tables" << endl;
+ test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND));
+ }
+ odbc_columns: {
+ unsigned found[tabCount][maxColCount];
+ for (unsigned i = 0; i < tabCount; i++) {
+ for (unsigned j = 0; j < maxColCount; j++)
+ found[i][j] = 0;
+ }
+ test.run(HStmt(hStmt), SQLColumns(hStmt, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0));
+ char tableName[200] = "";
+ char columnName[200] = "";
+ long dataType = 0;
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 3, SQL_C_CHAR, tableName, sizeof(tableName), 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 4, SQL_C_CHAR, columnName, sizeof(columnName), 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 5, SQL_C_SLONG, &dataType, 0, 0));
+ unsigned cnt = 0;
+ while (1) {
+ test.exp(SQL_NO_DATA, 0, 0, false);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ if (test.m_ret == SQL_NO_DATA)
+ break;
+ test.timerCnt(1);
+ cnt++;
+ for (unsigned i = 0; i < tabCount; i++) {
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ if (! blankeq(tab.m_name, tableName))
+ continue;
+ bool columnFound = false;
+ for (unsigned j = 0; j < tab.m_colCount; j++) {
+ const Col& col = tab.m_colList[j];
+ if (! blankeq(col.m_name, columnName))
+ continue;
+ test.chk(HStmt(hStmt), found[i][j] == 0, "duplicate column %s.%s", tableName, columnName);
+ found[i][j]++;
+ columnFound = true;
+ }
+ test.chk(HStmt(hStmt), columnFound, "unknown column %s.%s", tableName, columnName);
+ }
+ }
+#ifndef iODBC
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+#else
+ freeStmt(test, hDbc, hStmt);
+ allocStmt(test, hDbc, hStmt);
+#endif
+ for (unsigned i = 0; i < tabCount; i++) {
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ for (unsigned j = 0; j < tab.m_colCount; j++) {
+ const Col& col = tab.m_colList[j];
+ test.chk(HStmt(hStmt), found[i][j] == 1, "column %s.%s not found", tab.m_name, col.m_name);
+ }
+ }
+ if (opt.m_v >= 2)
+ ndbout << "found " << cnt << " columns" << endl;
+ test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND));
+ }
+ odbc_primarykeys: {
+ // table patterns are no allowed
+ for (unsigned i = 0; i < tabCount; i++) {
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ char tmp[200]; // p.i.t.a
+ strcpy(tmp, tab.m_name);
+ for (char* a = tmp; *a != 0; a++) {
+ if ('a' <= *a && *a <= 'z')
+ *a -= 'a' - 'A';
+ }
+ test.run(HStmt(hStmt), SQLPrimaryKeys(hStmt, (SQLCHAR*)0, 0, (SQLCHAR*)0, 0, (SQLCHAR*)tmp, SQL_NTS));
+ char tableName[200] = "";
+ char columnName[200] = "";
+ long keySeq = -1;
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 3, SQL_C_CHAR, tableName, sizeof(tableName), 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 4, SQL_C_CHAR, columnName, sizeof(columnName), 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 5, SQL_C_SLONG, &keySeq, 0, 0));
+ unsigned cnt = 0;
+ while (1) {
+ if (cnt == tab.m_pkCount)
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ if (test.m_ret == SQL_NO_DATA)
+ break;
+ test.chk(HStmt(hStmt), keySeq == 1 + cnt, "got %ld != %u", keySeq, 1 + cnt);
+ const Col& col = tab.m_colList[tab.m_pkIndex[keySeq - 1]];
+ test.chk(HStmt(hStmt), blankeq(columnName, col.m_name), "got %s != %s", columnName, col.m_name);
+ test.timerCnt(1);
+ cnt++;
+ }
+#ifndef iODBC
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+#else
+ freeStmt(test, hDbc, hStmt);
+ allocStmt(test, hDbc, hStmt);
+#endif
+ }
+ test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND));
+ }
+ freeAll(test, hEnv, hDbc, hStmt);
+}
+
+// insert
+
+static void
+testInsert(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
+ allocAll(test, hEnv, hDbc, hStmtList, tabCount);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ // prepare
+ tab.insertAll(sqlptr = sql);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ SQLSMALLINT parCount = -1;
+ test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount));
+ test.chk(HStmt(hStmt), parCount == tab.m_colCount, "got %d != %d", (int)parCount, (int)tab.m_colCount);
+ // bind parameters
+ Row row(tab);
+ for (unsigned j = 0; j < tab.m_colCount; j++) {
+ Fld& fld = row.m_fldList[j];
+ const Col& col = fld.m_col;
+ // every other at-exec
+ SQLPOINTER caddr;
+ SQLINTEGER* ind;
+ if (opt.m_noputd || j % 2 == 0) {
+ caddr = fld.caddr();
+ ind = fld.ind();
+ } else {
+ caddr = (SQLPOINTER)j;
+ ind = fld.need();
+ }
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, caddr, col.csize(), ind));
+ }
+ // bind columns (none)
+ SQLSMALLINT colCount = -1;
+ test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount));
+ test.chk(HStmt(hStmt), colCount == 0, "got %d != 0", (int)colCount);
+ // execute
+ for (unsigned k = 0; k < opt.m_scale; k++) {
+ if (k % 5 == 0) {
+ // rebind
+ unsigned j = 0;
+ Fld& fld = row.m_fldList[j];
+ const Col& col = fld.m_col;
+ // every other at-exec
+ SQLPOINTER caddr;
+ SQLINTEGER* ind;
+ if (opt.m_noputd || j % 2 == 0) {
+ caddr = fld.caddr();
+ ind = fld.ind();
+ } else {
+ caddr = (SQLPOINTER)j;
+ ind = fld.need();
+ }
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, caddr, col.csize(), ind));
+ }
+ row.calcPk(test, k);
+ row.calcNk(test);
+ unsigned needData = opt.m_noputd ? 0 : tab.m_colCount / 2;
+ if (needData)
+ test.exp(SQL_NEED_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_INSERT, "got %d != %d", test.m_functionCode, SQL_DIAG_INSERT);
+ if (needData) {
+ while (1) {
+ SQLPOINTER jPtr = (SQLPOINTER)999;
+ if (needData)
+ test.exp(SQL_NEED_DATA, 0, 0, true);
+ // completes SQLExecute on success
+ test.run(HStmt(hStmt), SQLParamData(hStmt, &jPtr));
+ if (! needData)
+ break;
+ unsigned j = (unsigned)jPtr;
+ test.chk(HStmt(hStmt), j < tab.m_colCount && j % 2 != 0, "got %u 0x%x", j, j);
+ Fld& fld = row.m_fldList[j];
+ const Col& col = fld.m_col;
+ SQLSMALLINT ctype = col.ctype();
+ if (k % 2 == 0 || ctype != Col::CChar)
+ test.run(HStmt(hStmt), SQLPutData(hStmt, fld.caddr(), *fld.ind()));
+ else {
+ // put in pieces
+ unsigned size = col.csize() - 1; // omit null terminator
+ char* caddr = (char*)(fld.caddr());
+ unsigned off = 0;
+ while (off < size) {
+ unsigned m = size / 7; // bytes to put
+ if (m == 0)
+ m = 1;
+ if (m > size - off)
+ m = size - off;
+ bool putNull = (*fld.ind() == SQL_NULL_DATA);
+ // no null terminator
+ SQLINTEGER len = putNull ? SQL_NULL_DATA : (int)m;
+ test.run(HStmt(hStmt), SQLPutData(hStmt, caddr + off, len));
+ if (putNull)
+ break;
+ off += m;
+ }
+ }
+ needData--;
+ }
+ }
+ chkRowCount(test, hStmt, 1);
+ chkTuplesFetched(test, hStmt, 0);
+ }
+ test.timerCnt(opt.m_scale);
+ if (opt.m_v >= 3)
+ ndbout << "inserted " << opt.m_scale << " into " << tab.m_name << endl;
+ }
+ freeAll(test, hEnv, hDbc, hStmtList, tabCount);
+}
+
+// count
+
+static void
+testCount(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
+ allocAll(test, hEnv, hDbc, hStmtList, tabCount);
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ long count = -1;
+ selectCount(test, hStmt, tab, &count);
+ test.chk(HStmt(hStmt), count == opt.m_scale * opt.m_threads, "got %ld != %u", count, opt.m_scale * opt.m_threads);
+ test.timerCnt(count);
+ if (opt.m_v >= 3)
+ ndbout << "counted " << (int)count << " rows in " << tab.m_name << endl;
+ }
+ // scan all at same time
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ tab.selectAll(sqlptr = sql);
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ }
+ unsigned k = 0;
+ while (1) {
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ if (k == opt.m_scale * opt.m_threads)
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ if (k != opt.m_scale * opt.m_threads) {
+ chkTuplesFetched(test, hStmt, k + 1);
+ test.timerCnt(1);
+ } else {
+ chkTuplesFetched(test, hStmt, k);
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLMoreResults(hStmt));
+ }
+ }
+ if (k == opt.m_scale * opt.m_threads)
+ break;
+ k++;
+ }
+ if (opt.m_v >= 3)
+ ndbout << "scanned " << opt.m_scale << " rows from each table" << endl;
+ freeAll(test, hEnv, hDbc, hStmtList, tabCount);
+}
+
+// update
+
+static void
+testUpdatePk(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
+ allocAll(test, hEnv, hDbc, hStmtList, tabCount);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ // prepare
+ tab.updatePk(sqlptr = sql);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // bind parameters
+ Row row(tab);
+ SQLSMALLINT parCount = -1;
+ test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount));
+ test.chk(HStmt(hStmt), parCount == tab.m_colCount, "got %d != %d", (int)parCount, (int)tab.m_colCount);
+ for (unsigned j = 0; j < tab.m_nkCount; j++) {
+ Fld& fld = row.m_fldList[tab.m_nkIndex[j]];
+ const Col& col = fld.m_col;
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind()));
+ }
+ for (unsigned j = 0; j < tab.m_pkCount; j++) {
+ Fld& fld = row.m_fldList[tab.m_pkIndex[j]];
+ const Col& col = fld.m_col;
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + tab.m_nkCount + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind()));
+ }
+ // bind columns (none)
+ SQLSMALLINT colCount = -1;
+ test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount));
+ test.chk(HStmt(hStmt), colCount == 0, "got %d != 0", (int)colCount);
+ // execute
+ for (unsigned k = 0; k < opt.m_scale; k++) {
+ if (k % 5 == 0) {
+ unsigned j = 0;
+ Fld& fld = row.m_fldList[tab.m_nkIndex[j]];
+ const Col& col = fld.m_col;
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind()));
+ }
+ row.calcPk(test, k);
+ row.calcNk(test);
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_UPDATE_WHERE, "got %d != %d", test.m_functionCode, SQL_DIAG_UPDATE_WHERE);
+ chkRowCount(test, hStmt, 1);
+ // direct update, no read has been necessary
+ chkTuplesFetched(test, hStmt, 0);
+ }
+ test.timerCnt(opt.m_scale);
+ if (opt.m_v >= 3)
+ ndbout << "updated " << opt.m_scale << " in " << tab.m_name << endl;
+ }
+ freeAll(test, hEnv, hDbc, hStmtList, tabCount);
+}
+
+static void
+testUpdateScan(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
+ allocAll(test, hEnv, hDbc, hStmtList, tabCount);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ // prepare
+ tab.updateRange(sqlptr = sql);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // bind parameters
+ Row row(tab); // for set clause
+ Row rowlo(tab); // for pk ranges
+ Row rowhi(tab);
+ SQLSMALLINT parCount = -1;
+ test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount));
+ test.chk(HStmt(hStmt), parCount == tab.m_nkCount + 2 * tab.m_pkCount, "got %d != %d", (int)parCount, (int)tab.m_nkCount + 2 * (int)tab.m_pkCount);
+ for (unsigned j = 0; j < tab.m_nkCount; j++) {
+ const Col& col = tab.m_colList[tab.m_nkIndex[j]];
+ Fld& fld = row.m_fldList[tab.m_nkIndex[j]];
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind()));
+ }
+ bool canInterp = true;
+ for (unsigned j = 0; j < tab.m_pkCount; j++) {
+ const Col& col = tab.m_colList[tab.m_pkIndex[j]];
+ Fld& fldlo = rowlo.m_fldList[tab.m_pkIndex[j]];
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + tab.m_nkCount + 2 * j + 0, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fldlo.caddr(), col.csize(), fldlo.ind()));
+ Fld& fldhi = rowhi.m_fldList[tab.m_pkIndex[j]];
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + tab.m_nkCount + 2 * j + 1, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fldhi.caddr(), col.csize(), fldhi.ind()));
+ if (col.m_type != Col::Char)
+ canInterp = false; // XXX no unsigned yet
+ }
+ // execute
+ row.calcPk(test, 0);
+ row.calcNk(test);
+ rowlo.calcPk(test, 0);
+ rowhi.calcPk(test, test.m_mul); // sucks
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_UPDATE_WHERE, "got %d != %d", test.m_functionCode, SQL_DIAG_UPDATE_WHERE);
+ chkRowCount(test, hStmt, opt.m_scale);
+ chkTuplesFetched(test, hStmt, canInterp ? opt.m_scale : opt.m_scale * opt.m_threads);
+ test.timerCnt(opt.m_scale);
+ if (opt.m_v >= 3)
+ ndbout << "updated " << opt.m_scale << " in " << tab.m_name << endl;
+ }
+ freeAll(test, hEnv, hDbc, hStmtList, tabCount);
+}
+
+// verify
+
+static void
+testVerifyPk(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
+ allocAll(test, hEnv, hDbc, hStmtList, tabCount);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ // prepare
+ tab.selectPk(sqlptr = sql);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // use same row for input and output
+ Row row(tab);
+ // bind parameters
+ SQLSMALLINT parCount = -1;
+ test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount));
+ test.chk(HStmt(hStmt), parCount == tab.m_pkCount, "got %d != %d", (int)parCount, (int)tab.m_pkCount);
+ for (unsigned j = 0; j < tab.m_pkCount; j++) {
+ Fld& fld = row.m_fldList[tab.m_pkIndex[j]];
+ const Col& col = fld.m_col;
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind()));
+ }
+ // bind columns
+ SQLSMALLINT colCount = -1;
+ test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount));
+ test.chk(HStmt(hStmt), colCount == tab.m_colCount, "got %d != %d", (int)colCount, (int)tab.m_colCount);
+ for (unsigned j = 0; j < tab.m_colCount; j++) {
+ Fld& fld = row.m_fldList[j];
+ const Col& col = fld.m_col;
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 1 + j, col.ctype(), fld.caddr(), col.csize(), fld.ind()));
+ }
+ // row for SQLGetData
+ Row rowGet(tab);
+ // reference row
+ Row rowRef(tab);
+ // execute
+ for (unsigned k = 0; k < opt.m_scale; k++) {
+ if (k % 5 == 0) {
+ // rebind
+ unsigned j = 0;
+ Fld& fld = row.m_fldList[tab.m_pkIndex[j]];
+ const Col& col = fld.m_col;
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind()));
+ }
+ row.calcPk(test, k);
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_SELECT_CURSOR, "got %d != %d", test.m_functionCode, SQL_DIAG_SELECT_CURSOR);
+ // fetch
+ for (unsigned k2 = 0; ; k2++) {
+ if (k2 == 1)
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ chkTuplesFetched(test, hStmt, 1);
+ if (k2 == 1)
+ break;
+ rowRef.calcPk(test, k);
+ test.chk(HStmt(hStmt), row.verifyPk(test, rowRef), "verify row=%d", k);
+ if (test.m_const)
+ rowRef.calcPk(test, 0);
+ rowRef.calcNk(test);
+ test.chk(HStmt(hStmt), row.verifyNk(test, rowRef), "verify row=%d", k);
+ // SQLGetData is supported independent of SQLBindCol
+ if (opt.m_nogetd)
+ continue;
+ for (unsigned j = 0; j < tab.m_colCount; j++) {
+ Fld& fld = rowGet.m_fldList[j];
+ fld.zero();
+ const Col& col = fld.m_col;
+ // test both variants
+ SQLSMALLINT ctype = k % 2 == 0 ? col.ctype() : SQL_ARD_TYPE;
+ if (ctype != Col::CChar)
+ test.run(HStmt(hStmt), SQLGetData(hStmt, 1 + j, ctype, fld.caddr(), col.csize(), fld.ind()));
+ else {
+ // get in pieces
+ unsigned size = col.csize() - 1; // omit null terminator
+ char* caddr = (char*)(fld.caddr());
+ unsigned off = 0;
+ while (off < size) {
+ unsigned m = size / 3; // bytes to get
+ if (m == 0)
+ m = 1;
+ if (m > size - off)
+ m = size - off;
+ bool getNull = (rowRef.m_fldList[j].m_ind == SQL_NULL_DATA);
+ if (off + m < size && ! getNull)
+ test.exp(SQL_SUCCESS_WITH_INFO, "01004", -1, true);
+ // include null terminator in buffer size
+ test.run(HStmt(hStmt), SQLGetData(hStmt, 1 + j, ctype, caddr + off, m + 1, fld.ind()));
+ int ind = *fld.ind();
+ if (getNull) {
+ test.chk(HStmt(hStmt), ind == SQL_NULL_DATA, "got %d", ind);
+ break;
+ }
+ test.chk(HStmt(hStmt), ind == size - off, "got %d != %u", ind, size - off);
+ off += m;
+ }
+ }
+ }
+ rowRef.calcPk(test, k);
+ test.chk(HStmt(hStmt), rowGet.verifyPk(test, rowRef), "verify row=%d", k);
+ if (test.m_const)
+ rowRef.calcPk(test, 0);
+ rowRef.calcNk(test);
+ test.chk(HStmt(hStmt), rowGet.verifyNk(test, rowRef), "verify row=%d", k);
+ // SQLGetData again
+ for (unsigned j = 0; j < tab.m_colCount; j++) {
+ Fld& fld = rowGet.m_fldList[j];
+ const Col& col = fld.m_col;
+ // test both variants
+ SQLSMALLINT ctype = k % 2 == 0 ? col.ctype() : SQL_ARD_TYPE;
+ // expect no more data
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLGetData(hStmt, 1 + j, ctype, fld.caddr(), col.csize(), fld.ind()));
+ }
+ }
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+ }
+ test.timerCnt(opt.m_scale);
+ if (opt.m_v >= 3)
+ ndbout << "verified " << opt.m_scale << " from " << tab.m_name << endl;
+ }
+ freeAll(test, hEnv, hDbc, hStmtList, tabCount);
+}
+
+static void
+testVerifyScan(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
+ allocAll(test, hEnv, hDbc, hStmtList, tabCount);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ // prepare
+ tab.selectRange(sqlptr = sql, ! opt.m_nosort);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // bind parameters
+ Row rowlo(tab); // use available PK fields..
+ Row rowhi(tab); // since we have no other way for now
+ SQLSMALLINT parCount = -1;
+ test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount));
+ test.chk(HStmt(hStmt), parCount == 2 * tab.m_pkCount, "got %d != %d", (int)parCount, 2 * (int)tab.m_pkCount);
+ for (unsigned j = 0; j < tab.m_pkCount; j++) {
+ const Col& col = tab.m_colList[tab.m_pkIndex[j]];
+ Fld& fldlo = rowlo.m_fldList[tab.m_pkIndex[j]];
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + 2 * j + 0, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fldlo.caddr(), col.csize(), fldlo.ind()));
+ Fld& fldhi = rowhi.m_fldList[tab.m_pkIndex[j]];
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + 2 * j + 1, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fldhi.caddr(), col.csize(), fldhi.ind()));
+ }
+ // bind columns
+ Row row(tab);
+ SQLSMALLINT colCount = -1;
+ test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount));
+ test.chk(HStmt(hStmt), colCount == tab.m_colCount, "got %d != %d", (int)colCount, (int)tab.m_colCount);
+ for (unsigned j = 0; j < tab.m_colCount; j++) {
+ Fld& fld = row.m_fldList[j];
+ const Col& col = fld.m_col;
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 1 + j, col.ctype(), fld.caddr(), col.csize(), fld.ind()));
+ }
+ // execute
+ rowlo.calcPk(test, 0);
+ rowhi.calcPk(test, test.m_mul); // sucks
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_SELECT_CURSOR, "got %d != %d", test.m_functionCode, SQL_DIAG_SELECT_CURSOR);
+ // reference row
+ Row rowRef(tab);
+ // fetch
+ unsigned k = 0;
+ SQLUINTEGER rowCount1 = (SQLUINTEGER)-1;
+ test.run(HStmt(hStmt), SQLSetStmtAttr(hStmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowCount1, SQL_IS_POINTER));
+ while (1) {
+ unsigned countExp;
+ if (k == opt.m_scale) {
+ countExp = k;
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ } else {
+ countExp = k + 1;
+ }
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ // let me count the ways..
+ chkRowCount(test, hStmt, countExp);
+ test.chk(HStmt(hStmt), rowCount1 == countExp, "got %lu != %u", rowCount1, countExp);
+ SQLUINTEGER rowCount2 = (SQLUINTEGER)-1;
+ test.run(HStmt(hStmt), SQLGetStmtAttr(hStmt, SQL_ATTR_ROW_NUMBER, &rowCount2, SQL_IS_POINTER, 0));
+ test.chk(HStmt(hStmt), rowCount2 == countExp, "got %lu != %u", rowCount2, countExp);
+ if (k == opt.m_scale)
+ break;
+ if (! opt.m_nosort) {
+ // expecting k-th row
+ rowRef.calcPk(test, k);
+ test.chk(HStmt(hStmt), row.verifyPk(test, rowRef), "verify row=%d", k);
+ if (test.m_const)
+ rowRef.calcPk(test, 0);
+ rowRef.calcNk(test);
+ test.chk(HStmt(hStmt), row.verifyNk(test, rowRef), "verify row=%d", k);
+ } else {
+ // expecting random row
+ rowRef.copy(row);
+ test.chk(HStmt(hStmt), row.verifyNk(test, rowRef), "verify row=%d", k);
+ }
+ k++;
+ }
+ test.timerCnt(opt.m_scale);
+ if (opt.m_v >= 3)
+ ndbout << "verified " << opt.m_scale << " from " << tab.m_name << endl;
+ }
+ freeAll(test, hEnv, hDbc, hStmtList, tabCount);
+}
+
+// self-join (scan followed by pk lookups)
+
+static void
+testJoin(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
+ allocAll(test, hEnv, hDbc, hStmtList, tabCount);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned cnt = opt.m_depth; cnt <= opt.m_depth; cnt++) {
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ tab.selectJoin(sqlptr = sql, cnt);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ }
+ unsigned k = 0;
+ while (1) {
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ if (k == opt.m_scale * opt.m_threads)
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ if (k == opt.m_scale * opt.m_threads) {
+ chkTuplesFetched(test, hStmt, k * opt.m_depth);
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+ } else {
+ chkTuplesFetched(test, hStmt, (k + 1) * opt.m_depth);
+ test.timerCnt(1);
+ }
+ }
+ if (k == opt.m_scale * opt.m_threads)
+ break;
+ k++;
+ }
+ }
+ freeAll(test, hEnv, hDbc, hStmtList, tabCount);
+}
+
+// cartesian join (multiple nested scans)
+
+static void
+testCart(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
+ allocAll(test, hEnv, hDbc, hStmtList, tabCount);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned cnt = 2; cnt <= 2; cnt++) {
+ unsigned rows = 1;
+ //for (unsigned k = 0; k < opt.m_depth; k++) {
+ //rows *= opt.m_scale * opt.m_threads;
+ //}
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ tab.selectCart(sqlptr = sql, cnt);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ }
+ unsigned k = 0;
+ while (1) {
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ if (k == rows)
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ if (k == rows) {
+ //chkTuplesFetched(test, hStmt, k);
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+ } else {
+ //chkTuplesFetched(test, hStmt, k + 1);
+ test.timerCnt(1);
+ }
+ }
+ if (k == rows)
+ break;
+ k++;
+ }
+ }
+ freeAll(test, hEnv, hDbc, hStmtList, tabCount);
+}
+
+// delete
+
+static void
+testDeleteAll(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
+ allocAll(test, hEnv, hDbc, hStmtList, tabCount);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ long count0 = -1;
+ selectCount(test, hStmt, tab, &count0);
+ tab.deleteAll(sqlptr = sql);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ if (count0 == 0)
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+#ifndef iODBC
+ test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_DELETE_WHERE, "got %d != %d", test.m_functionCode, SQL_DIAG_DELETE_WHERE);
+#endif
+ SQLINTEGER rowCount = -1;
+ getRowCount(test, hStmt, &rowCount);
+ test.timerCnt(rowCount);
+ test.chk(HStmt(hStmt), rowCount == count0, "got %d != %ld", (int)rowCount, count0);
+ chkTuplesFetched(test, hStmt, rowCount);
+ if (opt.m_v >= 3)
+ ndbout << "deleted " << (int)rowCount << " from " << tab.m_name << endl;
+ long count = -1;
+ selectCount(test, hStmt, tab, &count);
+ test.chk(HStmt(hStmt), count == 0, "got %ld != 0", count);
+ }
+ freeAll(test, hEnv, hDbc, hStmtList, tabCount);
+}
+
+static void
+testDeletePk(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
+ allocAll(test, hEnv, hDbc, hStmtList, tabCount);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ // prepare
+ tab.deletePk(sqlptr = sql);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // bind parameters
+ Row row(tab);
+ SQLSMALLINT parCount = -1;
+ test.run(HStmt(hStmt), SQLNumParams(hStmt, &parCount));
+ test.chk(HStmt(hStmt), parCount == tab.m_pkCount, "got %d != %d", (int)parCount, (int)tab.m_colCount);
+ for (unsigned j = 0; j < tab.m_pkCount; j++) {
+ Fld& fld = row.m_fldList[tab.m_pkIndex[j]];
+ const Col& col = fld.m_col;
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, col.ctype(), col.type(), col.size(), 0, fld.caddr(), col.csize(), fld.ind()));
+ }
+ // bind columns (none)
+ SQLSMALLINT colCount = -1;
+ test.run(HStmt(hStmt), SQLNumResultCols(hStmt, &colCount));
+ test.chk(HStmt(hStmt), colCount == 0, "got %d != 0", (int)colCount);
+ // execute
+ for (unsigned k = 0; k < opt.m_scale; k++) {
+ row.calcPk(test, k);
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ test.chk(HStmt(hStmt), test.m_functionCode == SQL_DIAG_DELETE_WHERE, "got %d != %d", test.m_functionCode, SQL_DIAG_DELETE_WHERE);
+ chkRowCount(test, hStmt, 1);
+ // direct delete, no fetch required
+ chkTuplesFetched(test, hStmt, 0);
+ }
+ test.timerCnt(opt.m_scale);
+ if (opt.m_v >= 3)
+ ndbout << "updated " << opt.m_scale << " in " << tab.m_name << endl;
+ }
+ freeAll(test, hEnv, hDbc, hStmtList, tabCount);
+}
+
+static void
+testTrans(Test& test)
+{
+#ifdef unixODBC
+ if (opt.m_v >= 1)
+ ndbout << "unixODBC does not support transactions - test skipped" << endl;
+#else
+ SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
+ allocAll(test, hEnv, hDbc, hStmtList, tabCount);
+ char sql[MAX_SQL], *sqlptr;
+ // delete all
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ tab.deleteAll(sqlptr = sql);
+ test.exp(SQL_NO_DATA, 0, 0, false);
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ SQLINTEGER rowCount = -1;
+ getRowCount(test, hStmt, &rowCount);
+ if (opt.m_v >= 3)
+ ndbout << "deleted " << (int)rowCount << " from " << tab.m_name << endl;
+ }
+ setAutocommit(test, hDbc, false);
+ if (opt.m_v >= 2)
+ ndbout << "set autocommit OFF" << endl;
+ for (int commit = 0; commit < opt.m_scale; commit += 1) {
+ bool rollback = (commit % 2 == 0);
+ // XXX delete with no data leaves trans in error state for 2nd table
+ if (commit > 0 && rollback) { // previous case was commit
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ tab.deleteDirect(sqlptr = sql, 0);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.exp(SQL_NO_DATA, 0, 0, false);
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ }
+ test.run(HDbc(hDbc), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_COMMIT));
+ }
+ // insert
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ tab.insertDirect(sqlptr = sql, 0);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ if (opt.m_v >= 2)
+ ndbout << tab.m_name << ": inserted 1 row" << endl;
+ }
+ // count them via pk
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ tab.countDirect(sqlptr = sql, 0);
+ long count = -1;
+ long countExp = 1;
+ selectCount(test, hStmt, sql, &count);
+ test.chk(HStmt(hStmt), count == countExp, "got %ld != %ld", count, countExp);
+ }
+ // count them via scan
+ for (unsigned i = 0; i < tabCount; i++) {
+ // XXX hupp no work
+ break;
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ long count = -1;
+ long countExp = 1;
+ selectCount(test, hStmt, tab, &count);
+ test.chk(HStmt(hStmt), count == countExp, "got %ld != %ld", count, countExp);
+ }
+ // rollback or commit
+ if (rollback) {
+ if (opt.m_v >= 2)
+ ndbout << "end trans ROLLBACK" << endl;
+ test.run(HDbc(hDbc), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_ROLLBACK));
+ } else {
+ if (opt.m_v >= 2)
+ ndbout << "end trans COMMIT" << endl;
+ test.run(HDbc(hDbc), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_COMMIT));
+ }
+ // count them via pk again
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ tab.countDirect(sqlptr = sql, 0);
+ long count = -1;
+ long countExp = rollback ? 0 : 1;
+ selectCount(test, hStmt, sql, &count);
+ test.chk(HStmt(hStmt), count == countExp, "got %ld != %ld", count, countExp);
+ }
+ // count them via scan again
+ for (unsigned i = 0; i < tabCount; i++) {
+ // XXX hupp no work
+ break;
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ long count = -1;
+ long countExp = rollback ? 0 : 1;
+ selectCount(test, hStmt, tab, &count);
+ test.chk(HStmt(hStmt), count == countExp, "got %ld != %ld", count, countExp);
+ }
+ }
+ freeAll(test, hEnv, hDbc, hStmtList, tabCount);
+#endif
+}
+
+static void
+testConcur(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmtList[tabCount];
+ allocAll(test, hEnv, hDbc, hStmtList, tabCount);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned i = 0; i < tabCount; i++) {
+ SQLHANDLE& hStmt = hStmtList[i];
+ const Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ // delete all
+ tab.deleteAll(sqlptr = sql);
+ test.exp(SQL_NO_DATA, 0, 0, false);
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // insert some
+ unsigned rowcount = 10;
+ for (unsigned n = 0; n < rowcount; n++) {
+ tab.insertDirect(sqlptr = sql, n);
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ }
+ verifyCount(test, hStmt, tab, rowcount);
+ // start query scan followed by pk lookups
+ tab.selectJoin(sqlptr = sql, 2);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // start fetch
+ unsigned k = 0;
+ while (1) {
+ if (k > 0)
+ test.exp(SQL_ERROR, "24000", -1, true); // commit closed cursor
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ if (k > 0)
+ break;
+ // delete some random row
+ tab.deleteDirect(sqlptr = sql, k);
+ // try using same statement
+ test.exp(SQL_ERROR, "24000", -1, true); // cursor is open
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // try using different statement
+ SQLHANDLE hStmt2;
+ allocStmt(test, hDbc, hStmt2);
+ test.run(HStmt(hStmt2), SQLExecDirect(hStmt2, (SQLCHAR*)sql, SQL_NTS));
+ k++;
+ }
+ test.exp(SQL_ERROR, "24000", -1, true); // cursor is not open
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+ test.timerCnt(rowcount);
+ }
+ freeAll(test, hEnv, hDbc, hStmtList, tabCount);
+}
+
+static void
+testReadcom(Test& test)
+{
+ testDeleteAll(test);
+ testInsert(test);
+ const unsigned nc = 3;
+ SQLHANDLE hEnv[nc], hDbc[nc], hStmt[nc];
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned j = 0; j < nc; j++)
+ allocAll(test, hEnv[j], hDbc[j], hStmt[j]);
+ for (unsigned i = 0; i < tabCount; i++) {
+ Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ long count;
+ // check count
+ count = -1;
+ selectCount(test, hStmt[0], tab, &count);
+ test.chk(HStmt(hStmt[0]), count == opt.m_scale, "got %d != %d", (int)count, (int)opt.m_scale);
+ // scan delete uncommitted with handle 0
+ setAutocommit(test, hDbc[0], false);
+ tab.deleteAll(sqlptr = sql);
+ if (opt.m_scale == 0)
+ test.exp(SQL_NO_DATA, 0, 0, false);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt[0]), SQLExecDirect(hStmt[0], (SQLCHAR*)sql, SQL_NTS));
+ // scan via other tx should not hang and see all rows
+ for (unsigned j = 0; j < nc; j++) {
+ count = -1;
+ int want = j == 0 ? 0 : opt.m_scale;
+ selectCount(test, hStmt[j], tab, &count);
+ test.chk(HStmt(hStmt[j]), count == want, "tx %u: got %d != %d", j, (int)count, want);
+ if (opt.m_v >= 2)
+ ndbout << "tx " << j << " ok !" << endl;
+ }
+ // setting autocommit on commits the delete
+ setAutocommit(test, hDbc[0], true);
+ // check count
+ count = -1;
+ selectCount(test, hStmt[0], tab, &count);
+ test.chk(HStmt(hStmt[0]), count == 0, "got %d != 0", (int)count);
+ }
+ for (unsigned j = 0; j < nc; j++)
+ freeAll(test, hEnv[j], hDbc[j], hStmt[j]);
+}
+
+static void
+testPerf(Test& test)
+{
+ if (test.m_stuff == 0) {
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+ char sql[MAX_SQL], *sqlptr;
+ for (unsigned i = 0; i < tabCount; i++) {
+ Tab& tab = tabList[i];
+ if (! tab.optok())
+ continue;
+ test.exp(SQL_NO_DATA, 0, 0, false);
+ tab.deleteAll(sqlptr = sql);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ long count0 = -1;
+ // XXX triggers SEGV somewhere
+ //selectCount(test, hStmt, tab, &count0);
+ //test.chk(HStmt(hStmt), count0 == 0, "got %d != 0", (int)count0);
+ }
+ freeAll(test, hEnv, hDbc, hStmt);
+ return;
+ }
+ assert(test.m_stuff == 1 || test.m_stuff == 2);
+ bool ndbapi = (test.m_stuff == 1);
+ tt01: {
+ const unsigned OFF = 1000000;
+ const unsigned N = 25;
+ Tab& tab = tabList[1];
+ if (! tab.optok())
+ goto out;
+ if (ndbapi) {
+#ifndef ndbODBC
+ if (opt.m_v >= 1)
+ ndbout << "running via DM - test skipped" << endl;
+#else
+ Ndb* ndb = new Ndb("TEST_DB");
+ ndb->init();
+ if (ndb->waitUntilReady() != 0) {
+ ndbout << ndb->getNdbError() << endl;
+ fatal("waitUntilReady");
+ }
+ Uint32 val[1+N];
+ // insert
+ for (unsigned k = 1; k <= opt.m_scale; k++) {
+ NdbConnection* con = ndb->startTransaction();
+ if (con == 0) {
+ ndbout << ndb->getNdbError() << endl;
+ fatal("startTransaction");
+ }
+ NdbOperation* op = con->getNdbOperation(tab.m_upperName);
+ if (op == 0) {
+ ndbout << con->getNdbError() << endl;
+ fatal("getNdbOperation");
+ }
+ if (op->insertTuple() == -1) {
+ ndbout << op->getNdbError() << endl;
+ fatal("insertTuple");
+ }
+ for (unsigned j = 0; j <= N; j++) {
+ val[j] = (j == 0 ? k + test.m_no * OFF : k * j);
+ if (j == 0) {
+ if (op->equal(j, val[j]) == -1) {
+ ndbout << op->getNdbError() << endl;
+ fatal("equal");
+ }
+ } else {
+ if (op->setValue(j, val[j]) == -1) {
+ ndbout << op->getNdbError() << endl;
+ fatal("setValue");
+ }
+ }
+ }
+ if (con->execute(Commit) == -1) {
+ ndbout << con->getNdbError() << endl;
+ fatal("execute");
+ }
+ ndb->closeTransaction(con);
+ }
+ test.timerCnt(opt.m_scale);
+ // select PK
+ for (unsigned k = 1; k <= opt.m_scale; k++) {
+ NdbConnection* con = ndb->startTransaction();
+ if (con == 0) {
+ ndbout << ndb->getNdbError() << endl;
+ fatal("startTransaction");
+ }
+ NdbOperation* op = con->getNdbOperation(tab.m_upperName);
+ if (op == 0) {
+ ndbout << con->getNdbError() << endl;
+ fatal("getNdbOperation");
+ }
+ if (op->readTuple() == -1) {
+ ndbout << op->getNdbError() << endl;
+ fatal("insertTuple");
+ }
+ for (unsigned j = 0; j <= N; j++) {
+ val[j] = (j == 0 ? k + test.m_no * OFF : 0);
+ if (j == 0) {
+ if (op->equal(j, val[j]) == -1) {
+ ndbout << op->getNdbError() << endl;
+ fatal("equal");
+ }
+ } else {
+ if (op->getValue(j, (char*)&val[j]) == 0) {
+ ndbout << op->getNdbError() << endl;
+ fatal("getValue");
+ }
+ }
+ }
+ if (con->execute(Commit) == -1) {
+ ndbout << con->getNdbError() << endl;
+ fatal("execute");
+ }
+ for (unsigned j = 1; j <= N; j++) {
+ assert(val[j] == k * j);
+ }
+ ndb->closeTransaction(con);
+ }
+ test.timerCnt(opt.m_scale);
+ // delete PK
+ for (unsigned k = 1; k <= opt.m_scale; k++) {
+ NdbConnection* con = ndb->startTransaction();
+ if (con == 0) {
+ ndbout << ndb->getNdbError() << endl;
+ fatal("startTransaction");
+ }
+ NdbOperation* op = con->getNdbOperation(tab.m_upperName);
+ if (op == 0) {
+ ndbout << con->getNdbError() << endl;
+ fatal("getNdbOperation");
+ }
+ if (op->deleteTuple() == -1) {
+ ndbout << op->getNdbError() << endl;
+ fatal("deleteTuple");
+ }
+ unsigned j = 0;
+ val[j] = k + test.m_no * OFF;
+ if (op->equal(j, val[j]) == -1) {
+ ndbout << op->getNdbError() << endl;
+ fatal("equal");
+ }
+ if (con->execute(Commit) == -1) {
+ ndbout << con->getNdbError() << endl;
+ fatal("execute");
+ }
+ ndb->closeTransaction(con);
+ }
+ test.timerCnt(opt.m_scale);
+ delete ndb;
+#endif
+ } else {
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+ long val[1+N];
+ char sql[MAX_SQL], *sqlptr;
+ // insert
+ tab.insertAll(sqlptr = sql);
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ for (unsigned j = 0; j <= N; j++) {
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &val[j], 0, 0));
+ }
+ test.m_perf = true;
+ for (unsigned k = 1; k <= opt.m_scale; k++) {
+ for (unsigned j = 0; j <= N; j++) {
+ val[j] = (j == 0 ? k + test.m_no * OFF : k * j);
+ }
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ }
+ test.m_perf = false;
+ test.timerCnt(opt.m_scale);
+ // select PK
+ tab.selectPk(sqlptr = sql);
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ for (unsigned j = 0; j <= N; j++) {
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 1 + j, SQL_C_SLONG, &val[j], 0, 0));
+ }
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + N + 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &val[0], 0, 0));
+ test.m_perf = true;
+ for (unsigned k = 1; k <= opt.m_scale; k++) {
+ val[0] = k + test.m_no * OFF;
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ for (unsigned j = 1; j <= N; j++) {
+ assert(val[j] == k * j);
+ }
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+ }
+ test.m_perf = false;
+ test.timerCnt(opt.m_scale);
+ // delete PK
+ tab.deletePk(sqlptr = sql);
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ unsigned j = 0;
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1 + j, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &val[j], 0, 0));
+ test.m_perf = true;
+ for (unsigned k = 1; k <= opt.m_scale; k++) {
+ val[j] = k + test.m_no * OFF;
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ }
+ test.m_perf = false;
+ test.timerCnt(opt.m_scale);
+ freeAll(test, hEnv, hDbc, hStmt);
+ }
+ out:
+ ;
+ }
+}
+
+struct Sql {
+ const char* m_sql;
+ int m_functionCode;
+ int m_rowCount;
+ int m_tuplesFetched;
+ long m_lastValue;
+ unsigned long m_bindValue;
+ int m_ret;
+ const char* m_state;
+ SQLINTEGER m_native;
+ bool m_reset;
+ // run this function instead
+ typedef void (*TestFunc)(Test& test);
+ TestFunc m_testFunc;
+ Sql() :
+ m_sql(0) {
+ }
+ Sql(const char* do_cmd) :
+ m_sql(do_cmd) {
+ }
+ Sql(const char* sql, int functionCode, int rowCount, int tuplesFetched, long lastValue, long bindValue) :
+ m_sql(sql),
+ m_functionCode(functionCode),
+ m_rowCount(rowCount),
+ m_tuplesFetched(tuplesFetched),
+ m_lastValue(lastValue),
+ m_bindValue(bindValue),
+ m_ret(SQL_SUCCESS),
+ m_state(0),
+ m_native(0),
+ m_reset(true),
+ m_testFunc(0) {
+ }
+ // the 4 numbers after SQL_DIAG... rowCount tuplesFetched lastValue bindValue
+ Sql(const char* sql, int functionCode, int rowCount, int tuplesFetched, long lastValue, long bindValue, int ret, const char* state, SQLINTEGER native, bool reset) :
+ m_sql(sql),
+ m_functionCode(functionCode),
+ m_rowCount(rowCount),
+ m_tuplesFetched(tuplesFetched),
+ m_lastValue(lastValue),
+ m_bindValue(bindValue),
+ m_ret(ret),
+ m_state(state),
+ m_native(native),
+ m_reset(reset),
+ m_testFunc(0) {
+ }
+ Sql(const char* text, TestFunc testFunc) :
+ m_sql(text),
+ m_testFunc(testFunc) {
+ }
+ static const char* set_autocommit_on() {
+ return "set autocommit on";
+ }
+ static const char* set_autocommit_off() {
+ return "set autocommit off";
+ }
+ static const char* do_commit() {
+ return "commit";
+ }
+ static const char* do_rollback() {
+ return "rollback";
+ }
+};
+
+// 90
+
+static const Sql
+miscSql90[] = {
+ Sql("select * from dual",
+ SQL_DIAG_SELECT_CURSOR, 1, 0, -1, -1),
+ Sql("drop table tt90a",
+ SQL_DIAG_DROP_TABLE, -1, 0, -1, -1,
+ SQL_ERROR, "IM000", 2040709, false),
+ Sql("create table tt90a (a int, b int, c int, primary key(b, c)) storage(large) logging",
+ SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1),
+ Sql()
+};
+
+// 91
+
+static const Sql
+miscSql91[] = {
+ Sql("drop table tt91a",
+ SQL_DIAG_DROP_TABLE, -1, 0, -1, -1,
+ SQL_ERROR, "IM000", 2040709, false),
+ Sql("create table tt91a (a bigint unsigned primary key, b bigint unsigned not null, c varchar(10))",
+ SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1),
+ Sql("insert into tt91a values (1, 111, 'aaa')",
+ SQL_DIAG_INSERT, 1, 0, -1, -1),
+ // fails
+ Sql("insert into tt91a values (2, null, 'ccc')",
+ SQL_DIAG_INSERT, -1, 0, -1, -1,
+ SQL_ERROR, "IM000", 2014203, true),
+ Sql("update tt91a set b = 222 where a = 2",
+ SQL_DIAG_UPDATE_WHERE, 0, 0, -1, -1,
+ SQL_NO_DATA, 0, 0, true),
+ // two more
+ Sql("insert into tt91a values (2, 222, 'ccc')",
+ SQL_DIAG_INSERT, 1, 0, -1, -1),
+ Sql("insert into tt91a values (3, 333, 'bbb')",
+ SQL_DIAG_INSERT, 1, 0, -1, -1),
+ // direct update
+ Sql("update tt91a set b = 112 where a = 1",
+ SQL_DIAG_UPDATE_WHERE, 1, 0, -1, -1),
+ Sql("update tt91a set b = 113 where a = 1 and b > 111",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+ // update and delete with interpreted scan
+ Sql("update tt91a set b = 114 where b < 114",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+ Sql("delete from tt91a where b < 115",
+ SQL_DIAG_DELETE_WHERE, 1, 1, -1, -1),
+ Sql("insert into tt91a values (1, 111, 'aaa')",
+ SQL_DIAG_INSERT, 1, 0, -1, -1),
+ // check rows: 1,111,aaa + 2,222,ccc + 3,333,bbb
+ Sql("select * from tt91a order by c",
+ SQL_DIAG_SELECT_CURSOR, 3, 3, 2, -1),
+ Sql("select * from tt91a order by c desc",
+ SQL_DIAG_SELECT_CURSOR, 3, 3, 1, -1),
+ Sql("select * from tt91a where a = 2",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, -1, -1),
+ Sql("select * from tt91a where a + b = 224",
+ SQL_DIAG_SELECT_CURSOR, 1, 3, -1, -1),
+ Sql("select * from tt91a where a = 4",
+ SQL_DIAG_SELECT_CURSOR, 0, 0, -1, -1),
+ Sql("select b-a from tt91a order by a-b",
+ SQL_DIAG_SELECT_CURSOR, 3, 3, 110, -1),
+ Sql("select sum(a+b) from tt91a",
+ SQL_DIAG_SELECT_CURSOR, 1, 3, 672, -1),
+ Sql("select x.b, y.b, z.b from tt91a x, tt91a y, tt91a z where x.b <= y.b and y.b < z.b order by x.b",
+ SQL_DIAG_SELECT_CURSOR, 4, 13, 222, -1),
+ Sql("select x.b, y.b, z.b from tt91a x, tt91a y, tt91a z where x.b + y.b = z.b order by x.b",
+ SQL_DIAG_SELECT_CURSOR, 3, 15, 222, -1),
+ // tmp index
+ Sql("create unique hash index xx91a on tt91a(b)",
+ SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1),
+ Sql("select x.b, y.b, z.b from tt91a x, tt91a y, tt91a z where x.b + y.b = z.b order by x.b",
+ SQL_DIAG_SELECT_CURSOR, 3, 15, 222, -1),
+ Sql("drop index xx91a on tt91a",
+ SQL_DIAG_DROP_INDEX, -1, -1, -1, -1),
+ // add some duplicates
+ Sql("insert into tt91a values (4, 222, 'ccc')",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ Sql("insert into tt91a values (5, 333, 'bbb')",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ Sql("insert into tt91a values (6, 333, 'bbb')",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ // check rows: 1,111,aaa + 2 * 2,222,ccc + 3 * 3,333,bbb
+ Sql("select count(*) from tt91a",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 6, -1),
+ Sql("select a+b from tt91a where (b = 111 or b = 222 ) and (b = 222 or b = 333) and a > 1 and a < 3",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 224, -1),
+ Sql("select sum(a) from tt91a having min(a) = 1 and max(a) = 6",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 21, -1),
+ Sql("select sum(a) from tt91a where a = 2 or a = 4 having min(a) = 2 and max(a) = 4",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 6, -1),
+ Sql("select sum(a) from tt91a having min(a) = 1 and max(a) = 5",
+ SQL_DIAG_SELECT_CURSOR, 0, -1, -1, -1),
+ Sql("select sum(a), b from tt91a group by b order by b",
+ SQL_DIAG_SELECT_CURSOR, 3, -1, 14, -1),
+ Sql("select sum(a), b, c from tt91a group by b, c order by c",
+ SQL_DIAG_SELECT_CURSOR, 3, -1, 6, -1),
+ Sql("select b, sum(a) from tt91a group by b having b = 37 * sum(a)",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 222, -1),
+ // simple varchar vs interpreter test
+ Sql("select count(*) from tt91a where c = 'ccc'",
+ SQL_DIAG_SELECT_CURSOR, 1, 2, 2, -1),
+ Sql("select count(*) from tt91a where c like '%b%'",
+ SQL_DIAG_SELECT_CURSOR, 1, 3, 3, -1),
+ // interpreter limits (crashes in api on v211)
+#if NDB_VERSION_MAJOR >= 3
+ Sql("select count(*) from tt91a where a in (99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2)",
+ SQL_DIAG_SELECT_CURSOR, 1, 5, 5, -1),
+ Sql("select count(*) from tt91a where c in ('xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','bbb','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy','zzzzz','xxxxx','yyyyy')",
+ SQL_DIAG_SELECT_CURSOR, 1, 3, 3, -1),
+#endif
+ // distinct
+ Sql("select distinct b from tt91a order by b",
+ SQL_DIAG_SELECT_CURSOR, 3, -1, 333, -1),
+ // some illegal groupings
+ Sql("select a from tt91a group by b",
+ -1, -1, -1, -1, -1,
+ SQL_ERROR, "IM000", -1, -1),
+ Sql("select sum(a) from tt91a group by b having a = 2",
+ -1, -1, -1, -1, -1,
+ SQL_ERROR, "IM000", -1, -1),
+ Sql("select sum(a) from tt91a group by b order by a",
+ -1, -1, -1, -1, -1,
+ SQL_ERROR, "IM000", -1, -1),
+ // string functions
+ Sql("insert into tt91a (c, b, a) values ('abcdef', 999, 9)",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ Sql("select count(*) from tt91a where left(c, 2) = 'ab' and substr(c, 3, 2) = 'cd' and right(c, 2) = 'ef'",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 1, -1),
+ // nulls
+ Sql("update tt91a set c = null where a > 8",
+ SQL_DIAG_UPDATE_WHERE, 1, -1, -1, -1),
+ Sql("select a from tt91a where c is null and b is not null order by a",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 9, -1),
+ Sql("select a from tt91a where not (c is not null or b is null) order by a",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 9, -1),
+ // null value guard in interpreter
+ Sql("select count(*) from tt91a where c < 'x' or c > 'x' or c != 'x' or c = 'x'",
+ SQL_DIAG_SELECT_CURSOR, 1, 6, 6, -1),
+ Sql("delete from tt91a where c is null",
+ SQL_DIAG_DELETE_WHERE, 1, -1, -1, -1),
+ // indexes
+ Sql("update tt91a set b = a + 5",
+ SQL_DIAG_UPDATE_WHERE, 6, 6, -1, -1),
+ Sql("create unique hash index xx91a on tt91a(b)",
+ SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1),
+ // scan y primary key x
+ Sql("select x.b from tt91a x, tt91a y where x.a = y.b + 0",
+ SQL_DIAG_SELECT_CURSOR, 1, 7, 11, -1),
+ // scan x index y
+ Sql("select x.b from tt91a x, tt91a y where x.a + 0 = y.b",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 11, -1),
+ // scan x scan y
+ Sql("select x.b from tt91a x, tt91a y where x.a + 0 = y.b + 0",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 11, -1),
+ // dml ops
+ Sql("delete from tt91a where b = 11 and a > 999",
+ SQL_DIAG_DELETE_WHERE, 0, 1, -1, -1,
+ SQL_NO_DATA, 0, 0, true),
+ Sql("delete from tt91a where b = 11",
+ SQL_DIAG_DELETE_WHERE, 1, 0, -1, -1),
+ Sql("delete from tt91a where b = 11",
+ SQL_DIAG_DELETE_WHERE, 0, 0, -1, -1,
+ SQL_NO_DATA, 0, 0, true),
+ Sql("update tt91a set b = 10*10 where b = 10",
+ SQL_DIAG_UPDATE_WHERE, 1, 0, -1, -1),
+ Sql("update tt91a set b = 10 where b = 10*10",
+ SQL_DIAG_UPDATE_WHERE, 1, 0, -1, -1),
+ Sql("update tt91a set b = 10*10 where b = 10 and b >= 10",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+ Sql("update tt91a set b = 10 where b = 10*10 and b >= 10*10",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+ // char vs varchar
+ Sql("drop table tt91b",
+ SQL_DIAG_DROP_TABLE, -1, -1, -1, -1,
+ SQL_ERROR, "IM000", 2040709, false),
+ Sql("create table tt91b (a int primary key, b char(5), c varchar(5))",
+ SQL_DIAG_CREATE_TABLE, -1, -1, -1, -1),
+ Sql("insert into tt91b values (1, 'abc', 'abc')",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ Sql("insert into tt91b values (2, 'xyz', 'xyz')",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ Sql("insert into tt91b values (3, 'xyz', 'xyz ')",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ // char = char strips blanks
+ Sql("select count(*) from tt91b x where (x.b = 'abc') or x.a = x.a+1",
+ SQL_DIAG_SELECT_CURSOR, 1, 3, 1, -1),
+ Sql("select count(*) from tt91b x where (x.b = 'abc')",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql("select count(*) from tt91b x where (x.b = 'abc ') or x.a = x.a+1",
+ SQL_DIAG_SELECT_CURSOR, 1, 3, 1, -1),
+ Sql("select count(*) from tt91b x where (x.b = 'abc ')",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ // varchar = char
+ Sql("select count(*) from tt91b x where (x.c = 'abc') or x.a = x.a+1",
+ SQL_DIAG_SELECT_CURSOR, 1, 3, 1, -1),
+ Sql("select count(*) from tt91b x where (x.c = 'abc')",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql("select count(*) from tt91b x where (x.c = 'abc ') or x.a = x.a+1",
+ SQL_DIAG_SELECT_CURSOR, 1, 3, 0, -1),
+ Sql("select count(*) from tt91b x where (x.c = 'abc ')",
+ SQL_DIAG_SELECT_CURSOR, 1, 0, 0, -1),
+ // char = varchar
+ Sql("select count(*) from tt91b x, tt91b y where (x.b = y.c) or x.a = x.a+1 or y.a = y.a+1",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 2, -1),
+ Sql("select count(*) from tt91b x, tt91b y where (x.b = y.c)",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 2, -1),
+ // varchar = varchar
+ Sql("select count(*) from tt91b x, tt91b y where (x.c = y.c) or x.a = x.a+1 or y.a = y.a+1",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 3, -1),
+ Sql("select count(*) from tt91b x, tt91b y where (x.c = y.c)",
+ SQL_DIAG_SELECT_CURSOR, 1, -1, 3, -1),
+ // less
+ Sql("select 10 * x.a + y.a from tt91b x, tt91b y where (x.b < y.b) or x.a = x.a+1 or y.a = y.a+1 order by x.a, y.a",
+ SQL_DIAG_SELECT_CURSOR, 2, -1, 13, -1),
+ Sql("select 10 * x.a + y.a from tt91b x, tt91b y where (x.b < y.b) order by x.a, y.a",
+ SQL_DIAG_SELECT_CURSOR, 2, -1, 13, -1),
+ Sql("select 10 * x.a + y.a from tt91b x, tt91b y where (x.c < y.c) or x.a = x.a+1 or y.a = y.a+1 order by x.a, y.a",
+ SQL_DIAG_SELECT_CURSOR, 3, -1, 23, -1),
+ Sql("select 10 * x.a + y.a from tt91b x, tt91b y where (x.c < y.c) order by x.a, y.a",
+ SQL_DIAG_SELECT_CURSOR, 3, -1, 23, -1),
+ // like
+ Sql("select count(*) from tt91b x where (x.b like 'a%') or x.a = x.a+1",
+ SQL_DIAG_SELECT_CURSOR, 1, 3, 1, -1),
+ Sql("select count(*) from tt91b x where (x.b like 'a%')",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql("select count(*) from tt91b x where (x.b like 'x%z') or x.a = x.a+1",
+ SQL_DIAG_SELECT_CURSOR, 1, 3, 0, -1),
+ Sql("select count(*) from tt91b x where (x.b like 'x%z')",
+ SQL_DIAG_SELECT_CURSOR, 1, 0, 0, -1),
+ Sql("select count(*) from tt91b x where (x.a+0 = 2 and x.c like 'x%z') or x.a = x.a+1",
+ SQL_DIAG_SELECT_CURSOR, 1, 3, 1, -1),
+ Sql("select count(*) from tt91b x where (x.a+0 = 2 and x.c like 'x%z')",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql("select count(*) from tt91b x where (x.a+0 = 3 and x.c like 'x%z ') or x.a = x.a+1",
+ SQL_DIAG_SELECT_CURSOR, 1, 3, 1, -1),
+ Sql("select count(*) from tt91b x where (x.a+0 = 3 and x.c like 'x%z ')",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql()
+};
+
+// 92
+
+static void
+testMisc92a(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+ char sql[MAX_SQL];
+ char tname[20];
+ sprintf(tname, "tt92%c", 0140 + test.m_no);
+ if (test.m_loop == 1) {
+ lock_mutex();
+ sprintf(sql, "drop table %s", tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.exp(SQL_ERROR, "IM000", 2040709, false);
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ sprintf(sql, "create table %s (a int unsigned primary key, b int unsigned not null)", tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ unlock_mutex();
+ } else {
+ sprintf(sql, "delete from %s", tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.exp(SQL_NO_DATA, 0, 0, false);
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ test.run(HStmt(hStmt), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_COMMIT));
+ }
+ for (int on = true; on >= false; on--) {
+ if (opt.m_v >= 2)
+ ndbout << "set autocommit " << (on ? "ON" : "OFF") << endl;
+ setAutocommit(test, hDbc, on);
+ // insert rows
+ if (opt.m_v >= 2)
+ ndbout << "SQL: insert into " << tname << " ..." << opt.m_scale << endl;
+ for (unsigned k = 0; k < opt.m_scale; k++) {
+ sprintf(sql, "insert into %s values (%u, %u)", tname, k, 10 * k);
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ }
+ // commit always
+ test.run(HStmt(hStmt), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_COMMIT));
+ // scan delete
+ sprintf(sql, "delete from %s", tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // rollback or commit
+ test.run(HStmt(hStmt), SQLEndTran(SQL_HANDLE_DBC, hDbc, on ? SQL_COMMIT : SQL_ROLLBACK));
+ // count
+ long count = -1;
+ sprintf(sql, "select count(*) from %s", tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ selectCount(test, hStmt, sql, &count);
+ test.chk(HStmt(hStmt), count == on ? 0 : opt.m_scale, "%s: got %d != %d", tname, (int)count, (int)opt.m_scale);
+ }
+ freeAll(test, hEnv, hDbc, hStmt);
+}
+
+static const Sql
+miscSql92[] = {
+ // create in C func
+ Sql("testMisc92a", testMisc92a),
+ Sql()
+};
+
+// 93
+
+static void
+testMisc93a(Test& test)
+{
+ SQLHANDLE hEnv[2], hDbc[2], hStmt[2];
+ allocAll(test, hEnv[0], hDbc[0], hStmt[0]);
+ allocAll(test, hEnv[1], hDbc[1], hStmt[1]);
+ char sql[MAX_SQL];
+ // select via primary key
+ setAutocommit(test, hDbc[0], false);
+ sprintf(sql, "select c1 from tt93a where c0 = 1");
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt[0]), SQLExecDirect(hStmt[0], (SQLCHAR*)sql, SQL_NTS));
+ // update via another trans must time out
+ sprintf(sql, "update tt93a set c1 = 'b' where c0 = 1");
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt[1]), SQLExecDirect(hStmt[1], (SQLCHAR*)sql, SQL_NTS));
+ freeAll(test, hEnv[0], hDbc[0], hStmt[0]);
+ freeAll(test, hEnv[1], hDbc[1], hStmt[1]);
+}
+
+static const Sql
+miscSql93[] = {
+ // create in C func
+ Sql("drop table tt93a",
+ SQL_DIAG_DROP_TABLE, -1, 0, -1, -1,
+ SQL_ERROR, "IM000", 2040709, false),
+ Sql("create table tt93a (c0 int primary key, c1 char(10))",
+ SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1),
+ Sql("insert into tt93a values(1, 'a')",
+ SQL_DIAG_INSERT, 1, 0, -1, -1),
+ Sql("testMisc93a", testMisc93a),
+ Sql()
+};
+
+// 95
+
+static const Sql
+miscSql95[] = {
+ Sql("drop table tt95a",
+ SQL_DIAG_DROP_TABLE, -1, 0, -1, -1,
+ SQL_ERROR, "IM000", 2040709, false),
+ Sql("create table tt95a (a int not null, b char(10) not null, c int not null, d char(10), primary key(a, b)) storage(small)",
+ SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1),
+ // ordered index create and drop
+ Sql("create index xx95a on tt95a (c, d) nologging",
+ SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1),
+ Sql("drop index xx95a on tt95a",
+ SQL_DIAG_DROP_INDEX, -1, -1, -1, -1),
+ Sql("create index xx95a on tt95a (c) nologging",
+ SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1),
+ Sql("insert into tt95a values(1, 'a', 10, 'b')",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ Sql("insert into tt95a values(2, 'a', 20, 'b')",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ Sql("insert into tt95a values(3, 'a', 30, 'b')",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ Sql("select a from tt95a where c = 20",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 2, -1),
+ Sql("delete from tt95a where c = 10",
+ SQL_DIAG_DELETE_WHERE, 1, 1, -1, -1),
+ Sql("update tt95a set c = 300 where c = 30",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+ Sql("delete from tt95a where c = 300",
+ SQL_DIAG_DELETE_WHERE, 1, 1, -1, -1),
+ Sql("delete from tt95a",
+ SQL_DIAG_DELETE_WHERE, 1, 1, -1, -1),
+ // simple insert and rollback
+ Sql("-- simple insert and rollback"),
+ Sql(Sql::set_autocommit_off()),
+ Sql("insert into tt95a values(1, 'a', 10, 'b')",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ Sql("select count(*) from tt95a",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql("select count(*) from tt95a where c = 10",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql(Sql::do_rollback()),
+ Sql(Sql::set_autocommit_on()),
+ Sql("select count(*) from tt95a",
+ SQL_DIAG_SELECT_CURSOR, 1, 0, 0, -1),
+ // simple update and rollback
+ Sql("-- simple update and rollback"),
+ Sql("insert into tt95a values(1, 'a', 10, 'b')",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ Sql(Sql::set_autocommit_off()),
+ Sql("update tt95a set c = 20 where c = 10",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+ Sql("select count(*) from tt95a",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql("select count(*) from tt95a where c = 20",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql(Sql::do_rollback()),
+ Sql(Sql::set_autocommit_on()),
+ Sql("select count(*) from tt95a where c = 10",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ // simple delete and rollback
+ Sql("-- simple delete and rollback"),
+ Sql(Sql::set_autocommit_off()),
+ Sql("delete from tt95a where c = 10",
+ SQL_DIAG_DELETE_WHERE, 1, 1, -1, -1),
+ Sql("select count(*) from tt95a",
+ SQL_DIAG_SELECT_CURSOR, 0, 0, 0, -1),
+ Sql("select count(*) from tt95a where c = 10",
+ SQL_DIAG_SELECT_CURSOR, 0, 0, 0, -1),
+ Sql(Sql::do_rollback()),
+ Sql(Sql::set_autocommit_on()),
+ Sql("select count(*) from tt95a where c = 10",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ // multiple update
+ Sql("-- multiple update and rollback"),
+ Sql(Sql::set_autocommit_off()),
+ Sql("update tt95a set c = 20 where c = 10",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+ Sql("select count(*) from tt95a where c = 20",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql("update tt95a set c = 30 where c = 20",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+ Sql("select count(*) from tt95a where c = 30",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql("update tt95a set c = 40 where c = 30",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+ Sql("select count(*) from tt95a where c = 40",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql("update tt95a set c = 50 where c = 40",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+ Sql("select count(*) from tt95a where c = 50",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ Sql(Sql::do_rollback()),
+ Sql(Sql::set_autocommit_on()),
+ Sql("select count(*) from tt95a where c = 10",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1, -1),
+ // another variant which found no tuple via index (aligment issue)
+ Sql("drop table tt95b",
+ SQL_DIAG_DROP_TABLE, -1, 0, -1, -1,
+ SQL_ERROR, "IM000", 2040709, false),
+ Sql("create table tt95b (a int primary key, b char(10) not null, c int not null)",
+ SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1),
+ Sql("create index xx95b on tt95b (b, c) nologging",
+ SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1),
+ Sql("insert into tt95b values(0,'0123456789',1)",
+ SQL_DIAG_INSERT, 1, -1, -1, -1),
+ Sql("select a from tt95b where b='0123456789'",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 0, -1),
+ // update index key to different value
+ Sql("update tt95b set b = '9876543210' where b = '0123456789'",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+ // same value goes nuts...
+ Sql("update tt95b set b = '9876543210'",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+#if 0
+ // ...if done via index key (variant of halloween problem)
+ Sql("update tt95b set b = '9876543210' where b = '9876543210'",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, -1),
+#endif
+ Sql()
+};
+
+// 96
+
+static void
+testMisc96a(Test& test)
+{
+ // single thread
+ if (test.m_no != 1)
+ return;
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+ char sql[MAX_SQL], *sqlptr;
+ char tname[20];
+ strcpy(tname, "tt96a");
+ // drop table
+ scopy(sqlptr = sql, "drop table %s", tname);
+ test.exp(SQL_ERROR, "IM000", 2040709, false);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // create table with many attributes
+ unsigned attrs = 1 + opt.m_scale;
+ if (attrs > MAX_ATTRIBUTES_IN_TABLE)
+ attrs = MAX_ATTRIBUTES_IN_TABLE;
+ if (attrs > 64)
+ attrs = 64;
+ scopy(sqlptr = sql, "create table %s (c0 int primary key", tname);
+ for (unsigned j = 1; j < attrs; j++) {
+ if (j % 2 == 0)
+ scopy(sqlptr, ", c%d int unsigned not null", j);
+ else
+ scopy(sqlptr, ", c%d char(10) not null", j);
+ }
+ scopy(sqlptr, ")");
+ if (opt.m_fragtype != 0)
+ scopy(sqlptr, " storage(%s)", opt.m_fragtype);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // create or drop indexes
+ const unsigned seed = 1000037 * test.m_loop + 1000039 * opt.m_scale;
+ srandom(seed);
+ const unsigned imax = opt.m_scale < 20 ? opt.m_scale : 20;
+ AttributeMask* imasks = new AttributeMask[imax];
+ unsigned ccnt = 0;
+ unsigned dcnt = 0;
+ for (unsigned n = 0; n < imax; n++)
+ imasks[n].clear();
+ while (ccnt + dcnt < opt.m_scale) {
+ char iname[20];
+ unsigned n = urandom(imax);
+ sprintf(iname, "xx96a%02d", n);
+ AttributeMask& imask = imasks[n];
+ unsigned sel = urandom(10);
+ if (imask.isclear()) {
+ // create one
+ unsigned ncol = 0;
+ unsigned cols[MAX_ATTRIBUTES_IN_INDEX];
+ unsigned cnum = urandom(attrs);
+ cols[ncol++] = cnum;
+ while (ncol < MAX_ATTRIBUTES_IN_INDEX) {
+ unsigned sel2 = urandom(10);
+ if (sel2 < 2)
+ break;
+ unsigned cnum2 = urandom(attrs);
+ if (sel2 < 9 && cnum2 == 0)
+ continue;
+ unsigned j;
+ for (j = 0; j < ncol; j++) {
+ if (cols[j] == cnum2)
+ break;
+ }
+ if (j == ncol)
+ cols[ncol++] = cnum2;
+ }
+ if (sel < 3) {
+ scopy(sqlptr = sql, "create unique hash index %s on %s (", iname, tname);
+ for (unsigned j = 0; j < ncol; j++)
+ scopy(sqlptr, "%sc%d", j == 0 ? "" : ", ", cols[j]);
+ scopy(sqlptr, ")");
+ } else {
+ scopy(sqlptr = sql, "create index %s on %s (", iname, tname);
+ for (unsigned j = 0; j < ncol; j++)
+ scopy(sqlptr, "%sc%d", j == 0 ? "" : ", ", cols[j]);
+ scopy(sqlptr, ") nologging");
+ }
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ for (unsigned j = 0; j < ncol; j++)
+ imask.set(cols[j]);
+ ccnt++;
+ } else if (sel < 5 && ccnt > dcnt + 1) {
+ scopy(sqlptr = sql, "drop index %s on %s", iname, tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ imask.clear();
+ dcnt++;
+ }
+ }
+ // insert unique data
+ unsigned rows = opt.m_scale;
+ unsigned* uval = new unsigned[rows];
+ for (unsigned i = 0; i < rows; i++) {
+ uval[i] = urandom(4);
+ scopy(sqlptr = sql, "insert into %s values(", tname);
+ for (unsigned j = 0; j < attrs; j++) {
+ if (j != 0)
+ scopy(sqlptr, ",");
+ unsigned v = (i << 10) | (j << 2) | uval[i];
+ if (j == 0)
+ scopy(sqlptr, "%u", i);
+ else if (j % 2 == 0)
+ scopy(sqlptr, "%u", v);
+ else
+ scopy(sqlptr, "'%010u'", v);
+ }
+ scopy(sqlptr, ")");
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ }
+ // update each row via random index
+ for (unsigned i = 0; i < rows; i++) {
+ unsigned uold = uval[i];
+ uval[i] = 3 - uval[i];
+ AttributeMask imask;
+ do {
+ unsigned j = urandom(imax);
+ imask = imasks[j];
+ } while (imask.isclear());
+ scopy(sqlptr = sql, "update %s set", tname);
+ for (unsigned j = 1; j < attrs; j++) {
+ if (j != 1)
+ scopy(sqlptr, ",");
+ /*
+ * Equality update is just barely doable before savepoints
+ * provided we change value of keys in every index.
+ */
+ unsigned v = (i << 10) | (j << 2) | uval[i];
+ if (j == 0)
+ ;
+ else if (j % 2 == 0)
+ scopy(sqlptr, " c%d=%u", j, v);
+ else
+ scopy(sqlptr, " c%d='%010u'", j, v);
+ }
+ scopy(sqlptr, " where 1=1");
+ while (! imask.isclear()) {
+ unsigned j = urandom(attrs);
+ if (imask.get(j)) {
+ unsigned v = (i << 10) | (j << 2) | uold;
+ scopy(sqlptr, " and c%d=", j);
+ if (j == 0)
+ scopy(sqlptr, "%u", i);
+ else if (j % 2 == 0)
+ scopy(sqlptr, "%u", v);
+ else
+ scopy(sqlptr, "'%010u'", v);
+ imask.clear(j);
+ }
+ }
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ chkRowCount(test, hStmt, 1);
+ }
+ // delete all
+ scopy(sqlptr = sql, "delete from %s", tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ //
+ if (opt.m_v >= 2)
+ ndbout << tname << ": creates " << ccnt << " drops " << dcnt << endl;
+ delete [] imasks;
+ delete [] uval;
+ freeAll(test, hEnv, hDbc, hStmt);
+}
+
+static const Sql
+miscSql96[] = {
+ Sql("testMisc96a", testMisc96a),
+ Sql()
+};
+
+// 97
+
+static void
+testMisc97a(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+ const char* tname = "TT97A";
+ const char* iname = "XX97A";
+ char sql[MAX_SQL];
+ // create in some thread
+ lock_mutex();
+ if (my_sema == 0) {
+ if (opt.m_v >= 1)
+ ndbout << "thread " << test.m_no << " does setup" << endl;
+ sprintf(sql, "drop table %s", tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL[" << test.m_no << "]: " << sql << endl;
+ test.exp(SQL_ERROR, "IM000", 2040709, false);
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // a-pk b-index c-counter
+ sprintf(sql, "create table %s (a int primary key, b int, c int) storage(small)", tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL[" << test.m_no << "]: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ for (unsigned i = 0; i < opt.m_scale; i++) {
+ sprintf(sql, "insert into %s values (%d, %d, %d)", tname, i, 10 * i, 0);
+ if (opt.m_v >= 3)
+ ndbout << "SQL[" << test.m_no << "]: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ }
+ sprintf(sql, "create index %s on %s (b) nologging", iname, tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL[" << test.m_no << "]: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ my_sema = 1;
+ }
+ unlock_mutex();
+ assert(my_sema == 1);
+ // parallel run - default rotating pk, ts, is
+ // frob: low 3 hex digits give alt sequence e.g. 0x311 = pk, pk, is
+ // frob: 4-th hex digit non-zero says use NDB API e.g. 0x1000
+ unsigned typelist[3] = { 1, 2, 3 };
+ for (unsigned i = 0; i < 3; i++) {
+ unsigned t = (opt.m_frob >> (i * 4)) & 0xf;
+ if (t != 0)
+ typelist[i] = t;
+ }
+ unsigned type = typelist[(test.m_no - 1) % 3];
+ if ((opt.m_frob & 0xf000) == 0) {
+ for (unsigned i = 0; i < opt.m_scale; i++) {
+ if (type == 1) {
+ // pk update
+ sprintf(sql, "update %s set c = c + 1 where a = %d", tname, i % opt.m_scale);
+ if (opt.m_v >= 3)
+ ndbout << lock << "SQL[" << test.m_no << "]: " << sql << endl << unlock;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ }
+ if (type == 2) {
+ // table scan update
+ sprintf(sql, "update %s set c = c + 1 where b + 0 = %d", tname, 10 * i);
+ if (opt.m_v >= 3)
+ ndbout << lock << "SQL[" << test.m_no << "]: " << sql << endl << unlock;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ }
+ if (type == 3) {
+ // index scan update
+ sprintf(sql, "update %s set c = c + 1 where b = %d", tname, 10 * i);
+ if (opt.m_v >= 3)
+ ndbout << lock << "SQL[" << test.m_no << "]: " << sql << endl << unlock;
+ test.exp(SQL_NO_DATA, 0, 0, false);
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ }
+ }
+ } else {
+#ifdef ndbODBC
+#define CHK(o, x) do { if (! (x)) { fatal("line %d: %d %s", __LINE__, o->getNdbError().code, o->getNdbError().message); } } while (0)
+ Ndb* ndb = new Ndb("TEST_DB");
+ ndb->init();
+ CHK(ndb, ndb->waitUntilReady() == 0);
+ Int32 a, b, c;
+ for (unsigned i = 0; i < opt.m_scale; i++) {
+ if (type == 1) {
+ // pk update with exclusive read
+ NdbConnection* con;
+ NdbOperation* op;
+ CHK(ndb, (con = ndb->startTransaction()) != 0);
+ a = i;
+ c = -1;
+ CHK(con, (op = con->getNdbOperation(tname)) != 0);
+ CHK(op, op->readTupleExclusive() == 0);
+ CHK(op, op->equal((unsigned)0, (char*)&a, 0) == 0);
+ CHK(op, op->getValue(2, (char*)&c) != 0);
+ CHK(con, con->execute(NoCommit) == 0);
+ c = c + 1;
+ CHK(con, (op = con->getNdbOperation(tname)) != 0);
+ CHK(op, op->updateTuple() == 0);
+ CHK(op, op->equal((unsigned)0, (char*)&a, 0) == 0);
+ CHK(op, op->setValue(2, (char*)&c) == 0);
+ CHK(con, con->execute(Commit) == 0);
+ ndb->closeTransaction(con);
+ if (opt.m_v >= 3)
+ ndbout << lock << "thr " << test.m_no << " pk a=" << i << " c=" << c << endl << unlock;
+ }
+ if (type == 2) {
+ // table scan update
+ NdbConnection* con;
+ NdbOperation* op;
+ CHK(ndb, (con = ndb->startTransaction()) != 0);
+ CHK(con, (op = con->getNdbOperation(tname)) != 0);
+ CHK(con, op->openScanExclusive(240) == 0);
+ CHK(op, op->getValue((unsigned)0, (char*)&a) != 0);
+ CHK(op, op->getValue(2, (char*)&c) != 0);
+ CHK(con, con->executeScan() == 0);
+ unsigned rows = 0;
+ unsigned updates = 0;
+ while (1) {
+ int ret;
+ a = -1;
+ c = -1;
+ CHK(con, (ret = con->nextScanResult()) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ rows++;
+ if (a == i) {
+ NdbConnection* con2;
+ NdbOperation* op2;
+ CHK(ndb, (con2 = ndb->startTransaction()) != 0);
+ CHK(op, (op2 = op->takeOverForUpdate(con2)) != 0);
+ c = c + 1;
+ CHK(op2, op2->setValue(2, (char*)&c) == 0);
+ CHK(con2, con2->execute(Commit) == 0);
+ ndb->closeTransaction(con2);
+ updates++;
+ if (opt.m_v >= 3)
+ ndbout << lock << "thr " << test.m_no << " ts rows=" << rows << " a=" << i << " c=" << c << endl << unlock;
+ // test stop scan too
+ CHK(con, con->stopScan() == 0);
+ break;
+ }
+ }
+ ndb->closeTransaction(con);
+ test.chk(HStmt(hStmt), updates == 1, "got %u != 1", updates);
+ }
+ if (type == 3) {
+ // index scan update
+ NdbConnection* con;
+ NdbOperation* op;
+ CHK(ndb, (con = ndb->startTransaction()) != 0);
+ CHK(con, (op = con->getNdbOperation(iname, tname)) != 0);
+ CHK(con, op->openScanExclusive(240) == 0);
+ b = 10 * i;
+ CHK(con, op->setBound((unsigned)0, 4, &b, sizeof(b)) == 0);
+ CHK(op, op->getValue((unsigned)0, (char*)&a) != 0);
+ CHK(op, op->getValue(2, (char*)&c) != 0);
+ CHK(con, con->executeScan() == 0);
+ unsigned rows = 0;
+ unsigned updates = 0;
+ while (1) {
+ int ret;
+ a = -1;
+ c = -1;
+ CHK(con, (ret = con->nextScanResult()) == 0 || ret == 1);
+ if (ret == 1)
+ break;
+ rows++;
+ if (a == i) {
+ NdbConnection* con2;
+ NdbOperation* op2;
+ CHK(ndb, (con2 = ndb->startTransaction()) != 0);
+ CHK(op, (op2 = op->takeOverForUpdate(con2)) != 0);
+ c = c + 1;
+ CHK(op2, op2->setValue(2, (char*)&c) == 0);
+ CHK(con2, con2->execute(Commit) == 0);
+ ndb->closeTransaction(con2);
+ updates++;
+ if (opt.m_v >= 3)
+ ndbout << lock << "thr " << test.m_no << " is rows=" << rows << " a=" << i << " c=" << c << endl << unlock;
+ // test stop scan too
+ CHK(con, con->stopScan() == 0);
+ break;
+ }
+ }
+ ndb->closeTransaction(con);
+ test.chk(HStmt(hStmt), rows == 1, "got %u != 1", rows);
+ test.chk(HStmt(hStmt), updates == 1, "got %u != 1", updates);
+ }
+ }
+ delete ndb;
+#undef CHK
+#endif
+ }
+ // verify result
+ lock_mutex();
+ if (++my_sema == 1 + opt.m_threads) {
+ if (opt.m_v >= 1)
+ ndbout << "thread " << test.m_no << " does verification" << endl;
+ sprintf(sql, "select * from %s order by a", tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL[" << test.m_no << "]: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ long a, b, c;
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_SLONG, &a, 0, 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 2, SQL_C_SLONG, &b, 0, 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 3, SQL_C_SLONG, &c, 0, 0));
+ for (unsigned i = 0; i < opt.m_scale; i++) {
+ a = b = c = -1;
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ test.chk(HStmt(hStmt), a == i, "a: got %ld != %u", a, i);
+ test.chk(HStmt(hStmt), b == 10 * i, "b: got %ld != %u", b, 10 * i);
+ test.chk(HStmt(hStmt), c == opt.m_threads, "c: got %ld != %u", c, opt.m_threads);
+ if (opt.m_v >= 4)
+ ndbout << "verified " << i << endl;
+ }
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ if (opt.m_v >= 2)
+ ndbout << "thr " << test.m_no << " verified " << opt.m_scale << " rows" << endl;
+ my_sema = 0;
+ }
+ unlock_mutex();
+ freeAll(test, hEnv, hDbc, hStmt);
+}
+
+static const Sql
+miscSql97[] = {
+ Sql("testMisc97a", testMisc97a),
+ Sql()
+};
+
+// 99
+
+static void
+testMisc99a(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+ // bad
+ const char* sqlInsertBad = "insert into tt99a values(?, ?, ?, ?, ?)";
+ test.exp(SQL_ERROR, "21S01", -1, true);
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sqlInsertBad, SQL_NTS));
+ // good
+ const char* sqlInsert = "insert into tt99a (col1, col2, col3, col4, col5) values(?, ?, ?, ?, ?)";
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sqlInsert, SQL_NTS));
+ unsigned long value;
+ for (unsigned i = 1; i <= 5; i++) {
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, i, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &value, 0, 0));
+ }
+ const unsigned long base = 1000000000;
+ const unsigned long scale = 10;
+ for (value = base; value < base + scale; value++) {
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ }
+ // bug1: re-analyze of converted expression...
+ const char* sqlSelect = "select col5 from tt99a where col2 + 0 = ?";
+ unsigned long output;
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_ULONG, &output, 0, 0));
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sqlSelect, SQL_NTS));
+ // bug2: previous bind must survive a new SQLPrepare
+ if (0) {
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &value, 0, 0));
+ }
+ for (value = base; value < base + scale; value++) {
+ if (value > base + 4) {
+ // bug1: ...when IPD changed by JDBC
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &value, 0, 0));
+ }
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ output = (unsigned long)-1;
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ test.chk(HStmt(hStmt), output == value, "got %lu != %lu", output, value);
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+ test.timerCnt(1);
+ }
+ freeAll(test, hEnv, hDbc, hStmt);
+}
+
+static void
+testMisc99c(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+ const char* sql = "select b from tt99c where a = ?";
+ const unsigned long c1 = 2100000000U;
+ const unsigned long c2 = 4100000000U;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ unsigned long aval, bval;
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &aval, 0, 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_ULONG, &bval, 0, 0));
+ // uno
+ for (unsigned i = 0; i < opt.m_scale; i++) {
+ aval = c1;
+ bval = (unsigned long)-1;
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << " [?=" << (Uint64)aval << "]" << endl;
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ test.chk(HStmt(hStmt), bval == c2, "got %lu != %lu", bval, c2);
+ //test.exp(SQL_NO_DATA, 0, 0, true);
+ //test.run(HStmt(hStmt), SQLFetch(hStmt));
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+ }
+ // dos
+ for (unsigned i = 0; i < opt.m_scale; i++) {
+ break; // XXX not yet, hangs in NDB ?!?
+ aval = c2;
+ bval = (unsigned long)-1;
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << " [?=" << (Uint64)aval << "]" << endl;
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ test.chk(HStmt(hStmt), bval == c1, "got %lu != %lu", bval, c2);
+ //test.exp(SQL_NO_DATA, 0, 0, true);
+ //test.run(HStmt(hStmt), SQLFetch(hStmt));
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+ }
+ freeAll(test, hEnv, hDbc, hStmt);
+}
+
+static void
+testMisc99d(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+ const char* tname = "TT99D";
+ char sql[MAX_SQL];
+ sprintf(sql, "drop table %s", tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.exp(SQL_ERROR, "IM000", 2040709, false);
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ sprintf(sql, "create table %s (a bigint unsigned, b bigint, primary key (a))", tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLExecDirect(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ sprintf(sql, "insert into %s values (?, ?)", tname);
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql << endl;
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ // XXX replace by 100 when signed vs unsigned resolved
+ const unsigned num = 78;
+ SQLUBIGINT aval;
+ SQLBIGINT bval;
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT, 0, 0, &aval, 0, 0));
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 2, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &bval, 0, 0));
+ for (SQLBIGINT i = 0; i < num; i++) {
+ if (opt.m_v >= 3)
+ ndbout << "insert " << i << endl;
+ aval = i * i * i * i * i * i * i * i * i * i; // 10
+ bval = -aval;
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ }
+ sprintf(sql, "select a, b from tt99d where a = ?");
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql, SQL_NTS));
+ SQLUBIGINT kval;
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT, 0, 0, &kval, 0, 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_UBIGINT, &aval, 0, 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 2, SQL_C_SBIGINT, &bval, 0, 0));
+ for (SQLBIGINT i = 0; i < num; i++) {
+ kval = i * i * i * i * i * i * i * i * i * i; // 10
+ if (opt.m_v >= 3)
+ ndbout << "fetch " << i << " key " << kval << endl;
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ aval = bval = 0;
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ test.chk(HStmt(hStmt), aval == kval && bval == -kval, "got %llu, %lld != %llu, %lld", aval, bval, kval, -kval);
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+ }
+ freeAll(test, hEnv, hDbc, hStmt);
+}
+
+static void
+testMiscC2(Test& test)
+{
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+#if 0
+ {
+ char POP[255];
+ char PORT[255];
+ char ACCESSNODE[255];
+
+ const char* sqlSelect = "select PORT from AAA where POP=? and ACCESSNODE=?";
+
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sqlSelect, SQL_NTS));
+
+ for (int j=0; j<5; j++) {
+ printf("Loop %u\n", j);
+ printf("LINE %u\n", __LINE__);
+
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 255, 0, POP, 255, 0));
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 255, 0, ACCESSNODE, 255, 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_CHAR, PORT, 255, 0));
+
+ sprintf(POP, "a");
+ sprintf(ACCESSNODE, "b");
+
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ printf("got %s\n", PORT);
+ printf("LINE %u\n", __LINE__);
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ printf("LINE %u\n", __LINE__);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ printf("LINE %u\n", __LINE__);
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+ printf("LINE %u\n", __LINE__);
+ }
+ }
+ return;
+#endif
+
+ char POP[255];
+ char PORT[255];
+ char ACCESSNODE[255];
+ unsigned long VLAN = 0;
+ unsigned long SNMP_INDEX = 0;
+ unsigned long PORT_STATE = 0;
+ unsigned long STATIC_PORT = 0;
+ unsigned long COMMENT = 0;
+
+ const char* sqlSelect = "select PORT, PORT_STATE from PORTS where POP=? and ACCESSNODE=?";
+
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sqlSelect, SQL_NTS));
+
+ for (int j=0; j<5; j++) {
+ printf("Loop %u\n", j);
+ printf("LINE %u\n", __LINE__);
+
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 255, 0, POP, 255, 0));
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 255, 0, ACCESSNODE, 255, 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_CHAR, PORT, 255, 0));
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 2, SQL_C_ULONG, &PORT_STATE, 0, 0));
+
+ sprintf(POP, "row%u.i%u.bredband.com", 2, 3);
+ sprintf(ACCESSNODE, "as%u", 2);
+
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ for (int i=0; i < 3; i++) {
+ PORT_STATE=0;
+ sprintf(PORT, "XXXXXXXXXXXXXXXXXXXXX");
+
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ printf("got %s %lu\n", PORT, PORT_STATE);
+ // test.chk(HStmt(hStmt), false, "got %s != %s", "xxx", PORT);
+ }
+ printf("LINE %u\n", __LINE__);
+ test.exp(SQL_NO_DATA, 0, 0, true);
+ printf("LINE %u\n", __LINE__);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ printf("LINE %u\n", __LINE__);
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+ printf("LINE %u\n", __LINE__);
+ }
+}
+
+static const Sql
+miscSqlC2[] = {
+ Sql("drop table PORTS",
+ SQL_DIAG_DROP_TABLE, -1, 0, -1, -1,
+ SQL_ERROR, "IM000", 2040709, false),
+ Sql("create table PORTS (POP varchar(200) not null, ACCESSNODE varchar(200) not null, PORT varchar(200) not null, VLAN int unsigned, SNMP_INDEX int unsigned, PORT_STATE int unsigned, STATIC_PORT int unsigned, COMMENT int unsigned, primary key (POP,ACCESSNODE,PORT))",
+ SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1),
+ Sql("create index xxPORTS on PORTS (POP, ACCESSNODE) nologging",
+ SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1),
+ Sql("insert into PORTS values ('row2.i3.bredband.com','as2','Fa0/0',0,1,2,3,4)",
+ SQL_DIAG_INSERT, 1, 0, -1, -1),
+ Sql("insert into PORTS values ('row2.i3.bredband.com','as2','Fa0/1',1,2,3,4,5)",
+ SQL_DIAG_INSERT, 1, 0, -1, -1),
+ Sql("insert into PORTS values ('row2.i3.bredband.com','as2','Fa0/2',2,3,4,5,6)",
+ SQL_DIAG_INSERT, 1, 0, -1, -1),
+ Sql("select PORT, PORT_STATE from PORTS where POP='row2.i3.bredband.com' and ACCESSNODE='as2'",
+ SQL_DIAG_SELECT_CURSOR, 3, 3, -1, -1),
+
+ Sql("drop table AAA",
+ SQL_DIAG_DROP_TABLE, -1, 0, -1, -1,
+ SQL_ERROR, "IM000", 2040709, false),
+ Sql("create table AAA (POP varchar(200), ACCESSNODE varchar(200) not null, PORT varchar(200) not null, primary key (POP,ACCESSNODE,PORT))",
+ SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1),
+ Sql("create index xxAAA on AAA (POP, ACCESSNODE) nologging",
+ SQL_DIAG_CREATE_INDEX, -1, -1, -1, -1),
+ Sql("insert into AAA values ('a','b','A')",
+ SQL_DIAG_INSERT, 1, 0, -1, -1),
+
+ Sql("testMiscC2", testMiscC2),
+ Sql()
+};
+
+/*
+> SELECT PORT, PORT_STATE FROM PORTS where pop=? and accessnode=?
+> SELECT VLAN, SNMP_INDEX, PORT_STATE, STATIC_PORT, COMMENT FROM PORTS WHERE POP=? AND ACCESSNODE=? AND PORT=?
+> select count(*) from ports
+> select snmp_index from ports where pop='row2.i3.bredband.com' and accessnode='as2' and port='Fa0/2'
+
+> SELECT MAC, MAC_EXPIRE, IP, IP_EXPIRE, HOSTNAME, DETECTED, STATUS, STATIC_DNS, BLOCKED, NUM_REQUESTS, ACCESSTYPE, OS_TYPE, GATE_WAY, DIRTY_FLAG, LOCKED_IP FROM CLIENTS WHERE PORT=? AND ACCESSNODE=? AND POP=?
+> SELECT SERVICES.ACCESSTYPE, SERVICES.NUM_IP, SERVICES.TEXPIRE, SERVICES.CUSTOMER_ID, SERVICES.LEASED_NUM_IP, SERVICES.PROVIDER, SERVICES.LOCKED_IP, SERVICES.STATIC_DNS, SERVICES.SUSPENDED_SERVICE FROM SERVICES , ACCESSTYPES WHERE SERVICES.PORT = ? AND SERVICES.ACCESSNODE = ? AND SERVICES.POP = ? AND SERVICES.ACCESSTYPE=ACCESSTYPES.ACCESSTYPE
+*/
+
+static const Sql
+miscSql99[] = {
+ Sql("drop table tt99a",
+ SQL_DIAG_DROP_TABLE, -1, 0, -1, -1,
+ SQL_ERROR, "IM000", 2040709, false),
+ Sql("create table tt99a (col1 int unsigned primary key, col2 int unsigned, col3 int unsigned, col4 int unsigned, col5 int unsigned, col6 varchar(7) default 'abc123')",
+ SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1),
+ // inserts 10 rows, all same, start value 1000000000
+ Sql("testMisc99a", testMisc99a),
+ // interpreted scan plus bind parameter
+ Sql("select col1 from tt99a where col2 = ?",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1000000004, 1000000004),
+ Sql("select col1 from tt99a where col2 = 1000000000 + ?",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1000000004, 4),
+ Sql("select col1 from tt99a where col2 = ? + 1000000000",
+ SQL_DIAG_SELECT_CURSOR, 1, 1, 1000000004, 4),
+ // same not interpreted, tuple count 10
+ Sql("select col1 from tt99a where col2 + 0 = 1000000000 + ?",
+ SQL_DIAG_SELECT_CURSOR, 1, 10, 1000000004, 4),
+ // varchar variations
+ Sql("select count(*) from tt99a where col6 = 'abc123'",
+ SQL_DIAG_SELECT_CURSOR, 1, 10, 10, -1),
+ Sql("select count(*) from tt99a where left(col6, ?) = 'abc1'",
+ SQL_DIAG_SELECT_CURSOR, 1, 10, 10, 4),
+ Sql("select count(*) from tt99a where left(col6, ?) = 'abc1'",
+ SQL_DIAG_SELECT_CURSOR, 1, 10, 0, 3),
+ // tpc-b inspired, wrong optimization to direct update
+ Sql("drop table tt99b",
+ SQL_DIAG_DROP_TABLE, -1, 0, -1, -1,
+ SQL_ERROR, "IM000", 2040709, false),
+ Sql("create table tt99b(a int primary key, b int not null, c double precision)",
+ SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1),
+ Sql("insert into tt99b values(1, 10, 100.0)",
+ SQL_DIAG_INSERT, 1, 0, -1, -1),
+ Sql("insert into tt99b values(9, 90, 900.0)",
+ SQL_DIAG_INSERT, 1, 0, -1, -1),
+ Sql("create unique hash index tt99y on tt99b (b)",
+ SQL_DIAG_CREATE_INDEX, -1, 0, -1, -1),
+ // first scan update..
+ Sql("update tt99b set c = c + ? where a+0 = 1",
+ SQL_DIAG_UPDATE_WHERE, 1, 2, -1, 10),
+ Sql("update tt99b set c = c + ? where b+0 = 10",
+ SQL_DIAG_UPDATE_WHERE, 1, 2, -1, 10),
+ // then optimized..
+ Sql("update tt99b set c = c + ? where a = 1",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, 10),
+ Sql("update tt99b set c = c + ? where b = 10",
+ SQL_DIAG_UPDATE_WHERE, 1, 1, -1, 10),
+ // verify..
+ Sql("select count(*) from tt99b where 100-1 < c and c < 140-1",
+ SQL_DIAG_SELECT_CURSOR, 1, 2, 0, -1),
+ Sql("select count(*) from tt99b where 140-.001 < c and c < 140+.001",
+ SQL_DIAG_SELECT_CURSOR, 1, 2, 1, -1),
+ // unsigned test
+ Sql("drop table tt99c",
+ SQL_DIAG_DROP_TABLE, -1, 0, -1, -1,
+ SQL_ERROR, "IM000", 2040709, false),
+ Sql("create table tt99c(a int unsigned primary key, b int unsigned)",
+ SQL_DIAG_CREATE_TABLE, -1, 0, -1, -1),
+ Sql("insert into tt99c values(2100000000, 4100000000)",
+ SQL_DIAG_INSERT, 1, 0, -1, -1),
+ Sql("insert into tt99c (a, b) select b, a from tt99c",
+ SQL_DIAG_INSERT, 1, 1, -1, -1),
+ Sql("testMisc99c", testMisc99c),
+ // new external type SQL_C_[SU]BIGINT
+ Sql("testMisc99d", testMisc99d),
+ Sql()
+};
+
+static const struct { const Sql* sql; int minscale; }
+miscSql[11] = {
+ { miscSql90, 0 },
+ { miscSql91, 0 },
+ { miscSql92, 0 },
+ { miscSql93, 0 },
+ { 0, 0 }, // 94
+ { miscSql95, 0 },
+ { miscSql96, 0 },
+ { miscSql97, 0 },
+ { 0, 0 }, // 98
+ { miscSql99, 0 },
+ { miscSqlC2, 0 }
+};
+
+static void
+testSql(Test& test)
+{
+ const unsigned salt = test.m_stuff; // mess
+ if (opt.m_scale < miscSql[salt].minscale) {
+ if (opt.m_v >= 1)
+ ndbout << "skip - requires scale >= " << miscSql[salt].minscale << endl;
+ return;
+ }
+ assert(0 <= salt && salt < 11 && miscSql[salt].sql != 0);
+ SQLHANDLE hEnv, hDbc, hStmt;
+ allocAll(test, hEnv, hDbc, hStmt);
+ for (unsigned i = 0; ; i++) {
+ const Sql& sql = miscSql[salt].sql[i];
+ if (sql.m_sql == 0)
+ break;
+ if (opt.m_v >= 2)
+ ndbout << "SQL: " << sql.m_sql << endl;
+ if (sql.m_testFunc != 0) {
+ (*sql.m_testFunc)(test);
+ continue;
+ }
+ if (strncmp(sql.m_sql, "--", 2) == 0) {
+ continue;
+ }
+ if (strcmp(sql.m_sql, Sql::set_autocommit_on()) == 0) {
+ setAutocommit(test, hDbc, true);
+ continue;
+ }
+ if (strcmp(sql.m_sql, Sql::set_autocommit_off()) == 0) {
+ setAutocommit(test, hDbc, false);
+ continue;
+ }
+ if (strcmp(sql.m_sql, Sql::do_commit()) == 0) {
+ test.run(HDbc(hDbc), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_COMMIT));
+ continue;
+ }
+ if (strcmp(sql.m_sql, Sql::do_rollback()) == 0) {
+ test.run(HDbc(hDbc), SQLEndTran(SQL_HANDLE_DBC, hDbc, SQL_ROLLBACK));
+ continue;
+ }
+ if (opt.m_v >= 3) {
+ ndbout << "expect:";
+ ndbout << " ret=" << sql.m_ret;
+ ndbout << " rows=" << sql.m_rowCount;
+ ndbout << " tuples=" << sql.m_tuplesFetched;
+ ndbout << endl;
+ }
+ test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_UNBIND));
+ test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_RESET_PARAMS));
+ // prep
+ test.exp(sql.m_ret, sql.m_state, sql.m_native, false);
+ test.run(HStmt(hStmt), SQLPrepare(hStmt, (SQLCHAR*)sql.m_sql, SQL_NTS));
+ if (test.m_ret != SQL_SUCCESS)
+ continue;
+ // bind between prep and exec like JDBC
+ unsigned long bindValue = sql.m_bindValue;
+ for (int k = 0; k <= 1; k++) {
+ if (bindValue != -1) {
+ assert(strchr(sql.m_sql, '?') != 0);
+ test.run(HStmt(hStmt), SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &bindValue, 0, 0));
+ }
+ if (k == 0) {
+ if (bindValue != -1) {
+ test.run(HStmt(hStmt), SQLFreeStmt(hStmt, SQL_RESET_PARAMS));
+ // exec with unbound parameter
+ test.exp(SQL_ERROR, "HY010", -1, true);
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ test.chk(HStmt(hStmt), test.m_functionCode == sql.m_functionCode || sql.m_functionCode == -1, "func: got %d != %d", (int)test.m_functionCode, (int)sql.m_functionCode);
+ }
+ } else {
+ // exec
+ test.exp(sql.m_ret, sql.m_state, sql.m_native, sql.m_reset);
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ test.chk(HStmt(hStmt), test.m_functionCode == sql.m_functionCode || sql.m_functionCode == -1, "func: got %d != %d", (int)test.m_functionCode, (int)sql.m_functionCode);
+ }
+ }
+ if (sql.m_rowCount != -1) {
+ if (sql.m_functionCode == SQL_DIAG_SELECT_CURSOR) {
+ long lastValue;
+ if (sql.m_lastValue != -1)
+ test.run(HStmt(hStmt), SQLBindCol(hStmt, 1, SQL_C_SLONG, &lastValue, 0, 0));
+ unsigned k = 0;
+ do {
+ int rowCount = 0;
+ lastValue = -1;
+ while (1) {
+ test.exp(SQL_NO_DATA, 0, 0, false);
+ test.run(HStmt(hStmt), SQLFetch(hStmt));
+ if (test.m_ret == SQL_NO_DATA)
+ break;
+ rowCount++;
+ }
+ test.chk(HStmt(hStmt), rowCount == sql.m_rowCount, "rowCount: got %d != %d", (int)rowCount, (int)sql.m_rowCount);
+ if (sql.m_tuplesFetched != -1)
+ chkTuplesFetched(test, hStmt, sql.m_tuplesFetched);
+ if (rowCount > 0 && sql.m_lastValue != -1)
+ test.chk(HStmt(hStmt), lastValue == sql.m_lastValue, "lastValue: got %ld != %ld", (long)lastValue, (long)sql.m_lastValue);
+ test.run(HStmt(hStmt), SQLCloseCursor(hStmt));
+ if (++k >= opt.m_scale)
+ break;
+ test.run(HStmt(hStmt), SQLExecute(hStmt));
+ } while (1);
+ test.timerCnt(opt.m_scale);
+ } else {
+ assert(sql.m_lastValue == -1);
+ chkRowCount(test, hStmt, sql.m_rowCount);
+ if (sql.m_tuplesFetched != -1)
+ chkTuplesFetched(test, hStmt, sql.m_tuplesFetched);
+ test.timerCnt(1);
+ }
+ }
+ }
+ freeAll(test, hEnv, hDbc, hStmt);
+}
+
+// name, function, runmode, salt (0=const or n/a), description
+static const Case caseList[] = {
+ Case( "00alloc", testAlloc, Case::Thread, 0, "allocate handles" ),
+ Case( "01create", testCreate, Case::Single, 0, "create tables for the test" ),
+ Case( "02prepare", testPrepare, Case::Thread, 0, "prepare without execute" ),
+ Case( "03catalog", testCatalog, Case::Thread, 0, "catalog functions" ),
+ Case( "10insert", testInsert, Case::Thread, 1, "insert computed rows" ),
+ Case( "11delall", testDeleteAll, Case::Single, 0, "delete all rows via scan" ),
+ Case( "12insert", testInsert, Case::Thread, 1, "insert computed rows again" ),
+ Case( "13count", testCount, Case::Single, 0, "count rows" ),
+ Case( "14verpk", testVerifyPk, Case::Thread, 1, "verify via primary key" ),
+ Case( "15verscan", testVerifyScan, Case::Serial, 1, "verify via range scans" ),
+ Case( "16join", testJoin, Case::Single, 0, "multiple self-join" ),
+ Case( "17cart", testCart, Case::Single, 0, "cartesian join" ),
+ Case( "20updpk", testUpdatePk, Case::Thread, 2, "update via primary key" ),
+ Case( "21verpk", testVerifyPk, Case::Thread, 2, "verify via primary key" ),
+ Case( "22verscan", testVerifyScan, Case::Serial, 2, "verify via range scans" ),
+ Case( "23updscan", testUpdateScan, Case::Serial, 0, "update via scan" ),
+ Case( "24verpk", testVerifyPk, Case::Thread, 0, "verify via primary key" ),
+ Case( "25verscan", testVerifyScan, Case::Serial, 0, "verify via range scans" ),
+ Case( "26delpk", testDeletePk, Case::Thread, 0, "delete via primary key" ),
+ Case( "30trans", testTrans, Case::Single, 3, "rollback and commit" ),
+ Case( "31concur", testConcur, Case::Single, 0, "commit across open cursor" ),
+ Case( "32readcom", testReadcom, Case::Single, 0, "read committed" ),
+ Case( "40perf", testPerf, Case::Single, 0, "perf test prepare" ),
+ Case( "41perf", testPerf, Case::Thread, 1, "perf test NDB API" ),
+ Case( "42perf", testPerf, Case::Thread, 2, "perf test NDB ODBC" ),
+ Case( "90sql", testSql, Case::Single, 0, "misc SQL: metadata" ),
+ Case( "91sql", testSql, Case::Single, 1, "misc SQL: misc" ),
+ Case( "92sql", testSql, Case::Thread, 2, "misc SQL: scan rollback" ),
+ Case( "93sql", testSql, Case::Single, 3, "misc SQL: locking" ),
+ Case( "95sql", testSql, Case::Single, 5, "misc SQL: indexes (simple)" ),
+ Case( "96sql", testSql, Case::Single, 6, "misc SQL: indexes" ),
+ Case( "97sql", testSql, Case::Thread, 7, "misc SQL: indexes" ),
+ Case( "99sql", testSql, Case::Single, 9, "misc SQL: bug du jour" ),
+ Case( "C2", testSql, Case::Single, 10, "misc SQL: C2" )
+};
+
+static const unsigned caseCount = arraySize(caseList);
+
+static bool
+findCase(const char* name)
+{
+ for (unsigned i = 0; i < caseCount; i++) {
+ const Case& cc = caseList[i];
+ if (strstr(cc.m_name, name) != 0)
+ return true;
+ }
+ return false;
+}
+
+static void
+listCases()
+{
+ ndbout << "cases:" << endl;
+ unsigned m = 0;
+ for (unsigned i = 0; i < caseCount; i++) {
+ const Case& cc = caseList[i];
+ if (m < strlen(cc.m_name))
+ m = strlen(cc.m_name);
+ }
+ for (unsigned i = 0; i < caseCount; i++) {
+ const Case& cc = caseList[i];
+ char buf[200];
+ sprintf(buf, "%-*s [%-6s] %s", m, cc.m_name, cc.modename(), cc.m_desc);
+ ndbout << buf << endl;
+ }
+}
+
+// threads
+
+extern "C" { static void* testThr(void* arg); }
+
+struct Thr {
+ enum State {
+ Wait = 1, // wait for test case
+ Run = 2, // run the test case
+ Done = 3, // done with the case
+ Exit = 4 // exit thread
+ };
+ unsigned m_no; // thread number 1 .. max
+ NdbThread* m_thr; // thread id etc
+ const Case* m_case; // current case
+ State m_state; // condition variable
+ NdbMutex* m_mutex; // condition guard
+ NdbCondition* m_cond;
+ void* m_status; // exit status (not used)
+ Test m_test; // test runner
+ Thr(unsigned no, unsigned loop) :
+ m_no(no),
+ m_thr(0),
+ m_case(0),
+ m_state(Wait),
+ m_mutex(NdbMutex_Create()),
+ m_cond(NdbCondition_Create()),
+ m_status(0),
+ m_test(no, loop) {
+ }
+ ~Thr() {
+ destroy();
+ NdbCondition_Destroy(m_cond);
+ NdbMutex_Destroy(m_mutex);
+ }
+ void create() {
+ assert(m_thr == 0);
+ m_thr = NdbThread_Create(testThr, (void**)this, 64*1024, "test", NDB_THREAD_PRIO_LOW);
+ }
+ void destroy() {
+ if (m_thr != 0)
+ NdbThread_Destroy(&m_thr);
+ m_thr = 0;
+ }
+ void lock() {
+ NdbMutex_Lock(m_mutex);
+ }
+ void unlock() {
+ NdbMutex_Unlock(m_mutex);
+ }
+ void wait() {
+ NdbCondition_Wait(m_cond, m_mutex);
+ }
+ void signal() {
+ NdbCondition_Signal(m_cond);
+ }
+ void join() {
+ NdbThread_WaitFor(m_thr, &m_status);
+ m_thr = 0;
+ }
+ // called from main
+ void mainStart(const Case& cc) {
+ lock();
+ m_case = &cc;
+ m_state = Run;
+ signal();
+ unlock();
+ }
+ void mainStop() {
+ lock();
+ while (m_state != Done) {
+ if (opt.m_v >= 4)
+ ndbout << ::lock << "thr " << m_no << " [main] wait state=" << m_state << endl << ::unlock;
+ wait();
+ }
+ if (opt.m_v >= 4)
+ ndbout << ::lock << "thr " << m_no << " [main] done" << endl << ::unlock;
+ m_state = Wait;
+ unlock();
+ }
+ // run in thread
+ void testSelf() {
+ while (1) {
+ lock();
+ while (m_state != Run && m_state != Exit) {
+ if (opt.m_v >= 4)
+ ndbout << ::lock << "thr " << m_no << " [self] wait state=" << m_state << endl << ::unlock;
+ wait();
+ }
+ if (m_state == Run) {
+ if (opt.m_v >= 4)
+ ndbout << ::lock << "thr " << m_no << " [self] run" << endl << ::unlock;
+ assert(m_case != 0);
+ m_test.timerOn();
+ m_test.runCase(*m_case);
+ m_test.timerOff();
+ m_state = Done;
+ if (opt.m_v >= 4)
+ ndbout << ::lock << "thr " << m_no << " [self] done" << endl << ::unlock;
+ signal();
+ unlock();
+ } else if (m_state == Exit) {
+ unlock();
+ break;
+ } else {
+ assert(false);
+ }
+ }
+ if (opt.m_v >= 4)
+ ndbout << ::lock << "thr " << m_no << " [self] exit" << endl << ::unlock;
+ }
+};
+
+static void*
+testThr(void* arg)
+{
+ Thr& thr = *(Thr*)arg;
+ thr.testSelf();
+ return 0;
+}
+
+#ifdef DMALLOC
+extern "C" {
+
+static int malloc_bytes = 0;
+static int free_bytes = 0;
+
+static void
+malloc_track(const char *file, const unsigned int line, const int func_id, const DMALLOC_SIZE byte_size, const DMALLOC_SIZE alignment, const DMALLOC_PNT old_addr, const DMALLOC_PNT new_addr)
+{
+ if (func_id == DMALLOC_FUNC_MALLOC) {
+ malloc_bytes += byte_size;
+ return;
+ }
+ if (func_id == DMALLOC_FUNC_FREE) {
+ DMALLOC_SIZE size = 0;
+ dmalloc_examine(old_addr, &size, 0, 0, 0);
+ free_bytes += size;
+ // XXX useless - byte_size and size are 0
+ return;
+ }
+}
+
+}
+#endif /* DMALLOC */
+
+static void
+testMain()
+{
+#ifndef NDB_LINUX /* valgrind-1.0.3 does not support */
+ NdbThread_SetConcurrencyLevel(opt.m_threads + 2);
+#endif
+#ifdef DMALLOC
+ dmalloc_track(malloc_track);
+#endif
+ Test test(0, 0);
+#ifdef ndbODBC
+ Ndb* ndb = 0;
+ if (1) { // pre-alloc one Ndb object
+ ndb = new Ndb("TEST_DB");
+ ndb->init();
+ if (ndb->waitUntilReady() != 0) {
+ ndbout << ndb->getNdbError() << endl;
+ fatal("waitUntilReady");
+ }
+ }
+#endif
+ for (unsigned loop = 1; opt.m_loop == 0 || loop <= opt.m_loop; loop++) {
+ if (opt.m_v >= 2)
+ ndbout << "loop " << loop << endl;
+ // create new set of threads in each loop
+ Thr** thrList = new Thr* [1 + opt.m_threads];
+ for (unsigned n = 1; n <= opt.m_threads; n++) {
+ Thr& thr = *(thrList[n] = new Thr(n, loop));
+ thr.create();
+ if (opt.m_v >= 4)
+ ndbout << "thr " << n << " [main] created" << endl;
+ }
+#ifdef DMALLOC
+ malloc_bytes = free_bytes = 0;
+#endif
+ for (unsigned i = 0; i < caseCount; i++) {
+ const Case& cc = caseList[i];
+ if (! cc.matchcase())
+ continue;
+ if (opt.m_v >= 2)
+ ndbout << "RUN: " << cc.m_name << " - " << cc.m_desc << endl;
+ test.timerOn();
+ for (unsigned subloop = 1; subloop <= opt.m_subloop; subloop++) {
+ my_sema = 0;
+ if (opt.m_v >= 3)
+ ndbout << "subloop " << subloop << endl;
+ if (cc.m_mode == Case::Single) {
+ Thr& thr = *thrList[1];
+ thr.mainStart(cc);
+ thr.mainStop();
+ test.timerCnt(thr.m_test);
+ } else if (cc.m_mode == Case::Serial) {
+ for (unsigned n = 1; n <= opt.m_threads; n++) {
+ Thr& thr = *thrList[n];
+ thr.mainStart(cc);
+ thr.mainStop();
+ test.timerCnt(thr.m_test);
+ }
+ } else if (cc.m_mode == Case::Thread) {
+ for (unsigned n = 1; n <= opt.m_threads; n++) {
+ Thr& thr = *thrList[n];
+ thr.mainStart(cc);
+ }
+ for (unsigned n = 1; n <= opt.m_threads; n++) {
+ Thr& thr = *thrList[n];
+ thr.mainStop();
+ test.timerCnt(thr.m_test);
+ }
+ } else {
+ assert(false);
+ }
+ }
+ test.timerOff();
+ if (opt.m_v >= 1)
+ ndbout << cc.m_name << " total " << test << endl;
+ }
+#ifdef DMALLOC
+ if (opt.m_v >= 9) // XXX useless now
+ ndbout << "malloc " << malloc_bytes << " free " << free_bytes << " lost " << malloc_bytes - free_bytes << endl;
+#endif
+ // tell threads to exit
+ for (unsigned n = 1; n <= opt.m_threads; n++) {
+ Thr& thr = *thrList[n];
+ thr.lock();
+ thr.m_state = Thr::Exit;
+ thr.signal();
+ thr.unlock();
+ if (opt.m_v >= 4)
+ ndbout << "thr " << n << " [main] told to exit" << endl;
+ }
+ for (unsigned n = 1; n <= opt.m_threads; n++) {
+ Thr& thr = *thrList[n];
+ thr.join();
+ if (opt.m_v >= 4)
+ ndbout << "thr " << n << " [main] joined" << endl;
+ delete &thr;
+ }
+ delete[] thrList;
+ }
+#ifdef ndbODBC
+ delete ndb;
+#endif
+}
+
+static bool
+str2num(const char* arg, const char* str, unsigned* num, unsigned lo = 0, unsigned hi = 0)
+{
+ char* end = 0;
+ long n = strtol(str, &end, 0);
+ if (end == 0 || *end != 0 || n < 0) {
+ ndbout << arg << " " << str << " is invalid number" << endl;
+ return false;
+ }
+ if (lo != 0 && n < lo) {
+ ndbout << arg << " " << str << " is too small min = " << lo << endl;
+ return false;
+ }
+ if (hi != 0 && n > hi) {
+ ndbout << arg << " " << str << " is too large max = " << hi << endl;
+ return false;
+ }
+ *num = n;
+ return true;
+}
+
+NDB_COMMAND(testOdbcDriver, "testOdbcDriver", "testOdbcDriver", "testOdbcDriver", 65535)
+{
+ while (++argv, --argc > 0) {
+ const char* arg = argv[0];
+ if (strcmp(arg, "-case") == 0) {
+ if (++argv, --argc > 0) {
+ assert(opt.m_namecnt < arraySize(opt.m_name));
+ opt.m_name[opt.m_namecnt++] = argv[0];
+ if (findCase(argv[0]))
+ continue;
+ }
+ }
+ if (strcmp(arg, "-core") == 0) {
+ opt.m_core = true;
+ continue;
+ }
+ if (strcmp(arg, "-depth") == 0) {
+ if (++argv, --argc > 0) {
+ if (str2num(arg, argv[0], &opt.m_depth))
+ continue;
+ }
+ }
+ if (strcmp(arg, "-dsn") == 0) {
+ if (++argv, --argc > 0) {
+ opt.m_dsn = argv[0];
+ continue;
+ }
+ }
+ if (strcmp(arg, "-frob") == 0) {
+ if (++argv, --argc > 0) {
+ if (str2num(arg, argv[0], &opt.m_frob))
+ continue;
+ }
+ }
+ if (strcmp(arg, "-errs") == 0) {
+ if (++argv, --argc > 0) {
+ if (str2num(arg, argv[0], &opt.m_errs))
+ continue;
+ }
+ }
+ if (strcmp(arg, "-fragtype") == 0) {
+ if (++argv, --argc > 0) {
+ opt.m_fragtype = argv[0];
+ continue;
+ }
+ }
+ if (strcmp(arg, "-home") == 0) {
+ if (++argv, --argc > 0) {
+ opt.m_home = argv[0];
+ continue;
+ }
+ }
+ if (strcmp(arg, "-loop") == 0) {
+ if (++argv, --argc > 0) {
+ if (str2num(arg, argv[0], &opt.m_loop))
+ continue;
+ }
+ }
+ if (strcmp(arg, "-nogetd") == 0) {
+ opt.m_nogetd = true;
+ continue;
+ }
+ if (strcmp(arg, "-noputd") == 0) {
+ opt.m_noputd = true;
+ continue;
+ }
+ if (strcmp(arg, "-nosort") == 0) {
+ opt.m_nosort = true;
+ continue;
+ }
+ if (strcmp(arg, "-scale") == 0) {
+ if (++argv, --argc > 0) {
+ if (str2num(arg, argv[0], &opt.m_scale))
+ continue;
+ }
+ }
+ if (strcmp(arg, "-serial") == 0) {
+ opt.m_serial = true;
+ continue;
+ }
+ if (strcmp(arg, "-skip") == 0) {
+ if (++argv, --argc > 0) {
+ assert(opt.m_skipcnt < arraySize(opt.m_skip));
+ opt.m_skip[opt.m_skipcnt++] = argv[0];
+ if (findCase(argv[0]))
+ continue;
+ }
+ }
+ if (strcmp(arg, "-subloop") == 0) {
+ if (++argv, --argc > 0) {
+ if (str2num(arg, argv[0], &opt.m_subloop))
+ continue;
+ }
+ }
+ if (strcmp(arg, "-table") == 0) {
+ if (++argv, --argc > 0) {
+ opt.m_table = argv[0];
+ if (findTable())
+ continue;
+ }
+ }
+ if (strcmp(arg, "-threads") == 0) {
+ if (++argv, --argc > 0) {
+ if (str2num(arg, argv[0], &opt.m_threads, 1, MAX_THR))
+ continue;
+ }
+ }
+ if (strcmp(arg, "-trace") == 0) {
+ if (++argv, --argc > 0) {
+ if (str2num(arg, argv[0], &opt.m_trace))
+ continue;
+ }
+ }
+ if (strcmp(arg, "-v") == 0) {
+ if (++argv, --argc > 0) {
+ if (str2num(arg, argv[0], &opt.m_v))
+ continue;
+ }
+ }
+ if (strncmp(arg, "-v", 2) == 0 && isdigit(arg[2])) {
+ if (str2num(arg, &arg[2], &opt.m_v))
+ continue;
+ }
+ printusage();
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ homeEnv: {
+ static char env[1000];
+ if (opt.m_home != 0) {
+ sprintf(env, "NDB_HOME=%s", opt.m_home);
+ putenv(env);
+ }
+ }
+ traceEnv: {
+ static char env[40];
+ sprintf(env, "NDB_ODBC_TRACE=%u", opt.m_trace);
+ putenv(env);
+ }
+ debugEnv: {
+ static char env[40];
+ sprintf(env, "NDB_ODBC_DEBUG=%d", 1);
+ putenv(env);
+ }
+ testMain();
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
+// vim: set sw=4:
diff --git a/storage/ndb/test/odbc/test_compiler/Makefile b/storage/ndb/test/odbc/test_compiler/Makefile
new file mode 100644
index 00000000000..34819f21171
--- /dev/null
+++ b/storage/ndb/test/odbc/test_compiler/Makefile
@@ -0,0 +1,21 @@
+include .defs.mk
+
+TYPE = odbcdriver
+
+BIN_TARGET = test_compiler
+
+SOURCES = test_compiler.cpp
+
+CCFLAGS_LOC += \
+ -I$(NDB_TOP)/src/client/odbc/common \
+ -I$(NDB_TOP)/src/client/odbc/dictionary \
+ -I$(NDB_TOP)/src/client/odbc/compiler
+
+CCFLAGS_WARNINGS += -Wno-unused
+
+LIBS_SPEC += \
+ -lodbccompiler_pic
+
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/odbc/test_compiler/test_compiler.cpp b/storage/ndb/test/odbc/test_compiler/test_compiler.cpp
new file mode 100644
index 00000000000..042e9e6d4bf
--- /dev/null
+++ b/storage/ndb/test/odbc/test_compiler/test_compiler.cpp
@@ -0,0 +1,233 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**********************************************************************************
+ test_compiler.cpp
+ Tests the code tree generated
+ by the compiler
+***********************************************************************************/
+#include <stdio.h>
+#include <memory.h>
+#include "SQL_compiler.hpp"
+#include "SQL_code_tree.hpp"
+
+typedef struct stSTMT_REF_tag {
+ char* szTestStmt ;
+ SQL_compiler* pC ;
+ SQL_code_tree* pRefTree ;
+ int nFlag ; /* indicate if the struct haa a code tree and compiler and should be processed */
+} stSTMT_REF ;
+
+int compare_trees(SQL_code_tree* pCompilerOutput, SQL_code_tree* pReference) ;
+
+/* Assign statements to szTestStmt and NULL to pTestRef */
+
+static stSTMT_REF stTestRef[] = {
+/* 0 */ {"create table foo (pk integer primary key, a integer, b varchar(20), check (a is not null))", NULL, NULL, 0},
+/* 1 */ {"insert into foo (pk, a, b) values (1, 10, 'ett')", NULL, NULL, 0},
+/* 2 */ {"insert into foo values (2, 20)", NULL, NULL, 0},
+/* 3 */ {"delete from foo", NULL, NULL, 1},
+/* 4 */ {"delete from foo where pk=5", NULL, NULL, 0},
+/* 5 */ {"delete from foo where a<10 or b='test'", NULL, NULL, 0},
+/* 6 */ {"update foo set a=100, b=null", NULL, NULL, 0},
+/* 7 */ {"update foo set a=0 where pk=1", NULL, NULL, 0},
+/* 8 */ {"update foo set a=a+pk where b is null", NULL, NULL, 0},
+/* 9 */ {"select * from foo", NULL, NULL, 0},
+/* 10 */ {"select pk, a, b from foo where pk=1", NULL, NULL, 0},
+/* 11 */ {"select * from foo order by a", NULL, NULL, 0},
+/* 12 */ {"select * from foo A, foo B where A.pk=B.a and A.a<2*B.a", NULL, NULL, 0}
+} ;
+
+
+int main(int argc, char* argv[]){
+
+ int retcode = 0 ;
+ int nTests = sizeof(stTestRef)/sizeof(stSTMT_REF) ;
+
+ for(int c = 0 ; c < nTests ; c++) {
+ if(stTestRef[c].nFlag){
+ stTestRef[c].pC = new SQL_compiler() ;
+ stTestRef[c].pRefTree = new SQL_code_tree() ;
+ }
+ }
+
+ /* Create reference code trees */
+
+ /*
+ Statement: 0 "create table foo (pk integer primary key, a integer, b varchar(20), check (a is not null))"
+ */
+
+
+ /*
+ Statement: 1
+ */
+
+
+
+ /*
+ Statement: 2
+ */
+
+
+
+ /*
+ Statement: 3 "delete from foo"
+ */
+
+ stTestRef[3].pRefTree->shift('N') ;
+ stTestRef[3].pRefTree->shift('D') ;
+ stTestRef[3].pRefTree->shift('B') ;
+ stTestRef[3].pRefTree->reduce(0x2050400e, 3) ;
+ stTestRef[3].pRefTree->shift('F') ;
+ stTestRef[3].pRefTree->shift('O') ;
+ stTestRef[3].pRefTree->shift('O') ;
+ stTestRef[3].pRefTree->reduce(0x20502003, 3) ;
+ stTestRef[3].pRefTree->reduce(0x2050400f, 1) ;
+ stTestRef[3].pRefTree->reduce(0x20504007, 2) ;
+ stTestRef[3].pRefTree->reduce(0x21407003, 1) ;
+ stTestRef[3].pRefTree->shift(0x205021ca) ;
+ stTestRef[3].pRefTree->reduce(0x20630001, 1) ;
+ stTestRef[3].pRefTree->reduce(0x20815001, 1) ;
+ stTestRef[3].pRefTree->shift(0x21407002) ;
+ stTestRef[3].pRefTree->reduce(0x21407004, 3) ;
+ stTestRef[3].pRefTree->shift(0x21407002) ;
+ stTestRef[3].pRefTree->reduce(0x21407005, 1) ;
+ stTestRef[3].pRefTree->shift(0x21414001) ;
+ stTestRef[3].pRefTree->shift(0x21414002) ;
+ stTestRef[3].pRefTree->reduce(0x21407001, 4) ;
+ stTestRef[3].pRefTree->reduce(0x51506004, 1) ;
+ stTestRef[3].pRefTree->reduce(0x51506003, 1) ;
+
+ /*
+ Statement: 4
+ */
+
+
+
+ /*
+ Statement: 5
+ */
+
+
+
+
+ /*
+ Statement: 6
+ */
+
+
+
+
+ /*
+ Statement: 7
+ */
+
+
+
+ /*
+ Statement: 8
+ */
+
+
+
+ /*
+ Statement: 9
+ */
+
+
+
+ /*
+ Statement: 10
+ */
+
+
+
+ /*
+ Statement: 11
+ */
+
+
+
+ /*
+ Statement: 12
+ */
+
+
+
+ for(int i = 0 ; i < nTests ; i++){
+ /* Check to see if the statement has an associated code tree and compiler */
+ if(stTestRef[i].nFlag){
+ stTestRef[i].pC->prepare( stTestRef[i].szTestStmt, strlen(stTestRef[i].szTestStmt)) ;
+ if( 0 != compare_trees(&stTestRef[i].pC->m_code_tree, stTestRef[i].pRefTree) ){
+ printf("\nCompiler generated tree for statement #%d: \"%s\"\ndeviates from its reference\n", i, stTestRef[i].szTestStmt) ;
+ retcode = -1 ;
+ break ;
+ }else{
+ printf("\nTrees for statement #%d: \"%s\" match nicely -- OK\n", i, stTestRef[i].szTestStmt) ;
+ retcode = 0 ;
+ }
+ }
+ }
+
+ for(int d = 0 ; d < nTests ; d++) {
+ if(stTestRef[d].nFlag){
+ delete stTestRef[d].pC ;
+ delete stTestRef[d].pRefTree ;
+ }
+ }
+
+ return retcode ;
+
+}
+
+
+
+
+int compare_trees(SQL_code_tree* pCompilerOutput, SQL_code_tree* pReference){
+
+ int nTop = -1 ;
+
+ if(pCompilerOutput->top()== pReference->top()){
+
+ nTop = pReference->top() ;
+
+ }else{
+ printf("\npCompilerOutput->top() = %d;\tpReference->top() = %d\n", pCompilerOutput->top(), pReference->top()) ;
+ return -1 ;
+ }
+
+ pCompilerOutput->beginPostfix() ;
+ pReference->beginPostfix() ;
+
+ for(int r = 0 ; r < nTop ; r++){
+ if(pCompilerOutput->symbol() != pReference->symbol()){
+
+ printf("Deviation found in position %d\n", r) ;
+ printf("pCompilerOutput->symbol() = 0x%X;\tpReference->symbol() = 0x%X\n", pCompilerOutput->symbol(), pReference->symbol()) ;
+ return -1 ;
+
+ }else{
+
+ pCompilerOutput->nextPostfix() ;
+ pReference->nextPostfix() ;
+
+ }
+ }
+
+ return 0 ;
+
+}
+
diff --git a/storage/ndb/test/run-test/16node-tests.txt b/storage/ndb/test/run-test/16node-tests.txt
new file mode 100644
index 00000000000..11ade56c28c
--- /dev/null
+++ b/storage/ndb/test/run-test/16node-tests.txt
@@ -0,0 +1,733 @@
+# BASIC FUNCTIONALITY
+max-time: 500
+cmd: testBasic
+args: -n PkRead
+
+max-time: 500
+cmd: testBasic
+args: -n PkUpdate
+
+max-time: 500
+cmd: testBasic
+args: -n PkDelete
+
+max-time: 500
+cmd: testBasic
+args: -n PkInsert
+
+max-time: 600
+cmd: testBasic
+args: -n UpdateAndRead
+
+max-time: 500
+cmd: testBasic
+args: -n PkReadAndLocker T6
+
+max-time: 500
+cmd: testBasic
+args: -n PkReadAndLocker2 T6
+
+max-time: 500
+cmd: testBasic
+args: -n PkReadUpdateAndLocker T6
+
+max-time: 500
+cmd: testBasic
+args: -n ReadWithLocksAndInserts T6
+
+max-time: 500
+cmd: testBasic
+args: -n PkInsertTwice T1 T6 T10
+
+max-time: 1500
+cmd: testBasic
+args: -n Fill T13
+
+max-time: 1500
+cmd: testBasic
+args: -n Fill T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitSleep T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommit626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitAndClose T6
+
+max-time: 500
+cmd: testBasic
+args: -n Commit626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitTry626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitAsMuch626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommit626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitRollback626 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n Commit630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitTry630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitAsMuch630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommit630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitRollback630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitAndClose T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n RollbackUpdate T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n RollbackDeleteMultiple T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n ImplicitRollbackDelete T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitDelete T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n RollbackNothing T1 T6
+
+max-time: 500
+cmd: testBasicAsynch
+args: -n PkInsertAsynch
+
+max-time: 500
+cmd: testBasicAsynch
+args: -n PkReadAsynch
+
+max-time: 500
+cmd: testBasicAsynch
+args: -n PkUpdateAsynch
+
+max-time: 500
+cmd: testBasicAsynch
+args: -n PkDeleteAsynch
+
+max-time: 500
+cmd: testBasic
+args: -n MassiveRollback T1 T6 T13
+
+max-time: 500
+cmd: testBasic
+args: -n MassiveRollback2 T1 T6 T13
+
+max-time: 500
+cmd: testTimeout
+args: T1
+
+# SCAN TESTS
+#
+max-time: 500
+cmd: testScan
+args: -n ScanRead16
+
+max-time: 500
+cmd: testScan
+args: -n ScanRead240
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadCommitted240
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdate
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdate2 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanDelete
+
+max-time: 500
+cmd: testScan
+args: -n ScanDelete2 T10
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdateAndScanRead T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAndLocker T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAndPkRead T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanRead488 -l 10 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanRead488O -l 10 T6
+
+max-time: 1000
+cmd: testScan
+args: -n ScanRead488_Mixed -l 10 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanRead488Timeout -l 10 T6
+
+max-time: 600
+cmd: testScan
+args: -n ScanRead40 -l 100 T2
+
+max-time: 1800
+cmd: testScan
+args: -n ScanRead100 -l 100 T1
+
+max-time: 600
+cmd: testScan
+args: -n ScanRead40 -l 100 T1
+
+max-time: 1800
+cmd: testScan
+args: -n ScanRead40RandomTable -l 100 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanWithLocksAndInserts T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAbort T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAbort15 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAbort240 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdateAbort16 T6
+
+max-time: 3600
+cmd: testScan
+args: -n ScanReadRestart T1 T6 T13
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdateRestart T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckGetValue T6
+
+max-time: 500
+cmd: testScan
+args: -n CloseWithoutStop T6
+
+max-time: 500
+cmd: testScan
+args: -n NextScanWhenNoMore T6
+
+max-time: 500
+cmd: testScan
+args: -n ExecuteScanWithoutOpenScan T6
+
+max-time: 500
+cmd: testScan
+args: -n OnlyOpenScanOnce T6
+
+max-time: 500
+cmd: testScan
+args: -n OnlyOneOpInScanTrans T6
+
+max-time: 500
+cmd: testScan
+args: -n OnlyOneOpBeforeOpenScan T6
+
+max-time: 500
+cmd: testScan
+args: -n OnlyOneScanPerTrans T6
+
+max-time: 500
+cmd: testScan
+args: -n NoCloseTransaction T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckInactivityTimeOut T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckInactivityBeforeClose T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckAfterTerror T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5021 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReaderror5022 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5023 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5024 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5025 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5030 T1
+
+max-time: 500
+cmd: testScan
+args: -n InsertDelete T1 T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckAfterTerror T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadWhileNodeIsDown T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanRestart T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanParallelism
+
+#
+# DICT TESTS
+max-time: 1500
+cmd: testDict
+args: -n CreateAndDrop
+
+max-time: 1500
+cmd: testDict
+args: -n CreateAndDropWithData
+
+max-time: 1500
+cmd: testDict
+args: -n CreateAndDropDuring T6 T10
+
+max-time: 1500
+cmd: testDict
+args: -n CreateInvalidTables
+
+max-time: 1500
+cmd: testDict
+args: -n CreateTableWhenDbIsFull T6
+
+max-time: 1500
+cmd: testDict
+args: -n CreateMaxTables T6
+
+max-time: 500
+cmd: testDict
+args: -n FragmentTypeSingle T1
+
+max-time: 1500
+cmd: testDict
+args: -n FragmentTypeAllSmall T1 T6 T7 T8
+
+max-time: 1500
+cmd: testDict
+args: -n FragmentTypeAllLarge T1 T6 T7 T8
+
+max-time: 1500
+cmd: testDict
+args: -n TemporaryTables T1 T6 T7 T8
+
+#
+# TEST NDBAPI
+#
+max-time: 500
+cmd: testDataBuffers
+args:
+
+# Testsuite: testNdbApi
+# Number of tests: 5
+max-time: 500
+cmd: testNdbApi
+args: -n MaxNdb T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n MaxTransactions T1 T6 T7 T8 T13
+
+max-time: 500
+cmd: testNdbApi
+args: -n MaxOperations T1 T6 T7 T8 T13
+
+max-time: 500
+cmd: testNdbApi
+args: -n MaxGetValue T1 T6 T7 T8 T13
+
+max-time: 500
+cmd: testNdbApi
+args: -n MaxEqual
+
+max-time: 500
+cmd: testNdbApi
+args: -n DeleteNdb T1 T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n WaitUntilReady T1 T6 T7 T8 T13
+
+max-time: 500
+cmd: testNdbApi
+args: -n GetOperationNoTab T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n NdbErrorOperation T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n MissingOperation T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n GetValueInUpdate T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n UpdateWithoutKeys T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n UpdateWithoutValues T6
+
+#max-time: 500
+#cmd: testInterpreter
+#args: T1
+#
+max-time: 150000
+cmd: testOperations
+args:
+
+max-time: 15000
+cmd: testTransactions
+args:
+
+max-time: 1500
+cmd: testRestartGci
+args: T6
+
+max-time: 600
+cmd: testBlobs
+args:
+
+max-time: 5000
+cmd: testOIBasic
+args:
+
+max-time: 2500
+cmd: testBitfield
+args:
+
+max-time: 2500
+cmd: testPartitioning
+args:
+
+max-time: 25000
+cmd: atrt-mysql-test-run
+args: --force
+
+#
+# INDEX
+#
+max-time: 1500
+cmd: testIndex
+args: -n CreateAll T1 T6 T13
+
+#-m 7200 1: testIndex -n InsertDeleteGentle T7
+max-time: 3600
+cmd: testIndex
+args: -n InsertDelete T1 T10
+
+#-m 3600 1: testIndex -n CreateLoadDropGentle T7
+max-time: 3600
+cmd: testIndex
+args: -n CreateLoadDrop T1 T10
+
+#
+# BACKUP
+#
+max-time: 600
+cmd: atrt-testBackup
+args: -n BackupOne T1 T6 T3 I3
+
+#
+#
+# SYSTEM RESTARTS
+#
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR3 T6
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR4 T6
+
+#
+# NODE RESTARTS
+#
+max-time: 2500
+cmd: testNodeRestart
+args: -n NoLoad T6
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n MixedPkRead T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -l 1 -n MixedPkReadPkUpdate
+
+max-time: 2500
+cmd: testNodeRestart
+args: -l 1 -n MixedReadUpdateScan
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n CommittedRead T1
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n FullDb T6 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartRandomNode T6 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartRandomNodeError T6 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartRandomNodeInitial T6 T13
+
+max-time: 3600
+cmd: testNodeRestart
+args: -l 1 -n RestartNFDuringNR T6 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartMasterNodeError T6 T8 T13
+
+max-time: 3600
+cmd: testNodeRestart
+args: -n RestartNodeDuringLCP T6
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n TwoNodeFailure T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n TwoMasterNodeFailure T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n FiftyPercentFail T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartAllNodes T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartAllNodesAbort T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartAllNodesError9999 T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n FiftyPercentStopAndWait T6 T8 T13
+
+#max-time: 500
+#cmd: testNodeRestart
+#args: -n StopOnError T1
+#
+#
+max-time: 2500
+cmd: testIndex
+args: -n NFNR1 T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n NFNR2 T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n NFNR3 T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n BuildDuring T6
+
+max-time: 2500
+cmd: testIndex
+args: -l 2 -n SR1 T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n NFNR1_O T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n NFNR2_O T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n NFNR3_O T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n BuildDuring_O T6
+
+max-time: 2500
+cmd: testIndex
+args: -l 2 -n SR1_O T6 T13
+
+max-time: 500
+cmd: testIndex
+args: -n MixedTransaction T1
+
+max-time: 2500
+cmd: testDict
+args: -n NF1 T1 T6 T13
+
+#
+max-time: 1500
+cmd: testSystemRestart
+args: -l 1 -n SR6 T1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -l 1 -n SR7 T1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -l 1 -n SR8 T1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -l 1 -n SR9 T1
+
+#
+max-time: 2500
+cmd: test_event
+args: -n BasicEventOperation T1 T6
+
+#
+#
+# SYSTEM RESTARTS
+#
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR1 T1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR1 T6
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR1 T7
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR1 T8
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR2 T1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR2 T6
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR2 T7
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_UNDO T1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_UNDO T6
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_UNDO T7
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_UNDO T8
+
+# OLD FLEX
+max-time: 500
+cmd: flexBench
+args: -c 25 -t 10
+
+max-time: 500
+cmd: flexHammer
+args: -r 5 -t 32
+
diff --git a/storage/ndb/test/run-test/Makefile.am b/storage/ndb/test/run-test/Makefile.am
new file mode 100644
index 00000000000..1067328dcc3
--- /dev/null
+++ b/storage/ndb/test/run-test/Makefile.am
@@ -0,0 +1,29 @@
+
+testdir=$(prefix)/mysql-test/ndb
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_util.mk.am
+include $(top_srcdir)/ndb/config/type_mgmapiclient.mk.am
+
+test_PROGRAMS = atrt
+test_DATA=daily-basic-tests.txt daily-devel-tests.txt 16node-tests.txt
+test_SCRIPTS=atrt-analyze-result.sh atrt-gather-result.sh atrt-setup.sh \
+ atrt-clear-result.sh make-config.sh make-index.sh make-html-reports.sh
+
+atrt_SOURCES = main.cpp run-test.hpp
+INCLUDES_LOC = -I$(top_srcdir)/ndb/test/include
+LDADD_LOC = $(top_builddir)/ndb/test/src/libNDBT.a \
+ $(top_builddir)/ndb/src/libndbclient.la \
+ $(top_builddir)/dbug/libdbug.a \
+ $(top_builddir)/mysys/libmysys.a \
+ $(top_builddir)/strings/libmystrings.a @NDB_SCI_LIBS@
+
+wrappersdir=$(prefix)/bin
+wrappers_SCRIPTS=atrt-testBackup atrt-mysql-test-run
+
+EXTRA_DIST = $(test_DATA) $(test_SCRIPTS) $(wrappers_SCRIPTS) README.ATRT
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp:
diff --git a/storage/ndb/test/run-test/README b/storage/ndb/test/run-test/README
new file mode 100644
index 00000000000..d5da8f05c17
--- /dev/null
+++ b/storage/ndb/test/run-test/README
@@ -0,0 +1,43 @@
+run-test/README
+
+This document describes how atrt works and how to use it.
+
+atrt is a test program driver.
+atrt supports fully distributed test and utilizes ndb_cpcd.
+
+=================================
+atrt has the following main loop:
+
+/**
+ * Psuedo code for atrt
+ */
+ read config file (default d.txt)
+ contact each ndb_cpcd
+ start each ndb_mgmd
+ connect to each ndb_mgmd
+ for each read(test case)
+ do
+ if previous test failed (or is first test)
+ stop each ndbd
+ start each ndbd
+ wait for ndbd to get started
+
+ start each mysqld
+
+ start each test prg
+
+ wait while all is running and max time not elapsed
+
+ stop each mysqld
+
+ stop each test prg
+
+ gather result
+
+ done
+/**
+ * End of psuedo code
+ */
+
+=================================
+
diff --git a/storage/ndb/test/run-test/README.ATRT b/storage/ndb/test/run-test/README.ATRT
new file mode 100644
index 00000000000..7fe04ccdac4
--- /dev/null
+++ b/storage/ndb/test/run-test/README.ATRT
@@ -0,0 +1,34 @@
+
+!-- install ndb_cpcd
+!-- many steps? future RPM
+
+!-- deploy binaries and libraries to hosts, rsync
+% export DEPLOY_DST="mc05:/space/tomas/keso"
+% ssh mc05 mkdir /space/tomas/keso
+% export RSYNC_RSH=ssh
+% make
+
+% mkdir -p /tmp/atrt-run-2-node
+% cd /tmp/atrt-run-2-node
+% cat > d.txt
+baseport: 9321
+basedir: /space/tomas/keso
+mgm: localhost
+ndb: localhost localhost
+api: localhost
+% cat > default.txt
+[DB DEFAULT]
+NoOfReplicas: 2
+% bin/make-config.sh -m d.txt -t default.txt -d .
+
+% atrt -v -v
+test_event -r 5 T1
+
+
+!-- check output
+% tail -f /space/tomas/keso/run/4.ndb_api/log.out
+
+
+!-- check processes
+% export NDB_CPCC_HOSTS="mc05"
+% ndb_cpcc
diff --git a/storage/ndb/test/run-test/atrt-analyze-result.sh b/storage/ndb/test/run-test/atrt-analyze-result.sh
new file mode 100755
index 00000000000..0fa46e918ef
--- /dev/null
+++ b/storage/ndb/test/run-test/atrt-analyze-result.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+f=`find result -name 'log.out' | xargs grep "NDBT_ProgramExit: " | grep -c "Failed"`
+o=`find result -name 'log.out' | xargs grep "NDBT_ProgramExit: " | grep -c "OK"`
+
+if [ $o -gt 0 -a $f -eq 0 ]
+then
+ exit 0
+fi
+
+exit 1
+
diff --git a/storage/ndb/test/run-test/atrt-clear-result.sh b/storage/ndb/test/run-test/atrt-clear-result.sh
new file mode 100755
index 00000000000..57d3d43d247
--- /dev/null
+++ b/storage/ndb/test/run-test/atrt-clear-result.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+set -e
+rm -rf result
diff --git a/storage/ndb/test/run-test/atrt-example.tgz b/storage/ndb/test/run-test/atrt-example.tgz
new file mode 100644
index 00000000000..8455b2eb00d
--- /dev/null
+++ b/storage/ndb/test/run-test/atrt-example.tgz
Binary files differ
diff --git a/storage/ndb/test/run-test/atrt-gather-result.sh b/storage/ndb/test/run-test/atrt-gather-result.sh
new file mode 100755
index 00000000000..93d4ae428d0
--- /dev/null
+++ b/storage/ndb/test/run-test/atrt-gather-result.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+set -e
+
+mkdir -p result
+cd result
+rm -rf *
+
+while [ $# -gt 0 ]
+do
+ rsync -a "$1" .
+ shift
+done
+
+
+
diff --git a/storage/ndb/test/run-test/atrt-mysql-test-run b/storage/ndb/test/run-test/atrt-mysql-test-run
new file mode 100755
index 00000000000..2ebc11b0070
--- /dev/null
+++ b/storage/ndb/test/run-test/atrt-mysql-test-run
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+set -x
+p=`pwd`
+cd $MYSQL_BASE_DIR/mysql-test
+./mysql-test-run --with-ndbcluster --ndb-connectstring=$NDB_CONNECTSTRING $* | tee $p/output.txt
+
+f=`grep -c '\[ fail \]' $p/output.txt`
+o=`grep -c '\[ pass \]' $p/output.txt`
+
+if [ $o -gt 0 -a $f -eq 0 ]
+then
+ echo "NDBT_ProgramExit: OK"
+ exit 0
+fi
+
+echo "NDBT_ProgramExit: Failed"
+exit 1
diff --git a/storage/ndb/test/run-test/atrt-setup.sh b/storage/ndb/test/run-test/atrt-setup.sh
new file mode 100755
index 00000000000..aff5d4119dc
--- /dev/null
+++ b/storage/ndb/test/run-test/atrt-setup.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+set -e
+
+ssh $1 mkdir -p $3
+rsync -a --delete --force --ignore-errors $2 $1:$3
diff --git a/storage/ndb/test/run-test/atrt-testBackup b/storage/ndb/test/run-test/atrt-testBackup
new file mode 100755
index 00000000000..3ed7641a42e
--- /dev/null
+++ b/storage/ndb/test/run-test/atrt-testBackup
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+PATH=$PATH:$MYSQL_BASE_DIR/bin
+export PATH
+
+testBackup $*
diff --git a/storage/ndb/test/run-test/basic.txt b/storage/ndb/test/run-test/basic.txt
new file mode 100644
index 00000000000..ec9e21359e5
--- /dev/null
+++ b/storage/ndb/test/run-test/basic.txt
@@ -0,0 +1,763 @@
+# BASIC FUNCTIONALITY
+max-time: 500
+cmd: testBasic
+args: -n PkRead
+
+max-time: 500
+cmd: testBasic
+args: -n PkUpdate
+
+max-time: 500
+cmd: testBasic
+args: -n PkDelete
+
+max-time: 500
+cmd: testBasic
+args: -n PkInsert
+
+max-time: 600
+cmd: testBasic
+args: -n UpdateAndRead
+
+max-time: 500
+cmd: testBasic
+args: -n PkReadAndLocker T6
+
+max-time: 500
+cmd: testBasic
+args: -n PkReadAndLocker2 T6
+
+max-time: 500
+cmd: testBasic
+args: -n PkReadUpdateAndLocker T6
+
+max-time: 500
+cmd: testBasic
+args: -n ReadWithLocksAndInserts T6
+
+max-time: 500
+cmd: testBasic
+args: -n PkInsertTwice T1 T6 T10
+
+max-time: 1500
+cmd: testBasic
+args: -n Fill T1
+
+max-time: 1500
+cmd: testBasic
+args: -n Fill T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitSleep T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommit626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitAndClose T6
+
+max-time: 500
+cmd: testBasic
+args: -n Commit626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitTry626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitAsMuch626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommit626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitRollback626 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n Commit630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitTry630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitAsMuch630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommit630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitRollback630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitAndClose T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n RollbackUpdate T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n RollbackDeleteMultiple T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n ImplicitRollbackDelete T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitDelete T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n RollbackNothing T1 T6
+
+max-time: 500
+cmd: testBasicAsynch
+args: -n PkInsertAsynch
+
+max-time: 500
+cmd: testBasicAsynch
+args: -n PkReadAsynch
+
+max-time: 500
+cmd: testBasicAsynch
+args: -n PkUpdateAsynch
+
+max-time: 500
+cmd: testBasicAsynch
+args: -n PkDeleteAsynch
+
+max-time: 500
+cmd: testBasic
+args: -n MassiveRollback T1 T6 T13
+
+max-time: 500
+cmd: testBasic
+args: -n MassiveRollback2 T1 T6 T13
+
+#-m 500 1: testBasic -n ReadConsistency T6
+cmd: testTimeout
+args: -n DontTimeoutTransaction T1
+
+cmd: testTimeout
+args: -n DontTimeoutTransaction5 T1
+
+cmd: testTimeout
+args: -n TimeoutTransaction T1
+
+cmd: testTimeout
+args: -n TimeoutTransaction5 T1
+
+cmd: testTimeout
+args: -n BuddyTransNoTimeout T1
+
+cmd: testTimeout
+args: -n BuddyTransNoTimeout5 T1
+
+#
+# SCAN TESTS
+#
+max-time: 500
+cmd: testScan
+args: -n ScanRead16
+
+max-time: 500
+cmd: testScan
+args: -n ScanRead240
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadCommitted240
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdate
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdate2 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanDelete
+
+max-time: 500
+cmd: testScan
+args: -n ScanDelete2 T10
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdateAndScanRead T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAndLocker T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAndPkRead T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanRead488 -l 10 T6
+
+max-time: 600
+cmd: testScan
+args: -n ScanRead40 -l 100 T2
+
+max-time: 1800
+cmd: testScan
+args: -n ScanRead100 -l 100 T1
+
+max-time: 600
+cmd: testScan
+args: -n ScanRead40 -l 100 T1
+
+max-time: 1800
+cmd: testScan
+args: -n ScanRead40RandomTable -l 100 T1
+
+max-time: 3600
+cmd: testScan
+args: -n ScanRead40RandomTable -l 1000 T2
+
+max-time: 500
+cmd: testScan
+args: -n ScanWithLocksAndInserts T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAbort T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAbort15 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAbort240 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdateAbort16 T6
+
+max-time: 3600
+cmd: testScan
+args: -n ScanReadRestart T1 T6 T13
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdateRestart T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckGetValue T6
+
+max-time: 500
+cmd: testScan
+args: -n CloseWithoutStop T6
+
+max-time: 500
+cmd: testScan
+args: -n NextScanWhenNoMore T6
+
+max-time: 500
+cmd: testScan
+args: -n ExecuteScanWithoutOpenScan T6
+
+max-time: 500
+cmd: testScan
+args: -n OnlyOpenScanOnce T6
+
+max-time: 500
+cmd: testScan
+args: -n OnlyOneOpInScanTrans T6
+
+max-time: 500
+cmd: testScan
+args: -n OnlyOneOpBeforeOpenScan T6
+
+max-time: 500
+cmd: testScan
+args: -n OnlyOneScanPerTrans T6
+
+max-time: 500
+cmd: testScan
+args: -n NoCloseTransaction T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckInactivityTimeOut T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckInactivityBeforeClose T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckAfterTerror T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5021 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReaderror5022 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5023 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5024 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5025 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5030 T1
+
+# OLD FLEX
+max-time: 500
+cmd: flexBench
+args: -c 25 -t 10
+
+max-time: 500
+cmd: flexHammer
+args: -r 5 -t 32
+
+#
+# DICT TESTS
+max-time: 1500
+cmd: testDict
+args: -n CreateAndDrop
+
+max-time: 1500
+cmd: testDict
+args: -n CreateAndDropWithData
+
+max-time: 1500
+cmd: testDict
+args: -n CreateAndDropDuring T6 T10
+
+max-time: 1500
+cmd: testDict
+args: -n CreateInvalidTables
+
+max-time: 1500
+cmd: testDict
+args: -n CreateTableWhenDbIsFull T6
+
+max-time: 1500
+cmd: testDict
+args: -n CreateMaxTables T6
+
+max-time: 500
+cmd: testDict
+args: -n FragmentTypeSingle T1
+
+max-time: 1500
+cmd: testDict
+args: -n FragmentTypeAllSmall T1 T6 T7 T8
+
+max-time: 1500
+cmd: testDict
+args: -n FragmentTypeAllLarge T1 T6 T7 T8
+
+max-time: 1500
+cmd: testDict
+args: -n TemporaryTables T1 T6 T7 T8
+
+#
+# TEST NDBAPI
+#
+max-time: 500
+cmd: testDataBuffers
+args:
+
+# Testsuite: testNdbApi
+# Number of tests: 5
+max-time: 500
+cmd: testNdbApi
+args: -n MaxNdb T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n MaxTransactions T1 T6 T7 T8 T13
+
+max-time: 500
+cmd: testNdbApi
+args: -n MaxOperations T1 T6 T7 T8 T13
+
+max-time: 500
+cmd: testNdbApi
+args: -n MaxGetValue T1 T6 T7 T8 T13
+
+max-time: 500
+cmd: testNdbApi
+args: -n MaxEqual
+
+max-time: 500
+cmd: testNdbApi
+args: -n DeleteNdb T1 T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n WaitUntilReady T1 T6 T7 T8 T13
+
+max-time: 500
+cmd: testNdbApi
+args: -n GetOperationNoTab T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n NdbErrorOperation T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n MissingOperation T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n GetValueInUpdate T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n UpdateWithoutKeys T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n UpdateWithoutValues T6
+
+max-time: 500
+cmd: testInterpreter
+args: T1
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadRead
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadReadEx
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadInsert
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadUpdate
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadDelete
+
+max-time: 1500
+cmd: testOperations
+args: -n FReadRead
+
+max-time: 1500
+cmd: testOperations
+args: -n FReadReadEx
+
+max-time: 1500
+cmd: testOperations
+args: -n FReadInsert
+
+max-time: 1500
+cmd: testOperations
+args: -n FReadUpdate
+
+max-time: 1500
+cmd: testOperations
+args: -n FReadDelete
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadExRead
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadExReadEx
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadExInsert
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadExUpdate
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadExDelete
+
+max-time: 1500
+cmd: testOperations
+args: -n InsertRead
+
+max-time: 1500
+cmd: testOperations
+args: -n InsertReadEx
+
+max-time: 1500
+cmd: testOperations
+args: -n InsertInsert
+
+max-time: 1500
+cmd: testOperations
+args: -n InsertUpdate
+
+max-time: 1500
+cmd: testOperations
+args: -n InsertDelete
+
+max-time: 1500
+cmd: testOperations
+args: -n UpdateRead
+
+max-time: 1500
+cmd: testOperations
+args: -n UpdateReadEx
+
+max-time: 1500
+cmd: testOperations
+args: -n UpdateInsert
+
+max-time: 1500
+cmd: testOperations
+args: -n UpdateUpdate
+
+max-time: 1500
+cmd: testOperations
+args: -n UpdateDelete
+
+max-time: 1500
+cmd: testOperations
+args: -n DeleteRead
+
+max-time: 1500
+cmd: testOperations
+args: -n DeleteReadEx
+
+max-time: 1500
+cmd: testOperations
+args: -n DeleteInsert
+
+max-time: 1500
+cmd: testOperations
+args: -n DeleteUpdate
+
+max-time: 1500
+cmd: testOperations
+args: -n DeleteDelete
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadSimpleRead
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadDirtyRead
+
+max-time: 1500
+cmd: testOperations
+args: -n FReadSimpleRead
+
+max-time: 1500
+cmd: testOperations
+args: -n FReadDirtyRead
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadExSimpleRead
+
+max-time: 1500
+cmd: testOperations
+args: -n ReadExDirtyRead
+
+max-time: 1500
+cmd: testOperations
+args: -n InsertSimpleRead
+
+max-time: 1500
+cmd: testOperations
+args: -n InsertDirtyRead
+
+max-time: 1500
+cmd: testOperations
+args: -n UpdateSimpleRead
+
+max-time: 1500
+cmd: testOperations
+args: -n UpdateDirtyRead
+
+max-time: 1500
+cmd: testOperations
+args: -n DeleteSimpleRead
+
+max-time: 1500
+cmd: testOperations
+args: -n DeleteDirtyRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadReadEx
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadInsert
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadUpdate
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadDelete
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadExRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadExReadEx
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadExInsert
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadExUpdate
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadExDelete
+
+max-time: 1500
+cmd: testTransactions
+args: -n InsertRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n InsertReadEx
+
+max-time: 1500
+cmd: testTransactions
+args: -n InsertInsert
+
+max-time: 1500
+cmd: testTransactions
+args: -n InsertUpdate
+
+max-time: 1500
+cmd: testTransactions
+args: -n InsertDelete
+
+max-time: 1500
+cmd: testTransactions
+args: -n UpdateRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n UpdateReadEx
+
+max-time: 1500
+cmd: testTransactions
+args: -n UpdateInsert
+
+max-time: 1500
+cmd: testTransactions
+args: -n UpdateUpdate
+
+max-time: 1500
+cmd: testTransactions
+args: -n UpdateDelete
+
+max-time: 1500
+cmd: testTransactions
+args: -n DeleteRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n DeleteReadEx
+
+max-time: 1500
+cmd: testTransactions
+args: -n DeleteInsert
+
+max-time: 1500
+cmd: testTransactions
+args: -n DeleteUpdate
+
+max-time: 1500
+cmd: testTransactions
+args: -n DeleteDelete
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadSimpleRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadDirtyRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadExSimpleRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n ReadExDirtyRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n InsertSimpleRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n InsertDirtyRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n UpdateSimpleRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n UpdateDirtyRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n DeleteSimpleRead
+
+max-time: 1500
+cmd: testTransactions
+args: -n DeleteDirtyRead
+
+max-time: 1500
+cmd: testRestartGci
+args: T6
+
diff --git a/storage/ndb/test/run-test/daily-basic-tests.txt b/storage/ndb/test/run-test/daily-basic-tests.txt
new file mode 100644
index 00000000000..8528e709eb3
--- /dev/null
+++ b/storage/ndb/test/run-test/daily-basic-tests.txt
@@ -0,0 +1,584 @@
+max-time: 3600
+cmd: atrt-mysql-test-run
+args: --force
+
+max-time: 600
+cmd: atrt-testBackup
+args: -n BackupOne T1 T6 T3 I3
+
+# BASIC FUNCTIONALITY
+max-time: 500
+cmd: testBasic
+args: -n PkRead
+
+max-time: 500
+cmd: testBasic
+args: -n PkUpdate
+
+max-time: 500
+cmd: testBasic
+args: -n PkDelete
+
+max-time: 500
+cmd: testBasic
+args: -n PkInsert
+
+max-time: 660
+cmd: testBasic
+args: -n UpdateAndRead
+
+max-time: 500
+cmd: testBasic
+args: -n PkReadAndLocker T6
+
+max-time: 500
+cmd: testBasic
+args: -n PkReadAndLocker2 T6
+
+max-time: 500
+cmd: testBasic
+args: -n PkReadUpdateAndLocker T6
+
+max-time: 500
+cmd: testBasic
+args: -n ReadWithLocksAndInserts T6
+
+max-time: 500
+cmd: testBasic
+args: -n PkInsertTwice T1 T6 T10
+
+max-time: 1500
+cmd: testBasic
+args: -n Fill T13
+
+max-time: 1500
+cmd: testBasic
+args: -n Fill T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitSleep T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommit626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitAndClose T6
+
+max-time: 500
+cmd: testBasic
+args: -n Commit626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitTry626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitAsMuch626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommit626 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitRollback626 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n Commit630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitTry630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitAsMuch630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommit630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitRollback630 T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n NoCommitAndClose T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n RollbackUpdate T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n RollbackDeleteMultiple T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n ImplicitRollbackDelete T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n CommitDelete T1 T6
+
+max-time: 500
+cmd: testBasic
+args: -n RollbackNothing T1 T6
+
+max-time: 500
+cmd: testBasicAsynch
+args: -n PkInsertAsynch
+
+max-time: 500
+cmd: testBasicAsynch
+args: -n PkReadAsynch
+
+max-time: 500
+cmd: testBasicAsynch
+args: -n PkUpdateAsynch
+
+max-time: 500
+cmd: testBasicAsynch
+args: -n PkDeleteAsynch
+
+max-time: 500
+cmd: testBasic
+args: -n MassiveRollback T1 T6 T13
+
+max-time: 500
+cmd: testBasic
+args: -n MassiveRollback2 T1 T6 T13
+
+max-time: 500
+cmd: testTimeout
+args: T1
+
+# SCAN TESTS
+#
+max-time: 500
+cmd: testScan
+args: -n ScanRead16
+
+max-time: 500
+cmd: testScan
+args: -n ScanRead240
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadCommitted240
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdate
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdate2 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanDelete
+
+max-time: 500
+cmd: testScan
+args: -n ScanDelete2 T10
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdateAndScanRead T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAndLocker T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAndPkRead T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanRead488 -l 10 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanRead488O -l 10 T6
+
+max-time: 1000
+cmd: testScan
+args: -n ScanRead488_Mixed -l 10 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanRead488Timeout -l 10 T6
+
+max-time: 600
+cmd: testScan
+args: -n ScanRead40 -l 100 T2
+
+max-time: 1800
+cmd: testScan
+args: -n ScanRead100 -l 100 T1
+
+max-time: 600
+cmd: testScan
+args: -n ScanRead40 -l 100 T1
+
+max-time: 1800
+cmd: testScan
+args: -n ScanRead40RandomTable -l 100 T1
+
+max-time: 3600
+cmd: testScan
+args: -n ScanRead40RandomTable -l 1000 T2
+
+max-time: 500
+cmd: testScan
+args: -n ScanWithLocksAndInserts T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAbort T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAbort15 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadAbort240 T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdateAbort16 T6
+
+max-time: 3600
+cmd: testScan
+args: -n ScanReadRestart T1 T6 T13
+
+max-time: 500
+cmd: testScan
+args: -n ScanUpdateRestart T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckGetValue T6
+
+max-time: 500
+cmd: testScan
+args: -n CloseWithoutStop T6
+
+max-time: 500
+cmd: testScan
+args: -n NextScanWhenNoMore T6
+
+max-time: 500
+cmd: testScan
+args: -n ExecuteScanWithoutOpenScan T6
+
+max-time: 500
+cmd: testScan
+args: -n OnlyOpenScanOnce T6
+
+max-time: 500
+cmd: testScan
+args: -n OnlyOneOpInScanTrans T6
+
+max-time: 500
+cmd: testScan
+args: -n OnlyOneOpBeforeOpenScan T6
+
+max-time: 500
+cmd: testScan
+args: -n OnlyOneScanPerTrans T6
+
+max-time: 500
+cmd: testScan
+args: -n NoCloseTransaction T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckInactivityTimeOut T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckInactivityBeforeClose T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckAfterTerror T6
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5021 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReaderror5022 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5023 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5024 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5025 T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadError5030 T1
+
+max-time: 500
+cmd: testScan
+args: -n InsertDelete T1 T6
+
+max-time: 500
+cmd: testScan
+args: -n CheckAfterTerror T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanReadWhileNodeIsDown T1
+
+max-time: 500
+cmd: testScan
+args: -n ScanRestart T1
+
+max-time: 500
+cmd: testScan
+args: -l 100 -n Scan-bug8262 T7
+
+max-time: 500
+cmd: testScan
+args: -n ScanParallelism
+
+#
+# DICT TESTS
+max-time: 1500
+cmd: testDict
+args: -n CreateAndDrop
+
+max-time: 1500
+cmd: testDict
+args: -n CreateAndDropWithData
+
+max-time: 1500
+cmd: testDict
+args: -n CreateAndDropDuring T6 T10
+
+max-time: 1500
+cmd: testDict
+args: -n CreateInvalidTables
+
+max-time: 1500
+cmd: testDict
+args: -n CreateTableWhenDbIsFull T6
+
+max-time: 1500
+cmd: testDict
+args: -n CreateMaxTables T6
+
+max-time: 500
+cmd: testDict
+args: -n FragmentTypeSingle T1
+
+max-time: 1500
+cmd: testDict
+args: -n FragmentTypeAllSmall T1 T6 T7 T8
+
+max-time: 1500
+cmd: testDict
+args: -n FragmentTypeAllLarge T1 T6 T7 T8
+
+max-time: 1500
+cmd: testDict
+args: -n TemporaryTables T1 T6 T7 T8
+
+#
+# TEST NDBAPI
+#
+max-time: 500
+cmd: testDataBuffers
+args:
+
+# Testsuite: testNdbApi
+# Number of tests: 5
+max-time: 500
+cmd: testNdbApi
+args: -n MaxNdb T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n MaxTransactions T1 T6 T7 T8 T13
+
+max-time: 500
+cmd: testNdbApi
+args: -n MaxOperations T1 T6 T7 T8 T13
+
+max-time: 500
+cmd: testNdbApi
+args: -n MaxGetValue T1 T6 T7 T8 T13
+
+max-time: 500
+cmd: testNdbApi
+args: -n MaxEqual
+
+max-time: 500
+cmd: testNdbApi
+args: -n DeleteNdb T1 T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n WaitUntilReady T1 T6 T7 T8 T13
+
+max-time: 500
+cmd: testNdbApi
+args: -n GetOperationNoTab T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n NdbErrorOperation T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n MissingOperation T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n GetValueInUpdate T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n UpdateWithoutKeys T6
+
+max-time: 500
+cmd: testNdbApi
+args: -n UpdateWithoutValues T6
+
+#max-time: 500
+#cmd: testInterpreter
+#args: T1
+#
+max-time: 150000
+cmd: testOperations
+args:
+
+max-time: 15000
+cmd: testTransactions
+args:
+
+max-time: 1500
+cmd: testRestartGci
+args: T6
+
+max-time: 600
+cmd: testBlobs
+args:
+
+max-time: 5000
+cmd: testOIBasic
+args:
+
+max-time: 2500
+cmd: testBitfield
+args:
+
+max-time: 2500
+cmd: testPartitioning
+args:
+
+#
+#
+# SYSTEM RESTARTS
+#
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR1 T1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR1 T6
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR1 T7
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR1 T8
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR2 T1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR2 T6
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR2 T7
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_UNDO T1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_UNDO T6
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_UNDO T7
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_UNDO T8
+
+# OLD FLEX
+max-time: 500
+cmd: flexBench
+args: -c 25 -t 10
+
+max-time: 500
+cmd: flexHammer
+args: -r 5 -t 32
+
+max-time: 300
+cmd: DbCreate
+args:
+
+max-time: 180
+cmd: DbAsyncGenerator
+args: -time 60 -p 1
+type: bench
+
+max-time: 180
+cmd: DbAsyncGenerator
+args: -time 60 -p 25
+type: bench
+
+max-time: 180
+cmd: DbAsyncGenerator
+args: -time 60 -p 100
+type: bench
+
+max-time: 180
+cmd: DbAsyncGenerator
+args: -time 60 -p 200
+type: bench
+
+max-time: 180
+cmd: DbAsyncGenerator
+args: -time 60 -p 1 -proc 25
+type: bench
+
diff --git a/storage/ndb/test/run-test/daily-devel-tests.txt b/storage/ndb/test/run-test/daily-devel-tests.txt
new file mode 100644
index 00000000000..20f54e031e5
--- /dev/null
+++ b/storage/ndb/test/run-test/daily-devel-tests.txt
@@ -0,0 +1,210 @@
+#
+# INDEX
+#
+max-time: 1500
+cmd: testIndex
+args: -n CreateAll T1 T6 T13
+
+#-m 7200 1: testIndex -n InsertDeleteGentle T7
+max-time: 3600
+cmd: testIndex
+args: -n InsertDelete T1 T10
+
+#-m 3600 1: testIndex -n CreateLoadDropGentle T7
+max-time: 3600
+cmd: testIndex
+args: -n CreateLoadDrop T1 T10
+
+#
+# BACKUP
+#
+max-time: 1000
+cmd: atrt-testBackup
+args: -n BackupBank T6
+
+#
+# MGMAPI AND MGSRV
+#
+max-time: 1800
+cmd: testMgm
+args: -n SingleUserMode T1
+
+#
+#
+# SYSTEM RESTARTS
+#
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR3 T6
+
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR4 T6
+
+#
+max-time: 1500
+cmd: testSystemRestart
+args: -n SR_FULLDB T6
+
+#
+# NODE RESTARTS
+#
+max-time: 2500
+cmd: testNodeRestart
+args: -n NoLoad T6
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n MixedPkRead T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -l 1 -n MixedPkReadPkUpdate
+
+max-time: 2500
+cmd: testNodeRestart
+args: -l 1 -n MixedReadUpdateScan
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n CommittedRead T1
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n LateCommit T1
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n Terror T6 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n FullDb T6 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartRandomNode T6 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartRandomNodeError T6 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartRandomNodeInitial T6 T13
+
+max-time: 3600
+cmd: testNodeRestart
+args: -l 1 -n RestartNFDuringNR T6 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartMasterNodeError T6 T8 T13
+
+max-time: 3600
+cmd: testNodeRestart
+args: -n RestartNodeDuringLCP T6
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n TwoNodeFailure T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n TwoMasterNodeFailure T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n FiftyPercentFail T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartAllNodes T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartAllNodesAbort T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n RestartAllNodesError9999 T6 T8 T13
+
+max-time: 2500
+cmd: testNodeRestart
+args: -n FiftyPercentStopAndWait T6 T8 T13
+
+#max-time: 500
+#cmd: testNodeRestart
+#args: -n StopOnError T1
+#
+#
+max-time: 2500
+cmd: testIndex
+args: -n NFNR1 T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n NFNR2 T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n NFNR3 T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n BuildDuring T6
+
+max-time: 2500
+cmd: testIndex
+args: -l 2 -n SR1 T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n NFNR1_O T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n NFNR2_O T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n NFNR3_O T6 T13
+
+max-time: 2500
+cmd: testIndex
+args: -n BuildDuring_O T6
+
+max-time: 2500
+cmd: testIndex
+args: -l 2 -n SR1_O T6 T13
+
+max-time: 500
+cmd: testIndex
+args: -n MixedTransaction T1
+
+max-time: 2500
+cmd: testDict
+args: -n NF1 T1 T6 T13
+
+#
+max-time: 1500
+cmd: testSystemRestart
+args: -l 1 -n SR6 T1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -l 1 -n SR7 T1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -l 1 -n SR8 T1
+
+max-time: 1500
+cmd: testSystemRestart
+args: -l 1 -n SR9 T1
+
+#
+max-time: 2500
+cmd: test_event
+args: -n EventOperationApplier
+
diff --git a/storage/ndb/test/run-test/example.conf b/storage/ndb/test/run-test/example.conf
new file mode 100644
index 00000000000..1e152da332d
--- /dev/null
+++ b/storage/ndb/test/run-test/example.conf
@@ -0,0 +1,10 @@
+target=pc-linux-i686
+base_dir=/ndb
+src_clone_base=mysqldev@bk-internal.mysql.com:/home/bk/mysql
+run_dir=/space/autotest
+build_dir=/ndb
+hosts="ndb01 ndb02 ndb03 ndb04 ndb05 ndb06 ndb07 ndb08 ndb09 ndb10 ndb11 ndb12"
+result_host="ndb.mysql.com"
+result_path="public_html"
+configure='CC=gcc CXX=gcc CFLAGS="-Wall -pedantic -Wno-long-long" CXXFLAGS="-Wall -pedantic -Wno-long-long" ./configure --with-ndbcluster --with-ndb-test --with-ndbcc-flags="-g -DERROR_INSERT"'
+
diff --git a/storage/ndb/test/run-test/main.cpp b/storage/ndb/test/run-test/main.cpp
new file mode 100644
index 00000000000..02c2cc862a3
--- /dev/null
+++ b/storage/ndb/test/run-test/main.cpp
@@ -0,0 +1,1004 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <getarg.h>
+#include <BaseString.hpp>
+#include <Parser.hpp>
+#include <NdbOut.hpp>
+#include <Properties.hpp>
+#include <NdbAutoPtr.hpp>
+
+#include "run-test.hpp"
+#include <SysLogHandler.hpp>
+#include <FileLogHandler.hpp>
+
+#include <mgmapi.h>
+#include "CpcClient.hpp"
+
+/** Global variables */
+static const char progname[] = "ndb_atrt";
+static const char * g_gather_progname = "atrt-gather-result.sh";
+static const char * g_analyze_progname = "atrt-analyze-result.sh";
+static const char * g_clear_progname = "atrt-clear-result.sh";
+static const char * g_setup_progname = "atrt-setup.sh";
+
+static const char * g_setup_path = 0;
+static const char * g_process_config_filename = "d.txt";
+static const char * g_log_filename = 0;
+static const char * g_test_case_filename = 0;
+static const char * g_report_filename = 0;
+
+static const char * g_default_user = 0;
+static const char * g_default_base_dir = 0;
+static int g_default_base_port = 0;
+static int g_mysqld_use_base = 1;
+
+static int g_report = 0;
+static int g_verbosity = 0;
+static FILE * g_report_file = 0;
+static FILE * g_test_case_file = stdin;
+
+Logger g_logger;
+atrt_config g_config;
+
+static int g_mode_bench = 0;
+static int g_mode_regression = 0;
+static int g_mode_interactive = 0;
+static int g_mode = 0;
+
+static
+struct getargs args[] = {
+ { "process-config", 0, arg_string, &g_process_config_filename, 0, 0 },
+ { "setup-path", 0, arg_string, &g_setup_path, 0, 0 },
+ { 0, 'v', arg_counter, &g_verbosity, 0, 0 },
+ { "log-file", 0, arg_string, &g_log_filename, 0, 0 },
+ { "testcase-file", 'f', arg_string, &g_test_case_filename, 0, 0 },
+ { 0, 'R', arg_flag, &g_report, 0, 0 },
+ { "report-file", 0, arg_string, &g_report_filename, 0, 0 },
+ { "interactive", 'i', arg_flag, &g_mode_interactive, 0, 0 },
+ { "regression", 'r', arg_flag, &g_mode_regression, 0, 0 },
+ { "bench", 'b', arg_flag, &g_mode_bench, 0, 0 },
+};
+
+const int arg_count = 10;
+
+int
+main(int argc, const char ** argv){
+ ndb_init();
+
+ bool restart = true;
+ int lineno = 1;
+ int test_no = 1;
+
+ const int p_ndb = atrt_process::NDB_MGM | atrt_process::NDB_DB;
+ const int p_servers = atrt_process::MYSQL_SERVER | atrt_process::NDB_REP;
+ const int p_clients = atrt_process::MYSQL_CLIENT | atrt_process::NDB_API;
+
+ g_logger.setCategory(progname);
+ g_logger.enable(Logger::LL_ALL);
+ g_logger.createConsoleHandler();
+
+ if(!parse_args(argc, argv))
+ goto end;
+
+ g_logger.info("Starting...");
+ if(!setup_config(g_config))
+ goto end;
+
+ g_logger.info("Connecting to hosts");
+ if(!connect_hosts(g_config))
+ goto end;
+
+ if(!setup_hosts(g_config))
+ goto end;
+
+ /**
+ * Main loop
+ */
+ while(!feof(g_test_case_file)){
+ /**
+ * Do we need to restart ndb
+ */
+ if(restart){
+ g_logger.info("(Re)starting ndb processes");
+ if(!stop_processes(g_config, atrt_process::NDB_MGM))
+ goto end;
+
+ if(!stop_processes(g_config, atrt_process::NDB_DB))
+ goto end;
+
+ if(!start_processes(g_config, atrt_process::NDB_MGM))
+ goto end;
+
+ if(!connect_ndb_mgm(g_config)){
+ goto end;
+ }
+
+ if(!start_processes(g_config, atrt_process::NDB_DB))
+ goto end;
+
+ if(!wait_ndb(g_config, NDB_MGM_NODE_STATUS_NOT_STARTED))
+ goto end;
+
+ for(Uint32 i = 0; i<3; i++)
+ if(wait_ndb(g_config, NDB_MGM_NODE_STATUS_STARTED))
+ goto started;
+
+ goto end;
+
+ started:
+ g_logger.info("Ndb start completed");
+ }
+
+ const int start_line = lineno;
+ atrt_testcase test_case;
+ if(!read_test_case(g_test_case_file, test_case, lineno))
+ goto end;
+
+ g_logger.info("#%d - %s %s",
+ test_no,
+ test_case.m_command.c_str(), test_case.m_args.c_str());
+
+ // Assign processes to programs
+ if(!setup_test_case(g_config, test_case))
+ goto end;
+
+ if(!start_processes(g_config, p_servers))
+ goto end;
+
+ if(!start_processes(g_config, p_clients))
+ goto end;
+
+ int result = 0;
+
+ const time_t start = time(0);
+ time_t now = start;
+ do {
+ if(!update_status(g_config, atrt_process::ALL))
+ goto end;
+
+ int count = 0;
+
+ if((count = is_running(g_config, p_ndb)) != 2){
+ result = ERR_NDB_FAILED;
+ break;
+ }
+
+ if((count = is_running(g_config, p_servers)) != 2){
+ result = ERR_SERVERS_FAILED;
+ break;
+ }
+
+ if((count = is_running(g_config, p_clients)) == 0){
+ break;
+ }
+
+ now = time(0);
+ if(now > (start + test_case.m_max_time)){
+ result = ERR_MAX_TIME_ELAPSED;
+ break;
+ }
+ sleep(1);
+ } while(true);
+
+ const time_t elapsed = time(0) - start;
+
+ if(!stop_processes(g_config, p_clients))
+ goto end;
+
+ if(!stop_processes(g_config, p_servers))
+ goto end;
+
+ if(!gather_result(g_config, &result))
+ goto end;
+
+ g_logger.info("#%d %s(%d)",
+ test_no,
+ (result == 0 ? "OK" : "FAILED"), result);
+
+ if(g_report_file != 0){
+ fprintf(g_report_file, "%s %s ; %d ; %d ; %ld\n",
+ test_case.m_command.c_str(),
+ test_case.m_args.c_str(),
+ test_no, result, elapsed);
+ fflush(g_report_file);
+ }
+
+ if(test_case.m_report || g_mode_bench || (g_mode_regression && result)){
+ BaseString tmp;
+ tmp.assfmt("result.%d", test_no);
+ if(rename("result", tmp.c_str()) != 0){
+ g_logger.critical("Failed to rename %s as %s",
+ "result", tmp.c_str());
+ goto end;
+ }
+ }
+
+ if(g_mode_interactive && result){
+ g_logger.info
+ ("Encountered failed test in interactive mode - terminating");
+ break;
+ }
+
+ if(result != 0){
+ restart = true;
+ } else {
+ restart = false;
+ }
+ test_no++;
+ }
+
+ end:
+ if(g_report_file != 0){
+ fclose(g_report_file);
+ g_report_file = 0;
+ }
+
+ if(g_test_case_file != 0 && g_test_case_file != stdin){
+ fclose(g_test_case_file);
+ g_test_case_file = 0;
+ }
+
+ stop_processes(g_config, atrt_process::ALL);
+ return 0;
+}
+
+bool
+parse_args(int argc, const char** argv){
+ int optind = 0;
+ if(getarg(args, arg_count, argc, argv, &optind)) {
+ arg_printusage(args, arg_count, progname, "");
+ return false;
+ }
+
+ if(g_log_filename != 0){
+ g_logger.removeConsoleHandler();
+ g_logger.addHandler(new FileLogHandler(g_log_filename));
+ }
+
+ {
+ int tmp = Logger::LL_WARNING - g_verbosity;
+ tmp = (tmp < Logger::LL_DEBUG ? Logger::LL_DEBUG : tmp);
+ g_logger.disable(Logger::LL_ALL);
+ g_logger.enable(Logger::LL_ON);
+ g_logger.enable((Logger::LoggerLevel)tmp, Logger::LL_ALERT);
+ }
+
+
+
+ if(!g_process_config_filename){
+ g_logger.critical("Process config not specified!");
+ return false;
+ }
+
+ if(!g_setup_path){
+ char buf[1024];
+ if(getcwd(buf, sizeof(buf))){
+ g_setup_path = strdup(buf);
+ g_logger.info("Setup path not specified, using %s", buf);
+ } else {
+ g_logger.critical("Setup path not specified!\n");
+ return false;
+ }
+ }
+
+ if(g_report & !g_report_filename){
+ g_report_filename = "report.txt";
+ }
+
+ if(g_report_filename){
+ g_report_file = fopen(g_report_filename, "w");
+ if(g_report_file == 0){
+ g_logger.critical("Unable to create report file: %s", g_report_filename);
+ return false;
+ }
+ }
+
+ if(g_test_case_filename){
+ g_test_case_file = fopen(g_test_case_filename, "r");
+ if(g_test_case_file == 0){
+ g_logger.critical("Unable to open file: %s", g_test_case_filename);
+ return false;
+ }
+ }
+
+ int sum = g_mode_interactive + g_mode_regression + g_mode_bench;
+ if(sum == 0){
+ g_mode_interactive = 1;
+ }
+
+ if(sum > 1){
+ g_logger.critical
+ ("Only one of bench/regression/interactive can be specified");
+ return false;
+ }
+
+ g_default_user = strdup(getenv("LOGNAME"));
+
+ return true;
+}
+
+
+static
+atrt_host *
+find(const BaseString& host, Vector<atrt_host> & hosts){
+ for(size_t i = 0; i<hosts.size(); i++){
+ if(hosts[i].m_hostname == host){
+ return &hosts[i];
+ }
+ }
+ return 0;
+}
+
+bool
+setup_config(atrt_config& config){
+
+ FILE * f = fopen(g_process_config_filename, "r");
+ if(!f){
+ g_logger.critical("Failed to open process config file: %s",
+ g_process_config_filename);
+ return false;
+ }
+ bool result = true;
+
+ int lineno = 0;
+ char buf[2048];
+ BaseString connect_string;
+ int mysql_port_offset = 0;
+ while(fgets(buf, 2048, f)){
+ lineno++;
+
+ BaseString tmp(buf);
+ tmp.trim(" \t\n\r");
+
+ if(tmp.length() == 0 || tmp == "" || tmp.c_str()[0] == '#')
+ continue;
+
+ Vector<BaseString> split1;
+ if(tmp.split(split1, ":", 2) != 2){
+ g_logger.warning("Invalid line %d in %s - ignoring",
+ lineno, g_process_config_filename);
+ continue;
+ }
+
+ if(split1[0].trim() == "basedir"){
+ g_default_base_dir = strdup(split1[1].trim().c_str());
+ continue;
+ }
+
+ if(split1[0].trim() == "baseport"){
+ g_default_base_port = atoi(split1[1].trim().c_str());
+ continue;
+ }
+
+ if(split1[0].trim() == "user"){
+ g_default_user = strdup(split1[1].trim().c_str());
+ continue;
+ }
+
+ if(split1[0].trim() == "mysqld-use-base" && split1[1].trim() == "no"){
+ g_mysqld_use_base = 0;
+ continue;
+ }
+
+ Vector<BaseString> hosts;
+ if(split1[1].trim().split(hosts) <= 0){
+ g_logger.warning("Invalid line %d in %s - ignoring",
+ lineno, g_process_config_filename);
+ }
+
+ // 1 - Check hosts
+ for(size_t i = 0; i<hosts.size(); i++){
+ Vector<BaseString> tmp;
+ hosts[i].split(tmp, ":");
+ BaseString hostname = tmp[0].trim();
+ BaseString base_dir;
+ if(tmp.size() >= 2)
+ base_dir = tmp[1];
+ else if(g_default_base_dir == 0){
+ g_logger.critical("Basedir not specified...");
+ return false;
+ }
+
+ atrt_host * host_ptr;
+ if((host_ptr = find(hostname, config.m_hosts)) == 0){
+ atrt_host host;
+ host.m_index = config.m_hosts.size();
+ host.m_cpcd = new SimpleCpcClient(hostname.c_str(), 1234);
+ host.m_base_dir = (base_dir.empty() ? g_default_base_dir : base_dir);
+ host.m_user = g_default_user;
+ host.m_hostname = hostname.c_str();
+ config.m_hosts.push_back(host);
+ } else {
+ if(!base_dir.empty() && (base_dir == host_ptr->m_base_dir)){
+ g_logger.critical("Inconsistent base dir definition for host %s"
+ ", \"%s\" != \"%s\"", hostname.c_str(),
+ base_dir.c_str(), host_ptr->m_base_dir.c_str());
+ return false;
+ }
+ }
+ }
+
+ for(size_t i = 0; i<hosts.size(); i++){
+ BaseString & tmp = hosts[i];
+ atrt_host * host = find(tmp, config.m_hosts);
+ BaseString & dir = host->m_base_dir;
+
+ const int index = config.m_processes.size() + 1;
+
+ atrt_process proc;
+ proc.m_index = index;
+ proc.m_host = host;
+ proc.m_proc.m_id = -1;
+ proc.m_proc.m_type = "temporary";
+ proc.m_proc.m_owner = "atrt";
+ proc.m_proc.m_group = "group";
+ proc.m_proc.m_cwd.assign(dir).append("/run/");
+ proc.m_proc.m_stdout = "log.out";
+ proc.m_proc.m_stderr = "2>&1";
+ proc.m_proc.m_runas = proc.m_host->m_user;
+ proc.m_proc.m_ulimit = "c:unlimited";
+ proc.m_proc.m_env.assfmt("MYSQL_BASE_DIR=%s", dir.c_str());
+ proc.m_hostname = proc.m_host->m_hostname;
+ proc.m_ndb_mgm_port = g_default_base_port;
+ if(split1[0] == "mgm"){
+ proc.m_type = atrt_process::NDB_MGM;
+ proc.m_proc.m_name.assfmt("%d-%s", index, "ndb_mgmd");
+ proc.m_proc.m_path.assign(dir).append("/libexec/ndb_mgmd");
+ proc.m_proc.m_args = "--nodaemon -f config.ini";
+ proc.m_proc.m_cwd.appfmt("%d.ndb_mgmd", index);
+ connect_string.appfmt("host=%s:%d;",
+ proc.m_hostname.c_str(), proc.m_ndb_mgm_port);
+ } else if(split1[0] == "ndb"){
+ proc.m_type = atrt_process::NDB_DB;
+ proc.m_proc.m_name.assfmt("%d-%s", index, "ndbd");
+ proc.m_proc.m_path.assign(dir).append("/libexec/ndbd");
+ proc.m_proc.m_args = "--initial --nodaemon -n";
+ proc.m_proc.m_cwd.appfmt("%d.ndbd", index);
+ } else if(split1[0] == "mysqld"){
+ proc.m_type = atrt_process::MYSQL_SERVER;
+ proc.m_proc.m_name.assfmt("%d-%s", index, "mysqld");
+ proc.m_proc.m_path.assign(dir).append("/libexec/mysqld");
+ proc.m_proc.m_args = "--core-file --ndbcluster";
+ proc.m_proc.m_cwd.appfmt("%d.mysqld", index);
+ if(mysql_port_offset > 0 || g_mysqld_use_base){
+ // setup mysql specific stuff
+ const char * basedir = proc.m_proc.m_cwd.c_str();
+ proc.m_proc.m_args.appfmt("--datadir=%s", basedir);
+ proc.m_proc.m_args.appfmt("--pid-file=%s/mysql.pid", basedir);
+ proc.m_proc.m_args.appfmt("--socket=%s/mysql.sock", basedir);
+ proc.m_proc.m_args.appfmt("--port=%d",
+ g_default_base_port-(++mysql_port_offset));
+ }
+ } else if(split1[0] == "api"){
+ proc.m_type = atrt_process::NDB_API;
+ proc.m_proc.m_name.assfmt("%d-%s", index, "ndb_api");
+ proc.m_proc.m_path = "";
+ proc.m_proc.m_args = "";
+ proc.m_proc.m_cwd.appfmt("%d.ndb_api", index);
+ } else {
+ g_logger.critical("%s:%d: Unhandled process type: %s",
+ g_process_config_filename, lineno,
+ split1[0].c_str());
+ result = false;
+ goto end;
+ }
+ config.m_processes.push_back(proc);
+ }
+ }
+
+ // Setup connect string
+ for(size_t i = 0; i<config.m_processes.size(); i++){
+ config.m_processes[i].m_proc.m_env.appfmt(" NDB_CONNECTSTRING=%s",
+ connect_string.c_str());
+ }
+
+ end:
+ fclose(f);
+ return result;
+}
+
+bool
+connect_hosts(atrt_config& config){
+ for(size_t i = 0; i<config.m_hosts.size(); i++){
+ if(config.m_hosts[i].m_cpcd->connect() != 0){
+ g_logger.error("Unable to connect to cpc %s:%d",
+ config.m_hosts[i].m_cpcd->getHost(),
+ config.m_hosts[i].m_cpcd->getPort());
+ return false;
+ }
+ g_logger.debug("Connected to %s:%d",
+ config.m_hosts[i].m_cpcd->getHost(),
+ config.m_hosts[i].m_cpcd->getPort());
+ }
+
+ return true;
+}
+
+bool
+connect_ndb_mgm(atrt_process & proc){
+ NdbMgmHandle handle = ndb_mgm_create_handle();
+ if(handle == 0){
+ g_logger.critical("Unable to create mgm handle");
+ return false;
+ }
+ BaseString tmp = proc.m_hostname;
+ tmp.appfmt(":%d", proc.m_ndb_mgm_port);
+
+ if (ndb_mgm_set_connectstring(handle,tmp.c_str()))
+ {
+ g_logger.critical("Unable to create parse connectstring");
+ return false;
+ }
+
+ if(ndb_mgm_connect(handle, 30, 1, 0) != -1)
+ {
+ proc.m_ndb_mgm_handle = handle;
+ return true;
+ }
+
+ g_logger.critical("Unable to connect to ndb mgm %s", tmp.c_str());
+ return false;
+}
+
+bool
+connect_ndb_mgm(atrt_config& config){
+ for(size_t i = 0; i<config.m_processes.size(); i++){
+ atrt_process & proc = config.m_processes[i];
+ if((proc.m_type & atrt_process::NDB_MGM) != 0){
+ if(!connect_ndb_mgm(proc)){
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static int remap(int i){
+ if(i == NDB_MGM_NODE_STATUS_NO_CONTACT) return NDB_MGM_NODE_STATUS_UNKNOWN;
+ if(i == NDB_MGM_NODE_STATUS_UNKNOWN) return NDB_MGM_NODE_STATUS_NO_CONTACT;
+ return i;
+}
+
+bool
+wait_ndb(atrt_config& config, int goal){
+
+ goal = remap(goal);
+
+
+ /**
+ * Get mgm handle for cluster
+ */
+ NdbMgmHandle handle = 0;
+ for(size_t i = 0; i<config.m_processes.size(); i++){
+ atrt_process & proc = config.m_processes[i];
+ if((proc.m_type & atrt_process::NDB_MGM) != 0){
+ handle = proc.m_ndb_mgm_handle;
+ break;
+ }
+ }
+ if(handle == 0){
+ g_logger.critical("Unable to find mgm handle");
+ return false;
+ }
+
+ if(goal == NDB_MGM_NODE_STATUS_STARTED){
+ /**
+ * 1) wait NOT_STARTED
+ * 2) send start
+ * 3) wait STARTED
+ */
+ if(!wait_ndb(config, NDB_MGM_NODE_STATUS_NOT_STARTED))
+ return false;
+
+ ndb_mgm_start(handle, 0, 0);
+ }
+
+ struct ndb_mgm_cluster_state * state;
+
+ time_t now = time(0);
+ time_t end = now + 360;
+ int min = remap(NDB_MGM_NODE_STATUS_NO_CONTACT);
+ int min2 = goal;
+
+ while(now < end){
+ /**
+ * 1) retreive current state
+ */
+ state = 0;
+ do {
+ state = ndb_mgm_get_status(handle);
+ if(state == 0){
+ const int err = ndb_mgm_get_latest_error(handle);
+ g_logger.error("Unable to poll db state: %d %s %s",
+ ndb_mgm_get_latest_error(handle),
+ ndb_mgm_get_latest_error_msg(handle),
+ ndb_mgm_get_latest_error_desc(handle));
+ if(err == NDB_MGM_SERVER_NOT_CONNECTED && connect_ndb_mgm(config)){
+ g_logger.error("Reconnected...");
+ continue;
+ }
+ return false;
+ }
+ } while(state == 0);
+ NdbAutoPtr<void> tmp(state);
+
+ min2 = goal;
+ for(int i = 0; i<state->no_of_nodes; i++){
+ if(state->node_states[i].node_type == NDB_MGM_NODE_TYPE_NDB){
+ const int s = remap(state->node_states[i].node_status);
+ min2 = (min2 < s ? min2 : s );
+
+ if(s < remap(NDB_MGM_NODE_STATUS_NO_CONTACT) ||
+ s > NDB_MGM_NODE_STATUS_STARTED){
+ g_logger.critical("Strange DB status during start: %d %d", i, min2);
+ return false;
+ }
+
+ if(min2 < min){
+ g_logger.critical("wait ndb failed node: %d %d %d %d",
+ state->node_states[i].node_id, min, min2, goal);
+ }
+ }
+ }
+
+ if(min2 < min){
+ g_logger.critical("wait ndb failed %d %d %d", min, min2, goal);
+ return false;
+ }
+
+ if(min2 == goal){
+ return true;
+ break;
+ }
+
+ min = min2;
+ now = time(0);
+ }
+
+ g_logger.critical("wait ndb timed out %d %d %d", min, min2, goal);
+
+ return false;
+}
+
+bool
+start_process(atrt_process & proc){
+ if(proc.m_proc.m_id != -1){
+ g_logger.critical("starting already started process: %d", proc.m_index);
+ return false;
+ }
+
+ BaseString path = proc.m_proc.m_cwd.substr(proc.m_host->m_base_dir.length()+BaseString("/run").length());
+
+ BaseString tmp = g_setup_progname;
+ tmp.appfmt(" %s %s/%s/ %s",
+ proc.m_host->m_hostname.c_str(),
+ g_setup_path,
+ path.c_str(),
+ proc.m_proc.m_cwd.c_str());
+
+ const int r1 = system(tmp.c_str());
+ if(r1 != 0){
+ g_logger.critical("Failed to setup process");
+ return false;
+ }
+
+ {
+ Properties reply;
+ if(proc.m_host->m_cpcd->define_process(proc.m_proc, reply) != 0){
+ BaseString msg;
+ reply.get("errormessage", msg);
+ g_logger.error("Unable to define process: %s", msg.c_str());
+ return false;
+ }
+ }
+ {
+ Properties reply;
+ if(proc.m_host->m_cpcd->start_process(proc.m_proc.m_id, reply) != 0){
+ BaseString msg;
+ reply.get("errormessage", msg);
+ g_logger.error("Unable to start process: %s", msg.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+start_processes(atrt_config& config, int types){
+ for(size_t i = 0; i<config.m_processes.size(); i++){
+ atrt_process & proc = config.m_processes[i];
+ if((types & proc.m_type) != 0 && proc.m_proc.m_path != ""){
+ if(!start_process(proc)){
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool
+stop_process(atrt_process & proc){
+ if(proc.m_proc.m_id == -1){
+ return true;
+ }
+
+ {
+ Properties reply;
+ if(proc.m_host->m_cpcd->stop_process(proc.m_proc.m_id, reply) != 0){
+ Uint32 status;
+ reply.get("status", &status);
+ if(status != 4){
+ BaseString msg;
+ reply.get("errormessage", msg);
+ g_logger.error("Unable to stop process: %s(%d)", msg.c_str(), status);
+ return false;
+ }
+ }
+ }
+ {
+ Properties reply;
+ if(proc.m_host->m_cpcd->undefine_process(proc.m_proc.m_id, reply) != 0){
+ BaseString msg;
+ reply.get("errormessage", msg);
+ g_logger.error("Unable to undefine process: %s", msg.c_str());
+ return false;
+ }
+ proc.m_proc.m_id = -1;
+ }
+ return true;
+}
+
+bool
+stop_processes(atrt_config& config, int types){
+ for(size_t i = 0; i<config.m_processes.size(); i++){
+ atrt_process & proc = config.m_processes[i];
+ if((types & proc.m_type) != 0){
+ if(!stop_process(proc)){
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool
+update_status(atrt_config& config, int){
+
+ Vector<Vector<SimpleCpcClient::Process> > m_procs;
+
+ Vector<SimpleCpcClient::Process> dummy;
+ m_procs.fill(config.m_hosts.size(), dummy);
+ for(size_t i = 0; i<config.m_hosts.size(); i++){
+ Properties p;
+ config.m_hosts[i].m_cpcd->list_processes(m_procs[i], p);
+ }
+
+ for(size_t i = 0; i<config.m_processes.size(); i++){
+ atrt_process & proc = config.m_processes[i];
+ if(proc.m_proc.m_id != -1){
+ Vector<SimpleCpcClient::Process> &h_procs= m_procs[proc.m_host->m_index];
+ bool found = false;
+ for(size_t j = 0; j<h_procs.size(); j++){
+ if(proc.m_proc.m_id == h_procs[j].m_id){
+ found = true;
+ proc.m_proc.m_status = h_procs[j].m_status;
+ break;
+ }
+ }
+ if(!found){
+ g_logger.error("update_status: not found");
+ g_logger.error("id: %d host: %s cmd: %s",
+ proc.m_proc.m_id,
+ proc.m_hostname.c_str(),
+ proc.m_proc.m_path.c_str());
+ for(size_t j = 0; j<h_procs.size(); j++){
+ g_logger.error("found: %d %s", h_procs[j].m_id,
+ h_procs[j].m_path.c_str());
+ }
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+int
+is_running(atrt_config& config, int types){
+ int found = 0, running = 0;
+ for(size_t i = 0; i<config.m_processes.size(); i++){
+ atrt_process & proc = config.m_processes[i];
+ if((types & proc.m_type) != 0){
+ found++;
+ if(proc.m_proc.m_status == "running")
+ running++;
+ }
+ }
+
+ if(found == running)
+ return 2;
+ if(running == 0)
+ return 0;
+ return 1;
+}
+
+
+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;
+}
+
+bool
+read_test_case(FILE * file, atrt_testcase& tc, int& line){
+
+ Properties p;
+ int elements = 0;
+ char buf[1024];
+ while(!feof(file)){
+ if(!fgets(buf, 1024, file))
+ break;
+
+ line++;
+ BaseString tmp = buf;
+
+ if(tmp.length() > 0 && tmp.c_str()[0] == '#')
+ continue;
+
+ if(insert(tmp.c_str(), p) != 0)
+ break;
+
+ elements++;
+ }
+
+ if(elements == 0){
+ if(file == stdin){
+ BaseString tmp(buf);
+ tmp.trim(" \t\n\r");
+ Vector<BaseString> split;
+ tmp.split(split, " ", 2);
+ tc.m_command = split[0];
+ if(split.size() == 2)
+ tc.m_args = split[1];
+ else
+ tc.m_args = "";
+ tc.m_max_time = 60000;
+ return true;
+ }
+ return false;
+ }
+
+ if(!p.get("cmd", tc.m_command)){
+ g_logger.critical("Invalid test file: cmd is missing near line: %d", line);
+ return false;
+ }
+
+ if(!p.get("args", tc.m_args))
+ tc.m_args = "";
+
+ const char * mt = 0;
+ if(!p.get("max-time", &mt))
+ tc.m_max_time = 60000;
+ else
+ tc.m_max_time = atoi(mt);
+
+ if(p.get("type", &mt) && strcmp(mt, "bench") == 0)
+ tc.m_report= true;
+ else
+ tc.m_report= false;
+
+ return true;
+}
+
+bool
+setup_test_case(atrt_config& config, const atrt_testcase& tc){
+ const int r1 = system(g_clear_progname);
+ if(r1 != 0){
+ g_logger.critical("Failed to clear result");
+ return false;
+ }
+
+ size_t i = 0;
+ for(; i<config.m_processes.size(); i++){
+ atrt_process & proc = config.m_processes[i];
+ if(proc.m_type == atrt_process::NDB_API){
+ proc.m_proc.m_path.assfmt("%s/bin/%s", proc.m_host->m_base_dir.c_str(),
+ tc.m_command.c_str());
+ proc.m_proc.m_args.assign(tc.m_args);
+ break;
+ }
+ }
+ for(i++; i<config.m_processes.size(); i++){
+ atrt_process & proc = config.m_processes[i];
+ if(proc.m_type == atrt_process::NDB_API){
+ proc.m_proc.m_path.assign("");
+ proc.m_proc.m_args.assign("");
+ }
+ }
+ return true;
+}
+
+bool
+gather_result(atrt_config& config, int * result){
+ BaseString tmp = g_gather_progname;
+ for(size_t i = 0; i<config.m_processes.size(); i++){
+ atrt_process & proc = config.m_processes[i];
+ if(proc.m_proc.m_path != ""){
+ tmp.appfmt(" %s:%s",
+ proc.m_hostname.c_str(),
+ proc.m_proc.m_cwd.c_str());
+ }
+ }
+
+ const int r1 = system(tmp.c_str());
+ if(r1 != 0){
+ g_logger.critical("Failed to gather result");
+ return false;
+ }
+
+ const int r2 = system(g_analyze_progname);
+
+ if(r2 == -1 || r2 == (127 << 8)){
+ g_logger.critical("Failed to analyze results");
+ return false;
+ }
+
+ * result = r2 ;
+ return true;
+}
+
+bool
+setup_hosts(atrt_config& config){
+ const int r1 = system(g_clear_progname);
+ if(r1 != 0){
+ g_logger.critical("Failed to clear result");
+ return false;
+ }
+
+ for(size_t i = 0; i<config.m_hosts.size(); i++){
+ BaseString tmp = g_setup_progname;
+ tmp.appfmt(" %s %s/ %s/run",
+ config.m_hosts[i].m_hostname.c_str(),
+ g_setup_path,
+ config.m_hosts[i].m_base_dir.c_str());
+
+ const int r1 = system(tmp.c_str());
+ if(r1 != 0){
+ g_logger.critical("Failed to setup %s",
+ config.m_hosts[i].m_hostname.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
+template class Vector<Vector<SimpleCpcClient::Process> >;
+template class Vector<atrt_host>;
+template class Vector<atrt_process>;
diff --git a/storage/ndb/test/run-test/make-config.sh b/storage/ndb/test/run-test/make-config.sh
new file mode 100755
index 00000000000..5394b0654d4
--- /dev/null
+++ b/storage/ndb/test/run-test/make-config.sh
@@ -0,0 +1,465 @@
+#!/bin/sh
+# NAME
+# make-config.sh - Makes a config file for mgm server
+#
+# SYNOPSIS
+# make-config.sh [ -t <template> ] [-s] [ -m <machine conf> [ -d <directory> ]
+#
+# DESCRIPTION
+#
+# OPTIONS
+#
+# EXAMPLES
+#
+#
+# ENVIRONMENT
+# NDB_PROJ_HOME Home dir for ndb
+#
+# FILES
+# $NDB_PROJ_HOME/lib/funcs.sh general shell script functions
+#
+#
+# SEE ALSO
+#
+# DIAGNOSTICTS
+#
+# VERSION
+# 1.0
+# 1.1 021112 epesson: Adapted for new mgmt server in NDB 2.00
+#
+# AUTHOR
+# Jonas Oreland
+#
+# CHANGES
+# also generate ndbnet config
+#
+
+progname=`basename $0`
+synopsis="make-config.sh [ -t template ] [ -m <machine conf> ] [ -d <dst directory> ][-s] [<mgm host>]"
+
+#: ${NDB_PROJ_HOME:?} # If undefined, exit with error message
+
+#: ${NDB_LOCAL_BUILD_OPTIONS:=--} # If undef, set to --. Keeps getopts happy.
+ # You may have to experiment a bit
+ # to get quoting right (if you need it).
+
+
+#. $NDB_PROJ_HOME/lib/funcs.sh # Load some good stuff
+trace() {
+ echo $* 1>&2
+}
+syndie() {
+ trace $*
+ exit 1
+}
+
+# defaults for options related variables
+#
+
+mgm_nodes=0
+ndb_nodes=0
+api_nodes=0
+uniq_id=$$.$$
+own_host=`hostname`
+dst_dir=""
+template=/dev/null
+machines=/dev/null
+verbose=yes
+
+# used if error when parsing the options environment variable
+#
+env_opterr="options environment variable: <<$options>>"
+
+# Option parsing, for the options variable as well as the command line.
+#
+# We want to be able to set options in an environment variable,
+# as well as on the command line. In order not to have to repeat
+# the same getopts information twice, we loop two times over the
+# getopts while loop. The first time, we process options from
+# the options environment variable, the second time we process
+# options from the command line.
+#
+# The things to change are the actual options and what they do.
+#
+add_node(){
+ no=$1; shift
+ type=$1; shift
+ echo $* | awk 'BEGIN{FS=":";}{h=$1; if(h=="localhost") h="'$own_host'";
+ printf("%s_%d_host=%s\n", "'$type'", "'$no'", h);
+ if(NF>1 && $2!="") printf("%s_%d_port=%d\n",
+ "'$type'", "'$no'", $2);
+ if(NF>2 && $3!="") printf("%s_%d_dir=%s\n",
+ "'$type'", "'$no'", $3);
+ }'
+}
+
+
+add_mgm_node(){
+ mgm_nodes=`cat /tmp/mgm_nodes.$uniq_id | grep "_host=" | wc -l`
+ mgm_nodes=`expr $mgm_nodes + 1`
+ while [ $# -gt 0 ]
+ do
+ add_node ${mgm_nodes} mgm_node $1 >> /tmp/mgm_nodes.$uniq_id
+ shift
+ mgm_nodes=`expr $mgm_nodes + 1`
+ done
+}
+
+add_ndb_node(){
+ ndb_nodes=`cat /tmp/ndb_nodes.$uniq_id | grep "_host=" | wc -l`
+ ndb_nodes=`expr $ndb_nodes + 1`
+ while [ $# -gt 0 ]
+ do
+ add_node ${ndb_nodes} ndb_node $1 >> /tmp/ndb_nodes.$uniq_id
+ shift
+ ndb_nodes=`expr $ndb_nodes + 1`
+ done
+}
+
+add_api_node(){
+ api_nodes=`cat /tmp/api_nodes.$uniq_id | grep "_host=" |wc -l`
+ api_nodes=`expr $api_nodes + 1`
+ while [ $# -gt 0 ]
+ do
+ add_node ${api_nodes} api_node $1 >> /tmp/api_nodes.$uniq_id
+ shift
+ api_nodes=`expr $api_nodes + 1`
+ done
+}
+
+rm -rf /tmp/mgm_nodes.$uniq_id ; touch /tmp/mgm_nodes.$uniq_id
+rm -rf /tmp/ndb_nodes.$uniq_id ; touch /tmp/ndb_nodes.$uniq_id
+rm -rf /tmp/api_nodes.$uniq_id ; touch /tmp/api_nodes.$uniq_id
+
+for optstring in "$options" "" # 1. options variable 2. cmd line
+do
+
+ while getopts d:m:t:n:o:a:b:p:s i $optstring # optstring empty => no arg => cmd line
+ do
+ case $i in
+
+ q) verbose="";; # echo important things
+ t) template=$OPTARG;; # Template
+ d) dst_dir=$OPTARG;; # Destination directory
+ m) machines=$OPTARG;; # Machine configuration
+ s) mgm_start=yes;; # Make mgm start script
+ \?) syndie $env_opterr;; # print synopsis and exit
+
+ esac
+ done
+
+ [ -n "$optstring" ] && OPTIND=1 # Reset for round 2, cmdline options
+
+ env_opterr= # Round 2 should not use the value
+
+done
+shift `expr $OPTIND - 1`
+
+if [ -z "$dst_dir" ]
+then
+ verbose=
+fi
+
+skip(){
+ no=$1; shift
+ shift $no
+ echo $*
+}
+
+# --- option parsing done ---
+grep "^ndb: " $machines | while read node
+do
+ node=`skip 1 $node`
+ add_ndb_node $node
+done
+
+grep "^api: " $machines | while read node
+do
+ node=`skip 1 $node`
+ add_api_node $node
+done
+
+grep "^mgm: " $machines | while read node
+do
+ node=`skip 1 $node`
+ add_mgm_node $node
+done
+
+tmp=`grep "^baseport: " $machines | tail -1 | cut -d ":" -f 2`
+if [ "$tmp" ]
+then
+ baseport=`echo $tmp`
+else
+ syndie "Unable to find baseport"
+fi
+
+trim(){
+ echo $*
+}
+tmp=`grep "^basedir: " $machines | tail -1 | cut -d ":" -f 2`
+if [ "$tmp" ]
+then
+ basedir=`trim $tmp`
+fi
+
+# -- Load enviroment --
+ndb_nodes=`cat /tmp/ndb_nodes.$uniq_id | grep "_host=" | wc -l`
+api_nodes=`cat /tmp/api_nodes.$uniq_id | grep "_host=" | wc -l`
+mgm_nodes=`cat /tmp/mgm_nodes.$uniq_id | grep "_host=" | wc -l`
+. /tmp/ndb_nodes.$uniq_id
+. /tmp/api_nodes.$uniq_id
+. /tmp/mgm_nodes.$uniq_id
+rm -f /tmp/ndb_nodes.$uniq_id /tmp/api_nodes.$uniq_id /tmp/mgm_nodes.$uniq_id
+
+# -- Verify
+trace "Verifying arguments"
+
+if [ ! -r $template ]
+then
+ syndie "Unable to read template file: $template"
+fi
+
+if [ $ndb_nodes -le 0 ]
+then
+ syndie "No ndb nodes specified"
+fi
+
+if [ $api_nodes -le 0 ]
+then
+ syndie "No api nodes specified"
+fi
+
+if [ $mgm_nodes -gt 1 ]
+then
+ syndie "More than one mgm node specified"
+fi
+
+if [ $mgm_nodes -eq 0 ]
+then
+ trace "No managment server specified using `hostname`"
+ mgm_nodes=1
+ mgm_node_1=`hostname`
+fi
+
+if [ -n "$dst_dir" ]
+then
+ mkdir -p $dst_dir
+ if [ ! -d $dst_dir ]
+ then
+ syndie "Unable to create dst dir: $dst_dir"
+ fi
+ DST=/tmp/$uniq_id
+fi
+
+# --- option verifying done ---
+
+# Find uniq computers
+i=1
+while [ $i -le $mgm_nodes ]
+do
+ echo `eval echo "\$"mgm_node_${i}_host` >> /tmp/hosts.$uniq_id
+ i=`expr $i + 1`
+done
+
+i=1
+while [ $i -le $ndb_nodes ]
+do
+ echo `eval echo "\$"ndb_node_${i}_host` >> /tmp/hosts.$uniq_id
+ i=`expr $i + 1`
+done
+
+i=1
+while [ $i -le $api_nodes ]
+do
+ echo `eval echo "\$"api_node_${i}_host` >> /tmp/hosts.$uniq_id
+ i=`expr $i + 1`
+done
+
+sort -u -o /tmp/hosts.$uniq_id /tmp/hosts.$uniq_id
+
+get_computer_id(){
+ grep -w -n $1 /tmp/hosts.$uniq_id | cut -d ":" -f 1
+}
+
+get_mgm_computer_id(){
+ a=`eval echo "\$"mgm_node_${1}_host`
+ get_computer_id $a
+}
+
+get_ndb_computer_id(){
+ a=`eval echo "\$"ndb_node_${1}_host`
+ get_computer_id $a
+}
+
+get_api_computer_id(){
+ a=`eval echo "\$"api_node_${1}_host`
+ get_computer_id $a
+}
+
+# -- Write config files --
+
+mgm_port=$baseport
+
+(
+ i=1
+ #echo "COMPUTERS"
+ cat /tmp/hosts.$uniq_id | while read host
+ do
+ echo "[COMPUTER]"
+ echo "Id: $i"
+ echo "ByteOrder: Big"
+ echo "HostName: $host"
+ echo
+ i=`expr $i + 1`
+ done
+
+ node_id=1
+ echo
+
+ # Mgm process
+ echo
+ echo "[MGM]"
+ echo "Id: $node_id"
+ echo "ExecuteOnComputer: `get_mgm_computer_id 1`"
+ echo "PortNumber: $mgm_port"
+ node_id=`expr $node_id + 1`
+
+ # Ndb processes
+ i=1
+ ndb_nodes=`trim $ndb_nodes`
+ while [ $i -le $ndb_nodes ]
+ do
+ echo
+ echo "[DB]"
+ echo "Id: $node_id"
+ echo "ExecuteOnComputer: `get_ndb_computer_id $i`"
+ echo "FileSystemPath: $basedir/run/node-${node_id}-fs"
+ i=`expr $i + 1`
+ node_id=`expr $node_id + 1`
+ done
+
+ # API processes
+ i=1
+ while [ $i -le $api_nodes ]
+ do
+ echo
+ echo "[API]"
+ echo "Id: $node_id"
+ echo "ExecuteOnComputer: `get_api_computer_id $i`"
+ i=`expr $i + 1`
+ node_id=`expr $node_id + 1`
+ done
+
+ # Connections
+ current_port=`expr $mgm_port + 1`
+ echo
+
+ # Connect Mgm with all ndb-nodes
+ i=1
+ while [ $i -le $ndb_nodes ]
+ do
+ echo
+ echo "[TCP]"
+ echo "NodeId1: 1"
+ echo "NodeId2: `expr $i + 1`"
+ echo "PortNumber: $current_port"
+ i=`expr $i + 1`
+ current_port=`expr $current_port + 1`
+ done
+
+ # Connect All ndb processes with all ndb processes
+ i=1
+ while [ $i -le $ndb_nodes ]
+ do
+ j=`expr $i + 1`
+ while [ $j -le $ndb_nodes ]
+ do
+ echo
+ echo "[TCP]"
+ echo "NodeId1: `expr $i + 1`"
+ echo "NodeId2: `expr $j + 1`"
+ echo "PortNumber: $current_port"
+ j=`expr $j + 1`
+ current_port=`expr $current_port + 1`
+ done
+ i=`expr $i + 1`
+ done
+
+ # Connect all ndb-nodes with all api nodes
+ i=1
+ while [ $i -le $ndb_nodes ]
+ do
+ j=1
+ while [ $j -le $api_nodes ]
+ do
+ echo
+ echo "[TCP]"
+ echo "NodeId1: `expr $i + 1`"
+ echo "NodeId2: `expr $j + $ndb_nodes + 1`"
+ echo "PortNumber: $current_port"
+ j=`expr $j + 1`
+ current_port=`expr $current_port + 1`
+ done
+ i=`expr $i + 1`
+ done
+ echo
+) > $DST
+
+trace "Init config file done"
+
+if [ -z "$dst_dir" ]
+then
+ cat $DST
+ rm -f $DST
+ rm -f /tmp/hosts.$uniq_id
+ exit 0
+fi
+
+###
+# Create Ndb.cfg files
+
+# nodeid=2;host=localhost:2200
+
+# Mgm node
+mkcfg(){
+ mkdir -p $dst_dir/${2}.ndb_${1}
+ (
+ echo "OwnProcessId $2"
+ echo "host://${mgm_node_1_host}:${mgm_port}"
+ ) > $dst_dir/${2}.ndb_${1}/Ndb.cfg
+ if [ $1 = "db" ]
+ then
+ mkdir $dst_dir/node-${2}-fs
+ fi
+}
+
+mkcfg mgm 1
+cat $DST > $dst_dir/1.ndb_mgm/initconfig.txt
+
+trace "Creating Ndb.cfg for ndb nodes"
+
+current_node=2
+i=1
+while [ $i -le $ndb_nodes ]
+do
+ mkcfg db ${current_node}
+ i=`expr $i + 1`
+ current_node=`expr $current_node + 1`
+done
+
+trace "Creating Ndb.cfg for api nodes"
+
+i=1
+while [ $i -le $api_nodes ]
+do
+ mkcfg api ${current_node}
+ i=`expr $i + 1`
+ current_node=`expr $current_node + 1`
+done
+
+rm -f $DST
+rm -f /tmp/hosts.$uniq_id
+
+
+exit 0
+# vim: set sw=4:
diff --git a/storage/ndb/test/run-test/make-html-reports.sh b/storage/ndb/test/run-test/make-html-reports.sh
new file mode 100755
index 00000000000..67395ceba47
--- /dev/null
+++ b/storage/ndb/test/run-test/make-html-reports.sh
@@ -0,0 +1,192 @@
+#!/bin/sh
+
+src_dir=$1
+run=$2
+date=$3
+src_file=$src_dir/report.txt
+
+if [ ! -f $src_dir/report.txt ]
+then
+ echo "$src_dir/report.txt is missing"
+ exit 1
+fi
+
+###
+#
+# General html functions
+trim(){
+ echo $*
+}
+
+header(){
+ cat <<EOF
+<html><head><title>$*</title></head>
+<body>
+EOF
+}
+
+footer(){
+ cat <<EOF
+</body></html>
+EOF
+}
+
+heading(){
+ h=$1; shift
+ cat <<EOF
+<h$h>$*</h$h>
+EOF
+}
+
+table(){
+ echo "<table $*>"
+}
+
+end_table(){
+ echo "</table>"
+}
+
+row(){
+ echo "<tr>"
+}
+
+end_row(){
+ echo "</tr>"
+}
+
+c_column(){
+ cat <<EOF
+<td valign=center align=center>$*</td>
+EOF
+}
+
+bold(){
+ cat <<EOF
+<b>$*</b>
+EOF
+}
+column(){
+ cat <<EOF
+<td valign=center align=left>$*</td>
+EOF
+}
+
+para(){
+ cat <<EOF
+<p></p>
+EOF
+}
+
+hr(){
+ cat <<EOF
+<hr>
+EOF
+}
+
+# -- Verify
+time_spec(){
+ # $1 - secs
+ _ts_tmp=$1
+
+ _ts_s=`expr $_ts_tmp % 60`
+ _ts_tmp=`expr $_ts_tmp / 60`
+
+ _ts_m=`expr $_ts_tmp % 60`
+ if [ $_ts_tmp -ge 60 ]
+ then
+ _ts_tmp=`expr $_ts_tmp / 60`
+ else
+ _ts_tmp=0
+ fi
+
+ a=3
+ _ts_h=$_ts_tmp
+
+ if [ $_ts_h -gt 0 ]
+ then
+ ret="${_ts_h}h"
+ fi
+
+ [ $_ts_m -gt 0 ] || [ $_ts_h -gt 0 ] && ret="$ret${_ts_m}m"
+
+ ret="$ret${_ts_s}s"
+ echo $ret
+}
+
+### Main
+
+report_file=$src_dir/report.html
+summary_file=$src_dir/summary.html
+
+passed=0
+failed=0
+total=0
+
+pass(){
+ passed=`expr $passed + 1`
+}
+
+fail(){
+ failed=`expr $failed + 1`
+}
+
+(
+ header Report $run $date
+ table "border=1"
+ row
+ column `bold Test case`
+ column `bold Result`
+ column `bold Elapsed`
+ column `bold Log`
+ end_row
+) > $report_file
+
+cat $src_file | while read line
+do
+ eval `echo $line | awk -F";" '{ printf("prg=\"%s\"; no=\"%s\"; res=\"%s\"; time=\"%s\"", $1, $2, $3, $4); }'`
+
+ prg=`trim $prg`
+ no=`trim $no`
+ res=`trim $res`
+ time=`trim $time`
+ res_dir="<a href=\"result.$no/\">log</a>"
+
+ ts=`time_spec $time`
+ res_txt=""
+ case $res in
+ 0) pass; res_txt="PASSED";;
+ *) fail; res_txt="FAILED";;
+ esac
+
+ if [ ! -d "$src_dir/result.$no" ]; then res_dir="&nbsp;"; fi
+
+ total=`expr $total + $time`
+
+ (
+ row
+ column $prg
+ column $res_txt
+ column $ts
+ column $res_dir
+ end_row
+ ) >> $report_file
+
+ (
+ row
+ column $run
+ column $date
+ column $passed
+ column $failed
+ column `time_spec $total`
+ column "<a href=\"result-$run/$date/report.html\">report</a>"
+ column "<a href=\"result-$run/$date/log.txt\">log.txt</a>"
+ end_row
+ ) > $summary_file
+done
+
+(
+ end_table
+ footer
+) >> $report_file
+
+exit 0
diff --git a/storage/ndb/test/run-test/make-index.sh b/storage/ndb/test/run-test/make-index.sh
new file mode 100755
index 00000000000..944f0df790b
--- /dev/null
+++ b/storage/ndb/test/run-test/make-index.sh
@@ -0,0 +1,242 @@
+#!/bin/sh
+# NAME
+# make-index.sh
+#
+# SYNOPSIS
+# make-index.sh [ -d <dir> ]
+#
+# DESCRIPTION
+#
+# OPTIONS
+#
+# EXAMPLES
+#
+#
+# ENVIRONMENT
+# NDB_PROJ_HOME Home dir for ndb
+#
+# FILES
+# $NDB_PROJ_HOME/lib/funcs.sh general shell script functions
+#
+#
+# SEE ALSO
+#
+# DIAGNOSTICTS
+#
+# VERSION
+# 1.0
+#
+# AUTHOR
+# Jonas Oreland
+#
+
+progname=`basename $0`
+synopsis="make-index.sh [ -d <dir> ]"
+
+: ${NDB_PROJ_HOME:?} # If undefined, exit with error message
+
+: ${NDB_LOCAL_BUILD_OPTIONS:=--} # If undef, set to --. Keeps getopts happy.
+ # You may have to experiment a bit
+ # to get quoting right (if you need it).
+
+
+. $NDB_PROJ_HOME/lib/funcs.sh # Load some good stuff
+
+# defaults for options related variables
+#
+
+dst_dir=/home/autotest/html
+report_date=`date '+%Y-%m-%d'`
+uniq_id=$$.$$
+verbose=yes
+
+# used if error when parsing the options environment variable
+#
+env_opterr="options environment variable: <<$options>>"
+
+# Option parsing, for the options variable as well as the command line.
+#
+# We want to be able to set options in an environment variable,
+# as well as on the command line. In order not to have to repeat
+# the same getopts information twice, we loop two times over the
+# getopts while loop. The first time, we process options from
+# the options environment variable, the second time we process
+# options from the command line.
+#
+# The things to change are the actual options and what they do.
+#
+#
+
+for optstring in "$options" "" # 1. options variable 2. cmd line
+do
+
+ while getopts q:s:R:d: i $optstring # optstring empty => no arg => cmd line
+ do
+ case $i in
+
+ q) verbose="";; # echo important things
+ d) dst_dir=$OPTARG;; # Destination directory
+ \?) syndie $env_opterr;; # print synopsis and exit
+
+ esac
+ done
+
+ [ -n "$optstring" ] && OPTIND=1 # Reset for round 2, cmdline options
+
+ env_opterr= # Round 2 should not use the value
+
+done
+shift `expr $OPTIND - 1`
+
+dst_dir=`abspath $dst_dir`
+
+###
+#
+# General html functions
+header(){
+ cat <<EOF
+<html><head><title>$*</title></head>
+<body>
+EOF
+}
+
+footer(){
+ cat <<EOF
+</body></html>
+EOF
+}
+
+heading(){
+ h=$1; shift
+ cat <<EOF
+<h$h>$*</h$h>
+EOF
+}
+
+table(){
+ echo "<table $*>"
+}
+
+end_table(){
+ echo "</table>"
+}
+
+row(){
+ echo "<tr>"
+}
+
+end_row(){
+ echo "</tr>"
+}
+
+c_column(){
+ cat <<EOF
+<td valign=center align=center>$*</td>
+EOF
+}
+
+bold(){
+ cat <<EOF
+<b>$*</b>
+EOF
+}
+column(){
+ cat <<EOF
+<td valign=center align=left>$*</td>
+EOF
+}
+
+para(){
+ cat <<EOF
+<p></p>
+EOF
+}
+
+hr(){
+ cat <<EOF
+<hr>
+EOF
+}
+
+inc_summary() {
+ grep -v 'html>' $2 | grep -v body | sed 's/href="/href="'$1'\//g'
+}
+
+# --- option parsing done ---
+
+
+
+# -- Verify
+trace "Verifying arguments"
+
+# --- option verifying done ---
+
+### Main
+
+# Re creating index
+trace "Creating index"
+(
+ header "Autotest super-duper index"
+ heading 1 "<center>Autotest super-duper index</center>"
+ cat -E README.autotest | sed 's/\$/<BR>/g'
+ echo "<br>"
+ echo "Current <a href="crontab.current">crontab</a> installed on mc01 running [" `uname -a` "]"
+ hr
+
+ dirs=`find $dst_dir -name 'summary.*.html' -type f -maxdepth 2 -exec dirname {} \; | sort -u`
+
+ dates=`find $dst_dir -name 'summary.*.html' -type f -maxdepth 2 -exec basename {} \; | sed 's/summary\.\(.*\)\.html/\1/g' | sort -u | sort -r`
+
+ echo "<p align=center>"
+
+#inline 5 latest reports
+ r_count=5
+ for d in $dates
+ do
+ for o in $dirs
+ do
+ o=`basename $o`
+ if [ -r $dst_dir/$o/summary.$d.html ]
+ then
+ inc_summary $o $dst_dir/$o/summary.$d.html
+ hr
+
+ r_count=`expr $r_count - 1`
+ if [ $r_count -eq 0 ]
+ then
+ break 2
+ fi
+ fi
+ done
+ done
+
+ table "border=1"
+ row
+ for i in $dirs
+ do
+ i=`basename $i`
+ column `bold $i`
+ done
+ end_row
+
+
+ for d in $dates
+ do
+ row
+ for o in $dirs
+ do
+ o=`basename $o`
+ if [ -r $dst_dir/$o/summary.$d.html ]
+ then
+ column "<a href=$o/summary.$d.html>$d</a>"
+ else
+ column ""
+ fi
+ done
+ end_row
+ done
+ end_table
+ footer
+) > $dst_dir/index.html
+
+exit 0
diff --git a/storage/ndb/test/run-test/ndb-autotest.sh b/storage/ndb/test/run-test/ndb-autotest.sh
new file mode 100755
index 00000000000..397df97d52f
--- /dev/null
+++ b/storage/ndb/test/run-test/ndb-autotest.sh
@@ -0,0 +1,228 @@
+#!/bin/sh
+
+save_args=$*
+VERSION="ndb-autotest.sh version 1.0"
+
+DATE=`date '+%Y-%m-%d'`
+export DATE
+
+set -e
+ulimit -Sc unlimited
+
+echo "`date` starting: $*"
+
+RSYNC_RSH=ssh
+export RSYNC_RSH
+
+do_clone=yes
+build=yes
+deploy=yes
+
+clone=5.0-ndb
+RUN="daily-basic daily-devel"
+conf=autotest.conf
+
+while [ "$1" ]
+do
+ case "$1" in
+ --no-clone) do_clone="";;
+ --no-build) build="";;
+ --no-deploy) deploy="";;
+ --clone=*) clone=`echo $1 | sed s/--clone=//`;;
+ --conf=*) conf=`echo $1 | sed s/--conf=//`;;
+ --version) echo $VERSION; exit;;
+ *) RUN=$*;;
+ esac
+ shift
+done
+
+if [ -f $conf ]
+then
+ . $conf
+else
+ echo "Can't find config file: $conf"
+ exit
+fi
+
+env
+
+LOCK=$HOME/.autotest-lock
+src_clone=$src_clone_base-$clone
+
+if [ -f $LOCK ]
+then
+ echo "Lock file exists: $LOCK"
+ exit 1
+fi
+
+echo "$DATE $RUN" > $LOCK
+trap "rm -f $LOCK" ERR
+
+dst_place=${build_dir}/clone-mysql-$clone-$DATE
+
+if [ "$do_clone" ]
+then
+ rm -rf $dst_place
+ bk clone $src_clone $dst_place
+fi
+
+if [ "$build" ]
+then
+ cd $dst_place
+ rm -rf $run_dir/*
+ aclocal; autoheader; autoconf; automake
+ (cd innobase; aclocal; autoheader; autoconf; automake)
+ (cd bdb/dist; sh s_all)
+ eval $configure --prefix=$run_dir
+ make
+ make install
+fi
+
+###
+# check script version
+#
+script=$run_dir/mysql-test/ndb/ndb-autotest.sh
+if [ -x $script ]
+then
+ $script --version > /tmp/version.$$
+else
+ echo $VERSION > /tmp/version.$$
+fi
+match=`grep -c "$VERSION" /tmp/version.$$`
+rm -f /tmp/version.$$
+if [ $match -eq 0 ]
+then
+ echo "Incorrect script version...restarting"
+ cp $run_dir/mysql-test/ndb/ndb-autotest.sh /tmp/at.$$.sh
+ rm -rf $run_dir $dst_place
+ sh /tmp/at.$$.sh $save_args
+ exit
+fi
+
+# Check that all interesting files are present
+test_dir=$run_dir/mysql-test/ndb
+atrt=$test_dir/atrt
+html=$test_dir/make-html-reports.sh
+PATH=$test_dir:$PATH
+export PATH
+
+filter(){
+ neg=$1
+ shift
+ while [ $# -gt 0 ]
+ do
+ if [ `grep -c $1 $neg` -eq 0 ] ; then echo $1; fi
+ shift
+ done
+}
+
+###
+# check ndb_cpcc fail hosts
+#
+ndb_cpcc $hosts | awk '{ if($1=="Failed"){ print;}}' > /tmp/failed.$DATE
+filter /tmp/failed.$DATE $hosts > /tmp/hosts.$DATE
+hosts=`cat /tmp/hosts.$DATE`
+
+if [ "$deploy" ]
+then
+ (cd / && tar cfz /tmp/build.$DATE.tgz $run_dir )
+ for i in $hosts
+ do
+ ok=0
+ scp /tmp/build.$DATE.tgz $i:/tmp/build.$DATE.$$.tgz && \
+ ssh $i "rm -rf /space/autotest/*" && \
+ ssh $i "cd / && tar xfz /tmp/build.$DATE.$$.tgz" && \
+ ssh $i "rm /tmp/build.$DATE.$$.tgz" && ok=1
+ if [ $ok -eq 0 ]
+ then
+ echo "$i failed during scp/ssh, excluding"
+ echo $i >> /tmp/failed.$DATE
+ fi
+ done
+fi
+rm -f /tmp/build.$DATE.tgz
+
+###
+# handle scp failed hosts
+#
+filter /tmp/failed.$DATE $hosts > /tmp/hosts.$DATE
+hosts=`cat /tmp/hosts.$DATE`
+cat /tmp/failed.$DATE > /tmp/filter_hosts.$$
+
+###
+# functions for running atrt
+#
+choose(){
+ SRC=$1
+ TMP1=/tmp/choose.$$
+ TMP2=/tmp/choose.$$.$$
+ shift
+
+ cp $SRC $TMP1
+ i=1
+ while [ $# -gt 0 ]
+ do
+ sed -e s,"CHOOSE_host$i",$1,g < $TMP1 > $TMP2
+ mv $TMP2 $TMP1
+ shift
+ i=`expr $i + 1`
+ done
+ cat $TMP1
+ rm -f $TMP1
+}
+start(){
+ rm -rf report.txt result* log.txt
+ $atrt -v -v -r -R --log-file=log.txt --testcase-file=$test_dir/$2-tests.txt &
+ pid=$!
+ echo $pid > run.pid
+ wait $pid
+ rm run.pid
+ [ -f log.txt ] && mv log.txt $3
+ [ -f report.txt ] && mv report.txt $3
+ [ "`find . -name 'result*'`" ] && mv result* $3
+ cd $3
+ sh $html . $1 $DATE
+ cd ..
+ p2=`pwd`
+ cd ..
+ tar cfz /tmp/res.$$.tgz `basename $p2`/$DATE
+ scp /tmp/res.$$.tgz $result_host:$result_path
+ ssh $result_host "cd $result_path && tar xfz res.$$.tgz && rm -f res.$$.tgz"
+ rm -f /tmp/res.$$.tgz
+}
+
+p=`pwd`
+for dir in $RUN
+do
+ echo "Fixing hosts for $dir"
+
+ run_dir=$base_dir/run-$dir-mysql-$clone-$target
+ res_dir=$base_dir/result-$dir-mysql-$clone-$target/$DATE
+
+ mkdir -p $res_dir
+ rm -rf $res_dir/*
+
+ count=`grep -c "COMPUTER" $run_dir/1.ndb_mgmd/initconfig.template`
+ avail_hosts=`filter /tmp/filter_hosts.$$ $hosts`
+ avail=`echo $avail_hosts | wc -w`
+ if [ $count -gt $avail ]
+ then
+ echo "Not enough hosts"
+ echo "Needs: $count available: $avail ($avail_hosts)"
+ break;
+ fi
+
+ run_hosts=`echo $avail_hosts| awk '{for(i=1;i<='$count';i++)print $i;}'`
+ choose $run_dir/d.template $run_hosts > $run_dir/d.txt
+ choose $run_dir/1.ndb_mgmd/initconfig.template $run_hosts > $run_dir/1.ndb_mgmd/config.ini
+ echo $run_hosts >> /tmp/filter_hosts.$$
+
+ cd $run_dir
+ start $dir-mysql-$clone-$target $dir $res_dir &
+done
+cd $p
+rm /tmp/filter_hosts.$$
+
+wait
+
+rm -f $LOCK
diff --git a/storage/ndb/test/run-test/run-test.hpp b/storage/ndb/test/run-test/run-test.hpp
new file mode 100644
index 00000000000..ff7f916d4ef
--- /dev/null
+++ b/storage/ndb/test/run-test/run-test.hpp
@@ -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 */
+
+#ifndef atrt_config_hpp
+#define atrt_config_hpp
+
+#include <getarg.h>
+#include <Vector.hpp>
+#include <BaseString.hpp>
+#include <Logger.hpp>
+#include <mgmapi.h>
+#include <CpcClient.hpp>
+
+#undef MYSQL_CLIENT
+
+enum ErrorCodes {
+ ERR_OK = 0,
+ ERR_NDB_FAILED = 101,
+ ERR_SERVERS_FAILED = 102,
+ ERR_MAX_TIME_ELAPSED = 103
+};
+
+struct atrt_host {
+ size_t m_index;
+ BaseString m_user;
+ BaseString m_base_dir;
+ BaseString m_hostname;
+ SimpleCpcClient * m_cpcd;
+};
+
+struct atrt_process {
+ size_t m_index;
+ BaseString m_hostname;
+ struct atrt_host * m_host;
+
+ enum Type {
+ ALL = 255,
+ NDB_DB = 1,
+ NDB_API = 2,
+ NDB_MGM = 4,
+ NDB_REP = 8,
+ MYSQL_SERVER = 16,
+ MYSQL_CLIENT = 32
+ } m_type;
+
+ SimpleCpcClient::Process m_proc;
+ short m_ndb_mgm_port;
+ NdbMgmHandle m_ndb_mgm_handle; // if type == ndb_mgm
+};
+
+struct atrt_config {
+ BaseString m_key;
+ Vector<atrt_host> m_hosts;
+ Vector<atrt_process> m_processes;
+};
+
+struct atrt_testcase {
+ bool m_report;
+ time_t m_max_time;
+ BaseString m_command;
+ BaseString m_args;
+};
+
+extern Logger g_logger;
+
+bool parse_args(int argc, const char** argv);
+bool setup_config(atrt_config&);
+bool connect_hosts(atrt_config&);
+bool connect_ndb_mgm(atrt_config&);
+bool wait_ndb(atrt_config&, int ndb_mgm_node_status);
+bool start_processes(atrt_config&, int);
+bool stop_processes(atrt_config&, int);
+bool update_status(atrt_config&, int);
+int is_running(atrt_config&, int);
+bool gather_result(atrt_config&, int * result);
+
+bool read_test_case(FILE *, atrt_testcase&, int& line);
+bool setup_test_case(atrt_config&, const atrt_testcase&);
+
+bool setup_hosts(atrt_config&);
+
+#endif
diff --git a/storage/ndb/test/src/CpcClient.cpp b/storage/ndb/test/src/CpcClient.cpp
new file mode 100644
index 00000000000..1d1b4fcb977
--- /dev/null
+++ b/storage/ndb/test/src/CpcClient.cpp
@@ -0,0 +1,557 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <NdbTCP.h>
+#include "CpcClient.hpp"
+
+#define CPC_CMD(name, value, desc) \
+ { (name), \
+ 0, \
+ ParserRow_t::Cmd, \
+ ParserRow_t::String, \
+ ParserRow_t::Optional, \
+ ParserRow_t::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ (desc), \
+ (value) }
+
+#define CPC_ARG(name, type, opt, desc) \
+ { (name), \
+ 0, \
+ ParserRow_t::Arg, \
+ ParserRow_t::type, \
+ ParserRow_t::opt, \
+ ParserRow_t::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ (desc), 0 }
+
+#define CPC_END() \
+ { 0, \
+ 0, \
+ ParserRow_t::Arg, \
+ ParserRow_t::Int, \
+ ParserRow_t::Optional, \
+ ParserRow_t::IgnoreMinMax, \
+ 0, 0, \
+ 0, \
+ 0, 0 }
+
+#ifdef DEBUG_PRINT_PROPERTIES
+static void printprop(const Properties &p) {
+ Properties::Iterator iter(&p);
+ const char *name;
+ while((name = iter.next()) != NULL) {
+ PropertiesType t;
+ Uint32 val_i;
+ BaseString val_s;
+
+ p.getTypeOf(name, &t);
+ switch(t) {
+ case PropertiesType_Uint32:
+ p.get(name, &val_i);
+ ndbout << name << " (Uint32): " << val_i << endl;
+ break;
+ case PropertiesType_char:
+ p.get(name, val_s);
+ ndbout << name << " (string): " << val_s << endl;
+ break;
+ default:
+ ndbout << "Unknown type " << t << endl;
+ break;
+ }
+ }
+}
+#endif
+
+void
+SimpleCpcClient::cmd_stop(char *arg) {
+ Properties p;
+ Vector<Process> proc_list;
+
+ list_processes(proc_list, p);
+ bool stopped = false;
+
+ for(size_t i = 0; i < proc_list.size(); i++) {
+ if(strcmp(proc_list[i].m_name.c_str(), arg) == 0) {
+ stopped = true;
+ Properties reply;
+ stop_process(proc_list[i].m_id, reply);
+
+ Uint32 status;
+ reply.get("status", &status);
+ if(status != 0) {
+ BaseString msg;
+ reply.get("errormessage", msg);
+ ndbout << "Stop failed: " << msg << endl;
+ }
+ }
+ }
+
+ if(!stopped)
+ ndbout << "No such process" << endl;
+}
+
+int
+SimpleCpcClient::stop_process(Uint32 id, Properties& reply){
+ const ParserRow_t stop_reply[] = {
+ CPC_CMD("stop process", NULL, ""),
+ CPC_ARG("status", Int, Mandatory, ""),
+ CPC_ARG("id", Int, Optional, ""),
+ CPC_ARG("errormessage", String, Optional, ""),
+
+ CPC_END()
+ };
+
+ Properties args;
+ args.put("id", id);
+
+ const Properties* ret = cpc_call("stop process", args, stop_reply);
+ if(ret == 0){
+ reply.put("status", (Uint32)0);
+ reply.put("errormessage", "unknown error");
+ return -1;
+ }
+
+ Uint32 status = 0;
+ ret->get("status", &status);
+ reply.put("status", status);
+ if(status != 0) {
+ BaseString msg;
+ ret->get("errormessage", msg);
+ reply.put("errormessage", msg.c_str());
+ }
+
+ return status;
+}
+
+void
+SimpleCpcClient::cmd_start(char *arg) {
+ Properties p;
+ Vector<Process> proc_list;
+ list_processes(proc_list, p);
+ bool startped = false;
+
+ for(size_t i = 0; i < proc_list.size(); i++) {
+ if(strcmp(proc_list[i].m_name.c_str(), arg) == 0) {
+ startped = true;
+
+ Properties reply;
+ start_process(proc_list[i].m_id, reply);
+
+ Uint32 status;
+ reply.get("status", &status);
+ if(status != 0) {
+ BaseString msg;
+ reply.get("errormessage", msg);
+ ndbout << "Start failed: " << msg << endl;
+ }
+ }
+ }
+
+ if(!startped)
+ ndbout << "No such process" << endl;
+}
+
+int
+SimpleCpcClient::start_process(Uint32 id, Properties& reply){
+ const ParserRow_t start_reply[] = {
+ CPC_CMD("start process", NULL, ""),
+ CPC_ARG("status", Int, Mandatory, ""),
+ CPC_ARG("id", Int, Optional, ""),
+ CPC_ARG("errormessage", String, Optional, ""),
+
+ CPC_END()
+ };
+
+ Properties args;
+ args.put("id", id);
+
+ const Properties* ret = cpc_call("start process", args, start_reply);
+ if(ret == 0){
+ reply.put("status", (Uint32)0);
+ reply.put("errormessage", "unknown error");
+ return -1;
+ }
+
+ Uint32 status = 0;
+ ret->get("status", &status);
+ reply.put("status", status);
+ if(status != 0) {
+ BaseString msg;
+ ret->get("errormessage", msg);
+ reply.put("errormessage", msg.c_str());
+ }
+
+ return status;
+}
+
+int
+SimpleCpcClient::undefine_process(Uint32 id, Properties& reply){
+ const ParserRow_t stop_reply[] = {
+ CPC_CMD("undefine process", NULL, ""),
+ CPC_ARG("status", Int, Mandatory, ""),
+ CPC_ARG("id", Int, Optional, ""),
+ CPC_ARG("errormessage", String, Optional, ""),
+
+ CPC_END()
+ };
+
+ Properties args;
+ args.put("id", id);
+
+ const Properties* ret = cpc_call("undefine process", args, stop_reply);
+ if(ret == 0){
+ reply.put("status", (Uint32)0);
+ reply.put("errormessage", "unknown error");
+ return -1;
+ }
+
+ Uint32 status = 0;
+ ret->get("status", &status);
+ reply.put("status", status);
+ if(status != 0) {
+ BaseString msg;
+ ret->get("errormessage", msg);
+ reply.put("errormessage", msg.c_str());
+ }
+
+ return status;
+}
+
+static void
+printproc(SimpleCpcClient::Process & p) {
+ ndbout.println("Name: %s", p.m_name.c_str());
+ ndbout.println("Id: %d", p.m_id);
+ ndbout.println("Type: %s", p.m_type.c_str());
+ ndbout.println("Group: %s", p.m_group.c_str());
+ ndbout.println("Program path: %s", p.m_path.c_str());
+ ndbout.println("Arguments: %s", p.m_args.c_str());
+ ndbout.println("Environment: %s", p.m_env.c_str());
+ ndbout.println("Working directory: %s", p.m_cwd.c_str());
+ ndbout.println("Owner: %s", p.m_owner.c_str());
+ ndbout.println("Runas: %s", p.m_runas.c_str());
+ ndbout.println("Ulimit: %s", p.m_ulimit.c_str());
+ ndbout.println("");
+}
+
+void
+SimpleCpcClient::cmd_list(char *arg) {
+ Properties p;
+ Vector<Process> proc_list;
+ list_processes(proc_list, p);
+
+ for(size_t i = 0; i < proc_list.size(); i++) {
+ printproc(proc_list[i]);
+ }
+}
+
+static int
+convert(const Properties & src, SimpleCpcClient::Process & dst){
+ bool b = true;
+ b &= src.get("id", (Uint32*)&dst.m_id);
+ b &= src.get("name", dst.m_name);
+ b &= src.get("type", dst.m_type);
+ b &= src.get("status", dst.m_status);
+ b &= src.get("owner", dst.m_owner);
+ b &= src.get("group", dst.m_group);
+ b &= src.get("path", dst.m_path);
+ b &= src.get("args", dst.m_args);
+ b &= src.get("env", dst.m_env);
+ b &= src.get("cwd", dst.m_cwd);
+ b &= src.get("runas", dst.m_runas);
+
+ b &= src.get("stdin", dst.m_stdin);
+ b &= src.get("stdout", dst.m_stdout);
+ b &= src.get("stderr", dst.m_stderr);
+ b &= src.get("ulimit", dst.m_ulimit);
+
+ return b;
+}
+
+static int
+convert(const SimpleCpcClient::Process & src, Properties & dst ){
+ bool b = true;
+ //b &= dst.put("id", (Uint32)src.m_id);
+ b &= dst.put("name", src.m_name.c_str());
+ b &= dst.put("type", src.m_type.c_str());
+ //b &= dst.put("status", src.m_status.c_str());
+ b &= dst.put("owner", src.m_owner.c_str());
+ b &= dst.put("group", src.m_group.c_str());
+ b &= dst.put("path", src.m_path.c_str());
+ b &= dst.put("args", src.m_args.c_str());
+ b &= dst.put("env", src.m_env.c_str());
+ b &= dst.put("cwd", src.m_cwd.c_str());
+ b &= dst.put("runas", src.m_runas.c_str());
+
+ b &= dst.put("stdin", src.m_stdin.c_str());
+ b &= dst.put("stdout", src.m_stdout.c_str());
+ b &= dst.put("stderr", src.m_stderr.c_str());
+ b &= dst.put("ulimit", src.m_ulimit.c_str());
+
+ return b;
+}
+
+int
+SimpleCpcClient::define_process(Process & p, Properties& reply){
+ const ParserRow_t define_reply[] = {
+ CPC_CMD("define process", NULL, ""),
+ CPC_ARG("status", Int, Mandatory, ""),
+ CPC_ARG("id", Int, Optional, ""),
+ CPC_ARG("errormessage", String, Optional, ""),
+
+ CPC_END()
+ };
+
+ Properties args;
+ convert(p, args);
+
+ const Properties* ret = cpc_call("define process", args, define_reply);
+ if(ret == 0){
+ reply.put("status", (Uint32)0);
+ reply.put("errormessage", "unknown error");
+ return -1;
+ }
+
+ Uint32 status = 0;
+ ret->get("status", &status);
+ reply.put("status", status);
+ if(status != 0) {
+ BaseString msg;
+ ret->get("errormessage", msg);
+ reply.put("errormessage", msg.c_str());
+ }
+
+ Uint32 id;
+ if(!ret->get("id", &id)){
+ return -1;
+ }
+
+ p.m_id = id;
+
+ return status;
+}
+
+int
+SimpleCpcClient::list_processes(Vector<Process> &procs, Properties& reply) {
+ int start, end, entry;
+ const ParserRow_t list_reply[] = {
+ CPC_CMD("start processes", &start, ""),
+ CPC_CMD("end processes", &end, ""),
+
+ CPC_CMD("process", &entry, ""),
+ CPC_ARG("id", Int, Mandatory, "Id of process."),
+ CPC_ARG("name", String, Mandatory, "Name of process"),
+ CPC_ARG("group", String, Mandatory, "Group of process"),
+ CPC_ARG("env", String, Mandatory, "Environment variables for process"),
+ CPC_ARG("path", String, Mandatory, "Path to binary"),
+ CPC_ARG("args", String, Mandatory, "Arguments to process"),
+ CPC_ARG("type", String, Mandatory, "Type of process"),
+ CPC_ARG("cwd", String, Mandatory, "Working directory of process"),
+ CPC_ARG("owner", String, Mandatory, "Owner of process"),
+ CPC_ARG("status",String, Mandatory, "Status of process"),
+ CPC_ARG("runas", String, Mandatory, "Run as user"),
+ CPC_ARG("stdin", String, Mandatory, "Redirect stdin"),
+ CPC_ARG("stdout",String, Mandatory, "Redirect stdout"),
+ CPC_ARG("stderr",String, Mandatory, "Redirect stderr"),
+ CPC_ARG("ulimit",String, Mandatory, "ulimit"),
+
+ CPC_END()
+ };
+
+ reply.clear();
+
+ const Properties args;
+
+ cpc_send("list processes", args);
+
+ bool done = false;
+ while(!done) {
+ const Properties *proc;
+ void *p;
+ cpc_recv(list_reply, &proc, &p);
+
+ if(p == &start)
+ {
+ /* do nothing */
+ }
+ else if(p == &end)
+ {
+ done = true;
+ }
+ else if(p == &entry)
+ {
+ if(proc != NULL){
+ Process p;
+ convert(* proc, p);
+ procs.push_back(p);
+ }
+ }
+ else
+ {
+ ndbout_c("internal error: %d", __LINE__);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void
+SimpleCpcClient::cmd_help(char *arg) {
+ ndbout
+ << "HELP Print help text" << endl
+ << "LIST List processes" << endl
+ << "START Start process" << endl
+ << "STOP Stop process" << endl;
+}
+
+SimpleCpcClient::SimpleCpcClient(const char *_host, int _port) {
+ host = strdup(_host);
+ port = _port;
+ cpc_sock = -1;
+ cpc_in = NULL;
+ cpc_out = NULL;
+}
+
+SimpleCpcClient::~SimpleCpcClient() {
+ if(host != NULL) {
+ free(host);
+ host = NULL;
+ }
+
+ port = 0;
+
+ if(cpc_sock == -1) {
+ close(cpc_sock);
+ cpc_sock = -1;
+ }
+
+ if(cpc_in != NULL)
+ delete cpc_in;
+
+ if(cpc_out != NULL)
+ delete cpc_out;
+}
+
+int
+SimpleCpcClient::connect() {
+ struct sockaddr_in sa;
+ struct hostent *hp;
+
+ /* Create socket */
+ cpc_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if(cpc_sock < 0)
+ return -1;
+
+ /* Connect socket */
+ sa.sin_family = AF_INET;
+ hp = gethostbyname(host);
+ if(hp == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
+ sa.sin_port = htons(port);
+ if (::connect(cpc_sock, (struct sockaddr*) &sa, sizeof(sa)) < 0)
+ return -1;
+
+ cpc_in = new SocketInputStream(cpc_sock, 60000);
+ cpc_out = new SocketOutputStream(cpc_sock);
+
+ return 0;
+}
+
+int
+SimpleCpcClient::cpc_send(const char *cmd,
+ const Properties &args) {
+
+ cpc_out->println(cmd);
+
+ Properties::Iterator iter(&args);
+ const char *name;
+ while((name = iter.next()) != NULL) {
+ PropertiesType t;
+ Uint32 val_i;
+ BaseString val_s;
+
+ args.getTypeOf(name, &t);
+ switch(t) {
+ case PropertiesType_Uint32:
+ args.get(name, &val_i);
+ cpc_out->println("%s: %d", name, val_i);
+ break;
+ case PropertiesType_char:
+ args.get(name, val_s);
+ cpc_out->println("%s: %s", name, val_s.c_str());
+ break;
+ default:
+ /* Silently ignore */
+ break;
+ }
+ }
+ cpc_out->println("");
+
+ return 0;
+}
+
+/**
+ * Receive a response from the CPCD. The argument reply will point
+ * to a Properties object describing the reply. Note that the caller
+ * is responsible for deleting the Properties object returned.
+ */
+SimpleCpcClient::Parser_t::ParserStatus
+SimpleCpcClient::cpc_recv(const ParserRow_t *syntax,
+ const Properties **reply,
+ void **user_value) {
+ Parser_t::Context ctx;
+ ParserDummy session(cpc_sock);
+ Parser_t parser(syntax, *cpc_in, true, true, true);
+ *reply = parser.parse(ctx, session);
+ if(user_value != NULL)
+ *user_value = ctx.m_currentCmd->user_value;
+ return ctx.m_status;
+}
+
+const Properties *
+SimpleCpcClient::cpc_call(const char *cmd,
+ const Properties &args,
+ const ParserRow_t *reply_syntax) {
+ cpc_send(cmd, args);
+
+#if 0
+ Parser_t::Context ctx;
+ ParserDummy session(cpc_sock);
+ Parser_t parser(reply_syntax, *cpc_in, true, true, true);
+ const Properties *ret = parser.parse(ctx, session);
+ return ret;
+#endif
+ const Properties *ret;
+ cpc_recv(reply_syntax, &ret);
+ return ret;
+}
+
+
+SimpleCpcClient::ParserDummy::ParserDummy(NDB_SOCKET_TYPE sock)
+ : SocketServer::Session(sock) {
+}
+
+template class Vector<SimpleCpcClient::Process>;
+template class Vector<ParserRow<SimpleCpcClient::ParserDummy> const*>;
diff --git a/storage/ndb/test/src/HugoAsynchTransactions.cpp b/storage/ndb/test/src/HugoAsynchTransactions.cpp
new file mode 100644
index 00000000000..5d2eb451c0b
--- /dev/null
+++ b/storage/ndb/test/src/HugoAsynchTransactions.cpp
@@ -0,0 +1,498 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <HugoAsynchTransactions.hpp>
+
+HugoAsynchTransactions::HugoAsynchTransactions(const NdbDictionary::Table& _t)
+ : HugoTransactions(_t),
+ transactionsCompleted(0),
+ numTransactions(0),
+ transactions(NULL)
+{
+}
+
+HugoAsynchTransactions::~HugoAsynchTransactions(){
+ deallocTransactions();
+}
+
+void asynchCallback(int result, NdbConnection* pTrans,
+ void* anObject) {
+ HugoAsynchTransactions* pHugo = (HugoAsynchTransactions*) anObject;
+
+ pHugo->transactionCompleted();
+
+ if (result == -1) {
+ const NdbError err = pTrans->getNdbError();
+ switch(err.status) {
+ case NdbError::Success:
+ ERR(err);
+ g_info << "ERROR: NdbError reports success when transcaction failed"
+ << endl;
+ break;
+
+ case NdbError::TemporaryError:
+ ERR(err);
+ break;
+
+#if 0
+ case 626: // Tuple did not exist
+ g_info << (unsigned int)pHugo->getTransactionsCompleted() << ": "
+ << err.code << " " << err.message << endl;
+ break;
+#endif
+
+ case NdbError::UnknownResult:
+ ERR(err);
+ break;
+
+ case NdbError::PermanentError:
+ switch (err.classification) {
+ case NdbError::ConstraintViolation:
+ // Tuple already existed, OK in this application,
+ // but should be reported
+ g_info << (unsigned int)pHugo->getTransactionsCompleted()
+ << ": " << err.code << " " << err.message << endl;
+ break;
+ default:
+ ERR(err);
+ break;
+ }
+ break;
+ }
+ } else {// if (result == -1)
+ /*
+ ndbout << (unsigned int)pHugo->getTransactionsCompleted() << " completed"
+ << endl;
+ */
+ }
+}
+
+int
+HugoAsynchTransactions::loadTableAsynch(Ndb* pNdb,
+ int records,
+ int batch,
+ int trans,
+ int operations){
+
+ int result = executeAsynchOperation(pNdb, records, batch, trans, operations,
+ NO_INSERT);
+ g_info << (unsigned int)transactionsCompleted * operations
+ << "|- inserted..." << endl;
+
+ return result;
+}
+
+void
+HugoAsynchTransactions::transactionCompleted() {
+ transactionsCompleted++;
+}
+
+long
+HugoAsynchTransactions::getTransactionsCompleted() {
+ return transactionsCompleted;
+}
+
+int
+HugoAsynchTransactions::pkDelRecordsAsynch(Ndb* pNdb,
+ int records,
+ int batch,
+ int trans,
+ int operations) {
+
+ g_info << "|- Deleting records asynchronous..." << endl;
+
+ int result = executeAsynchOperation(pNdb, records, batch, trans,
+ operations,
+ NO_DELETE);
+ g_info << "|- " << (unsigned int)transactionsCompleted * operations
+ << " deleted..." << endl;
+
+ return result;
+}
+
+int
+HugoAsynchTransactions::pkReadRecordsAsynch(Ndb* pNdb,
+ int records,
+ int batch,
+ int trans,
+ int operations) {
+
+ g_info << "|- Reading records asynchronous..." << endl;
+
+ allocRows(trans*operations);
+ int result = executeAsynchOperation(pNdb, records, batch, trans, operations,
+ NO_READ);
+
+ g_info << "|- " << (unsigned int)transactionsCompleted * operations
+ << " read..."
+ << endl;
+
+ deallocRows();
+
+ return result;
+}
+
+int
+HugoAsynchTransactions::pkUpdateRecordsAsynch(Ndb* pNdb,
+ int records,
+ int batch,
+ int trans,
+ int operations) {
+
+ g_info << "|- Updating records asynchronous..." << endl;
+
+ int check = 0;
+ int cTrans = 0;
+ int cReadRecords = 0;
+ int cReadIndex = 0;
+ int cRecords = 0;
+ int cIndex = 0;
+
+ transactionsCompleted = 0;
+
+ allocRows(trans*operations);
+ allocTransactions(trans);
+ int a, t, r;
+
+ for (int i = 0; i < batch; i++) { // For each batch
+ while (cRecords < records*batch) {
+ cTrans = 0;
+ cReadIndex = 0;
+ for (t = 0; t < trans; t++) { // For each transaction
+ transactions[t] = pNdb->startTransaction();
+ if (transactions[t] == NULL) {
+ ERR(pNdb->getNdbError());
+ return NDBT_FAILED;
+ }
+ for (int k = 0; k < operations; k++) { // For each operation
+ NdbOperation* pOp = transactions[t]->getNdbOperation(tab.getName());
+ if (pOp == NULL) {
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+
+ // Read
+ // Define primary keys
+ check = pOp->readTupleExclusive();
+ for (a = 0; a < tab.getNoOfColumns(); a++) {
+ if (tab.getColumn(a)->getPrimaryKey() == true) {
+ if (equalForAttr(pOp, a, cReadRecords) != 0){
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+ }
+ }
+ // Define attributes to read
+ for (a = 0; a < tab.getNoOfColumns(); a++) {
+ if ((rows[cReadIndex]->attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+ }
+ cReadIndex++;
+ cReadRecords++;
+
+ } // For each operation
+
+ // Let's prepare...
+ transactions[t]->executeAsynchPrepare(NoCommit, &asynchCallback,
+ this);
+ cTrans++;
+
+ if (cReadRecords >= records) {
+ // No more transactions needed
+ break;
+ }
+ } // For each transaction
+
+ // Wait for all outstanding transactions
+ pNdb->sendPollNdb(3000, 0, 0);
+
+ // Verify the data!
+ for (r = 0; r < trans*operations; r++) {
+ if (calc.verifyRowValues(rows[r]) != 0) {
+ g_info << "|- Verify failed..." << endl;
+ // Close all transactions
+ for (int t = 0; t < cTrans; t++) {
+ pNdb->closeTransaction(transactions[t]);
+ }
+ return NDBT_FAILED;
+ }
+ }
+
+ // Update
+ cTrans = 0;
+ cIndex = 0;
+ for (t = 0; t < trans; t++) { // For each transaction
+ for (int k = 0; k < operations; k++) { // For each operation
+ NdbOperation* pOp = transactions[t]->getNdbOperation(tab.getName());
+ if (pOp == NULL) {
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+
+ int updates = calc.getUpdatesValue(rows[cIndex]) + 1;
+
+ check = pOp->updateTuple();
+ if (check == -1) {
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+
+ // Set search condition for the record
+ for (a = 0; a < tab.getNoOfColumns(); a++) {
+ if (tab.getColumn(a)->getPrimaryKey() == true) {
+ if (equalForAttr(pOp, a, cRecords) != 0) {
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ // Update the record
+ for (a = 0; a < tab.getNoOfColumns(); a++) {
+ if (tab.getColumn(a)->getPrimaryKey() == false) {
+ if (setValueForAttr(pOp, a, cRecords, updates) != 0) {
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+ }
+ }
+ cIndex++;
+ cRecords++;
+
+ } // For each operation
+
+ // Let's prepare...
+ transactions[t]->executeAsynchPrepare(Commit, &asynchCallback,
+ this);
+ cTrans++;
+
+ if (cRecords >= records) {
+ // No more transactions needed
+ break;
+ }
+ } // For each transaction
+
+ // Wait for all outstanding transactions
+ pNdb->sendPollNdb(3000, 0, 0);
+
+ // Close all transactions
+ for (t = 0; t < cTrans; t++) {
+ pNdb->closeTransaction(transactions[t]);
+ }
+
+ } // while (cRecords < records*batch)
+
+ } // For each batch
+
+ deallocTransactions();
+ deallocRows();
+
+ g_info << "|- " << ((unsigned int)transactionsCompleted * operations)/2
+ << " updated..." << endl;
+ return NDBT_OK;
+}
+
+void
+HugoAsynchTransactions::allocTransactions(int trans) {
+ if (transactions != NULL) {
+ deallocTransactions();
+ }
+ numTransactions = trans;
+ transactions = new NdbConnection*[numTransactions];
+}
+
+void
+HugoAsynchTransactions::deallocTransactions() {
+ if (transactions != NULL){
+ delete[] transactions;
+ }
+ transactions = NULL;
+}
+
+int
+HugoAsynchTransactions::executeAsynchOperation(Ndb* pNdb,
+ int records,
+ int batch,
+ int trans,
+ int operations,
+ NDB_OPERATION theOperation,
+ ExecType theType) {
+
+ int check = 0;
+ // int retryAttempt = 0; // Not used at the moment
+ // int retryMax = 5; // Not used at the moment
+ int cTrans = 0;
+ int cRecords = 0;
+ int cIndex = 0;
+ int a,t,r;
+
+ transactionsCompleted = 0;
+ allocTransactions(trans);
+
+ for (int i = 0; i < batch; i++) { // For each batch
+ while (cRecords < records*batch) {
+ cTrans = 0;
+ cIndex = 0;
+ for (t = 0; t < trans; t++) { // For each transaction
+ transactions[t] = pNdb->startTransaction();
+ if (transactions[t] == NULL) {
+ ERR(pNdb->getNdbError());
+ return NDBT_FAILED;
+ }
+ for (int k = 0; k < operations; k++) { // For each operation
+ NdbOperation* pOp = transactions[t]->getNdbOperation(tab.getName());
+ if (pOp == NULL) {
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+
+ switch (theOperation) {
+ case NO_INSERT:
+ // Insert
+ check = pOp->insertTuple();
+ if (check == -1) {
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+
+ // Set a calculated value for each attribute in this table
+ for (a = 0; a < tab.getNoOfColumns(); a++) {
+ if (setValueForAttr(pOp, a, cRecords, 0 ) != 0) {
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+ } // For each attribute
+ break;
+ case NO_UPDATE:
+ // This is a special case and is handled in the calling client...
+ break;
+ break;
+ case NO_READ:
+ // Define primary keys
+ check = pOp->readTuple();
+ for (a = 0; a < tab.getNoOfColumns(); a++) {
+ if (tab.getColumn(a)->getPrimaryKey() == true) {
+ if (equalForAttr(pOp, a, cRecords) != 0){
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+ }
+ }
+ // Define attributes to read
+ for (a = 0; a < tab.getNoOfColumns(); a++) {
+ if ((rows[cIndex]->attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+ }
+ break;
+ case NO_DELETE:
+ // Delete
+ check = pOp->deleteTuple();
+ if (check == -1) {
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+
+ // Define primary keys
+ for (a = 0; a < tab.getNoOfColumns(); a++) {
+ if (tab.getColumn(a)->getPrimaryKey() == true){
+ if (equalForAttr(pOp, a, cRecords) != 0) {
+ ERR(transactions[t]->getNdbError());
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+ }
+ }
+ break;
+ default:
+ // Should not happen...
+ pNdb->closeTransaction(transactions[t]);
+ return NDBT_FAILED;
+ }
+
+ cIndex++;
+ cRecords++;
+
+ } // For each operation
+
+ // Let's prepare...
+ transactions[t]->executeAsynchPrepare(theType, &asynchCallback,
+ this);
+ cTrans++;
+
+ if (cRecords >= records) {
+ // No more transactions needed
+ break;
+ }
+ } // For each transaction
+
+ // Wait for all outstanding transactions
+ pNdb->sendPollNdb(3000, 0, 0);
+
+ // ugly... it's starts to resemble flexXXX ...:(
+ switch (theOperation) {
+ case NO_READ:
+ // Verify the data!
+ for (r = 0; r < trans*operations; r++) {
+ if (calc.verifyRowValues(rows[r]) != 0) {
+ g_info << "|- Verify failed..." << endl;
+ // Close all transactions
+ for (int t = 0; t < cTrans; t++) {
+ pNdb->closeTransaction(transactions[t]);
+ }
+ return NDBT_FAILED;
+ }
+ }
+ break;
+ case NO_INSERT:
+ case NO_UPDATE:
+ case NO_DELETE:
+ break;
+ }
+
+ // Close all transactions
+ for (t = 0; t < cTrans; t++) {
+ pNdb->closeTransaction(transactions[t]);
+ }
+
+ } // while (cRecords < records*batch)
+
+ } // For each batch
+
+ deallocTransactions();
+
+ return NDBT_OK;
+
+}
diff --git a/storage/ndb/test/src/HugoCalculator.cpp b/storage/ndb/test/src/HugoCalculator.cpp
new file mode 100644
index 00000000000..8e01f6442bb
--- /dev/null
+++ b/storage/ndb/test/src/HugoCalculator.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 <ndb_global.h>
+#include "HugoCalculator.hpp"
+#include <NDBT.hpp>
+#include <Base64.hpp>
+
+static
+Uint32
+myRand(Uint64 * seed)
+{
+ const Uint64 mul= 0x5deece66dull;
+ const Uint64 add= 0xb;
+ Uint64 loc_result = *seed * mul + add;
+
+ * seed= loc_result;
+ return loc_result >> 1;
+}
+
+static char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+/* *************************************************************
+ * HugoCalculator
+ *
+ * Comon class for the Hugo test suite, provides the functions
+ * that is used for calculating values to load in to table and
+ * also knows how to verify a row that's been read from db
+ *
+ * ************************************************************/
+HugoCalculator::HugoCalculator(const NdbDictionary::Table& tab) : m_tab(tab) {
+
+ // The "id" column of this table is found in the first integer column
+ int i;
+ for (i=0; i<m_tab.getNoOfColumns(); i++){
+ const NdbDictionary::Column* attr = m_tab.getColumn(i);
+ if (attr->getType() == NdbDictionary::Column::Unsigned){
+ m_idCol = i;
+ break;
+ }
+ }
+
+ // The "number of updates" column for this table is found in the last column
+ for (i=m_tab.getNoOfColumns()-1; i>=0; i--){
+ const NdbDictionary::Column* attr = m_tab.getColumn(i);
+ if (attr->getType() == NdbDictionary::Column::Unsigned &&
+ !attr->getPrimaryKey()){
+ m_updatesCol = i;
+ break;
+ }
+ }
+#if 0
+ ndbout << "idCol = " << m_idCol << endl;
+ ndbout << "updatesCol = " << m_updatesCol << endl;
+#endif
+ // Check that idCol is not conflicting with updatesCol
+ assert(m_idCol != m_updatesCol && m_idCol != -1 && m_updatesCol != -1);
+}
+
+Int32
+HugoCalculator::calcValue(int record,
+ int attrib,
+ int updates) const {
+
+ Int32 i;
+ calcValue(record, attrib, updates, (char*)&i, sizeof(i));
+
+ return i;
+}
+#if 0
+HugoCalculator::U_Int32 calcValue(int record, int attrib, int updates) const;
+HugoCalculator::U_Int64 calcValue(int record, int attrib, int updates) const;
+HugoCalculator::Int64 calcValue(int record, int attrib, int updates) const;
+HugoCalculator::float calcValue(int record, int attrib, int updates) const;
+HugoCalculator::double calcValue(int record, int attrib, int updates) const;
+#endif
+
+const char*
+HugoCalculator::calcValue(int record,
+ int attrib,
+ int updates,
+ char* buf,
+ int len) const {
+ Uint64 seed;
+ const NdbDictionary::Column* attr = m_tab.getColumn(attrib);
+ Uint32 val;
+ do
+ {
+ if (attrib == m_idCol)
+ {
+ val= record;
+ memcpy(buf, &val, 4);
+ return buf;
+ }
+
+ // If this is the update column
+ if (attrib == m_updatesCol)
+ {
+ val= updates;
+ memcpy(buf, &val, 4);
+ return buf;
+ }
+
+ if (attr->getPrimaryKey())
+ {
+ seed = record + attrib;
+ }
+ else
+ {
+ seed = record + attrib + updates;
+ }
+ } while (0);
+
+ val = myRand(&seed);
+
+ if(attr->getNullable() && (((val >> 16) & 255) > 220))
+ return NULL;
+
+ int pos= 0;
+ switch(attr->getType()){
+ 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::Int:
+ case NdbDictionary::Column::Unsigned:
+ case NdbDictionary::Column::Bigint:
+ case NdbDictionary::Column::Bigunsigned:
+ case NdbDictionary::Column::Float:
+ case NdbDictionary::Column::Double:
+ case NdbDictionary::Column::Olddecimal:
+ case NdbDictionary::Column::Olddecimalunsigned:
+ case NdbDictionary::Column::Decimal:
+ case NdbDictionary::Column::Decimalunsigned:
+ case NdbDictionary::Column::Binary:
+ case NdbDictionary::Column::Datetime:
+ case NdbDictionary::Column::Time:
+ case NdbDictionary::Column::Date:
+ case NdbDictionary::Column::Bit:
+ while (len > 4)
+ {
+ memcpy(buf+pos, &val, 4);
+ pos += 4;
+ len -= 4;
+ val= myRand(&seed);
+ }
+
+ memcpy(buf+pos, &val, len);
+ if(attr->getType() == NdbDictionary::Column::Bit)
+ {
+ Uint32 bits= attr->getLength();
+ Uint32 tmp = bits >> 5;
+ Uint32 size = bits & 31;
+ ((Uint32*)buf)[tmp] &= ((1 << size) - 1);
+ }
+ break;
+ case NdbDictionary::Column::Varbinary:
+ case NdbDictionary::Column::Varchar:
+ case NdbDictionary::Column::Text:
+ case NdbDictionary::Column::Char:
+ case NdbDictionary::Column::Longvarchar:
+ case NdbDictionary::Column::Longvarbinary:
+ {
+ char* ptr= (char*)&val;
+ while(len >= 4)
+ {
+ len -= 4;
+ buf[pos++] = base64_table[ptr[0] & 0x3f];
+ buf[pos++] = base64_table[ptr[1] & 0x3f];
+ buf[pos++] = base64_table[ptr[2] & 0x3f];
+ buf[pos++] = base64_table[ptr[3] & 0x3f];
+ val= myRand(&seed);
+ }
+
+ for(; len; len--, pos++)
+ buf[pos] = base64_table[ptr[len] & 0x3f];
+
+ pos--;
+ break;
+ }
+ case NdbDictionary::Column::Blob:
+ case NdbDictionary::Column::Undefined:
+ abort();
+ break;
+ }
+
+
+ return buf;
+}
+
+int
+HugoCalculator::verifyRowValues(NDBT_ResultRow* const pRow) const{
+ int id, updates;
+
+ id = pRow->attributeStore(m_idCol)->u_32_value();
+ updates = pRow->attributeStore(m_updatesCol)->u_32_value();
+ int result = 0;
+
+ // Check the values of each column
+ for (int i = 0; i<m_tab.getNoOfColumns(); i++){
+ if (i != m_updatesCol && id != m_idCol) {
+ const NdbDictionary::Column* attr = m_tab.getColumn(i);
+ Uint32 len = attr->getSizeInBytes();
+ char buf[8000];
+ const char* res = calcValue(id, i, updates, buf, len);
+ if (res == NULL){
+ if (!pRow->attributeStore(i)->isNULL()){
+ g_err << "|- NULL ERROR: expected a NULL but the column was not null" << endl;
+ g_err << "|- The row: \"" << (*pRow) << "\"" << endl;
+ result = -1;
+ }
+ } else{
+ if (memcmp(res, pRow->attributeStore(i)->aRef(), len) != 0){
+ g_err << "Column: " << attr->getName() << endl;
+ const char* buf2 = pRow->attributeStore(i)->aRef();
+ for (Uint32 j = 0; j < len; j++)
+ {
+ g_err << j << ":" << hex << (Uint32)(Uint8)buf[j] << "[" << hex << (Uint32)(Uint8)buf2[j] << "]";
+ if (buf[j] != buf2[j])
+ {
+ g_err << "==>Match failed!";
+ }
+ g_err << endl;
+ }
+ g_err << endl;
+ g_err << "|- Invalid data found in attribute " << i << ": \""
+ << pRow->attributeStore(i)->aRef()
+ << "\" != \"" << res << "\"" << endl
+ << "Length of expected=" << (unsigned)strlen(res) << endl
+ << "Lenght of read="
+ << (unsigned)strlen(pRow->attributeStore(i)->aRef()) << endl;
+ g_err << "|- The row: \"" << (* pRow) << "\"" << endl;
+ result = -1;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+int
+HugoCalculator::getIdValue(NDBT_ResultRow* const pRow) const {
+ return pRow->attributeStore(m_idCol)->u_32_value();
+}
+
+int
+HugoCalculator::getUpdatesValue(NDBT_ResultRow* const pRow) const {
+ return pRow->attributeStore(m_updatesCol)->u_32_value();
+}
+
diff --git a/storage/ndb/test/src/HugoOperations.cpp b/storage/ndb/test/src/HugoOperations.cpp
new file mode 100644
index 00000000000..3dcbe3d5ffd
--- /dev/null
+++ b/storage/ndb/test/src/HugoOperations.cpp
@@ -0,0 +1,634 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <HugoOperations.hpp>
+
+
+int HugoOperations::startTransaction(Ndb* pNdb){
+
+ if (pTrans != NULL){
+ ndbout << "HugoOperations::startTransaction, pTrans != NULL" << endl;
+ return NDBT_FAILED;
+ }
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+ ERR(err);
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+int HugoOperations::setTransaction(NdbTransaction* new_trans){
+
+ if (pTrans != NULL){
+ ndbout << "HugoOperations::startTransaction, pTrans != NULL" << endl;
+ return NDBT_FAILED;
+ }
+ pTrans = new_trans;
+ if (pTrans == NULL) {
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+void
+HugoOperations::setTransactionId(Uint64 id){
+ if (pTrans != NULL){
+ pTrans->setTransactionId(id);
+ }
+}
+
+int HugoOperations::closeTransaction(Ndb* pNdb){
+
+ if (pTrans != NULL){
+ pNdb->closeTransaction(pTrans);
+ pTrans = NULL;
+ }
+ pTrans = NULL;
+
+ m_result_sets.clear();
+ m_executed_result_sets.clear();
+
+ return NDBT_OK;
+}
+
+NdbConnection* HugoOperations::getTransaction(){
+ return pTrans;
+}
+
+int HugoOperations::pkReadRecord(Ndb* pNdb,
+ int recordNo,
+ int numRecords,
+ NdbOperation::LockMode lm){
+ int a;
+ allocRows(numRecords);
+ int check;
+
+ NdbOperation* pOp = 0;
+ pIndexScanOp = 0;
+
+ for(int r=0; r < numRecords; r++){
+
+ if(pOp == 0)
+ {
+ pOp = getOperation(pTrans, NdbOperation::ReadRequest);
+ }
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+rand_lock_mode:
+ switch(lm){
+ case NdbOperation::LM_Read:
+ case NdbOperation::LM_Exclusive:
+ case NdbOperation::LM_CommittedRead:
+ if(idx && idx->getType() == NdbDictionary::Index::OrderedIndex &&
+ pIndexScanOp == 0)
+ {
+ pIndexScanOp = ((NdbIndexScanOperation*)pOp);
+ check = pIndexScanOp->readTuples(lm);
+ }
+ else
+ check = pOp->readTuple(lm);
+ break;
+ default:
+ lm = (NdbOperation::LockMode)((rand() >> 16) & 3);
+ goto rand_lock_mode;
+ }
+
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Define primary keys
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == true){
+ if(equalForAttr(pOp, a, r+recordNo) != 0){
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ if(pIndexScanOp)
+ pIndexScanOp->end_of_bound(r);
+
+ if(r == 0 || pIndexScanOp == 0)
+ {
+ // Define attributes to read
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if((rows[r]->attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+ }
+ pOp = pIndexScanOp;
+ }
+ return NDBT_OK;
+}
+
+int HugoOperations::pkUpdateRecord(Ndb* pNdb,
+ int recordNo,
+ int numRecords,
+ int updatesValue){
+ int a;
+ allocRows(numRecords);
+ int check;
+ for(int r=0; r < numRecords; r++){
+ NdbOperation* pOp = getOperation(pTrans, NdbOperation::UpdateRequest);
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->updateTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ if(setValues(pOp, r+recordNo, updatesValue) != NDBT_OK)
+ {
+ return NDBT_FAILED;
+ }
+ }
+ return NDBT_OK;
+}
+
+int
+HugoOperations::setValues(NdbOperation* pOp, int rowId, int updateId)
+{
+ // Define primary keys
+ int a;
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == true){
+ if(equalForAttr(pOp, a, rowId) != 0){
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == false){
+ if(setValueForAttr(pOp, a, rowId, updateId ) != 0){
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ return NDBT_OK;
+}
+
+int HugoOperations::pkInsertRecord(Ndb* pNdb,
+ int recordNo,
+ int numRecords,
+ int updatesValue){
+
+ int a, check;
+ for(int r=0; r < numRecords; r++){
+ NdbOperation* pOp = getOperation(pTrans, NdbOperation::InsertRequest);
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->insertTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ if(setValues(pOp, r+recordNo, updatesValue) != NDBT_OK)
+ {
+ return NDBT_FAILED;
+ }
+ }
+ return NDBT_OK;
+}
+
+int HugoOperations::pkDeleteRecord(Ndb* pNdb,
+ int recordNo,
+ int numRecords){
+
+ int a, check;
+ for(int r=0; r < numRecords; r++){
+ NdbOperation* pOp = getOperation(pTrans, NdbOperation::DeleteRequest);
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->deleteTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Define primary keys
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == true){
+ if(equalForAttr(pOp, a, r+recordNo) != 0){
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+ }
+ }
+ return NDBT_OK;
+}
+
+int HugoOperations::execute_Commit(Ndb* pNdb,
+ AbortOption eao){
+
+ int check = 0;
+ check = pTrans->execute(Commit, eao);
+
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ ERR(err);
+ NdbOperation* pOp = pTrans->getNdbErrorOperation();
+ if (pOp != NULL){
+ const NdbError err2 = pOp->getNdbError();
+ ERR(err2);
+ }
+ if (err.code == 0)
+ return NDBT_FAILED;
+ return err.code;
+ }
+
+ for(int i = 0; i<m_result_sets.size(); i++){
+ m_executed_result_sets.push_back(m_result_sets[i]);
+
+ int rows = m_result_sets[i].records;
+ NdbScanOperation* rs = m_result_sets[i].m_result_set;
+ int res = rs->nextResult();
+ switch(res){
+ case 1:
+ return 626;
+ case -1:
+ const NdbError err = pTrans->getNdbError();
+ ERR(err);
+ return (err.code > 0 ? err.code : NDBT_FAILED);
+ }
+
+ // A row found
+
+ switch(rows){
+ case 0:
+ return 4000;
+ default:
+ m_result_sets[i].records--;
+ break;
+ }
+ }
+
+ m_result_sets.clear();
+
+ return NDBT_OK;
+}
+
+int HugoOperations::execute_NoCommit(Ndb* pNdb, AbortOption eao){
+
+ int check;
+ check = pTrans->execute(NoCommit, eao);
+
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ ERR(err);
+ NdbOperation* pOp;
+ while ((pOp = pTrans->getNdbErrorOperation()) != NULL){
+ const NdbError err2 = pOp->getNdbError();
+ ERR(err2);
+ }
+ if (err.code == 0)
+ return NDBT_FAILED;
+ return err.code;
+ }
+
+ for(int i = 0; i<m_result_sets.size(); i++){
+ m_executed_result_sets.push_back(m_result_sets[i]);
+
+ int rows = m_result_sets[i].records;
+ NdbScanOperation* rs = m_result_sets[i].m_result_set;
+ int res = rs->nextResult();
+ switch(res){
+ case 1:
+ return 626;
+ case -1:
+ const NdbError err = pTrans->getNdbError();
+ ERR(err);
+ return (err.code > 0 ? err.code : NDBT_FAILED);
+ }
+
+ // A row found
+
+ switch(rows){
+ case 0:
+ return 4000;
+ default:
+ case 1:
+ break;
+ }
+ }
+
+ m_result_sets.clear();
+
+ return NDBT_OK;
+}
+
+int HugoOperations::execute_Rollback(Ndb* pNdb){
+ int check;
+ check = pTrans->execute(Rollback);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ ERR(err);
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+HugoOperations::HugoOperations(const NdbDictionary::Table& _tab,
+ const NdbDictionary::Index* idx):
+ UtilTransactions(_tab, idx),
+ calc(_tab)
+{
+}
+
+HugoOperations::~HugoOperations(){
+ deallocRows();
+ if (pTrans != NULL){
+ pTrans->close();
+ pTrans = NULL;
+ }
+}
+
+
+int HugoOperations::equalForAttr(NdbOperation* pOp,
+ int attrId,
+ int rowId){
+ int check = -1;
+ const NdbDictionary::Column* attr = tab.getColumn(attrId);
+ if (attr->getPrimaryKey() == false){
+ g_info << "Can't call equalForAttr on non PK attribute" << endl;
+ return NDBT_FAILED;
+ }
+
+ int len = attr->getSizeInBytes();
+ char buf[8000];
+ memset(buf, 0, sizeof(buf));
+ return pOp->equal( attr->getName(),
+ calc.calcValue(rowId, attrId, 0, buf, len));
+}
+
+int HugoOperations::setValueForAttr(NdbOperation* pOp,
+ int attrId,
+ int rowId,
+ int updateId){
+ int check = -1;
+ const NdbDictionary::Column* attr = tab.getColumn(attrId);
+
+ int len = attr->getSizeInBytes();
+ char buf[8000];
+ memset(buf, 0, sizeof(buf));
+ return pOp->setValue( attr->getName(),
+ calc.calcValue(rowId, attrId, updateId, buf, len));
+}
+
+int
+HugoOperations::verifyUpdatesValue(int updatesValue, int _numRows){
+ _numRows = (_numRows == 0 ? rows.size() : _numRows);
+
+ int result = NDBT_OK;
+
+ for(int i = 0; i<_numRows; i++){
+ if(calc.verifyRowValues(rows[i]) != NDBT_OK){
+ g_err << "Inconsistent row"
+ << endl << "\t" << rows[i]->c_str().c_str() << endl;
+ result = NDBT_FAILED;
+ continue;
+ }
+
+ if(calc.getUpdatesValue(rows[i]) != updatesValue){
+ result = NDBT_FAILED;
+ g_err << "Invalid updates value for row " << i << endl
+ << " updatesValue: " << updatesValue << endl
+ << " calc.getUpdatesValue: " << calc.getUpdatesValue(rows[i]) << endl
+ << rows[i]->c_str().c_str() << endl;
+ continue;
+ }
+ }
+
+ if(_numRows == 0){
+ g_err << "No rows -> Invalid updates value" << endl;
+ return NDBT_FAILED;
+ }
+
+ return result;
+}
+
+void HugoOperations::allocRows(int _numRows){
+ if(_numRows <= 0){
+ g_info << "Illegal value for num rows : " << _numRows << endl;
+ abort();
+ }
+
+ for(int b=rows.size(); b<_numRows; b++){
+ rows.push_back(new NDBT_ResultRow(tab));
+ }
+}
+
+void HugoOperations::deallocRows(){
+ while(rows.size() > 0){
+ delete rows.back();
+ rows.erase(rows.size() - 1);
+ }
+}
+
+int HugoOperations::saveCopyOfRecord(int numRecords ){
+
+ if (numRecords > (int)rows.size())
+ return NDBT_FAILED;
+
+ for (int i = 0; i < numRecords; i++){
+ savedRecords.push_back(rows[i]->c_str());
+ }
+ return NDBT_OK;
+}
+
+BaseString HugoOperations::getRecordStr(int recordNum){
+ if (recordNum > (int)rows.size())
+ return NULL;
+ return rows[recordNum]->c_str();
+}
+
+int HugoOperations::getRecordGci(int recordNum){
+ return pTrans->getGCI();
+}
+
+
+int HugoOperations::compareRecordToCopy(int numRecords ){
+ if (numRecords > (int)rows.size())
+ return NDBT_FAILED;
+ if ((unsigned)numRecords > savedRecords.size())
+ return NDBT_FAILED;
+
+ int result = NDBT_OK;
+ for (int i = 0; i < numRecords; i++){
+ BaseString str = rows[i]->c_str();
+ ndbout << "row["<<i<<"]: " << str << endl;
+ ndbout << "sav["<<i<<"]: " << savedRecords[i] << endl;
+ if (savedRecords[i] == str){
+ ;
+ } else {
+ result = NDBT_FAILED;
+ }
+ }
+ return result;
+}
+
+void
+HugoOperations::refresh() {
+ NdbConnection* t = getTransaction();
+ if(t)
+ t->refresh();
+}
+
+int HugoOperations::indexReadRecords(Ndb*, const char * idxName, int recordNo,
+ bool exclusive,
+ int numRecords){
+
+ int a;
+ allocRows(numRecords);
+ int check;
+ for(int r=0; r < numRecords; r++){
+ NdbOperation* pOp = pTrans->getNdbIndexOperation(idxName, tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ if (exclusive == true)
+ check = pOp->readTupleExclusive();
+ else
+ check = pOp->readTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Define primary keys
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == true){
+ if(equalForAttr(pOp, a, r+recordNo) != 0){
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ // Define attributes to read
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if((rows[r]->attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+ }
+ return NDBT_OK;
+}
+
+int
+HugoOperations::indexUpdateRecord(Ndb*,
+ const char * idxName,
+ int recordNo,
+ int numRecords,
+ int updatesValue){
+ int a;
+ allocRows(numRecords);
+ int check;
+ for(int r=0; r < numRecords; r++){
+ NdbOperation* pOp = pTrans->getNdbIndexOperation(idxName, tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pOp->updateTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Define primary keys
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == true){
+ if(equalForAttr(pOp, a, r+recordNo) != 0){
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ // Define attributes to update
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == false){
+ if(setValueForAttr(pOp, a, recordNo+r, updatesValue ) != 0){
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+ }
+ }
+ return NDBT_OK;
+}
+
+int
+HugoOperations::scanReadRecords(Ndb* pNdb, NdbScanOperation::LockMode lm,
+ int records){
+
+ allocRows(records);
+ NdbScanOperation * pOp = pTrans->getNdbScanOperation(tab.getName());
+
+ if(!pOp)
+ return -1;
+
+ if(pOp->readTuples(lm, 1, 1)){
+ return -1;
+ }
+
+ for(int a = 0; a<tab.getNoOfColumns(); a++){
+ if((rows[0]->attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+
+ RsPair p = {pOp, records};
+ m_result_sets.push_back(p);
+
+ return 0;
+}
+
+template class Vector<HugoOperations::RsPair>;
diff --git a/storage/ndb/test/src/HugoTransactions.cpp b/storage/ndb/test/src/HugoTransactions.cpp
new file mode 100644
index 00000000000..bfe7ea72394
--- /dev/null
+++ b/storage/ndb/test/src/HugoTransactions.cpp
@@ -0,0 +1,2050 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "HugoTransactions.hpp"
+#include <NdbSleep.h>
+
+
+HugoTransactions::HugoTransactions(const NdbDictionary::Table& _tab,
+ const NdbDictionary::Index* idx):
+ HugoOperations(_tab, idx),
+ row(_tab){
+
+ m_defaultScanUpdateMethod = 3;
+}
+
+HugoTransactions::~HugoTransactions(){
+ deallocRows();
+}
+
+int
+HugoTransactions::scanReadRecords(Ndb* pNdb,
+ int records,
+ int abortPercent,
+ int parallelism,
+ NdbOperation::LockMode lm)
+{
+
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check, a;
+ NdbScanOperation *pOp;
+
+ while (true){
+
+ if (retryAttempt >= retryMax){
+ g_err << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ pOp = getScanOperation(pTrans);
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ if( pOp ->readTuples(lm, 0, parallelism) ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if((row.attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ // Abort after 1-100 or 1-records rows
+ int ranVal = rand();
+ int abortCount = ranVal % (records == 0 ? 100 : records);
+ bool abortTrans = false;
+ if (abort > 0){
+ // Abort if abortCount is less then abortPercent
+ if (abortCount < abortPercent)
+ abortTrans = true;
+ }
+
+ int eof;
+ int rows = 0;
+ while((eof = pOp->nextResult(true)) == 0){
+ rows++;
+ if (calc.verifyRowValues(&row) != 0){
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ if (abortCount == rows && abortTrans == true){
+ ndbout << "Scan is aborted" << endl;
+ g_info << "Scan is aborted" << endl;
+ pOp->close();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ closeTransaction(pNdb);
+ return NDBT_OK;
+ }
+ }
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR_INFO(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ switch (err.code){
+ case 488:
+ case 245:
+ case 490:
+ // Too many active scans, no limit on number of retry attempts
+ break;
+ default:
+ retryAttempt++;
+ }
+ continue;
+ }
+ ERR(err);
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ closeTransaction(pNdb);
+
+ g_info << rows << " rows have been read" << endl;
+ if (records != 0 && rows != records){
+ g_err << "Check expected number of records failed" << endl
+ << " expected=" << records <<", " << endl
+ << " read=" << rows << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+}
+
+int
+HugoTransactions::scanReadRecords(Ndb* pNdb,
+ const NdbDictionary::Index * pIdx,
+ int records,
+ int abortPercent,
+ int parallelism,
+ NdbOperation::LockMode lm,
+ bool sorted)
+{
+
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check, a;
+ NdbIndexScanOperation *pOp;
+
+ while (true){
+
+ if (retryAttempt >= retryMax){
+ g_err << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ pOp = pTrans->getNdbIndexScanOperation(pIdx->getName(), tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ if( pOp ->readTuples(lm, 0, parallelism, sorted) ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if((row.attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ // Abort after 1-100 or 1-records rows
+ int ranVal = rand();
+ int abortCount = ranVal % (records == 0 ? 100 : records);
+ bool abortTrans = false;
+ if (abort > 0){
+ // Abort if abortCount is less then abortPercent
+ if (abortCount < abortPercent)
+ abortTrans = true;
+ }
+
+ int eof;
+ int rows = 0;
+ while((eof = pOp->nextResult(true)) == 0){
+ rows++;
+ if (calc.verifyRowValues(&row) != 0){
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ if (abortCount == rows && abortTrans == true){
+ ndbout << "Scan is aborted" << endl;
+ g_info << "Scan is aborted" << endl;
+ pOp->close();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ closeTransaction(pNdb);
+ return NDBT_OK;
+ }
+ }
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR_INFO(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ switch (err.code){
+ case 488:
+ case 245:
+ case 490:
+ // Too many active scans, no limit on number of retry attempts
+ break;
+ default:
+ retryAttempt++;
+ }
+ continue;
+ }
+ ERR(err);
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ closeTransaction(pNdb);
+
+ g_info << rows << " rows have been read" << endl;
+ if (records != 0 && rows != records){
+ g_err << "Check expected number of records failed" << endl
+ << " expected=" << records <<", " << endl
+ << " read=" << rows << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+}
+
+
+#define RESTART_SCAN 99
+
+int
+HugoTransactions::scanUpdateRecords(Ndb* pNdb,
+ int records,
+ int abortPercent,
+ int parallelism){
+ if(m_defaultScanUpdateMethod == 1){
+ return scanUpdateRecords1(pNdb, records, abortPercent, parallelism);
+ } else if(m_defaultScanUpdateMethod == 2){
+ return scanUpdateRecords2(pNdb, records, abortPercent, parallelism);
+ } else {
+ return scanUpdateRecords3(pNdb, records, abortPercent, parallelism);
+ }
+}
+
+// Scan all records exclusive and update
+// them one by one
+int
+HugoTransactions::scanUpdateRecords1(Ndb* pNdb,
+ int records,
+ int abortPercent,
+ int parallelism){
+ return scanUpdateRecords3(pNdb, records, abortPercent, 1);
+}
+
+// Scan all records exclusive and update
+// them batched by asking nextScanResult to
+// give us all cached records before fetching new
+// records from db
+int
+HugoTransactions::scanUpdateRecords2(Ndb* pNdb,
+ int records,
+ int abortPercent,
+ int parallelism){
+ return scanUpdateRecords3(pNdb, records, abortPercent, parallelism);
+}
+
+int
+HugoTransactions::scanUpdateRecords3(Ndb* pNdb,
+ int records,
+ int abortPercent,
+ int parallelism){
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check, a;
+ NdbScanOperation *pOp;
+
+
+ while (true){
+restart:
+ if (retryAttempt++ >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+ ERR(err);
+ if (err.status == NdbError::TemporaryError){
+ NdbSleep_MilliSleep(50);
+ continue;
+ }
+ return NDBT_FAILED;
+ }
+
+ pOp = getScanOperation(pTrans);
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuplesExclusive(parallelism) ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ // Read all attributes from this table
+ for(a=0; a<tab.getNoOfColumns(); a++){
+ if((row.attributeStore(a) = pOp->getValue(tab.getColumn(a)->getName())) == NULL){
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ ERR(err);
+ closeTransaction(pNdb);
+ if (err.status == NdbError::TemporaryError){
+ NdbSleep_MilliSleep(50);
+ continue;
+ }
+ return NDBT_FAILED;
+ }
+
+ // Abort after 1-100 or 1-records rows
+ int ranVal = rand();
+ int abortCount = ranVal % (records == 0 ? 100 : records);
+ bool abortTrans = false;
+ if (abort > 0){
+ // Abort if abortCount is less then abortPercent
+ if (abortCount < abortPercent)
+ abortTrans = true;
+ }
+
+ int rows = 0;
+ while((check = pOp->nextResult(true)) == 0){
+ do {
+ rows++;
+ NdbOperation* pUp = pOp->updateCurrentTuple();
+ if(pUp == 0){
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ const int updates = calc.getUpdatesValue(&row) + 1;
+ const int r = calc.getIdValue(&row);
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == false){
+ if(setValueForAttr(pUp, a, r, updates ) != 0){
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ if (rows == abortCount && abortTrans == true){
+ g_info << "Scan is aborted" << endl;
+ // This scan should be aborted
+ closeTransaction(pNdb);
+ return NDBT_OK;
+ }
+ } while((check = pOp->nextResult(false)) == 0);
+
+ if(check != -1){
+ check = pTrans->execute(Commit);
+ pTrans->restart();
+ }
+
+ const NdbError err = pTrans->getNdbError();
+ if( check == -1 ) {
+ closeTransaction(pNdb);
+ ERR(err);
+ if (err.status == NdbError::TemporaryError){
+ NdbSleep_MilliSleep(50);
+ goto restart;
+ }
+ return NDBT_FAILED;
+ }
+ }
+
+ const NdbError err = pTrans->getNdbError();
+ if( check == -1 ) {
+ closeTransaction(pNdb);
+ ERR(err);
+ if (err.status == NdbError::TemporaryError){
+ NdbSleep_MilliSleep(50);
+ goto restart;
+ }
+ return NDBT_FAILED;
+ }
+
+ closeTransaction(pNdb);
+
+ g_info << rows << " rows have been updated" << endl;
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+}
+
+int
+HugoTransactions::loadTable(Ndb* pNdb,
+ int records,
+ int batch,
+ bool allowConstraintViolation,
+ int doSleep,
+ bool oneTrans){
+ int check, a;
+ int retryAttempt = 0;
+ int retryMax = 5;
+ NdbOperation *pOp;
+ bool first_batch = true;
+
+ const int org = batch;
+ const int cols = tab.getNoOfColumns();
+ const int brow = tab.getRowSizeInBytes();
+ const int bytes = 12 + brow + 4 * cols;
+ batch = (batch * 256); // -> 512 -> 65536k per commit
+ batch = batch/bytes; //
+ batch = batch == 0 ? 1 : batch;
+
+ if(batch != org){
+ g_info << "batch = " << org << " rowsize = " << bytes
+ << " -> rows/commit = " << batch << endl;
+ }
+
+ g_info << "|- Inserting records..." << endl;
+ for (int c=0 ; c<records ; ){
+ bool closeTrans = true;
+
+ if(c + batch > records)
+ batch = records - c;
+
+ if (retryAttempt >= retryMax){
+ g_info << "Record " << c << " could not be inserted, has retried "
+ << retryAttempt << " times " << endl;
+ // Reset retry counters and continue with next record
+ retryAttempt = 0;
+ c++;
+ }
+ if (doSleep > 0)
+ NdbSleep_MilliSleep(doSleep);
+
+ // if (first_batch || !oneTrans) {
+ if (first_batch || !pTrans) {
+ first_batch = false;
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+ }
+
+ if(pkInsertRecord(pNdb, c, batch) != NDBT_OK)
+ {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ // Execute the transaction and insert the record
+ if (!oneTrans || (c + batch) >= records) {
+ // closeTrans = true;
+ closeTrans = false;
+ check = pTrans->execute( Commit );
+ pTrans->restart();
+ } else {
+ closeTrans = false;
+ check = pTrans->execute( NoCommit );
+ }
+ if(check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ closeTransaction(pNdb);
+ pTrans= 0;
+ switch(err.status){
+ case NdbError::Success:
+ ERR(err);
+ g_info << "ERROR: NdbError reports success when transcaction failed"
+ << endl;
+ return NDBT_FAILED;
+ break;
+
+ case NdbError::TemporaryError:
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ break;
+
+ case NdbError::UnknownResult:
+ ERR(err);
+ return NDBT_FAILED;
+ break;
+
+ case NdbError::PermanentError:
+ if (allowConstraintViolation == true){
+ switch (err.classification){
+ case NdbError::ConstraintViolation:
+ // Tuple already existed, OK but should be reported
+ g_info << c << ": " << err.code << " " << err.message << endl;
+ c++;
+ continue;
+ break;
+ default:
+ break;
+ }
+ }
+ ERR(err);
+ return err.code;
+ break;
+ }
+ }
+ else{
+ if (closeTrans) {
+ closeTransaction(pNdb);
+ pTrans= 0;
+ }
+ }
+
+ // Step to next record
+ c = c+batch;
+ retryAttempt = 0;
+ }
+
+ if(pTrans)
+ closeTransaction(pNdb);
+ return NDBT_OK;
+}
+
+int
+HugoTransactions::fillTable(Ndb* pNdb,
+ int batch){
+ int check, a, b;
+ int retryAttempt = 0;
+ int retryMax = 5;
+ NdbOperation *pOp;
+
+ const int org = batch;
+ const int cols = tab.getNoOfColumns();
+ const int brow = tab.getRowSizeInBytes();
+ const int bytes = 12 + brow + 4 * cols;
+ batch = (batch * 256); // -> 512 -> 65536k per commit
+ batch = batch/bytes; //
+ batch = batch == 0 ? 1 : batch;
+
+ if(batch != org){
+ g_info << "batch = " << org << " rowsize = " << bytes
+ << " -> rows/commit = " << batch << endl;
+ }
+
+ for (int c=0 ; ; ){
+
+ if (retryAttempt >= retryMax){
+ g_info << "Record " << c << " could not be inserted, has retried "
+ << retryAttempt << " times " << endl;
+ // Reset retry counters and continue with next record
+ retryAttempt = 0;
+ c++;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ if(pkInsertRecord(pNdb, c, batch) != NDBT_OK)
+ {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ // Execute the transaction and insert the record
+ check = pTrans->execute( Commit, CommitAsMuchAsPossible );
+ if(check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ closeTransaction(pNdb);
+
+ switch(err.status){
+ case NdbError::Success:
+ ERR(err);
+ g_info << "ERROR: NdbError reports success when transcaction failed"
+ << endl;
+ return NDBT_FAILED;
+ break;
+
+ case NdbError::TemporaryError:
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ break;
+
+ case NdbError::UnknownResult:
+ ERR(err);
+ return NDBT_FAILED;
+ break;
+
+ case NdbError::PermanentError:
+ // if (allowConstraintViolation == true){
+ // switch (err.classification){
+ // case NdbError::ConstraintViolation:
+ // // Tuple already existed, OK but should be reported
+ // g_info << c << ": " << err.code << " " << err.message << endl;
+ // c++;
+ // continue;
+ // break;
+ // default:
+ // break;es
+ // }
+ // }
+
+ // Check if this is the "db full" error
+ if (err.classification==NdbError::InsufficientSpace){
+ ERR(err);
+ return NDBT_OK;
+ }
+
+ if (err.classification == NdbError::ConstraintViolation){
+ ERR(err);
+ break;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ break;
+ }
+ }
+ else{
+ closeTransaction(pNdb);
+ }
+
+ // Step to next record
+ c = c+batch;
+ retryAttempt = 0;
+ }
+ return NDBT_OK;
+}
+
+int
+HugoTransactions::createEvent(Ndb* pNdb){
+
+ char eventName[1024];
+ sprintf(eventName,"%s_EVENT",tab.getName());
+
+ NdbDictionary::Dictionary *myDict = pNdb->getDictionary();
+
+ if (!myDict) {
+ g_err << "Dictionary not found "
+ << pNdb->getNdbError().code << " "
+ << pNdb->getNdbError().message << endl;
+ return NDBT_FAILED;
+ }
+
+ NdbDictionary::Event myEvent(eventName);
+ myEvent.setTable(tab.getName());
+ myEvent.addTableEvent(NdbDictionary::Event::TE_ALL);
+ // myEvent.addTableEvent(NdbDictionary::Event::TE_INSERT);
+ // myEvent.addTableEvent(NdbDictionary::Event::TE_UPDATE);
+ // myEvent.addTableEvent(NdbDictionary::Event::TE_DELETE);
+
+ // const NdbDictionary::Table *_table = myDict->getTable(tab.getName());
+ for(int a = 0; a < tab.getNoOfColumns(); a++){
+ // myEvent.addEventColumn(_table->getColumn(a)->getName());
+ myEvent.addEventColumn(a);
+ }
+
+ int res = myDict->createEvent(myEvent); // Add event to database
+
+ if (res == 0)
+ myEvent.print();
+ else if (myDict->getNdbError().classification ==
+ NdbError::SchemaObjectExists)
+ {
+ g_info << "Event creation failed event exists\n";
+ res = myDict->dropEvent(eventName);
+ if (res) {
+ g_err << "Failed to drop event: "
+ << myDict->getNdbError().code << " : "
+ << myDict->getNdbError().message << endl;
+ return NDBT_FAILED;
+ }
+ // try again
+ res = myDict->createEvent(myEvent); // Add event to database
+ if (res) {
+ g_err << "Failed to create event (1): "
+ << myDict->getNdbError().code << " : "
+ << myDict->getNdbError().message << endl;
+ return NDBT_FAILED;
+ }
+ }
+ else
+ {
+ g_err << "Failed to create event (2): "
+ << myDict->getNdbError().code << " : "
+ << myDict->getNdbError().message << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+}
+
+#include <NdbEventOperation.hpp>
+#include "TestNdbEventOperation.hpp"
+#include <NdbAutoPtr.hpp>
+
+struct receivedEvent {
+ Uint32 pk;
+ Uint32 count;
+ Uint32 event;
+};
+
+int XXXXX = 0;
+
+int
+HugoTransactions::eventOperation(Ndb* pNdb, void* pstats,
+ int records) {
+ int myXXXXX = XXXXX++;
+ Uint32 i;
+ const char function[] = "HugoTransactions::eventOperation: ";
+ struct receivedEvent* recInsertEvent;
+ NdbAutoObjArrayPtr<struct receivedEvent>
+ p00( recInsertEvent = new struct receivedEvent[3*records] );
+ struct receivedEvent* recUpdateEvent = &recInsertEvent[records];
+ struct receivedEvent* recDeleteEvent = &recInsertEvent[2*records];
+
+ EventOperationStats &stats = *(EventOperationStats*)pstats;
+
+ stats.n_inserts = 0;
+ stats.n_deletes = 0;
+ stats.n_updates = 0;
+ stats.n_consecutive = 0;
+ stats.n_duplicates = 0;
+ stats.n_inconsistent_gcis = 0;
+
+ for (i = 0; i < records; i++) {
+ recInsertEvent[i].pk = 0xFFFFFFFF;
+ recInsertEvent[i].count = 0;
+ recInsertEvent[i].event = 0xFFFFFFFF;
+
+ recUpdateEvent[i].pk = 0xFFFFFFFF;
+ recUpdateEvent[i].count = 0;
+ recUpdateEvent[i].event = 0xFFFFFFFF;
+
+ recDeleteEvent[i].pk = 0xFFFFFFFF;
+ recDeleteEvent[i].count = 0;
+ recDeleteEvent[i].event = 0xFFFFFFFF;
+ }
+
+ NdbDictionary::Dictionary *myDict = pNdb->getDictionary();
+
+ if (!myDict) {
+ g_err << function << "Event Creation failedDictionary not found\n";
+ return NDBT_FAILED;
+ }
+
+ int r = 0;
+ NdbEventOperation *pOp;
+
+ char eventName[1024];
+ sprintf(eventName,"%s_EVENT",tab.getName());
+ int noEventColumnName = tab.getNoOfColumns();
+
+ g_info << function << "create EventOperation\n";
+ pOp = pNdb->createEventOperation(eventName, 100);
+ if ( pOp == NULL ) {
+ g_err << function << "Event operation creation failed\n";
+ return NDBT_FAILED;
+ }
+
+ g_info << function << "get values\n";
+ NdbRecAttr* recAttr[1024];
+ NdbRecAttr* recAttrPre[1024];
+
+ const NdbDictionary::Table *_table = myDict->getTable(tab.getName());
+
+ for (int a = 0; a < noEventColumnName; a++) {
+ recAttr[a] = pOp->getValue(_table->getColumn(a)->getName());
+ recAttrPre[a] = pOp->getPreValue(_table->getColumn(a)->getName());
+ }
+
+ // set up the callbacks
+ g_info << function << "execute\n";
+ if (pOp->execute()) { // This starts changes to "start flowing"
+ g_err << function << "operation execution failed: \n";
+ g_err << pOp->getNdbError().code << " "
+ << pOp->getNdbError().message << endl;
+ return NDBT_FAILED;
+ }
+
+ g_info << function << "ok\n";
+
+ int count = 0;
+ Uint32 last_inconsitant_gci = 0xEFFFFFF0;
+
+ while (r < records){
+ //printf("now waiting for event...\n");
+ int res = pNdb->pollEvents(1000); // wait for event or 1000 ms
+
+ if (res > 0) {
+ //printf("got data! %d\n", r);
+ int overrun;
+ while (pOp->next(&overrun) > 0) {
+ r++;
+ r += overrun;
+ count++;
+
+ Uint32 gci = pOp->getGCI();
+ Uint32 pk = recAttr[0]->u_32_value();
+
+ if (!pOp->isConsistent()) {
+ if (last_inconsitant_gci != gci) {
+ last_inconsitant_gci = gci;
+ stats.n_inconsistent_gcis++;
+ }
+ g_warning << "A node failure has occured and events might be missing\n";
+ }
+ g_info << function << "GCI " << gci << ": " << count;
+ struct receivedEvent* recEvent;
+ switch (pOp->getEventType()) {
+ case NdbDictionary::Event::TE_INSERT:
+ stats.n_inserts++;
+ g_info << " INSERT: ";
+ recEvent = recInsertEvent;
+ break;
+ case NdbDictionary::Event::TE_DELETE:
+ stats.n_deletes++;
+ g_info << " DELETE: ";
+ recEvent = recDeleteEvent;
+ break;
+ case NdbDictionary::Event::TE_UPDATE:
+ stats.n_updates++;
+ g_info << " UPDATE: ";
+ recEvent = recUpdateEvent;
+ break;
+ case NdbDictionary::Event::TE_ALL:
+ abort();
+ }
+
+ if ((int)pk < records) {
+ recEvent[pk].pk = pk;
+ recEvent[pk].count++;
+ }
+
+ g_info << "overrun " << overrun << " pk " << pk;
+ for (i = 1; i < noEventColumnName; i++) {
+ if (recAttr[i]->isNULL() >= 0) { // we have a value
+ g_info << " post[" << i << "]=";
+ if (recAttr[i]->isNULL() == 0) // we have a non-null value
+ g_info << recAttr[i]->u_32_value();
+ else // we have a null value
+ g_info << "NULL";
+ }
+ if (recAttrPre[i]->isNULL() >= 0) { // we have a value
+ g_info << " pre[" << i << "]=";
+ if (recAttrPre[i]->isNULL() == 0) // we have a non-null value
+ g_info << recAttrPre[i]->u_32_value();
+ else // we have a null value
+ g_info << "NULL";
+ }
+ }
+ g_info << endl;
+ }
+ } else
+ ;//printf("timed out\n");
+ }
+
+ // sleep ((XXXXX-myXXXXX)*2);
+
+ g_info << myXXXXX << "dropping event operation" << endl;
+
+ int res = pNdb->dropEventOperation(pOp);
+ if (res != 0) {
+ g_err << "operation execution failed\n";
+ return NDBT_FAILED;
+ }
+
+ g_info << myXXXXX << " ok" << endl;
+
+ if (stats.n_inserts > 0) {
+ stats.n_consecutive++;
+ }
+ if (stats.n_deletes > 0) {
+ stats.n_consecutive++;
+ }
+ if (stats.n_updates > 0) {
+ stats.n_consecutive++;
+ }
+ for (i = 0; i < (Uint32)records/3; i++) {
+ if (recInsertEvent[i].pk != i) {
+ stats.n_consecutive ++;
+ ndbout << "missing insert pk " << i << endl;
+ } else if (recInsertEvent[i].count > 1) {
+ ndbout << "duplicates insert pk " << i
+ << " count " << recInsertEvent[i].count << endl;
+ stats.n_duplicates += recInsertEvent[i].count-1;
+ }
+ if (recUpdateEvent[i].pk != i) {
+ stats.n_consecutive ++;
+ ndbout << "missing update pk " << i << endl;
+ } else if (recUpdateEvent[i].count > 1) {
+ ndbout << "duplicates update pk " << i
+ << " count " << recUpdateEvent[i].count << endl;
+ stats.n_duplicates += recUpdateEvent[i].count-1;
+ }
+ if (recDeleteEvent[i].pk != i) {
+ stats.n_consecutive ++;
+ ndbout << "missing delete pk " << i << endl;
+ } else if (recDeleteEvent[i].count > 1) {
+ ndbout << "duplicates delete pk " << i
+ << " count " << recDeleteEvent[i].count << endl;
+ stats.n_duplicates += recDeleteEvent[i].count-1;
+ }
+ }
+
+ return NDBT_OK;
+}
+
+int
+HugoTransactions::pkReadRecords(Ndb* pNdb,
+ int records,
+ int batch,
+ NdbOperation::LockMode lm){
+ int reads = 0;
+ int r = 0;
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check, a;
+
+ if (batch == 0) {
+ g_info << "ERROR: Argument batch == 0 in pkReadRecords(). Not allowed." << endl;
+ return NDBT_FAILED;
+ }
+
+ while (r < records){
+ if(r + batch > records)
+ batch = records - r;
+
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ if(pkReadRecord(pNdb, r, batch, lm) != NDBT_OK)
+ {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ switch(err.code){
+ case 626: // Tuple did not exist
+ g_info << r << ": " << err.code << " " << err.message << endl;
+ r++;
+ break;
+
+ default:
+ ERR(err);
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ } else {
+ if(pIndexScanOp)
+ {
+ int rows_found = 0;
+ while((check = pIndexScanOp->nextResult()) == 0)
+ {
+ rows_found++;
+ if (calc.verifyRowValues(rows[0]) != 0){
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ if(check != 1 || rows_found > batch)
+ {
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ else if(rows_found < batch)
+ {
+ if(batch == 1){
+ g_info << r << ": not found" << endl; abort(); }
+ else
+ g_info << "Found " << rows_found << " of "
+ << batch << " rows" << endl;
+ }
+ r += batch;
+ reads += rows_found;
+ }
+ else
+ {
+ for (int b=0; (b<batch) && (r+b<records); b++){
+ if (calc.verifyRowValues(rows[b]) != 0){
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ reads++;
+ r++;
+ }
+ }
+ }
+
+ closeTransaction(pNdb);
+ }
+ deallocRows();
+ g_info << reads << " records read" << endl;
+ return NDBT_OK;
+}
+
+
+
+int
+HugoTransactions::pkUpdateRecords(Ndb* pNdb,
+ int records,
+ int batch,
+ int doSleep){
+ int updated = 0;
+ int r = 0;
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check, a, b;
+ NdbOperation *pOp;
+
+ allocRows(batch);
+
+ g_info << "|- Updating records (batch=" << batch << ")..." << endl;
+ while (r < records){
+ if(r + batch > records)
+ batch = records - r;
+
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ if (doSleep > 0)
+ NdbSleep_MilliSleep(doSleep);
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ if(pkReadRecord(pNdb, r, batch, NdbOperation::LM_Exclusive) != NDBT_OK)
+ {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ if(pIndexScanOp)
+ {
+ int rows_found = 0;
+ while((check = pIndexScanOp->nextResult(true)) == 0)
+ {
+ do {
+
+ if (calc.verifyRowValues(rows[0]) != 0){
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ int updates = calc.getUpdatesValue(rows[0]) + 1;
+
+ if(pkUpdateRecord(pNdb, r+rows_found, 1, updates) != NDBT_OK)
+ {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ rows_found++;
+ } while((check = pIndexScanOp->nextResult(false)) == 0);
+
+ if(check != 2)
+ break;
+ if((check = pTrans->execute(NoCommit)) != 0)
+ break;
+ }
+ if(check != 1 || rows_found != batch)
+ {
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ else
+ {
+ for(b = 0; b<batch && (b+r)<records; b++)
+ {
+ if (calc.verifyRowValues(rows[b]) != 0)
+ {
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ int updates = calc.getUpdatesValue(rows[b]) + 1;
+
+ if(pkUpdateRecord(pNdb, r+b, 1, updates) != NDBT_OK)
+ {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ check = pTrans->execute(Commit);
+ }
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ ndbout << "r = " << r << endl;
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ else{
+ updated += batch;
+ }
+
+ closeTransaction(pNdb);
+
+ r += batch; // Read next record
+ }
+
+ deallocRows();
+ g_info << "|- " << updated << " records updated" << endl;
+ return NDBT_OK;
+}
+
+int
+HugoTransactions::pkInterpretedUpdateRecords(Ndb* pNdb,
+ int records,
+ int batch){
+ int updated = 0;
+ int r = 0;
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check, a;
+
+ while (r < records){
+
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ NdbOperation* pOp = pTrans->getNdbOperation(tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->readTupleExclusive();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ // Define primary keys
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == true){
+ if(equalForAttr(pOp, a, r) != 0){
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ // Read update value
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (calc.isUpdateCol(a) == true){
+ if((row.attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ int updates = calc.getUpdatesValue(&row) + 1;
+
+ NdbOperation* pUpdOp;
+ pUpdOp = pTrans->getNdbOperation(tab.getName());
+ if (pUpdOp == NULL) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ check = pUpdOp->interpretedUpdateTuple();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ // PKs
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == true){
+ if(equalForAttr(pUpdOp, a, r) != 0){
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ // Update col
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if ((tab.getColumn(a)->getPrimaryKey() == false) &&
+ (calc.isUpdateCol(a) == true)){
+
+ // TODO switch for 32/64 bit
+ const NdbDictionary::Column* attr = tab.getColumn(a);
+ Uint32 valToIncWith = 1;
+ check = pUpdOp->incValue(attr->getName(), valToIncWith);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ // Remaining attributes
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if ((tab.getColumn(a)->getPrimaryKey() == false) &&
+ (calc.isUpdateCol(a) == false)){
+ if(setValueForAttr(pUpdOp, a, r, updates ) != 0){
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+
+
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ ndbout << "r = " << r << endl;
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ else{
+ updated++;
+ }
+
+
+ closeTransaction(pNdb);
+
+ r++; // Read next record
+
+ }
+
+ g_info << "|- " << updated << " records updated" << endl;
+ return NDBT_OK;
+}
+
+int
+HugoTransactions::pkDelRecords(Ndb* pNdb,
+ int records,
+ int batch,
+ bool allowConstraintViolation,
+ int doSleep){
+ // TODO Batch is not implemented
+ int deleted = 0;
+ int r = 0;
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check, a;
+ NdbOperation *pOp;
+
+ g_info << "|- Deleting records..." << endl;
+ while (r < records){
+ if(r + batch > records)
+ batch = records - r;
+
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ if (doSleep > 0)
+ NdbSleep_MilliSleep(doSleep);
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ if(pkDeleteRecord(pNdb, r, batch) != NDBT_OK)
+ {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ check = pTrans->execute(Commit);
+ if( check == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ switch(err.status){
+ case NdbError::TemporaryError:
+ ERR(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ break;
+
+ case NdbError::PermanentError:
+ if (allowConstraintViolation == true){
+ switch (err.classification){
+ case NdbError::ConstraintViolation:
+ // Tuple did not exist, OK but should be reported
+ g_info << r << ": " << err.code << " " << err.message << endl;
+ continue;
+ break;
+ default:
+ break;
+ }
+ }
+ ERR(err);
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ break;
+
+ default:
+ ERR(err);
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ else {
+ deleted += batch;
+ }
+ closeTransaction(pNdb);
+
+ r += batch; // Read next record
+
+ }
+
+ g_info << "|- " << deleted << " records deleted" << endl;
+ return NDBT_OK;
+}
+
+
+int
+HugoTransactions::lockRecords(Ndb* pNdb,
+ int records,
+ int percentToLock,
+ int lockTime){
+ // Place a lock on percentToLock% of the records in the Db
+ // Keep the locks for lockTime ms, commit operation
+ // and lock som other records
+ int r = 0;
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check, a, b;
+ NdbOperation *pOp;
+ NdbOperation::LockMode lm = NdbOperation::LM_Exclusive;
+
+ // Calculate how many records to lock in each batch
+ if (percentToLock <= 0)
+ percentToLock = 1;
+ double percentVal = (double)percentToLock / 100;
+ int lockBatch = (int)(records * percentVal);
+ if (lockBatch <= 0)
+ lockBatch = 1;
+
+ allocRows(lockBatch);
+
+ while (r < records){
+ if(r + lockBatch > records)
+ lockBatch = records - r;
+
+ g_info << "|- Locking " << lockBatch << " records..." << endl;
+
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ if(pkReadRecord(pNdb, r, lockBatch, lm) != NDBT_OK)
+ {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ // NoCommit lockTime times with 100 millis interval
+ int sleepInterval = 50;
+ int lockCount = lockTime / sleepInterval;
+ int commitCount = 0;
+ do {
+ check = pTrans->execute(NoCommit);
+ if( check == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ for (int b=0; (b<lockBatch) && (r+b<records); b++){
+ if (calc.verifyRowValues(rows[b]) != 0){
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ commitCount++;
+ NdbSleep_MilliSleep(sleepInterval);
+ } while (commitCount < lockCount);
+
+ // Really commit the trans, puuh!
+ check = pTrans->execute(Commit);
+ if( check == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ else{
+ for (int b=0; (b<lockBatch) && (r<records); b++){
+ if (calc.verifyRowValues(rows[b]) != 0){
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ r++; // Read next record
+ }
+ }
+
+ closeTransaction(pNdb);
+
+ }
+ deallocRows();
+ g_info << "|- Record locking completed" << endl;
+ return NDBT_OK;
+}
+
+int
+HugoTransactions::indexReadRecords(Ndb* pNdb,
+ const char * idxName,
+ int records,
+ int batch){
+ int reads = 0;
+ int r = 0;
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check, a;
+ NdbOperation *pOp;
+ NdbIndexScanOperation *sOp;
+
+ const NdbDictionary::Index* pIndex
+ = pNdb->getDictionary()->getIndex(idxName, tab.getName());
+
+ const bool ordered = (pIndex->getType()==NdbDictionary::Index::OrderedIndex);
+
+ if (batch == 0) {
+ g_info << "ERROR: Argument batch == 0 in indexReadRecords(). "
+ << "Not allowed." << endl;
+ return NDBT_FAILED;
+ }
+
+ if (ordered) {
+ batch = 1;
+ }
+
+ allocRows(batch);
+
+ while (r < records){
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ for(int b=0; (b<batch) && (r+b < records); b++){
+ if(!ordered){
+ pOp = pTrans->getNdbIndexOperation(idxName, tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ check = pOp->readTuple();
+ } else {
+ pOp = sOp = pTrans->getNdbIndexScanOperation(idxName, tab.getName());
+ if (sOp == NULL) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ check = sOp->readTuples();
+ }
+
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ // Define primary keys
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == true){
+ if(equalForAttr(pOp, a, r+b) != 0){
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ // Define attributes to read
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if((rows[b]->attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ check = pTrans->execute(Commit);
+ check = (check == -1 ? -1 : !ordered ? check : sOp->nextResult(true));
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ closeTransaction(pNdb);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ switch(err.code){
+ case 626: // Tuple did not exist
+ g_info << r << ": " << err.code << " " << err.message << endl;
+ r++;
+ break;
+
+ default:
+ ERR(err);
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ } else{
+ for (int b=0; (b<batch) && (r+b<records); b++){
+ if (calc.verifyRowValues(rows[b]) != 0){
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ reads++;
+ r++;
+ }
+ if(ordered && sOp->nextResult(true) == 0){
+ ndbout << "Error when comparing records "
+ << " - index op next_result to many" << endl;
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ closeTransaction(pNdb);
+ }
+ deallocRows();
+ g_info << reads << " records read" << endl;
+ return NDBT_OK;
+}
+
+
+
+int
+HugoTransactions::indexUpdateRecords(Ndb* pNdb,
+ const char * idxName,
+ int records,
+ int batch){
+
+ int updated = 0;
+ int r = 0;
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check, a, b;
+ NdbOperation *pOp;
+ NdbScanOperation * sOp;
+
+ const NdbDictionary::Index* pIndex
+ = pNdb->getDictionary()->getIndex(idxName, tab.getName());
+
+ const bool ordered = (pIndex->getType()==NdbDictionary::Index::OrderedIndex);
+ if (ordered){
+ batch = 1;
+ }
+
+ allocRows(batch);
+
+ while (r < records){
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ for(b = 0; b<batch && (b+r)<records; b++){
+ if(!ordered){
+ pOp = pTrans->getNdbIndexOperation(idxName, tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->readTupleExclusive();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ } else {
+ pOp = sOp = pTrans->getNdbIndexScanOperation(idxName, tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ check = 0;
+ sOp->readTuplesExclusive();
+ }
+
+ // Define primary keys
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == true){
+ if(equalForAttr(pOp, a, r+b) != 0){
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ // Define attributes to read
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if((rows[b]->attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ check = pTrans->execute(NoCommit);
+ check = (check == -1 ? -1 : !ordered ? check : sOp->nextResult(true));
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ ERR(err);
+ closeTransaction(pNdb);
+
+ if (err.status == NdbError::TemporaryError){
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ return NDBT_FAILED;
+ }
+
+ if(ordered && check != 0){
+ g_err << check << " - Row: " << r << " not found!!" << endl;
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ for(b = 0; b<batch && (b+r)<records; b++){
+ if (calc.verifyRowValues(rows[b]) != 0){
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ int updates = calc.getUpdatesValue(rows[b]) + 1;
+
+ NdbOperation* pUpdOp;
+ if(!ordered){
+ pUpdOp = pTrans->getNdbIndexOperation(idxName, tab.getName());
+ check = (pUpdOp == 0 ? -1 : pUpdOp->updateTuple());
+ } else {
+ pUpdOp = sOp->updateCurrentTuple();
+ }
+
+ if (pUpdOp == NULL) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+
+ if(!ordered){
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == true){
+ if(equalForAttr(pUpdOp, a, r+b) != 0){
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ }
+ }
+
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if (tab.getColumn(a)->getPrimaryKey() == false){
+ if(setValueForAttr(pUpdOp, a, r+b, updates ) != 0){
+ ERR(pTrans->getNdbError());
+ closeTransaction(pNdb);
+ return NDBT_FAILED;
+ }
+ }
+ }
+ }
+
+ check = pTrans->execute(Commit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ ERR(err);
+ closeTransaction(pNdb);
+
+ if (err.status == NdbError::TemporaryError){
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ndbout << "r = " << r << endl;
+ return NDBT_FAILED;
+ } else {
+ updated += batch;
+ }
+
+ closeTransaction(pNdb);
+
+ r+= batch; // Read next record
+ }
+
+ g_info << "|- " << updated << " records updated" << endl;
+ return NDBT_OK;
+}
+
+template class Vector<NDBT_ResultRow*>;
diff --git a/storage/ndb/test/src/Makefile.am b/storage/ndb/test/src/Makefile.am
new file mode 100644
index 00000000000..289633b060a
--- /dev/null
+++ b/storage/ndb/test/src/Makefile.am
@@ -0,0 +1,35 @@
+
+noinst_LIBRARIES = libNDBT.a
+
+libNDBT_a_SOURCES = \
+ NDBT_ReturnCodes.cpp \
+ NDBT_Error.cpp NDBT_Tables.cpp NDBT_ResultRow.cpp \
+ NDBT_Test.cpp HugoCalculator.cpp \
+ HugoOperations.cpp HugoTransactions.cpp \
+ HugoAsynchTransactions.cpp UtilTransactions.cpp \
+ NdbRestarter.cpp NdbRestarts.cpp NDBT_Output.cpp \
+ NdbBackup.cpp NdbConfig.cpp NdbGrep.cpp NDBT_Table.cpp \
+ NdbSchemaCon.cpp NdbSchemaOp.cpp getarg.c \
+ CpcClient.cpp
+
+INCLUDES_LOC = -I$(top_srcdir)/ndb/src/common/mgmcommon -I$(top_srcdir)/ndb/include/mgmcommon -I$(top_srcdir)/ndb/include/kernel -I$(top_srcdir)/ndb/src/mgmapi
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_ndbapitest.mk.am
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp: libNDBT.dsp
+
+libNDBT.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 $@ $(libNDBT_a_SOURCES)
+ @$(top_srcdir)/ndb/config/win-libraries $@ LIB
diff --git a/storage/ndb/test/src/NDBT_Error.cpp b/storage/ndb/test/src/NDBT_Error.cpp
new file mode 100644
index 00000000000..ffacb3eb928
--- /dev/null
+++ b/storage/ndb/test/src/NDBT_Error.cpp
@@ -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 */
+
+/* NDBT_Error.cpp */
+/* This program deals with error handling */
+
+#include <ndb_global.h>
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbTest.hpp>
+#include <NDBT_Error.hpp>
+#include <NdbSleep.h>
+
+
+ErrorData::ErrorData()
+{
+ errorCountArray = new Uint32[6000];
+ resetErrorCounters();
+
+ key_error = false;
+ temporary_resource_error = true;
+ insufficient_space_error = false;
+ node_recovery_error = true;
+ overload_error = true;
+ timeout_error = true;
+ internal_error = true;
+ user_error = true;
+ application_error = false;
+}
+
+ErrorData::~ErrorData()
+{
+ delete [] errorCountArray;
+}
+
+
+//-------------------------------------------------------------------
+// Error Handling routines
+//-------------------------------------------------------------------
+
+int ErrorData::handleErrorCommon(const NdbError & error)
+{
+ int retValue = 1;
+ if (error.code > 6000) {
+ if (user_error == true) {
+ retValue = 0;
+ }//if
+ return retValue;
+ }//if
+ errorCountArray[error.code]++;
+ switch(error.classification){
+ case NdbError::NoDataFound:
+ case NdbError::ConstraintViolation:
+ if (key_error == true) {
+ retValue = 0;
+ }//if
+ break;
+ case NdbError::TemporaryResourceError:
+ if (temporary_resource_error == true) {
+ retValue = 0;
+ }//if
+ break;
+ case NdbError::InsufficientSpace:
+ if (insufficient_space_error == true) {
+ retValue = 0;
+ }//if
+ break;
+ case NdbError::NodeRecoveryError:
+ if (node_recovery_error == true) {
+ retValue = 0;
+ }//if
+ break;
+
+ case NdbError::UnknownResultError:
+ if(error.code == 4012){
+ retValue = 0;
+ }
+ if(error.code == 4115){
+ retValue = 2;
+ }
+ if(error.code == 4007 && node_recovery_error == true){
+ retValue = 3;
+ }
+ break;
+ case NdbError::OverloadError:
+ if (overload_error == true) {
+ NdbSleep_MilliSleep(50);
+ retValue = 0;
+ }//if
+ break;
+ case NdbError::TimeoutExpired:
+ if (timeout_error == true) {
+ retValue = 0;
+ }//if
+ break;
+ case NdbError::InternalError:
+ if (internal_error == true) {
+ retValue = 0;
+ }//if
+ break;
+ case NdbError::ApplicationError:
+ if (application_error == true) {
+ retValue = 0;
+ }//if
+ break;
+ case NdbError::UserDefinedError:
+ if (user_error == true) {
+ retValue = 0;
+ }//if
+ break;
+ default:
+ break;
+ }//switch
+ if(error.status == NdbError::TemporaryError)
+ retValue = 0;
+
+ return retValue;
+}//handleErrorCommon()
+
+
+void ErrorData::printErrorCounters(NdbOut & out) const
+{
+ int localLoop;
+ for (localLoop = 0; localLoop < 6000; localLoop++) {
+ int errCount = (int)errorCountArray[localLoop];
+ if (errCount > 0) {
+ out << "NDBT: ErrorCode = " << localLoop << " occurred ";
+ out << errCount << " times" << endl;
+ }//if
+ }//for
+}//printErrorCounters()
+
+
+void ErrorData::printSettings(NdbOut & out)
+{
+ out << "Key Errors are ";
+ if (key_error == false) {
+ out << "disallowed" << endl;
+ } else {
+ out << "allowed" << endl;
+ }//if
+ out << "Temporary Resource Errors are ";
+ if (temporary_resource_error == false) {
+ out << "disallowed" << endl;
+ } else {
+ out << "allowed" << endl;
+ }//if
+ if (internal_error == true) {
+ out << "Insufficient Space Errors are ";
+ }
+ if (insufficient_space_error == false) {
+ out << "disallowed" << endl;
+ } else {
+ out << "allowed" << endl;
+ }//if
+ out << "Node Recovery Errors are ";
+ if (node_recovery_error == false) {
+ out << "disallowed" << endl;
+ } else {
+ out << "allowed" << endl;
+ }//if
+ out << "Overload Errors are ";
+ if (overload_error == false) {
+ out << "disallowed" << endl;
+ } else {
+ out << "allowed" << endl;
+ }//if
+ out << "Timeout Errors are ";
+ if (timeout_error == false) {
+ out << "disallowed" << endl;
+ } else {
+ out << "allowed" << endl;
+ }//if
+ out << "Internal NDB Errors are ";
+ if (internal_error == false) {
+ out << "disallowed" << endl;
+ } else {
+ out << "allowed" << endl;
+ }//if
+ out << "User logic reported Errors are ";
+ if (user_error == false) {
+ out << "disallowed" << endl;
+ } else {
+ out << "allowed" << endl;
+ }//if
+ out << "Application Errors are ";
+ if (application_error == false) {
+ out << "disallowed" << endl;
+ } else {
+ out << "allowed" << endl;
+ }//if
+}//printSettings
+
+
+void ErrorData::printCmdLineArgs(NdbOut & out)
+{
+ out << " -key_err Allow key errors" << endl;
+ out << " -no_key_err Disallow key errors (default)" << endl;
+ out << " -temp_res_err Allow temporary resource errors (default)";
+ out << endl;
+ out << " -no_temp_res_err Disallow temporary resource errors" << endl;
+ out << " -ins_space_err Allow insufficient space errors" << endl;
+ out << " -no_ins_space_err Disallow insufficient space errors (default)";
+ out << endl;
+ out << " -noderec_err Allow Node Recovery errors (default)" << endl;
+ out << " -no_noderec_err Disallow Node Recovery errors" << endl;
+ out << " -overload_err Allow Overload errors (default)" << endl;
+ out << " -no_overload_err Disallow Overload errors" << endl;
+ out << " -timeout_err Allow Time-out errors (default)" << endl;
+ out << " -no_timeout_err Disallow Time-out errors" << endl;
+ out << " -internal_err Allow Internal NDB errors" << endl;
+ out << " -no_internal_err Disallow Internal NDB errors (default)";
+ out << " -user_err Allow user logic reported errors (default)";
+ out << endl;
+ out << " -no_user_err Disallow user logic reported errors";
+ out << endl;
+
+}//printCmdLineArgs()
+
+
+bool ErrorData::parseCmdLineArg(const char** argv, int & i)
+{
+ bool ret_Value = true;
+ if (strcmp(argv[i], "-key_err") == 0){
+ key_error = true;
+ } else if (strcmp(argv[i], "-no_key_err") == 0){
+ key_error = false;
+ } else if (strcmp(argv[i], "-temp_res_err") == 0){
+ temporary_resource_error = true;
+ } else if (strcmp(argv[i], "-no_temp_res_err") == 0){
+ temporary_resource_error = false;
+ } else if (strcmp(argv[i], "-ins_space_err") == 0){
+ insufficient_space_error = true;
+ } else if (strcmp(argv[i], "-no_ins_space_err") == 0){
+ insufficient_space_error = false;
+ } else if (strcmp(argv[i], "-noderec_err") == 0){
+ node_recovery_error = true;
+ } else if (strcmp(argv[i], "-no_noderec_err") == 0){
+ node_recovery_error = false;
+ } else if (strcmp(argv[i], "-overload_err") == 0){
+ overload_error = true;
+ } else if (strcmp(argv[i], "-no_overload_err") == 0){
+ overload_error = false;
+ } else if (strcmp(argv[i], "-timeout_err") == 0){
+ timeout_error = true;
+ } else if (strcmp(argv[i], "-no_timeout_err") == 0){
+ timeout_error = false;
+ } else if (strcmp(argv[i], "-internal_err") == 0){
+ internal_error = true;
+ } else if (strcmp(argv[i], "-no_internal_err") == 0){
+ internal_error = false;
+ } else if (strcmp(argv[i], "-user_err") == 0){
+ user_error = true;
+ } else if (strcmp(argv[i], "-no_user_err") == 0){
+ user_error = false;
+ } else {
+ ret_Value = false;
+ }//if
+ return ret_Value;
+}//bool parseCmdline
+
+void ErrorData::resetErrorCounters()
+{
+ for (int i = 0; i < 6000; i++){
+ errorCountArray[i] = 0 ;
+ }
+}
+
+
+
diff --git a/storage/ndb/test/src/NDBT_Output.cpp b/storage/ndb/test/src/NDBT_Output.cpp
new file mode 100644
index 00000000000..633d71991d0
--- /dev/null
+++ b/storage/ndb/test/src/NDBT_Output.cpp
@@ -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 "NDBT_Output.hpp"
+
+FileOutputStream gerr_fileoutputstream(stderr);
+FileOutputStream gwarning_fileoutputstream(stderr);
+FileOutputStream ginfo_fileoutputstream(stdout);
+FileOutputStream gdebug_fileoutputstream(stdout);
+
+FilteredNdbOut g_err(gerr_fileoutputstream, 0, 2);
+FilteredNdbOut g_warning(gwarning_fileoutputstream, 1, 2);
+FilteredNdbOut g_info(ginfo_fileoutputstream, 2, 2);
+FilteredNdbOut g_debug(gdebug_fileoutputstream, 3, 2);
+
+void
+setOutputLevel(int i){
+ g_err.setLevel(i);
+ g_warning.setLevel(i);
+ g_info.setLevel(i);
+ g_debug.setLevel(i);
+}
diff --git a/storage/ndb/test/src/NDBT_ResultRow.cpp b/storage/ndb/test/src/NDBT_ResultRow.cpp
new file mode 100644
index 00000000000..8e92a57d2e4
--- /dev/null
+++ b/storage/ndb/test/src/NDBT_ResultRow.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 <ndb_global.h>
+#include "NDBT_ResultRow.hpp"
+#include <NdbOut.hpp>
+#include <NdbSchemaCon.hpp>
+
+NDBT_ResultRow::NDBT_ResultRow(const NdbDictionary::Table& tab,
+ char attrib_delimiter)
+ : m_table(tab)
+{
+ assert(tab.getObjectStatus() == NdbDictionary::Object::Retrieved);
+
+ cols = tab.getNoOfColumns();
+ names = new char * [cols];
+ data = new NdbRecAttr * [cols];
+
+ for(int i = 0; i<cols; i++){
+ names[i] = new char[255];
+ strcpy(names[i], tab.getColumn(i)->getName());
+ }
+
+ ad[0] = attrib_delimiter;
+ ad[1] = 0;
+ m_ownData = false;
+}
+
+NDBT_ResultRow::~NDBT_ResultRow(){
+ for(int i = 0; i<cols; i++){
+ delete [] names[i];
+ }
+ delete [] names;
+
+ if(m_ownData){
+ for(int i = 0; i<cols; i++)
+ delete data[i];
+ }
+ delete [] data;
+}
+
+NdbRecAttr* &
+NDBT_ResultRow::attributeStore(int i){
+
+ return data[i];
+}
+
+const NdbRecAttr*
+NDBT_ResultRow::attributeStore(int i) const {
+ return data[i];
+}
+
+const
+NdbRecAttr *
+NDBT_ResultRow::attributeStore(const char* name) const {
+ for(int i = 0; i<cols; i++){
+ if (strcmp(names[i], name) == 0)
+ return data[i];
+ }
+ assert(false);
+ return 0;
+}
+
+NdbOut &
+NDBT_ResultRow::header (NdbOut & out) const {
+ for(int i = 0; i<cols; i++){
+ out << names[i];
+ if (i < cols-1)
+ out << ad;
+ }
+ return out;
+}
+
+BaseString NDBT_ResultRow::c_str() const {
+
+ BaseString str;
+
+ char buf[10];
+ for(int i = 0; i<cols; i++){
+ if(data[i]->isNULL()){
+ sprintf(buf, "NULL");
+ str.append(buf);
+ }else{
+ Uint32* p = (Uint32*)data[i]->aRef();
+ Uint32 sizeInBytes = data[i]->attrSize() * data[i]->arraySize();
+ for (Uint32 j = 0; j < sizeInBytes; j+=(sizeof(Uint32))){
+ str.append("H'");
+ sprintf(buf, "%.8x", *p);
+ p++;
+ str.append(buf);
+ if ((j + sizeof(Uint32)) < sizeInBytes)
+ str.append(", ");
+ }
+ }
+ str.append("\n");
+ }
+ str.append("*");
+
+ //ndbout << "NDBT_ResultRow::c_str() = " << str.c_str() << endl;
+
+ return str;
+}
+
+NdbOut &
+operator << (NdbOut& ndbout, const NDBT_ResultRow & res) {
+ for(int i = 0; i<res.cols; i++)
+ ndbout << *(res.data[i]) << "\t";
+ return ndbout;
+}
+
+NDBT_ResultRow *
+NDBT_ResultRow::clone () const {
+
+ NDBT_ResultRow * row = new NDBT_ResultRow(m_table, ad[0]);
+ row->m_ownData = true;
+ Uint32 noOfColumns = m_table.getNoOfColumns();
+ for(Uint32 i = 0; i < noOfColumns; i++){
+ row->data[i] = data[i]->clone();
+ }
+
+ return row;
+}
+
+bool
+NDBT_ResultRow::operator==(const NDBT_ResultRow& other) const
+{
+ // quick and dirty
+ return c_str() == other.c_str();
+}
diff --git a/storage/ndb/test/src/NDBT_ReturnCodes.cpp b/storage/ndb/test/src/NDBT_ReturnCodes.cpp
new file mode 100644
index 00000000000..5bffc00177f
--- /dev/null
+++ b/storage/ndb/test/src/NDBT_ReturnCodes.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 */
+
+/* System include files */
+#include <ndb_global.h>
+
+#include "NDBT_ReturnCodes.h"
+
+/* Ndb include files */
+#include <NdbOut.hpp>
+
+const char* rcodeToChar(int rcode){
+ switch (rcode){
+ case NDBT_OK:
+ return "OK";
+ break;
+ case NDBT_FAILED:
+ return "Failed";
+ break;
+ case NDBT_WRONGARGS:
+ return "Wrong arguments";
+ break;
+ case NDBT_TEMPORARY:
+ return "Temporary error";
+ break;
+
+ default:
+ return "Unknown";
+ break;
+ }
+}
+
+int NDBT_ProgramExit(int rcode){
+ ndbout_c("\nNDBT_ProgramExit: %d - %s\n", rcode, rcodeToChar(rcode));
+ // exit(rcode);
+ return rcode;
+}
diff --git a/storage/ndb/test/src/NDBT_Table.cpp b/storage/ndb/test/src/NDBT_Table.cpp
new file mode 100644
index 00000000000..8d398b75d81
--- /dev/null
+++ b/storage/ndb/test/src/NDBT_Table.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 <NDBT_Table.hpp>
+#include <NdbTimer.hpp>
+#include <NDBT.hpp>
+
+class NdbOut&
+operator <<(class NdbOut& ndbout, const NDBT_Table & tab)
+{
+ ndbout << "-- " << tab.getName() << " --" << endl;
+
+ ndbout << "Version: " << tab.getObjectVersion() << endl;
+ ndbout << "Fragment type: " << (unsigned) tab.getFragmentType() << endl;
+ ndbout << "K Value: " << tab.getKValue()<< endl;
+ ndbout << "Min load factor: " << tab.getMinLoadFactor()<< endl;
+ ndbout << "Max load factor: " << tab.getMaxLoadFactor()<< endl;
+ ndbout << "Temporary table: " << (tab.getStoredTable() ? "no" : "yes") << endl;
+ ndbout << "Number of attributes: " << tab.getNoOfColumns() << endl;
+ ndbout << "Number of primary keys: " << tab.getNoOfPrimaryKeys() << endl;
+ ndbout << "Length of frm data: " << tab.getFrmLength() << endl;
+
+
+ //<< ((tab.getTupleKey() == TupleId) ? " tupleid" : "") <<endl;
+ ndbout << "TableStatus: ";
+ switch(tab.getObjectStatus()){
+ case NdbDictionary::Object::New:
+ ndbout << "New" << endl;
+ break;
+ case NdbDictionary::Object::Changed:
+ ndbout << "Changed" << endl;
+ break;
+ case NdbDictionary::Object::Retrieved:
+ ndbout << "Retrieved" << endl;
+ break;
+ default:
+ ndbout << "Unknown(" << (unsigned) tab.getObjectStatus() << ")" << endl;
+ }
+
+ ndbout << "-- Attributes -- " << endl;
+ int noOfAttributes = tab.getNoOfColumns();
+ for(int i = 0; i<noOfAttributes; i++){
+ ndbout << (* (const NDBT_Attribute*)tab.getColumn(i)) << endl;
+ }
+
+ return ndbout;
+}
+
+class NdbOut& operator <<(class NdbOut&, const NdbDictionary::Index & idx)
+{
+ ndbout << idx.getName();
+ ndbout << "(";
+ for (unsigned i=0; i < idx.getNoOfColumns(); i++)
+ {
+ const NdbDictionary::Column *col = idx.getColumn(i);
+ ndbout << col->getName();
+ if (i < idx.getNoOfColumns()-1)
+ ndbout << ", ";
+ }
+ ndbout << ")";
+
+ ndbout << " - ";
+ switch (idx.getType()) {
+ case NdbDictionary::Object::UniqueHashIndex:
+ ndbout << "UniqueHashIndex";
+ break;
+ case NdbDictionary::Object::OrderedIndex:
+ ndbout << "OrderedIndex";
+ break;
+ default:
+ ndbout << "Type " << (unsigned) idx.getType();
+ break;
+ }
+ return ndbout;
+}
+
diff --git a/storage/ndb/test/src/NDBT_Tables.cpp b/storage/ndb/test/src/NDBT_Tables.cpp
new file mode 100644
index 00000000000..5a5fecd85c1
--- /dev/null
+++ b/storage/ndb/test/src/NDBT_Tables.cpp
@@ -0,0 +1,945 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NDBT.hpp>
+#include <NDBT_Table.hpp>
+#include <NDBT_Tables.hpp>
+
+/* ******************************************************* */
+// Define Ndb standard tables
+//
+// USE ONLY UPPERLETTERS IN TAB AND COLUMN NAMES
+/* ******************************************************* */
+
+/*
+ * These are our "official" test tables
+ *
+ */
+/* T1 */
+static
+const
+NDBT_Attribute T1Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL2", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned),
+};
+static
+const
+NDBT_Table T1("T1", sizeof(T1Attribs)/sizeof(NDBT_Attribute), T1Attribs);
+
+/* T2 */
+static
+const
+NDBT_Attribute T2Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Bigunsigned, 1, true),
+ NDBT_Attribute("KOL2", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Bit, 23),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned,
+ 1, false, true), // Nullable
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned)
+};
+static
+const
+NDBT_Table T2("T2", sizeof(T2Attribs)/sizeof(NDBT_Attribute), T2Attribs);
+
+/* T3 */
+static
+const
+NDBT_Attribute T3Attribs[] = {
+ NDBT_Attribute("ID", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("PERSNR", NdbDictionary::Column::Char, 10),
+ NDBT_Attribute("NAME", NdbDictionary::Column::Char, 25),
+ NDBT_Attribute("ADRESS", NdbDictionary::Column::Char, 50),
+ NDBT_Attribute("ADRESS2", NdbDictionary::Column::Char,
+ 30, false, true), // Nullable
+ NDBT_Attribute("FÖDELSEÅR", NdbDictionary::Column::Unsigned)
+};
+static
+const
+NDBT_Table T3("T3", sizeof(T3Attribs)/sizeof(NDBT_Attribute), T3Attribs);
+
+/* T4 */
+static
+const
+NDBT_Attribute T4Attribs[] = {
+ NDBT_Attribute("REGNR", NdbDictionary::Column::Char, 6, true),
+ NDBT_Attribute("YEAR", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("OWNER", NdbDictionary::Column::Char, 25),
+ NDBT_Attribute("ADRESS", NdbDictionary::Column::Char, 50),
+ NDBT_Attribute("ADRESS2", NdbDictionary::Column::Char,
+ 30, false, true), // Nullable
+ NDBT_Attribute("OWNERID", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("CHECKDATE", NdbDictionary::Column::Unsigned)
+};
+static
+const
+NDBT_Table T4("T4", sizeof(T4Attribs)/sizeof(NDBT_Attribute), T4Attribs);
+
+/* T5 */
+static
+const
+NDBT_Attribute T5Attribs[] = {
+ NDBT_Attribute("OWNERID", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("REGNR", NdbDictionary::Column::Char, 6, true),
+ NDBT_Attribute("CREATEDDATE", NdbDictionary::Column::Unsigned)
+};
+static
+const
+NDBT_Table T5("T5", sizeof(T5Attribs)/sizeof(NDBT_Attribute), T5Attribs);
+
+/* T6 */
+static
+const
+NDBT_Attribute T6Attribs[] = {
+ NDBT_Attribute("PK1", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("ATTR1", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR2", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR3", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR4", NdbDictionary::Column::Char,
+ 47, false, true),// Nullable
+ NDBT_Attribute("ATTR5", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR6", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR7", NdbDictionary::Column::Char,
+ 48, false, true),// Nullable
+ NDBT_Attribute("ATTR8", NdbDictionary::Column::Char,
+ 50, false, true), // Nullable
+ NDBT_Attribute("ATTR9", NdbDictionary::Column::Int),
+ NDBT_Attribute("ATTR10", NdbDictionary::Column::Float),
+ NDBT_Attribute("ATTR11", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR12", NdbDictionary::Column::Char, 49),
+ NDBT_Attribute("ATTR13", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR14", NdbDictionary::Column::Char, 50),
+ NDBT_Attribute("ATTR15", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR16", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR17", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR18", NdbDictionary::Column::Char, 257),
+ NDBT_Attribute("ATTR19", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR20", NdbDictionary::Column::Unsigned),
+};
+static
+const
+NDBT_Table T6("T6", sizeof(T6Attribs)/sizeof(NDBT_Attribute), T6Attribs);
+
+/* T7 */
+static
+const
+NDBT_Attribute T7Attribs[] = {
+ NDBT_Attribute("PK1", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("PK2", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("PK3", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("PK4", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("ATTR1", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR2", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR3", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR4", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR5", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR6", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR7", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR8", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR9", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR10", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR11", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR12", NdbDictionary::Column::Char, 259),
+ NDBT_Attribute("ATTR13", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR14", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR15", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR16", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR17", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR18", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR19", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ATTR20", NdbDictionary::Column::Unsigned),
+};
+static
+const
+NDBT_Table T7("T7", sizeof(T7Attribs)/sizeof(NDBT_Attribute), T7Attribs);
+
+/* T8 */
+static
+const
+NDBT_Attribute T8Attribs[] = {
+ NDBT_Attribute("PERSON_ID", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("NAME", NdbDictionary::Column::Char, 257),
+ NDBT_Attribute("ADRESS", NdbDictionary::Column::Char, 513),
+ NDBT_Attribute("POSTADRESS", NdbDictionary::Column::Char, 1173),
+ NDBT_Attribute("VALUE", NdbDictionary::Column::Unsigned),
+
+};
+static
+const
+NDBT_Table T8("T8", sizeof(T8Attribs)/sizeof(NDBT_Attribute), T8Attribs);
+
+/* T9 */
+static
+const
+NDBT_Attribute T9Attribs[] = {
+ NDBT_Attribute("KF_SKAPAD", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("PLATS_ID", NdbDictionary::Column::Char, 2, true),
+ NDBT_Attribute("TNR_SKAPAD", NdbDictionary::Column::Char, 12, true),
+ NDBT_Attribute("DELG_MOT", NdbDictionary::Column::Char, 1, true),
+ NDBT_Attribute("VALUE", NdbDictionary::Column::Unsigned),
+};
+static
+const
+NDBT_Table T9("T9", sizeof(T9Attribs)/sizeof(NDBT_Attribute), T9Attribs);
+
+/* T10 - Long key table */
+static
+const
+NDBT_Attribute T10Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Char, 256, true),
+ NDBT_Attribute("KOL2", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Char, 257),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned),
+};
+static
+const
+NDBT_Table T10("T10", sizeof(T10Attribs)/sizeof(NDBT_Attribute), T10Attribs);
+
+
+/* T11 - Primary key is not first attribute */
+static
+const
+NDBT_Attribute T11Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL2", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Char, 111),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Char, 113)
+};
+
+static
+const
+NDBT_Table T11("T11", sizeof(T11Attribs)/sizeof(NDBT_Attribute), T11Attribs);
+
+/* T12 - 16 primary keys */
+static
+const
+NDBT_Attribute T12Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL2", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL6", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL7", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL8", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL9", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL10", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL11", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL12", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL13", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL14", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL15", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL16", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL20", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL30", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL40", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL50", NdbDictionary::Column::Unsigned)
+};
+
+static
+const
+NDBT_Table T12("T12", sizeof(T12Attribs)/sizeof(NDBT_Attribute), T12Attribs);
+
+/* T13 - Long key table */
+static
+const
+NDBT_Attribute T13Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Char, 257, true),
+ NDBT_Attribute("KOL2", NdbDictionary::Column::Char, 259, true),
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Char, 113, true),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL6", NdbDictionary::Column::Unsigned),
+};
+static
+const
+NDBT_Table T13("T13", sizeof(T13Attribs)/sizeof(NDBT_Attribute), T13Attribs);
+
+/* T14 - 5 primary keys */
+static
+const
+NDBT_Attribute T14Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL2", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Char, 4, true),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL20", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL30", NdbDictionary::Column::Int),
+ NDBT_Attribute("KOL40", NdbDictionary::Column::Float),
+ NDBT_Attribute("KOL50", NdbDictionary::Column::Char, 200)
+};
+
+
+static
+const
+NDBT_Table T14("T14", sizeof(T14Attribs)/sizeof(NDBT_Attribute), T14Attribs);
+
+/*
+ C2 DHCP TABLES, MAYBE THESE SHOULD BE MOVED TO THE UTIL_TABLES?
+*/
+static
+const
+NDBT_Attribute I1_Cols[] = {
+ NDBT_Attribute("ID", NdbDictionary::Column::Unsigned, true),
+ NDBT_Attribute("PORT", NdbDictionary::Column::Char, 16, true),
+ NDBT_Attribute("ACCESSNODE", NdbDictionary::Column::Char, 16, true),
+ NDBT_Attribute("POP", NdbDictionary::Column::Char, 64, true),
+ NDBT_Attribute("VLAN", NdbDictionary::Column::Char, 16),
+ NDBT_Attribute("COMMENT", NdbDictionary::Column::Char, 128),
+ NDBT_Attribute("SNMPINDEX", NdbDictionary::Column::Int),
+ NDBT_Attribute("PORTSTATE", NdbDictionary::Column::Int),
+ NDBT_Attribute("UPDATES", NdbDictionary::Column::Unsigned)
+};
+
+static
+const
+char* I1_Indexes[] = {
+ "UNIQUE", "ID", "PORT", "ACCESSNODE", "POP", "PORTSTATE", 0,
+ 0
+};
+
+static
+NDBT_Table I1("I1", sizeof(I1_Cols)/sizeof(NDBT_Attribute), I1_Cols
+ );// ,I1_Indexes);
+
+static
+const
+NDBT_Attribute I2_Cols[] = {
+ NDBT_Attribute("ID", NdbDictionary::Column::Unsigned, true),
+ NDBT_Attribute("PORT", NdbDictionary::Column::Char, 16, true),
+ NDBT_Attribute("ACCESSNODE", NdbDictionary::Column::Char, 16, true),
+ NDBT_Attribute("POP", NdbDictionary::Column::Char, 64, true),
+ NDBT_Attribute("ACCESSTYPE", NdbDictionary::Column::Int, true),
+ NDBT_Attribute("CUSTOMER_ID", NdbDictionary::Column::Int),
+ NDBT_Attribute("PROVIDER", NdbDictionary::Column::Int),
+ NDBT_Attribute("TEXPIRE", NdbDictionary::Column::Int),
+ NDBT_Attribute("NUM_IP", NdbDictionary::Column::Int),
+ NDBT_Attribute("LEASED_NUM_IP", NdbDictionary::Column::Int),
+ NDBT_Attribute("LOCKED_IP", NdbDictionary::Column::Int),
+ NDBT_Attribute("STATIC_DNS", NdbDictionary::Column::Int),
+ NDBT_Attribute("SUSPENDED_SERVICES", NdbDictionary::Column::Int),
+ NDBT_Attribute("UPDATES", NdbDictionary::Column::Unsigned)
+};
+
+const
+char* I2_Indexes[] = {
+ "ORDERED", "CUSTOMER_ID", 0,
+ "ORDERED", "NUM_IP", 0,
+ 0
+};
+
+static
+NDBT_Table I2("I2", sizeof(I2_Cols)/sizeof(NDBT_Attribute), I2_Cols
+ );//, I2_Indexes);
+
+static
+const
+NDBT_Attribute I3_Cols[] = {
+ NDBT_Attribute("ID", NdbDictionary::Column::Unsigned, true),
+ NDBT_Attribute("PORT", NdbDictionary::Column::Char, 16), // SI2
+ NDBT_Attribute("ACCESSNODE", NdbDictionary::Column::Char, 16), // SI2
+ NDBT_Attribute("POP", NdbDictionary::Column::Char, 64), // SI2
+ NDBT_Attribute("MAC", NdbDictionary::Column::Char, 12, true),
+ NDBT_Attribute("MAC_EXPIRE", NdbDictionary::Column::Int, 1),
+ NDBT_Attribute("IIP", NdbDictionary::Column::Int), // SI1
+ NDBT_Attribute("P_EXPIRE", NdbDictionary::Column::Int),
+ NDBT_Attribute("HOSTNAME", NdbDictionary::Column::Char, 32),
+ NDBT_Attribute("DETECTED", NdbDictionary::Column::Int),
+ NDBT_Attribute("STATUS", NdbDictionary::Column::Int),
+ NDBT_Attribute("NUM_REQUESTS", NdbDictionary::Column::Int),
+ NDBT_Attribute("ACCESSTYPE", NdbDictionary::Column::Int),
+ NDBT_Attribute("OS_TYPE", NdbDictionary::Column::Int),
+ NDBT_Attribute("GW", NdbDictionary::Column::Int),
+ NDBT_Attribute("UPDATES", NdbDictionary::Column::Unsigned)
+};
+
+const
+char* I3_Indexes[] = {
+ "UNIQUE", "ID", 0,
+ "ORDERED", "MAC", 0,
+ "ORDERED", "GW", 0,
+ 0
+};
+
+static
+NDBT_Table I3("I3", sizeof(I3_Cols)/sizeof(NDBT_Attribute), I3_Cols
+ ); // ,I3_Indexes);
+
+// Define array with pointer to all tables
+static
+const
+NDBT_Table *test_tables[]=
+{
+ &T1,
+ &T2,
+ &T3,
+ &T4,
+ &T5,
+ &T6,
+ &T7,
+ &T8,
+ &T9,
+ &T10,
+ &T11,
+ &T12,
+ &T13,
+ &T14,
+ &I1,
+ &I2,
+ &I3
+};
+
+struct NDBT_IndexList {
+ const char * m_table;
+ const char ** m_indexes;
+};
+
+static
+const
+NDBT_IndexList indexes[] = {
+ "I1", I1_Indexes,
+ "I2", I2_Indexes,
+ "I3", I3_Indexes,
+ 0, 0
+};
+
+static
+const
+int numTestTables = sizeof(test_tables)/sizeof(NDBT_Table*);
+
+
+/**
+ * Define tables we should not be able to create
+ */
+
+/* F1
+ *
+ * Error: PK and column with same name
+ */
+static
+const
+NDBT_Attribute F1Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Unsigned)
+};
+
+static
+const
+NDBT_Table F1("F1", sizeof(F1Attribs)/sizeof(NDBT_Attribute), F1Attribs);
+
+/* F2
+ *
+ * Error: Two columns with same name
+ */
+static
+const
+NDBT_Attribute F2Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL2", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL2", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned)
+};
+
+static
+const
+NDBT_Table F2("F2", sizeof(F2Attribs)/sizeof(NDBT_Attribute), F2Attribs);
+
+/* F3
+ *
+ * Error: Too many primary keys defined, 32 is max
+ */
+static
+const
+NDBT_Attribute F3Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL2", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL6", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL7", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL8", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL9", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL10", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL11", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL12", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL13", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL14", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL15", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL16", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL17", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL18", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL19", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL20", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL21", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL22", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL23", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL24", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL25", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL26", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL27", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL28", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL29", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL30", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL31", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL32", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL33", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("KOL40", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL50", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL60", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL70", NdbDictionary::Column::Unsigned)
+};
+
+static
+const
+NDBT_Table F3("F3", sizeof(F3Attribs)/sizeof(NDBT_Attribute), F3Attribs);
+
+/* F4
+ *
+ * Error: Too long key
+ */
+static
+const
+NDBT_Attribute F4Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL2", NdbDictionary::Column::Unsigned, 9999999, true),
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned)
+};
+
+static
+const
+NDBT_Table F4("F4", sizeof(F4Attribs)/sizeof(NDBT_Attribute), F4Attribs);
+
+/* F5
+ *
+ * Error: Too long attr name
+ */
+static
+const
+NDBT_Attribute F5Attribs[] = {
+ NDBT_Attribute("KOL1WITHVERRYLONGNAME_ISITTOLONG", NdbDictionary::Column::Unsigned, true),
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned)
+};
+
+static
+const
+NDBT_Table F5("F5", sizeof(F5Attribs)/sizeof(NDBT_Attribute), F5Attribs);
+
+/* F6
+ *
+ * Error: Zero length of pk attribute
+ */
+static
+const
+NDBT_Attribute F6Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Char, 0, true, false),
+ NDBT_Attribute("KOL2", NdbDictionary::Column::Char, 256),
+};
+
+static
+const
+NDBT_Table F6("F6", sizeof(F6Attribs)/sizeof(NDBT_Attribute), F6Attribs);
+
+/* F7
+ *
+ * Error: Table without primary key
+ */
+static
+const
+NDBT_Attribute F7Attribs[] = {
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Unsigned)
+};
+
+NDBT_Table F7("F7", sizeof(F7Attribs)/sizeof(NDBT_Attribute), F7Attribs);
+
+/* F8
+ *
+ * Error: Table without nullable primary key
+ */
+static
+const
+NDBT_Attribute F8Attribs[] = {
+ NDBT_Attribute("KOL3", NdbDictionary::Column::Int, 1, true, true),
+ NDBT_Attribute("KOL4", NdbDictionary::Column::Int),
+ NDBT_Attribute("KOL5", NdbDictionary::Column::Int)
+};
+
+NDBT_Table F8("F8", sizeof(F8Attribs)/sizeof(NDBT_Attribute), F8Attribs);
+
+
+/* F15 - 2-node crash in v20x */
+static
+const
+NDBT_Attribute F15Attribs[] = {
+ NDBT_Attribute("KOL1", NdbDictionary::Column::Char, 40, true)
+};
+static
+const
+NDBT_Table F15("F15", sizeof(F15Attribs)/sizeof(NDBT_Attribute), F15Attribs);
+
+// Define array with pointer to tables that we should not be able to create
+static
+const
+NDBT_Table *fail_tables[]=
+{
+ &F1,
+ &F2,
+ &F3,
+ &F4,
+ &F5,
+ &F6,
+ &F7,
+ &F8,
+ &F15
+};
+
+static
+const
+int numFailTables = sizeof(fail_tables)/sizeof(NDBT_Table*);
+
+
+/**
+ * Define util tables that we may create
+ */
+
+
+/* GL
+ * General ledger table for bank application
+ */
+static
+const
+NDBT_Attribute GL_Attribs[] = {
+ NDBT_Attribute("TIME", NdbDictionary::Column::Bigunsigned, 1, true),
+ NDBT_Attribute("ACCOUNT_TYPE", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("BALANCE", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("DEPOSIT_COUNT", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("DEPOSIT_SUM", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("WITHDRAWAL_COUNT", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("WITHDRAWAL_SUM", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("PURGED", NdbDictionary::Column::Unsigned)
+};
+
+static
+NDBT_Table GL("GL", sizeof(GL_Attribs)/sizeof(NDBT_Attribute), GL_Attribs);
+
+/* ACCOUNT
+ * Account table for bank application
+ */
+static
+const
+NDBT_Attribute ACCOUNT_Attribs[] = {
+ NDBT_Attribute("ACCOUNT_ID", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("OWNER", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("BALANCE", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("ACCOUNT_TYPE", NdbDictionary::Column::Unsigned),
+};
+
+static
+NDBT_Table ACCOUNT("ACCOUNT", sizeof(ACCOUNT_Attribs)/sizeof(NDBT_Attribute), ACCOUNT_Attribs);
+
+/* TRANSACTION
+ * Transaction table for bank application
+ */
+static
+const
+NDBT_Attribute TRANSACTION_Attribs[] = {
+ NDBT_Attribute("TRANSACTION_ID", NdbDictionary::Column::Bigunsigned, 1, true),
+ NDBT_Attribute("ACCOUNT", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("ACCOUNT_TYPE", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("OTHER_ACCOUNT", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("TRANSACTION_TYPE", NdbDictionary::Column::Unsigned),
+ NDBT_Attribute("TIME", NdbDictionary::Column::Bigunsigned),
+ NDBT_Attribute("AMOUNT", NdbDictionary::Column::Unsigned),
+};
+
+static
+NDBT_Table TRANSACTION("TRANSACTION", sizeof(TRANSACTION_Attribs)/sizeof(NDBT_Attribute), TRANSACTION_Attribs);
+
+/* SYSTEM_VALUES
+ * System values table for bank application
+ */
+static
+const
+NDBT_Attribute SYSTEM_VALUES_Attribs[] = {
+ NDBT_Attribute("SYSTEM_VALUES_ID", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("VALUE", NdbDictionary::Column::Bigunsigned)
+};
+
+static
+NDBT_Table SYSTEM_VALUES("SYSTEM_VALUES", sizeof(SYSTEM_VALUES_Attribs)/sizeof(NDBT_Attribute), SYSTEM_VALUES_Attribs);
+
+/* ACCOUNT_TYPES
+ * Account types table for bank application
+ */
+static
+const
+NDBT_Attribute ACCOUNT_TYPES_Attribs[] = {
+ NDBT_Attribute("ACCOUNT_TYPE_ID", NdbDictionary::Column::Unsigned, 1, true),
+ NDBT_Attribute("DESCRIPTION", NdbDictionary::Column::Char, 64)
+};
+
+static
+NDBT_Table ACCOUNT_TYPES("ACCOUNT_TYPE", sizeof(ACCOUNT_TYPES_Attribs)/sizeof(NDBT_Attribute), ACCOUNT_TYPES_Attribs);
+
+
+// Define array with pointer to util tables
+static
+const
+NDBT_Table *util_tables[]=
+{
+ &GL,
+ &ACCOUNT,
+ &TRANSACTION,
+ &SYSTEM_VALUES,
+ &ACCOUNT_TYPES
+};
+
+static
+const
+int numUtilTables = sizeof(util_tables)/sizeof(NDBT_Table*);
+
+
+const
+NdbDictionary::Table*
+NDBT_Tables::getTable(const char* _nam){
+ // Search tables list to find a table
+ NDBT_Table* tab = NULL;
+ int i;
+ for (i=0; i<numTestTables; i++){
+ if (strcmp(test_tables[i]->getName(), _nam) == 0){
+ return test_tables[i];
+ }
+ }
+ for (i=0; i<numFailTables; i++){
+ if (strcmp(fail_tables[i]->getName(), _nam) == 0){
+ return fail_tables[i];
+ }
+ }
+ for (i=0; i<numUtilTables; i++){
+ if (strcmp(util_tables[i]->getName(), _nam) == 0){
+ return util_tables[i];
+ }
+ }
+ // TPK_no tables
+ // Dynamcially create table vith primary key size
+ // set to no
+ // Useful for testing key sizes 1 - max
+ int pkSizeOfTable;
+ if(sscanf(_nam, "TPK_%d", &pkSizeOfTable) == 1){
+ return tableWithPkSize(_nam, pkSizeOfTable);
+ }
+ return tab;
+}
+
+const NdbDictionary::Table*
+NDBT_Tables::tableWithPkSize(const char* _nam, Uint32 pkSize){
+ NdbDictionary::Table* tab = new NdbDictionary::Table(_nam);
+
+ // Add one PK of the desired length
+ tab->addColumn(NDBT_Attribute("PK1",
+ NdbDictionary::Column::Char,
+ pkSize,
+ true));
+
+ // Add 4 attributes
+ tab->addColumn(NDBT_Attribute("ATTR1",
+ NdbDictionary::Column::Char,
+ 21));
+
+ tab->addColumn(NDBT_Attribute("ATTR2",
+ NdbDictionary::Column::Char,
+ 124));
+
+ tab->addColumn(NDBT_Attribute("ATTR3",
+ NdbDictionary::Column::Unsigned));
+
+ tab->addColumn(NDBT_Attribute("ATTR4",
+ NdbDictionary::Column::Unsigned));
+
+ return tab;
+}
+
+const NdbDictionary::Table*
+NDBT_Tables::getTable(int _num){
+ // Get table at pos _num
+ assert(_num < numTestTables);
+ return test_tables[_num];
+}
+
+int
+NDBT_Tables::getNumTables(){
+ return numTestTables;
+}
+
+int
+NDBT_Tables::createAllTables(Ndb* pNdb, bool _temp, bool existsOk){
+
+ for (int i=0; i < NDBT_Tables::getNumTables(); i++){
+ pNdb->getDictionary()->dropTable(NDBT_Tables::getTable(i)->getName());
+ int ret= createTable(pNdb,
+ NDBT_Tables::getTable(i)->getName(), _temp, existsOk);
+ if(ret){
+ return ret;
+ }
+ }
+ return NDBT_OK;
+}
+
+int
+NDBT_Tables::createAllTables(Ndb* pNdb){
+ return createAllTables(pNdb, false);
+}
+
+int
+NDBT_Tables::createTable(Ndb* pNdb, const char* _name, bool _temp,
+ bool existsOk, NDBT_CreateTableHook f){
+
+ const NdbDictionary::Table* tab = NDBT_Tables::getTable(_name);
+ if (tab == NULL){
+ ndbout << "Could not create table " << _name
+ << ", it doesn't exist in list of tables "\
+ "that NDBT_Tables can create!" << endl;
+ return NDBT_WRONGARGS;
+ }
+
+ int r = 0;
+ do {
+ NdbDictionary::Table tmpTab(* tab);
+ tmpTab.setStoredTable(_temp ? 0 : 1);
+ if(f != 0 && f(pNdb, tmpTab, 0))
+ {
+ ndbout << "Failed to create table" << endl;
+ return NDBT_FAILED;
+ }
+ r = pNdb->getDictionary()->createTable(tmpTab);
+ if(r == -1){
+ if(!existsOk){
+ ndbout << "Error: " << pNdb->getDictionary()->getNdbError() << endl;
+ break;
+ }
+ if(pNdb->getDictionary()->getNdbError().code != 721){
+ ndbout << "Error: " << pNdb->getDictionary()->getNdbError() << endl;
+ break;
+ }
+ r = 0;
+ }
+
+ Uint32 i = 0;
+ for(i = 0; indexes[i].m_table != 0; i++){
+ if(strcmp(indexes[i].m_table, _name) != 0)
+ continue;
+ Uint32 j = 0;
+ while(indexes[i].m_indexes[j] != 0){
+ NdbDictionary::Index tmpIndx;
+ BaseString name;
+ name.assfmt("%s$NDBT_IDX%d", _name, j);
+ tmpIndx.setName(name.c_str());
+ tmpIndx.setTable(_name);
+ bool logging = !_temp;
+ if(strcmp(indexes[i].m_indexes[j], "ORDERED") == 0){
+ logging = false;
+ tmpIndx.setType(NdbDictionary::Index::OrderedIndex);
+ } else if(strcmp(indexes[i].m_indexes[j], "UNIQUE") == 0){
+ tmpIndx.setType(NdbDictionary::Index::UniqueHashIndex);
+ } else {
+ ndbout << "Unknown index type";
+ abort();
+ }
+ tmpIndx.setLogging(logging);
+
+ j++;
+ while(indexes[i].m_indexes[j] != 0){
+ tmpIndx.addIndexColumn(indexes[i].m_indexes[j]);
+ j++;
+ }
+ j++;
+ if(pNdb->getDictionary()->createIndex(tmpIndx) != 0){
+ ndbout << pNdb->getDictionary()->getNdbError() << endl;
+ return NDBT_FAILED;
+ }
+ }
+ }
+ if(f != 0 && f(pNdb, tmpTab, 1))
+ {
+ ndbout << "Failed to create table" << endl;
+ return NDBT_FAILED;
+ }
+ } while(false);
+
+ return r;
+}
+
+int
+NDBT_Tables::dropAllTables(Ndb* pNdb){
+
+ for (int i=0; i < NDBT_Tables::getNumTables(); i++){
+
+ const NdbDictionary::Table* tab = NDBT_Tables::getTable(i);
+ if (tab == NULL){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ if(pNdb->getDictionary()->dropTable(tab->getName()) == -1){
+ return NDBT_FAILED;
+ }
+ }
+ return NDBT_OK;
+}
+
+
+int
+NDBT_Tables::print(const char * _name){
+
+ const NDBT_Table * tab = (const NDBT_Table*)NDBT_Tables::getTable(_name);
+ if (tab == NULL){
+ ndbout << "Could not print table " << _name
+ << ", it doesn't exist in list of tables "
+ << "that NDBT_Tables can create!" << endl;
+ return NDBT_WRONGARGS;
+ }
+ ndbout << (* tab) << endl;
+ return NDBT_OK;
+}
+
+int
+NDBT_Tables::printAll(){
+
+ for (int i=0; i < getNumTables(); i++){
+
+ const NdbDictionary::Table* tab = getTable(i);
+ if (tab == NULL){
+ abort();
+ }
+ ndbout << (* (NDBT_Table*)tab) << endl;
+ }
+
+ return NDBT_OK;
+}
diff --git a/storage/ndb/test/src/NDBT_Test.cpp b/storage/ndb/test/src/NDBT_Test.cpp
new file mode 100644
index 00000000000..7fd92db533e
--- /dev/null
+++ b/storage/ndb/test/src/NDBT_Test.cpp
@@ -0,0 +1,1188 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 "NDBT.hpp"
+#include "NDBT_Test.hpp"
+
+#include <PortDefs.h>
+
+#include <getarg.h>
+#include <time.h>
+
+// No verbose outxput
+
+NDBT_Context::NDBT_Context(Ndb_cluster_connection& con)
+ : m_cluster_connection(con)
+{
+ tab = NULL;
+ suite = NULL;
+ testcase = NULL;
+ ndb = NULL;
+ records = 1;
+ loops = 1;
+ stopped = false;
+ remote_mgm ="";
+ propertyMutexPtr = NdbMutex_Create();
+ propertyCondPtr = NdbCondition_Create();
+}
+
+
+char * NDBT_Context::getRemoteMgm() const {
+ return remote_mgm;
+}
+void NDBT_Context::setRemoteMgm(char * mgm) {
+ remote_mgm = strdup(mgm);
+}
+
+
+NDBT_Context::~NDBT_Context(){
+ NdbCondition_Destroy(propertyCondPtr);
+ NdbMutex_Destroy(propertyMutexPtr);
+}
+
+const NdbDictionary::Table* NDBT_Context::getTab(){
+ assert(tab != NULL);
+ return tab;
+}
+
+NDBT_TestSuite* NDBT_Context::getSuite(){
+ assert(suite != NULL);
+ return suite;
+}
+
+NDBT_TestCase* NDBT_Context::getCase(){
+ assert(testcase != NULL);
+ return testcase;
+}
+
+int NDBT_Context::getNumRecords() const{
+ return records;
+}
+
+int NDBT_Context::getNumLoops() const{
+ return loops;
+}
+
+int NDBT_Context::getNoOfRunningSteps() const {
+ return testcase->getNoOfRunningSteps();
+
+}
+int NDBT_Context::getNoOfCompletedSteps() const {
+ return testcase->getNoOfCompletedSteps();
+}
+
+
+Uint32 NDBT_Context::getProperty(const char* _name, Uint32 _default){
+ Uint32 val;
+ NdbMutex_Lock(propertyMutexPtr);
+ if(!props.get(_name, &val))
+ val = _default;
+ NdbMutex_Unlock(propertyMutexPtr);
+ return val;
+}
+
+bool NDBT_Context::getPropertyWait(const char* _name, Uint32 _waitVal){
+ bool result;
+ NdbMutex_Lock(propertyMutexPtr);
+ Uint32 val =! _waitVal;
+
+ while((!props.get(_name, &val) || (props.get(_name, &val) && val != _waitVal)) &&
+ !stopped)
+ NdbCondition_Wait(propertyCondPtr,
+ propertyMutexPtr);
+ result = (val == _waitVal);
+ NdbMutex_Unlock(propertyMutexPtr);
+ return stopped;
+}
+
+const char* NDBT_Context::getProperty(const char* _name, const char* _default){
+ const char* val;
+ NdbMutex_Lock(propertyMutexPtr);
+ if(!props.get(_name, &val))
+ val = _default;
+ NdbMutex_Unlock(propertyMutexPtr);
+ return val;
+}
+
+const char* NDBT_Context::getPropertyWait(const char* _name, const char* _waitVal){
+ const char* val;
+ NdbMutex_Lock(propertyMutexPtr);
+ while(!props.get(_name, &val) && (strcmp(val, _waitVal)==0))
+ NdbCondition_Wait(propertyCondPtr,
+ propertyMutexPtr);
+
+ NdbMutex_Unlock(propertyMutexPtr);
+ return val;
+}
+
+void NDBT_Context::setProperty(const char* _name, Uint32 _val){
+ NdbMutex_Lock(propertyMutexPtr);
+ const bool b = props.put(_name, _val, true);
+ assert(b == true);
+ NdbMutex_Unlock(propertyMutexPtr);
+}
+void
+NDBT_Context::decProperty(const char * name){
+ NdbMutex_Lock(propertyMutexPtr);
+ Uint32 val = 0;
+ if(props.get(name, &val)){
+ assert(val > 0);
+ props.put(name, (val - 1), true);
+ }
+ NdbCondition_Broadcast(propertyCondPtr);
+ NdbMutex_Unlock(propertyMutexPtr);
+}
+
+void NDBT_Context::setProperty(const char* _name, const char* _val){
+ NdbMutex_Lock(propertyMutexPtr);
+ const bool b = props.put(_name, _val);
+ assert(b == true);
+ NdbMutex_Unlock(propertyMutexPtr);
+}
+
+void NDBT_Context::stopTest(){
+ NdbMutex_Lock(propertyMutexPtr);
+ g_info << "|- stopTest called" << endl;
+ stopped = true;
+ NdbCondition_Broadcast(propertyCondPtr);
+ NdbMutex_Unlock(propertyMutexPtr);
+}
+
+bool NDBT_Context::isTestStopped(){
+ NdbMutex_Lock(propertyMutexPtr);
+ bool val = stopped;
+ NdbMutex_Unlock(propertyMutexPtr);
+ return val;
+}
+
+void NDBT_Context::wait(){
+ NdbMutex_Lock(propertyMutexPtr);
+ NdbCondition_Wait(propertyCondPtr,
+ propertyMutexPtr);
+ NdbMutex_Unlock(propertyMutexPtr);
+}
+
+void NDBT_Context::wait_timeout(int msec){
+ NdbMutex_Lock(propertyMutexPtr);
+ NdbCondition_WaitTimeout(propertyCondPtr,
+ propertyMutexPtr,
+ msec);
+ NdbMutex_Unlock(propertyMutexPtr);
+}
+
+void NDBT_Context::broadcast(){
+ NdbMutex_Lock(propertyMutexPtr);
+ NdbCondition_Broadcast(propertyCondPtr);
+ NdbMutex_Unlock(propertyMutexPtr);
+}
+
+Uint32 NDBT_Context::getDbProperty(const char*){
+ abort();
+ return 0;
+}
+
+bool NDBT_Context::setDbProperty(const char*, Uint32){
+ abort();
+ return true;
+}
+
+void NDBT_Context::setTab(const NdbDictionary::Table* ptab){
+ assert(ptab != NULL);
+ tab = ptab;
+}
+
+void NDBT_Context::setSuite(NDBT_TestSuite* psuite){
+ assert(psuite != NULL);
+ suite = psuite;
+}
+
+void NDBT_Context::setCase(NDBT_TestCase* pcase){
+ assert(pcase != NULL);
+ testcase = pcase;
+}
+
+void NDBT_Context::setNumRecords(int _records){
+ records = _records;
+
+}
+
+void NDBT_Context::setNumLoops(int _loops){
+ loops = _loops;
+}
+
+NDBT_Step::NDBT_Step(NDBT_TestCase* ptest, const char* pname,
+ NDBT_TESTFUNC* pfunc): name(pname){
+ assert(pfunc != NULL);
+ func = pfunc;
+ testcase = ptest;
+ step_no = -1;
+}
+
+int NDBT_Step::execute(NDBT_Context* ctx) {
+ assert(ctx != NULL);
+
+ int result;
+
+ g_info << " |- " << name << " started [" << ctx->suite->getDate() << "]"
+ << endl;
+
+ result = setUp(ctx->m_cluster_connection);
+ if (result != NDBT_OK){
+ return result;
+ }
+
+ result = func(ctx, this);
+
+ if (result != NDBT_OK) {
+ g_err << " |- " << name << " FAILED [" << ctx->suite->getDate()
+ << "]" << endl;
+ }
+ else {
+ g_info << " |- " << name << " PASSED [" << ctx->suite->getDate() << "]"
+ << endl;
+ }
+
+ tearDown();
+
+ return result;
+}
+
+void NDBT_Step::setContext(NDBT_Context* pctx){
+ assert(pctx != NULL);
+ m_ctx = pctx;
+}
+
+NDBT_Context* NDBT_Step::getContext(){
+ assert(m_ctx != NULL);
+ return m_ctx;
+}
+
+NDBT_NdbApiStep::NDBT_NdbApiStep(NDBT_TestCase* ptest,
+ const char* pname,
+ NDBT_TESTFUNC* pfunc)
+ : NDBT_Step(ptest, pname, pfunc),
+ ndb(NULL) {
+}
+
+
+int
+NDBT_NdbApiStep::setUp(Ndb_cluster_connection& con){
+ ndb = new Ndb(&con, "TEST_DB" );
+ ndb->init(1024);
+
+ int result = ndb->waitUntilReady(300); // 5 minutes
+ if (result != 0){
+ g_err << name << ": Ndb was not ready" << endl;
+ return NDBT_FAILED;
+ }
+ return NDBT_OK;
+}
+
+void
+NDBT_NdbApiStep::tearDown(){
+ delete ndb;
+ ndb = NULL;
+}
+
+Ndb* NDBT_NdbApiStep::getNdb(){
+ assert(ndb != NULL);
+ return ndb;
+}
+
+
+NDBT_ParallelStep::NDBT_ParallelStep(NDBT_TestCase* ptest,
+ const char* pname,
+ NDBT_TESTFUNC* pfunc)
+ : NDBT_NdbApiStep(ptest, pname, pfunc) {
+}
+NDBT_Verifier::NDBT_Verifier(NDBT_TestCase* ptest,
+ const char* pname,
+ NDBT_TESTFUNC* pfunc)
+ : NDBT_NdbApiStep(ptest, pname, pfunc) {
+}
+NDBT_Initializer::NDBT_Initializer(NDBT_TestCase* ptest,
+ const char* pname,
+ NDBT_TESTFUNC* pfunc)
+ : NDBT_NdbApiStep(ptest, pname, pfunc) {
+}
+NDBT_Finalizer::NDBT_Finalizer(NDBT_TestCase* ptest,
+ const char* pname,
+ NDBT_TESTFUNC* pfunc)
+ : NDBT_NdbApiStep(ptest, pname, pfunc) {
+}
+
+NDBT_TestCase::NDBT_TestCase(NDBT_TestSuite* psuite,
+ const char* pname,
+ const char* pcomment) :
+ name(strdup(pname)) ,
+ comment(strdup(pcomment)),
+ suite(psuite)
+{
+ _name.assign(pname);
+ _comment.assign(pcomment);
+ name= _name.c_str();
+ comment= _comment.c_str();
+ assert(suite != NULL);
+}
+
+NDBT_TestCaseImpl1::NDBT_TestCaseImpl1(NDBT_TestSuite* psuite,
+ const char* pname,
+ const char* pcomment) :
+ NDBT_TestCase(psuite, pname, pcomment){
+
+ numStepsOk = 0;
+ numStepsFail = 0;
+ numStepsCompleted = 0;
+ waitThreadsMutexPtr = NdbMutex_Create();
+ waitThreadsCondPtr = NdbCondition_Create();
+}
+
+NDBT_TestCaseImpl1::~NDBT_TestCaseImpl1(){
+ NdbCondition_Destroy(waitThreadsCondPtr);
+ NdbMutex_Destroy(waitThreadsMutexPtr);
+ size_t i;
+ for(i = 0; i < initializers.size(); i++)
+ delete initializers[i];
+ initializers.clear();
+ for(i = 0; i < verifiers.size(); i++)
+ delete verifiers[i];
+ verifiers.clear();
+ for(i = 0; i < finalizers.size(); i++)
+ delete finalizers[i];
+ finalizers.clear();
+ for(i = 0; i < steps.size(); i++)
+ delete steps[i];
+ steps.clear();
+ results.clear();
+ for(i = 0; i < testTables.size(); i++)
+ delete testTables[i];
+ testTables.clear();
+ for(i = 0; i < testResults.size(); i++)
+ delete testResults[i];
+ testResults.clear();
+
+}
+
+int NDBT_TestCaseImpl1::addStep(NDBT_Step* pStep){
+ assert(pStep != NULL);
+ steps.push_back(pStep);
+ pStep->setStepNo(steps.size());
+ int res = NORESULT;
+ results.push_back(res);
+ return 0;
+}
+
+int NDBT_TestCaseImpl1::addVerifier(NDBT_Verifier* pVerifier){
+ assert(pVerifier != NULL);
+ verifiers.push_back(pVerifier);
+ return 0;
+}
+
+int NDBT_TestCaseImpl1::addInitializer(NDBT_Initializer* pInitializer){
+ assert(pInitializer != NULL);
+ initializers.push_back(pInitializer);
+ return 0;
+}
+
+int NDBT_TestCaseImpl1::addFinalizer(NDBT_Finalizer* pFinalizer){
+ assert(pFinalizer != NULL);
+ finalizers.push_back(pFinalizer);
+ return 0;
+}
+
+void NDBT_TestCaseImpl1::addTable(const char* tableName, bool isVerify) {
+ assert(tableName != NULL);
+ const NdbDictionary::Table* pTable = NDBT_Tables::getTable(tableName);
+ assert(pTable != NULL);
+ testTables.push_back(pTable);
+ isVerifyTables = isVerify;
+}
+
+bool NDBT_TestCaseImpl1::tableExists(NdbDictionary::Table* aTable) {
+ for (unsigned i = 0; i < testTables.size(); i++) {
+ if (strcasecmp(testTables[i]->getName(), aTable->getName()) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool NDBT_TestCaseImpl1::isVerify(const NdbDictionary::Table* aTable) {
+ if (testTables.size() > 0) {
+ int found = false;
+ // OK, we either exclude or include this table in the actual test
+ for (unsigned i = 0; i < testTables.size(); i++) {
+ if (strcasecmp(testTables[i]->getName(), aTable->getName()) == 0) {
+ // Found one!
+ if (isVerifyTables) {
+ // Found one to test
+ found = true;
+ } else {
+ // Skip this one!
+ found = false;
+ }
+ }
+ } // for
+ return found;
+ } else {
+ // No included or excluded test tables, i.e., all tables should be
+ // tested
+ return true;
+ }
+ return true;
+}
+
+void NDBT_TestCase::setProperty(const char* _name, Uint32 _val){
+ const bool b = props.put(_name, _val);
+ assert(b == true);
+}
+
+void NDBT_TestCase::setProperty(const char* _name, const char* _val){
+ const bool b = props.put(_name, _val);
+ assert(b == true);
+}
+
+
+void *
+runStep(void * s){
+ assert(s != NULL);
+ NDBT_Step* pStep = (NDBT_Step*)s;
+ NDBT_Context* ctx = pStep->getContext();
+ assert(ctx != NULL);
+ // Execute function
+ int res = pStep->execute(ctx);
+ if(res != NDBT_OK){
+ ctx->stopTest();
+ }
+ // Report
+ NDBT_TestCaseImpl1* pCase = (NDBT_TestCaseImpl1*)ctx->getCase();
+ assert(pCase != NULL);
+ pCase->reportStepResult(pStep, res);
+ return NULL;
+}
+
+extern "C"
+void *
+runStep_C(void * s)
+{
+ runStep(s);
+ return NULL;
+}
+
+
+void NDBT_TestCaseImpl1::startStepInThread(int stepNo, NDBT_Context* ctx){
+ NDBT_Step* pStep = steps[stepNo];
+ pStep->setContext(ctx);
+ char buf[16];
+ BaseString::snprintf(buf, sizeof(buf), "step_%d", stepNo);
+ NdbThread* pThread = NdbThread_Create(runStep_C,
+ (void**)pStep,
+ 65535,
+ buf,
+ NDB_THREAD_PRIO_LOW);
+ threads.push_back(pThread);
+}
+
+void NDBT_TestCaseImpl1::waitSteps(){
+ NdbMutex_Lock(waitThreadsMutexPtr);
+ while(numStepsCompleted != steps.size())
+ NdbCondition_Wait(waitThreadsCondPtr,
+ waitThreadsMutexPtr);
+
+ unsigned completedSteps = 0;
+ unsigned i;
+ for(i=0; i<steps.size(); i++){
+ if (results[i] != NORESULT){
+ completedSteps++;
+ if (results[i] == NDBT_OK)
+ numStepsOk++;
+ else
+ numStepsFail++;
+ }
+ }
+ assert(completedSteps == steps.size());
+ assert(completedSteps == numStepsCompleted);
+
+ NdbMutex_Unlock(waitThreadsMutexPtr);
+ void *status;
+ for(i=0; i<steps.size();i++){
+ NdbThread_WaitFor(threads[i], &status);
+ NdbThread_Destroy(&threads[i]);
+ }
+ threads.clear();
+}
+
+
+int
+NDBT_TestCaseImpl1::getNoOfRunningSteps() const {
+ return steps.size() - getNoOfCompletedSteps();
+}
+
+int
+NDBT_TestCaseImpl1::getNoOfCompletedSteps() const {
+ return numStepsCompleted;
+}
+
+void NDBT_TestCaseImpl1::reportStepResult(const NDBT_Step* pStep, int result){
+ NdbMutex_Lock(waitThreadsMutexPtr);
+ assert(pStep != NULL);
+ for (unsigned i = 0; i < steps.size(); i++){
+ if(steps[i] != NULL && steps[i] == pStep){
+ results[i] = result;
+ numStepsCompleted++;
+ }
+ }
+ if(numStepsCompleted == steps.size()){
+ NdbCondition_Signal(waitThreadsCondPtr);
+ }
+ NdbMutex_Unlock(waitThreadsMutexPtr);
+}
+
+
+int NDBT_TestCase::execute(NDBT_Context* ctx){
+ int res;
+
+ ndbout << "- " << name << " started [" << ctx->suite->getDate()
+ << "]" << endl;
+
+ ctx->setCase(this);
+
+ // Copy test case properties to ctx
+ Properties::Iterator it(&props);
+ for(const char * key = it.first(); key != 0; key = it.next()){
+
+ PropertiesType pt;
+ const bool b = props.getTypeOf(key, &pt);
+ assert(b == true);
+ switch(pt){
+ case PropertiesType_Uint32:{
+ Uint32 val;
+ props.get(key, &val);
+ ctx->setProperty(key, val);
+ break;
+ }
+ case PropertiesType_char:{
+ const char * val;
+ props.get(key, &val);
+ ctx->setProperty(key, val);
+ break;
+ }
+ default:
+ abort();
+ }
+ }
+
+ // start timer so that we get a time even if
+ // test case consist only of initializer
+ startTimer(ctx);
+
+ if ((res = runInit(ctx)) == NDBT_OK){
+ // If initialiser is ok, run steps
+
+ res = runSteps(ctx);
+ if (res == NDBT_OK){
+ // If steps is ok, run verifier
+ res = runVerifier(ctx);
+ }
+
+ }
+
+ stopTimer(ctx);
+ printTimer(ctx);
+
+ // Always run finalizer to clean up db
+ runFinal(ctx);
+
+ if (res == NDBT_OK) {
+ ndbout << "- " << name << " PASSED [" << ctx->suite->getDate() << "]"
+ << endl;
+ }
+ else {
+ ndbout << "- " << name << " FAILED [" << ctx->suite->getDate() << "]"
+ << endl;
+ }
+ return res;
+}
+
+void NDBT_TestCase::startTimer(NDBT_Context* ctx){
+ timer.doStart();
+}
+
+void NDBT_TestCase::stopTimer(NDBT_Context* ctx){
+ timer.doStop();
+}
+
+void NDBT_TestCase::printTimer(NDBT_Context* ctx){
+ if (suite->timerIsOn()){
+ g_info << endl;
+ timer.printTestTimer(ctx->getNumLoops(), ctx->getNumRecords());
+ }
+}
+
+int NDBT_TestCaseImpl1::runInit(NDBT_Context* ctx){
+ int res = NDBT_OK;
+ for (unsigned i = 0; i < initializers.size(); i++){
+ initializers[i]->setContext(ctx);
+ res = initializers[i]->execute(ctx);
+ if (res != NDBT_OK)
+ break;
+ }
+ return res;
+}
+
+int NDBT_TestCaseImpl1::runSteps(NDBT_Context* ctx){
+ int res = NDBT_OK;
+
+ // Reset variables
+ numStepsOk = 0;
+ numStepsFail = 0;
+ numStepsCompleted = 0;
+ unsigned i;
+ for (i = 0; i < steps.size(); i++)
+ startStepInThread(i, ctx);
+ waitSteps();
+
+ for(i = 0; i < steps.size(); i++)
+ if (results[i] != NDBT_OK)
+ res = NDBT_FAILED;
+ return res;
+}
+
+int NDBT_TestCaseImpl1::runVerifier(NDBT_Context* ctx){
+ int res = NDBT_OK;
+ for (unsigned i = 0; i < verifiers.size(); i++){
+ verifiers[i]->setContext(ctx);
+ res = verifiers[i]->execute(ctx);
+ if (res != NDBT_OK)
+ break;
+ }
+ return res;
+}
+
+int NDBT_TestCaseImpl1::runFinal(NDBT_Context* ctx){
+ int res = NDBT_OK;
+ for (unsigned i = 0; i < finalizers.size(); i++){
+ finalizers[i]->setContext(ctx);
+ res = finalizers[i]->execute(ctx);
+ if (res != NDBT_OK)
+ break;
+ }
+ return res;
+}
+
+
+void NDBT_TestCaseImpl1::saveTestResult(const NdbDictionary::Table* ptab,
+ int result){
+ testResults.push_back(new NDBT_TestCaseResult(ptab->getName(),
+ result,
+ timer.elapsedTime()));
+}
+
+void NDBT_TestCaseImpl1::printTestResult(){
+
+ char buf[255];
+ ndbout << name<<endl;
+
+ for (unsigned i = 0; i < testResults.size(); i++){
+ NDBT_TestCaseResult* tcr = testResults[i];
+ const char* res;
+ if (tcr->getResult() == NDBT_OK)
+ res = "OK";
+ else if (tcr->getResult() == NDBT_FAILED)
+ res = "FAIL";
+ else if (tcr->getResult() == FAILED_TO_CREATE)
+ res = "FAILED TO CREATE TABLE";
+ else if (tcr->getResult() == FAILED_TO_DISCOVER)
+ res = "FAILED TO DISCOVER TABLE";
+ BaseString::snprintf(buf, 255," %-10s %-5s %-20s", tcr->getName(), res, tcr->getTimeStr());
+ ndbout << buf<<endl;
+ }
+}
+
+
+
+
+
+NDBT_TestSuite::NDBT_TestSuite(const char* pname):name(pname){
+ numTestsOk = 0;
+ numTestsFail = 0;
+ numTestsExecuted = 0;
+ records = 0;
+ loops = 0;
+ createTable = true;
+}
+
+
+NDBT_TestSuite::~NDBT_TestSuite(){
+ for(unsigned i=0; i<tests.size(); i++){
+ delete tests[i];
+ }
+ tests.clear();
+}
+
+void NDBT_TestSuite::setCreateTable(bool _flag){
+ createTable = _flag;
+}
+
+bool NDBT_TestSuite::timerIsOn(){
+ return (timer != 0);
+}
+
+int NDBT_TestSuite::addTest(NDBT_TestCase* pTest){
+ assert(pTest != NULL);
+ tests.push_back(pTest);
+ return 0;
+}
+
+int NDBT_TestSuite::executeAll(Ndb_cluster_connection& con,
+ const char* _testname){
+
+ if(tests.size() == 0)
+ return NDBT_FAILED;
+ Ndb ndb(&con, "TEST_DB");
+ ndb.init(1024);
+
+ int result = ndb.waitUntilReady(500); // 5 minutes
+ if (result != 0){
+ g_err << name <<": Ndb was not ready" << endl;
+ return NDBT_FAILED;
+ }
+
+ ndbout << name << " started [" << getDate() << "]" << endl;
+
+ testSuiteTimer.doStart();
+
+ for (int t=0; t < NDBT_Tables::getNumTables(); t++){
+ const NdbDictionary::Table* ptab = NDBT_Tables::getTable(t);
+ ndbout << "|- " << ptab->getName() << endl;
+ execute(con, &ndb, ptab, _testname);
+ }
+ testSuiteTimer.doStop();
+ return reportAllTables(_testname);
+}
+
+int
+NDBT_TestSuite::executeOne(Ndb_cluster_connection& con,
+ const char* _tabname, const char* _testname){
+
+ if(tests.size() == 0)
+ return NDBT_FAILED;
+ Ndb ndb(&con, "TEST_DB");
+ ndb.init(1024);
+
+ int result = ndb.waitUntilReady(300); // 5 minutes
+ if (result != 0){
+ g_err << name <<": Ndb was not ready" << endl;
+ return NDBT_FAILED;
+ }
+
+ ndbout << name << " started [" << getDate() << "]" << endl;
+
+ const NdbDictionary::Table* ptab = NDBT_Tables::getTable(_tabname);
+ if (ptab == NULL)
+ return NDBT_FAILED;
+
+ ndbout << "|- " << ptab->getName() << endl;
+
+ execute(con, &ndb, ptab, _testname);
+
+ if (numTestsFail > 0){
+ return NDBT_FAILED;
+ }else{
+ return NDBT_OK;
+ }
+}
+
+void NDBT_TestSuite::execute(Ndb_cluster_connection& con,
+ Ndb* ndb, const NdbDictionary::Table* pTab,
+ const char* _testname){
+ int result;
+
+
+ for (unsigned t = 0; t < tests.size(); t++){
+
+ if (_testname != NULL &&
+ strcasecmp(tests[t]->getName(), _testname) != 0)
+ continue;
+
+ if (tests[t]->isVerify(pTab) == false) {
+ continue;
+ }
+
+ tests[t]->initBeforeTest();
+
+ NdbDictionary::Dictionary* pDict = ndb->getDictionary();
+ const NdbDictionary::Table* pTab2 = pDict->getTable(pTab->getName());
+ if (createTable == true){
+
+ if(pTab2 != 0 && pDict->dropTable(pTab->getName()) != 0){
+ numTestsFail++;
+ numTestsExecuted++;
+ g_err << "ERROR0: Failed to drop table " << pTab->getName() << endl;
+ tests[t]->saveTestResult(pTab, FAILED_TO_CREATE);
+ continue;
+ }
+
+ if(NDBT_Tables::createTable(ndb, pTab->getName()) != 0){
+ numTestsFail++;
+ numTestsExecuted++;
+ g_err << "ERROR1: Failed to create table " << pTab->getName()
+ << pDict->getNdbError() << endl;
+ tests[t]->saveTestResult(pTab, FAILED_TO_CREATE);
+ continue;
+ }
+ pTab2 = pDict->getTable(pTab->getName());
+ } else if(!pTab2) {
+ pTab2 = pTab;
+ }
+
+ ctx = new NDBT_Context(con);
+ ctx->setTab(pTab2);
+ ctx->setNumRecords(records);
+ ctx->setNumLoops(loops);
+ if(remote_mgm != NULL)
+ ctx->setRemoteMgm(remote_mgm);
+ ctx->setSuite(this);
+
+ result = tests[t]->execute(ctx);
+ tests[t]->saveTestResult(pTab, result);
+ if (result != NDBT_OK)
+ numTestsFail++;
+ else
+ numTestsOk++;
+ numTestsExecuted++;
+
+ if (result == NDBT_OK && createTable == true){
+ pDict->dropTable(pTab->getName());
+ }
+
+ delete ctx;
+ }
+}
+
+
+
+
+int
+NDBT_TestSuite::report(const char* _tcname){
+ int result;
+ ndbout << "Completed " << name << " [" << getDate() << "]" << endl;
+ printTestCaseSummary(_tcname);
+ ndbout << numTestsExecuted << " test(s) executed" << endl;
+ ndbout << numTestsOk << " test(s) OK"
+ << endl;
+ if(numTestsFail > 0)
+ ndbout << numTestsFail << " test(s) failed"
+ << endl;
+ testSuiteTimer.printTotalTime();
+ if (numTestsFail > 0 || numTestsExecuted == 0){
+ result = NDBT_FAILED;
+ }else{
+ result = NDBT_OK;
+ }
+ return result;
+}
+
+void NDBT_TestSuite::printTestCaseSummary(const char* _tcname){
+ ndbout << "= SUMMARY OF TEST EXECUTION ==============" << endl;
+ for (unsigned t = 0; t < tests.size(); t++){
+ if (_tcname != NULL &&
+ strcasecmp(tests[t]->getName(), _tcname) != 0)
+ continue;
+
+ tests[t]->printTestResult();
+ }
+ ndbout << "==========================================" << endl;
+}
+
+int NDBT_TestSuite::reportAllTables(const char* _testname){
+ int result;
+ ndbout << "Completed running test [" << getDate() << "]" << endl;
+ const int totalNumTests = numTestsExecuted;
+ printTestCaseSummary(_testname);
+ ndbout << numTestsExecuted<< " test(s) executed" << endl;
+ ndbout << numTestsOk << " test(s) OK("
+ <<(int)(((float)numTestsOk/totalNumTests)*100.0) <<"%)"
+ << endl;
+ if(numTestsFail > 0)
+ ndbout << numTestsFail << " test(s) failed("
+ <<(int)(((float)numTestsFail/totalNumTests)*100.0) <<"%)"
+ << endl;
+ testSuiteTimer.printTotalTime();
+ if (numTestsExecuted > 0){
+ if (numTestsFail > 0){
+ result = NDBT_FAILED;
+ }else{
+ result = NDBT_OK;
+ }
+ } else {
+ result = NDBT_FAILED;
+ }
+ return result;
+}
+
+int NDBT_TestSuite::execute(int argc, const char** argv){
+ int res = NDBT_FAILED;
+ /* Arguments:
+ Run only a subset of tests
+ -n testname Which test to run
+ Recommendations to test functions:
+ --records Number of records to use(default: 10000)
+ --loops Number of loops to execute in the test(default: 100)
+
+ Other parameters should:
+ * be calculated from the above two parameters
+ * be divided into different test cases, ex. one testcase runs
+ with FragmentType = Single and another perfoms the same
+ test with FragmentType = Large
+ * let the test case iterate over all/subset of appropriate parameters
+ ex. iterate over FragmentType = Single to FragmentType = AllLarge
+
+ Remeber that the intention is that it should be _easy_ to run
+ a complete test suite without any greater knowledge of what
+ should be tested ie. keep arguments at a minimum
+ */
+ int _records = 1000;
+ int _loops = 5;
+ int _timer = 0;
+ char * _remote_mgm =NULL;
+ char* _testname = NULL;
+ const char* _tabname = NULL;
+ int _print = false;
+ int _print_html = false;
+
+ int _print_cases = false;
+ int _verbose = false;
+#ifndef DBUG_OFF
+ const char *debug_option= 0;
+#endif
+
+ struct getargs args[] = {
+ { "print", '\0', arg_flag, &_print, "Print execution tree", "" },
+ { "print_html", '\0', arg_flag, &_print_html, "Print execution tree in html table format", "" },
+ { "print_cases", '\0', arg_flag, &_print_cases, "Print list of test cases", "" },
+ { "records", 'r', arg_integer, &_records, "Number of records", "records" },
+ { "loops", 'l', arg_integer, &_loops, "Number of loops", "loops" },
+ { "testname", 'n', arg_string, &_testname, "Name of test to run", "testname" },
+ { "remote_mgm", 'm', arg_string, &_remote_mgm,
+ "host:port to mgmsrv of remote cluster", "host:port" },
+ { "timer", 't', arg_flag, &_timer, "Print execution time", "time" },
+#ifndef DBUG_OFF
+ { "debug", 0, arg_string, &debug_option,
+ "Specify debug options e.g. d:t:i:o,out.trace", "options" },
+#endif
+ { "verbose", 'v', arg_flag, &_verbose, "Print verbose status", "verbose" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+
+ if(getarg(args, num_args, argc, argv, &optind)) {
+ arg_printusage(args, num_args, argv[0], "tabname1 tabname2 ... tabnameN\n");
+ return NDBT_WRONGARGS;
+ }
+
+#ifndef DBUG_OFF
+ if (debug_option)
+ DBUG_PUSH(debug_option);
+#endif
+
+ // Check if table name is supplied
+ if (argv[optind] != NULL)
+ _tabname = argv[optind];
+
+ if (_print == true){
+ printExecutionTree();
+ return 0;
+ }
+
+ if (_print_html == true){
+ printExecutionTreeHTML();
+ return 0;
+ }
+
+ if (_print_cases == true){
+ printCases();
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ if (_verbose)
+ setOutputLevel(2); // Show g_info
+ else
+ setOutputLevel(0); // Show only g_err ?
+
+ remote_mgm = _remote_mgm;
+ records = _records;
+ loops = _loops;
+ timer = _timer;
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1))
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ if(optind == argc){
+ // No table specified
+ res = executeAll(con, _testname);
+ } else {
+ testSuiteTimer.doStart();
+ for(int i = optind; i<argc; i++){
+ executeOne(con, argv[i], _testname);
+ }
+ testSuiteTimer.doStop();
+ res = report(_testname);
+ }
+
+ return NDBT_ProgramExit(res);
+}
+
+
+
+void NDBT_TestSuite::printExecutionTree(){
+ ndbout << "Testsuite: " << name << endl;
+ for (unsigned t = 0; t < tests.size(); t++){
+ tests[t]->print();
+ ndbout << endl;
+ }
+}
+
+void NDBT_TestSuite::printExecutionTreeHTML(){
+ ndbout << "<tr>" << endl;
+ ndbout << "<td><h3>" << name << "</h3></td>" << endl;
+ ndbout << "</tr>" << endl;
+ for (unsigned t = 0; t < tests.size(); t++){
+ tests[t]->printHTML();
+ ndbout << endl;
+ }
+
+}
+
+void NDBT_TestSuite::printCases(){
+ ndbout << "# Testsuite: " << name << endl;
+ ndbout << "# Number of tests: " << tests.size() << endl;
+ for (unsigned t = 0; t < tests.size(); t++){
+ ndbout << name << " -n " << tests[t]->getName() << endl;
+ }
+}
+
+const char* NDBT_TestSuite::getDate(){
+ static char theTime[128];
+ struct tm* tm_now;
+ time_t now;
+ now = time((time_t*)NULL);
+#ifdef NDB_WIN32
+ tm_now = localtime(&now);
+#else
+ tm_now = gmtime(&now);
+#endif
+
+ BaseString::snprintf(theTime, 128,
+ "%d-%.2d-%.2d %.2d:%.2d:%.2d",
+ tm_now->tm_year + 1900,
+ tm_now->tm_mon + 1,
+ tm_now->tm_mday,
+ tm_now->tm_hour,
+ tm_now->tm_min,
+ tm_now->tm_sec);
+
+ return theTime;
+}
+
+void NDBT_TestCaseImpl1::printHTML(){
+
+ ndbout << "<tr><td>&nbsp;</td>" << endl;
+ ndbout << "<td name=tc>" << endl << name << "</td><td width=70%>"
+ << comment << "</td></tr>" << endl;
+}
+
+void NDBT_TestCaseImpl1::print(){
+ ndbout << "Test case: " << name << endl;
+ ndbout << "Description: "<< comment << endl;
+
+ ndbout << "Parameters: " << endl;
+
+ Properties::Iterator it(&props);
+ for(const char * key = it.first(); key != 0; key = it.next()){
+ PropertiesType pt;
+ const bool b = props.getTypeOf(key, &pt);
+ assert(b == true);
+ switch(pt){
+ case PropertiesType_Uint32:{
+ Uint32 val;
+ props.get(key, &val);
+ ndbout << " " << key << ": " << val << endl;
+ break;
+ }
+ case PropertiesType_char:{
+ const char * val;
+ props.get(key, &val);
+ ndbout << " " << key << ": " << val << endl;
+ break;
+ }
+ default:
+ abort();
+ }
+ }
+ unsigned i;
+ for(i=0; i<initializers.size(); i++){
+ ndbout << "Initializers[" << i << "]: " << endl;
+ initializers[i]->print();
+ }
+ for(i=0; i<steps.size(); i++){
+ ndbout << "Step[" << i << "]: " << endl;
+ steps[i]->print();
+ }
+ for(i=0; i<verifiers.size(); i++){
+ ndbout << "Verifier[" << i << "]: " << endl;
+ verifiers[i]->print();
+ }
+ for(i=0; i<finalizers.size(); i++){
+ ndbout << "Finalizer[" << i << "]: " << endl;
+ finalizers[i]->print();
+ }
+
+}
+
+void NDBT_Step::print(){
+ ndbout << " "<< name << endl;
+
+}
+
+void
+NDBT_Context::sync_down(const char * key){
+ Uint32 threads = getProperty(key, (unsigned)0);
+ if(threads){
+ decProperty(key);
+ }
+}
+
+void
+NDBT_Context::sync_up_and_wait(const char * key, Uint32 value){
+ setProperty(key, value);
+ getPropertyWait(key, (unsigned)0);
+}
+
+template class Vector<NDBT_TestCase*>;
+template class Vector<NDBT_TestCaseResult*>;
+template class Vector<NDBT_Step*>;
+template class Vector<NdbThread*>;
+template class Vector<NDBT_Verifier*>;
+template class Vector<NDBT_Initializer*>;
+template class Vector<NDBT_Finalizer*>;
+template class Vector<const NdbDictionary::Table*>;
diff --git a/storage/ndb/test/src/NdbBackup.cpp b/storage/ndb/test/src/NdbBackup.cpp
new file mode 100644
index 00000000000..bc5baeae4b5
--- /dev/null
+++ b/storage/ndb/test/src/NdbBackup.cpp
@@ -0,0 +1,440 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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/DumpStateOrd.hpp>
+#include <NdbBackup.hpp>
+#include <NdbOut.hpp>
+#include <NDBT_Output.hpp>
+#include <NdbConfig.h>
+#include <ndb_version.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <random.h>
+#include <NdbTick.h>
+
+#define CHECK(b, m) { int _xx = b; if (!(_xx)) { \
+ ndbout << "ERR: "<< m \
+ << " " << "File: " << __FILE__ \
+ << " (Line: " << __LINE__ << ")" << "- " << _xx << endl; \
+ return NDBT_FAILED; } }
+
+#include <ConfigRetriever.hpp>
+#include <mgmapi.h>
+#include <mgmapi_config_parameters.h>
+#include <mgmapi_configuration.hpp>
+
+int
+NdbBackup::start(unsigned int & _backup_id){
+
+
+ if (!isConnected())
+ return -1;
+
+ ndb_mgm_reply reply;
+ reply.return_code = 0;
+
+ if (ndb_mgm_start_backup(handle,
+ 2, // wait until completed
+ &_backup_id,
+ &reply) == -1) {
+ g_err << "Could not start backup " << endl;
+ g_err << "Error: " << reply.message << endl;
+ return -1;
+ }
+
+ if(reply.return_code != 0){
+ g_err << "PLEASE CHECK CODE NdbBackup.cpp line=" << __LINE__ << endl;
+ g_err << "Error: " << reply.message << endl;
+ return reply.return_code;
+ }
+ return 0;
+}
+
+
+const char *
+NdbBackup::getBackupDataDirForNode(int _node_id){
+
+ /**
+ * Fetch configuration from management server
+ */
+ ndb_mgm_configuration *p;
+ if (connect())
+ return NULL;
+
+ if ((p = ndb_mgm_get_configuration(handle, 0)) == 0)
+ {
+ const char * s= ndb_mgm_get_latest_error_msg(handle);
+ if(s == 0)
+ s = "No error given!";
+
+ ndbout << "Could not fetch configuration" << endl;
+ ndbout << s << endl;
+ return NULL;
+ }
+
+ /**
+ * Setup cluster configuration data
+ */
+ ndb_mgm_configuration_iterator iter(* p, CFG_SECTION_NODE);
+ if (iter.find(CFG_NODE_ID, _node_id)){
+ ndbout << "Invalid configuration fetched, DB missing" << endl;
+ return NULL;
+ }
+
+ unsigned int type = NODE_TYPE_DB + 1;
+ if(iter.get(CFG_TYPE_OF_SECTION, &type) || type != NODE_TYPE_DB){
+ ndbout <<"type = " << type << endl;
+ ndbout <<"Invalid configuration fetched, I'm wrong type of node" << endl;
+ return NULL;
+ }
+
+ const char * path;
+ if (iter.get(CFG_DB_BACKUP_DATADIR, &path)){
+ ndbout << "BackupDataDir not found" << endl;
+ return NULL;
+ }
+
+ return path;
+
+}
+
+int
+NdbBackup::execRestore(bool _restore_data,
+ bool _restore_meta,
+ int _node_id,
+ unsigned _backup_id){
+ const int buf_len = 1000;
+ char buf[buf_len];
+
+ ndbout << "getBackupDataDir "<< _node_id <<endl;
+
+ const char* path = getBackupDataDirForNode(_node_id);
+ if (path == NULL)
+ return -1;
+
+ ndbout << "getHostName "<< _node_id <<endl;
+ const char *host;
+ if (!getHostName(_node_id, &host)){
+ return -1;
+ }
+
+ /*
+ * Copy backup files to local dir
+ */
+
+ BaseString::snprintf(buf, buf_len,
+ "scp %s:%s/BACKUP/BACKUP-%d/BACKUP-%d*.%d.* .",
+ host, path,
+ _backup_id,
+ _backup_id,
+ _node_id);
+
+ ndbout << "buf: "<< buf <<endl;
+ int res = system(buf);
+
+ ndbout << "scp res: " << res << endl;
+
+ BaseString::snprintf(buf, 255, "%sndb_restore -c \"%s:%d\" -n %d -b %d %s %s .",
+#if 1
+ "",
+#else
+ "valgrind --leak-check=yes -v "
+#endif
+ ndb_mgm_get_connected_host(handle),
+ ndb_mgm_get_connected_port(handle),
+ _node_id,
+ _backup_id,
+ _restore_data?"-r":"",
+ _restore_meta?"-m":"");
+
+ ndbout << "buf: "<< buf <<endl;
+ res = system(buf);
+
+ ndbout << "ndb_restore res: " << res << endl;
+
+ return res;
+
+}
+
+int
+NdbBackup::restore(unsigned _backup_id){
+
+ if (!isConnected())
+ return -1;
+
+ if (getStatus() != 0)
+ return -1;
+
+ int res;
+
+ // restore metadata first and data for first node
+ res = execRestore(true, true, ndbNodes[0].node_id, _backup_id);
+
+ // Restore data once for each node
+ for(size_t i = 1; i < ndbNodes.size(); i++){
+ res = execRestore(true, false, ndbNodes[i].node_id, _backup_id);
+ }
+
+ return 0;
+}
+
+// Master failure
+int
+NFDuringBackupM_codes[] = {
+ 10003,
+ 10004,
+ 10005,
+ 10007,
+ 10008,
+ 10009,
+ 10010,
+ 10012,
+ 10013
+};
+
+// Slave failure
+int
+NFDuringBackupS_codes[] = {
+ 10014,
+ 10015,
+ 10016,
+ 10017,
+ 10018,
+ 10020
+};
+
+// Master takeover etc...
+int
+NFDuringBackupSL_codes[] = {
+ 10001,
+ 10002,
+ 10021
+};
+
+int
+NdbBackup::NFMaster(NdbRestarter& _restarter){
+ const int sz = sizeof(NFDuringBackupM_codes)/sizeof(NFDuringBackupM_codes[0]);
+ return NF(_restarter, NFDuringBackupM_codes, sz, true);
+}
+
+int
+NdbBackup::NFMasterAsSlave(NdbRestarter& _restarter){
+ const int sz = sizeof(NFDuringBackupS_codes)/sizeof(NFDuringBackupS_codes[0]);
+ return NF(_restarter, NFDuringBackupS_codes, sz, true);
+}
+
+int
+NdbBackup::NFSlave(NdbRestarter& _restarter){
+ const int sz = sizeof(NFDuringBackupS_codes)/sizeof(NFDuringBackupS_codes[0]);
+ return NF(_restarter, NFDuringBackupS_codes, sz, false);
+}
+
+int
+NdbBackup::NF(NdbRestarter& _restarter, int *NFDuringBackup_codes, const int sz, bool onMaster){
+ {
+ int nodeId = _restarter.getMasterNodeId();
+
+ CHECK(_restarter.restartOneDbNode(nodeId, false, true, true) == 0,
+ "Could not restart node "<< nodeId);
+
+ CHECK(_restarter.waitNodesNoStart(&nodeId, 1) == 0,
+ "waitNodesNoStart failed");
+
+ CHECK(_restarter.startNodes(&nodeId, 1) == 0,
+ "failed to start node");
+
+ NdbSleep_SecSleep(10);
+ }
+
+ CHECK(_restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+
+ int nNodes = _restarter.getNumDbNodes();
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+
+ for(int i = 0; i<sz; i++){
+
+ int error = NFDuringBackup_codes[i];
+ unsigned int backupId;
+
+ const int masterNodeId = _restarter.getMasterNodeId();
+ CHECK(masterNodeId > 0, "getMasterNodeId failed");
+ int nodeId;
+
+ nodeId = masterNodeId;
+ if (!onMaster) {
+ int randomId;
+ while (nodeId == masterNodeId) {
+ randomId = myRandom48(nNodes);
+ nodeId = _restarter.getDbNodeId(randomId);
+ }
+ }
+
+ g_err << "NdbBackup::NF node = " << nodeId
+ << " error code = " << error << " masterNodeId = "
+ << masterNodeId << endl;
+
+
+ int val = DumpStateOrd::CmvmiSetRestartOnErrorInsert;
+ CHECK(_restarter.dumpStateOneNode(nodeId, &val, 1) == 0,
+ "failed to set RestartOnErrorInsert");
+ CHECK(_restarter.insertErrorInNode(nodeId, error) == 0,
+ "failed to set error insert");
+
+ g_info << "error inserted" << endl;
+
+ g_info << "starting backup" << endl;
+ int r = start(backupId);
+ g_info << "r = " << r
+ << " (which should fail) started with id = " << backupId << endl;
+ if (r == 0) {
+ g_err << "Backup should have failed on error_insertion " << error << endl
+ << "Master = " << masterNodeId << "Node = " << nodeId << endl;
+ }
+
+ CHECK(_restarter.waitNodesNoStart(&nodeId, 1) == 0,
+ "waitNodesNoStart failed");
+
+ g_info << "number of nodes running " << _restarter.getNumDbNodes() << endl;
+
+ if (_restarter.getNumDbNodes() != nNodes) {
+ g_err << "Failure: cluster not up" << endl;
+ return NDBT_FAILED;
+ }
+
+ NdbSleep_SecSleep(1);
+
+ g_info << "starting new backup" << endl;
+ CHECK(start(backupId) == 0,
+ "failed to start backup");
+ g_info << "(which should succeed) started with id = " << backupId << endl;
+
+ g_info << "starting node" << endl;
+ CHECK(_restarter.startNodes(&nodeId, 1) == 0,
+ "failed to start node");
+
+ CHECK(_restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+ g_info << "node started" << endl;
+
+ CHECK(_restarter.insertErrorInNode(nodeId, 10099) == 0,
+ "failed to set error insert");
+ }
+
+ return NDBT_OK;
+}
+
+int
+FailS_codes[] = {
+ 10023,
+ 10024,
+ 10025,
+ 10026,
+ 10027,
+ 10028,
+ 10029,
+ 10030,
+ 10031
+};
+
+int
+FailM_codes[] = {
+ 10023,
+ 10024,
+ 10025,
+ 10026,
+ 10027,
+ 10028,
+ 10029,
+ 10030,
+ 10031
+};
+
+int
+NdbBackup::FailMaster(NdbRestarter& _restarter){
+ const int sz = sizeof(FailM_codes)/sizeof(FailM_codes[0]);
+ return Fail(_restarter, FailM_codes, sz, true);
+}
+
+int
+NdbBackup::FailMasterAsSlave(NdbRestarter& _restarter){
+ const int sz = sizeof(FailS_codes)/sizeof(FailS_codes[0]);
+ return Fail(_restarter, FailS_codes, sz, true);
+}
+
+int
+NdbBackup::FailSlave(NdbRestarter& _restarter){
+ const int sz = sizeof(FailS_codes)/sizeof(FailS_codes[0]);
+ return Fail(_restarter, FailS_codes, sz, false);
+}
+
+int
+NdbBackup::Fail(NdbRestarter& _restarter, int *Fail_codes, const int sz, bool onMaster){
+
+ CHECK(_restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+
+ int nNodes = _restarter.getNumDbNodes();
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+
+ for(int i = 0; i<sz; i++){
+ int error = Fail_codes[i];
+ unsigned int backupId;
+
+ const int masterNodeId = _restarter.getMasterNodeId();
+ CHECK(masterNodeId > 0, "getMasterNodeId failed");
+ int nodeId;
+
+ nodeId = masterNodeId;
+ if (!onMaster) {
+ int randomId;
+ while (nodeId == masterNodeId) {
+ randomId = myRandom48(nNodes);
+ nodeId = _restarter.getDbNodeId(randomId);
+ }
+ }
+
+ g_err << "NdbBackup::Fail node = " << nodeId
+ << " error code = " << error << " masterNodeId = "
+ << masterNodeId << endl;
+
+ CHECK(_restarter.insertErrorInNode(nodeId, error) == 0,
+ "failed to set error insert");
+
+ g_info << "error inserted" << endl;
+ g_info << "waiting some before starting backup" << endl;
+
+ g_info << "starting backup" << endl;
+ int r = start(backupId);
+ g_info << "r = " << r
+ << " (which should fail) started with id = " << backupId << endl;
+ if (r == 0) {
+ g_err << "Backup should have failed on error_insertion " << error << endl
+ << "Master = " << masterNodeId << "Node = " << nodeId << endl;
+ }
+
+ CHECK(_restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+
+ CHECK(_restarter.insertErrorInNode(nodeId, 10099) == 0,
+ "failed to set error insert");
+ }
+
+ return NDBT_OK;
+}
+
diff --git a/storage/ndb/test/src/NdbConfig.cpp b/storage/ndb/test/src/NdbConfig.cpp
new file mode 100644
index 00000000000..2fb466d1b8f
--- /dev/null
+++ b/storage/ndb/test/src/NdbConfig.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 "NdbConfig.hpp"
+#include <NdbOut.hpp>
+#include <NDBT_Output.hpp>
+#include <assert.h>
+#include <NdbConfig.h>
+#include <ConfigRetriever.hpp>
+#include <ndb_version.h>
+#include <mgmapi.h>
+#include <mgmapi_config_parameters.h>
+#include <mgmapi_configuration.hpp>
+
+bool
+NdbConfig::getHostName(unsigned int node_id, const char ** hostname) {
+
+ ndb_mgm_configuration * p = getConfig();
+ if(p == 0){
+ return false;
+ }
+
+ /**
+ * Setup cluster configuration data
+ */
+ ndb_mgm_configuration_iterator iter(* p, CFG_SECTION_NODE);
+ if (iter.find(CFG_NODE_ID, node_id)){
+ ndbout << "Invalid configuration fetched, DB missing" << endl;
+ return false;
+ }
+
+ if (iter.get(CFG_NODE_HOST, hostname)){
+ ndbout << "Host not found" << endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool
+NdbConfig::getProperty(unsigned nodeid,
+ unsigned type, unsigned key, Uint32 * val){
+ ndb_mgm_configuration * p = getConfig();
+ if(p == 0){
+ return false;
+ }
+
+ /**
+ * Setup cluster configuration data
+ */
+ ndb_mgm_configuration_iterator iter(* p, CFG_SECTION_NODE);
+ if (iter.find(CFG_NODE_ID, nodeid)){
+ ndbout << "Invalid configuration fetched, DB missing" << endl;
+ return false;
+ }
+
+ unsigned _type;
+ if (iter.get(CFG_TYPE_OF_SECTION, &_type) || type != _type){
+ ndbout << "No such node in configuration" << endl;
+ return false;
+ }
+
+ if (iter.get(key, val)){
+ ndbout << "No such key: " << key << " in configuration" << endl;
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/storage/ndb/test/src/NdbGrep.cpp b/storage/ndb/test/src/NdbGrep.cpp
new file mode 100644
index 00000000000..6c0c9cabfcb
--- /dev/null
+++ b/storage/ndb/test/src/NdbGrep.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 <signaldata/DumpStateOrd.hpp>
+#include <NdbGrep.hpp>
+#include <NdbOut.hpp>
+#include <NDBT_Output.hpp>
+#include <NdbConfig.h>
+#include <ConfigRetriever.hpp>
+#include <ndb_version.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <random.h>
+#include <NdbTick.h>
+
+#define CHECK(b, m) { int _xx = b; if (!(_xx)) { \
+ ndbout << "ERR: "<< m \
+ << " " << "File: " << __FILE__ \
+ << " (Line: " << __LINE__ << ")" << "- " << _xx << endl; \
+ return NDBT_FAILED; } }
+
+
+int
+NdbGrep::start(){
+
+ return 1;
+}
+
+int
+NdbGrep::stop(){
+
+ return 1;
+}
+
+
+int
+NdbGrep::query(){
+
+ return 1;
+}
+
+
+int
+NdbGrep::verify(NDBT_Context * ctx){
+
+ if (!isConnected())
+ return -1;
+
+ char cheat_table[255];
+ BaseString::snprintf(cheat_table, 255, "TEST_DB/def/%s",ctx->getTab()->getName());
+
+ char buf[255];
+ BaseString::snprintf(buf, 255, "testGrepVerify -c \"nodeid=%d;host=%s\" -t %s -r %d",
+ 4, //cheat. Hardcoded nodeid....
+ ctx->getRemoteMgm(),
+ cheat_table,
+ ctx->getNumRecords());
+
+
+ ndbout << "buf: "<< buf <<endl;
+ int res = system(buf);
+
+ ndbout << "res: " << res << endl;
+
+ return res;
+
+
+
+
+}
+
+
+// Master failure
+int
+NFDuringGrepM_codes[] = {
+ 10003,
+ 10004,
+ 10005,
+ 10007,
+ 10008,
+ 10009,
+ 10010,
+ 10012,
+ 10013
+};
+
+// Slave failure
+int
+NFDuringGrepS_codes[] = {
+ 10014,
+ 10015,
+ 10016,
+ 10017,
+ 10018,
+ 10020
+};
+
+// Master takeover etc...
+int
+NFDuringGrepSL_codes[] = {
+ 10001,
+ 10002,
+ 10021
+};
+
+int
+NdbGrep::NFMaster(NdbRestarter& _restarter){
+ const int sz = sizeof(NFDuringGrepM_codes)/sizeof(NFDuringGrepM_codes[0]);
+ return NF(_restarter, NFDuringGrepM_codes, sz, true);
+}
+
+int
+NdbGrep::NFMasterAsSlave(NdbRestarter& _restarter){
+ const int sz = sizeof(NFDuringGrepS_codes)/sizeof(NFDuringGrepS_codes[0]);
+ return NF(_restarter, NFDuringGrepS_codes, sz, true);
+}
+
+int
+NdbGrep::NFSlave(NdbRestarter& _restarter){
+ const int sz = sizeof(NFDuringGrepS_codes)/sizeof(NFDuringGrepS_codes[0]);
+ return NF(_restarter, NFDuringGrepS_codes, sz, false);
+}
+
+int
+NdbGrep::NF(NdbRestarter& _restarter, int *NFDuringGrep_codes, const int sz, bool onMaster){
+ {
+ int nodeId = _restarter.getMasterNodeId();
+
+ CHECK(_restarter.restartOneDbNode(nodeId, false, true, true) == 0,
+ "Could not restart node "<< nodeId);
+
+ CHECK(_restarter.waitNodesNoStart(&nodeId, 1) == 0,
+ "waitNodesNoStart failed");
+
+ CHECK(_restarter.startNodes(&nodeId, 1) == 0,
+ "failed to start node");
+
+ NdbSleep_SecSleep(10);
+ }
+
+ CHECK(_restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+
+ int nNodes = _restarter.getNumDbNodes();
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+
+ for(int i = 0; i<sz; i++){
+
+ int error = NFDuringGrep_codes[i];
+ unsigned int backupId;
+
+ const int masterNodeId = _restarter.getMasterNodeId();
+ CHECK(masterNodeId > 0, "getMasterNodeId failed");
+ int nodeId;
+
+ nodeId = masterNodeId;
+ if (!onMaster) {
+ int randomId;
+ while (nodeId == masterNodeId) {
+ randomId = myRandom48(nNodes);
+ nodeId = _restarter.getDbNodeId(randomId);
+ }
+ }
+
+ g_err << "NdbGrep::NF node = " << nodeId
+ << " error code = " << error << " masterNodeId = "
+ << masterNodeId << endl;
+
+
+ int val = DumpStateOrd::CmvmiSetRestartOnErrorInsert;
+ CHECK(_restarter.dumpStateOneNode(nodeId, &val, 1) == 0,
+ "failed to set RestartOnErrorInsert");
+ CHECK(_restarter.insertErrorInNode(nodeId, error) == 0,
+ "failed to set error insert");
+
+ g_info << "error inserted" << endl;
+
+ g_info << "starting backup" << endl;
+ int r = start();
+ g_info << "r = " << r
+ << " (which should fail) started with id = " << backupId << endl;
+ if (r == 0) {
+ g_err << "Grep should have failed on error_insertion " << error << endl
+ << "Master = " << masterNodeId << "Node = " << nodeId << endl;
+ }
+
+ CHECK(_restarter.waitNodesNoStart(&nodeId, 1) == 0,
+ "waitNodesNoStart failed");
+
+ g_info << "number of nodes running " << _restarter.getNumDbNodes() << endl;
+
+ if (_restarter.getNumDbNodes() != nNodes) {
+ g_err << "Failure: cluster not up" << endl;
+ return NDBT_FAILED;
+ }
+
+ NdbSleep_SecSleep(1);
+
+ g_info << "starting new backup" << endl;
+ CHECK(start() == 0,
+ "failed to start backup");
+ g_info << "(which should succeed) started with id = " << backupId << endl;
+
+ g_info << "starting node" << endl;
+ CHECK(_restarter.startNodes(&nodeId, 1) == 0,
+ "failed to start node");
+
+ CHECK(_restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+ g_info << "node started" << endl;
+
+ CHECK(_restarter.insertErrorInNode(nodeId, 10099) == 0,
+ "failed to set error insert");
+ }
+
+ return NDBT_OK;
+}
+
+int
+FailS_codes[] = {
+ 10023,
+ 10024,
+ 10025,
+ 10026,
+ 10027,
+ 10028,
+ 10029,
+ 10030,
+ 10031
+};
+
+int
+FailM_codes[] = {
+ 10023,
+ 10024,
+ 10025,
+ 10026,
+ 10027,
+ 10028,
+ 10029,
+ 10030,
+ 10031
+};
+
+int
+NdbGrep::FailMaster(NdbRestarter& _restarter){
+ const int sz = sizeof(FailM_codes)/sizeof(FailM_codes[0]);
+ return Fail(_restarter, FailM_codes, sz, true);
+}
+
+int
+NdbGrep::FailMasterAsSlave(NdbRestarter& _restarter){
+ const int sz = sizeof(FailS_codes)/sizeof(FailS_codes[0]);
+ return Fail(_restarter, FailS_codes, sz, true);
+}
+
+int
+NdbGrep::FailSlave(NdbRestarter& _restarter){
+ const int sz = sizeof(FailS_codes)/sizeof(FailS_codes[0]);
+ return Fail(_restarter, FailS_codes, sz, false);
+}
+
+int
+NdbGrep::Fail(NdbRestarter& _restarter, int *Fail_codes, const int sz, bool onMaster){
+
+ CHECK(_restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+
+ int nNodes = _restarter.getNumDbNodes();
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+
+ for(int i = 0; i<sz; i++){
+ int error = Fail_codes[i];
+ unsigned int backupId;
+
+ const int masterNodeId = _restarter.getMasterNodeId();
+ CHECK(masterNodeId > 0, "getMasterNodeId failed");
+ int nodeId;
+
+ nodeId = masterNodeId;
+ if (!onMaster) {
+ int randomId;
+ while (nodeId == masterNodeId) {
+ randomId = myRandom48(nNodes);
+ nodeId = _restarter.getDbNodeId(randomId);
+ }
+ }
+
+ g_err << "NdbGrep::Fail node = " << nodeId
+ << " error code = " << error << " masterNodeId = "
+ << masterNodeId << endl;
+
+ CHECK(_restarter.insertErrorInNode(nodeId, error) == 0,
+ "failed to set error insert");
+
+ g_info << "error inserted" << endl;
+ g_info << "waiting some before starting backup" << endl;
+
+ g_info << "starting backup" << endl;
+ int r = start();
+ g_info << "r = " << r
+ << " (which should fail) started with id = " << backupId << endl;
+ if (r == 0) {
+ g_err << "Grep should have failed on error_insertion " << error << endl
+ << "Master = " << masterNodeId << "Node = " << nodeId << endl;
+ }
+
+ CHECK(_restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+
+ CHECK(_restarter.insertErrorInNode(nodeId, 10099) == 0,
+ "failed to set error insert");
+ }
+
+ return NDBT_OK;
+}
+
+
diff --git a/storage/ndb/test/src/NdbRestarter.cpp b/storage/ndb/test/src/NdbRestarter.cpp
new file mode 100644
index 00000000000..91c0963feae
--- /dev/null
+++ b/storage/ndb/test/src/NdbRestarter.cpp
@@ -0,0 +1,651 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NdbRestarter.hpp>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NdbTick.h>
+#include <mgmapi_debug.h>
+#include <NDBT_Output.hpp>
+#include <random.h>
+#include <kernel/ndb_limits.h>
+#include <ndb_version.h>
+
+#define MGMERR(h) \
+ ndbout << "latest_error="<<ndb_mgm_get_latest_error(h) \
+ << ", line="<<ndb_mgm_get_latest_error_line(h) \
+ << endl;
+
+
+NdbRestarter::NdbRestarter(const char* _addr):
+ connected(false),
+ handle(NULL),
+ m_config(0)
+{
+ if (_addr == NULL){
+ addr.assign("");
+ } else {
+ addr.assign(_addr);
+ }
+}
+
+NdbRestarter::~NdbRestarter(){
+ disconnect();
+}
+
+int NdbRestarter::getDbNodeId(int _i){
+ if (!isConnected())
+ return -1;
+
+ if (getStatus() != 0)
+ return -1;
+
+ for(size_t i = 0; i < ndbNodes.size(); i++){
+ if (i == (unsigned)_i){
+ return ndbNodes[i].node_id;
+ }
+ }
+ return -1;
+}
+
+
+int
+NdbRestarter::restartOneDbNode(int _nodeId,
+ bool inital,
+ bool nostart,
+ bool abort){
+ if (!isConnected())
+ return -1;
+
+ int ret = 0;
+
+ if ((ret = ndb_mgm_restart2(handle, 1, &_nodeId,
+ inital, nostart, abort)) <= 0) {
+ /**
+ * ndb_mgm_restart2 returned error, one reason could
+ * be that the node have not stopped fast enough!
+ * Check status of the node to see if it's on the
+ * way down. If that's the case ignore the error
+ */
+
+ if (getStatus() != 0)
+ return -1;
+
+ g_info << "ndb_mgm_restart2 returned with error, checking node state" << endl;
+
+ for(size_t i = 0; i < ndbNodes.size(); i++){
+ if(ndbNodes[i].node_id == _nodeId){
+ g_info <<_nodeId<<": status="<<ndbNodes[i].node_status<<endl;
+ /* Node found check state */
+ switch(ndbNodes[i].node_status){
+ case NDB_MGM_NODE_STATUS_RESTARTING:
+ case NDB_MGM_NODE_STATUS_SHUTTING_DOWN:
+ return 0;
+ default:
+ break;
+ }
+ }
+ }
+
+ MGMERR(handle);
+ g_err << "Could not stop node with id = "<< _nodeId << endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+NdbRestarter::getMasterNodeId(){
+ if (!isConnected())
+ return -1;
+
+ if (getStatus() != 0)
+ return -1;
+
+ int min = 0;
+ int node = -1;
+ for(size_t i = 0; i < ndbNodes.size(); i++){
+ if(min == 0 || ndbNodes[i].dynamic_id < min){
+ min = ndbNodes[i].dynamic_id;
+ node = ndbNodes[i].node_id;
+ }
+ }
+
+ return node;
+}
+
+int
+NdbRestarter::getRandomNotMasterNodeId(int rand){
+ int master = getMasterNodeId();
+ if(master == -1)
+ return -1;
+
+ Uint32 counter = 0;
+ rand = rand % ndbNodes.size();
+ while(counter++ < ndbNodes.size() && ndbNodes[rand].node_id == master)
+ rand = (rand + 1) % ndbNodes.size();
+
+ if(ndbNodes[rand].node_id != master)
+ return ndbNodes[rand].node_id;
+ return -1;
+}
+
+int
+NdbRestarter::getRandomNodeOtherNodeGroup(int nodeId, int rand){
+ if (!isConnected())
+ return -1;
+
+ if (getStatus() != 0)
+ return -1;
+
+ int node_group = -1;
+ for(size_t i = 0; i < ndbNodes.size(); i++){
+ if(ndbNodes[i].node_id == nodeId){
+ node_group = ndbNodes[i].node_group;
+ break;
+ }
+ }
+ if(node_group == -1){
+ return -1;
+ }
+
+ Uint32 counter = 0;
+ rand = rand % ndbNodes.size();
+ while(counter++ < ndbNodes.size() && ndbNodes[rand].node_group == node_group)
+ rand = (rand + 1) % ndbNodes.size();
+
+ if(ndbNodes[rand].node_group != node_group)
+ return ndbNodes[rand].node_id;
+
+ return -1;
+}
+
+int
+NdbRestarter::waitClusterStarted(unsigned int _timeout){
+ return waitClusterState(NDB_MGM_NODE_STATUS_STARTED, _timeout);
+}
+
+int
+NdbRestarter::waitClusterStartPhase(int _startphase, unsigned int _timeout){
+ return waitClusterState(NDB_MGM_NODE_STATUS_STARTING, _timeout, _startphase);
+}
+
+int
+NdbRestarter::waitClusterSingleUser(unsigned int _timeout){
+ return waitClusterState(NDB_MGM_NODE_STATUS_SINGLEUSER, _timeout);
+}
+
+int
+NdbRestarter::waitClusterNoStart(unsigned int _timeout){
+ return waitClusterState(NDB_MGM_NODE_STATUS_NOT_STARTED, _timeout);
+}
+
+int
+NdbRestarter::waitClusterState(ndb_mgm_node_status _status,
+ unsigned int _timeout,
+ int _startphase){
+
+ int nodes[MAX_NDB_NODES];
+ int numNodes = 0;
+
+ if (getStatus() != 0)
+ return -1;
+
+ // Collect all nodes into nodes
+ for (size_t i = 0; i < ndbNodes.size(); i++){
+ nodes[i] = ndbNodes[i].node_id;
+ numNodes++;
+ }
+
+ return waitNodesState(nodes, numNodes, _status, _timeout, _startphase);
+}
+
+
+int
+NdbRestarter::waitNodesState(int * _nodes, int _num_nodes,
+ ndb_mgm_node_status _status,
+ unsigned int _timeout,
+ int _startphase){
+
+ if (!isConnected()){
+ g_err << "!isConnected"<<endl;
+ return -1;
+ }
+
+ unsigned int attempts = 0;
+ unsigned int resetAttempts = 0;
+ const unsigned int MAX_RESET_ATTEMPTS = 10;
+ bool allInState = false;
+ while (allInState == false){
+ if (_timeout > 0 && attempts > _timeout){
+ /**
+ * Timeout has expired waiting for the nodes to enter
+ * the state we want
+ */
+ bool waitMore = false;
+ /**
+ * Make special check if we are waiting for
+ * cluster to become started
+ */
+ if(_status == NDB_MGM_NODE_STATUS_STARTED){
+ waitMore = true;
+ /**
+ * First check if any node is not starting
+ * then it's no idea to wait anymore
+ */
+ for (size_t n = 0; n < ndbNodes.size(); n++){
+ if (ndbNodes[n].node_status != NDB_MGM_NODE_STATUS_STARTED &&
+ ndbNodes[n].node_status != NDB_MGM_NODE_STATUS_STARTING)
+ waitMore = false;
+
+ }
+ }
+
+ if (!waitMore || resetAttempts > MAX_RESET_ATTEMPTS){
+ g_err << "waitNodeState("
+ << ndb_mgm_get_node_status_string(_status)
+ <<", "<<_startphase<<")"
+ << " timeout after " << attempts <<" attemps" << endl;
+ return -1;
+ }
+
+ g_err << "waitNodeState("
+ << ndb_mgm_get_node_status_string(_status)
+ <<", "<<_startphase<<")"
+ << " resetting number of attempts "
+ << resetAttempts << endl;
+ attempts = 0;
+ resetAttempts++;
+
+ }
+
+ allInState = true;
+ if (getStatus() != 0){
+ g_err << "getStatus != 0" << endl;
+ return -1;
+ }
+
+ // ndbout << "waitNodeState; _num_nodes = " << _num_nodes << endl;
+ // for (int i = 0; i < _num_nodes; i++)
+ // ndbout << " node["<<i<<"] =" <<_nodes[i] << endl;
+
+ for (int i = 0; i < _num_nodes; i++){
+ ndb_mgm_node_state* ndbNode = NULL;
+ for (size_t n = 0; n < ndbNodes.size(); n++){
+ if (ndbNodes[n].node_id == _nodes[i])
+ ndbNode = &ndbNodes[n];
+ }
+
+ if(ndbNode == NULL){
+ allInState = false;
+ continue;
+ }
+
+ g_info << "State node " << ndbNode->node_id << " "
+ << ndb_mgm_get_node_status_string(ndbNode->node_status)<< endl;
+
+ assert(ndbNode != NULL);
+
+ if(_status == NDB_MGM_NODE_STATUS_STARTING &&
+ ((ndbNode->node_status == NDB_MGM_NODE_STATUS_STARTING &&
+ ndbNode->start_phase >= _startphase) ||
+ (ndbNode->node_status == NDB_MGM_NODE_STATUS_STARTED)))
+ continue;
+
+ if (_status == NDB_MGM_NODE_STATUS_STARTING){
+ g_info << "status = "
+ << ndb_mgm_get_node_status_string(ndbNode->node_status)
+ <<", start_phase="<<ndbNode->start_phase<<endl;
+ if (ndbNode->node_status != _status) {
+ if (ndbNode->node_status < _status)
+ allInState = false;
+ else
+ g_info << "node_status(" << ndbNode->node_status
+ <<") != _status("<<_status<<")"<<endl;
+ } else if (ndbNode->start_phase < _startphase)
+ allInState = false;
+ } else {
+ if (ndbNode->node_status != _status)
+ allInState = false;
+ }
+ }
+ g_info << "Waiting for cluster enter state"
+ << ndb_mgm_get_node_status_string(_status)<< endl;
+ NdbSleep_SecSleep(1);
+ attempts++;
+ }
+ return 0;
+}
+
+int NdbRestarter::waitNodesStarted(int * _nodes, int _num_nodes,
+ unsigned int _timeout){
+ return waitNodesState(_nodes, _num_nodes,
+ NDB_MGM_NODE_STATUS_STARTED, _timeout);
+}
+
+int NdbRestarter::waitNodesStartPhase(int * _nodes, int _num_nodes,
+ int _startphase, unsigned int _timeout){
+ return waitNodesState(_nodes, _num_nodes,
+ NDB_MGM_NODE_STATUS_STARTING, _timeout,
+ _startphase);
+}
+
+int NdbRestarter::waitNodesNoStart(int * _nodes, int _num_nodes,
+ unsigned int _timeout){
+ return waitNodesState(_nodes, _num_nodes,
+ NDB_MGM_NODE_STATUS_NOT_STARTED, _timeout);
+}
+
+bool
+NdbRestarter::isConnected(){
+ if (connected == true)
+ return true;
+ return connect() == 0;
+}
+
+int
+NdbRestarter::connect(){
+ disconnect();
+ handle = ndb_mgm_create_handle();
+ if (handle == NULL){
+ g_err << "handle == NULL" << endl;
+ return -1;
+ }
+ g_info << "Connecting to mgmsrv at " << addr.c_str() << endl;
+ if (ndb_mgm_set_connectstring(handle,addr.c_str()))
+ {
+ MGMERR(handle);
+ g_err << "Connection to " << addr.c_str() << " failed" << endl;
+ return -1;
+ }
+
+ if (ndb_mgm_connect(handle, 0, 0, 0) == -1)
+ {
+ MGMERR(handle);
+ g_err << "Connection to " << addr.c_str() << " failed" << endl;
+ return -1;
+ }
+
+ connected = true;
+ return 0;
+}
+
+void
+NdbRestarter::disconnect(){
+ if (handle != NULL){
+ ndb_mgm_disconnect(handle);
+ ndb_mgm_destroy_handle(&handle);
+ }
+ connected = false;
+}
+
+int
+NdbRestarter::getStatus(){
+ int retries = 0;
+ struct ndb_mgm_cluster_state * status;
+ struct ndb_mgm_node_state * node;
+
+ ndbNodes.clear();
+ mgmNodes.clear();
+ apiNodes.clear();
+
+ if (!isConnected())
+ return -1;
+
+ while(retries < 10){
+ status = ndb_mgm_get_status(handle);
+ if (status == NULL){
+ ndbout << "status==NULL, retries="<<retries<<endl;
+ MGMERR(handle);
+ retries++;
+ continue;
+ }
+ for (int i = 0; i < status->no_of_nodes; i++){
+ node = &status->node_states[i];
+ switch(node->node_type){
+ case NDB_MGM_NODE_TYPE_NDB:
+ ndbNodes.push_back(*node);
+ break;
+ case NDB_MGM_NODE_TYPE_MGM:
+ mgmNodes.push_back(*node);
+ break;
+ case NDB_MGM_NODE_TYPE_API:
+ apiNodes.push_back(*node);
+ break;
+ default:
+ if(node->node_status == NDB_MGM_NODE_STATUS_UNKNOWN ||
+ node->node_status == NDB_MGM_NODE_STATUS_NO_CONTACT){
+ retries++;
+ ndbNodes.clear();
+ mgmNodes.clear();
+ apiNodes.clear();
+ free(status);
+ status = NULL;
+ i = status->no_of_nodes;
+
+ ndbout << "kalle"<< endl;
+ break;
+ }
+ abort();
+ break;
+ }
+ }
+ if(status == 0){
+ ndbout << "status == 0" << endl;
+ continue;
+ }
+ free(status);
+ return 0;
+ }
+
+ g_err << "getStatus failed" << endl;
+ return -1;
+}
+
+
+int NdbRestarter::getNumDbNodes(){
+ if (!isConnected())
+ return -1;
+
+ if (getStatus() != 0)
+ return -1;
+
+ return ndbNodes.size();
+}
+
+int NdbRestarter::restartAll(bool initial,
+ bool nostart,
+ bool abort){
+
+ if (!isConnected())
+ return -1;
+
+ if (ndb_mgm_restart2(handle, 0, NULL, initial, 1, abort) == -1) {
+ MGMERR(handle);
+ g_err << "Could not restart(stop) all nodes " << endl;
+ // return -1; Continue anyway - Magnus
+ }
+
+ if (waitClusterNoStart(60) != 0){
+ g_err << "Cluster didnt enter STATUS_NOT_STARTED within 60s" << endl;
+ return -1;
+ }
+
+ if(nostart){
+ g_debug << "restartAll: nostart == true" << endl;
+ return 0;
+ }
+
+ if (ndb_mgm_start(handle, 0, NULL) == -1) {
+ MGMERR(handle);
+ g_err << "Could not restart(start) all nodes " << endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+int NdbRestarter::startAll(){
+ if (!isConnected())
+ return -1;
+
+ if (ndb_mgm_start(handle, 0, NULL) == -1) {
+ MGMERR(handle);
+ g_err << "Could not start all nodes " << endl;
+ return -1;
+ }
+
+ return 0;
+
+}
+
+int NdbRestarter::startNodes(int * nodes, int num_nodes){
+ if (!isConnected())
+ return -1;
+
+ if (ndb_mgm_start(handle, num_nodes, nodes) != num_nodes) {
+ MGMERR(handle);
+ g_err << "Could not start all nodes " << endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+int NdbRestarter::insertErrorInNode(int _nodeId, int _error){
+ if (!isConnected())
+ return -1;
+
+ ndb_mgm_reply reply;
+ reply.return_code = 0;
+
+ if (ndb_mgm_insert_error(handle, _nodeId, _error, &reply) == -1){
+ MGMERR(handle);
+ g_err << "Could not insert error in node with id = "<< _nodeId << endl;
+ }
+ if(reply.return_code != 0){
+ g_err << "Error: " << reply.message << endl;
+ }
+ return 0;
+}
+
+int NdbRestarter::insertErrorInAllNodes(int _error){
+ if (!isConnected())
+ return -1;
+
+ if (getStatus() != 0)
+ return -1;
+
+ int result = 0;
+
+ for(size_t i = 0; i < ndbNodes.size(); i++){
+ g_debug << "inserting error in node " << ndbNodes[i].node_id << endl;
+ if (insertErrorInNode(ndbNodes[i].node_id, _error) == -1)
+ result = -1;
+ }
+ return result;
+
+}
+
+
+
+int NdbRestarter::dumpStateOneNode(int _nodeId, int * _args, int _num_args){
+ if (!isConnected())
+ return -1;
+
+ ndb_mgm_reply reply;
+ reply.return_code = 0;
+
+ if (ndb_mgm_dump_state(handle, _nodeId, _args, _num_args, &reply) == -1){
+ MGMERR(handle);
+ g_err << "Could not dump state in node with id = "<< _nodeId << endl;
+ }
+
+ if(reply.return_code != 0){
+ g_err << "Error: " << reply.message << endl;
+ }
+ return reply.return_code;
+}
+
+int NdbRestarter::dumpStateAllNodes(int * _args, int _num_args){
+ if (!isConnected())
+ return -1;
+
+ if (getStatus() != 0)
+ return -1;
+
+ int result = 0;
+
+ for(size_t i = 0; i < ndbNodes.size(); i++){
+ g_debug << "dumping state in node " << ndbNodes[i].node_id << endl;
+ if (dumpStateOneNode(ndbNodes[i].node_id, _args, _num_args) == -1)
+ result = -1;
+ }
+ return result;
+
+}
+
+
+int NdbRestarter::enterSingleUserMode(int _nodeId){
+ if (!isConnected())
+ return -1;
+
+ ndb_mgm_reply reply;
+ reply.return_code = 0;
+
+ if (ndb_mgm_enter_single_user(handle, _nodeId, &reply) == -1){
+ MGMERR(handle);
+ g_err << "Could not enter single user mode api node = "<< _nodeId << endl;
+ }
+
+ if(reply.return_code != 0){
+ g_err << "Error: " << reply.message << endl;
+ }
+
+ return reply.return_code;
+}
+
+
+int NdbRestarter::exitSingleUserMode(){
+ if (!isConnected())
+ return -1;
+
+ ndb_mgm_reply reply;
+ reply.return_code = 0;
+
+ if (ndb_mgm_exit_single_user(handle, &reply) == -1){
+ MGMERR(handle);
+ g_err << "Could not exit single user mode " << endl;
+ }
+
+ if(reply.return_code != 0){
+ g_err << "Error: " << reply.message << endl;
+ }
+ return reply.return_code;
+}
+
+ndb_mgm_configuration*
+NdbRestarter::getConfig(){
+ if(m_config) return m_config;
+
+ if (!isConnected())
+ return 0;
+ m_config = ndb_mgm_get_configuration(handle, 0);
+ return m_config;
+}
+
+template class Vector<ndb_mgm_node_state>;
diff --git a/storage/ndb/test/src/NdbRestarts.cpp b/storage/ndb/test/src/NdbRestarts.cpp
new file mode 100644
index 00000000000..c0f31af84ce
--- /dev/null
+++ b/storage/ndb/test/src/NdbRestarts.cpp
@@ -0,0 +1,875 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <NdbRestarts.hpp>
+#include <NDBT.hpp>
+#include <string.h>
+#include <NdbSleep.h>
+#include <kernel/ndb_limits.h>
+#include <signaldata/DumpStateOrd.hpp>
+#include <NdbEnv.h>
+
+
+int restartRandomNodeGraceful(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int restartRandomNodeAbort(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int restartRandomNodeError(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int restartRandomNodeInitial(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int restartNFDuringNR(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int restartMasterNodeError(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int twoNodeFailure(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int fiftyPercentFail(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int twoMasterNodeFailure(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int restartAllNodesGracfeul(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int restartAllNodesAbort(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int restartAllNodesError9999(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int fiftyPercentStopAndWait(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int restartNodeDuringLCP(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart);
+int stopOnError(NdbRestarter&, const NdbRestarts::NdbRestart*);
+int getRandomNodeId(NdbRestarter& _restarter);
+
+/**
+ * Define list of restarts
+ * - name of restart
+ * - function perfoming the restart
+ * - required number of nodes
+ * - ...
+ * - arg1, used depending of restart
+ * - arg2, used depending of restart
+ */
+
+const NdbRestarts::NdbRestart NdbRestarts::m_restarts[] = {
+ /*********************************************************
+ *
+ * NODE RESTARTS with 1 node restarted
+ *
+ *********************************************************/
+ /**
+ * Restart a randomly selected node
+ * with graceful shutdown
+ */
+ NdbRestart("RestartRandomNode",
+ NODE_RESTART,
+ restartRandomNodeGraceful,
+ 2),
+ /**
+ * Restart a randomly selected node
+ * with immediate(abort) shutdown
+ */
+ NdbRestart("RestartRandomNodeAbort",
+ NODE_RESTART,
+ restartRandomNodeAbort,
+ 2),
+ /**
+ * Restart a randomly selected node
+ * with error insert
+ *
+ */
+ NdbRestart("RestartRandomNodeError",
+ NODE_RESTART,
+ restartRandomNodeError,
+ 2),
+ /**
+ * Restart the master node
+ * with error insert
+ */
+ NdbRestart("RestartMasterNodeError",
+ NODE_RESTART,
+ restartMasterNodeError,
+ 2),
+ /**
+ * Restart a randomly selected node without fileystem
+ *
+ */
+ NdbRestart("RestartRandomNodeInitial",
+ NODE_RESTART,
+ restartRandomNodeInitial,
+ 2),
+ /**
+ * Restart a randomly selected node and then
+ * crash it while restarting
+ *
+ */
+ NdbRestart("RestartNFDuringNR",
+ NODE_RESTART,
+ restartNFDuringNR,
+ 2),
+
+ /**
+ * Set StopOnError and crash the node by sending
+ * SYSTEM_ERROR to it
+ *
+ */
+ NdbRestart("StopOnError",
+ NODE_RESTART,
+ stopOnError,
+ 1),
+
+ /*********************************************************
+ *
+ * MULTIPLE NODE RESTARTS with more than 1 node
+ *
+ *********************************************************/
+ /**
+ * 2 nodes restart, select nodes to restart randomly and restart
+ * with a small random delay between restarts
+ */
+ NdbRestart("TwoNodeFailure",
+ MULTIPLE_NODE_RESTART,
+ twoNodeFailure,
+ 4),
+ /**
+ * 2 nodes restart, select master nodes and restart with
+ * a small random delay between restarts
+ */
+
+ NdbRestart("TwoMasterNodeFailure",
+ MULTIPLE_NODE_RESTART,
+ twoMasterNodeFailure,
+ 4),
+
+ NdbRestart("FiftyPercentFail",
+ MULTIPLE_NODE_RESTART,
+ fiftyPercentFail,
+ 2),
+
+ /*********************************************************
+ *
+ * SYSTEM RESTARTS
+ *
+ *********************************************************/
+ /**
+ * Restart all nodes with graceful shutdown
+ *
+ */
+
+ NdbRestart("RestartAllNodes",
+ SYSTEM_RESTART,
+ restartAllNodesGracfeul,
+ 1),
+ /**
+ * Restart all nodes immediately without
+ * graful shutdown
+ */
+ NdbRestart("RestartAllNodesAbort",
+ SYSTEM_RESTART,
+ restartAllNodesAbort,
+ 1),
+ /**
+ * Restart all nodes with error insert 9999
+ * TODO! We can later add more errors like 9998, 9997 etc.
+ */
+ NdbRestart("RestartAllNodesError9999",
+ SYSTEM_RESTART,
+ restartAllNodesError9999,
+ 1),
+ /**
+ * Stop 50% of all nodes with error insert 9999
+ * Wait for a random number of minutes
+ * Stop the rest of the nodes and then start all again
+ */
+ NdbRestart("FiftyPercentStopAndWait",
+ SYSTEM_RESTART,
+ fiftyPercentStopAndWait,
+ 2),
+ /**
+ * Restart a master node during LCP with error inserts.
+ */
+ NdbRestart("RestartNodeDuringLCP",
+ NODE_RESTART,
+ restartNodeDuringLCP,
+ 2),
+};
+
+const int NdbRestarts::m_NoOfRestarts = sizeof(m_restarts) / sizeof(NdbRestart);
+
+
+const NdbRestarts::NdbErrorInsert NdbRestarts::m_errors[] = {
+ NdbErrorInsert("Error9999", 9999)
+};
+
+const int NdbRestarts::m_NoOfErrors = sizeof(m_errors) / sizeof(NdbErrorInsert);
+
+NdbRestarts::NdbRestart::NdbRestart(const char* _name,
+ NdbRestartType _type,
+ restartFunc* _func,
+ int _requiredNodes,
+ int _arg1){
+ m_name = _name;
+ m_type = _type;
+ m_restartFunc = _func;
+ m_numRequiredNodes = _requiredNodes;
+ // m_arg1 = arg1;
+}
+
+
+int NdbRestarts::getNumRestarts(){
+ return m_NoOfRestarts;
+}
+
+const NdbRestarts::NdbRestart* NdbRestarts::getRestart(int _num){
+ if (_num >= m_NoOfRestarts)
+ return NULL;
+
+ return &m_restarts[_num];
+}
+
+const NdbRestarts::NdbRestart* NdbRestarts::getRestart(const char* _name){
+ for(int i = 0; i < m_NoOfRestarts; i++){
+ if (strcmp(m_restarts[i].m_name, _name) == 0){
+ return &m_restarts[i];
+ }
+ }
+ g_err << "The restart \""<< _name << "\" not found in NdbRestarts" << endl;
+ return NULL;
+}
+
+
+int NdbRestarts::executeRestart(const NdbRestarts::NdbRestart* _restart,
+ unsigned int _timeout){
+ // Check that there are enough nodes in the cluster
+ // for this test
+ NdbRestarter restarter;
+ if (_restart->m_numRequiredNodes > restarter.getNumDbNodes()){
+ g_err << "This test requires " << _restart->m_numRequiredNodes << " nodes "
+ << "there are only "<< restarter.getNumDbNodes() <<" nodes in cluster"
+ << endl;
+ return NDBT_OK;
+ }
+ if (restarter.waitClusterStarted(120) != 0){
+ // If cluster is not started when we shall peform restart
+ // the restart can not be executed and the test fails
+ return NDBT_FAILED;
+ }
+
+ int res = _restart->m_restartFunc(restarter, _restart);
+
+ // Sleep a little waiting for nodes to react to command
+ NdbSleep_SecSleep(2);
+
+ if (_timeout == 0){
+ // If timeout == 0 wait for ever
+ while(restarter.waitClusterStarted(60) != 0)
+ g_err << "Cluster is not started after restart. Waiting 60s more..."
+ << endl;
+ } else {
+ if (restarter.waitClusterStarted(_timeout) != 0){
+ g_err<<"Cluster failed to start" << endl;
+ res = NDBT_FAILED;
+ }
+ }
+
+ return res;
+}
+
+int NdbRestarts::executeRestart(int _num,
+ unsigned int _timeout){
+ const NdbRestarts::NdbRestart* r = getRestart(_num);
+ if (r == NULL)
+ return NDBT_FAILED;
+
+ int res = executeRestart(r, _timeout);
+ return res;
+}
+
+int NdbRestarts::executeRestart(const char* _name,
+ unsigned int _timeout){
+ const NdbRestarts::NdbRestart* r = getRestart(_name);
+ if (r == NULL)
+ return NDBT_FAILED;
+
+ int res = executeRestart(r, _timeout);
+ return res;
+}
+
+void NdbRestarts::listRestarts(NdbRestartType _type){
+ for(int i = 0; i < m_NoOfRestarts; i++){
+ if (m_restarts[i].m_type == _type)
+ ndbout << " " << m_restarts[i].m_name << ", min "
+ << m_restarts[i].m_numRequiredNodes
+ << " nodes"<< endl;
+ }
+}
+
+void NdbRestarts::listRestarts(){
+ ndbout << "NODE RESTARTS" << endl;
+ listRestarts(NODE_RESTART);
+ ndbout << "MULTIPLE NODE RESTARTS" << endl;
+ listRestarts(MULTIPLE_NODE_RESTART);
+ ndbout << "SYSTEM RESTARTS" << endl;
+ listRestarts(SYSTEM_RESTART);
+}
+
+NdbRestarts::NdbErrorInsert::NdbErrorInsert(const char* _name,
+ int _errorNo){
+
+ m_name = _name;
+ m_errorNo = _errorNo;
+}
+
+int NdbRestarts::getNumErrorInserts(){
+ return m_NoOfErrors;
+}
+
+const NdbRestarts::NdbErrorInsert* NdbRestarts::getError(int _num){
+ if (_num >= m_NoOfErrors)
+ return NULL;
+ return &m_errors[_num];
+}
+
+const NdbRestarts::NdbErrorInsert* NdbRestarts::getRandomError(){
+ int randomId = myRandom48(m_NoOfErrors);
+ return &m_errors[randomId];
+}
+
+
+
+/**
+ *
+ * IMPLEMENTATION OF THE DIFFERENT RESTARTS
+ * Each function should perform it's action
+ * and the returned NDBT_OK or NDBT_FAILED
+ *
+ */
+
+
+#define CHECK(b, m) { int _xx = b; if (!(_xx)) { \
+ ndbout << "ERR: "<< m \
+ << " " << "File: " << __FILE__ \
+ << " (Line: " << __LINE__ << ")" << "- " << _xx << endl; \
+ return NDBT_FAILED; } }
+
+
+
+int restartRandomNodeGraceful(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ int randomId = myRandom48(_restarter.getNumDbNodes());
+ int nodeId = _restarter.getDbNodeId(randomId);
+
+ g_info << _restart->m_name << ": node = "<<nodeId << endl;
+
+ CHECK(_restarter.restartOneDbNode(nodeId) == 0,
+ "Could not restart node "<<nodeId);
+
+ return NDBT_OK;
+}
+
+int restartRandomNodeAbort(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ int randomId = myRandom48(_restarter.getNumDbNodes());
+ int nodeId = _restarter.getDbNodeId(randomId);
+
+ g_info << _restart->m_name << ": node = "<<nodeId << endl;
+
+ CHECK(_restarter.restartOneDbNode(nodeId, false, false, true) == 0,
+ "Could not restart node "<<nodeId);
+
+ return NDBT_OK;
+}
+
+int restartRandomNodeError(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ int randomId = myRandom48(_restarter.getNumDbNodes());
+ int nodeId = _restarter.getDbNodeId(randomId);
+
+ ndbout << _restart->m_name << ": node = "<<nodeId << endl;
+
+ CHECK(_restarter.insertErrorInNode(nodeId, 9999) == 0,
+ "Could not restart node "<<nodeId);
+
+ return NDBT_OK;
+}
+
+int restartMasterNodeError(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ int nodeId = _restarter.getDbNodeId(0);
+
+ g_info << _restart->m_name << ": node = "<<nodeId << endl;
+
+ CHECK(_restarter.insertErrorInNode(nodeId, 39999) == 0,
+ "Could not restart node "<<nodeId);
+
+ return NDBT_OK;
+}
+
+int restartRandomNodeInitial(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ int randomId = myRandom48(_restarter.getNumDbNodes());
+ int nodeId = _restarter.getDbNodeId(randomId);
+
+ g_info << _restart->m_name << ": node = "<<nodeId << endl;
+
+ CHECK(_restarter.restartOneDbNode(nodeId, true) == 0,
+ "Could not restart node "<<nodeId);
+
+ return NDBT_OK;
+}
+
+int twoNodeFailure(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ int randomId = myRandom48(_restarter.getNumDbNodes());
+ int nodeId = _restarter.getDbNodeId(randomId);
+ g_info << _restart->m_name << ": node = "<< nodeId << endl;
+
+ CHECK(_restarter.insertErrorInNode(nodeId, 9999) == 0,
+ "Could not restart node "<< nodeId);
+
+ // Create random value, max 10 secs
+ int max = 10;
+ int seconds = (myRandom48(max)) + 1;
+ g_info << "Waiting for " << seconds << "(" << max
+ << ") secs " << endl;
+ NdbSleep_SecSleep(seconds);
+
+ randomId = (rand() % _restarter.getNumDbNodes());
+ nodeId = _restarter.getDbNodeId(randomId);
+ g_info << _restart->m_name << ": node = "<< nodeId << endl;
+
+ CHECK(_restarter.insertErrorInNode(nodeId, 9999) == 0,
+ "Could not restart node "<< nodeId);
+
+ return NDBT_OK;
+}
+
+int twoMasterNodeFailure(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ int nodeId = _restarter.getDbNodeId(0);
+ g_info << _restart->m_name << ": node = "<< nodeId << endl;
+
+ CHECK(_restarter.insertErrorInNode(nodeId, 39999) == 0,
+ "Could not restart node "<< nodeId);
+
+ // Create random value, max 10 secs
+ int max = 10;
+ int seconds = (myRandom48(max)) + 1;
+ g_info << "Waiting for " << seconds << "(" << max
+ << ") secs " << endl;
+ NdbSleep_SecSleep(seconds);
+
+ nodeId = _restarter.getDbNodeId(0);
+ g_info << _restart->m_name << ": node = "<< nodeId << endl;
+
+ CHECK(_restarter.insertErrorInNode(nodeId, 39999) == 0,
+ "Could not restart node "<< nodeId);
+
+ return NDBT_OK;
+}
+
+int get50PercentOfNodes(NdbRestarter& restarter,
+ int * _nodes){
+ // For now simply return all nodes with even node id
+ // TODO Check nodegroup and return one node from each
+
+ int num50Percent = restarter.getNumDbNodes() / 2;
+ assert(num50Percent <= MAX_NDB_NODES);
+
+ // Calculate which nodes to stop, select all even nodes
+ for (int i = 0; i < num50Percent; i++){
+ _nodes[i] = restarter.getDbNodeId(i*2);
+ }
+ return num50Percent;
+}
+
+int fiftyPercentFail(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+
+ int nodes[MAX_NDB_NODES];
+
+ int numNodes = get50PercentOfNodes(_restarter, nodes);
+
+ // Stop the nodes, with nostart and abort
+ for (int i = 0; i < numNodes; i++){
+ g_info << "Stopping node "<< nodes[i] << endl;
+ int res = _restarter.restartOneDbNode(nodes[i], false, true, true);
+ CHECK(res == 0, "Could not stop node: "<< nodes[i]);
+ }
+
+ CHECK(_restarter.waitNodesNoStart(nodes, numNodes) == 0,
+ "waitNodesNoStart");
+
+ // Order all nodes to start
+ ndbout << "Starting all nodes" << endl;
+ CHECK(_restarter.startAll() == 0,
+ "Could not start all nodes");
+
+ return NDBT_OK;
+}
+
+
+int restartAllNodesGracfeul(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ g_info << _restart->m_name << endl;
+
+ // Restart graceful
+ CHECK(_restarter.restartAll() == 0,
+ "Could not restart all nodes");
+
+ return NDBT_OK;
+
+}
+
+int restartAllNodesAbort(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ g_info << _restart->m_name << endl;
+
+ // Restart abort
+ CHECK(_restarter.restartAll(false, false, true) == 0,
+ "Could not restart all nodes");
+
+ return NDBT_OK;
+}
+
+int restartAllNodesError9999(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ g_info << _restart->m_name << endl;
+
+ // Restart with error insert
+ CHECK(_restarter.insertErrorInAllNodes(9999) == 0,
+ "Could not restart all nodes ");
+
+ return NDBT_OK;
+}
+
+int fiftyPercentStopAndWait(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ int nodes[MAX_NDB_NODES];
+ int numNodes = get50PercentOfNodes(_restarter, nodes);
+
+ // Stop the nodes, with nostart and abort
+ for (int i = 0; i < numNodes; i++){
+ g_info << "Stopping node "<<nodes[i] << endl;
+ int res = _restarter.restartOneDbNode(nodes[i], false, true, true);
+ CHECK(res == 0, "Could not stop node: "<< nodes[i]);
+ }
+
+ CHECK(_restarter.waitNodesNoStart(nodes, numNodes) == 0,
+ "waitNodesNoStart");
+
+ // Create random value, max 120 secs
+ int max = 120;
+ int seconds = (myRandom48(max)) + 1;
+ g_info << "Waiting for " << seconds << "(" << max
+ << ") secs " << endl;
+ NdbSleep_SecSleep(seconds);
+
+
+ // Restart graceful
+ CHECK(_restarter.restartAll() == 0,
+ "Could not restart all nodes");
+
+ g_info << _restart->m_name << endl;
+
+ return NDBT_OK;
+}
+
+int
+NFDuringNR_codes[] = {
+ 7121,
+ 5027,
+ 7172,
+ 6000,
+ 6001,
+ 6002,
+ 7171,
+ 7130,
+ 7133,
+ 7138,
+ 7154,
+ 7144,
+ 5026,
+ 7139,
+ 7132,
+
+ //LCP
+ 8000,
+ 8001,
+ 5010,
+ 7022,
+ 7024,
+ 7016,
+ 7017,
+ 5002
+};
+
+int restartNFDuringNR(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ int i;
+ const int sz = sizeof(NFDuringNR_codes)/sizeof(NFDuringNR_codes[0]);
+ for(i = 0; i<sz; i++){
+ int randomId = myRandom48(_restarter.getNumDbNodes());
+ int nodeId = _restarter.getDbNodeId(randomId);
+ int error = NFDuringNR_codes[i];
+
+ g_info << _restart->m_name << ": node = " << nodeId
+ << " error code = " << error << endl;
+
+ CHECK(_restarter.restartOneDbNode(nodeId, false, true, true) == 0,
+ "Could not restart node "<< nodeId);
+
+ CHECK(_restarter.waitNodesNoStart(&nodeId, 1) == 0,
+ "waitNodesNoStart failed");
+
+ int val = DumpStateOrd::CmvmiSetRestartOnErrorInsert;
+ CHECK(_restarter.dumpStateOneNode(nodeId, &val, 1) == 0,
+ "failed to set RestartOnErrorInsert");
+
+ CHECK(_restarter.insertErrorInNode(nodeId, error) == 0,
+ "failed to set error insert");
+
+ CHECK(_restarter.startNodes(&nodeId, 1) == 0,
+ "failed to start node");
+
+ NdbSleep_SecSleep(3);
+
+ //CHECK(_restarter.waitNodesNoStart(&nodeId, 1) == 0,
+ // "waitNodesNoStart failed");
+ _restarter.waitNodesNoStart(&nodeId, 1);
+
+ CHECK(_restarter.startNodes(&nodeId, 1) == 0,
+ "failed to start node");
+
+ CHECK(_restarter.waitNodesStarted(&nodeId, 1) == 0,
+ "waitNodesStarted failed");
+ }
+
+ return NDBT_OK;
+
+ if(_restarter.getNumDbNodes() < 4)
+ return NDBT_OK;
+
+ char buf[256];
+ if(NdbEnv_GetEnv("USER", buf, 256) == 0 || strcmp(buf, "ejonore") != 0)
+ return NDBT_OK;
+
+ for(i = 0; i<sz; i++){
+ const int randomId = myRandom48(_restarter.getNumDbNodes());
+ int nodeId = _restarter.getDbNodeId(randomId);
+ const int error = NFDuringNR_codes[i];
+
+ const int masterNodeId = _restarter.getMasterNodeId();
+ CHECK(masterNodeId > 0, "getMasterNodeId failed");
+ int crashNodeId = 0;
+ do {
+ int rand = myRandom48(1000);
+ crashNodeId = _restarter.getRandomNodeOtherNodeGroup(nodeId, rand);
+ } while(crashNodeId == masterNodeId);
+
+ CHECK(crashNodeId > 0, "getMasterNodeId failed");
+
+ g_info << _restart->m_name << " restarting node = " << nodeId
+ << " error code = " << error
+ << " crash node = " << crashNodeId << endl;
+
+ CHECK(_restarter.restartOneDbNode(nodeId, false, true, true) == 0,
+ "Could not restart node "<< nodeId);
+
+ CHECK(_restarter.waitNodesNoStart(&nodeId, 1) == 0,
+ "waitNodesNoStart failed");
+
+ int val = DumpStateOrd::CmvmiSetRestartOnErrorInsert;
+ CHECK(_restarter.dumpStateOneNode(crashNodeId, &val, 2) == 0,
+ "failed to set RestartOnErrorInsert");
+
+ CHECK(_restarter.insertErrorInNode(crashNodeId, error) == 0,
+ "failed to set error insert");
+
+ CHECK(_restarter.startNodes(&nodeId, 1) == 0,
+ "failed to start node");
+
+ CHECK(_restarter.waitClusterStarted() == 0,
+ "waitClusterStarted failed");
+ }
+
+ return NDBT_OK;
+}
+
+int
+NRDuringLCP_Master_codes[] = {
+ 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.
+ 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.
+ 7019, // Insert system error in master when local checkpoint is in the
+ // state clcpStatus = IDLE before sending CONTINUEB(ZCHECK_TC_COUNTER).
+ 7075, // Master. Don't send any LCP_FRAG_ORD(last=true)
+ // And crash when all have "not" been sent
+ 7021, // Crash in master when receiving START_LCP_REQ
+ 7023, // Crash in master when sending START_LCP_CONF
+ 7025, // Crash in master when receiving LCP_FRAG_REP
+ 7026, // Crash in master when changing state to LCP_TAB_COMPLETED
+ 7027 // Crash in master when changing state to LCP_TAB_SAVED
+};
+
+int
+NRDuringLCP_NonMaster_codes[] = {
+ 7020, // Insert system error in local checkpoint participant at reception
+ // of COPY_GCIREQ.
+ 8000, // Crash particpant when receiving TCGETOPSIZEREQ
+ 8001, // Crash particpant when receiving TC_CLOPSIZEREQ
+ 5010, // Crash any when receiving LCP_FRAGORD
+ 7022, // Crash in !master when receiving START_LCP_REQ
+ 7024, // Crash in !master when sending START_LCP_CONF
+ 7016, // Crash in !master when receiving LCP_FRAG_REP
+ 7017, // Crash in !master when changing state to LCP_TAB_COMPLETED
+ 7018 // Crash in !master when changing state to LCP_TAB_SAVED
+};
+
+int restartNodeDuringLCP(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart) {
+ int i;
+ // Master
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(_restarter.dumpStateAllNodes(&val, 1) == 0,
+ "Failed to set LCP to min value"); // Set LCP to min val
+ int sz = sizeof(NRDuringLCP_Master_codes)/
+ sizeof(NRDuringLCP_Master_codes[0]);
+ for(i = 0; i<sz; i++) {
+
+ int error = NRDuringLCP_Master_codes[i];
+ int masterNodeId = _restarter.getMasterNodeId();
+
+ CHECK(masterNodeId > 0, "getMasterNodeId failed");
+
+ ndbout << _restart->m_name << " restarting master node = " << masterNodeId
+ << " error code = " << error << endl;
+
+ {
+ int val = DumpStateOrd::CmvmiSetRestartOnErrorInsert;
+ CHECK(_restarter.dumpStateAllNodes(&val, 1) == 0,
+ "failed to set RestartOnErrorInsert");
+ }
+
+ CHECK(_restarter.insertErrorInNode(masterNodeId, error) == 0,
+ "failed to set error insert");
+
+ CHECK(_restarter.waitNodesNoStart(&masterNodeId, 1, 300) == 0,
+ "failed to wait no start");
+
+ CHECK(_restarter.startNodes(&masterNodeId, 1) == 0,
+ "failed to start node");
+
+ CHECK(_restarter.waitClusterStarted(300) == 0,
+ "waitClusterStarted failed");
+
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(_restarter.dumpStateOneNode(masterNodeId, &val, 1) == 0,
+ "failed to set error insert");
+ }
+ }
+
+ // NON-Master
+ sz = sizeof(NRDuringLCP_NonMaster_codes)/
+ sizeof(NRDuringLCP_NonMaster_codes[0]);
+ for(i = 0; i<sz; i++) {
+
+ int error = NRDuringLCP_NonMaster_codes[i];
+ int nodeId = getRandomNodeId(_restarter);
+ int masterNodeId = _restarter.getMasterNodeId();
+ CHECK(masterNodeId > 0, "getMasterNodeId failed");
+
+ while (nodeId == masterNodeId) {
+ nodeId = getRandomNodeId(_restarter);
+ }
+
+ ndbout << _restart->m_name << " restarting non-master node = " << nodeId
+ << " error code = " << error << endl;
+
+ int val = DumpStateOrd::CmvmiSetRestartOnErrorInsert;
+ CHECK(_restarter.dumpStateAllNodes(&val, 1) == 0,
+ "failed to set RestartOnErrorInsert");
+
+ CHECK(_restarter.insertErrorInNode(nodeId, error) == 0,
+ "failed to set error insert");
+
+ CHECK(_restarter.waitNodesNoStart(&nodeId, 1, 300) == 0,
+ "failed to wait no start");
+
+ CHECK(_restarter.startNodes(&nodeId, 1) == 0,
+ "failed to start node");
+
+ CHECK(_restarter.waitClusterStarted(300) == 0,
+ "waitClusterStarted failed");
+
+ {
+ int val = DumpStateOrd::DihMinTimeBetweenLCP;
+ CHECK(_restarter.dumpStateOneNode(nodeId, &val, 1) == 0,
+ "failed to set error insert");
+ }
+ }
+
+ return NDBT_OK;
+}
+
+int stopOnError(NdbRestarter& _restarter,
+ const NdbRestarts::NdbRestart* _restart){
+
+ myRandom48Init(NdbTick_CurrentMillisecond());
+
+ int randomId = myRandom48(_restarter.getNumDbNodes());
+ int nodeId = _restarter.getDbNodeId(randomId);
+
+ do {
+ g_info << _restart->m_name << ": node = " << nodeId
+ << endl;
+
+ CHECK(_restarter.waitClusterStarted(300) == 0,
+ "waitClusterStarted failed");
+
+ int val = DumpStateOrd::NdbcntrTestStopOnError;
+ CHECK(_restarter.dumpStateOneNode(nodeId, &val, 1) == 0,
+ "failed to set NdbcntrTestStopOnError");
+
+ NdbSleep_SecSleep(3);
+
+ CHECK(_restarter.waitClusterStarted(300) == 0,
+ "waitClusterStarted failed");
+ } while (false);
+
+ return NDBT_OK;
+}
+
+int getRandomNodeId(NdbRestarter& _restarter) {
+ myRandom48Init(NdbTick_CurrentMillisecond());
+ int randomId = myRandom48(_restarter.getNumDbNodes());
+ int nodeId = _restarter.getDbNodeId(randomId);
+
+ return nodeId;
+}
diff --git a/storage/ndb/test/src/NdbSchemaCon.cpp b/storage/ndb/test/src/NdbSchemaCon.cpp
new file mode 100644
index 00000000000..0de49ff983f
--- /dev/null
+++ b/storage/ndb/test/src/NdbSchemaCon.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 */
+
+
+
+/*********************************************************************
+Name: NdbSchemaCon.cpp
+Include:
+Link:
+Author: UABMNST Mona Natterkvist UAB/B/SD
+ EMIKRON Mikael Ronstrom
+Date: 020826
+Version: 3.0
+Description: Old Interface between application and NDB
+Documentation:
+Adjust: 980126 UABMNST First version.
+ 020826 EMIKRON New version adapted to new DICT version
+ 040524 Magnus Svensson - Adapted to not be included in public NdbApi
+ unless the user wants to use it.
+
+ NOTE: This file is only used as a compatibility layer for old test programs,
+ New programs should use NdbDictionary.hpp
+*********************************************************************/
+
+#include <ndb_global.h>
+#include <NdbApi.hpp>
+#include <NdbSchemaCon.hpp>
+#include <NdbSchemaOp.hpp>
+
+
+/*********************************************************************
+NdbSchemaCon(Ndb* aNdb);
+
+Parameters: aNdb: Pointers to the Ndb object
+Remark: Creates a schemacon object.
+************************************************************************************************/
+NdbSchemaCon::NdbSchemaCon( Ndb* aNdb ) :
+ theNdb(aNdb),
+ theFirstSchemaOpInList(NULL),
+ theMagicNumber(0x75318642)
+{
+ theError.code = 0;
+}//NdbSchemaCon::NdbSchemaCon()
+
+/*********************************************************************
+~NdbSchemaCon();
+
+Remark: Deletes the connection object.
+************************************************************************************************/
+NdbSchemaCon::~NdbSchemaCon()
+{
+}//NdbSchemaCon::~NdbSchemaCon()
+
+/*********************************************************************
+NdbSchemaOp* getNdbSchemaOp();
+
+Return Value Return a pointer to a NdbSchemaOp object if getNdbSchemaOp was sussesful.
+ Return NULL: In all other case.
+Parameters: tableId : Id of the database table beeing deleted.
+************************************************************************************************/
+NdbSchemaOp*
+NdbSchemaCon::getNdbSchemaOp()
+{
+ NdbSchemaOp* tSchemaOp;
+ if (theFirstSchemaOpInList != NULL) {
+ theError.code = 4401; // Only support one add table per transaction
+ return NULL;
+ }//if
+ tSchemaOp = new NdbSchemaOp(theNdb);
+ if ( tSchemaOp == NULL ) {
+ theError.code = 4000; // Could not allocate schema operation
+ return NULL;
+ }//if
+ theFirstSchemaOpInList = tSchemaOp;
+ int retValue = tSchemaOp->init(this);
+ if (retValue == -1) {
+ release();
+ theError.code = 4000; // Could not allocate buffer in schema operation
+ return NULL;
+ }//if
+ return tSchemaOp;
+}//NdbSchemaCon::getNdbSchemaOp()
+
+/*********************************************************************
+int execute();
+
+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
+NdbSchemaCon::execute()
+{
+ if(theError.code != 0) {
+ return -1;
+ }//if
+
+ NdbSchemaOp* tSchemaOp;
+
+ tSchemaOp = theFirstSchemaOpInList;
+ if (tSchemaOp == NULL) {
+ theError.code = 4402;
+ return -1;
+ }//if
+
+ if ((tSchemaOp->sendRec() == -1) || (theError.code != 0)) {
+ // Error Code already set in other place
+ return -1;
+ }//if
+
+ return 0;
+}//NdbSchemaCon::execute()
+
+/*********************************************************************
+void release();
+
+Remark: Release all schemaop.
+************************************************************************************************/
+void
+NdbSchemaCon::release()
+{
+ NdbSchemaOp* tSchemaOp;
+ tSchemaOp = theFirstSchemaOpInList;
+ if (tSchemaOp != NULL) {
+ tSchemaOp->release();
+ delete tSchemaOp;
+ }//if
+ theFirstSchemaOpInList = NULL;
+ return;
+}//NdbSchemaCon::release()
+
+#include <NdbError.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 &
+NdbSchemaCon::getNdbError() const {
+ update(theError);
+ return theError;
+}
+
+
+
+
+
+
+
+
diff --git a/storage/ndb/test/src/NdbSchemaOp.cpp b/storage/ndb/test/src/NdbSchemaOp.cpp
new file mode 100644
index 00000000000..4281ceb02c8
--- /dev/null
+++ b/storage/ndb/test/src/NdbSchemaOp.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 */
+
+
+/*****************************************************************************
+Name: NdbSchemaOp.cpp
+Include:
+Link:
+Author: UABMNST Mona Natterkvist UAB/B/SD
+ EMIKRON Mikael Ronstrom
+Date: 040524
+Version: 3.0
+Description: Interface between application and NDB
+Documentation: Handles createTable and createAttribute calls
+
+Adjust: 980125 UABMNST First version.
+ 020826 EMIKRON New version for new DICT
+ 040524 Magnus Svensson - Adapted to not be included in public NdbApi
+ unless the user wants to use it.
+
+ NOTE: This file is only used as a compatibility layer for old test programs,
+ New programs should use NdbDictionary.hpp
+*****************************************************************************/
+
+#include <ndb_global.h>
+#include <NdbApi.hpp>
+#include <NdbSchemaOp.hpp>
+#include <NdbSchemaCon.hpp>
+
+
+/*****************************************************************************
+NdbSchemaOp(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 NdbSchemaOp.
+*****************************************************************************/
+NdbSchemaOp::NdbSchemaOp(Ndb* aNdb) :
+ theNdb(aNdb),
+ theSchemaCon(NULL),
+ m_currentTable(NULL)
+{
+}//NdbSchemaOp::NdbSchemaOp()
+
+/*****************************************************************************
+~NdbSchemaOp();
+
+Remark: Delete tables for connection pointers (id).
+*****************************************************************************/
+NdbSchemaOp::~NdbSchemaOp( )
+{
+}//~NdbSchemaOp::NdbSchemaOp()
+
+/*****************************************************************************
+int createTable( const char* tableName )
+*****************************************************************************/
+int
+NdbSchemaOp::createTable(const char* aTableName,
+ Uint32 aTableSize,
+ KeyType aTupleKey,
+ int aNrOfPages,
+ FragmentType aFragmentType,
+ int aKValue,
+ int aMinLoadFactor,
+ int aMaxLoadFactor,
+ int aMemoryType,
+ bool aStoredTable)
+{
+ if(m_currentTable != 0){
+ return -1;
+ }
+
+ m_currentTable = new NdbDictionary::Table(aTableName);
+ m_currentTable->setKValue(aKValue);
+ m_currentTable->setMinLoadFactor(aMinLoadFactor);
+ m_currentTable->setMaxLoadFactor(aMaxLoadFactor);
+ m_currentTable->setLogging(aStoredTable);
+ m_currentTable->setFragmentType(NdbDictionary::Object::FragAllMedium);
+ return 0;
+}//NdbSchemaOp::createTable()
+
+/******************************************************************************
+int createAttribute( const char* anAttrName,
+ KeyType aTupleyKey,
+ int anAttrSize,
+ int anArraySize,
+ AttrType anAttrType,
+ SafeType aSafeType,
+ StorageMode aStorageMode,
+ int aNullAttr,
+ int aStorageAttr );
+
+******************************************************************************/
+int
+NdbSchemaOp::createAttribute( const char* anAttrName,
+ KeyType aTupleKey,
+ int anAttrSize,
+ int anArraySize,
+ AttrType anAttrType,
+ StorageMode aStorageMode,
+ bool nullable,
+ int aStorageAttr,
+ int aDistributionKeyFlag,
+ int aDistributionGroupFlag,
+ int aDistributionGroupNoOfBits,
+ bool aAutoIncrement,
+ const char* aDefaultValue)
+{
+ if (m_currentTable == 0){
+ return -1;
+ }//if
+
+ NdbDictionary::Column col(anAttrName);
+ switch(anAttrType){
+ case Signed:
+ if(anAttrSize == 64)
+ col.setType(NdbDictionary::Column::Bigint);
+ else
+ col.setType(NdbDictionary::Column::Int);
+ break;
+ case UnSigned:
+ if(anAttrSize == 64)
+ col.setType(NdbDictionary::Column::Bigunsigned);
+ else
+ col.setType(NdbDictionary::Column::Unsigned);
+ break;
+ case Float:
+ if(anAttrSize == 64)
+ col.setType(NdbDictionary::Column::Double);
+ else
+ col.setType(NdbDictionary::Column::Float);
+ break;
+ case String:
+ col.setType(NdbDictionary::Column::Char);
+ break;
+ case NoAttrTypeDef:
+ abort();
+ }
+ col.setLength(anArraySize);
+ col.setNullable(nullable);
+ if(aTupleKey != NoKey)
+ col.setPrimaryKey(true);
+ else
+ col.setPrimaryKey(false);
+
+ col.setDistributionKey(aDistributionKeyFlag);
+ col.setAutoIncrement(aAutoIncrement);
+ col.setDefaultValue(aDefaultValue != 0 ? aDefaultValue : "");
+
+ m_currentTable->addColumn(col);
+ return 0;
+}
+
+/******************************************************************************
+void release();
+
+Remark: Release all objects connected to the schemaop object.
+******************************************************************************/
+void
+NdbSchemaOp::release(){
+}//NdbSchemaOp::release()
+
+/******************************************************************************
+int sendRec()
+
+Return Value: Return 0 : send was succesful.
+ Return -1: In all other case.
+Parameters:
+Remark: Send and receive signals for schema transaction based on state
+******************************************************************************/
+int
+NdbSchemaOp::sendRec(){
+ int retVal = 0;
+ if(m_currentTable == 0){
+ retVal = -1;
+ } else {
+ retVal = theNdb->getDictionary()->createTable(* m_currentTable);
+ delete m_currentTable;
+ theSchemaCon->theError.code = theNdb->getDictionary()->getNdbError().code;
+ }
+
+ return retVal;
+}//NdbSchemaOp::sendRec()
+
+/******************************************************************************
+int init();
+
+Return Value: Return 0 : init was successful.
+ Return -1: In all other case.
+Remark: Initiates SchemaOp record after allocation.
+******************************************************************************/
+int
+NdbSchemaOp::init(NdbSchemaCon* aSchemaCon)
+{
+ theSchemaCon = aSchemaCon;
+ return 0;
+}//NdbSchemaOp::init()
+
+
+const NdbError &
+NdbSchemaOp::getNdbError() const
+{
+ return theSchemaCon->getNdbError();
+}
+
diff --git a/storage/ndb/test/src/UtilTransactions.cpp b/storage/ndb/test/src/UtilTransactions.cpp
new file mode 100644
index 00000000000..65c1a7ded31
--- /dev/null
+++ b/storage/ndb/test/src/UtilTransactions.cpp
@@ -0,0 +1,1451 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "UtilTransactions.hpp"
+#include <NdbSleep.h>
+#include <NdbScanFilter.hpp>
+
+#define VERBOSE 0
+
+UtilTransactions::UtilTransactions(const NdbDictionary::Table& _tab,
+ const NdbDictionary::Index* _idx):
+ tab(_tab), idx(_idx), pTrans(0)
+{
+ m_defaultClearMethod = 3;
+}
+
+UtilTransactions::UtilTransactions(Ndb* ndb,
+ const char * name,
+ const char * index) :
+ tab(* ndb->getDictionary()->getTable(name)),
+ idx(index ? ndb->getDictionary()->getIndex(index, name) : 0),
+ pTrans(0)
+{
+ m_defaultClearMethod = 3;
+}
+
+#define RESTART_SCAN 99
+
+#define RETURN_FAIL(err) return (err.code != 0 ? err.code : NDBT_FAILED)
+
+int
+UtilTransactions::clearTable(Ndb* pNdb,
+ int records,
+ int parallelism){
+ if(m_defaultClearMethod == 1){
+ return clearTable1(pNdb, records, parallelism);
+ } else if(m_defaultClearMethod == 2){
+ return clearTable2(pNdb, records, parallelism);
+ } else {
+ return clearTable3(pNdb, records, parallelism);
+ }
+}
+
+
+int
+UtilTransactions::clearTable1(Ndb* pNdb,
+ int records,
+ int parallelism)
+{
+ return clearTable3(pNdb, records, 1);
+}
+
+int
+UtilTransactions::clearTable2(Ndb* pNdb,
+ int records,
+ int parallelism)
+{
+ return clearTable3(pNdb, records, parallelism);
+}
+
+int
+UtilTransactions::clearTable3(Ndb* pNdb,
+ int records,
+ int parallelism){
+ // Scan all records exclusive and delete
+ // them one by one
+ int retryAttempt = 0;
+ const int retryMax = 10;
+ int deletedRows = 0;
+ int check;
+ NdbScanOperation *pOp;
+ NdbError err;
+
+ int par = parallelism;
+ while (true){
+ restart:
+ if (retryAttempt++ >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ err = pNdb->getNdbError();
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ continue;
+ }
+ goto failed;
+ }
+
+ pOp = getScanOperation(pTrans);
+ if (pOp == NULL) {
+ err = pTrans->getNdbError();
+ if(err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ par = 1;
+ goto restart;
+ }
+ goto failed;
+ }
+
+ if( pOp->readTuplesExclusive(par) ) {
+ err = pTrans->getNdbError();
+ goto failed;
+ }
+
+ if(pTrans->execute(NoCommit) != 0){
+ err = pTrans->getNdbError();
+ if(err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ continue;
+ }
+ goto failed;
+ }
+
+ while((check = pOp->nextResult(true)) == 0){
+ do {
+ if (pOp->deleteCurrentTuple() != 0){
+ goto failed;
+ }
+ deletedRows++;
+ } while((check = pOp->nextResult(false)) == 0);
+
+ if(check != -1){
+ check = pTrans->execute(Commit);
+ pTrans->restart();
+ }
+
+ err = pTrans->getNdbError();
+ if(check == -1){
+ if(err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ par = 1;
+ goto restart;
+ }
+ goto failed;
+ }
+ }
+ if(check == -1){
+ err = pTrans->getNdbError();
+ if(err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ par = 1;
+ goto restart;
+ }
+ goto failed;
+ }
+ pNdb->closeTransaction(pTrans);
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+
+ failed:
+ if(pTrans != 0) pNdb->closeTransaction(pTrans);
+ ERR(err);
+ return (err.code != 0 ? err.code : NDBT_FAILED);
+}
+
+int
+UtilTransactions::copyTableData(Ndb* pNdb,
+ const char* destName){
+ // Scan all records and copy
+ // them to destName table
+ int retryAttempt = 0;
+ const int retryMax = 10;
+ int insertedRows = 0;
+ int parallelism = 240;
+ int check;
+ NdbScanOperation *pOp;
+ NDBT_ResultRow row(tab);
+
+ while (true){
+
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ pOp = pTrans->getNdbScanOperation(tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples(NdbScanOperation::LM_Read, parallelism) ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ // Read all attributes
+ for (int a = 0; a < tab.getNoOfColumns(); a++){
+ if ((row.attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ while((eof = pOp->nextResult(true)) == 0){
+ do {
+ insertedRows++;
+ if (addRowToInsert(pNdb, pTrans, row, destName) != 0){
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ } while((eof = pOp->nextResult(false)) == 0);
+
+ check = pTrans->execute(Commit);
+ pTrans->restart();
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ // If error = 488 there should be no limit on number of retry attempts
+ if (err.code != 488)
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ pNdb->closeTransaction(pTrans);
+
+ g_info << insertedRows << " rows copied" << endl;
+
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+}
+
+int
+UtilTransactions::addRowToInsert(Ndb* pNdb,
+ NdbConnection* pInsTrans,
+ NDBT_ResultRow & row,
+ const char *insertTabName){
+
+ int check;
+ NdbOperation* pInsOp;
+
+ pInsOp = pInsTrans->getNdbOperation(insertTabName);
+ if (pInsOp == NULL) {
+ ERR(pInsTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ check = pInsOp->insertTuple();
+ if( check == -1 ) {
+ ERR(pInsTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+
+ // Set all attributes
+ for (int a = 0; a < tab.getNoOfColumns(); a++){
+ NdbRecAttr* r = row.attributeStore(a);
+ int sz = r->attrSize() * r->arraySize();
+ if (pInsOp->setValue(tab.getColumn(a)->getName(),
+ r->aRef(),
+ sz) != 0) {
+ ERR(pInsTrans->getNdbError());
+ return NDBT_FAILED;
+ }
+ }
+
+ return NDBT_OK;
+}
+
+
+int
+UtilTransactions::scanReadRecords(Ndb* pNdb,
+ int parallelism,
+ NdbOperation::LockMode lm,
+ int records,
+ int noAttribs,
+ int *attrib_list,
+ ReadCallBackFn* fn){
+
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check;
+ NdbScanOperation *pOp;
+ NDBT_ResultRow row(tab);
+
+ while (true){
+
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ pOp = getScanOperation(pTrans);
+ if (pOp == NULL) {
+ const NdbError err = pNdb->getNdbError();
+ pNdb->closeTransaction(pTrans);
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples(lm, 0, parallelism) ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ // Call getValue for all the attributes supplied in attrib_list
+ // ************************************************
+ for (int a = 0; a < noAttribs; a++){
+ if (attrib_list[a] < tab.getNoOfColumns()){
+ g_info << "getValue(" << attrib_list[a] << ")" << endl;
+ if ((row.attributeStore(attrib_list[a]) =
+ pOp->getValue(tab.getColumn(attrib_list[a])->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+ }
+ // *************************************************
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+
+
+ while((eof = pOp->nextResult()) == 0){
+ rows++;
+
+ // Call callback for each record returned
+ if(fn != NULL)
+ fn(&row);
+ }
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ pNdb->closeTransaction(pTrans);
+ g_info << rows << " rows have been read" << endl;
+ if (records != 0 && rows != records){
+ g_info << "Check expected number of records failed" << endl
+ << " expected=" << records <<", " << endl
+ << " read=" << rows << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+}
+
+int
+UtilTransactions::selectCount(Ndb* pNdb,
+ int parallelism,
+ int* count_rows,
+ NdbOperation::LockMode lm,
+ NdbConnection* pTrans){
+
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check;
+ NdbScanOperation *pOp;
+
+ if(!pTrans)
+ pTrans = pNdb->startTransaction();
+
+ while (true){
+
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+ pOp = getScanOperation(pTrans);
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples(lm) ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if(0){
+ NdbScanFilter sf(pOp);
+ sf.begin(NdbScanFilter::OR);
+ sf.eq(2, (Uint32)30);
+ sf.end();
+ } else {
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+
+
+ while((eof = pOp->nextResult()) == 0){
+ rows++;
+ }
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ pNdb->closeTransaction(pTrans);
+
+ if (count_rows != NULL){
+ *count_rows = rows;
+ }
+
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+}
+
+int
+UtilTransactions::verifyIndex(Ndb* pNdb,
+ const char* indexName,
+ int parallelism,
+ bool transactional){
+
+
+ const NdbDictionary::Index* pIndex
+ = pNdb->getDictionary()->getIndex(indexName, tab.getName());
+ if (pIndex == 0){
+ ndbout << " Index " << indexName << " does not exist!" << endl;
+ return NDBT_FAILED;
+ }
+
+ switch (pIndex->getType()){
+ case NdbDictionary::Index::UniqueHashIndex:
+ return verifyUniqueIndex(pNdb, pIndex, parallelism, transactional);
+ case NdbDictionary::Index::OrderedIndex:
+ return verifyOrderedIndex(pNdb, pIndex, parallelism, transactional);
+ break;
+ default:
+ ndbout << "Unknown index type" << endl;
+ break;
+ }
+
+ return NDBT_FAILED;
+}
+
+int
+UtilTransactions::verifyUniqueIndex(Ndb* pNdb,
+ const NdbDictionary::Index * pIndex,
+ int parallelism,
+ bool transactional){
+
+ /**
+ * Scan all rows in TABLE and for each found row make one read in
+ * TABLE and one using INDEX_TABLE. Then compare the two returned
+ * rows. They should be equal!
+ *
+ */
+
+ if (scanAndCompareUniqueIndex(pNdb,
+ pIndex,
+ parallelism,
+ transactional) != NDBT_OK){
+ return NDBT_FAILED;
+ }
+
+
+ return NDBT_OK;
+
+}
+
+
+int
+UtilTransactions::scanAndCompareUniqueIndex(Ndb* pNdb,
+ const NdbDictionary::Index* pIndex,
+ int parallelism,
+ bool transactional){
+
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check;
+ NdbScanOperation *pOp;
+ NDBT_ResultRow row(tab);
+
+ parallelism = 1;
+
+ while (true){
+restart:
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ pOp = pTrans->getNdbScanOperation(tab.getName());
+ if (pOp == NULL) {
+ const NdbError err = pNdb->getNdbError();
+ pNdb->closeTransaction(pTrans);
+ ERR(err);
+
+ if (err.status == NdbError::TemporaryError){
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ return NDBT_FAILED;
+ }
+
+ int rs;
+ if(transactional){
+ rs = pOp->readTuples(NdbScanOperation::LM_Read, 0, parallelism);
+ } else {
+ rs = pOp->readTuples(NdbScanOperation::LM_CommittedRead, 0, parallelism);
+ }
+
+ if( rs != 0 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ // Read all attributes
+ for (int a = 0; a < tab.getNoOfColumns(); a++){
+ if ((row.attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+
+
+ while((eof = pOp->nextResult()) == 0){
+ rows++;
+
+ // ndbout << row.c_str().c_str() << endl;
+
+ if (readRowFromTableAndIndex(pNdb,
+ pTrans,
+ pIndex,
+ row) != NDBT_OK){
+
+ while((eof= pOp->nextResult(false)) == 0);
+ if(eof == 2)
+ eof = pOp->nextResult(true); // this should give -1
+ if(eof == -1)
+ {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ goto restart;
+ }
+ }
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ pNdb->closeTransaction(pTrans);
+
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+}
+int
+UtilTransactions::readRowFromTableAndIndex(Ndb* pNdb,
+ NdbConnection* scanTrans,
+ const NdbDictionary::Index* pIndex,
+ NDBT_ResultRow& row ){
+
+
+ NdbDictionary::Index::Type indexType= pIndex->getType();
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check, a;
+ NdbConnection *pTrans1=NULL;
+ NdbOperation *pOp;
+
+ int return_code= NDBT_FAILED;
+
+ // Allocate place to store the result
+ NDBT_ResultRow tabRow(tab);
+ NDBT_ResultRow indexRow(tab);
+ const char * indexName = pIndex->getName();
+
+ while (true){
+ if(retryAttempt)
+ ndbout_c("retryAttempt %d", retryAttempt);
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ goto close_all;
+ }
+
+ pTrans1 = pNdb->hupp(scanTrans); //startTransaction();
+ if (pTrans1 == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+
+ if(err.code == 0){
+ return_code = NDBT_OK;
+ goto close_all;
+ }
+ ERR(err);
+ goto close_all;
+ }
+
+ /**
+ * Read the record from TABLE
+ */
+ pOp = pTrans1->getNdbOperation(tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans1->getNdbError());
+ goto close_all;
+ }
+
+ check = pOp->readTuple();
+ if( check == -1 ) {
+ ERR(pTrans1->getNdbError());
+ goto close_all;
+ }
+
+ // Define primary keys
+#if VERBOSE
+ printf("PK: ");
+#endif
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ const NdbDictionary::Column* attr = tab.getColumn(a);
+ if (attr->getPrimaryKey() == true){
+ if (pOp->equal(attr->getName(), row.attributeStore(a)->aRef()) != 0){
+ ERR(pTrans1->getNdbError());
+ goto close_all;
+ }
+#if VERBOSE
+ printf("%s = %d: ", attr->getName(), row.attributeStore(a)->aRef());
+#endif
+ }
+ }
+#if VERBOSE
+ printf("\n");
+#endif
+ // Read all attributes
+#if VERBOSE
+ printf("Reading %u attributes: ", tab.getNoOfColumns());
+#endif
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ if((tabRow.attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans1->getNdbError());
+ goto close_all;
+ }
+#if VERBOSE
+ printf("%s ", tab.getColumn(a)->getName());
+#endif
+ }
+#if VERBOSE
+ printf("\n");
+#endif
+
+ /**
+ * Read the record from INDEX_TABLE
+ */
+ NdbIndexOperation* pIndexOp= NULL;
+ NdbIndexScanOperation *pScanOp= NULL;
+ NdbOperation *pIOp= 0;
+
+ bool null_found= false;
+ for(a = 0; a<(int)pIndex->getNoOfColumns(); a++){
+ const NdbDictionary::Column * col = pIndex->getColumn(a);
+
+ if (row.attributeStore(col->getName())->isNULL())
+ {
+ null_found= true;
+ break;
+ }
+ }
+
+ const char * tabName= tab.getName();
+ if(!null_found)
+ {
+ if (indexType == NdbDictionary::Index::UniqueHashIndex) {
+ pIOp= pIndexOp= pTrans1->getNdbIndexOperation(indexName, tabName);
+ } else {
+ pIOp= pScanOp= pTrans1->getNdbIndexScanOperation(indexName, tabName);
+ }
+
+ if (pIOp == NULL) {
+ ERR(pTrans1->getNdbError());
+ goto close_all;
+ }
+
+ {
+ bool not_ok;
+ if (pIndexOp) {
+ not_ok = pIndexOp->readTuple() == -1;
+ } else {
+ not_ok = pScanOp->readTuples();
+ }
+
+ if( not_ok ) {
+ ERR(pTrans1->getNdbError());
+ goto close_all;
+ }
+ }
+
+ // Define primary keys for index
+#if VERBOSE
+ printf("SI: ");
+#endif
+ for(a = 0; a<(int)pIndex->getNoOfColumns(); a++){
+ const NdbDictionary::Column * col = pIndex->getColumn(a);
+
+ int r;
+ if ( !row.attributeStore(col->getName())->isNULL() ) {
+ if(pIOp->equal(col->getName(),
+ row.attributeStore(col->getName())->aRef()) != 0){
+ ERR(pTrans1->getNdbError());
+ goto close_all;
+ }
+ }
+#if VERBOSE
+ printf("%s = %d: ", col->getName(), row.attributeStore(a)->aRef());
+#endif
+ }
+#if VERBOSE
+ printf("\n");
+#endif
+
+ // Read all attributes
+#if VERBOSE
+ printf("Reading %u attributes: ", tab.getNoOfColumns());
+#endif
+ for(a = 0; a<tab.getNoOfColumns(); a++){
+ void* pCheck;
+
+ pCheck= indexRow.attributeStore(a)=
+ pIOp->getValue(tab.getColumn(a)->getName());
+
+ if(pCheck == NULL) {
+ ERR(pTrans1->getNdbError());
+ goto close_all;
+ }
+#if VERBOSE
+ printf("%s ", tab.getColumn(a)->getName());
+#endif
+ }
+ }
+#if VERBOSE
+ printf("\n");
+#endif
+ scanTrans->refresh();
+ check = pTrans1->execute(Commit);
+ if( check == -1 ) {
+ const NdbError err = pTrans1->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans1);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ndbout << "Error when comparing records - normal op" << endl;
+ ERR(err);
+ ndbout << "row: " << row.c_str().c_str() << endl;
+ goto close_all;
+ }
+
+ /**
+ * Compare the two rows
+ */
+ if(!null_found){
+ if (pScanOp) {
+ if (pScanOp->nextResult() != 0){
+ const NdbError err = pTrans1->getNdbError();
+ ERR(err);
+ ndbout << "Error when comparing records - index op next_result missing" << endl;
+ ndbout << "row: " << row.c_str().c_str() << endl;
+ goto close_all;
+ }
+ }
+ if (!(tabRow.c_str() == indexRow.c_str())){
+ ndbout << "Error when comapring records" << endl;
+ ndbout << " tabRow: \n" << tabRow.c_str().c_str() << endl;
+ ndbout << " indexRow: \n" << indexRow.c_str().c_str() << endl;
+ goto close_all;
+ }
+ if (pScanOp) {
+ if (pScanOp->nextResult() == 0){
+ ndbout << "Error when comparing records - index op next_result to many" << endl;
+ ndbout << "row: " << row.c_str().c_str() << endl;
+ goto close_all;
+ }
+ }
+ }
+ return_code= NDBT_OK;
+ goto close_all;
+ }
+
+close_all:
+ if (pTrans1)
+ pNdb->closeTransaction(pTrans1);
+
+ return return_code;
+}
+
+int
+UtilTransactions::verifyOrderedIndex(Ndb* pNdb,
+ const NdbDictionary::Index* pIndex,
+ int parallelism,
+ bool transactional){
+
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check;
+ NdbScanOperation *pOp;
+ NdbIndexScanOperation * iop = 0;
+
+ NDBT_ResultRow scanRow(tab);
+ NDBT_ResultRow pkRow(tab);
+ NDBT_ResultRow indexRow(tab);
+ const char * indexName = pIndex->getName();
+
+ int res;
+ parallelism = 1;
+
+ while (true){
+
+ if (retryAttempt >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ pOp = pTrans->getNdbScanOperation(tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if( pOp->readTuples(NdbScanOperation::LM_Read, 0, parallelism) ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if(get_values(pOp, scanRow))
+ {
+ abort();
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ int eof;
+ int rows = 0;
+ while(check == 0 && (eof = pOp->nextResult()) == 0){
+ rows++;
+
+ bool null_found= false;
+ for(int a = 0; a<(int)pIndex->getNoOfColumns(); a++){
+ const NdbDictionary::Column * col = pIndex->getColumn(a);
+ if (scanRow.attributeStore(col->getName())->isNULL())
+ {
+ null_found= true;
+ break;
+ }
+ }
+
+ // Do pk lookup
+ NdbOperation * pk = pTrans->getNdbOperation(tab.getName());
+ if(!pk || pk->readTuple())
+ goto error;
+ if(equal(&tab, pk, scanRow) || get_values(pk, pkRow))
+ goto error;
+
+ if(!null_found)
+ {
+ if(!iop && (iop= pTrans->getNdbIndexScanOperation(indexName,
+ tab.getName())))
+ {
+ if(iop->readTuples(NdbScanOperation::LM_CommittedRead,
+ parallelism))
+ goto error;
+ iop->interpret_exit_ok();
+ if(get_values(iop, indexRow))
+ goto error;
+ }
+ else if(!iop || iop->reset_bounds())
+ {
+ goto error;
+ }
+
+ if(equal(pIndex, iop, scanRow))
+ goto error;
+ }
+
+ check = pTrans->execute(NoCommit);
+ if(check)
+ goto error;
+
+ if(scanRow.c_str() != pkRow.c_str()){
+ g_err << "Error when comapring records" << endl;
+ g_err << " scanRow: \n" << scanRow.c_str().c_str() << endl;
+ g_err << " pkRow: \n" << pkRow.c_str().c_str() << endl;
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if(!null_found)
+ {
+
+ if((res= iop->nextResult()) != 0){
+ g_err << "Failed to find row using index: " << res << endl;
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if(scanRow.c_str() != indexRow.c_str()){
+ g_err << "Error when comapring records" << endl;
+ g_err << " scanRow: \n" << scanRow.c_str().c_str() << endl;
+ g_err << " indexRow: \n" << indexRow.c_str().c_str() << endl;
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if(iop->nextResult() == 0){
+ g_err << "Found extra row!!" << endl;
+ g_err << " indexRow: \n" << indexRow.c_str().c_str() << endl;
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+ }
+
+ if (eof == -1 || check == -1) {
+ error:
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ iop = 0;
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ rows--;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ pNdb->closeTransaction(pTrans);
+
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+}
+
+int
+UtilTransactions::get_values(NdbOperation* op, NDBT_ResultRow& dst)
+{
+ for (int a = 0; a < tab.getNoOfColumns(); a++){
+ NdbRecAttr*& ref= dst.attributeStore(a);
+ if ((ref= op->getValue(a)) == 0)
+ {
+ return NDBT_FAILED;
+ }
+ }
+ return 0;
+}
+
+int
+UtilTransactions::equal(const NdbDictionary::Index* pIndex,
+ NdbOperation* op, const NDBT_ResultRow& src)
+{
+ for(Uint32 a = 0; a<pIndex->getNoOfColumns(); a++){
+ const NdbDictionary::Column * col = pIndex->getColumn(a);
+ if(op->equal(col->getName(),
+ src.attributeStore(col->getName())->aRef()) != 0){
+ return NDBT_FAILED;
+ }
+ }
+ return 0;
+}
+
+int
+UtilTransactions::equal(const NdbDictionary::Table* pTable,
+ NdbOperation* op, const NDBT_ResultRow& src)
+{
+ for(Uint32 a = 0; a<tab.getNoOfColumns(); a++){
+ const NdbDictionary::Column* attr = tab.getColumn(a);
+ if (attr->getPrimaryKey() == true){
+ if (op->equal(attr->getName(), src.attributeStore(a)->aRef()) != 0){
+ return NDBT_FAILED;
+ }
+ }
+ }
+ return 0;
+}
+
+NdbScanOperation*
+UtilTransactions::getScanOperation(NdbConnection* pTrans)
+{
+ return (NdbScanOperation*)
+ getOperation(pTrans, NdbOperation::OpenScanRequest);
+}
+
+NdbOperation*
+UtilTransactions::getOperation(NdbConnection* pTrans,
+ NdbOperation::OperationType type)
+{
+ switch(type){
+ case NdbOperation::ReadRequest:
+ case NdbOperation::ReadExclusive:
+ if(idx)
+ {
+ switch(idx->getType()){
+ case NdbDictionary::Index::UniqueHashIndex:
+ return pTrans->getNdbIndexOperation(idx->getName(), tab.getName());
+ case NdbDictionary::Index::OrderedIndex:
+ return pTrans->getNdbIndexScanOperation(idx->getName(), tab.getName());
+ }
+ }
+ case NdbOperation::InsertRequest:
+ case NdbOperation::WriteRequest:
+ return pTrans->getNdbOperation(tab.getName());
+ case NdbOperation::UpdateRequest:
+ case NdbOperation::DeleteRequest:
+ if(idx)
+ {
+ switch(idx->getType()){
+ case NdbDictionary::Index::UniqueHashIndex:
+ return pTrans->getNdbIndexOperation(idx->getName(), tab.getName());
+ }
+ }
+ return pTrans->getNdbOperation(tab.getName());
+ case NdbOperation::OpenScanRequest:
+ if(idx)
+ {
+ switch(idx->getType()){
+ case NdbDictionary::Index::OrderedIndex:
+ return pTrans->getNdbIndexScanOperation(idx->getName(), tab.getName());
+ }
+ }
+ return pTrans->getNdbScanOperation(tab.getName());
+ case NdbOperation::OpenRangeScanRequest:
+ if(idx)
+ {
+ switch(idx->getType()){
+ case NdbDictionary::Index::OrderedIndex:
+ return pTrans->getNdbIndexScanOperation(idx->getName(), tab.getName());
+ }
+ }
+ return 0;
+ }
+}
+
+#include <HugoOperations.hpp>
+
+int
+UtilTransactions::compare(Ndb* pNdb, const char* tab_name2, int flags){
+
+
+ NdbError err;
+ int return_code= -1, row_count= 0;
+ int retryAttempt = 0, retryMax = 10;
+
+ HugoCalculator calc(tab);
+ NDBT_ResultRow row(tab);
+ NdbTransaction* pTrans= 0;
+ const NdbDictionary::Table* tmp= pNdb->getDictionary()->getTable(tab_name2);
+ if(tmp == 0)
+ {
+ g_err << "Unable to lookup table: " << tab_name2
+ << endl << pNdb->getDictionary()->getNdbError() << endl;
+ return -1;
+ }
+ const NdbDictionary::Table& tab2= *tmp;
+
+ HugoOperations cmp(tab2);
+ UtilTransactions count(tab2);
+
+ while (true){
+
+ if (retryAttempt++ >= retryMax){
+ g_info << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return -1;
+ }
+
+ NdbScanOperation *pOp= 0;
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ err = pNdb->getNdbError();
+ goto error;
+ }
+
+ pOp= pTrans->getNdbScanOperation(tab.getName());
+ if (pOp == NULL) {
+ ERR(err= pTrans->getNdbError());
+ goto error;
+ }
+
+ if( pOp->readTuples(NdbScanOperation::LM_Read) ) {
+ ERR(err= pTrans->getNdbError());
+ goto error;
+ }
+
+ if( pOp->interpret_exit_ok() == -1 ) {
+ ERR(err= pTrans->getNdbError());
+ goto error;
+ }
+
+ // Read all attributes
+ {
+ for (int a = 0; a < tab.getNoOfColumns(); a++){
+ if ((row.attributeStore(a) =
+ pOp->getValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(err= pTrans->getNdbError());
+ goto error;
+ }
+ }
+ }
+
+ if( pTrans->execute(NoCommit) == -1 ) {
+ ERR(err= pTrans->getNdbError());
+ goto error;
+ }
+
+ {
+ int eof;
+ while((eof = pOp->nextResult(true)) == 0)
+ {
+ do {
+ row_count++;
+ if(cmp.startTransaction(pNdb) != NDBT_OK)
+ {
+ ERR(err= pNdb->getNdbError());
+ goto error;
+ }
+ int rowNo= calc.getIdValue(&row);
+ if(cmp.pkReadRecord(pNdb, rowNo, 1) != NDBT_OK)
+ {
+ ERR(err= cmp.getTransaction()->getNdbError());
+ goto error;
+ }
+ if(cmp.execute_Commit(pNdb) != NDBT_OK)
+ {
+ ERR(err= cmp.getTransaction()->getNdbError());
+ goto error;
+ }
+ if(row != cmp.get_row(0))
+ {
+ g_err << "COMPARE FAILED" << endl;
+ g_err << row << endl;
+ g_err << cmp.get_row(0) << endl;
+ return_code= 1;
+ goto close;
+ }
+ retryAttempt= 0;
+ cmp.closeTransaction(pNdb);
+ } while((eof = pOp->nextResult(false)) == 0);
+ }
+ if (eof == -1)
+ {
+ err = pTrans->getNdbError();
+ goto error;
+ }
+ }
+
+ pTrans->close(); pTrans= 0;
+
+ g_info << row_count << " rows compared" << endl;
+ {
+ int row_count2;
+ if(count.selectCount(pNdb, 0, &row_count2) != NDBT_OK)
+ {
+ g_err << "Failed to count rows in tab_name2" << endl;
+ return -1;
+ }
+
+ g_info << row_count2 << " rows in tab_name2" << endl;
+ return (row_count == row_count2 ? 0 : 1);
+ }
+error:
+ if(err.status == NdbError::TemporaryError)
+ {
+ NdbSleep_MilliSleep(50);
+ if(pTrans != 0)
+ {
+ pTrans->close();
+ pTrans= 0;
+ }
+ if(cmp.getTransaction())
+ cmp.closeTransaction(pNdb);
+ continue;
+ }
+ break;
+ }
+
+close:
+ if(pTrans != 0) pTrans->close();
+
+ return return_code;
+}
diff --git a/storage/ndb/test/src/getarg.c b/storage/ndb/test/src/getarg.c
new file mode 100644
index 00000000000..5b67eb6343d
--- /dev/null
+++ b/storage/ndb/test/src/getarg.c
@@ -0,0 +1,608 @@
+/* -*- c-basic-offset: 4; -*- */
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ndb_global.h>
+
+#include "getarg.h"
+#include <basestring_vsnprintf.h>
+
+#ifndef HAVE_STRLCPY
+static size_t
+strlcpy (char *dst, const char *src, size_t dst_sz)
+{
+ size_t n;
+ char *p;
+ for (p = dst, n = 0;
+ n + 1 < dst_sz && *src != '\0';
+ ++p, ++src, ++n)
+ *p = *src;
+ *p = '\0';
+ if (*src == '\0')
+ return n;
+ else
+ return n + strlen (src);
+}
+#endif
+#ifndef HAVE_STRLCAT
+static size_t
+strlcat (char *dst, const char *src, size_t dst_sz)
+{
+ size_t len = strlen(dst);
+ return len + strlcpy (dst + len, src, dst_sz - len);
+}
+#endif
+
+#define ISFLAG(X) ((X).type == arg_flag || (X).type == arg_negative_flag)
+
+#ifndef max
+#define max(a, b) (a) > (b) ? (a) : (b)
+#endif
+
+#ifdef HAVE___PROGNAME
+extern char *__progname;
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+char *
+strupr(char *str)
+{
+ char *s;
+
+ for(s = str; *s; s++)
+ *s = toupper(*s);
+ return str;
+}
+
+static size_t
+print_arg (char *string, size_t len, int mdoc, int longp, struct getargs *arg)
+{
+ const char *s;
+
+ *string = '\0';
+
+ if (ISFLAG(*arg) || (!longp && arg->type == arg_counter))
+ return 0;
+
+ if(mdoc){
+ if(longp)
+ strlcat(string, "= Ns", len);
+ strlcat(string, " Ar ", len);
+ }else
+ if (longp)
+ strlcat (string, "=", len);
+ else
+ strlcat (string, " ", len);
+
+ if (arg->arg_help)
+ s = arg->arg_help;
+ else if (arg->type == arg_integer || arg->type == arg_counter)
+ s = "integer";
+ else if (arg->type == arg_string)
+ s = "string";
+ else if (arg->type == arg_double)
+ s = "float";
+ else
+ s = "<undefined>";
+
+ strlcat(string, s, len);
+ return 1 + strlen(s);
+}
+
+#ifdef GETARGMANDOC
+static void
+mandoc_template(struct getargs *args,
+ size_t num_args,
+ const char *progname,
+ const char *extra_string)
+{
+ size_t i;
+ char timestr[64], cmd[64];
+ char buf[128];
+ const char *p;
+ time_t t;
+
+ printf(".\\\" Things to fix:\n");
+ printf(".\\\" * correct section, and operating system\n");
+ printf(".\\\" * remove Op from mandatory flags\n");
+ printf(".\\\" * use better macros for arguments (like .Pa for files)\n");
+ printf(".\\\"\n");
+ t = time(NULL);
+ strftime(timestr, sizeof(timestr), "%B %e, %Y", localtime(&t));
+ printf(".Dd %s\n", timestr);
+ p = strrchr(progname, '/');
+ if(p) p++; else p = progname;
+ strlcpy(cmd, p, sizeof(cmd));
+ strupr(cmd);
+
+ printf(".Dt %s SECTION\n", cmd);
+ printf(".Os OPERATING_SYSTEM\n");
+ printf(".Sh NAME\n");
+ printf(".Nm %s\n", p);
+ printf(".Nd\n");
+ printf("in search of a description\n");
+ printf(".Sh SYNOPSIS\n");
+ printf(".Nm\n");
+ for(i = 0; i < num_args; i++){
+ /* we seem to hit a limit on number of arguments if doing
+ short and long flags with arguments -- split on two lines */
+ if(ISFLAG(args[i]) ||
+ args[i].short_name == 0 || args[i].long_name == NULL) {
+ printf(".Op ");
+
+ if(args[i].short_name) {
+ print_arg(buf, sizeof(buf), 1, 0, args + i);
+ printf("Fl %c%s", args[i].short_name, buf);
+ if(args[i].long_name)
+ printf(" | ");
+ }
+ if(args[i].long_name) {
+ print_arg(buf, sizeof(buf), 1, 1, args + i);
+ printf("Fl -%s%s%s",
+ args[i].type == arg_negative_flag ? "no-" : "",
+ args[i].long_name, buf);
+ }
+ printf("\n");
+ } else {
+ print_arg(buf, sizeof(buf), 1, 0, args + i);
+ printf(".Oo Fl %c%s \\*(Ba Xo\n", args[i].short_name, buf);
+ print_arg(buf, sizeof(buf), 1, 1, args + i);
+ printf(".Fl -%s%s Oc\n.Xc\n", args[i].long_name, buf);
+ }
+ /*
+ if(args[i].type == arg_strings)
+ fprintf (stderr, "...");
+ */
+ }
+ if (extra_string && *extra_string)
+ printf (".Ar %s\n", extra_string);
+ printf(".Sh DESCRIPTION\n");
+ printf("Supported options:\n");
+ printf(".Bl -tag -width Ds\n");
+ for(i = 0; i < num_args; i++){
+ printf(".It Xo\n");
+ if(args[i].short_name){
+ printf(".Fl %c", args[i].short_name);
+ print_arg(buf, sizeof(buf), 1, 0, args + i);
+ printf("%s", buf);
+ if(args[i].long_name)
+ printf(" Ns ,");
+ printf("\n");
+ }
+ if(args[i].long_name){
+ printf(".Fl -%s%s",
+ args[i].type == arg_negative_flag ? "no-" : "",
+ args[i].long_name);
+ print_arg(buf, sizeof(buf), 1, 1, args + i);
+ printf("%s\n", buf);
+ }
+ printf(".Xc\n");
+ if(args[i].help)
+ printf("%s\n", args[i].help);
+ /*
+ if(args[i].type == arg_strings)
+ fprintf (stderr, "...");
+ */
+ }
+ printf(".El\n");
+ printf(".\\\".Sh ENVIRONMENT\n");
+ printf(".\\\".Sh FILES\n");
+ printf(".\\\".Sh EXAMPLES\n");
+ printf(".\\\".Sh DIAGNOSTICS\n");
+ printf(".\\\".Sh SEE ALSO\n");
+ printf(".\\\".Sh STANDARDS\n");
+ printf(".\\\".Sh HISTORY\n");
+ printf(".\\\".Sh AUTHORS\n");
+ printf(".\\\".Sh BUGS\n");
+}
+#endif /* GETARGMANDOC */
+
+static int
+check_column(FILE *f, int col, int len, int columns)
+{
+ if(col + len > columns) {
+ fprintf(f, "\n");
+ col = fprintf(f, " ");
+ }
+ return col;
+}
+
+void
+arg_printusage (struct getargs *args,
+ size_t num_args,
+ const char *progname,
+ const char *extra_string)
+{
+ unsigned int i;
+ size_t max_len = 0;
+ char buf[128];
+ int col = 0, columns;
+
+#ifdef HAVE___PROGNAME
+ if (progname == NULL)
+ progname = __progname;
+#endif
+ if (progname == NULL)
+ progname = "";
+
+#ifdef GETARGMANDOC
+ if(getenv("GETARGMANDOC")){
+ mandoc_template(args, num_args, progname, extra_string);
+ return;
+ }
+#endif
+
+ columns = 80; /* Always assume that the window is 80 chars wide */
+ col = 0;
+ col += fprintf (stderr, "Usage: %s", progname);
+ for (i = 0; i < num_args; ++i) {
+ size_t len = 0;
+
+ if (args[i].long_name) {
+ buf[0] = '\0';
+ strlcat(buf, "[--", sizeof(buf));
+ len += 2;
+ if(args[i].type == arg_negative_flag) {
+ strlcat(buf, "no-", sizeof(buf));
+ len += 3;
+ }
+ strlcat(buf, args[i].long_name, sizeof(buf));
+ len += strlen(args[i].long_name);
+ len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ 0, 1, &args[i]);
+ strlcat(buf, "]", sizeof(buf));
+ if(args[i].type == arg_strings)
+ strlcat(buf, "...", sizeof(buf));
+ col = check_column(stderr, col, strlen(buf) + 1, columns);
+ col += fprintf(stderr, " %s", buf);
+ }
+ if (args[i].short_name) {
+ basestring_snprintf(buf, sizeof(buf), "[-%c", args[i].short_name);
+ len += 2;
+ len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ 0, 0, &args[i]);
+ strlcat(buf, "]", sizeof(buf));
+ if(args[i].type == arg_strings)
+ strlcat(buf, "...", sizeof(buf));
+ col = check_column(stderr, col, strlen(buf) + 1, columns);
+ col += fprintf(stderr, " %s", buf);
+ }
+ if (args[i].long_name && args[i].short_name)
+ len += 2; /* ", " */
+ max_len = max(max_len, len);
+ }
+ if (extra_string) {
+ col = check_column(stderr, col, strlen(extra_string) + 1, columns);
+ fprintf (stderr, " %s\n", extra_string);
+ } else
+ fprintf (stderr, "\n");
+ for (i = 0; i < num_args; ++i) {
+ if (args[i].help) {
+ size_t count = 0;
+
+ if (args[i].short_name) {
+ count += fprintf (stderr, "-%c", args[i].short_name);
+ print_arg (buf, sizeof(buf), 0, 0, &args[i]);
+ count += fprintf(stderr, "%s", buf);
+ }
+ if (args[i].short_name && args[i].long_name)
+ count += fprintf (stderr, ", ");
+ if (args[i].long_name) {
+ count += fprintf (stderr, "--");
+ if (args[i].type == arg_negative_flag)
+ count += fprintf (stderr, "no-");
+ count += fprintf (stderr, "%s", args[i].long_name);
+ print_arg (buf, sizeof(buf), 0, 1, &args[i]);
+ count += fprintf(stderr, "%s", buf);
+ }
+ while(count++ <= max_len)
+ putc (' ', stderr);
+ fprintf (stderr, "%s\n", args[i].help);
+ }
+ }
+}
+
+static void
+add_string(getarg_strings *s, char *value)
+{
+ s->strings = realloc(s->strings, (s->num_strings + 1) * sizeof(*s->strings));
+ s->strings[s->num_strings] = value;
+ s->num_strings++;
+}
+
+static int
+arg_match_long(struct getargs *args, size_t num_args,
+ char *argv, int argc, const char **rargv, int *optind)
+{
+ unsigned int i;
+ const char *optarg = NULL;
+ int negate = 0;
+ int partial_match = 0;
+ struct getargs *partial = NULL;
+ struct getargs *current = NULL;
+ int argv_len;
+ char *p;
+
+ argv_len = strlen(argv);
+ p = strchr (argv, '=');
+ if (p != NULL)
+ argv_len = p - argv;
+
+ for (i = 0; i < num_args; ++i) {
+ if(args[i].long_name) {
+ int len = strlen(args[i].long_name);
+ char *p = argv;
+ int p_len = argv_len;
+ negate = 0;
+
+ for (;;) {
+ if (strncmp (args[i].long_name, p, p_len) == 0) {
+ if(p_len == len)
+ current = &args[i];
+ else {
+ ++partial_match;
+ partial = &args[i];
+ }
+ optarg = p + p_len;
+ } else if (ISFLAG(args[i]) && strncmp (p, "no-", 3) == 0) {
+ negate = !negate;
+ p += 3;
+ p_len -= 3;
+ continue;
+ }
+ break;
+ }
+ if (current)
+ break;
+ }
+ }
+ if (current == NULL) {
+ if (partial_match == 1)
+ current = partial;
+ else
+ return ARG_ERR_NO_MATCH;
+ }
+
+ if(*optarg == '\0'
+ && !ISFLAG(*current)
+ && current->type != arg_collect
+ && current->type != arg_counter)
+ return ARG_ERR_NO_MATCH;
+ switch(current->type){
+ case arg_integer:
+ {
+ int tmp;
+ if(sscanf(optarg + 1, "%d", &tmp) != 1)
+ return ARG_ERR_BAD_ARG;
+ *(int*)current->value = tmp;
+ return 0;
+ }
+ case arg_string:
+ {
+ *(char**)current->value = (char*)optarg + 1;
+ return 0;
+ }
+ case arg_strings:
+ {
+ add_string((getarg_strings*)current->value, (char*)optarg + 1);
+ return 0;
+ }
+ case arg_flag:
+ case arg_negative_flag:
+ {
+ int *flag = current->value;
+ if(*optarg == '\0' ||
+ strcmp(optarg + 1, "yes") == 0 ||
+ strcmp(optarg + 1, "true") == 0){
+ *flag = !negate;
+ return 0;
+ } else if (*optarg && strcmp(optarg + 1, "maybe") == 0) {
+ *flag = rand() & 1;
+ } else {
+ *flag = negate;
+ return 0;
+ }
+ return ARG_ERR_BAD_ARG;
+ }
+ case arg_counter :
+ {
+ int val;
+
+ if (*optarg == '\0')
+ val = 1;
+ else {
+ char *endstr;
+
+ val = strtol (optarg, &endstr, 0);
+ if (endstr == optarg)
+ return ARG_ERR_BAD_ARG;
+ }
+ *(int *)current->value += val;
+ return 0;
+ }
+ case arg_double:
+ {
+ double tmp;
+ if(sscanf(optarg + 1, "%lf", &tmp) != 1)
+ return ARG_ERR_BAD_ARG;
+ *(double*)current->value = tmp;
+ return 0;
+ }
+ case arg_collect:{
+ struct getarg_collect_info *c = current->value;
+ int o = argv - rargv[*optind];
+ return (*c->func)(FALSE, argc, rargv, optind, &o, c->data);
+ }
+
+ default:
+ abort ();
+ }
+}
+
+static int
+arg_match_short (struct getargs *args, size_t num_args,
+ char *argv, int argc, const char **rargv, int *optind)
+{
+ int j, k;
+
+ for(j = 1; j > 0 && j < (int)strlen(rargv[*optind]); j++) {
+ for(k = 0; k < (int)num_args; k++) {
+ char *optarg;
+
+ if(args[k].short_name == 0)
+ continue;
+ if(argv[j] == args[k].short_name) {
+ if(args[k].type == arg_flag) {
+ *(int*)args[k].value = 1;
+ break;
+ }
+ if(args[k].type == arg_negative_flag) {
+ *(int*)args[k].value = 0;
+ break;
+ }
+ if(args[k].type == arg_counter) {
+ ++*(int *)args[k].value;
+ break;
+ }
+ if(args[k].type == arg_collect) {
+ struct getarg_collect_info *c = args[k].value;
+
+ if((*c->func)(TRUE, argc, rargv, optind, &j, c->data))
+ return ARG_ERR_BAD_ARG;
+ break;
+ }
+
+ if(argv[j + 1])
+ optarg = &argv[j + 1];
+ else {
+ ++*optind;
+ optarg = (char *) rargv[*optind];
+ }
+ if(optarg == NULL) {
+ --*optind;
+ return ARG_ERR_NO_ARG;
+ }
+ if(args[k].type == arg_integer) {
+ int tmp;
+ if(sscanf(optarg, "%d", &tmp) != 1)
+ return ARG_ERR_BAD_ARG;
+ *(int*)args[k].value = tmp;
+ return 0;
+ } else if(args[k].type == arg_string) {
+ *(char**)args[k].value = optarg;
+ return 0;
+ } else if(args[k].type == arg_strings) {
+ add_string((getarg_strings*)args[k].value, optarg);
+ return 0;
+ } else if(args[k].type == arg_double) {
+ double tmp;
+ if(sscanf(optarg, "%lf", &tmp) != 1)
+ return ARG_ERR_BAD_ARG;
+ *(double*)args[k].value = tmp;
+ return 0;
+ }
+ return ARG_ERR_BAD_ARG;
+ }
+ }
+ if (k == (int)num_args)
+ return ARG_ERR_NO_MATCH;
+ }
+ return 0;
+}
+
+int
+getarg(struct getargs *args, size_t num_args,
+ int argc, const char **argv, int *optind)
+{
+ int i;
+ int ret = 0;
+
+ srand (time(NULL));
+ (*optind)++;
+ for(i = *optind; i < argc; i++) {
+ if(argv[i][0] != '-')
+ break;
+ if(argv[i][1] == '-'){
+ if(argv[i][2] == 0){
+ i++;
+ break;
+ }
+ ret = arg_match_long (args, num_args, (char *) argv[i] + 2,
+ argc, argv, &i);
+ } else {
+ ret = arg_match_short (args, num_args, (char *) argv[i],
+ argc, argv, &i);
+ }
+ if(ret)
+ break;
+ }
+ *optind = i;
+ return ret;
+}
+
+
+#if TEST
+int foo_flag = 2;
+int flag1 = 0;
+int flag2 = 0;
+int bar_int;
+char *baz_string;
+
+struct getargs args[] = {
+ { NULL, '1', arg_flag, &flag1, "one", NULL },
+ { NULL, '2', arg_flag, &flag2, "two", NULL },
+ { "foo", 'f', arg_negative_flag, &foo_flag, "foo", NULL },
+ { "bar", 'b', arg_integer, &bar_int, "bar", "seconds"},
+ { "baz", 'x', arg_string, &baz_string, "baz", "name" },
+};
+
+int main(int argc, char **argv)
+{
+ int optind = 0;
+ while(getarg(args, 5, argc, argv, &optind))
+ printf("Bad arg: %s\n", argv[optind]);
+ printf("flag1 = %d\n", flag1);
+ printf("flag2 = %d\n", flag2);
+ printf("foo_flag = %d\n", foo_flag);
+ printf("bar_int = %d\n", bar_int);
+ printf("baz_flag = %s\n", baz_string);
+ arg_printusage (args, 5, argv[0], "nothing here");
+}
+#endif
diff --git a/storage/ndb/test/tools/Makefile.am b/storage/ndb/test/tools/Makefile.am
new file mode 100644
index 00000000000..873136e254d
--- /dev/null
+++ b/storage/ndb/test/tools/Makefile.am
@@ -0,0 +1,30 @@
+
+ndbtest_PROGRAMS = hugoLoad hugoFill hugoLockRecords hugoPkDelete hugoPkRead hugoPkReadRecord hugoPkUpdate hugoScanRead hugoScanUpdate restart verify_index copy_tab create_index ndb_cpcc
+
+# transproxy
+
+hugoFill_SOURCES = hugoFill.cpp
+hugoLoad_SOURCES = hugoLoad.cpp
+hugoLockRecords_SOURCES = hugoLockRecords.cpp
+hugoPkDelete_SOURCES = hugoPkDelete.cpp
+hugoPkRead_SOURCES = hugoPkRead.cpp
+hugoPkReadRecord_SOURCES = hugoPkReadRecord.cpp
+hugoPkUpdate_SOURCES = hugoPkUpdate.cpp
+hugoScanRead_SOURCES = hugoScanRead.cpp
+hugoScanUpdate_SOURCES = hugoScanUpdate.cpp
+restart_SOURCES = restart.cpp
+# transproxy_SOURCES = transproxy.cpp
+verify_index_SOURCES = verify_index.cpp
+copy_tab_SOURCES = copy_tab.cpp
+create_index_SOURCES = create_index.cpp
+ndb_cpcc_SOURCES = cpcc.cpp
+
+include $(top_srcdir)/ndb/config/common.mk.am
+include $(top_srcdir)/ndb/config/type_ndbapitest.mk.am
+
+ndb_cpcc_LDADD = $(LDADD)
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
+
+windoze-dsp:
diff --git a/storage/ndb/test/tools/copy_tab.cpp b/storage/ndb/test/tools/copy_tab.cpp
new file mode 100644
index 00000000000..97370b170ef
--- /dev/null
+++ b/storage/ndb/test/tools/copy_tab.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_global.h>
+
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NDBT.hpp>
+#include "UtilTransactions.hpp"
+
+#include <getarg.h>
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ const char* _tabname = NULL;
+ const char* _to_tabname = NULL;
+ const char* _dbname = "TEST_DB";
+ const char* _connectstr = NULL;
+ int _copy_data = true;
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "database", 'd', arg_string, &_dbname, "dbname",
+ "Name of database table is in"},
+ { "connstr", 'c', arg_string, &_connectstr, "connect string",
+ "How to connect to NDB"},
+ { "copy-data", '\0', arg_negative_flag, &_copy_data, "Don't copy data to new table",
+ "How to connect to NDB"},
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "srctab desttab\n"\
+ "This program will copy one table in Ndb\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) ||
+ argv[optind] == NULL || argv[optind + 1] == NULL || _help){
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _tabname = argv[optind];
+ _to_tabname = argv[optind+1];
+
+ Ndb_cluster_connection con(_connectstr);
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb MyNdb(&con,_dbname);
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ ndbout << "Copying table " << _tabname << " to " << _to_tabname << "...";
+ const NdbDictionary::Table* ptab = MyNdb.getDictionary()->getTable(_tabname);
+ if (ptab){
+ NdbDictionary::Table tab2(*ptab);
+ tab2.setName(_to_tabname);
+ if (MyNdb.getDictionary()->createTable(tab2) != 0){
+ ndbout << endl << MyNdb.getDictionary()->getNdbError() << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ } else {
+ ndbout << endl << MyNdb.getDictionary()->getNdbError() << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ ndbout << "OK" << endl;
+ if (_copy_data){
+ ndbout << "Copying data..."<<endl;
+ const NdbDictionary::Table * tab3 =
+ NDBT_Table::discoverTableFromDb(&MyNdb,
+ _tabname);
+ // if (!tab3)
+
+ UtilTransactions util(*tab3);
+
+ if(util.copyTableData(&MyNdb,
+ _to_tabname) != NDBT_OK){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ ndbout << "OK" << endl;
+ }
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/test/tools/cpcc.cpp b/storage/ndb/test/tools/cpcc.cpp
new file mode 100644
index 00000000000..dd59e577f2c
--- /dev/null
+++ b/storage/ndb/test/tools/cpcc.cpp
@@ -0,0 +1,352 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <getarg.h>
+#include "CpcClient.hpp"
+#include <NdbEnv.h>
+
+#define DEFAULT_PORT 1234
+#define ENV_HOSTS "NDB_CPCC_HOSTS"
+
+struct settings {
+ int m_longl;
+ short m_port;
+} g_settings = { 0 , DEFAULT_PORT };
+
+Vector<SimpleCpcClient*> g_hosts;
+int connect(Vector<SimpleCpcClient*>&);
+
+class Expression {
+public:
+ virtual bool evaluate(SimpleCpcClient*, const SimpleCpcClient::Process &)= 0;
+};
+
+int for_each(Vector<SimpleCpcClient*>& list, Expression &);
+int start_stop(const char * cmd, Vector<SimpleCpcClient*>& list,
+ Vector<Vector<Uint32> >& procs);
+
+class True : public Expression {
+public:
+ virtual bool evaluate(SimpleCpcClient*, const SimpleCpcClient::Process & p){
+ return true;
+ }
+};
+
+class FieldEQ : public Expression {
+ BaseString m_field;
+ BaseString m_value;
+public:
+ FieldEQ(const BaseString & field, const BaseString & value){
+ m_field = field;
+ m_value = value;
+ }
+ virtual ~FieldEQ(){}
+
+ virtual bool evaluate(SimpleCpcClient*, const SimpleCpcClient::Process & p){
+ BaseString v;
+ if(m_field == "name") v = p.m_name;
+
+ if(m_field == "type") v = p.m_type;
+ if(m_field == "status") v = p.m_status;
+ if(m_field == "owner") v = p.m_owner;
+ if(m_field == "group") v = p.m_group;
+ if(m_field == "path") v = p.m_path;
+ if(m_field == "args") v = p.m_args;
+ if(m_field == "env") v = p.m_env;
+ if(m_field == "cwd") v = p.m_cwd;
+
+ if(m_field == "stdin") v = p.m_stdin;
+ if(m_field == "stdout") v = p.m_stdout;
+ if(m_field == "stderr") v = p.m_stderr;
+
+ return v == m_value;
+ }
+};
+
+class Match : public Expression {
+ Expression & m_cond;
+ Expression & m_apply;
+public:
+ Match(Expression& condition, Expression & rule)
+ : m_cond(condition), m_apply(rule) {
+ }
+ virtual ~Match(){}
+
+ virtual bool evaluate(SimpleCpcClient* c,const SimpleCpcClient::Process & p){
+ if(m_cond.evaluate(c, p))
+ return m_apply.evaluate(c, p);
+ return false;
+ }
+};
+
+class Operate : public Expression {
+ const char * cmd;
+ SimpleCpcClient * host;
+ settings & sets;
+public:
+ Operate(const char * c, settings & s) : sets(s) {
+ cmd = c;
+ host = 0;
+ }
+
+ virtual bool evaluate(SimpleCpcClient*, const SimpleCpcClient::Process & p);
+};
+
+class ProcEQ : public Expression {
+ SimpleCpcClient * host;
+ Uint32 id;
+public:
+ ProcEQ(SimpleCpcClient* h, Uint32 i){
+ host = h; id = i;
+ }
+
+ virtual bool evaluate(SimpleCpcClient* c,const SimpleCpcClient::Process & p){
+ return p.m_id == (int)id && c == host;
+ }
+};
+
+class OrExpr : public Expression {
+ Expression * m_rule;
+ Vector<Expression *> m_cond;
+ bool on_empty;
+public:
+ OrExpr(Expression * rule, bool onEmp = true){
+ m_rule = rule;
+ on_empty = onEmp;
+ }
+
+ virtual ~OrExpr(){}
+
+ virtual bool evaluate(SimpleCpcClient* c, const SimpleCpcClient::Process & p){
+ bool run = on_empty;
+ for(size_t i = 0; i<m_cond.size(); i++){
+ if(m_cond[i]->evaluate(c, p)){
+ run = true;
+ break;
+ }
+ }
+ if(run)
+ return m_rule->evaluate(c, p);
+ return false;
+ }
+
+ void push_back(Expression * expr){
+ m_cond.push_back(expr);
+ }
+};
+
+void
+add_host(Vector<SimpleCpcClient*> & hosts, BaseString tmp){
+ Vector<BaseString> split;
+ tmp.split(split, ":");
+
+ short port = g_settings.m_port;
+ if(split.size() > 1)
+ port = atoi(split[1].c_str());
+
+ hosts.push_back(new SimpleCpcClient(split[0].c_str(), port));
+}
+
+void
+add_hosts(Vector<SimpleCpcClient*> & hosts, BaseString list){
+ Vector<BaseString> split;
+ list.split(split);
+ for(size_t i = 0; i<split.size(); i++){
+ add_host(hosts, split[i]);
+ }
+}
+
+int
+main(int argc, const char** argv){
+ ndb_init();
+ int help = 0;
+ const char *cmd=0, *name=0, *group=0, *owner=0;
+ int list = 0, start = 0, stop = 0, rm = 0;
+ struct getargs args[] = {
+ { "cmd", 'c', arg_string, &cmd, "command", "command to run (default ls)" }
+ ,{ "name", 'n', arg_string, &name,
+ "apply command for all processes with name" }
+ ,{ "group", 'g', arg_string, &group,
+ "apply command for all processes in group" }
+ ,{ "owner", 'g', arg_string, &owner,
+ "apply command for all processes with owner" }
+ ,{ "long", 'l', arg_flag, &g_settings.m_longl, "long", "long listing"}
+ ,{ "usage", '?', arg_flag, &help, "Print help", "" }
+ ,{ "ls", 0, arg_flag, &list, "-c list", "list process(es)" }
+ ,{ "start", 0, arg_flag, &start, "-c start", "start process(es)" }
+ ,{ "stop", 0, arg_flag, &stop, "-c stop", "stop process(es)" }
+ ,{ "rm", 0, arg_flag, &rm, "-c rm", "undefine process(es)" }
+ };
+ const int num_args = 10;
+ int i;
+ int optind = 0;
+ char desc[] = "[host:[port]]\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return 1;
+ }
+
+ if(list + start + stop + rm > 1){
+ ndbout_c("Can only specify one command");
+ arg_printusage(args, num_args, argv[0], desc);
+ return 1;
+ }
+
+ if(list) cmd = "list";
+ if(start) cmd = "start";
+ if(stop) cmd = "stop";
+ if(rm) cmd = "rm";
+ if(!cmd) cmd = "list";
+
+ Expression * m_expr = 0;
+
+ for(i = optind; i<argc; i++){
+ add_host(g_hosts, argv[i]);
+ }
+
+ OrExpr * orE = new OrExpr(new Operate(cmd, g_settings), true);
+ m_expr = orE;
+ for(i = optind; i<argc; i++){
+ BaseString tmp(argv[i]);
+ Vector<BaseString> split;
+ tmp.split(split, ":");
+
+ if(split.size() > 2){
+ Uint32 id = atoi(split[2].c_str());
+ orE->push_back(new ProcEQ(g_hosts[i-optind], id));
+ }
+ }
+
+ if(g_hosts.size() == 0){
+ char buf[1024];
+ if(NdbEnv_GetEnv(ENV_HOSTS, buf, sizeof(buf))){
+ add_hosts(g_hosts, BaseString(buf));
+ }
+ }
+
+ if(g_hosts.size() == 0){
+ g_hosts.push_back(new SimpleCpcClient("localhost", g_settings.m_port));
+ }
+
+ if(group != 0){
+ Expression * tmp = new FieldEQ("group", group);
+ m_expr = new Match(* tmp, * m_expr);
+ }
+
+ if(name != 0){
+ Expression * tmp = new FieldEQ("name", name);
+ m_expr = new Match(* tmp, * m_expr);
+ }
+
+ if(owner != 0){
+ Expression * tmp = new FieldEQ("owner", owner);
+ m_expr = new Match(* tmp, * m_expr);
+ }
+
+ connect(g_hosts);
+ for_each(g_hosts, * m_expr);
+
+ return 0;
+}
+
+int
+connect(Vector<SimpleCpcClient*>& list){
+ for(size_t i = 0; i<list.size(); i++){
+ if(list[i]->connect() != 0){
+ ndbout_c("Failed to connect to %s:%d",
+ list[i]->getHost(), list[i]->getPort());
+ delete list[i]; list[i] = 0;
+ }
+ }
+ return 0;
+}
+
+int
+for_each(Vector<SimpleCpcClient*>& list, Expression & expr){
+ for(size_t i = 0; i<list.size(); i++){
+ if(list[i] == 0)
+ continue;
+ Properties p;
+ Vector<SimpleCpcClient::Process> procs;
+ if(list[i]->list_processes(procs, p) != 0){
+ ndbout << "Failed to list processes on "
+ << list[i]->getHost() << ":" << list[i]->getPort() << endl;
+ }
+ for(size_t j = 0; j<procs.size(); j++)
+ expr.evaluate(list[i], procs[j]);
+ }
+ return 0;
+}
+
+bool
+Operate::evaluate(SimpleCpcClient* c, const SimpleCpcClient::Process & pp){
+ Uint32 id = pp.m_id;
+ Properties p;
+ int res;
+
+ if(strcasecmp(cmd, "start") == 0)
+ res = c->start_process(id, p);
+ else if(strcasecmp(cmd, "stop") == 0)
+ res = c->stop_process(id, p);
+ else if(strcasecmp(cmd, "rm") == 0)
+ res = c->undefine_process(id, p);
+ else if(strcasecmp(cmd, "list") == 0){
+ if(!sets.m_longl){
+ if(host != c){
+ ndbout_c("--- %s:%d", c->getHost(), c->getPort());
+ host = c;
+ }
+ }
+
+ char s = 0;
+ const char * status = pp.m_status.c_str();
+ if(strcmp(status, "stopped") == 0) s = '-';
+ if(strcmp(status, "starting") == 0) s = 's';
+ if(strcmp(status, "running") == 0) s = 'r';
+ if(strcmp(status, "stopping") == 0) s = 'k';
+ if(s == 0) s = '?';
+
+ if(!sets.m_longl){
+ ndbout_c("%c%c\t%d\t%s\t%s\t%s(%s)",
+ s,
+ pp.m_type.c_str()[0], id, pp.m_owner.c_str(),
+ pp.m_group.c_str(), pp.m_name.c_str(), pp.m_path.c_str());
+ } else {
+ ndbout_c("%c%c %s:%d:%d %s %s %s(%s)",
+ s, pp.m_type.c_str()[0], c->getHost(), c->getPort(),
+ id, pp.m_owner.c_str(), pp.m_group.c_str(),
+ pp.m_name.c_str(), pp.m_path.c_str());
+ }
+ return true;
+ }
+
+ if(res != 0){
+ BaseString msg;
+ p.get("errormessage", msg);
+ ndbout_c("Failed to %s %d on %s:%d - %s",
+ cmd, id,
+ c->getHost(), c->getPort(), msg.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+template class Vector<Expression*>;
+template class Vector<SimpleCpcClient*>;
diff --git a/storage/ndb/test/tools/create_index.cpp b/storage/ndb/test/tools/create_index.cpp
new file mode 100644
index 00000000000..9f9c26aa0da
--- /dev/null
+++ b/storage/ndb/test/tools/create_index.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 <ndb_global.h>
+
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NDBT.hpp>
+
+#include <getarg.h>
+
+
+
+int
+main(int argc, const char** argv){
+ ndb_init();
+
+ const char* _dbname = "TEST_DB";
+ int _help = 0;
+ int _ordered = 0, _pk = 1;
+
+ struct getargs args[] = {
+ { "database", 'd', arg_string, &_dbname, "dbname",
+ "Name of database table is in"},
+ { "ordered", 'o', arg_flag, &_ordered, "Create ordered index", "" },
+ { "pk", 'p', arg_flag, &_pk, "Create index on primary key", "" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "<tabname>+\n"\
+ "This program will create one unique hash index named ind_<tabname> "
+ " for each table. The index will contain all columns in the table";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help ||
+ argv[optind] == NULL){
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ Ndb MyNdb(&con, _dbname);
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ NdbDictionary::Dictionary * dict = MyNdb.getDictionary();
+
+ for(int i = optind; i<argc; i++){
+ const NdbDictionary::Table * tab = dict->getTable(argv[i]);
+ if(tab == 0){
+ g_err << "Unknown table: " << argv[i] << endl;
+ continue;
+ }
+
+ if(tab->getNoOfColumns() > 16){
+ g_err << "Table " << argv[i] << " has more than 16 columns" << endl;
+ }
+
+ NdbDictionary::Index ind;
+ if(_ordered){
+ ind.setType(NdbDictionary::Index::OrderedIndex);
+ ind.setLogging(false);
+ } else {
+ ind.setType(NdbDictionary::Index::UniqueHashIndex);
+ }
+ char buf[512];
+ sprintf(buf, "IND_%s_%s_%c",
+ argv[i], (_pk ? "PK" : "FULL"), (_ordered ? 'O' : 'U'));
+ ind.setName(buf);
+ ind.setTable(argv[i]);
+ for(int c = 0; c<tab->getNoOfColumns(); c++){
+ if(!_pk || tab->getColumn(c)->getPrimaryKey())
+ ind.addIndexColumn(tab->getColumn(c)->getName());
+ }
+ ndbout << "creating index " << buf << " on table " << argv[i] << "...";
+ const int res = dict->createIndex(ind);
+ if(res != 0)
+ ndbout << endl << dict->getNdbError() << endl;
+ else
+ ndbout << "OK" << endl;
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
+
diff --git a/storage/ndb/test/tools/hugoCalculator.cpp b/storage/ndb/test/tools/hugoCalculator.cpp
new file mode 100644
index 00000000000..82c4bbff1a4
--- /dev/null
+++ b/storage/ndb/test/tools/hugoCalculator.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_global.h>
+
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <NDBT_Tables.hpp>
+#include <getarg.h>
+#include <NDBT.hpp>
+#include <HugoCalculator.hpp>
+
+//extern NdbOut g_info;
+
+int main(int argc, const char** argv)
+{
+ ndb_init();
+ int _row = 0;
+ int _column = 0;
+ int _updates = 0;
+ const char* _tableName = NULL;
+
+ struct getargs args[] = {
+ { "row", 'r', arg_integer, &_row, "The row number", "row" },
+ { "column", 'c', arg_integer, &_column, "The column id", "column" },
+ { "updates", 'u', arg_integer, &_updates, "# of updates", "updates" }
+ };
+
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+
+ if(getarg(args, num_args, argc, argv, &optind) || argv[optind] == NULL) {
+ arg_printusage(args, num_args, argv[0], "table name\n");
+ return NDBT_WRONGARGS;
+ }
+ // Check if table name is supplied
+ if (argv[optind] != NULL)
+ _tableName = argv[optind];
+
+
+ const NdbDictionary::Table* table = NDBT_Tables::getTable(_tableName);
+ const NdbDictionary::Column * attribute = table->getColumn(_column);
+
+ g_info << "Table " << _tableName << endl
+ << "Row: " << _row << ", "
+ << "Column(" << attribute->getName() << ")"
+ << "[" << attribute->getType() << "]"
+ << ", Updates: " << _updates
+ << endl;
+
+ HugoCalculator calc(*table);
+ char buf[8000];
+ g_info << "Value: " << calc.calcValue(_row, _column, _updates, buf)
+ << endl;
+
+ return 0;
+}
diff --git a/storage/ndb/test/tools/hugoFill.cpp b/storage/ndb/test/tools/hugoFill.cpp
new file mode 100644
index 00000000000..6408b2987f9
--- /dev/null
+++ b/storage/ndb/test/tools/hugoFill.cpp
@@ -0,0 +1,84 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <NdbApi.hpp>
+#include <NdbSleep.h>
+#include <NDBT.hpp>
+#include <HugoTransactions.hpp>
+#include <getarg.h>
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ int _records = 0;
+ const char* _tabname = NULL;
+ int _help = 0;
+ int _batch = 512;
+
+ struct getargs args[] = {
+ { "batch", 'b', arg_integer, &_batch, "Number of operations in each transaction", "batch" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "tabname\n"\
+ "This program will load one table in Ndb with calculated data \n"\
+ "until the database is full. \n";
+
+ if(getarg(args, num_args, argc, argv, &optind) ||
+ argv[optind] == NULL || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _tabname = argv[optind];
+
+ // Connect to Ndb
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb MyNdb(&con, "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ // Connect to Ndb and wait for it to become ready
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ // Check if table exists in db
+ const NdbDictionary::Table* pTab = NDBT_Table::discoverTableFromDb(&MyNdb, _tabname);
+ if(pTab == NULL){
+ ndbout << " Table " << _tabname << " does not exist!" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ HugoTransactions hugoTrans(*pTab);
+ if (hugoTrans.fillTable(&MyNdb,
+ _batch) != 0){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/test/tools/hugoLoad.cpp b/storage/ndb/test/tools/hugoLoad.cpp
new file mode 100644
index 00000000000..7d9d0dafaff
--- /dev/null
+++ b/storage/ndb/test/tools/hugoLoad.cpp
@@ -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 */
+
+
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NdbSleep.h>
+#include <NDBT.hpp>
+#include <HugoTransactions.hpp>
+#include <getarg.h>
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ int _records = 0;
+ const char* _tabname = NULL;
+ int _help = 0;
+ int _batch = 512;
+
+ struct getargs args[] = {
+ { "records", 'r', arg_integer, &_records, "Number of records", "recs" },
+ { "batch", 'b', arg_integer, &_batch, "Number of operations in each transaction", "batch" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "tabname\n"\
+ "This program will load one table in Ndb with calculated data. \n"\
+ "This means that it is possible to check the validity of the data \n"\
+ "at a later time. The last column in each table is used as an update \n"\
+ "counter, it's initialised to zero and should be incremented for each \n"\
+ "update of the record. \n";
+
+ if(getarg(args, num_args, argc, argv, &optind) ||
+ argv[optind] == NULL || _records == 0 || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _tabname = argv[optind];
+
+ // Connect to Ndb
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb MyNdb(&con, "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ // Connect to Ndb and wait for it to become ready
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ // Check if table exists in db
+ const NdbDictionary::Table* pTab = NDBT_Table::discoverTableFromDb(&MyNdb, _tabname);
+ if(pTab == NULL){
+ ndbout << " Table " << _tabname << " does not exist!" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ HugoTransactions hugoTrans(*pTab);
+ if (hugoTrans.loadTable(&MyNdb,
+ _records,
+ _batch) != 0){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/test/tools/hugoLockRecords.cpp b/storage/ndb/test/tools/hugoLockRecords.cpp
new file mode 100644
index 00000000000..c0d0b9f9c5a
--- /dev/null
+++ b/storage/ndb/test/tools/hugoLockRecords.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>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+
+#include <HugoTransactions.hpp>
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ int _records = 0;
+ int _loops = 1;
+ int _percentVal = 1;
+ int _lockTime = 1000;
+ const char* _tabname = NULL;
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "loops", 'l', arg_integer, &_loops, "number of times to run this program(0=infinite loop)", "loops" },
+ { "records", 'r', arg_integer, &_records, "Number of records", "recs" },
+ { "locktime", 't', arg_integer, &_lockTime, "Time in ms to hold lock(default=1000)", "ms" },
+ { "percent", 'p', arg_integer, &_percentVal, "Percent of records to lock(default=1%)", "%" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "tabname\n"\
+ "This program will lock p% of the records in the table for x milliseconds\n"\
+ "then it will lock the next 1% and continue to do so until it has locked \n"\
+ "all records in the table\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) ||
+ argv[optind] == NULL || _records == 0 || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _tabname = argv[optind];
+
+ // Connect to Ndb
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb MyNdb(&con, "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ // Check if table exists in db
+ const NdbDictionary::Table * pTab = NDBT_Table::discoverTableFromDb(&MyNdb, _tabname);
+ if(pTab == NULL){
+ ndbout << " Table " << _tabname << " does not exist!" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ HugoTransactions hugoTrans(*pTab);
+ int i = 0;
+ while (i<_loops || _loops==0) {
+ ndbout << i << ": ";
+ if (hugoTrans.lockRecords(&MyNdb, _records, _percentVal, _lockTime) != 0){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ i++;
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
diff --git a/storage/ndb/test/tools/hugoPkDelete.cpp b/storage/ndb/test/tools/hugoPkDelete.cpp
new file mode 100644
index 00000000000..84e7ded0add
--- /dev/null
+++ b/storage/ndb/test/tools/hugoPkDelete.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 <ndb_global.h>
+
+#include <NdbOut.hpp>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+
+#include <HugoTransactions.hpp>
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ int _records = 0;
+ int _loops = 1;
+ int _batch = 0;
+ const char* _tabname = NULL;
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "loops", 'l', arg_integer, &_loops, "number of times to run this program(0=infinite loop)", "loops" },
+ // { "batch", 'b', arg_integer, &_batch, "batch value", "batch" },
+ { "records", 'r', arg_integer, &_records, "Number of records", "records" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "tabname\n"\
+ "This program will delete all records in a table using PK \n";
+
+ if(getarg(args, num_args, argc, argv, &optind) ||
+ argv[optind] == NULL || _records == 0 || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _tabname = argv[optind];
+
+ // Connect to Ndb
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb MyNdb(&con, "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ // Check if table exists in db
+ const NdbDictionary::Table * pTab = NDBT_Table::discoverTableFromDb(&MyNdb, _tabname);
+ if(pTab == NULL){
+ ndbout << " Table " << _tabname << " does not exist!" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ HugoTransactions hugoTrans(*pTab);
+ int i = 0;
+ while (i<_loops || _loops==0) {
+ ndbout << i << ": ";
+ if (hugoTrans.pkDelRecords(&MyNdb, _records) != 0){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ i++;
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
diff --git a/storage/ndb/test/tools/hugoPkRead.cpp b/storage/ndb/test/tools/hugoPkRead.cpp
new file mode 100644
index 00000000000..e3702dc5ca1
--- /dev/null
+++ b/storage/ndb/test/tools/hugoPkRead.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 <NdbOut.hpp>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+
+#include <HugoTransactions.hpp>
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ int _records = 0;
+ int _loops = 1;
+ int _abort = 0;
+ int _batch = 1;
+ const char* _tabname = NULL;
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "aborts", 'a', arg_integer, &_abort, "percent of transactions that are aborted", "abort%" },
+ { "loops", 'l', arg_integer, &_loops, "number of times to run this program(0=infinite loop)", "loops" },
+ { "batch", 'b', arg_integer, &_batch, "batch value(not 0)", "batch" },
+ { "records", 'r', arg_integer, &_records, "Number of records", "records" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "tabname\n"\
+ "This program will read 'r' records from one table in Ndb. \n"\
+ "It will verify every column read by calculating the expected value.\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) ||
+ argv[optind] == NULL || _records == 0 || _batch == 0 || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _tabname = argv[optind];
+
+
+ // Connect to Ndb
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb MyNdb(&con, "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ // Check if table exists in db
+ const NdbDictionary::Table * pTab = NDBT_Table::discoverTableFromDb(&MyNdb, _tabname);
+ if(pTab == NULL){
+ ndbout << " Table " << _tabname << " does not exist!" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ HugoTransactions hugoTrans(*pTab);
+ int i = 0;
+ while (i<_loops || _loops==0) {
+ ndbout << i << ": ";
+ if (hugoTrans.pkReadRecords(&MyNdb, _records, _batch) != 0){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ i++;
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
diff --git a/storage/ndb/test/tools/hugoPkReadRecord.cpp b/storage/ndb/test/tools/hugoPkReadRecord.cpp
new file mode 100644
index 00000000000..c60a994c7d4
--- /dev/null
+++ b/storage/ndb/test/tools/hugoPkReadRecord.cpp
@@ -0,0 +1,199 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <NdbSleep.h>
+#include <NDBT_Tables.hpp>
+#include <getarg.h>
+#include <NDBT.hpp>
+#include <Ndb.hpp>
+#include <NdbDictionary.hpp>
+
+//extern NdbOut g_info;
+
+int main(int argc, const char** argv)
+{
+ ndb_init();
+ int _row = 0;
+ int _hex = 0;
+ int _primaryKey = 0;
+ const char* _tableName = NULL;
+
+ struct getargs args[] = {
+ { "row", 'r',
+ arg_integer, &_row, "The row number", "row" },
+ { "primarykey", 'p',
+ arg_integer, &_primaryKey, "The primary key", "primarykey" },
+ { "hex", 'h',
+ arg_flag, &_hex, "Print hex", "hex" }
+ };
+
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0, i;
+
+ if(getarg(args, num_args, argc, argv, &optind) || argv[optind] == NULL) {
+ arg_printusage(args, num_args, argv[0], "table name\n");
+ return NDBT_WRONGARGS;
+ }
+ // Check if table name is supplied
+ if (argv[optind] != NULL)
+ _tableName = argv[optind];
+
+
+ const NdbDictionary::Table* table = NDBT_Tables::getTable(_tableName);
+ // const NDBT_Attribute* attribute = table->getAttribute(_column);
+
+ g_info << "Table " << _tableName << endl
+ << "Row: " << _row << ", PrimaryKey: " << _primaryKey
+ << endl;
+
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb* ndb = new Ndb(&con, "TEST_DB");
+ if (ndb->init() == 0 && ndb->waitUntilReady(30) == 0)
+ {
+ NdbConnection* conn = ndb->startTransaction();
+ if (conn == NULL)
+ {
+ g_info << "ERROR: " << ndb->getNdbError() << endl;
+ delete ndb;
+ return -1;
+ }
+ NdbOperation* op = conn->getNdbOperation(_tableName);
+ if (op == NULL)
+ {
+ g_info << "ERROR: " << conn->getNdbError() << endl;
+ delete ndb;
+ return -1;
+ }
+ op->readTuple();
+ NdbRecAttr** data = new NdbRecAttr*[table->getNoOfColumns()];
+ for (i = 0; i < table->getNoOfColumns(); i++)
+ {
+ const NdbDictionary::Column* c = table->getColumn(i);
+ if (c->getPrimaryKey())
+ {
+ op->equal(c->getName(), _primaryKey);
+ data[i] = op->getValue(c->getName(), NULL);
+ }
+ else
+ {
+ data[i] = op->getValue(c->getName(), NULL);
+ }
+ }
+ if (conn->execute(Commit) == 0)
+ {
+ // Print column names
+ for (i = 0; i < table->getNoOfColumns(); i++)
+ {
+ const NdbDictionary::Column* c = table->getColumn(i);
+
+ g_info
+ << c->getName()
+ << "[" << c->getType() << "] ";
+ }
+ g_info << endl;
+
+ if (_hex)
+ {
+ g_info << hex;
+ }
+ for (i = 0; i < table->getNoOfColumns(); i++)
+ {
+ NdbRecAttr* a = data[i];
+ switch(a->getType())
+ {
+ case NdbDictionary::Column::Char:
+ case NdbDictionary::Column::Varchar:
+ case NdbDictionary::Column::Binary:
+ case NdbDictionary::Column::Varbinary:
+ {
+ if (_hex)
+ {
+ char* b = a->aRef();
+ for (int j = 0; j < a->arraySize(); j++)
+ {
+ //ndbout_c("%x", b[j]);
+ g_info << hex << b[j] << "[" << dec << j << "]";
+ }
+ }
+ else
+ {
+ g_info << "\""
+ << a->aRef() << "\"";
+ }
+ g_info << " ";
+ }
+ break;
+
+ case NdbDictionary::Column::Int:
+ case NdbDictionary::Column::Unsigned:
+ {
+ g_info << a->int32_value() << " ";
+ }
+ break;
+
+ case NdbDictionary::Column::Bigint:
+ case NdbDictionary::Column::Bigunsigned:
+ {
+ g_info << a->int64_value() << " ";
+ }
+ break;
+
+ case NdbDictionary::Column::Float:
+ {
+ g_info << a->float_value() << " ";
+ }
+ break;
+
+ case NdbDictionary::Column::Undefined:
+ default:
+ {
+ g_info << "Undefined!!! ";
+ }
+ break;
+
+ } // case
+ g_info << " ";
+ } // for
+ g_info << endl;
+ } // if (conn
+ else
+ {
+ g_info << "Failed to commit read transaction... "
+ << conn->getNdbError()
+ << ", commitStatus = " << conn->commitStatus()
+ << endl;
+ }
+
+ delete[] data;
+
+ ndb->closeTransaction(conn);
+ } // if (ndb.init
+ else
+ {
+ g_info << "ERROR: Unable to connect to NDB, "
+ << ndb->getNdbError() << endl;
+ }
+ delete ndb;
+
+ return 0;
+}
diff --git a/storage/ndb/test/tools/hugoPkUpdate.cpp b/storage/ndb/test/tools/hugoPkUpdate.cpp
new file mode 100644
index 00000000000..6e7ff39f903
--- /dev/null
+++ b/storage/ndb/test/tools/hugoPkUpdate.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 <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+
+#include <HugoTransactions.hpp>
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ int _records = 0;
+ int _loops = 1;
+ int _abort = 0;
+ int _batch = 0;
+ const char* _tabname = NULL;
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "aborts", 'a', arg_integer, &_abort, "percent of transactions that are aborted", "abort%" },
+ { "loops", 'l', arg_integer, &_loops, "number of times to run this program(0=infinite loop)", "loops" },
+ // { "batch", 'b', arg_integer, &_batch, "batch value", "batch" },
+ { "records", 'r', arg_integer, &_records, "Number of records", "records" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "tabname\n"\
+ "This program will update all records in a table using PK\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) ||
+ argv[optind] == NULL || _records == 0 || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _tabname = argv[optind];
+
+ // Connect to Ndb
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb MyNdb(&con, "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ // Check if table exists in db
+ const NdbDictionary::Table * pTab = NDBT_Table::discoverTableFromDb(&MyNdb, _tabname);
+ if(pTab == NULL){
+ ndbout << " Table " << _tabname << " does not exist!" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ HugoTransactions hugoTrans(*pTab);
+ int i = 0;
+ while (i<_loops || _loops==0) {
+ ndbout << "loop " << i << ": ";
+ if (hugoTrans.pkUpdateRecords(&MyNdb,
+ _records) != 0){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ i++;
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/test/tools/hugoScanRead.cpp b/storage/ndb/test/tools/hugoScanRead.cpp
new file mode 100644
index 00000000000..4f76362ecab
--- /dev/null
+++ b/storage/ndb/test/tools/hugoScanRead.cpp
@@ -0,0 +1,131 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+
+#include <HugoTransactions.hpp>
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ int _records = 0;
+ int _loops = 1;
+ int _abort = 0;
+ int _parallelism = 1;
+ const char* _tabname = NULL;
+ int _help = 0;
+ int lock = NdbOperation::LM_Read;
+ int sorted = 0;
+
+ struct getargs args[] = {
+ { "aborts", 'a', arg_integer, &_abort, "percent of transactions that are aborted", "abort%" },
+ { "loops", 'l', arg_integer, &_loops, "number of times to run this program(0=infinite loop)", "loops" },
+ { "parallelism", 'p', arg_integer, &_parallelism, "parallelism(1-240)", "para" },
+ { "records", 'r', arg_integer, &_records, "Number of records", "recs" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" },
+ { "lock", 'm', arg_integer, &lock, "lock mode", "" },
+ { "sorted", 's', arg_flag, &sorted, "sorted", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ " tabname\n"\
+ "This program will scan read all records in one table in Ndb.\n"\
+ "It will verify every column read by calculating the expected value.\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || argv[optind] == NULL || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _tabname = argv[optind];
+
+ // Connect to Ndb
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb MyNdb(&con, "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ // Check if table exists in db
+ const NdbDictionary::Table * pTab = NDBT_Table::discoverTableFromDb(&MyNdb, _tabname);
+ if(pTab == NULL){
+ ndbout << " Table " << _tabname << " does not exist!" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ const NdbDictionary::Index * pIdx = 0;
+ if(optind+1 < argc)
+ {
+ pIdx = MyNdb.getDictionary()->getIndex(argv[optind+1], _tabname);
+ if(!pIdx)
+ ndbout << " Index " << argv[optind+1] << " not found" << endl;
+ else
+ if(pIdx->getType() != NdbDictionary::Index::OrderedIndex)
+ {
+ ndbout << " Index " << argv[optind+1] << " is not scannable" << endl;
+ pIdx = 0;
+ }
+ }
+
+ HugoTransactions hugoTrans(*pTab);
+ int i = 0;
+ while (i<_loops || _loops==0) {
+ ndbout << i << ": ";
+ if(!pIdx)
+ {
+ if(hugoTrans.scanReadRecords(&MyNdb,
+ 0,
+ _abort,
+ _parallelism,
+ (NdbOperation::LockMode)lock) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ }
+ else
+ {
+ if(hugoTrans.scanReadRecords(&MyNdb, pIdx,
+ 0,
+ _abort,
+ _parallelism,
+ (NdbOperation::LockMode)lock,
+ sorted) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ }
+ i++;
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/test/tools/hugoScanUpdate.cpp b/storage/ndb/test/tools/hugoScanUpdate.cpp
new file mode 100644
index 00000000000..88c343f8fd3
--- /dev/null
+++ b/storage/ndb/test/tools/hugoScanUpdate.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 <ndb_global.h>
+
+#include <NdbOut.hpp>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+
+#include <HugoTransactions.hpp>
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ int _records = 0;
+ int _loops = 1;
+ int _parallelism = 1;
+ int _ver2 = 0;
+ const char* _tabname = NULL;
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "loops", 'l', arg_integer, &_loops, "number of times to run this program(0=infinite loop)", "loops" },
+ { "parallelism", 'p', arg_integer, &_parallelism, "parallelism(1-240)", "para" },
+ { "records", 'r', arg_integer, &_records, "Number of records", "recs" },
+ { "ver2", '2', arg_flag, &_ver2, "Use version 2 of scanUpdateRecords", "" },
+ { "ver2", '1', arg_negative_flag, &_ver2, "Use version 1 of scanUpdateRecords (default)", "" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "tabname\n"\
+ "This program will scan update all records in one table in Ndb\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) ||
+ argv[optind] == NULL || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _tabname = argv[optind];
+
+ // Connect to Ndb
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb MyNdb(&con, "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ // Check if table exists in db
+ const NdbDictionary::Table * pTab = NDBT_Table::discoverTableFromDb(&MyNdb, _tabname);
+ if(pTab == NULL){
+ ndbout << " Table " << _tabname << " does not exist!" << endl;
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+
+ HugoTransactions hugoTrans(*pTab);
+ int i = 0;
+ int res = NDBT_FAILED;
+ while (i<_loops || _loops==0) {
+ ndbout << i << ": ";
+ if (_ver2 == 0){
+ res = hugoTrans.scanUpdateRecords(&MyNdb,
+ _records,
+ 0,
+ _parallelism);
+ } else{
+ res = hugoTrans.scanUpdateRecords2(&MyNdb,
+ _records,
+ 0,
+ _parallelism);
+ }
+ if (res != NDBT_OK ){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ i++;
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/test/tools/old_dirs/hugoCalculator/Makefile b/storage/ndb/test/tools/old_dirs/hugoCalculator/Makefile
new file mode 100644
index 00000000000..a29deeaacd3
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/hugoCalculator/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := hugoCalculator
+
+# Source files of non-templated classes (.C files)
+SOURCES = hugoCalculator.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/tools/old_dirs/hugoFill/Makefile b/storage/ndb/test/tools/old_dirs/hugoFill/Makefile
new file mode 100644
index 00000000000..3da745810b6
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/hugoFill/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := hugoFill
+
+# Source files of non-templated classes (.C files)
+SOURCES = hugoFill.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/tools/old_dirs/hugoLoad/Makefile b/storage/ndb/test/tools/old_dirs/hugoLoad/Makefile
new file mode 100644
index 00000000000..7c5756d0d41
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/hugoLoad/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := hugoLoad
+
+# Source files of non-templated classes (.C files)
+SOURCES = hugoLoad.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/tools/old_dirs/hugoLockRecords/Makefile b/storage/ndb/test/tools/old_dirs/hugoLockRecords/Makefile
new file mode 100644
index 00000000000..3235750cbf8
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/hugoLockRecords/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := hugoLockRecords
+
+SOURCES := hugoLockRecords.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/tools/old_dirs/hugoPkDelete/Makefile b/storage/ndb/test/tools/old_dirs/hugoPkDelete/Makefile
new file mode 100644
index 00000000000..e6d53611c54
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/hugoPkDelete/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := hugoPkDelete
+
+SOURCES := hugoPkDel.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/tools/old_dirs/hugoPkRead/Makefile b/storage/ndb/test/tools/old_dirs/hugoPkRead/Makefile
new file mode 100644
index 00000000000..03580dc0d18
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/hugoPkRead/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := hugoPkRead
+
+SOURCES := hugoPkRead.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/tools/old_dirs/hugoPkReadRecord/Makefile b/storage/ndb/test/tools/old_dirs/hugoPkReadRecord/Makefile
new file mode 100644
index 00000000000..158a79a5666
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/hugoPkReadRecord/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := hugoPkReadRecord
+
+# Source files of non-templated classes (.C files)
+SOURCES = hugoPkReadRecord.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/tools/old_dirs/hugoPkUpdate/Makefile b/storage/ndb/test/tools/old_dirs/hugoPkUpdate/Makefile
new file mode 100644
index 00000000000..48795b62206
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/hugoPkUpdate/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := hugoPkUpdate
+
+SOURCES := hugoPkUpd.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/tools/old_dirs/hugoScanRead/Makefile b/storage/ndb/test/tools/old_dirs/hugoScanRead/Makefile
new file mode 100644
index 00000000000..b88377c299e
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/hugoScanRead/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := hugoScanRead
+
+SOURCES := hugoScanRead.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/tools/old_dirs/hugoScanUpdate/Makefile b/storage/ndb/test/tools/old_dirs/hugoScanUpdate/Makefile
new file mode 100644
index 00000000000..ec0e07bfd84
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/hugoScanUpdate/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := hugoScanUpdate
+
+SOURCES := hugoScanUpd.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/tools/old_dirs/restart/Makefile b/storage/ndb/test/tools/old_dirs/restart/Makefile
new file mode 100644
index 00000000000..05d9e98c5bc
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/restart/Makefile
@@ -0,0 +1,11 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := restart
+
+# Source files of non-templated classes (.C files)
+SOURCES = restart.cpp
+
+include $(NDB_TOP)/Epilogue.mk
+
diff --git a/storage/ndb/test/tools/old_dirs/transproxy/Makefile b/storage/ndb/test/tools/old_dirs/transproxy/Makefile
new file mode 100644
index 00000000000..d6a76ed2e3d
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/transproxy/Makefile
@@ -0,0 +1,29 @@
+include .defs.mk
+
+TYPE =
+
+BIN_TARGET = transproxy
+
+SOURCES = transproxy.cpp
+
+CCFLAGS_LOC +=\
+ -I$(NDB_TOP)/include/kernel \
+ -I$(NDB_TOP)/include/mgmcommon \
+ -I$(NDB_TOP)/src/common/mgmcommon \
+ -I$(NDB_TOP)/src/mgmsrv
+
+LIBS_LOC +=\
+ -L$(NDB_TOP)/lib
+
+LIBS_SPEC +=\
+ $(NDB_TOP)/src/mgmsrv/InitConfigFileParser.o \
+ $(NDB_TOP)/src/mgmsrv/Config.o \
+ $(NDB_TOP)/src/mgmsrv/Container.o \
+ $(NDB_TOP)/src/mgmsrv/Str.o \
+ $(NDB_TOP)/src/mgmsrv/convertStrToInt.o \
+ -lNDB_API \
+ -leventlogger \
+ -llogger \
+ -lportlib
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/tools/old_dirs/verify_index/Makefile b/storage/ndb/test/tools/old_dirs/verify_index/Makefile
new file mode 100644
index 00000000000..f6b31e4dc8e
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/verify_index/Makefile
@@ -0,0 +1,9 @@
+include .defs.mk
+
+TYPE := ndbapitest
+
+BIN_TARGET := verify_index
+
+SOURCES := verify_index.cpp
+
+include $(NDB_TOP)/Epilogue.mk
diff --git a/storage/ndb/test/tools/old_dirs/waiter/waiter.cpp b/storage/ndb/test/tools/old_dirs/waiter/waiter.cpp
new file mode 100644
index 00000000000..d57daff3aea
--- /dev/null
+++ b/storage/ndb/test/tools/old_dirs/waiter/waiter.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 "mgmapi.h"
+#include <string.h>
+#include <NdbMain.h>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+
+
+#include <NdbRestarter.hpp>
+#include <NDBT.hpp>
+
+int main(int argc, const char** argv){
+
+ const char* _hostName = NULL;
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "hostname:port\n"\
+ "This program will connect to the mgmsrv of a NDB cluster.\n"\
+ "It will then wait for all nodes to be started\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _hostName = argv[optind];
+
+ NdbRestarter restarter(_hostName);
+
+ if (restarter.waitClusterStarted() != 0)
+ return NDBT_ProgramExit(NDBT_FAILED);
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
diff --git a/storage/ndb/test/tools/restart.cpp b/storage/ndb/test/tools/restart.cpp
new file mode 100644
index 00000000000..9ad20801fd7
--- /dev/null
+++ b/storage/ndb/test/tools/restart.cpp
@@ -0,0 +1,84 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <OutputStream.hpp>
+#include <NdbOut.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+
+#include <NdbRestarter.hpp>
+#include <NDBT.hpp>
+
+int main(int argc, const char** argv){
+ ndb_init();
+
+ const char* _hostName = NULL;
+ int _initial = 0;
+ int _help = 0;
+ int _wait = 1;
+
+
+ struct getargs args[] = {
+ { "initial", 'i', arg_flag, &_initial, "Do initial restart"},
+ { "wait", '\0', arg_negative_flag, &_wait, "Wait until restarted(default=true)"},
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "hostname:port\n"\
+ "This program will connect to the mgmsrv of a NDB cluster\n"\
+ " and restart the cluster. \n";
+
+ if(getarg(args, num_args, argc, argv, &optind) || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _hostName = argv[optind];
+
+ NdbRestarter restarter(_hostName);
+ setOutputLevel(1); // Show only g_err
+ int result = NDBT_OK;
+ if (_initial){
+ ndbout << "Restarting cluster with initial restart" << endl;
+ if (restarter.restartAll(true, false, false) != 0)
+ result = NDBT_FAILED;
+ } else {
+ ndbout << "Restarting cluster " << endl;
+ if (restarter.restartAll() != 0)
+ result = NDBT_FAILED;
+ }
+ if (result == NDBT_FAILED){
+ g_err << "Failed to restart cluster" << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ if (_wait == 1){
+ ndbout << "Waiting for cluster to start" << endl;
+ if ( restarter.waitClusterStarted(120) != 0){
+ ndbout << "Failed waiting for restart of cluster" << endl;
+ result = NDBT_FAILED;
+ }
+ }
+ ndbout << "Cluster restarted" << endl;
+
+ return NDBT_ProgramExit(result);
+}
diff --git a/storage/ndb/test/tools/transproxy.cpp b/storage/ndb/test/tools/transproxy.cpp
new file mode 100644
index 00000000000..28a621fa584
--- /dev/null
+++ b/storage/ndb/test/tools/transproxy.cpp
@@ -0,0 +1,361 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 <NdbThread.h>
+#include <NdbSleep.h>
+#include <Properties.hpp>
+#include <LocalConfig.hpp>
+#include <Config.hpp>
+#include <InitConfigFileParser.hpp>
+#include <IPCConfig.hpp>
+
+static void
+fatal(char const* fmt, ...)
+{
+ va_list ap;
+ char buf[200];
+ va_start(ap, fmt);
+ BaseString::vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ ndbout << "FATAL: " << buf << endl;
+ sleep(1);
+ exit(1);
+}
+
+static void
+debug(char const* fmt, ...)
+{
+ va_list ap;
+ char buf[200];
+ va_start(ap, fmt);
+ BaseString::vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ ndbout << buf << endl;
+}
+
+// node
+struct Node {
+ enum Type { MGM = 1, DB = 2, API = 3 };
+ Type type;
+ unsigned id; // node id
+ static Node* list;
+ static unsigned count;
+ static Node* find(unsigned n) {
+ for (unsigned i = 0; i < count; i++) {
+ if (list[i].id == n)
+ return &list[i];
+ }
+ return 0;
+ }
+};
+
+unsigned Node::count = 0;
+Node* Node::list = 0;
+
+struct Copy {
+ int rfd; // read from
+ int wfd; // write to
+ unsigned char* buf;
+ unsigned bufsiz;
+ NdbThread* thread;
+ void run();
+ char info[20];
+};
+
+// connection between nodes 0-server side 1-client side
+// we are client to 0 and server to 1
+struct Conn {
+ Node* node[2]; // the nodes
+ unsigned port; // server port
+ unsigned proxy; // proxy port
+ static unsigned count;
+ static unsigned proxycount;
+ static Conn* list;
+ NdbThread* thread; // thread handling this connection
+ void run(); // run the connection
+ int sockfd[2]; // socket 0-on server side 1-client side
+ void conn0(); // connect to side 0
+ void conn1(); // connect to side 0
+ char info[20];
+ Copy copy[2]; // 0-to-1 and 1-to-0
+};
+
+unsigned Conn::count = 0;
+unsigned Conn::proxycount = 0;
+Conn* Conn::list = 0;
+
+// global data
+static char* hostname = 0;
+static struct sockaddr_in hostaddr;
+static char* localcfgfile = 0;
+static char* initcfgfile = 0;
+static unsigned ownnodeid = 0;
+
+static void
+properr(const Properties* props, const char* name, int i = -1)
+{
+ if (i < 0) {
+ fatal("get %s failed: errno = %d", name, props->getPropertiesErrno());
+ } else {
+ fatal("get %s_%d failed: errno = %d", name, i, props->getPropertiesErrno());
+ }
+}
+
+// read config and load it into our structs
+static void
+getcfg()
+{
+ LocalConfig lcfg;
+ if (! lcfg.read(localcfgfile)) {
+ fatal("read %s failed", localcfgfile);
+ }
+ ownnodeid = lcfg._ownNodeId;
+ debug("ownnodeid = %d", ownnodeid);
+ InitConfigFileParser pars(initcfgfile);
+ Config icfg;
+ if (! pars.getConfig(icfg)) {
+ fatal("parse %s failed", initcfgfile);
+ }
+ Properties* ccfg = icfg.getConfig(ownnodeid);
+ if (ccfg == 0) {
+ const char* err = "unknown error";
+ fatal("getConfig: %s", err);
+ }
+ ccfg->put("NodeId", ownnodeid);
+ ccfg->put("NodeType", "MGM");
+ if (! ccfg->get("NoOfNodes", &Node::count)) {
+ properr(ccfg, "NoOfNodes", -1);
+ }
+ debug("Node::count = %d", Node::count);
+ Node::list = new Node[Node::count];
+ for (unsigned i = 0; i < Node::count; i++) {
+ Node& node = Node::list[i];
+ const Properties* nodecfg;
+ if (! ccfg->get("Node", 1+i, &nodecfg)) {
+ properr(ccfg, "Node", 1+i);
+ }
+ const char* type;
+ if (! nodecfg->get("Type", &type)) {
+ properr(nodecfg, "Type");
+ }
+ if (strcmp(type, "MGM") == 0) {
+ node.type = Node::MGM;
+ } else if (strcmp(type, "DB") == 0) {
+ node.type = Node::DB;
+ } else if (strcmp(type, "API") == 0) {
+ node.type = Node::API;
+ } else {
+ fatal("prop %s_%d bad Type = %s", "Node", 1+i, type);
+ }
+ if (! nodecfg->get("NodeId", &node.id)) {
+ properr(nodecfg, "NodeId");
+ }
+ debug("node id=%d type=%d", node.id, node.type);
+ }
+ IPCConfig ipccfg(ccfg);
+ if (ipccfg.init() != 0) {
+ fatal("ipccfg init failed");
+ }
+ if (! ccfg->get("NoOfConnections", &Conn::count)) {
+ properr(ccfg, "NoOfConnections");
+ }
+ debug("Conn::count = %d", Conn::count);
+ Conn::list = new Conn[Conn::count];
+ for (unsigned i = 0; i < Conn::count; i++) {
+ Conn& conn = Conn::list[i];
+ const Properties* conncfg;
+ if (! ccfg->get("Connection", i, &conncfg)) {
+ properr(ccfg, "Connection", i);
+ }
+ unsigned n;
+ if (! conncfg->get("NodeId1", &n)) {
+ properr(conncfg, "NodeId1");
+ }
+ if ((conn.node[0] = Node::find(n)) == 0) {
+ fatal("node %d not found", n);
+ }
+ if (! conncfg->get("NodeId2", &n)) {
+ properr(conncfg, "NodeId2");
+ }
+ if ((conn.node[1] = Node::find(n)) == 0) {
+ fatal("node %d not found", n);
+ }
+ if (! conncfg->get("PortNumber", &conn.port)) {
+ properr(conncfg, "PortNumber");
+ }
+ conn.proxy = 0;
+ const char* proxy;
+ if (conncfg->get("Proxy", &proxy)) {
+ conn.proxy = atoi(proxy);
+ if (conn.proxy > 0) {
+ Conn::proxycount++;
+ }
+ }
+ sprintf(conn.info, "conn %d-%d", conn.node[0]->id, conn.node[1]->id);
+ }
+}
+
+void
+Conn::conn0()
+{
+ int fd;
+ while (1) {
+ if ((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
+ fatal("%s: create client socket failed: %s", info, strerror(errno));
+ }
+ struct sockaddr_in servaddr;
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_port = htons(port);
+ servaddr.sin_addr = hostaddr.sin_addr;
+#if 0 // coredump
+ if (Ndb_getInAddr(&servaddr.sin_addr, hostname) != 0) {
+ fatal("%s: hostname %s lookup failed", info, hostname);
+ }
+#endif
+ if (connect(fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == 0)
+ break;
+ if (errno != ECONNREFUSED) {
+ fatal("%s: connect failed: %s", info, strerror(errno));
+ }
+ close(fd);
+ NdbSleep_MilliSleep(100);
+ }
+ sockfd[0] = fd;
+ debug("%s: side 0 connected", info);
+}
+
+void
+Conn::conn1()
+{
+ int servfd;
+ if ((servfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
+ fatal("%s: create server socket failed: %s", info, strerror(errno));
+ }
+ 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(proxy);
+ const int on = 1;
+ setsockopt(servfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
+ if (bind(servfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) == -1) {
+ fatal("%s: bind %d failed: %s", info, proxy, strerror(errno));
+ }
+ if (listen(servfd, 1) == -1) {
+ fatal("%s: listen %d failed: %s", info, proxy, strerror(errno));
+ }
+ int fd;
+ if ((fd = accept(servfd, 0, 0)) == -1) {
+ fatal("%s: accept failed: %s", info, strerror(errno));
+ }
+ sockfd[1] = fd;
+ close(servfd);
+ debug("%s: side 1 connected", info);
+}
+
+void
+Copy::run()
+{
+ debug("%s: start", info);
+ int n, m;
+ while (1) {
+ n = read(rfd, buf, sizeof(buf));
+ if (n < 0)
+ fatal("read error: %s", strerror(errno));
+ m = write(wfd, buf, n);
+ if (m != n)
+ fatal("write error: %s", strerror(errno));
+ }
+ debug("%s: stop", info);
+}
+
+extern "C" void*
+copyrun_C(void* copy)
+{
+ ((Copy*) copy)->run();
+ return 0;
+}
+
+void
+Conn::run()
+{
+ debug("%s: start", info);
+ conn1();
+ conn0();
+ const unsigned siz = 32 * 1024;
+ for (int i = 0; i < 2; i++) {
+ Copy& copy = this->copy[i];
+ copy.rfd = sockfd[i];
+ copy.wfd = sockfd[1-i];
+ copy.buf = new unsigned char[siz];
+ copy.bufsiz = siz;
+ sprintf(copy.info, "copy %d-%d", this->node[i]->id, this->node[1-i]->id);
+ copy.thread = NdbThread_Create(copyrun_C, (void**)&copy,
+ 8192, "copyrun", NDB_THREAD_PRIO_LOW);
+ if (copy.thread == 0) {
+ fatal("%s: create thread %d failed errno=%d", i, errno);
+ }
+ }
+ debug("%s: stop", info);
+}
+
+extern "C" void*
+connrun_C(void* conn)
+{
+ ((Conn*) conn)->run();
+ return 0;
+}
+
+static void
+start()
+{
+ NdbThread_SetConcurrencyLevel(3 * Conn::proxycount + 2);
+ for (unsigned i = 0; i < Conn::count; i++) {
+ Conn& conn = Conn::list[i];
+ if (! conn.proxy)
+ continue;
+ conn.thread = NdbThread_Create(connrun_C, (void**)&conn,
+ 8192, "connrun", NDB_THREAD_PRIO_LOW);
+ if (conn.thread == 0) {
+ fatal("create thread %d failed errno=%d", i, errno);
+ }
+ }
+ sleep(3600);
+}
+
+int
+main(int av, char** ac)
+{
+ ndb_init();
+ debug("start");
+ hostname = "ndb-srv7";
+ if (Ndb_getInAddr(&hostaddr.sin_addr, hostname) != 0) {
+ fatal("hostname %s lookup failed", hostname);
+ }
+ localcfgfile = "Ndb.cfg";
+ initcfgfile = "config.txt";
+ getcfg();
+ start();
+ debug("done");
+ return 0;
+}
+
+// vim: set sw=4 noet:
diff --git a/storage/ndb/test/tools/verify_index.cpp b/storage/ndb/test/tools/verify_index.cpp
new file mode 100644
index 00000000000..acc97af883b
--- /dev/null
+++ b/storage/ndb/test/tools/verify_index.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 <NdbOut.hpp>
+
+#include <NdbApi.hpp>
+#include <NdbMain.h>
+#include <NDBT.hpp>
+#include <NdbSleep.h>
+#include <getarg.h>
+#include <UtilTransactions.hpp>
+
+
+int main(int argc, const char** argv){
+ ndb_init();
+ int _parallelism = 240;
+ const char* _tabname = NULL;
+ const char* _indexname = NULL;
+ int _help = 0;
+
+ struct getargs args[] = {
+ { "parallelism", 's', arg_integer, &_parallelism, "parallelism", "parallelism" },
+ { "usage", '?', arg_flag, &_help, "Print help", "" }
+ };
+ int num_args = sizeof(args) / sizeof(args[0]);
+ int optind = 0;
+ char desc[] =
+ "tabname indexname\n"\
+ "This program will verify the index [indexname] and compare it to data\n"
+ "in table [tablename]\n";
+
+ if(getarg(args, num_args, argc, argv, &optind) ||
+ argv[optind] == NULL || argv[optind+1] == NULL || _help) {
+ arg_printusage(args, num_args, argv[0], desc);
+ return NDBT_ProgramExit(NDBT_WRONGARGS);
+ }
+ _tabname = argv[optind];
+ _indexname = argv[optind+1];
+
+ // Connect to Ndb
+ Ndb_cluster_connection con;
+ if(con.connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ Ndb MyNdb(&con, "TEST_DB" );
+
+ if(MyNdb.init() != 0){
+ ERR(MyNdb.getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ // Connect to Ndb and wait for it to become ready
+ while(MyNdb.waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+
+ // Check if table exists in db
+ const NdbDictionary::Table * pTab = NDBT_Table::discoverTableFromDb(&MyNdb, _tabname);
+ if(pTab == NULL){
+ ndbout << " Table " << _tabname << " does not exist!" << endl;
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ int rows = 0;
+ UtilTransactions utilTrans(*pTab);
+ if (utilTrans.verifyIndex(&MyNdb,
+ _indexname,
+ _parallelism) != 0){
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ return NDBT_ProgramExit(NDBT_OK);
+}
+
+
+