MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
main.cpp
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 as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 
19 /*
20  ndbapi_blob_ndbrecord
21 
22  Illustrates the manipulation of BLOB (actually TEXT in this example).
23  This example uses the NdbRecord style way of accessing tuples.
24 
25  Shows insert, read, and update, using both inline value buffer and
26  read/write methods.
27  */
28 
29 #include <ndb_global.h>
30 #include <mysql.h>
31 #include <mysqld_error.h>
32 #include <NdbApi.hpp>
33 /* Used for cout. */
34 #include <iostream>
35 #include <stdio.h>
36 #include <ctype.h>
37 
38 
42 #define PRINT_ERROR(code,msg) \
43  std::cout << "Error in " << __FILE__ << ", line: " << __LINE__ \
44  << ", code: " << code \
45  << ", msg: " << msg << "." << std::endl
46 #define MYSQLERROR(mysql) { \
47  PRINT_ERROR(mysql_errno(&mysql),mysql_error(&mysql)); \
48  exit(-1); }
49 #define APIERROR(error) { \
50  PRINT_ERROR(error.code,error.message); \
51  exit(-1); }
52 
53 /* Quote taken from Project Gutenberg. */
54 const char *text_quote=
55 "Just at this moment, somehow or other, they began to run.\n"
56 "\n"
57 " Alice never could quite make out, in thinking it over\n"
58 "afterwards, how it was that they began: all she remembers is,\n"
59 "that they were running hand in hand, and the Queen went so fast\n"
60 "that it was all she could do to keep up with her: and still the\n"
61 "Queen kept crying 'Faster! Faster!' but Alice felt she COULD NOT\n"
62 "go faster, though she had not breath left to say so.\n"
63 "\n"
64 " The most curious part of the thing was, that the trees and the\n"
65 "other things round them never changed their places at all:\n"
66 "however fast they went, they never seemed to pass anything. 'I\n"
67 "wonder if all the things move along with us?' thought poor\n"
68 "puzzled Alice. And the Queen seemed to guess her thoughts, for\n"
69 "she cried, 'Faster! Don't try to talk!'\n"
70 "\n"
71 " Not that Alice had any idea of doing THAT. She felt as if she\n"
72 "would never be able to talk again, she was getting so much out of\n"
73 "breath: and still the Queen cried 'Faster! Faster!' and dragged\n"
74 "her along. 'Are we nearly there?' Alice managed to pant out at\n"
75 "last.\n"
76 "\n"
77 " 'Nearly there!' the Queen repeated. 'Why, we passed it ten\n"
78 "minutes ago! Faster!' And they ran on for a time in silence,\n"
79 "with the wind whistling in Alice's ears, and almost blowing her\n"
80 "hair off her head, she fancied.\n"
81 "\n"
82 " 'Now! Now!' cried the Queen. 'Faster! Faster!' And they\n"
83 "went so fast that at last they seemed to skim through the air,\n"
84 "hardly touching the ground with their feet, till suddenly, just\n"
85 "as Alice was getting quite exhausted, they stopped, and she found\n"
86 "herself sitting on the ground, breathless and giddy.\n"
87 "\n"
88 " The Queen propped her up against a tree, and said kindly, 'You\n"
89 "may rest a little now.'\n"
90 "\n"
91 " Alice looked round her in great surprise. 'Why, I do believe\n"
92 "we've been under this tree the whole time! Everything's just as\n"
93 "it was!'\n"
94 "\n"
95 " 'Of course it is,' said the Queen, 'what would you have it?'\n"
96 "\n"
97 " 'Well, in OUR country,' said Alice, still panting a little,\n"
98 "'you'd generally get to somewhere else--if you ran very fast\n"
99 "for a long time, as we've been doing.'\n"
100 "\n"
101 " 'A slow sort of country!' said the Queen. 'Now, HERE, you see,\n"
102 "it takes all the running YOU can do, to keep in the same place.\n"
103 "If you want to get somewhere else, you must run at least twice as\n"
104 "fast as that!'\n"
105 "\n"
106 " 'I'd rather not try, please!' said Alice. 'I'm quite content\n"
107 "to stay here--only I AM so hot and thirsty!'\n"
108 "\n"
109 " -- Lewis Carroll, 'Through the Looking-Glass'.";
110 
111 /* NdbRecord objects. */
112 
113 const NdbRecord *key_record; // For specifying table key
114 const NdbRecord *blob_record; // For accessing blob
115 const NdbRecord *full_record; // All columns, for insert
116 
117 /* C struct representing the row layout */
118 struct MyRow
119 {
120  unsigned int myId;
121 
122  /* Pointer to Blob handle for operations on the blob column
123  * Space must be left for it in the row, but a pointer to the
124  * blob handle can also be obtained via calls to
125  * NdbOperation::getBlobHandle()
126  */
127  NdbBlob* myText;
128 };
129 
130 static void setup_records(Ndb *myNdb)
131 {
133 
134  NdbDictionary::Dictionary *myDict= myNdb->getDictionary();
135  const NdbDictionary::Table *myTable= myDict->getTable("api_blob_ndbrecord");
136  if (myTable == NULL)
137  APIERROR(myDict->getNdbError());
138  const NdbDictionary::Column *col1= myTable->getColumn("my_id");
139  if (col1 == NULL)
140  APIERROR(myDict->getNdbError());
141  const NdbDictionary::Column *col2= myTable->getColumn("my_text");
142  if (col2 == NULL)
143  APIERROR(myDict->getNdbError());
144 
145  spec[0].column= col1;
146  spec[0].offset= offsetof(MyRow, myId);
147  spec[0].nullbit_byte_offset= 0;
148  spec[0].nullbit_bit_in_byte= 0;
149  spec[1].column= col2;
150  spec[1].offset= offsetof(MyRow, myText);
151  spec[1].nullbit_byte_offset= 0;
152  spec[1].nullbit_bit_in_byte= 0;
153 
154  key_record= myDict->createRecord(myTable, &spec[0], 1, sizeof(spec[0]));
155  if (key_record == NULL)
156  APIERROR(myDict->getNdbError());
157  blob_record= myDict->createRecord(myTable, &spec[1], 1, sizeof(spec[0]));
158  if (blob_record == NULL)
159  APIERROR(myDict->getNdbError());
160  full_record= myDict->createRecord(myTable, &spec[0], 2, sizeof(spec[0]));
161  if (full_record == NULL)
162  APIERROR(myDict->getNdbError());
163 }
164 
165 /*
166  Function to drop table.
167 */
168 void drop_table(MYSQL &mysql)
169 {
170  if (mysql_query(&mysql, "DROP TABLE api_blob_ndbrecord"))
171  MYSQLERROR(mysql);
172 }
173 
174 
175 /*
176  Functions to create table.
177 */
178 int try_create_table(MYSQL &mysql)
179 {
180  return mysql_query(&mysql,
181  "CREATE TABLE"
182  " api_blob_ndbrecord"
183  " (my_id INT UNSIGNED NOT NULL,"
184  " my_text TEXT NOT NULL,"
185  " PRIMARY KEY USING HASH (my_id))"
186  " ENGINE=NDB");
187 }
188 
189 void create_table(MYSQL &mysql)
190 {
191  if (try_create_table(mysql))
192  {
193  if (mysql_errno(&mysql) != ER_TABLE_EXISTS_ERROR)
194  MYSQLERROR(mysql);
195  std::cout << "MySQL Cluster already has example table: api_blob_ndbrecord. "
196  << "Dropping it..." << std::endl;
197  /******************
198  * Recreate table *
199  ******************/
200  drop_table(mysql);
201  if (try_create_table(mysql))
202  MYSQLERROR(mysql);
203  }
204 }
205 
206 int populate(Ndb *myNdb)
207 {
208  MyRow row;
209 
210  NdbTransaction *myTrans= myNdb->startTransaction();
211  if (myTrans == NULL)
212  APIERROR(myNdb->getNdbError());
213 
214  row.myId= 1;
215  const NdbOperation *myNdbOperation= myTrans->insertTuple(full_record, (const char*) &row);
216  if (myNdbOperation == NULL)
217  APIERROR(myTrans->getNdbError());
218 
219  NdbBlob *myBlobHandle= myNdbOperation->getBlobHandle("my_text");
220  if (myBlobHandle == NULL)
221  APIERROR(myNdbOperation->getNdbError());
222  myBlobHandle->setValue(text_quote, strlen(text_quote));
223 
224  int check= myTrans->execute(NdbTransaction::Commit);
225  myTrans->close();
226  return check != -1;
227 }
228 
229 
230 int update_key(Ndb *myNdb)
231 {
232  MyRow row;
233 
234  /*
235  Uppercase all characters in TEXT field, using primary key operation.
236  Use piece-wise read/write to avoid loading entire data into memory
237  at once.
238  */
239 
240  NdbTransaction *myTrans= myNdb->startTransaction();
241  if (myTrans == NULL)
242  APIERROR(myNdb->getNdbError());
243 
244  row.myId= 1;
245 
246  const NdbOperation *myNdbOperation=
247  myTrans->updateTuple(key_record,
248  (const char*) &row,
249  blob_record,
250  (const char*) &row);
251  if (myNdbOperation == NULL)
252  APIERROR(myTrans->getNdbError());
253 
254  NdbBlob *myBlobHandle= myNdbOperation->getBlobHandle("my_text");
255  if (myBlobHandle == NULL)
256  APIERROR(myNdbOperation->getNdbError());
257 
258  /* Execute NoCommit to make the blob handle active so
259  * that we can determine the actual Blob length
260  */
261  if (-1 == myTrans->execute(NdbTransaction::NoCommit))
262  APIERROR(myTrans->getNdbError());
263 
264  Uint64 length= 0;
265  if (-1 == myBlobHandle->getLength(length))
266  APIERROR(myBlobHandle->getNdbError());
267 
268  /*
269  A real application should use a much larger chunk size for
270  efficiency, preferably much larger than the part size, which
271  defaults to 2000. 64000 might be a good value.
272  */
273 #define CHUNK_SIZE 100
274  int chunk;
275  char buffer[CHUNK_SIZE];
276  for (chunk= (length-1)/CHUNK_SIZE; chunk >=0; chunk--)
277  {
278  Uint64 pos= chunk*CHUNK_SIZE;
279  Uint32 chunk_length= CHUNK_SIZE;
280  if (pos + chunk_length > length)
281  chunk_length= length - pos;
282 
283  /* Read from the end back, to illustrate seeking. */
284  if (-1 == myBlobHandle->setPos(pos))
285  APIERROR(myBlobHandle->getNdbError());
286  if (-1 == myBlobHandle->readData(buffer, chunk_length))
287  APIERROR(myBlobHandle->getNdbError());
288  int res= myTrans->execute(NdbTransaction::NoCommit);
289  if (-1 == res)
290  APIERROR(myTrans->getNdbError());
291 
292  /* Uppercase everything. */
293  for (Uint64 j= 0; j < chunk_length; j++)
294  buffer[j]= toupper(buffer[j]);
295 
296  if (-1 == myBlobHandle->setPos(pos))
297  APIERROR(myBlobHandle->getNdbError());
298  if (-1 == myBlobHandle->writeData(buffer, chunk_length))
299  APIERROR(myBlobHandle->getNdbError());
300  /* Commit on the final update. */
301  if (-1 == myTrans->execute(chunk ?
304  APIERROR(myTrans->getNdbError());
305  }
306 
307  myNdb->closeTransaction(myTrans);
308 
309  return 1;
310 }
311 
312 
313 int update_scan(Ndb *myNdb)
314 {
315  /*
316  Lowercase all characters in TEXT field, using a scan with
317  updateCurrentTuple().
318  */
319  char buffer[10000];
320 
321  NdbTransaction *myTrans= myNdb->startTransaction();
322  if (myTrans == NULL)
323  APIERROR(myNdb->getNdbError());
324 
325  NdbScanOperation *myScanOp=
326  myTrans->scanTable(blob_record, NdbOperation::LM_Exclusive);
327  if (myScanOp == NULL)
328  APIERROR(myTrans->getNdbError());
329  NdbBlob *myBlobHandle= myScanOp->getBlobHandle("my_text");
330  if (myBlobHandle == NULL)
331  APIERROR(myScanOp->getNdbError());
332  if (myBlobHandle->getValue(buffer, sizeof(buffer)))
333  APIERROR(myBlobHandle->getNdbError());
334 
335  /* Start the scan. */
336  if (-1 == myTrans->execute(NdbTransaction::NoCommit))
337  APIERROR(myTrans->getNdbError());
338 
339  const MyRow *out_row;
340  int res;
341  for (;;)
342  {
343  res= myScanOp->nextResult((const char**)&out_row, true, false);
344  if (res==1)
345  break; // Scan done.
346  else if (res)
347  APIERROR(myScanOp->getNdbError());
348 
349  Uint64 length= 0;
350  if (myBlobHandle->getLength(length) == -1)
351  APIERROR(myBlobHandle->getNdbError());
352 
353  /* Lowercase everything. */
354  for (Uint64 j= 0; j < length; j++)
355  buffer[j]= tolower(buffer[j]);
356 
357  /* 'Take over' the row locks from the scan to a separate
358  * operation for updating the tuple
359  */
360  const NdbOperation *myUpdateOp=
361  myScanOp->updateCurrentTuple(myTrans,
362  blob_record,
363  (const char*)out_row);
364  if (myUpdateOp == NULL)
365  APIERROR(myTrans->getNdbError());
366  NdbBlob *myBlobHandle2= myUpdateOp->getBlobHandle("my_text");
367  if (myBlobHandle2 == NULL)
368  APIERROR(myUpdateOp->getNdbError());
369  if (myBlobHandle2->setValue(buffer, length))
370  APIERROR(myBlobHandle2->getNdbError());
371 
372  if (-1 == myTrans->execute(NdbTransaction::NoCommit))
373  APIERROR(myTrans->getNdbError());
374  }
375 
376  if (-1 == myTrans->execute(NdbTransaction::Commit))
377  APIERROR(myTrans->getNdbError());
378 
379  myNdb->closeTransaction(myTrans);
380 
381  return 1;
382 }
383 
384 
385 struct ActiveHookData {
386  char buffer[10000];
387  Uint32 readLength;
388 };
389 
390 int myFetchHook(NdbBlob* myBlobHandle, void* arg)
391 {
392  ActiveHookData *ahd= (ActiveHookData *)arg;
393 
394  ahd->readLength= sizeof(ahd->buffer) - 1;
395  return myBlobHandle->readData(ahd->buffer, ahd->readLength);
396 }
397 
398 int fetch_key(Ndb *myNdb)
399 {
400  /* Fetch a blob without specifying how many bytes
401  * to read up front, in one execution using
402  * the 'ActiveHook' mechanism.
403  * The supplied ActiveHook procedure is called when
404  * the Blob handle becomes 'active'. At that point
405  * the length of the Blob can be obtained, and buffering
406  * arranged, and the data read requested.
407  */
408 
409  /* Separate rows used to specify key and hold result */
410  MyRow key_row;
411  MyRow out_row;
412 
413  /*
414  Fetch and show the blob field, using setActiveHook().
415  */
416 
417  NdbTransaction *myTrans= myNdb->startTransaction();
418  if (myTrans == NULL)
419  APIERROR(myNdb->getNdbError());
420 
421  key_row.myId= 1;
422  out_row.myText= NULL;
423  const NdbOperation *myNdbOperation=
424  myTrans->readTuple(key_record,
425  (const char*) &key_row,
426  blob_record,
427  (char*) &out_row);
428  if (myNdbOperation == NULL)
429  APIERROR(myTrans->getNdbError());
430 
431  /* This time, we'll get the blob handle from the row, because
432  * we can. Alternatively, we could use the normal mechanism
433  * of calling getBlobHandle().
434  */
435  NdbBlob *myBlobHandle= out_row.myText;
436  if (myBlobHandle == NULL)
437  APIERROR(myNdbOperation->getNdbError());
438  struct ActiveHookData ahd;
439  if (myBlobHandle->setActiveHook(myFetchHook, &ahd) == -1)
440  APIERROR(myBlobHandle->getNdbError());
441 
442  /*
443  Execute Commit, but calling our callback set up in setActiveHook()
444  before actually committing.
445  */
446  if (-1 == myTrans->execute(NdbTransaction::Commit))
447  APIERROR(myTrans->getNdbError());
448  myNdb->closeTransaction(myTrans);
449 
450  /* Our fetch callback will have been called during the execute(). */
451 
452  ahd.buffer[ahd.readLength]= '\0';
453  std::cout << "Fetched data:" << std::endl << ahd.buffer << std::endl;
454 
455  return 1;
456 }
457 
458 
459 int update2_key(Ndb *myNdb)
460 {
461  char buffer[10000];
462  MyRow row;
463 
464  /* Simple setValue() update specified before the
465  * Blob handle is made active
466  */
467 
468  NdbTransaction *myTrans= myNdb->startTransaction();
469  if (myTrans == NULL)
470  APIERROR(myNdb->getNdbError());
471 
472  row.myId= 1;
473  const NdbOperation *myNdbOperation=
474  myTrans->updateTuple(key_record,
475  (const char*)&row,
476  blob_record,
477  (char*) &row);
478  if (myNdbOperation == NULL)
479  APIERROR(myTrans->getNdbError());
480  NdbBlob *myBlobHandle= myNdbOperation->getBlobHandle("my_text");
481  if (myBlobHandle == NULL)
482  APIERROR(myNdbOperation->getNdbError());
483  memset(buffer, ' ', sizeof(buffer));
484  if (myBlobHandle->setValue(buffer, sizeof(buffer)) == -1)
485  APIERROR(myBlobHandle->getNdbError());
486 
487  if (-1 == myTrans->execute(NdbTransaction::Commit))
488  APIERROR(myTrans->getNdbError());
489  myNdb->closeTransaction(myTrans);
490 
491  return 1;
492 }
493 
494 
495 int delete_key(Ndb *myNdb)
496 {
497  MyRow row;
498 
499  /* Deletion of row containing blob via primary key. */
500 
501  NdbTransaction *myTrans= myNdb->startTransaction();
502  if (myTrans == NULL)
503  APIERROR(myNdb->getNdbError());
504 
505  row.myId= 1;
506  const NdbOperation *myNdbOperation= myTrans->deleteTuple(key_record,
507  (const char*)&row,
508  full_record);
509  if (myNdbOperation == NULL)
510  APIERROR(myTrans->getNdbError());
511 
512  if (-1 == myTrans->execute(NdbTransaction::Commit))
513  APIERROR(myTrans->getNdbError());
514  myNdb->closeTransaction(myTrans);
515 
516  return 1;
517 }
518 
519 
520 int main(int argc, char**argv)
521 {
522  if (argc != 3)
523  {
524  std::cout << "Arguments are <socket mysqld> <connect_string cluster>.\n";
525  exit(-1);
526  }
527  char *mysqld_sock = argv[1];
528  const char *connectstring = argv[2];
529  ndb_init();
530  MYSQL mysql;
531 
532  /* Connect to mysql server and create table. */
533  {
534  if ( !mysql_init(&mysql) ) {
535  std::cout << "mysql_init failed.\n";
536  exit(-1);
537  }
538  if ( !mysql_real_connect(&mysql, "localhost", "root", "", "",
539  0, mysqld_sock, 0) )
540  MYSQLERROR(mysql);
541 
542  mysql_query(&mysql, "CREATE DATABASE ndb_examples");
543  if (mysql_query(&mysql, "USE ndb_examples") != 0)
544  MYSQLERROR(mysql);
545 
546  create_table(mysql);
547  }
548 
549  /* Connect to ndb cluster. */
550 
551  Ndb_cluster_connection cluster_connection(connectstring);
552  if (cluster_connection.connect(4, 5, 1))
553  {
554  std::cout << "Unable to connect to cluster within 30 secs." << std::endl;
555  exit(-1);
556  }
557  /* Optionally connect and wait for the storage nodes (ndbd's). */
558  if (cluster_connection.wait_until_ready(30,0) < 0)
559  {
560  std::cout << "Cluster was not ready within 30 secs.\n";
561  exit(-1);
562  }
563 
564  Ndb myNdb(&cluster_connection,"ndb_examples");
565  if (myNdb.init(1024) == -1) { // Set max 1024 parallel transactions
566  APIERROR(myNdb.getNdbError());
567  exit(-1);
568  }
569 
570  setup_records(&myNdb);
571 
572  if(populate(&myNdb) > 0)
573  std::cout << "populate: Success!" << std::endl;
574 
575  if(update_key(&myNdb) > 0)
576  std::cout << "update_key: Success!" << std::endl;
577 
578  if(update_scan(&myNdb) > 0)
579  std::cout << "update_scan: Success!" << std::endl;
580 
581  if(fetch_key(&myNdb) > 0)
582  std::cout << "fetch_key: Success!" << std::endl;
583 
584  if(update2_key(&myNdb) > 0)
585  std::cout << "update2_key: Success!" << std::endl;
586 
587  if(delete_key(&myNdb) > 0)
588  std::cout << "delete_key: Success!" << std::endl;
589 
590  return 0;
591 }