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