1 /*
2 Copyright (c) 2007, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 //
26 // ndbapi_simple_index_ndbrecord.cpp: Using secondary unique hash indexes
27 // in NDB API, utilising the NdbRecord interface.
28 //
29 // Correct output from this program is (from a two-node cluster):
30 //
31 // ATTR1 ATTR2
32 // 0 0 (frag=0)
33 // 1 1 (frag=1)
34 // 2 2 (frag=1)
35 // 3 3 (frag=0)
36 // 4 4 (frag=1)
37 // 5 5 (frag=1)
38 // 6 6 (frag=0)
39 // 7 7 (frag=0)
40 // 8 8 (frag=1)
41 // 9 9 (frag=0)
42 // ATTR1 ATTR2
43 // 0 10
44 // 1 1
45 // 2 12
46 // Detected that deleted tuple doesn't exist!
47 // 4 14
48 // 5 5
49 // 6 16
50 // 7 7
51 // 8 18
52 // 9 9
53
54 #ifdef _WIN32
55 #include <winsock2.h>
56 #endif
57 #include <mysql.h>
58 #include <NdbApi.hpp>
59
60 #include <stddef.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 // Used for cout
64 #include <iostream>
65
66 #define PRINT_ERROR(code,msg) \
67 std::cout << "Error in " << __FILE__ << ", line: " << __LINE__ \
68 << ", code: " << code \
69 << ", msg: " << msg << "." << std::endl
70 #define MYSQLERROR(mysql) { \
71 PRINT_ERROR(mysql_errno(&mysql),mysql_error(&mysql)); \
72 exit(1); }
73 #define APIERROR(error) { \
74 PRINT_ERROR(error.code,error.message); \
75 exit(1); }
76
77 /* C struct representing layout of data from table
78 * api_s_i_ndbrecord in memory
79 * This can make it easier to work with rows in the application,
80 * but is not necessary - NdbRecord can map columns to any
81 * pattern of offsets.
82 * In this program, the same row offsets are used for columns
83 * specified as part of a key, and as part of an attribute or
84 * result. This makes the example simpler, but is not
85 * essential.
86 */
87 struct MyTableRow
88 {
89 unsigned int attr1;
90 unsigned int attr2;
91 };
92
main(int argc,char ** argv)93 int main(int argc, char** argv)
94 {
95 if (argc != 3)
96 {
97 std::cout << "Arguments are <socket mysqld> <connect_string cluster>.\n";
98 exit(1);
99 }
100 char * mysqld_sock = argv[1];
101 const char *connectstring = argv[2];
102 ndb_init();
103 MYSQL mysql;
104
105 /**************************************************************
106 * Connect to mysql server and create table *
107 **************************************************************/
108 {
109 if ( !mysql_init(&mysql) ) {
110 std::cout << "mysql_init failed\n";
111 exit(1);
112 }
113 if ( !mysql_real_connect(&mysql, "localhost", "root", "", "",
114 0, mysqld_sock, 0) )
115 MYSQLERROR(mysql);
116
117 mysql_query(&mysql, "CREATE DATABASE ndb_examples");
118 if (mysql_query(&mysql, "USE ndb_examples") != 0)
119 MYSQLERROR(mysql);
120
121 mysql_query(&mysql, "DROP TABLE api_s_i_ndbrecord");
122 if (mysql_query(&mysql,
123 "CREATE TABLE"
124 " api_s_i_ndbrecord"
125 " (ATTR1 INT UNSIGNED,"
126 " ATTR2 INT UNSIGNED NOT NULL,"
127 " PRIMARY KEY USING HASH (ATTR1),"
128 " UNIQUE MYINDEXNAME USING HASH (ATTR2))"
129 " ENGINE=NDB"))
130 MYSQLERROR(mysql);
131 }
132
133 /**************************************************************
134 * Connect to ndb cluster *
135 **************************************************************/
136
137 Ndb_cluster_connection *cluster_connection=
138 new Ndb_cluster_connection(connectstring); // Object representing the cluster
139
140 if (cluster_connection->connect(5,3,1))
141 {
142 std::cout << "Connect to cluster management server failed.\n";
143 exit(1);
144 }
145
146 if (cluster_connection->wait_until_ready(30,30))
147 {
148 std::cout << "Cluster was not ready within 30 secs.\n";
149 exit(1);
150 }
151
152 Ndb* myNdb = new Ndb( cluster_connection,
153 "ndb_examples" ); // Object representing the database
154 if (myNdb->init() == -1) {
155 APIERROR(myNdb->getNdbError());
156 exit(1);
157 }
158
159 NdbDictionary::Dictionary* myDict= myNdb->getDictionary();
160 const NdbDictionary::Table *myTable= myDict->getTable("api_s_i_ndbrecord");
161 if (myTable == NULL)
162 APIERROR(myDict->getNdbError());
163 const NdbDictionary::Index *myIndex= myDict->getIndex("MYINDEXNAME$unique","api_s_i_ndbrecord");
164 if (myIndex == NULL)
165 APIERROR(myDict->getNdbError());
166
167 /* Create NdbRecord descriptors. */
168 const NdbDictionary::Column *col1= myTable->getColumn("ATTR1");
169 if (col1 == NULL)
170 APIERROR(myDict->getNdbError());
171 const NdbDictionary::Column *col2= myTable->getColumn("ATTR2");
172 if (col2 == NULL)
173 APIERROR(myDict->getNdbError());
174
175 /* NdbRecord for primary key lookup. */
176 NdbDictionary::RecordSpecification spec[2];
177 spec[0].column= col1;
178 spec[0].offset= offsetof(MyTableRow, attr1);
179 // So that it goes nicely into the struct
180 spec[0].nullbit_byte_offset= 0;
181 spec[0].nullbit_bit_in_byte= 0;
182 const NdbRecord *pk_record=
183 myDict->createRecord(myTable, spec, 1, sizeof(spec[0]));
184 if (pk_record == NULL)
185 APIERROR(myDict->getNdbError());
186
187 /* NdbRecord for all table attributes (insert/read). */
188 spec[0].column= col1;
189 spec[0].offset= offsetof(MyTableRow, attr1);
190 spec[0].nullbit_byte_offset= 0;
191 spec[0].nullbit_bit_in_byte= 0;
192 spec[1].column= col2;
193 spec[1].offset= offsetof(MyTableRow, attr2);
194 spec[1].nullbit_byte_offset= 0;
195 spec[1].nullbit_bit_in_byte= 0;
196 const NdbRecord *attr_record=
197 myDict->createRecord(myTable, spec, 2, sizeof(spec[0]));
198 if (attr_record == NULL)
199 APIERROR(myDict->getNdbError());
200
201 /* NdbRecord for unique key lookup. */
202 spec[0].column= col2;
203 spec[0].offset= offsetof(MyTableRow, attr2);
204 spec[0].nullbit_byte_offset= 0;
205 spec[0].nullbit_bit_in_byte= 0;
206 const NdbRecord *key_record=
207 myDict->createRecord(myIndex, spec, 1, sizeof(spec[0]));
208 if (key_record == NULL)
209 APIERROR(myDict->getNdbError());
210
211 MyTableRow row;
212
213 /**************************************************************************
214 * Using 5 transactions, insert 10 tuples in table: (0,0),(1,1),...,(9,9) *
215 **************************************************************************/
216 for (int i = 0; i < 5; i++) {
217 NdbTransaction *myTransaction= myNdb->startTransaction();
218 if (myTransaction == NULL) APIERROR(myNdb->getNdbError());
219
220 /*
221 We initialise the row data and pass to each insertTuple operation
222 The data is copied in the call to insertTuple and so the original
223 row object can be reused for the two operations.
224 */
225 row.attr1= row.attr2= i;
226
227 const NdbOperation *myOperation=
228 myTransaction->insertTuple(attr_record, (const char*)&row);
229 if (myOperation == NULL)
230 APIERROR(myTransaction->getNdbError());
231
232 row.attr1= row.attr2= i+5;
233 myOperation=
234 myTransaction->insertTuple(attr_record, (const char*)&row);
235 if (myOperation == NULL)
236 APIERROR(myTransaction->getNdbError());
237
238 if (myTransaction->execute( NdbTransaction::Commit ) == -1)
239 APIERROR(myTransaction->getNdbError());
240
241 myNdb->closeTransaction(myTransaction);
242 }
243
244 /*****************************************
245 * Read and print all tuples using index *
246 *****************************************/
247 std::cout << "ATTR1 ATTR2" << std::endl;
248
249 for (int i = 0; i < 10; i++) {
250 NdbTransaction *myTransaction= myNdb->startTransaction();
251 if (myTransaction == NULL)
252 APIERROR(myNdb->getNdbError());
253
254 /* The optional OperationOptions parameter to NdbRecord methods
255 * can be used to specify extra reads of columns which are not in
256 * the NdbRecord specification, which need to be stored somewhere
257 * other than specified in the NdbRecord specification, or
258 * which cannot be specified as part of an NdbRecord (pseudo
259 * columns)
260 */
261 Uint32 frag;
262 NdbOperation::GetValueSpec getSpec[1];
263 getSpec[0].column=NdbDictionary::Column::FRAGMENT;
264 getSpec[0].appStorage=&frag;
265
266 NdbOperation::OperationOptions options;
267 options.optionsPresent = NdbOperation::OperationOptions::OO_GETVALUE;
268 options.extraGetValues = &getSpec[0];
269 options.numExtraGetValues = 1;
270
271 /* We're going to read using the secondary unique hash index
272 * Set the value of its column
273 */
274 row.attr2= i;
275
276 MyTableRow resultRow;
277
278 unsigned char mask[1]= { 0x01 }; // Only read ATTR1 into resultRow
279 const NdbOperation *myOperation=
280 myTransaction->readTuple(key_record, (const char*) &row,
281 attr_record, (char*) &resultRow,
282 NdbOperation::LM_Read, mask,
283 &options,
284 sizeof(NdbOperation::OperationOptions));
285 if (myOperation == NULL)
286 APIERROR(myTransaction->getNdbError());
287
288 if (myTransaction->execute( NdbTransaction::Commit,
289 NdbOperation::AbortOnError ) != -1)
290 {
291 printf(" %2d %2d (frag=%u)\n", resultRow.attr1, i, frag);
292 }
293
294 myNdb->closeTransaction(myTransaction);
295 }
296
297 /*****************************************************************
298 * Update the second attribute in half of the tuples (adding 10) *
299 *****************************************************************/
300 for (int i = 0; i < 10; i+=2) {
301 NdbTransaction *myTransaction= myNdb->startTransaction();
302 if (myTransaction == NULL)
303 APIERROR(myNdb->getNdbError());
304
305 /* Specify key column to lookup in secondary index */
306 row.attr2= i;
307
308 /* Specify new column value to set */
309 MyTableRow newRowData;
310 newRowData.attr2= i+10;
311 unsigned char mask[1]= { 0x02 }; // Only update ATTR2
312
313 const NdbOperation *myOperation=
314 myTransaction->updateTuple(key_record, (const char*)&row,
315 attr_record,(char*) &newRowData, mask);
316 if (myOperation == NULL)
317 APIERROR(myTransaction->getNdbError());
318
319 if ( myTransaction->execute( NdbTransaction::Commit ) == -1 )
320 APIERROR(myTransaction->getNdbError());
321
322 myNdb->closeTransaction(myTransaction);
323 }
324
325 /*************************************************
326 * Delete one tuple (the one with unique key 3) *
327 *************************************************/
328 {
329 NdbTransaction *myTransaction= myNdb->startTransaction();
330 if (myTransaction == NULL)
331 APIERROR(myNdb->getNdbError());
332
333 row.attr2= 3;
334 const NdbOperation *myOperation=
335 myTransaction->deleteTuple(key_record, (const char*) &row,
336 attr_record);
337 if (myOperation == NULL)
338 APIERROR(myTransaction->getNdbError());
339
340 if (myTransaction->execute(NdbTransaction::Commit) == -1)
341 APIERROR(myTransaction->getNdbError());
342
343 myNdb->closeTransaction(myTransaction);
344 }
345
346 /*****************************
347 * Read and print all tuples *
348 *****************************/
349 {
350 std::cout << "ATTR1 ATTR2" << std::endl;
351
352 for (int i = 0; i < 10; i++) {
353 NdbTransaction *myTransaction= myNdb->startTransaction();
354 if (myTransaction == NULL)
355 APIERROR(myNdb->getNdbError());
356
357 row.attr1= i;
358
359 /* Read using pk. Note the same row space is used as
360 * key and result storage space
361 */
362 const NdbOperation *myOperation=
363 myTransaction->readTuple(pk_record, (const char*) &row,
364 attr_record, (char*) &row);
365 if (myOperation == NULL)
366 APIERROR(myTransaction->getNdbError());
367
368 if (myTransaction->execute( NdbTransaction::Commit,
369 NdbOperation::AbortOnError ) == -1)
370 {
371 if (i == 3) {
372 std::cout << "Detected that deleted tuple doesn't exist!\n";
373 } else {
374 APIERROR(myTransaction->getNdbError());
375 }
376 }
377
378 if (i != 3)
379 printf(" %2d %2d\n", row.attr1, row.attr2);
380
381 myNdb->closeTransaction(myTransaction);
382 }
383 }
384
385 delete myNdb;
386 delete cluster_connection;
387
388 ndb_end(0);
389 return 0;
390 }
391