1 /*
2 Copyright (c) 2003, 2019, 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 /******************************************************************************
27 Name: NdbOperationSearch.C
28 Include:
29 Link:
30 Author: UABMNST Mona Natterkvist UAB/B/SD
31 Date: 970829
32 Version: 0.1
33 Description: Interface between TIS and NDB
34 Documentation:
35 Adjust: 971022 UABMNST First version.
36 971206 UABRONM
37 *****************************************************************************/
38 #include "API.hpp"
39
40
41 #include <AttributeHeader.hpp>
42 #include <signaldata/TcKeyReq.hpp>
43 #include <signaldata/KeyInfo.hpp>
44
45 /******************************************************************************
46 CondIdType equal(const char* anAttrName, char* aValue, Uint32 aVarKeylen);
47
48 Return Value Return 0 : Equal was successful.
49 Return -1: In all other case.
50 Parameters: anAttrName : Attribute name for search condition..
51 aValue : Referense to the search value.
52 aVariableKeylen : The length of key in bytes
53 Remark: Defines search condition with equality anAttrName.
54 ******************************************************************************/
55 int
equal_impl(const NdbColumnImpl * tAttrInfo,const char * aValuePassed)56 NdbOperation::equal_impl(const NdbColumnImpl* tAttrInfo,
57 const char* aValuePassed)
58 {
59 DBUG_ENTER("NdbOperation::equal_impl");
60 DBUG_PRINT("enter", ("col: %s op: %d val: 0x%lx",
61 (tAttrInfo == NULL) ? "NULL" :
62 tAttrInfo->m_name.c_str(),
63 theOperationType,
64 (long) aValuePassed));
65
66 const char* aValue = aValuePassed;
67 const Uint32 MaxKeyLenInLongWords= (NDB_MAX_KEY_SIZE + 7)/8;
68 Uint64 tempData[ MaxKeyLenInLongWords ];
69
70 if ((theStatus == OperationDefined) &&
71 (aValue != NULL) &&
72 (tAttrInfo != NULL )) {
73 /******************************************************************************
74 * Start by checking that the attribute is a tuple key.
75 * This value is also the word order in the tuple key of this
76 * tuple key attribute.
77 * Then check that this tuple key has not already been defined.
78 * Finally check if all tuple key attributes have been defined. If
79 * this is true then set Operation state to tuple key defined.
80 *****************************************************************************/
81
82 /*
83 * For each call theTupleKeyDefined stores 3 items:
84 *
85 * [0] = m_column_no (external column id)
86 * [1] = 1-based index of first word of accumulating keyinfo
87 * [2] = number of words of keyinfo
88 *
89 * This is used to re-order keyinfo if not in m_attrId order.
90 *
91 * Note: No point to "clean up" this code. The upcoming
92 * record-based ndb api makes it obsolete.
93 */
94
95 Uint32 tAttrId = tAttrInfo->m_column_no; // not m_attrId;
96 Uint32 i = 0;
97 if (tAttrInfo->m_pk) {
98 Uint32 tKeyDefined = theTupleKeyDefined[0][2];
99 Uint32 tKeyAttrId = theTupleKeyDefined[0][0];
100 do {
101 if (tKeyDefined == false) {
102 goto keyEntryFound;
103 } else {
104 if (tKeyAttrId != tAttrId) {
105 /******************************************************************
106 * We read the key defined variable in advance.
107 * It could potentially read outside its area when
108 * i = MAXNROFTUPLEKEY - 1,
109 * it is not a problem as long as the variable
110 * theTupleKeyDefined is defined
111 * in the middle of the object.
112 * Reading wrong data and not using it causes no problems.
113 *****************************************************************/
114 i++;
115 tKeyAttrId = theTupleKeyDefined[i][0];
116 tKeyDefined = theTupleKeyDefined[i][2];
117 continue;
118 } else {
119 goto equal_error2;
120 }//if
121 }//if
122 } while (i < NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY);
123 goto equal_error2;
124 } else {
125 goto equal_error1;
126 }
127 /*************************************************************************
128 * Now it is time to retrieve the tuple key data from the pointer supplied
129 * by the application.
130 * We have to retrieve the size of the attribute in words and bits.
131 *************************************************************************/
132 keyEntryFound:
133 Uint32 sizeInBytes;
134 if (! tAttrInfo->get_var_length(aValue, sizeInBytes)) {
135 setErrorCodeAbort(4209);
136 DBUG_RETURN(-1);
137 }
138
139 Uint32 tKeyInfoPosition =
140 i == 0 ? 1 : theTupleKeyDefined[i-1][1] + theTupleKeyDefined[i-1][2];
141 theTupleKeyDefined[i][0] = tAttrId;
142 theTupleKeyDefined[i][1] = tKeyInfoPosition;
143 theTupleKeyDefined[i][2] = (sizeInBytes + 3) / 4;
144
145 {
146 /************************************************************************
147 * Check if the pointer of the value passed is aligned on a 4 byte
148 * boundary. If so only assign the pointer to the internal variable
149 * aValue. If it is not aligned then we start by copying the value to
150 * tempData and use this as aValue instead.
151 ***********************************************************************/
152 const bool tDistrKey = tAttrInfo->m_distributionKey;
153 const int attributeSize = sizeInBytes;
154 const int slack = sizeInBytes & 3;
155 const int align = Uint32(UintPtr(aValue)) & 7;
156
157 if (((align & 3) != 0) || (slack != 0) || (tDistrKey && (align != 0)))
158 {
159 ((Uint32*)tempData)[attributeSize >> 2] = 0;
160 memcpy(&tempData[0], aValue, attributeSize);
161 aValue = (char*)&tempData[0];
162 }//if
163 }
164
165 Uint32 totalSizeInWords = (sizeInBytes + 3)/4; // Inc. bits in last word
166 theTupKeyLen += totalSizeInWords;
167 #if 0
168 else {
169 /************************************************************************
170 * The attribute is a variable array. We need to use the length parameter
171 * to know the size of this attribute in the key information and
172 * variable area. A key is however not allowed to be larger than 4
173 * kBytes and this is checked for variable array attributes
174 * used as keys.
175 ************************************************************************/
176 Uint32 tMaxVariableKeyLenInWord = (MAXTUPLEKEYLENOFATTERIBUTEINWORD -
177 tKeyInfoPosition);
178 tAttrSizeInBits = aVariableKeyLen << 3;
179 tAttrSizeInWords = tAttrSizeInBits >> 5;
180 tAttrBitsInLastWord = tAttrSizeInBits - (tAttrSizeInWords << 5);
181 tAttrLenInWords = ((tAttrSizeInBits + 31) >> 5);
182 if (tAttrLenInWords > tMaxVariableKeyLenInWord) {
183 setErrorCodeAbort(4207);
184 return -1;
185 }//if
186 theTupKeyLen = theTupKeyLen + tAttrLenInWords;
187 }//if
188 #endif
189
190 /**************************************************************************
191 * If the operation is an insert request and the attribute is stored then
192 * we also set the value in the stored part through putting the
193 * information in the ATTRINFO signals.
194 *************************************************************************/
195 OperationType tOpType = theOperationType;
196 if ((tOpType == InsertRequest) ||
197 (tOpType == WriteRequest)) {
198 Uint32 ahValue;
199
200 if(m_accessTable == m_currentTable) {
201 AttributeHeader::init(&ahValue, tAttrInfo->m_attrId, sizeInBytes);
202 } else {
203 assert(tOpType == WriteRequest && m_accessTable->m_index);
204 // use attrId of primary table column
205 int column_no_current_table =
206 m_accessTable->m_index->m_columns[tAttrId]->m_keyInfoPos;
207 int attr_id_current_table =
208 m_currentTable->m_columns[column_no_current_table]->m_attrId;
209 AttributeHeader::init(&ahValue, attr_id_current_table, sizeInBytes);
210 }
211
212 insertATTRINFO( ahValue );
213 insertATTRINFOloop((Uint32*)aValue, totalSizeInWords);
214 }//if
215
216 /**************************************************************************
217 * Store the Key information in the TCKEYREQ and KEYINFO signals.
218 *************************************************************************/
219 if (insertKEYINFO(aValue, tKeyInfoPosition, totalSizeInWords) != -1) {
220 /************************************************************************
221 * Add one to number of tuple key attributes defined.
222 * If all have been defined then set the operation state to indicate
223 * that tuple key is defined.
224 * Thereby no more search conditions are allowed in this version.
225 ***********************************************************************/
226 Uint32 tNoKeysDef = theNoOfTupKeyLeft - 1;
227 Uint32 tErrorLine = theErrorLine;
228 unsigned char tInterpretInd = theInterpretIndicator;
229 theNoOfTupKeyLeft = tNoKeysDef;
230 tErrorLine++;
231 theErrorLine = tErrorLine;
232
233 if (tNoKeysDef == 0) {
234
235 // re-order keyinfo if not entered in order
236 if (m_accessTable->m_noOfKeys != 1) {
237 for (Uint32 i = 0; i < m_accessTable->m_noOfKeys; i++) {
238 Uint32 k = theTupleKeyDefined[i][0]; // column_no
239 if (m_accessTable->m_columns[k]->m_keyInfoPos != i) {
240 DBUG_PRINT("info", ("key disorder at %d", i));
241 reorderKEYINFO();
242 break;
243 }
244 }
245 }
246
247 if (tOpType == UpdateRequest) {
248 if (tInterpretInd == 1) {
249 theStatus = GetValue;
250 } else {
251 theStatus = SetValue;
252 }//if
253 DBUG_RETURN(0);
254 } else if ((tOpType == ReadRequest) || (tOpType == DeleteRequest) ||
255 (tOpType == ReadExclusive)) {
256 theStatus = GetValue;
257 // create blob handles automatically
258 if (tOpType == DeleteRequest && m_currentTable->m_noOfBlobs != 0) {
259 for (unsigned i = 0; i < m_currentTable->m_columns.size(); i++) {
260 NdbColumnImpl* c = m_currentTable->m_columns[i];
261 assert(c != 0);
262 if (c->getBlobType()) {
263 if (getBlobHandle(theNdbCon, c) == NULL)
264 DBUG_RETURN(-1);
265 }
266 }
267 }
268 DBUG_RETURN(0);
269 } else if ((tOpType == InsertRequest) || (tOpType == WriteRequest)) {
270 theStatus = SetValue;
271 DBUG_RETURN(0);
272 } else {
273 setErrorCodeAbort(4005);
274 DBUG_RETURN(-1);
275 }//if
276 DBUG_RETURN(0);
277 }//if
278 } else {
279 DBUG_RETURN(-1);
280 }//if
281 DBUG_RETURN(0);
282 }
283
284 if (aValue == NULL) {
285 // NULL value in primary key
286 setErrorCodeAbort(4505);
287 DBUG_RETURN(-1);
288 }//if
289
290 if ( tAttrInfo == NULL ) {
291 // Attribute name not found in table
292 setErrorCodeAbort(4004);
293 DBUG_RETURN(-1);
294 }//if
295
296 if (theStatus == GetValue || theStatus == SetValue){
297 // All pk's defined
298 setErrorCodeAbort(4225);
299 DBUG_RETURN(-1);
300 }//if
301
302 ndbout_c("theStatus: %d", theStatus);
303
304 // If we come here, set a general errorcode
305 // and exit
306 setErrorCodeAbort(4200);
307 DBUG_RETURN(-1);
308
309 equal_error1:
310 setErrorCodeAbort(4205);
311 DBUG_RETURN(-1);
312
313 equal_error2:
314 setErrorCodeAbort(4206);
315 DBUG_RETURN(-1);
316 }
317
318 /******************************************************************************
319 * int insertKEYINFO(const char* aValue, aStartPosition,
320 * anAttrSizeInWords, Uint32 anAttrBitsInLastWord);
321 *
322 * Return Value: Return 0 : insertKEYINFO was succesful.
323 * Return -1: In all other case.
324 * Parameters: aValue: the data to insert into KEYINFO.
325 * aStartPosition : Start position for Tuplekey in
326 * KEYINFO (TCKEYREQ).
327 * aKeyLenInByte : Length of tuplekey or part of tuplekey
328 * anAttrBitsInLastWord : Nr of bits in last word.
329 * Remark: Puts the the data into either TCKEYREQ signal
330 * or KEYINFO signal.
331 *****************************************************************************/
332 int
insertKEYINFO(const char * aValue,Uint32 aStartPosition,Uint32 anAttrSizeInWords)333 NdbOperation::insertKEYINFO(const char* aValue,
334 Uint32 aStartPosition,
335 Uint32 anAttrSizeInWords)
336 {
337 NdbApiSignal* tSignal;
338 NdbApiSignal* tCurrentKEYINFO;
339 //register NdbApiSignal* tTCREQ = theTCREQ;
340 Uint32 tAttrPos;
341 Uint32 tPosition;
342 Uint32 tEndPos;
343 Uint32 tPos;
344 Uint32 signalCounter;
345
346 /*****************************************************************************
347 * Calculate the end position of the attribute in the key information. *
348 * Since the first attribute starts at position one we need to subtract *
349 * one to get the correct end position. *
350 * We must also remember the last word with only partial information. *
351 *****************************************************************************/
352 tEndPos = aStartPosition + anAttrSizeInWords - 1;
353
354 if ((tEndPos < 9)) {
355 Uint32 tkeyData = *(Uint32*)aValue;
356 //TcKeyReq* tcKeyReq = CAST_PTR(TcKeyReq, tTCREQ->getDataPtrSend());
357 Uint32* tDataPtr = (Uint32*)aValue;
358 tAttrPos = 1;
359 Uint32* tkeyDataPtr = theKEYINFOptr + aStartPosition - 1;
360 // (Uint32*)&tcKeyReq->keyInfo[aStartPosition - 1];
361 do {
362 tDataPtr++;
363 *tkeyDataPtr = tkeyData;
364 if (tAttrPos < anAttrSizeInWords) {
365 ;
366 } else {
367 return 0;
368 }//if
369 tkeyData = *tDataPtr;
370 tkeyDataPtr++;
371 tAttrPos++;
372 } while (1);
373 return 0;
374 }//if
375 /*****************************************************************************
376 * Allocate all the KEYINFO signals needed for this key before starting *
377 * to fill the signals with data. This simplifies error handling and *
378 * avoids duplication of code. *
379 *****************************************************************************/
380 tAttrPos = 0;
381 signalCounter = 1;
382 while(tEndPos > theTotalNrOfKeyWordInSignal)
383 {
384 tSignal = theNdb->getSignal();
385 if (tSignal == NULL)
386 {
387 setErrorCodeAbort(4000);
388 return -1;
389 }
390 if (tSignal->setSignal(m_keyInfoGSN, refToBlock(theNdbCon->m_tcRef)) == -1)
391 {
392 setErrorCodeAbort(4001);
393 return -1;
394 }
395 tSignal->setLength(KeyInfo::MaxSignalLength);
396 if (theTCREQ->next() != NULL)
397 theLastKEYINFO->next(tSignal);
398 else
399 theTCREQ->next(tSignal);
400
401 theLastKEYINFO = tSignal;
402 theLastKEYINFO->next(NULL);
403 theTotalNrOfKeyWordInSignal += 20;
404 }
405
406 /*****************************************************************************
407 * Change to variable tPosition for more appropriate naming of rest of *
408 * the code. We must set up current KEYINFO already here if the last *
409 * word is a word which is set at LastWordLabel and at the same time *
410 * this is the first word in a KEYINFO signal. *
411 *****************************************************************************/
412 tPosition = aStartPosition;
413 tCurrentKEYINFO = theTCREQ->next();
414
415 /*****************************************************************************
416 * Start by filling up Key information in the 8 words allocated in the *
417 * TC[KEY/INDX]REQ signal. *
418 *****************************************************************************/
419 while (tPosition < 9)
420 {
421 theKEYINFOptr[tPosition-1] = * (Uint32*)(aValue + (tAttrPos << 2));
422 tAttrPos++;
423 if (anAttrSizeInWords == tAttrPos)
424 goto LastWordLabel;
425 tPosition++;
426 }
427
428 /*****************************************************************************
429 * We must set up the start position of the writing of Key information *
430 * before we start the writing of KEYINFO signals. If the start is not *
431 * the first word of the first KEYINFO signals then we must step forward*
432 * to the proper KEYINFO signal and set the signalCounter properly. *
433 * signalCounter is set to the actual position in the signal ( = 4 for *
434 * first key word in KEYINFO signal. *
435 *****************************************************************************/
436 tPos = 8;
437 while ((tPosition - tPos) > 20)
438 {
439 tCurrentKEYINFO = tCurrentKEYINFO->next();
440 tPos += 20;
441 }
442 signalCounter = tPosition - tPos + 3;
443
444 /*****************************************************************************
445 * The loop that actually fills in the Key information into the KEYINFO *
446 * signals. Can be optimised by writing larger chunks than 4 bytes at a *
447 * time. *
448 *****************************************************************************/
449 do
450 {
451 if (signalCounter > 23)
452 {
453 tCurrentKEYINFO = tCurrentKEYINFO->next();
454 signalCounter = 4;
455 }
456 tCurrentKEYINFO->setData(*(Uint32*)(aValue + (tAttrPos << 2)),
457 signalCounter);
458 tAttrPos++;
459 if (anAttrSizeInWords == tAttrPos)
460 goto LastWordLabel;
461 tPosition++;
462 signalCounter++;
463 } while (1);
464
465 LastWordLabel:
466 return 0;
467 }
468
469 void
reorderKEYINFO()470 NdbOperation::reorderKEYINFO()
471 {
472 Uint32 data[ NDB_MAX_KEYSIZE_IN_WORDS ];
473 Uint32 size = NDB_MAX_KEYSIZE_IN_WORDS;
474 int rc = getKeyFromTCREQ(data, size);
475 (void)rc;
476 assert(rc == 0);
477 Uint32 pos = 1;
478 Uint32 k;
479 for (k = 0; k < m_accessTable->m_noOfKeys; k++) {
480 Uint32 i;
481 for (i = 0; i < m_accessTable->m_columns.size(); i++) {
482 NdbColumnImpl* col = m_accessTable->m_columns[i];
483 if (col->m_pk && col->m_keyInfoPos == k) {
484 Uint32 j;
485 for (j = 0; j < m_accessTable->m_noOfKeys; j++) {
486 if (theTupleKeyDefined[j][0] == i) {
487 Uint32 off = theTupleKeyDefined[j][1] - 1;
488 Uint32 len = theTupleKeyDefined[j][2];
489 assert(off < NDB_MAX_KEYSIZE_IN_WORDS &&
490 off + len <= NDB_MAX_KEYSIZE_IN_WORDS);
491 int ret = insertKEYINFO((char*)&data[off], pos, len);
492 (void)ret;
493 assert(ret == 0);
494 pos += len;
495 break;
496 }
497 }
498 assert(j < m_accessTable->m_columns.size());
499 break;
500 }
501 }
502 assert(i < m_accessTable->m_columns.size());
503 }
504 }
505
506 int
getKeyFromTCREQ(Uint32 * data,Uint32 & size)507 NdbOperation::getKeyFromTCREQ(Uint32* data, Uint32 & size)
508 {
509 /* Check that we can correctly return a valid key */
510 if ((size < theTupKeyLen) || (theTupKeyLen == 0))
511 return -1;
512
513 size = theTupKeyLen;
514 unsigned pos = 0;
515 while (pos < 8 && pos < size) {
516 data[pos] = theKEYINFOptr[pos];
517 pos++;
518 }
519 NdbApiSignal* tSignal = theTCREQ->next();
520 unsigned n = 0;
521 while (pos < size) {
522 if (n == KeyInfo::DataLength) {
523 tSignal = tSignal->next();
524 n = 0;
525 }
526 data[pos++] =
527 tSignal->getDataPtrSend()[KeyInfo::HeaderLength + n++];
528 }
529 return 0;
530 }
531
532 void
setPartitionId(Uint32 value)533 NdbOperation::setPartitionId(Uint32 value)
534 {
535 if (theStatus == UseNdbRecord)
536 {
537 /* Method not allowed for NdbRecord, use OperationOptions or
538 ScanOptions structure instead */
539 setErrorCodeAbort(4515);
540 return; // TODO : Consider adding int rc for error
541 }
542
543 /* We only allow setPartitionId() for :
544 * PrimaryKey ops on a UserDefined partitioned table
545 * Ordered index scans
546 * Table scans
547 *
548 * It is not allowed on :
549 * Primary key access to Natively partitioned tables
550 * Any unique key access
551 */
552 assert(((m_type == PrimaryKeyAccess) &&
553 (m_currentTable->getFragmentType() ==
554 NdbDictionary::Object::UserDefined)) ||
555 (m_type == OrderedIndexScan) ||
556 (m_type == TableScan));
557
558 theDistributionKey = value;
559 theDistrKeyIndicator_ = 1;
560 DBUG_PRINT("info", ("NdbOperation::setPartitionId: %u",
561 theDistributionKey));
562 }
563
564 Uint32
getPartitionId() const565 NdbOperation::getPartitionId() const
566 {
567 DBUG_PRINT("info", ("NdbOperation::getPartitionId: %u ind=%d",
568 theDistributionKey, theDistrKeyIndicator_));
569 return theDistributionKey;
570 }
571