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