1 /*
2    Copyright (c) 2003, 2020, 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 #include "Restore.hpp"
26 #include <NdbTCP.h>
27 #include <OutputStream.hpp>
28 #include <Bitmask.hpp>
29 
30 #include <AttributeHeader.hpp>
31 #include <trigger_definitions.h>
32 #include <SimpleProperties.hpp>
33 #include <signaldata/DictTabInfo.hpp>
34 #include <ndb_limits.h>
35 #include <NdbAutoPtr.hpp>
36 #include "../src/kernel/blocks/backup/BackupFormat.hpp"
37 #include "../src/ndbapi/NdbDictionaryImpl.hpp"
38 
39 #include "restore_tables.h"
40 #include <NdbThread.h>
41 #include "../src/kernel/vm/Emulator.hpp"
42 
43 extern thread_local EmulatedJamBuffer* NDB_THREAD_TLS_JAM;
44 
45 extern NdbRecordPrintFormat g_ndbrecord_print_format;
46 extern bool ga_skip_unknown_objects;
47 extern bool ga_skip_broken_objects;
48 extern bool opt_include_stored_grants;
49 
50 #define LOG_MSGLEN 1024
51 
52 Uint16 Twiddle16(Uint16 in); // Byte shift 16-bit data
53 Uint32 Twiddle32(Uint32 in); // Byte shift 32-bit data
54 Uint64 Twiddle64(Uint64 in); // Byte shift 64-bit data
55 
56 
57 /*
58   TwiddleUtil
59 
60   Utility class used when swapping byteorder
61   of one attribute in a table
62 
63 */
64 
65 class TwiddleUtil {
66   Uint32 m_twiddle_size;
67   Uint32 m_twiddle_array_size;
68 public:
69   TwiddleUtil(); // Not implemented
70   TwiddleUtil(const TwiddleUtil&); // Not implemented
71 
TwiddleUtil(const AttributeDesc * const attr_desc)72   TwiddleUtil(const AttributeDesc * const attr_desc) {
73     const NdbDictionary::Column::Type attribute_type =
74       attr_desc->m_column->getType();
75 
76     switch(attribute_type){
77     case NdbDictionary::Column::Datetime:
78       // Datetime is stored as 8x8, should be twiddled as 64 bit
79       assert(attr_desc->size == 8);
80       assert(attr_desc->arraySize == 8);
81       m_twiddle_size = 64;
82       m_twiddle_array_size = 1;
83       break;
84 
85     case NdbDictionary::Column::Timestamp:
86       // Timestamp is stored as 4x8, should be twiddled as 32 bit
87       assert(attr_desc->size == 8);
88       assert(attr_desc->arraySize == 4);
89       m_twiddle_size = 32;
90       m_twiddle_array_size = 1;
91       break;
92 
93     case NdbDictionary::Column::Blob:
94     case NdbDictionary::Column::Text:
95       if (attr_desc->m_column->getArrayType() ==
96           NdbDictionary::Column::ArrayTypeFixed)
97       {
98         // Length of fixed size blob which is stored in first 64 bit's
99         // has to be twiddled, the remaining byte stream left as is
100         assert(attr_desc->size == 8);
101         assert(attr_desc->arraySize > 8);
102         m_twiddle_size = 64;
103         m_twiddle_array_size = 1;
104         break;
105       }
106       // Fall through - for blob/text with ArrayTypeVar
107     default:
108       // Default twiddling parameters
109       m_twiddle_size = attr_desc->size;
110       m_twiddle_array_size = attr_desc->arraySize;
111       break;
112     }
113 
114     assert(m_twiddle_array_size);
115     assert(m_twiddle_size);
116   }
117 
is_aligned(void * data_ptr) const118   bool is_aligned (void* data_ptr) const {
119     switch (m_twiddle_size){
120     case 8:
121       // Always aligned
122       return true;
123       break;
124     case 16:
125       return ((((size_t)data_ptr) & 1) == 0);
126       break;
127     case 32:
128       return ((((size_t)data_ptr) & 3) == 0);
129       break;
130     case 64:
131       return ((((size_t)data_ptr) & 7) == 0);
132       break;
133     default:
134       abort();
135       break;
136     }
137     return false; // Never reached
138   }
139 
twiddle_aligned(void * const data_ptr) const140   void twiddle_aligned(void* const data_ptr) const {
141     // Make sure the data pointer is properly aligned
142     assert(is_aligned(data_ptr));
143 
144     switch(m_twiddle_size){
145     case 8:
146       // Nothing to swap
147       break;
148     case 16:
149     {
150       Uint16* ptr = (Uint16*)data_ptr;
151       for (Uint32 i = 0; i < m_twiddle_array_size; i++){
152         *ptr = Twiddle16(*ptr);
153         ptr++;
154       }
155       break;
156     }
157     case 32:
158     {
159       Uint32* ptr = (Uint32*)data_ptr;
160       for (Uint32 i = 0; i < m_twiddle_array_size; i++){
161         *ptr = Twiddle32(*ptr);
162         ptr++;
163       }
164       break;
165     }
166     case 64:
167     {
168       Uint64* ptr = (Uint64*)data_ptr;
169       for (Uint32 i = 0; i < m_twiddle_array_size; i++){
170         *ptr = Twiddle64(*ptr);
171         ptr++;
172       }
173       break;
174     }
175     default:
176       abort();
177     } // switch
178   }
179 };
180 
181 
182 /*
183   BackupFile::twiddle_attribute
184 
185   Swap the byte order of one attribute whose data may or may not
186   be properly aligned for the current datatype
187 
188 */
189 
190 void
twiddle_atribute(const AttributeDesc * const attr_desc,AttributeData * attr_data)191 BackupFile::twiddle_atribute(const AttributeDesc * const attr_desc,
192                              AttributeData* attr_data)
193 {
194   TwiddleUtil map(attr_desc);
195 
196   // Check if data is aligned properly
197   void* data_ptr = (char*)attr_data->void_value;
198   Uint32 data_sz = attr_desc->getSizeInBytes();
199   bool aligned= map.is_aligned(data_ptr);
200   if (!aligned)
201   {
202     // The pointer is not properly aligned, copy the data
203     // to aligned memory before twiddling
204     m_twiddle_buffer.assign(data_ptr, data_sz);
205     data_ptr = m_twiddle_buffer.get_data();
206   }
207 
208   // Swap the byteorder of the aligned data
209   map.twiddle_aligned(data_ptr);
210 
211   if (!aligned)
212   {
213     // Copy data back from aligned memory
214     memcpy(attr_data->void_value,
215            m_twiddle_buffer.get_data(),
216            data_sz);
217   }
218 }
219 
220 
221 /*
222   BackupFile::Twiddle
223 
224   Swap the byteorder for one attribute if it was stored
225   in different byteorder than current host
226 
227 */
228 
229 bool
Twiddle(const AttributeDesc * const attr_desc,AttributeData * attr_data)230 BackupFile::Twiddle(const AttributeDesc * const attr_desc,
231                     AttributeData* attr_data)
232 {
233   // Check parameters are not NULL
234   assert(attr_desc);
235   assert(attr_data);
236 
237   // Make sure there is data to fiddle with
238   assert(!attr_data->null);
239   assert(attr_data->void_value);
240 
241   if(unlikely(!m_hostByteOrder))
242   {
243     // The data file is not in host byte order, the
244     // attribute need byte order swapped
245     twiddle_atribute(attr_desc, attr_data);
246   }
247 #ifdef VM_TRACE
248   else
249   {
250     // Increase test converage in debug mode by doing
251     // a double byte order swap to prove that both ways work
252     twiddle_atribute(attr_desc, attr_data);
253     twiddle_atribute(attr_desc, attr_data);
254   }
255 #endif
256 
257   return true;
258 }
259 
260 
261 FilteredNdbOut err(* new FileOutputStream(stderr), 0, 0);
262 FilteredNdbOut info(* new FileOutputStream(stdout), 1, 1);
263 FilteredNdbOut debug(* new FileOutputStream(stdout), 2, 0);
264 RestoreLogger restoreLogger;
265 
266 // To decide in what byte order data is
267 const Uint32 magicByteOrder = 0x12345678;
268 const Uint32 swappedMagicByteOrder = 0x78563412;
269 
RestoreMetaData(const char * path,Uint32 nodeId,Uint32 bNo,Uint32 partId,Uint32 partCount)270 RestoreMetaData::RestoreMetaData(const char* path, Uint32 nodeId,
271                                  Uint32 bNo, Uint32 partId, Uint32 partCount)
272 {
273   debug << "RestoreMetaData constructor" << endl;
274   m_part_id = partId;
275   m_part_count = partCount;
276   setCtlFile(nodeId, bNo, path);
277 }
278 
~RestoreMetaData()279 RestoreMetaData::~RestoreMetaData(){
280   for(Uint32 i= 0; i < allTables.size(); i++)
281   {
282     TableS *table = allTables[i];
283     for(Uint32 j= 0; j < table->m_fragmentInfo.size(); j++)
284       delete table->m_fragmentInfo[j];
285     delete table;
286   }
287 
288   for (Uint32 i = 0; i < m_objects.size(); i++)
289   {
290     switch (m_objects[i].m_objType)
291     {
292     case DictTabInfo::Tablespace:
293     {
294       NdbDictionary::Tablespace * dst =
295         (NdbDictionary::Tablespace *)m_objects[i].m_objPtr;
296       delete dst;
297       break;
298     }
299     case DictTabInfo::LogfileGroup:
300     {
301       NdbDictionary::LogfileGroup * dst =
302         (NdbDictionary::LogfileGroup *)m_objects[i].m_objPtr;
303       delete dst;
304       break;
305     }
306     case DictTabInfo::Datafile:
307     {
308       NdbDictionary::Datafile * dst =
309         (NdbDictionary::Datafile *)m_objects[i].m_objPtr;
310       delete dst;
311       break;
312     }
313     case DictTabInfo::Undofile:
314     {
315       NdbDictionary::Undofile * dst =
316         (NdbDictionary::Undofile *)m_objects[i].m_objPtr;
317       delete dst;
318       break;
319     }
320     case DictTabInfo::HashMap:
321     {
322       NdbDictionary::HashMap * dst =
323         (NdbDictionary::HashMap *)m_objects[i].m_objPtr;
324       delete dst;
325       break;
326     }
327     case DictTabInfo::ForeignKey:
328     {
329       NdbDictionary::ForeignKey * dst =
330         (NdbDictionary::ForeignKey *)m_objects[i].m_objPtr;
331       delete dst;
332       break;
333     }
334     default:
335       err << "Unsupported table type!! " << endl;
336       assert(false);
337       break;
338     }
339   }
340   m_objects.clear();
341 }
342 
343 TableS *
getTable(Uint32 tableId) const344 RestoreMetaData::getTable(Uint32 tableId) const {
345   for(Uint32 i= 0; i < allTables.size(); i++)
346     if(allTables[i]->getTableId() == tableId)
347       return allTables[i];
348   return NULL;
349 }
350 
351 Uint32
getStartGCP() const352 RestoreMetaData::getStartGCP() const {
353   return m_startGCP;
354 }
355 
356 Uint32
getStopGCP() const357 RestoreMetaData::getStopGCP() const {
358   return m_stopGCP;
359 }
360 
361 int
loadContent()362 RestoreMetaData::loadContent()
363 {
364   Uint32 noOfTables = readMetaTableList();
365   if(noOfTables == 0) {
366     return 1;
367   }
368   for(Uint32 i = 0; i<noOfTables; i++){
369     if(!readMetaTableDesc()){
370       return 0;
371     }
372   }
373   if (!markSysTables())
374     return 0;
375   if (!fixBlobs())
376     return 0;
377   if(!readGCPEntry())
378     return 0;
379 
380   if(!readFragmentInfo())
381     return 0;
382   return 1;
383 }
384 
385 Uint32
readMetaTableList()386 RestoreMetaData::readMetaTableList() {
387 
388   Uint32 sectionInfo[2];
389 
390   if (buffer_read(&sectionInfo, sizeof(sectionInfo), 1) != 1){
391     restoreLogger.log_error("readMetaTableList read header error");
392     return 0;
393   }
394   sectionInfo[0] = ntohl(sectionInfo[0]);
395   sectionInfo[1] = ntohl(sectionInfo[1]);
396 
397   const Uint32 tabCount = sectionInfo[1] - 2;
398 
399   void *tmp;
400   Uint32 tabsRead = 0;
401   while (tabsRead < tabCount){
402     int count = buffer_get_ptr(&tmp, 4, tabCount - tabsRead);
403     if(count == 0)
404       break;
405     tabsRead += count;
406   }
407   if (tabsRead != tabCount){
408     restoreLogger.log_error("readMetaTableList read tabCount error, "
409             "expected count = %u, actual count = %u", tabCount, tabsRead);
410     return 0;
411   }
412 #ifdef ERROR_INSERT
413   if(m_error_insert == NDB_RESTORE_ERROR_INSERT_SMALL_BUFFER)
414   {
415     // clear error insert
416     m_error_insert = 0;
417     m_buffer_sz = BUFFER_SIZE;
418   }
419 #endif
420   return tabCount;
421 }
422 
423 bool
readMetaTableDesc()424 RestoreMetaData::readMetaTableDesc() {
425 
426   Uint32 sectionInfo[3];
427 
428   // Read section header
429   Uint32 sz = sizeof(sectionInfo) >> 2;
430   if (m_fileHeader.NdbVersion < NDBD_ROWID_VERSION ||
431       ndbd_drop6(m_fileHeader.NdbVersion))
432   {
433     sz = 2;
434     sectionInfo[2] = htonl(DictTabInfo::UserTable);
435   }
436   if (buffer_read(&sectionInfo, 4*sz, 1) != 1){
437     restoreLogger.log_error("readMetaTableDesc read header error");
438     return false;
439   } // if
440   sectionInfo[0] = ntohl(sectionInfo[0]);
441   sectionInfo[1] = ntohl(sectionInfo[1]);
442   sectionInfo[2] = ntohl(sectionInfo[2]);
443 
444   assert(sectionInfo[0] == BackupFormat::TABLE_DESCRIPTION);
445 
446   // Read dictTabInfo buffer
447   const Uint32 len = (sectionInfo[1] - sz);
448   void *ptr;
449   if (buffer_get_ptr(&ptr, 4, len) != len){
450     restoreLogger.log_error("readMetaTableDesc read error");
451     return false;
452   } // if
453 
454   int errcode = 0;
455   DictObject obj = { sectionInfo[2], 0 };
456   switch(obj.m_objType){
457   case DictTabInfo::SystemTable:
458   case DictTabInfo::UserTable:
459   case DictTabInfo::UniqueHashIndex:
460   case DictTabInfo::OrderedIndex:
461     return parseTableDescriptor((Uint32*)ptr, len);
462     break;
463   case DictTabInfo::Tablespace:
464   {
465     NdbDictionary::Tablespace * dst = new NdbDictionary::Tablespace;
466     errcode =
467       NdbDictInterface::parseFilegroupInfo(NdbTablespaceImpl::getImpl(* dst),
468 					   (Uint32*)ptr, len);
469     if (errcode)
470       delete dst;
471     obj.m_objPtr = dst;
472     restoreLogger.log_debug("%p %u %s", obj.m_objPtr, dst->getObjectId(), dst->getName());
473     break;
474   }
475   case DictTabInfo::LogfileGroup:
476   {
477     NdbDictionary::LogfileGroup * dst = new NdbDictionary::LogfileGroup;
478     errcode =
479       NdbDictInterface::parseFilegroupInfo(NdbLogfileGroupImpl::getImpl(* dst),
480 					   (Uint32*)ptr, len);
481     if (errcode)
482       delete dst;
483     obj.m_objPtr = dst;
484     restoreLogger.log_debug("%p %u %s", obj.m_objPtr, dst->getObjectId(), dst->getName());
485     break;
486   }
487   case DictTabInfo::Datafile:
488   {
489     NdbDictionary::Datafile * dst = new NdbDictionary::Datafile;
490     errcode =
491       NdbDictInterface::parseFileInfo(NdbDatafileImpl::getImpl(* dst),
492 				      (Uint32*)ptr, len);
493     if (errcode)
494       delete dst;
495     obj.m_objPtr = dst;
496     restoreLogger.log_debug("%p %u %s", obj.m_objPtr, dst->getObjectId(), dst->getPath());
497     break;
498   }
499   case DictTabInfo::Undofile:
500   {
501     NdbDictionary::Undofile * dst = new NdbDictionary::Undofile;
502     errcode =
503       NdbDictInterface::parseFileInfo(NdbUndofileImpl::getImpl(* dst),
504 				      (Uint32*)ptr, len);
505     if (errcode)
506       delete dst;
507     obj.m_objPtr = dst;
508     restoreLogger.log_debug("%p %u %s", obj.m_objPtr, dst->getObjectId(), dst->getPath());
509     break;
510   }
511   case DictTabInfo::HashMap:
512   {
513     NdbDictionary::HashMap * dst = new NdbDictionary::HashMap;
514     errcode =
515       NdbDictInterface::parseHashMapInfo(NdbHashMapImpl::getImpl(* dst),
516                                          (Uint32*)ptr, len);
517     if (errcode)
518       delete dst;
519     obj.m_objPtr = dst;
520 
521     if (!m_hostByteOrder)
522     {
523       /**
524        * Bloddy byte-array, need to twiddle
525        */
526       Vector<Uint32> values;
527       Uint32 len = dst->getMapLen();
528       Uint32 zero = 0;
529       values.fill(len - 1, zero);
530       dst->getMapValues(values.getBase(), values.size());
531       for (Uint32 i = 0; i<len; i++)
532       {
533         values[i] = Twiddle16(values[i]);
534       }
535       dst->setMap(values.getBase(), values.size());
536     }
537 
538     m_objects.push(obj, 0); // Put first
539     return true;
540     break;
541   }
542   case DictTabInfo::ForeignKey:
543   {
544     NdbDictionary::ForeignKey * dst = new NdbDictionary::ForeignKey;
545     errcode =
546       NdbDictInterface::parseForeignKeyInfo(NdbForeignKeyImpl::getImpl(* dst),
547                                             (const Uint32*)ptr, len);
548     if (errcode)
549       delete dst;
550     obj.m_objPtr = dst;
551     restoreLogger.log_debug("%p %u %s", obj.m_objPtr, dst->getObjectId(), dst->getName());
552     break;
553   }
554   default:
555     if (ga_skip_unknown_objects)
556     {
557       restoreLogger.log_info("Skipping schema object with unknown table type %u",
558                               sectionInfo[2]);
559       return true;
560     }
561     else
562     {
563       restoreLogger.log_error("Unsupported table type!! %u", sectionInfo[2]);
564       return false;
565     }
566   }
567   if (errcode)
568   {
569     restoreLogger.log_error("Unable to parse dict info...%u %u",
570        sectionInfo[2], errcode);
571     return false;
572   }
573 
574   /**
575    * DD objects need to be sorted...
576    */
577   for(Uint32 i = 0; i<m_objects.size(); i++)
578   {
579     switch(sectionInfo[2]){
580     case DictTabInfo::Tablespace:
581       if (DictTabInfo::isFile(m_objects[i].m_objType))
582       {
583 	m_objects.push(obj, i);
584 	goto end;
585       }
586       break;
587     case DictTabInfo::LogfileGroup:
588     {
589       if (DictTabInfo::isFile(m_objects[i].m_objType) ||
590 	  m_objects[i].m_objType == DictTabInfo::Tablespace)
591       {
592 	m_objects.push(obj, i);
593 	goto end;
594       }
595       break;
596     }
597     default:
598       m_objects.push_back(obj);
599       goto end;
600     }
601   }
602   m_objects.push_back(obj);
603 
604 end:
605   return true;
606 }
607 
608 #define OLD_NDB_REP_DB  "cluster"
609 #define OLD_NDB_APPLY_TABLE "apply_status"
610 #define OLD_NDB_SCHEMA_TABLE "schema"
611 
612 bool
markSysTables()613 RestoreMetaData::markSysTables()
614 {
615   Uint32 i;
616   for (i = 0; i < getNoOfTables(); i++) {
617     TableS* table = allTables[i];
618     table->m_local_id = i;
619     const char* tableName = table->getTableName();
620     if ( // XXX should use type
621         strcmp(tableName, "SYSTAB_0") == 0 ||
622         strcmp(tableName, "NDB$EVENTS_0") == 0 ||
623         strcmp(tableName, "sys/def/SYSTAB_0") == 0 ||
624         strcmp(tableName, "sys/def/NDB$EVENTS_0") == 0 ||
625         // index stats tables and indexes
626         strncmp(tableName, NDB_INDEX_STAT_PREFIX,
627                 sizeof(NDB_INDEX_STAT_PREFIX)-1) == 0 ||
628         strstr(tableName, "/" NDB_INDEX_STAT_PREFIX) != 0 ||
629         /*
630           The following is for old MySQL versions,
631            before we changed the database name of the tables from
632            "cluster_replication" -> "cluster" -> "mysql"
633         */
634         strcmp(tableName, "cluster_replication/def/" OLD_NDB_APPLY_TABLE) == 0 ||
635         strcmp(tableName, OLD_NDB_REP_DB "/def/" OLD_NDB_APPLY_TABLE) == 0 ||
636         strcmp(tableName, OLD_NDB_REP_DB "/def/" OLD_NDB_SCHEMA_TABLE) == 0 ||
637         strcmp(tableName, NDB_REP_DB "/def/" NDB_APPLY_TABLE) == 0 ||
638         strcmp(tableName, NDB_REP_DB "/def/" NDB_SCHEMA_TABLE)== 0 ||
639         strcmp(tableName, "mysql/def/ndb_schema_result") == 0 ||
640         (strcmp(tableName, "mysql/def/ndb_sql_metadata") == 0
641          && !opt_include_stored_grants)
642        )
643     {
644       table->m_isSysTable = true;
645       if (strcmp(tableName, "SYSTAB_0") == 0 ||
646           strcmp(tableName, "sys/def/SYSTAB_0") == 0)
647         table->m_isSYSTAB_0 = true;
648     }
649   }
650   for (i = 0; i < getNoOfTables(); i++) {
651     TableS* auxTable = allTables[i];
652     const char* auxTableName = auxTable->getTableName();
653     // Use pattern matching to find blob tables or ordered indexes and
654     // associate them with their main tables
655     static constexpr const char * indxPattern = "sys/def/%d/";
656     static constexpr const char * blobPattern = "%[^/]/%[^/]/NDB$BLOB_%d_%d";
657      int id1, id2 = ~(Uint32)0;
658     char buf[256];
659 
660     if((sscanf(auxTableName, indxPattern, &id1) == 1) ||
661        (sscanf(auxTableName, blobPattern, buf, buf, &id1, &id2) == 4)) {
662       TableS *mainTable = getTable(id1);
663       if(mainTable) {
664         auxTable->m_isSysTable = mainTable->m_isSysTable;
665         auxTable->m_main_table = mainTable;
666         auxTable->m_main_column_id = id2;
667       } else {
668         restoreLogger.log_error("Restore: Bad primary table id in %s", auxTableName);
669         return false;
670       }
671     }
672   }
673   return true;
674 }
675 
676 bool
fixBlobs()677 RestoreMetaData::fixBlobs()
678 {
679   Uint32 i;
680   for (i = 0; i < getNoOfTables(); i++) {
681     TableS* table = allTables[i];
682     assert(table->m_dictTable != NULL);
683     NdbTableImpl& t = NdbTableImpl::getImpl(*table->m_dictTable);
684     const Uint32 noOfBlobs = t.m_noOfBlobs;
685     if (noOfBlobs == 0)
686       continue;
687     Uint32 n = 0;
688     Uint32 j;
689     for (j = 0; n < noOfBlobs; j++) {
690       NdbColumnImpl* c = t.getColumn(j);
691       assert(c != NULL);
692       if (!c->getBlobType())
693         continue;
694       // tinyblobs are counted in noOfBlobs...
695       n++;
696       if (c->getPartSize() == 0)
697         continue;
698       Uint32 k;
699       TableS* blobTable = NULL;
700       for (k = 0; k < getNoOfTables(); k++) {
701         TableS* tmp = allTables[k];
702         if (tmp->m_main_table == table &&
703             tmp->m_main_column_id == j) {
704           blobTable = tmp;
705           break;
706         }
707       }
708       if (blobTable == NULL)
709       {
710         table->m_broken = true;
711         /* Corrupt backup, has main table, but no blob table */
712         restoreLogger.log_error("Table %s has blob column %u (%s)"
713                " with missing parts table in backup.",
714                table->m_dictTable->getName(), j, c->m_name.c_str());
715         if (ga_skip_broken_objects)
716         {
717           continue;
718         }
719         else
720         {
721           return false;
722         }
723       }
724       assert(blobTable->m_dictTable != NULL);
725       assert(blobTable->m_blobTables.size() == 0);
726       NdbTableImpl& bt = NdbTableImpl::getImpl(*blobTable->m_dictTable);
727       const char* colName = c->m_blobVersion == 1 ? "DATA" : "NDB$DATA";
728       const NdbColumnImpl* bc = bt.getColumn(colName);
729       assert(bc != NULL);
730       assert(c->m_storageType == NDB_STORAGETYPE_MEMORY);
731       c->m_storageType = bc->m_storageType;
732 
733       table->m_blobTables.push_back(blobTable);
734     }
735   }
736   return true;
737 }
738 
739 bool
readGCPEntry()740 RestoreMetaData::readGCPEntry() {
741 
742   BackupFormat::CtlFile::GCPEntry dst;
743 
744   if(buffer_read(&dst, 1, sizeof(dst)) != sizeof(dst)){
745     restoreLogger.log_error("readGCPEntry read error");
746     return false;
747   }
748 
749   dst.SectionType = ntohl(dst.SectionType);
750   dst.SectionLength = ntohl(dst.SectionLength);
751 
752   if(dst.SectionType != BackupFormat::GCP_ENTRY){
753     restoreLogger.log_error("readGCPEntry invalid format");
754     return false;
755   }
756 
757   dst.StartGCP = ntohl(dst.StartGCP);
758   dst.StopGCP = ntohl(dst.StopGCP);
759 
760   m_startGCP = dst.StartGCP;
761   /**
762    * Stop GCP is recorded as StopGCP -1 by Backup.cpp
763    * We correct this here
764    * Backup format not changed
765    */
766   m_stopGCP = dst.StopGCP + 1;
767   return true;
768 }
769 
770 bool
readFragmentInfo()771 RestoreMetaData::readFragmentInfo()
772 {
773   BackupFormat::CtlFile::FragmentInfo fragInfo;
774   TableS * table = 0;
775   Uint32 tableId = RNIL;
776 
777   while (buffer_read(&fragInfo, 4, 2) == 2)
778   {
779     fragInfo.SectionType = ntohl(fragInfo.SectionType);
780     fragInfo.SectionLength = ntohl(fragInfo.SectionLength);
781 
782     if (fragInfo.SectionType != BackupFormat::FRAGMENT_INFO)
783     {
784       restoreLogger.log_error("readFragmentInfo invalid section type: %u",
785         fragInfo.SectionType);
786       return false;
787     }
788 
789     if (buffer_read(&fragInfo.TableId, (fragInfo.SectionLength-2)*4, 1) != 1)
790     {
791       restoreLogger.log_error("readFragmentInfo invalid section length: %u",
792         fragInfo.SectionLength);
793       return false;
794     }
795 
796     fragInfo.TableId = ntohl(fragInfo.TableId);
797     if (fragInfo.TableId != tableId)
798     {
799       tableId = fragInfo.TableId;
800       table = getTable(tableId);
801     }
802 
803     FragmentInfo * tmp = new FragmentInfo;
804     tmp->fragmentNo = ntohl(fragInfo.FragmentNo);
805     tmp->noOfRecords = ntohl(fragInfo.NoOfRecordsLow) +
806       (((Uint64)ntohl(fragInfo.NoOfRecordsHigh)) << 32);
807     tmp->filePosLow = ntohl(fragInfo.FilePosLow);
808     tmp->filePosHigh = ntohl(fragInfo.FilePosHigh);
809     tmp->sliceSkip = false; /* Init, set later */
810 
811     table->m_fragmentInfo.push_back(tmp);
812     table->m_noOfRecords += tmp->noOfRecords;
813   }
814   return true;
815 }
816 
TableS(Uint32 version,NdbTableImpl * tableImpl)817 TableS::TableS(Uint32 version, NdbTableImpl* tableImpl)
818   : m_dictTable(tableImpl)
819 {
820   m_noOfNullable = m_nullBitmaskSize = 0;
821   m_auto_val_attrib = 0;
822   m_max_auto_val= 0;
823   m_noOfRecords= 0;
824   backupVersion = version;
825   m_isSysTable = false;
826   m_isSYSTAB_0 = false;
827   m_broken = false;
828   m_main_table = NULL;
829   m_main_column_id = ~(Uint32)0;
830   m_has_blobs = false;
831 
832   for (int i = 0; i < tableImpl->getNoOfColumns(); i++)
833     createAttr(tableImpl->getColumn(i));
834 
835   m_staging = false;
836   m_stagingTable = NULL;
837   m_stagingFlags = 0;
838 
839   m_pk_extended = false;
840   m_pk_index = NULL;
841 }
842 
~TableS()843 TableS::~TableS()
844 {
845   for (Uint32 i= 0; i < allAttributesDesc.size(); i++)
846   {
847     if (allAttributesDesc[i]->parameter)
848       free(allAttributesDesc[i]->parameter);
849     delete allAttributesDesc[i];
850   }
851   delete m_stagingTable;
852   delete m_dictTable;
853 }
854 
855 
856 // Parse dictTabInfo buffer and pushback to to vector storage
857 bool
parseTableDescriptor(const Uint32 * data,Uint32 len)858 RestoreMetaData::parseTableDescriptor(const Uint32 * data, Uint32 len)
859 {
860   NdbTableImpl* tableImpl = 0;
861   int ret = NdbDictInterface::parseTableInfo
862     (&tableImpl, data, len, false,
863      ndbd_drop6(m_fileHeader.NdbVersion) ? MAKE_VERSION(5,1,2) :
864      m_fileHeader.NdbVersion);
865 
866   if (ret != 0) {
867     ndberror_struct err_struct;
868     err_struct.code = ret;
869     ndberror_update(&err_struct);
870 
871     restoreLogger.log_error("parseTableInfo failed with error %u \"%s\"",
872         err_struct.code, err_struct.message);
873 
874     restoreLogger.log_error("Check version of backup and schema contained in backup.");
875     return false;
876   }
877   if(tableImpl == 0)
878     return false;
879 
880   restoreLogger.log_debug("parseTableInfo %s done", tableImpl->getName());
881   TableS * table = new TableS(m_fileHeader.NdbVersion, tableImpl);
882   if(table == NULL) {
883     return false;
884   }
885 
886   restoreLogger.log_debug("Parsed table id %u\nParsed table #attr %u\n"
887         "Parsed table schema version not used",
888         table->getTableId(),
889         table->getNoOfAttributes());
890 
891   restoreLogger.log_debug("Pushing table %s\n    with %u attributes",
892         table->getTableName(), table->getNoOfAttributes());
893 
894   allTables.push_back(table);
895 
896   return true;
897 }
898 
899 // Constructor
RestoreDataIterator(const RestoreMetaData & md,void (* _free_data_callback)(void *),void * ctx)900 RestoreDataIterator::RestoreDataIterator(const RestoreMetaData & md, void (* _free_data_callback)(void*), void *ctx)
901   : BackupFile(_free_data_callback, ctx), m_metaData(md),
902     m_current_table_has_transforms(false)
903 {
904   restoreLogger.log_debug("RestoreDataIterator constructor");
905   setDataFile(md, 0);
906 
907   alloc_extra_storage(8192);
908   m_row_max_extra_wordlen = 0;
909 }
910 
911 
912 bool
validateRestoreDataIterator()913 RestoreDataIterator::validateRestoreDataIterator()
914 {
915     if (!m_extra_storage_ptr)
916     {
917         restoreLogger.log_error("m_extra_storage_ptr is NULL");
918         return false;
919     }
920     return true;
921 }
922 
923 
~RestoreDataIterator()924 RestoreDataIterator::~RestoreDataIterator()
925 {
926   free_extra_storage();
927 }
928 
929 void
calc_row_extra_storage_words(const TableS * tableSpec)930 RestoreDataIterator::calc_row_extra_storage_words(const TableS* tableSpec)
931 {
932   const NdbDictionary::Table* tab = tableSpec->m_dictTable;
933   Uint32 bitmap_words = 0;
934   Uint32 transform_words = 0;
935   for (Uint32 i = 0; i<(Uint32)tab->getNoOfColumns(); i++)
936   {
937     /* Space for bitmap-copy out from PACKED format */
938     if (tab->getColumn(i)->getType() == NdbDictionary::Column::Bit)
939     {
940       bitmap_words += (tab->getColumn(i)->getLength() + 31) >> 5;
941     }
942     /* Space for output from this column transform */
943     const AttributeDesc* attr_desc = tableSpec->getAttributeDesc(i);
944     if (attr_desc->transform != NULL)
945     {
946       transform_words += attr_desc->getSizeInWords();
947     }
948   }
949 
950   m_current_table_has_transforms = (transform_words > 0);
951 
952   m_row_max_extra_wordlen = bitmap_words + transform_words;
953 }
954 
955 void
reset_extra_storage()956 RestoreDataIterator::reset_extra_storage()
957 {
958   m_extra_storage_curr_ptr = m_extra_storage_ptr;
959 }
960 
961 void
alloc_extra_storage(Uint32 words)962 RestoreDataIterator::alloc_extra_storage(Uint32 words)
963 {
964   m_extra_storage_wordlen = words;
965   m_extra_storage_ptr = (Uint32*)malloc(4 * words);
966   m_extra_storage_curr_ptr = m_extra_storage_ptr;
967 }
968 
969 void
free_extra_storage()970 RestoreDataIterator::free_extra_storage()
971 {
972   if (m_extra_storage_ptr)
973     free(m_extra_storage_ptr);
974   m_extra_storage_ptr = 0;
975   m_extra_storage_curr_ptr = 0;
976 }
977 
978 Uint32
get_free_extra_storage() const979 RestoreDataIterator::get_free_extra_storage() const
980 {
981 
982   return Uint32((m_extra_storage_ptr + m_extra_storage_wordlen) -
983     m_extra_storage_curr_ptr);
984 }
985 
986 void
check_extra_storage()987 RestoreDataIterator::check_extra_storage()
988 {
989   assert(m_row_max_extra_wordlen <= m_extra_storage_wordlen);
990   if (m_row_max_extra_wordlen >= get_free_extra_storage())
991   {
992     /**
993      * No more space available to buffer rows, flush
994      * what is outstanding, then reset buffers and
995      * continue.
996      */
997     flush_and_reset_buffers();
998     assert(get_free_extra_storage() > m_row_max_extra_wordlen);
999     assert(m_extra_storage_ptr == m_extra_storage_curr_ptr);
1000 
1001     /**
1002      * We do not want to break up batching due to a lack of
1003      * extra buffer storage, but that is what has happened
1004      * here.
1005      * So to avoid this in future we will take this chance
1006      * to double the extra storage size, so that batching
1007      * boundaries are eventually controlled by the file
1008      * buffering only.
1009      */
1010     const Uint32 newWords = m_extra_storage_wordlen * 2;
1011     free_extra_storage();
1012     alloc_extra_storage(newWords);
1013   }
1014 }
1015 
1016 
1017 Uint32*
get_extra_storage(Uint32 len)1018 RestoreDataIterator::get_extra_storage(Uint32 len)
1019 {
1020   Uint32 * currptr = m_extra_storage_curr_ptr;
1021   Uint32 * nextptr = currptr + len;
1022   Uint32 * endptr = m_extra_storage_ptr + m_extra_storage_wordlen;
1023 
1024   if (nextptr <= endptr)
1025   {
1026     m_extra_storage_curr_ptr = nextptr;
1027     return currptr;
1028   }
1029 
1030   abort();
1031   return 0;
1032 }
1033 
operator =(const TupleS & tuple)1034 TupleS & TupleS::operator=(const TupleS& tuple)
1035 {
1036   prepareRecord(*tuple.m_currentTable);
1037 
1038   if (allAttrData)
1039     memcpy(allAttrData, tuple.allAttrData, getNoOfAttributes()*sizeof(AttributeData));
1040 
1041   return *this;
1042 }
getNoOfAttributes() const1043 int TupleS::getNoOfAttributes() const {
1044   if (m_currentTable == 0)
1045     return 0;
1046   return m_currentTable->getNoOfAttributes();
1047 }
1048 
getTable() const1049 TableS * TupleS::getTable() const {
1050   return m_currentTable;
1051 }
1052 
getDesc(int i) const1053 AttributeDesc * TupleS::getDesc(int i) const {
1054   return m_currentTable->allAttributesDesc[i];
1055 }
1056 
getData(int i) const1057 AttributeData * TupleS::getData(int i) const{
1058   return &(allAttrData[i]);
1059 }
1060 
1061 bool
prepareRecord(TableS & tab)1062 TupleS::prepareRecord(TableS & tab){
1063   if (allAttrData) {
1064     if (getNoOfAttributes() == tab.getNoOfAttributes())
1065     {
1066       m_currentTable = &tab;
1067       return true;
1068     }
1069     delete [] allAttrData;
1070     m_currentTable= 0;
1071   }
1072 
1073   allAttrData = new AttributeData[tab.getNoOfAttributes()];
1074   if (allAttrData == 0)
1075     return false;
1076 
1077   m_currentTable = &tab;
1078 
1079   return true;
1080 }
1081 
1082 static
1083 inline
1084 Uint8*
pad(Uint8 * src,Uint32 align,Uint32 bitPos)1085 pad(Uint8* src, Uint32 align, Uint32 bitPos)
1086 {
1087   UintPtr ptr = UintPtr(src);
1088   switch(align){
1089   case DictTabInfo::aBit:
1090   case DictTabInfo::a32Bit:
1091   case DictTabInfo::a64Bit:
1092   case DictTabInfo::a128Bit:
1093     return (Uint8*)(((ptr + 3) & ~(UintPtr)3) + 4 * ((bitPos + 31) >> 5));
1094 charpad:
1095   case DictTabInfo::an8Bit:
1096   case DictTabInfo::a16Bit:
1097     return src + 4 * ((bitPos + 31) >> 5);
1098   default:
1099 #ifdef VM_TRACE
1100     abort();
1101 #endif
1102     goto charpad;
1103   }
1104 }
1105 
1106 bool
applyColumnTransform(const NdbDictionary::Column * col,const AttributeDesc * attr_desc,AttributeData * attr_data,void * dst_buf)1107 applyColumnTransform(const NdbDictionary::Column* col,
1108                      const AttributeDesc* attr_desc,
1109                      AttributeData* attr_data,
1110                      void* dst_buf)
1111 {
1112   assert(attr_desc->transform != NULL);
1113 
1114   void* src_ptr = (attr_data->null? NULL : attr_data->void_value);
1115   void* dst_ptr = dst_buf;
1116 
1117   if (!attr_desc->transform->apply(col,
1118                                    src_ptr,
1119                                    &dst_ptr))
1120   {
1121     return false;
1122   }
1123 
1124   if (dst_ptr == NULL)
1125   {
1126     assert(col->getNullable());
1127     attr_data->null = true;
1128     attr_data->size = 0;
1129     attr_data->void_value = NULL;
1130   }
1131   else
1132   {
1133     const uchar* dst_char = (const uchar*) dst_ptr;
1134     attr_data->null = false;
1135     attr_data->void_value = dst_ptr;
1136     switch(col->getArrayType())
1137     {
1138     case NDB_ARRAYTYPE_SHORT_VAR:
1139       attr_data->size = 1 + size_t(dst_char[0]);
1140       break;
1141     case NDB_ARRAYTYPE_MEDIUM_VAR:
1142       attr_data->size = 2 + size_t(dst_char[0])
1143         + (256 * size_t(dst_char[1]));
1144       break;
1145     default:
1146       /* No change */
1147       break;
1148     }
1149   }
1150 
1151   /* Check size is within 'word length' of column type */
1152   assert(attr_data->size <=
1153          4 * ((((Uint32)col->getSizeInBytes()) +3)/4));
1154 
1155   return true;
1156 }
1157 
1158 
1159 const TupleS *
getNextTuple(int & res,const bool skipFragment)1160 RestoreDataIterator::getNextTuple(int  & res, const bool skipFragment)
1161 {
1162   /* Check that we have space to return another tuple */
1163   check_extra_storage();
1164 
1165   while (true)
1166   {
1167     Uint32  dataLength = 0;
1168     // Read record length
1169     if (buffer_read(&dataLength, sizeof(dataLength), 1) != 1){
1170       restoreLogger.log_error("getNextTuple:Error reading length of data part");
1171       res = -1;
1172       return NULL;
1173     } // if
1174 
1175     // Convert length from network byte order
1176     dataLength = ntohl(dataLength);
1177     const Uint32 dataLenBytes = 4 * dataLength;
1178 
1179     if (dataLength == 0) {
1180       // Zero length for last tuple
1181       // End of this data fragment
1182       restoreLogger.log_debug("End of fragment");
1183       res = 0;
1184       return NULL;
1185     } // if
1186 
1187     // Read tuple data
1188     void *_buf_ptr;
1189     if (buffer_get_ptr(&_buf_ptr, 1, dataLenBytes) != dataLenBytes) {
1190       restoreLogger.log_error("getNextTuple:Read error: ");
1191       res = -1;
1192       return NULL;
1193     }
1194 
1195     m_count++;
1196 
1197     if (skipFragment)
1198     {
1199       /**
1200        * Skip unpacking work, we just want to read all the tuples up
1201        * to the end of this fragment
1202        */
1203       continue;
1204     }
1205 
1206     Uint32 *buf_ptr = (Uint32*)_buf_ptr;
1207     if (m_currentTable->backupVersion >= NDBD_RAW_LCP)
1208     {
1209       res = readTupleData_packed(buf_ptr, dataLength);
1210     }
1211     else
1212     {
1213       res = readTupleData_old(buf_ptr, dataLength);
1214     }
1215 
1216     if (res)
1217     {
1218       return NULL;
1219     }
1220 
1221     /* Apply column transforms if the table has any defined */
1222     if (m_current_table_has_transforms)
1223     {
1224       for (int i=0; i < m_currentTable->getNoOfAttributes(); i++)
1225       {
1226         const AttributeDesc* attr_desc = m_currentTable->getAttributeDesc(i);
1227         if (attr_desc->transform == NULL)
1228         {
1229           continue;
1230         }
1231         const NdbDictionary::Column* col = m_currentTable->m_dictTable->getColumn(i);
1232         void* dst_buf = get_extra_storage(attr_desc->getSizeInWords());
1233         assert(dst_buf != NULL);
1234 
1235         if (!applyColumnTransform(col,
1236                                   attr_desc,
1237                                   m_tuple.getData(i),
1238                                   dst_buf))
1239         {
1240           res = -1;
1241           return NULL;
1242         }
1243       }
1244     }
1245 
1246     res = 0;
1247     return &m_tuple;
1248   }
1249 } // RestoreDataIterator::getNextTuple
1250 
1251 TableS *
getCurrentTable()1252 RestoreDataIterator::getCurrentTable()
1253 {
1254   return m_currentTable;
1255 }
1256 
1257 int
readTupleData_packed(Uint32 * buf_ptr,Uint32 dataLength)1258 RestoreDataIterator::readTupleData_packed(Uint32 *buf_ptr,
1259                                           Uint32 dataLength)
1260 {
1261   Uint32 * ptr = buf_ptr;
1262   /**
1263    * Unpack READ_PACKED header
1264    */
1265   Uint32 rp = * ptr;
1266   if(unlikely(!m_hostByteOrder))
1267     rp = Twiddle32(rp);
1268 
1269   AttributeHeader ah(rp);
1270   assert(ah.getAttributeId() == AttributeHeader::READ_PACKED);
1271   Uint32 bmlen = ah.getByteSize();
1272   assert((bmlen & 3) == 0);
1273   Uint32 bmlen32 = bmlen / 4;
1274 
1275   /**
1276    * Twiddle READ_BACKED header
1277    */
1278   if (!m_hostByteOrder)
1279   {
1280     for (Uint32 i = 0; i < 1 + bmlen32; i++)
1281     {
1282       ptr[i] = Twiddle32(ptr[i]);
1283     }
1284   }
1285 
1286   const NdbDictionary::Table* tab = m_currentTable->m_dictTable;
1287 
1288   // All columns should be present...
1289   assert(((tab->getNoOfColumns() + 31) >> 5) <= (int)bmlen32);
1290 
1291   /**
1292    * Iterate through attributes...
1293    */
1294   const Uint32 * bmptr = ptr + 1;
1295   Uint8* src = (Uint8*)(bmptr + bmlen32);
1296   Uint32 bmpos = 0;
1297   Uint32 bitPos = 0;
1298   for (Uint32 i = 0; i < (Uint32)tab->getNoOfColumns(); i++, bmpos++)
1299   {
1300     // All columns should be present
1301     assert(BitmaskImpl::get(bmlen32, bmptr, bmpos));
1302     const NdbColumnImpl & col = NdbColumnImpl::getImpl(* tab->getColumn(i));
1303     AttributeData * attr_data = m_tuple.getData(i);
1304     const AttributeDesc * attr_desc = m_tuple.getDesc(i);
1305     if (col.getNullable())
1306     {
1307       bmpos++;
1308       if (BitmaskImpl::get(bmlen32, bmptr, bmpos))
1309       {
1310         attr_data->null = true;
1311         attr_data->void_value = NULL;
1312         continue;
1313       }
1314     }
1315 
1316     attr_data->null = false;
1317 
1318     /**
1319      * Handle padding
1320      */
1321     Uint32 align = col.m_orgAttrSize;
1322     Uint32 attrSize = col.m_attrSize;
1323     Uint32 array = col.m_arraySize;
1324     Uint32 len = col.m_length;
1325     Uint32 sz = attrSize * array;
1326     Uint32 arrayType = col.m_arrayType;
1327 
1328     switch(align){
1329     case DictTabInfo::aBit:{ // Bit
1330       src = pad(src, 0, 0);
1331       Uint32* src32 = (Uint32*)src;
1332 
1333       Uint32 len32 = (len + 31) >> 5;
1334       Uint32* tmp = get_extra_storage(len32);
1335       attr_data->null = false;
1336       attr_data->void_value = tmp;
1337       attr_data->size = 4*len32;
1338 
1339       if (m_hostByteOrder)
1340       {
1341         BitmaskImpl::getField(1 + len32, src32, bitPos, len, tmp);
1342       }
1343       else
1344       {
1345         Uint32 ii;
1346         for (ii = 0; ii< (1 + len32); ii++)
1347           src32[ii] = Twiddle32(src32[ii]);
1348         BitmaskImpl::getField(1 + len32, (Uint32*)src, bitPos, len, tmp);
1349         for (ii = 0; ii< (1 + len32); ii++)
1350           src32[ii] = Twiddle32(src32[ii]);
1351       }
1352 
1353       src += 4 * ((bitPos + len) >> 5);
1354       bitPos = (bitPos + len) & 31;
1355       goto next;
1356     }
1357     default:
1358       src = pad(src, align, bitPos);
1359     }
1360     switch(arrayType){
1361     case NDB_ARRAYTYPE_FIXED:
1362       break;
1363     case NDB_ARRAYTYPE_SHORT_VAR:
1364       sz = 1 + src[0];
1365       break;
1366     case NDB_ARRAYTYPE_MEDIUM_VAR:
1367       sz = 2 + src[0] + 256 * src[1];
1368       break;
1369     default:
1370       abort();
1371     }
1372 
1373     attr_data->void_value = src;
1374     attr_data->size = sz;
1375 
1376     if(!Twiddle(attr_desc, attr_data))
1377     {
1378       return -1;
1379     }
1380 
1381     /**
1382      * Next
1383      */
1384     bitPos = 0;
1385     src += sz;
1386 next:
1387     (void)1;
1388   }
1389   return 0;
1390 }
1391 
1392 int
readTupleData_old(Uint32 * buf_ptr,Uint32 dataLength)1393 RestoreDataIterator::readTupleData_old(Uint32 *buf_ptr,
1394                                        Uint32 dataLength)
1395 {
1396   Uint32 * ptr = buf_ptr;
1397   ptr += m_currentTable->m_nullBitmaskSize;
1398   Uint32 i;
1399   for(i= 0; i < m_currentTable->m_fixedKeys.size(); i++){
1400     assert(ptr < buf_ptr + dataLength);
1401 
1402     const Uint32 attrId = m_currentTable->m_fixedKeys[i]->attrId;
1403 
1404     AttributeData * attr_data = m_tuple.getData(attrId);
1405     const AttributeDesc * attr_desc = m_tuple.getDesc(attrId);
1406 
1407     const Uint32 sz = attr_desc->getSizeInWords();
1408 
1409     attr_data->null = false;
1410     attr_data->void_value = ptr;
1411     attr_data->size = 4*sz;
1412 
1413     if(!Twiddle(attr_desc, attr_data))
1414     {
1415       return -1;
1416     }
1417     ptr += sz;
1418   }
1419 
1420   for(i = 0; i < m_currentTable->m_fixedAttribs.size(); i++){
1421     assert(ptr < buf_ptr + dataLength);
1422 
1423     const Uint32 attrId = m_currentTable->m_fixedAttribs[i]->attrId;
1424 
1425     AttributeData * attr_data = m_tuple.getData(attrId);
1426     const AttributeDesc * attr_desc = m_tuple.getDesc(attrId);
1427 
1428     const Uint32 sz = attr_desc->getSizeInWords();
1429 
1430     attr_data->null = false;
1431     attr_data->void_value = ptr;
1432     attr_data->size = 4*sz;
1433 
1434     if(!Twiddle(attr_desc, attr_data))
1435     {
1436       return -1;
1437     }
1438 
1439     ptr += sz;
1440   }
1441 
1442   // init to NULL
1443   for(i = 0; i < m_currentTable->m_variableAttribs.size(); i++){
1444     const Uint32 attrId = m_currentTable->m_variableAttribs[i]->attrId;
1445 
1446     AttributeData * attr_data = m_tuple.getData(attrId);
1447 
1448     attr_data->null = true;
1449     attr_data->void_value = NULL;
1450   }
1451 
1452   int res;
1453   if (!ndbd_drop6(m_currentTable->backupVersion))
1454   {
1455     if ((res = readVarData(buf_ptr, ptr, dataLength)))
1456       return res;
1457   }
1458   else
1459   {
1460     if ((res = readVarData_drop6(buf_ptr, ptr, dataLength)))
1461       return res;
1462   }
1463 
1464   return 0;
1465 }
1466 
1467 int
readVarData(Uint32 * buf_ptr,Uint32 * ptr,Uint32 dataLength)1468 RestoreDataIterator::readVarData(Uint32 *buf_ptr, Uint32 *ptr,
1469                                   Uint32 dataLength)
1470 {
1471   while (ptr + 2 < buf_ptr + dataLength)
1472   {
1473     typedef BackupFormat::DataFile::VariableData VarData;
1474     VarData * data = (VarData *)ptr;
1475     Uint32 sz = ntohl(data->Sz);
1476     Uint32 attrId = ntohl(data->Id); // column_no
1477 
1478     AttributeData * attr_data = m_tuple.getData(attrId);
1479     const AttributeDesc * attr_desc = m_tuple.getDesc(attrId);
1480 
1481     // just a reminder - remove when backwards compat implemented
1482     if (m_currentTable->backupVersion < MAKE_VERSION(5,1,3) &&
1483         attr_desc->m_column->getNullable())
1484     {
1485       const Uint32 ind = attr_desc->m_nullBitIndex;
1486       if(BitmaskImpl::get(m_currentTable->m_nullBitmaskSize,
1487                           buf_ptr,ind))
1488       {
1489         attr_data->null = true;
1490         attr_data->void_value = NULL;
1491         continue;
1492       }
1493     }
1494 
1495     if (m_currentTable->backupVersion < MAKE_VERSION(5,1,3))
1496     {
1497       sz *= 4;
1498     }
1499 
1500     attr_data->null = false;
1501     attr_data->void_value = &data->Data[0];
1502     attr_data->size = sz;
1503 
1504     //convert the length of blob(v1) and text(v1)
1505     if(!Twiddle(attr_desc, attr_data))
1506     {
1507       return -1;
1508     }
1509 
1510     ptr += ((sz + 3) >> 2) + 2;
1511   }
1512 
1513   assert(ptr == buf_ptr + dataLength);
1514 
1515   return 0;
1516 }
1517 
1518 
1519 int
readVarData_drop6(Uint32 * buf_ptr,Uint32 * ptr,Uint32 dataLength)1520 RestoreDataIterator::readVarData_drop6(Uint32 *buf_ptr, Uint32 *ptr,
1521                                        Uint32 dataLength)
1522 {
1523   Uint32 i;
1524   for (i = 0; i < m_currentTable->m_variableAttribs.size(); i++)
1525   {
1526     const Uint32 attrId = m_currentTable->m_variableAttribs[i]->attrId;
1527 
1528     AttributeData * attr_data = m_tuple.getData(attrId);
1529     const AttributeDesc * attr_desc = m_tuple.getDesc(attrId);
1530 
1531     if(attr_desc->m_column->getNullable())
1532     {
1533       const Uint32 ind = attr_desc->m_nullBitIndex;
1534       if(BitmaskImpl::get(m_currentTable->m_nullBitmaskSize,
1535                           buf_ptr,ind))
1536       {
1537         attr_data->null = true;
1538         attr_data->void_value = NULL;
1539         continue;
1540       }
1541     }
1542 
1543     assert(ptr < buf_ptr + dataLength);
1544 
1545     typedef BackupFormat::DataFile::VariableData VarData;
1546     VarData * data = (VarData *)ptr;
1547     Uint32 sz = ntohl(data->Sz);
1548     assert(ntohl(data->Id) == attrId);
1549 
1550     attr_data->null = false;
1551     attr_data->void_value = &data->Data[0];
1552 
1553     if (!Twiddle(attr_desc, attr_data))
1554     {
1555       return -1;
1556     }
1557     ptr += (sz + 2);
1558   }
1559   assert(ptr == buf_ptr + dataLength);
1560   return 0;
1561 }
1562 
BackupFile(void (* _free_data_callback)(void *),void * ctx)1563 BackupFile::BackupFile(void (* _free_data_callback)(void*), void *ctx)
1564   : free_data_callback(_free_data_callback), m_ctx(ctx)
1565 {
1566   memset(&m_file,0,sizeof(m_file));
1567   m_path[0] = 0;
1568   m_fileName[0] = 0;
1569 
1570   m_buffer_sz = BUFFER_SIZE;
1571   m_buffer = malloc(m_buffer_sz);
1572   m_buffer_ptr = m_buffer;
1573   m_buffer_data_left = 0;
1574 
1575   m_file_size = 0;
1576   m_file_pos = 0;
1577   m_is_undolog = false;
1578 
1579   m_part_count = 1;
1580 #ifdef ERROR_INSERT
1581   m_error_insert = 0;
1582 #endif
1583 }
1584 
1585 bool
validateBackupFile()1586 BackupFile::validateBackupFile()
1587 {
1588     if (!m_buffer)
1589     {
1590         restoreLogger.log_error("m_buffer is NULL");
1591         return false;
1592     }
1593     return true;
1594 }
1595 
~BackupFile()1596 BackupFile::~BackupFile()
1597 {
1598   (void)ndbzclose(&m_file);
1599 
1600   if(m_buffer != 0)
1601     free(m_buffer);
1602 }
1603 
1604 bool
openFile()1605 BackupFile::openFile(){
1606   (void)ndbzclose(&m_file);
1607   m_file_size = 0;
1608   m_file_pos = 0;
1609 
1610   info.setLevel(254);
1611   restoreLogger.log_info("Opening file '%s'", m_fileName);
1612   int r= ndbzopen(&m_file, m_fileName, O_RDONLY);
1613 
1614   if(r != 1)
1615     return false;
1616 
1617   size_t size;
1618   if (ndbz_file_size(&m_file, &size) == 0)
1619   {
1620     m_file_size = (Uint64)size;
1621     restoreLogger.log_info("File size %llu bytes", m_file_size);
1622   }
1623   else
1624   {
1625     restoreLogger.log_info("Progress reporting degraded output since fstat failed,"
1626          "errno: %u", errno);
1627     m_file_size = 0;
1628   }
1629 
1630   return true;
1631 }
1632 
buffer_get_ptr_ahead(void ** p_buf_ptr,Uint32 size,Uint32 nmemb)1633 Uint32 BackupFile::buffer_get_ptr_ahead(void **p_buf_ptr, Uint32 size, Uint32 nmemb)
1634 {
1635   Uint32 sz = size*nmemb;
1636   if (sz > m_buffer_data_left) {
1637 
1638     flush_and_reset_buffers();
1639 
1640     if (m_is_undolog)
1641     {
1642       /* move the left data to the end of buffer
1643        */
1644       size_t r = 0;
1645       int error;
1646       /* move the left data to the end of buffer
1647        * m_buffer_ptr point the end of the left data. buffer_data_start point the start of left data
1648        * m_buffer_data_left is the length of left data.
1649        */
1650       Uint64 file_left_entry_data = 0;
1651       Uint32 buffer_free_space = m_buffer_sz - m_buffer_data_left;
1652       void * buffer_end = (char *)m_buffer + m_buffer_sz;
1653       void * buffer_data_start = (char *)m_buffer_ptr - m_buffer_data_left;
1654 
1655       memmove((char *)buffer_end - m_buffer_data_left, buffer_data_start, m_buffer_data_left);
1656       buffer_data_start = (char *)buffer_end - m_buffer_data_left;
1657       /*
1658        * For undo log file we should read log entris backwards from log file.
1659        *   That mean the first entries should start at sizeof(m_fileHeader).
1660        *   The end of the last entries should be the end of log file(EOF-1).
1661        * If ther are entries left in log file to read.
1662        *   m_file_pos should bigger than sizeof(m_fileHeader).
1663        * If the length of left log entries less than the residual length of buffer,
1664        *   we just need to read all the left entries from log file into the buffer.
1665        *   and all the left entries in log file should been read into buffer. Or
1666        * If the length of left entries is bigger than the residual length of buffer,
1667        *   we should fill the buffer because the current buffer can't contain
1668            all the left log entries, we should read more times.
1669        *
1670        */
1671       if (m_file_pos > sizeof(m_fileHeader))
1672       {
1673         /*
1674          * We read(consume) data from the end of the buffer.
1675          * If the left data is not enough for next read in buffer,
1676          *   we move the residual data to the end of buffer.
1677          *   Then we will fill the start of buffer with new data from log file.
1678          * eg. If the buffer length is 10. "+" denotes useless content.
1679          *                          top        end
1680          *   Bytes in file        abcdefgh0123456789
1681          *   Byte in buffer       0123456789             --after first read
1682          *   Consume datas...     (6789) (2345)
1683          *   Bytes in buffer      01++++++++             --after several consumes
1684          *   Move data to end     ++++++++01
1685          *   Bytes in buffer      abcdefgh01             --after second read
1686          */
1687 	file_left_entry_data = m_file_pos - sizeof(m_fileHeader);
1688         if (file_left_entry_data <= buffer_free_space)
1689         {
1690           /* All remaining data fits in space available in buffer.
1691 	   * Read data into buffer before existing data.
1692 	   */
1693           // Move to the start of data to be read
1694           ndbzseek(&m_file, sizeof(m_fileHeader), SEEK_SET);
1695           r = ndbzread(&m_file,
1696                        (char *)buffer_data_start - file_left_entry_data,
1697                        Uint32(file_left_entry_data),
1698                        &error);
1699           //move back
1700           ndbzseek(&m_file, sizeof(m_fileHeader), SEEK_SET);
1701         }
1702         else
1703         {
1704 	  // Fill remaing space at start of buffer with data from file.
1705           ndbzseek(&m_file, m_file_pos-buffer_free_space, SEEK_SET);
1706           r = ndbzread(&m_file, ((char *)m_buffer), buffer_free_space, &error);
1707           ndbzseek(&m_file, m_file_pos-buffer_free_space, SEEK_SET);
1708         }
1709       }
1710       m_file_pos -= r;
1711       m_buffer_data_left += (Uint32)r;
1712       //move to the end of buffer
1713       m_buffer_ptr = buffer_end;
1714     }
1715     else
1716     {
1717       memmove(m_buffer, m_buffer_ptr, m_buffer_data_left);
1718       int error;
1719       Uint32 r = ndbzread(&m_file,
1720                           ((char *)m_buffer) + m_buffer_data_left,
1721                           m_buffer_sz - m_buffer_data_left, &error);
1722       m_file_pos += r;
1723       m_buffer_data_left += r;
1724       m_buffer_ptr = m_buffer;
1725     }
1726 
1727     if (sz > m_buffer_data_left)
1728       sz = size * (m_buffer_data_left / size);
1729   }
1730 
1731   /*
1732    * For undolog, the m_buffer_ptr points to the end of the left data.
1733    * After we get data from the end of buffer, the data-end move forward.
1734    *   So we should move m_buffer_ptr to the right place.
1735    */
1736   if(m_is_undolog)
1737     *p_buf_ptr = (char *)m_buffer_ptr - sz;
1738   else
1739     *p_buf_ptr = m_buffer_ptr;
1740 
1741   return sz/size;
1742 }
buffer_get_ptr(void ** p_buf_ptr,Uint32 size,Uint32 nmemb)1743 Uint32 BackupFile::buffer_get_ptr(void **p_buf_ptr, Uint32 size, Uint32 nmemb)
1744 {
1745   Uint32 r = buffer_get_ptr_ahead(p_buf_ptr, size, nmemb);
1746 
1747   if(m_is_undolog)
1748   {
1749     /* we read from end of buffer to start of buffer.
1750      * m_buffer_ptr keep at the end of real data in buffer.
1751      */
1752     m_buffer_ptr = ((char*)m_buffer_ptr)-(r*size);
1753     m_buffer_data_left -= (r*size);
1754   }
1755   else
1756   {
1757     m_buffer_ptr = ((char*)m_buffer_ptr)+(r*size);
1758     m_buffer_data_left -= (r*size);
1759   }
1760 
1761   return r;
1762 }
1763 
buffer_read_ahead(void * ptr,Uint32 size,Uint32 nmemb)1764 Uint32 BackupFile::buffer_read_ahead(void *ptr, Uint32 size, Uint32 nmemb)
1765 {
1766   void *buf_ptr;
1767   Uint32 r = buffer_get_ptr_ahead(&buf_ptr, size, nmemb);
1768   memcpy(ptr, buf_ptr, r*size);
1769 
1770   return r;
1771 }
1772 
buffer_read(void * ptr,Uint32 size,Uint32 nmemb)1773 Uint32 BackupFile::buffer_read(void *ptr, Uint32 size, Uint32 nmemb)
1774 {
1775   void *buf_ptr;
1776   Uint32 r = buffer_get_ptr(&buf_ptr, size, nmemb);
1777   memcpy(ptr, buf_ptr, r*size);
1778 
1779   return r;
1780 }
1781 
1782 void
setCtlFile(Uint32 nodeId,Uint32 backupId,const char * path)1783 BackupFile::setCtlFile(Uint32 nodeId, Uint32 backupId, const char * path){
1784   m_nodeId = nodeId;
1785   m_expectedFileHeader.BackupId = backupId;
1786   m_expectedFileHeader.FileType = BackupFormat::CTL_FILE;
1787 
1788   char name[PATH_MAX]; const Uint32 sz = sizeof(name);
1789   BaseString::snprintf(name, sz, "BACKUP-%u.%d.ctl", backupId, nodeId);
1790 
1791   if (m_part_count > 1)
1792   {
1793     char multiset_name[PATH_MAX];
1794     BaseString::snprintf(multiset_name, sizeof(multiset_name),
1795                     "BACKUP-%u-PART-%u-OF-%u%s%s", backupId, m_part_id, m_part_count, DIR_SEPARATOR, name);
1796     setName(path, multiset_name);
1797   }
1798   else
1799   {
1800     setName(path, name);
1801   }
1802 }
1803 
1804 void
setDataFile(const BackupFile & bf,Uint32 no)1805 BackupFile::setDataFile(const BackupFile & bf, Uint32 no){
1806   m_nodeId = bf.m_nodeId;
1807   m_expectedFileHeader = bf.m_fileHeader;
1808   m_expectedFileHeader.FileType = BackupFormat::DATA_FILE;
1809 
1810   char name[PATH_MAX]; const Uint32 sz = sizeof(name);
1811   Uint32 backupId = m_expectedFileHeader.BackupId;
1812   if (bf.m_part_count > 1)
1813   {
1814     BaseString::snprintf(name, sz, "BACKUP-%u-PART-%d-OF-%d%sBACKUP-%u-%u.%d.Data",
1815           backupId, bf.m_part_id, bf.m_part_count, DIR_SEPARATOR, backupId, no, m_nodeId);
1816   }
1817   else
1818   {
1819     BaseString::snprintf(name, sz, "BACKUP-%u-%u.%d.Data",
1820           backupId, no, m_nodeId);
1821   }
1822   setName(bf.m_path, name);
1823 }
1824 
1825 void
setLogFile(const BackupFile & bf,Uint32 no)1826 BackupFile::setLogFile(const BackupFile & bf, Uint32 no){
1827   m_nodeId = bf.m_nodeId;
1828   m_expectedFileHeader = bf.m_fileHeader;
1829   m_expectedFileHeader.FileType = BackupFormat::LOG_FILE;
1830 
1831   char name[PATH_MAX]; const Uint32 sz = sizeof(name);
1832   Uint32 backupId = m_expectedFileHeader.BackupId;
1833   if (bf.m_part_count > 1)
1834   {
1835     BaseString::snprintf(name, sz, "BACKUP-%u-PART-%d-OF-%d%sBACKUP-%u.%d.log",
1836           backupId, bf.m_part_id, bf.m_part_count, DIR_SEPARATOR, backupId, m_nodeId);
1837   }
1838   else
1839   {
1840     BaseString::snprintf(name, sz, "BACKUP-%u.%d.log",
1841           backupId, m_nodeId);
1842   }
1843   setName(bf.m_path, name);
1844 }
1845 
1846 void
setName(const char * p,const char * n)1847 BackupFile::setName(const char * p, const char * n){
1848   const Uint32 sz = sizeof(m_path);
1849   if(p != 0 && strlen(p) > 0){
1850     if(p[strlen(p)-1] == DIR_SEPARATOR[0]){
1851       BaseString::snprintf(m_path, sz, "%s", p);
1852     } else {
1853       BaseString::snprintf(m_path, sz, "%s%s", p, DIR_SEPARATOR);
1854     }
1855   } else {
1856     m_path[0] = 0;
1857   }
1858 
1859   BaseString::snprintf(m_fileName, sizeof(m_fileName), "%s%s", m_path, n);
1860   restoreLogger.log_debug("Filename = %s", m_fileName);
1861 }
1862 
1863 bool
readHeader()1864 BackupFile::readHeader(){
1865   if(!openFile()){
1866     return false;
1867   }
1868 
1869   Uint32 oldsz = sizeof(BackupFormat::FileHeader_pre_backup_version);
1870   if(buffer_read(&m_fileHeader, oldsz, 1) != 1){
1871     restoreLogger.log_error("readDataFileHeader: Error reading header");
1872     return false;
1873   }
1874 
1875   // Convert from network to host byte order for platform compatibility
1876   /*
1877     Due to some optimization going on when using gcc 4.2.3 we
1878     have to read 'backup_version' into tmp variable. If
1879     'm_fileHeader.BackupVersion' is used directly in the if statement
1880     below it will have the wrong value.
1881   */
1882   Uint32 backup_version = ntohl(m_fileHeader.BackupVersion);
1883   m_fileHeader.BackupVersion = backup_version;
1884   m_fileHeader.SectionType = ntohl(m_fileHeader.SectionType);
1885   m_fileHeader.SectionLength = ntohl(m_fileHeader.SectionLength);
1886   m_fileHeader.FileType = ntohl(m_fileHeader.FileType);
1887   m_fileHeader.BackupId = ntohl(m_fileHeader.BackupId);
1888   m_fileHeader.BackupKey_0 = ntohl(m_fileHeader.BackupKey_0);
1889   m_fileHeader.BackupKey_1 = ntohl(m_fileHeader.BackupKey_1);
1890 
1891   if (backup_version >= NDBD_RAW_LCP)
1892   {
1893     if (buffer_read(&m_fileHeader.NdbVersion,
1894                     sizeof(m_fileHeader) - oldsz, 1) != 1)
1895     {
1896       restoreLogger.log_error("readDataFileHeader: Error reading header");
1897       return false;
1898     }
1899 
1900     m_fileHeader.NdbVersion = ntohl(m_fileHeader.NdbVersion);
1901     m_fileHeader.MySQLVersion = ntohl(m_fileHeader.MySQLVersion);
1902   }
1903   else
1904   {
1905     m_fileHeader.NdbVersion = m_fileHeader.BackupVersion;
1906     m_fileHeader.MySQLVersion = 0;
1907   }
1908 
1909   restoreLogger.log_debug("FileHeader: %s %u %u %u %u %u %u %u %u",
1910                           m_fileHeader.Magic,
1911                           m_fileHeader.BackupVersion,
1912                           m_fileHeader.SectionType,
1913                           m_fileHeader.SectionLength,
1914                           m_fileHeader.FileType,
1915                           m_fileHeader.BackupId,
1916                           m_fileHeader.BackupKey_0,
1917                           m_fileHeader.BackupKey_1,
1918                           m_fileHeader.ByteOrder);
1919 
1920   restoreLogger.log_debug("ByteOrder is %u", m_fileHeader.ByteOrder);
1921   restoreLogger.log_debug("magicByteOrder is %u", magicByteOrder);
1922 
1923 
1924   if (m_fileHeader.FileType != m_expectedFileHeader.FileType &&
1925       !(m_expectedFileHeader.FileType == BackupFormat::LOG_FILE &&
1926       m_fileHeader.FileType == BackupFormat::UNDO_FILE)){
1927     // UNDO_FILE will do in case where we expect LOG_FILE
1928     abort();
1929   }
1930 
1931   if(m_fileHeader.FileType == BackupFormat::UNDO_FILE){
1932       m_is_undolog = true;
1933       /* move pointer to end of data part.
1934          move 4 bytes from the end of file
1935          because footer contain 4 bytes 0 at the end of file.
1936          we discard the remain data stored in m_buffer.
1937       */
1938       size_t size;
1939       if (ndbz_file_size(&m_file, &size) == 0)
1940         m_file_size = (Uint64)size;
1941       ndbzseek(&m_file, 4, SEEK_END);
1942       m_file_pos = m_file_size - 4;
1943       m_buffer_data_left = 0;
1944       m_buffer_ptr = m_buffer;
1945   }
1946 
1947   // Check for BackupFormat::FileHeader::ByteOrder if swapping is needed
1948   if (m_fileHeader.ByteOrder == magicByteOrder) {
1949     m_hostByteOrder = true;
1950   } else if (m_fileHeader.ByteOrder == swappedMagicByteOrder){
1951     m_hostByteOrder = false;
1952   } else {
1953     abort();
1954   }
1955 
1956   return true;
1957 } // BackupFile::readHeader
1958 
1959 bool
validateFooter()1960 BackupFile::validateFooter(){
1961   return true;
1962 }
1963 
1964 #ifdef ERROR_INSERT
error_insert(unsigned int code)1965 void BackupFile::error_insert(unsigned int code)
1966 {
1967   if(code == NDB_RESTORE_ERROR_INSERT_SMALL_BUFFER)
1968   {
1969     // Reduce size of buffer to test buffer overflow
1970     // handling. The buffer must still be large enough to
1971     // accommodate the file header.
1972     m_buffer_sz = 256;
1973     m_error_insert = NDB_RESTORE_ERROR_INSERT_SMALL_BUFFER;
1974   }
1975 }
1976 #endif
1977 
readFragmentHeader(int & ret,Uint32 * fragmentId)1978 bool RestoreDataIterator::readFragmentHeader(int & ret, Uint32 *fragmentId)
1979 {
1980   BackupFormat::DataFile::FragmentHeader Header;
1981 
1982   restoreLogger.log_debug("RestoreDataIterator::getNextFragment");
1983 
1984   while (1)
1985   {
1986     /* read first part of header */
1987     if (buffer_read(&Header, 8, 1) != 1)
1988     {
1989       ret = 0;
1990       return false;
1991     } // if
1992 
1993     /* skip if EMPTY_ENTRY */
1994     Header.SectionType  = ntohl(Header.SectionType);
1995     Header.SectionLength  = ntohl(Header.SectionLength);
1996     if (Header.SectionType == BackupFormat::EMPTY_ENTRY)
1997     {
1998       void *tmp;
1999       if (Header.SectionLength < 2)
2000       {
2001         restoreLogger.log_error("getFragmentFooter:Error reading fragment footer");
2002         return false;
2003       }
2004       if (Header.SectionLength > 2)
2005         buffer_get_ptr(&tmp, Header.SectionLength*4-8, 1);
2006       continue;
2007     }
2008     break;
2009   }
2010   /* read rest of header */
2011   if (buffer_read(((char*)&Header)+8, Header.SectionLength*4-8, 1) != 1)
2012   {
2013     ret = 0;
2014     return false;
2015   }
2016   Header.TableId  = ntohl(Header.TableId);
2017   Header.FragmentNo  = ntohl(Header.FragmentNo);
2018   Header.ChecksumType  = ntohl(Header.ChecksumType);
2019 
2020   restoreLogger.log_debug("FragmentHeader: %u %u %u %u %u",
2021                            Header.SectionType,
2022                            Header.SectionLength,
2023                            Header.TableId,
2024                            Header.FragmentNo,
2025                            Header.ChecksumType);
2026 
2027   m_currentTable = m_metaData.getTable(Header.TableId);
2028   if(m_currentTable == 0){
2029     ret = -1;
2030     return false;
2031   }
2032 
2033   if(!m_tuple.prepareRecord(*m_currentTable))
2034   {
2035     ret =-1;
2036     return false;
2037   }
2038 
2039   calc_row_extra_storage_words(m_currentTable);
2040   info.setLevel(254);
2041   restoreLogger.log_info("_____________________________________________________"
2042                          "\nProcessing data in table: %s(%u) fragment %u",
2043                            m_currentTable->getTableName(),
2044                            Header.TableId, Header.FragmentNo);
2045 
2046   m_count = 0;
2047   ret = 0;
2048   *fragmentId = Header.FragmentNo;
2049   return true;
2050 } // RestoreDataIterator::getNextFragment
2051 
2052 
2053 bool
validateFragmentFooter()2054 RestoreDataIterator::validateFragmentFooter() {
2055   BackupFormat::DataFile::FragmentFooter footer;
2056 
2057   if (buffer_read(&footer, sizeof(footer), 1) != 1){
2058     restoreLogger.log_error("getFragmentFooter:Error reading fragment footer");
2059     return false;
2060   }
2061 
2062   // TODO: Handle footer, nothing yet
2063   footer.SectionType  = ntohl(footer.SectionType);
2064   footer.SectionLength  = ntohl(footer.SectionLength);
2065   footer.TableId  = ntohl(footer.TableId);
2066   footer.FragmentNo  = ntohl(footer.FragmentNo);
2067   footer.NoOfRecords  = ntohl(footer.NoOfRecords);
2068   footer.Checksum  = ntohl(footer.Checksum);
2069 
2070   assert(m_count == footer.NoOfRecords);
2071 
2072   return true;
2073 } // RestoreDataIterator::getFragmentFooter
2074 
AttributeDesc(NdbDictionary::Column * c)2075 AttributeDesc::AttributeDesc(NdbDictionary::Column *c)
2076   : m_column(c), transform(NULL), truncation_detected(false)
2077 {
2078   size = 8*NdbColumnImpl::getImpl(* c).m_attrSize;
2079   arraySize = NdbColumnImpl::getImpl(* c).m_arraySize;
2080   staging = false;
2081   parameterSz = 0;
2082 }
2083 
~AttributeDesc()2084 AttributeDesc::~AttributeDesc()
2085 {
2086   delete transform;
2087   transform = NULL;
2088 }
2089 
createAttr(NdbDictionary::Column * column)2090 void TableS::createAttr(NdbDictionary::Column *column)
2091 {
2092   AttributeDesc * d = new AttributeDesc(column);
2093   if(d == NULL) {
2094     restoreLogger.log_error("Restore: Failed to allocate memory");
2095     abort();
2096   }
2097   d->attrId = allAttributesDesc.size();
2098   d->convertFunc = NULL;
2099   d->parameter = NULL;
2100   d->m_exclude = false;
2101   allAttributesDesc.push_back(d);
2102 
2103   if (d->m_column->getAutoIncrement())
2104     m_auto_val_attrib = d;
2105 
2106   if(d->m_column->getPrimaryKey() && backupVersion <= MAKE_VERSION(4,1,7))
2107   {
2108     m_fixedKeys.push_back(d);
2109     return;
2110   }
2111 
2112   if (d->m_column->getArrayType() == NDB_ARRAYTYPE_FIXED &&
2113       ! d->m_column->getNullable())
2114   {
2115     m_fixedAttribs.push_back(d);
2116     return;
2117   }
2118 
2119   // just a reminder - does not solve backwards compat
2120   if (backupVersion < MAKE_VERSION(5,1,3) || ndbd_drop6(backupVersion))
2121   {
2122     d->m_nullBitIndex = m_noOfNullable;
2123     m_noOfNullable++;
2124     m_nullBitmaskSize = (m_noOfNullable + 31) / 32;
2125   }
2126   if ((d->m_column->getType() == NdbDictionary::Column::Blob) ||
2127       (d->m_column->getType() == NdbDictionary::Column::Text))
2128   {
2129     if (d->m_column->getPartSize() > 0)
2130     {
2131       m_has_blobs = true;
2132     }
2133   }
2134   m_variableAttribs.push_back(d);
2135 } // TableS::createAttr
2136 
2137 bool
get_auto_data(const TupleS & tuple,Uint32 * syskey,Uint64 * nextid) const2138 TableS::get_auto_data(const TupleS & tuple, Uint32 * syskey, Uint64 * nextid) const
2139 {
2140   /*
2141     Read current (highest) auto_increment value for
2142     a table. Currently there can only be one per table.
2143     The values are stored in sustable SYSTAB_0 as
2144     {SYSKEY,NEXTID} values where SYSKEY (32-bit) is
2145     the table_id and NEXTID (64-bit) is the next auto_increment
2146     value in the sequence (note though that sequences of
2147     values can have been fetched and that are cached in NdbAPI).
2148     SYSTAB_0 can contain other data so we need to check that
2149     the found SYSKEY value is a valid table_id (< 0x10000000).
2150    */
2151   AttributeData * attr_data = tuple.getData(0);
2152   AttributeDesc * attr_desc = tuple.getDesc(0);
2153   const AttributeS attr1 = {attr_desc, *attr_data};
2154   memcpy(syskey ,attr1.Data.u_int32_value, sizeof(Uint32));
2155   attr_data = tuple.getData(1);
2156   attr_desc = tuple.getDesc(1);
2157   const AttributeS attr2 = {attr_desc, *attr_data};
2158   memcpy(nextid, attr2.Data.u_int64_value, sizeof(Uint64));
2159   if (*syskey < 0x10000000)
2160   {
2161     return true;
2162   }
2163   return false;
2164 }
2165 
Twiddle16(Uint16 in)2166 Uint16 Twiddle16(Uint16 in)
2167 {
2168   Uint16 retVal = 0;
2169 
2170   retVal = ((in & 0xFF00) >> 8) |
2171     ((in & 0x00FF) << 8);
2172 
2173   return(retVal);
2174 } // Twiddle16
2175 
Twiddle32(Uint32 in)2176 Uint32 Twiddle32(Uint32 in)
2177 {
2178   Uint32 retVal = 0;
2179 
2180   retVal = ((in & 0x000000FF) << 24) |
2181     ((in & 0x0000FF00) << 8)  |
2182     ((in & 0x00FF0000) >> 8)  |
2183     ((in & 0xFF000000) >> 24);
2184 
2185   return(retVal);
2186 } // Twiddle32
2187 
Twiddle64(Uint64 in)2188 Uint64 Twiddle64(Uint64 in)
2189 {
2190   Uint64 retVal = 0;
2191 
2192   retVal =
2193     ((in & (Uint64)0x00000000000000FFLL) << 56) |
2194     ((in & (Uint64)0x000000000000FF00LL) << 40) |
2195     ((in & (Uint64)0x0000000000FF0000LL) << 24) |
2196     ((in & (Uint64)0x00000000FF000000LL) << 8) |
2197     ((in & (Uint64)0x000000FF00000000LL) >> 8) |
2198     ((in & (Uint64)0x0000FF0000000000LL) >> 24) |
2199     ((in & (Uint64)0x00FF000000000000LL) >> 40) |
2200     ((in & (Uint64)0xFF00000000000000LL) >> 56);
2201 
2202   return(retVal);
2203 } // Twiddle64
2204 
RestoreLogIterator(const RestoreMetaData & md)2205 RestoreLogIterator::RestoreLogIterator(const RestoreMetaData & md)
2206   : m_metaData(md)
2207 {
2208   restoreLogger.log_debug("RestoreLog constructor");
2209   setLogFile(md, 0);
2210 
2211   m_count = 0;
2212   m_last_gci = 0;
2213   m_rowBuffIndex = 0;
2214 }
2215 
2216 const LogEntry *
getNextLogEntry(int & res)2217 RestoreLogIterator::getNextLogEntry(int & res) {
2218   // Read record length
2219   const Uint32 startGCP = m_metaData.getStartGCP();
2220   const Uint32 stopGCP = m_metaData.getStopGCP();
2221   Uint32 tableId;
2222   Uint32 triggerEvent;
2223   Uint32 frag_id;
2224   Uint32 *attr_data;
2225   Uint32 attr_data_len;
2226   bool skip_entry = false;
2227   do {
2228     Uint32 len;
2229     Uint32 *logEntryPtr;
2230     if(m_is_undolog){
2231       int read_result = 0;
2232       read_result = buffer_read(&len, sizeof(Uint32), 1);
2233       //no more log data to read
2234       if (read_result == 0 ) {
2235         res = 0;
2236         return 0;
2237       }
2238       if (read_result != 1) {
2239         res= -1;
2240         return 0;
2241       }
2242     }
2243     else{
2244       if (buffer_read_ahead(&len, sizeof(Uint32), 1) != 1){
2245         res= -1;
2246         return 0;
2247       }
2248     }
2249     len= ntohl(len);
2250 
2251     Uint32 data_len = sizeof(Uint32) + len*4;
2252     if (buffer_get_ptr((void **)(&logEntryPtr), 1, data_len) != data_len) {
2253       res= -2;
2254       return 0;
2255     }
2256 
2257     if(len == 0){
2258       res= 0;
2259       return 0;
2260     }
2261 
2262     const Uint32 backup_file_version = m_metaData.getFileHeader().NdbVersion;
2263     if (unlikely(!ndbd_backup_file_fragid(backup_file_version)))
2264     {
2265       /*
2266         FragId was introduced in LogEntry in version
2267         5.1.6
2268         We set FragId to 0 in older versions (these versions
2269         do not support restore of user defined partitioned
2270         tables.
2271       */
2272       typedef BackupFormat::LogFile::LogEntry_no_fragid LogE_no_fragid;
2273       LogE_no_fragid * logE_no_fragid= (LogE_no_fragid *)logEntryPtr;
2274       tableId= ntohl(logE_no_fragid->TableId);
2275       triggerEvent= ntohl(logE_no_fragid->TriggerEvent);
2276       frag_id= 0;
2277       attr_data= &logE_no_fragid->Data[0];
2278       attr_data_len=
2279         len - BackupFormat::LogFile::LogEntry_no_fragid::HEADER_LENGTH_WORDS;
2280     }
2281     else /* normal case */
2282     {
2283       typedef BackupFormat::LogFile::LogEntry LogE;
2284       LogE * logE= (LogE *)logEntryPtr;
2285       tableId= ntohl(logE->TableId);
2286       triggerEvent= ntohl(logE->TriggerEvent);
2287       frag_id= ntohl(logE->FragId);
2288       attr_data= &logE->Data[0];
2289       attr_data_len=
2290         len - BackupFormat::LogFile::LogEntry::HEADER_LENGTH_WORDS;
2291     }
2292 
2293     const bool hasGcp= (triggerEvent & 0x10000) != 0;
2294     triggerEvent &= 0xFFFF;
2295 
2296     if(hasGcp){
2297       // last attr_data is gci info
2298       attr_data_len--;
2299       m_last_gci = ntohl(*(attr_data + attr_data_len));
2300     }
2301     if (m_is_undolog)
2302     {
2303       // Do not apply anything from startGCP or lower
2304       skip_entry = (m_last_gci <= startGCP);
2305     }
2306     else
2307     {
2308       // Do not apply anything after stopGCP
2309       skip_entry = (m_last_gci > stopGCP);
2310     }
2311     // Skip entries instead of stopping scan since entries are not ordered
2312     // by GCP. Entries from different GCPs may be interleaved, so scan till
2313     // EOF to read all matching entries.
2314   } while (skip_entry);
2315 
2316   m_logEntry.m_table = m_metaData.getTable(tableId);
2317   /* We should 'invert' the operation type when we restore an Undo log.
2318    *   To undo an insert operation, a delete is required.
2319    *   To undo a delete operation, an insert is required.
2320    * The backup have collected 'before values' for undoing 'delete+update' to make this work.
2321    * To undo insert, we only need primary key.
2322    */
2323   switch(triggerEvent){
2324   case TriggerEvent::TE_INSERT:
2325     if(m_is_undolog)
2326       m_logEntry.m_type = LogEntry::LE_DELETE;
2327     else
2328       m_logEntry.m_type = LogEntry::LE_INSERT;
2329     break;
2330   case TriggerEvent::TE_UPDATE:
2331     m_logEntry.m_type = LogEntry::LE_UPDATE;
2332     break;
2333   case TriggerEvent::TE_DELETE:
2334     if(m_is_undolog)
2335       m_logEntry.m_type = LogEntry::LE_INSERT;
2336     else
2337       m_logEntry.m_type = LogEntry::LE_DELETE;
2338     break;
2339   default:
2340     res = -1;
2341     return NULL;
2342   }
2343 
2344   const TableS * tab = m_logEntry.m_table;
2345   m_logEntry.clear();
2346   m_rowBuffIndex = 0;
2347 
2348   AttributeHeader * ah = (AttributeHeader *)attr_data;
2349   AttributeHeader *end = (AttributeHeader *)(attr_data + attr_data_len);
2350   AttributeS * attr;
2351   m_logEntry.m_frag_id = frag_id;
2352   while(ah < end){
2353     attr = m_logEntry.add_attr();
2354     if(attr == NULL) {
2355       restoreLogger.log_error("Restore: Failed to allocate memory");
2356       res = -1;
2357       return 0;
2358     }
2359 
2360     if(unlikely(!m_hostByteOrder))
2361       *(Uint32*)ah = Twiddle32(*(Uint32*)ah);
2362 
2363     attr->Desc = tab->getAttributeDesc(ah->getAttributeId());
2364     assert(attr->Desc != 0);
2365 
2366     const Uint32 sz = ah->getByteSize();
2367     if(sz == 0){
2368       attr->Data.null = true;
2369       attr->Data.void_value = NULL;
2370       attr->Data.size = 0;
2371     } else {
2372       attr->Data.null = false;
2373       attr->Data.void_value = ah->getDataPtr();
2374       attr->Data.size = sz;
2375       Twiddle(attr->Desc, &(attr->Data));
2376     }
2377 
2378     if (attr->Desc->transform)
2379     {
2380       const int col_idx = ah->getAttributeId();
2381       const NdbDictionary::Column* col = tab->m_dictTable->getColumn(col_idx);
2382       void* dst_buf = m_rowBuff + m_rowBuffIndex;
2383       m_rowBuffIndex+= attr->Desc->getSizeInWords();
2384       assert(m_rowBuffIndex <= RowBuffWords);
2385 
2386       if (!applyColumnTransform(col,
2387                                 attr->Desc,
2388                                 &attr->Data,
2389                                 dst_buf))
2390       {
2391         res = -1;
2392         return 0;
2393       }
2394     }
2395 
2396     ah = ah->getNext();
2397   }
2398 
2399   m_count ++;
2400   res = 0;
2401   return &m_logEntry;
2402 }
2403 
2404 NdbOut &
operator <<(NdbOut & ndbout,const AttributeS & attr)2405 operator<<(NdbOut& ndbout, const AttributeS& attr){
2406   const AttributeData & data = attr.Data;
2407   const AttributeDesc & desc = *(attr.Desc);
2408 
2409   if (data.null)
2410   {
2411     ndbout << g_ndbrecord_print_format.null_string;
2412     return ndbout;
2413   }
2414 
2415   NdbRecAttr tmprec(0);
2416   tmprec.setup(desc.m_column, 0);
2417 
2418   assert(desc.size % 8 == 0);
2419 #ifndef NDEBUG
2420   const Uint32 length = (desc.size)/8 * (desc.arraySize);
2421 #endif
2422   assert((desc.m_column->getArrayType() == NdbDictionary::Column::ArrayTypeFixed)
2423          ? (data.size == length)
2424          : (data.size <= length));
2425 
2426   tmprec.receive_data((Uint32*)data.void_value, data.size);
2427 
2428   ndbrecattr_print_formatted(ndbout, tmprec, g_ndbrecord_print_format);
2429 
2430   return ndbout;
2431 }
2432 
2433 // Print tuple data
2434 NdbOut&
operator <<(NdbOut & ndbout,const TupleS & tuple)2435 operator<<(NdbOut& ndbout, const TupleS& tuple)
2436 {
2437   for (int i = 0; i < tuple.getNoOfAttributes(); i++)
2438   {
2439     if (i > 0)
2440       ndbout << g_ndbrecord_print_format.fields_terminated_by;
2441     AttributeData * attr_data = tuple.getData(i);
2442     AttributeDesc * attr_desc = tuple.getDesc(i);
2443     const AttributeS attr = {attr_desc, *attr_data};
2444     debug << i << " " << attr_desc->m_column->getName();
2445     ndbout << attr;
2446   } // for
2447   return ndbout;
2448 }
2449 
2450 // Print tuple data
2451 NdbOut&
operator <<(NdbOut & ndbout,const LogEntry & logE)2452 operator<<(NdbOut& ndbout, const LogEntry& logE)
2453 {
2454   switch(logE.m_type)
2455   {
2456   case LogEntry::LE_INSERT:
2457     ndbout << "INSERT " << logE.m_table->getTableName() << " ";
2458     break;
2459   case LogEntry::LE_DELETE:
2460     ndbout << "DELETE " << logE.m_table->getTableName() << " ";
2461     break;
2462   case LogEntry::LE_UPDATE:
2463     ndbout << "UPDATE " << logE.m_table->getTableName() << " ";
2464     break;
2465   default:
2466     ndbout << "Unknown log entry type (not insert, delete or update)" ;
2467   }
2468 
2469   for (Uint32 i= 0; i < logE.size();i++)
2470   {
2471     const AttributeS * attr = logE[i];
2472     ndbout << attr->Desc->m_column->getName() << "=";
2473     ndbout << (* attr);
2474     if (i < (logE.size() - 1))
2475       ndbout << ", ";
2476   }
2477   return ndbout;
2478 }
2479 
2480 void
printAttributeValue() const2481 AttributeS::printAttributeValue() const {
2482   NdbDictionary::Column::Type columnType =
2483       this->Desc->m_column->getType();
2484   switch(columnType)
2485   {
2486     case NdbDictionary::Column::Char:
2487     case NdbDictionary::Column::Varchar:
2488     case NdbDictionary::Column::Binary:
2489     case NdbDictionary::Column::Varbinary:
2490     case NdbDictionary::Column::Datetime:
2491     case NdbDictionary::Column::Date:
2492     case NdbDictionary::Column::Longvarchar:
2493     case NdbDictionary::Column::Longvarbinary:
2494     case NdbDictionary::Column::Time:
2495     case NdbDictionary::Column::Timestamp:
2496     case NdbDictionary::Column::Time2:
2497     case NdbDictionary::Column::Datetime2:
2498     case NdbDictionary::Column::Timestamp2:
2499       ndbout << "\'" << (* this) << "\'";
2500       break;
2501     default:
2502       ndbout << (* this);
2503   }
2504 }
2505 
2506 void
printSqlLog() const2507 LogEntry::printSqlLog() const {
2508   /* Extract the table name from log entry which is stored in
2509    * database/schema/table and convert to database.table format
2510    */
2511   BaseString tableName(m_table->getTableName());
2512   Vector<BaseString> tableNameParts;
2513   Uint32 noOfPK = m_table->m_dictTable->getNoOfPrimaryKeys();
2514   tableName.split(tableNameParts, "/");
2515   tableName.assign("");
2516   tableName.assign(tableNameParts[0]);
2517   tableName.append(".");
2518   tableName.append(tableNameParts[2]);
2519   switch(m_type)
2520   {
2521     case LE_INSERT:
2522       ndbout << "INSERT INTO " << tableName.c_str() << " VALUES(";
2523       for (Uint32 i = noOfPK; i < size(); i++)
2524       {
2525         /* Skip the first field(s) which contains additional
2526          * instance of the primary key */
2527         const AttributeS * attr = m_values[i];
2528         attr->printAttributeValue();
2529         if (i < (size() - 1))
2530           ndbout << ",";
2531       }
2532       ndbout << ")";
2533       break;
2534     case LE_DELETE:
2535       ndbout << "DELETE FROM " << tableName.c_str() << " WHERE ";
2536       for (Uint32 i = 0; i < size();i++)
2537       {
2538         /* Primary key(s) clauses */
2539         const AttributeS * attr = m_values[i];
2540         const char* columnName = attr->Desc->m_column->getName();
2541         ndbout << columnName << "=";
2542         attr->printAttributeValue();
2543         if (i < (size() - 1))
2544           ndbout << " AND ";
2545       }
2546       break;
2547     case LE_UPDATE:
2548       ndbout << "UPDATE " << tableName.c_str() << " SET ";
2549       for (Uint32 i = noOfPK; i < size(); i++)
2550       {
2551         /* Print column(s) being set*/
2552         const AttributeS * attr = m_values[i];
2553         const char* columnName = attr->Desc->m_column->getName();
2554         ndbout << columnName << "=";
2555         attr->printAttributeValue();
2556         if (i < (size() - 1))
2557           ndbout << ", ";
2558       }
2559       /*Print where clause with primary key(s)*/
2560       ndbout << " WHERE ";
2561       for (Uint32 i = 0; i < noOfPK; i++)
2562       {
2563         const AttributeS * attr = m_values[i];
2564         const char* columnName = attr->Desc->m_column->getName();
2565         ndbout << columnName << "=";
2566         attr->printAttributeValue();
2567         if(i < noOfPK-1)
2568           ndbout << " AND ";
2569       }
2570       break;
2571     default:
2572       ndbout << "Unknown log entry type (not insert, delete or update)" ;
2573   }
2574   ndbout << ";";
2575 }
2576 
RestoreLogger()2577 RestoreLogger::RestoreLogger()
2578 {
2579   m_mutex = NdbMutex_Create();
2580 }
2581 
~RestoreLogger()2582 RestoreLogger::~RestoreLogger()
2583 {
2584   NdbMutex_Destroy(m_mutex);
2585 }
2586 
log_error(const char * fmt,...)2587 void RestoreLogger::log_error(const char* fmt, ...)
2588 {
2589   va_list ap;
2590   va_start(ap, fmt);
2591   char buf[LOG_MSGLEN];
2592   vsnprintf(buf, sizeof(buf), fmt, ap);
2593   va_end(ap);
2594 
2595   NdbMutex_Lock(m_mutex);
2596   err << getThreadPrefix() << buf << endl;
2597   NdbMutex_Unlock(m_mutex);
2598 }
2599 
log_info(const char * fmt,...)2600 void RestoreLogger::log_info(const char* fmt, ...)
2601 {
2602   va_list ap;
2603   va_start(ap, fmt);
2604   char buf[LOG_MSGLEN];
2605   vsnprintf(buf, sizeof(buf), fmt, ap);
2606   va_end(ap);
2607 
2608   NdbMutex_Lock(m_mutex);
2609   info << getThreadPrefix() << buf << endl;
2610   NdbMutex_Unlock(m_mutex);
2611 }
2612 
log_debug(const char * fmt,...)2613 void RestoreLogger::log_debug(const char* fmt, ...)
2614 {
2615   va_list ap;
2616   va_start(ap, fmt);
2617   char buf[LOG_MSGLEN];
2618   vsnprintf(buf, sizeof(buf), fmt, ap);
2619   va_end(ap);
2620 
2621   NdbMutex_Lock(m_mutex);
2622   debug << getThreadPrefix() << buf << endl;
2623   NdbMutex_Unlock(m_mutex);
2624 }
2625 
2626 void
setThreadPrefix(const char * prefix)2627 RestoreLogger::setThreadPrefix(const char* prefix)
2628 {
2629    /* Reuse 'JAM buffer' Tls key for a per-thread prefix string buffer pointer */
2630    NDB_THREAD_TLS_JAM = (EmulatedJamBuffer*)prefix;
2631 }
2632 
2633 const char*
getThreadPrefix() const2634 RestoreLogger::getThreadPrefix() const
2635 {
2636    const char* prefix = (const char*) NDB_THREAD_TLS_JAM;
2637    if (prefix == NULL)
2638    {
2639       prefix =  "";
2640     }
2641    return prefix;
2642 }
2643 
2644 NdbOut &
operator <<(NdbOut & ndbout,const TableS & table)2645 operator<<(NdbOut& ndbout, const TableS & table)
2646 {
2647   ndbout << "-- " << table.getTableName() << " --" << endl;
2648   ndbout << *(table.m_dictTable) << endl;
2649   return ndbout;
2650 }
2651 
2652 template class Vector<TableS*>;
2653 template class Vector<AttributeS*>;
2654 template class Vector<AttributeDesc*>;
2655 template class Vector<FragmentInfo*>;
2656 template class Vector<DictObject>;
2657