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