~ubuntu-branches/ubuntu/trusty/mysql-5.6/trusty

« back to all changes in this revision

Viewing changes to storage/ndb/test/include/NdbSchemaOp.hpp

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-02-12 11:54:27 UTC
  • Revision ID: package-import@ubuntu.com-20140212115427-oq6tfsqxl1wuwehi
Tags: upstream-5.6.15
ImportĀ upstreamĀ versionĀ 5.6.15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
   Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
 
3
 
 
4
   This program is free software; you can redistribute it and/or modify
 
5
   it under the terms of the GNU General Public License as published by
 
6
   the Free Software Foundation; version 2 of the License.
 
7
 
 
8
   This program is distributed in the hope that it will be useful,
 
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
   GNU General Public License for more details.
 
12
 
 
13
   You should have received a copy of the GNU General Public License
 
14
   along with this program; if not, write to the Free Software
 
15
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
 
16
*/
 
17
 
 
18
#ifndef NdbSchemaOp_H
 
19
#define NdbSchemaOp_H
 
20
 
 
21
#include <NdbDictionary.hpp>
 
22
 
 
23
#ifndef DOXYGEN_SHOULD_SKIP_DEPRECATED
 
24
 
 
25
  /**
 
26
   * Type of attribute
 
27
   *
 
28
   * NOTE! AttrType is deprecated, use NdbDictionary::Column::Type instead!
 
29
   */
 
30
  enum AttrType { 
 
31
    Signed,           ///< Attributes of this type can be read with:
 
32
                      ///< NdbRecAttr::int64_value, 
 
33
                      ///< NdbRecAttr::int32_value,
 
34
                      ///< NdbRecAttr::short_value, 
 
35
                      ///< NdbRecAttr::char_value
 
36
    UnSigned,         ///< Attributes of this type can be read with:
 
37
                      ///< NdbRecAttr::u_64_value, 
 
38
                      ///< NdbRecAttr::u_32_value,
 
39
                      ///< NdbRecAttr::u_short_value, 
 
40
                      ///< NdbRecAttr::u_char_value
 
41
    Float,            ///< Attributes of this type can be read with:
 
42
                      ///< NdbRecAttr::float_value and 
 
43
                      ///< NdbRecAttr::double_value
 
44
    String,           ///< Attributes of this type can be read with:
 
45
                      ///< NdbRecAttr::aRef, 
 
46
                      ///< NdbRecAttr::getAttributeObject
 
47
    NoAttrTypeDef     ///< Used for debugging only
 
48
  };
 
49
 
 
50
 
 
51
  /**
 
52
   * @deprecated
 
53
   */
 
54
  enum NullAttributeType { 
 
55
    NoNullTypeDefined = -1,
 
56
    NotNullAttribute, 
 
57
    NullAttribute,
 
58
    AttributeDefined 
 
59
  };
 
60
  /**
 
61
   * Indicates whether the attribute is part of a primary key or not
 
62
   */
 
63
  enum KeyType { 
 
64
    Undefined = -1,               ///< Used for debugging only
 
65
    NoKey,                        ///< Attribute is not part of primary key 
 
66
                                  ///< or tuple identity
 
67
    TupleKey,                     ///< Attribute is part of primary key
 
68
    TupleId                       ///< Attribute is part of tuple identity 
 
69
                                  ///< (This type of attribute is created 
 
70
                                  ///< internally, and should not be 
 
71
                                  ///< manually created.)
 
72
  };
 
73
  /**
 
74
   * Indicate whether the attribute should be stored on disk or not
 
75
   * Only for legacy createAttribute().
 
76
   */
 
77
  enum StorageMode { 
 
78
    MMBased = NDB_STORAGETYPE_MEMORY,
 
79
    DiskBased = NDB_STORAGETYPE_DISK
 
80
  };
 
81
  
 
82
  /**
 
83
   *  Type of fragmentation used for a table
 
84
   */
 
85
  enum FragmentType { 
 
86
    Default = 0,                  ///<  (All is default!)
 
87
    Single = 1,                   ///< Only one fragment
 
88
    All = 2,                      ///< Default value.  One fragment per node group
 
89
    DistributionGroup = 3,        ///< Distribution Group used for fragmentation.
 
90
                                  ///< One fragment per node group
 
91
    DistributionKey = 4,          ///< Distribution Key used for fragmentation.
 
92
                                  ///< One fragment per node group.
 
93
    AllLarge = 5,                 ///< Sixten fragments per node group.
 
94
    DGroupLarge = 6,              ///< Distribution Group used for fragmentation.
 
95
                                  ///< Sixten fragments per node group
 
96
    DKeyLarge = 7                 ///< Distribution Key used for fragmentation.
 
97
                                  ///< Sixten fragments per node group
 
98
  };
 
99
  
 
100
  /**
 
101
   *  Type of table or index.
 
102
   */
 
103
  enum TableType {
 
104
    UndefTableType = 0,
 
105
    SystemTable = 1,              ///< Internal.Table cannot be updated by user
 
106
    UserTable = 2,                  ///< Normal application table
 
107
    UniqueHashIndex = 3,          ///< Unique un-ordered hash index
 
108
    HashIndex = 4,                ///< Non-unique un-ordered hash index
 
109
    UniqueOrderedIndex = 5,       ///< Unique ordered index
 
110
    OrderedIndex = 6              ///< Non-unique ordered index
 
111
  };
 
112
 
 
113
 
 
114
class NdbSchemaCon;
 
115
class Ndb;
 
116
  
 
117
 
 
118
/** 
 
119
 * @class NdbSchemaOp
 
120
 * @brief Represents various operations for use in schema transactions
 
121
 *
 
122
 * This class is used for schema operations, e.g. creating tables and
 
123
 * attributes.
 
124
 *
 
125
 * The NdbSchemaOp object is created using NdbSchemaCon::getNdbSchemaOp.
 
126
 * 
 
127
 * @note  This class is deprecated and is now replaced with the class
 
128
 *        NdbDictionary.
 
129
 */
 
130
class NdbSchemaOp 
 
131
{
 
132
  friend class Ndb;
 
133
  friend class NdbSchemaCon;
 
134
 
 
135
public:
 
136
 
 
137
  
 
138
  /**
 
139
   * Create a new table in the database.
 
140
   * 
 
141
   * @note The NdbSchemaCon should be closed with 
 
142
   *       Ndb::closeSchemaTransaction, even if this method fails.
 
143
   *
 
144
   * @param  aTableName   Table name.  Should not be NULL.
 
145
   * @param  aTableSize   (Performance parameter.)
 
146
   *                      Initial size of the data part of the table
 
147
   *                      expressed in kByte. 
 
148
   *                      The database handles
 
149
   *                      bad parameter setting but at a certain 
 
150
   *                      loss in performance.
 
151
   *                      The size given here is
 
152
   *                      the initial size allocated for the table 
 
153
   *                      storage (the data part).
 
154
   *                      When calculating the data storage one should 
 
155
   *                      add the size of all attributes (each attribute
 
156
   *                      consumes at least 4 bytes) and also an overhead
 
157
   *                      of 12 byte. 
 
158
   *                      Variable size attributes (not supported yet)
 
159
   *                      will have a size of 12 bytes plus the actual 
 
160
   *                      data storage parts where there is an 
 
161
   *                      additional overhead based on the size of the
 
162
   *                      variable part.
 
163
   *                      <br>
 
164
   *                      An example table with 5 attributes: 
 
165
   *                      one 64 bit attribute, one 32 bit attribute, 
 
166
   *                      two 16 bit attributes and one array of 64 8 bits. 
 
167
   *                      This table will consume 
 
168
   *                        12 (overhead) + 8 + 4 + 2*4 (4 is minimum) + 64 = 
 
169
   *                        96 bytes per record.
 
170
   *                      Additionally an overhead of about 2 % as page 
 
171
   *                      headers and waste should be allocated. 
 
172
   *                      Thus, 1 million records should consume 96 MBytes
 
173
   *                      plus the overhead 2 MByte and rounded up to 
 
174
   *                      100 000 kBytes.
 
175
   *                      <br><em>
 
176
   *                      This parameter is currently not used.
 
177
   *                      </em>
 
178
   * @param  aTupleKey    Indicates if the table has a primary key or not.
 
179
   *                      <br>
 
180
   *                        <b>TupleKey</b> means that a <em>primary key</em> 
 
181
   *                        consisting of one to four attributes
 
182
   *                        (at most one of variable size) 
 
183
   *                        uniquely identifies each record in the created
 
184
   *                        table.
 
185
   *                        <br>
 
186
   *                        <b>TupleId</b> means that a <em>tuple identity</em>
 
187
   *                        is used.  The tuple identity is 
 
188
   *                        a unique key indentifying each record of the 
 
189
   *                        created table.
 
190
   *                        The tuple identity is a (non-stored)
 
191
   *                        64 bit attribute named <b>NDB$TID</b>.
 
192
   *                        <br>
 
193
   *                        When inserting a record (tuple), the method 
 
194
   *                        NdbOperation::setTupleId 
 
195
   *                        will generate a unique tuple identity
 
196
   *                        and return it to the user. 
 
197
   *                        <br>
 
198
   *                        When reading, updating or deleting a record
 
199
   *                        in a table with <b>TupleId</b>,
 
200
   *                        NdbOperation::equal("NDB$TID", value_Uint64)
 
201
   *                        can be used to identify the record.
 
202
   *                        <br>
 
203
   *                        Legal values: TupleKey or TupleId.
 
204
   * @param aNrOfPages    (Performance parameter.)
 
205
   *                      Specifies the initial size of the index storage. 
 
206
   *                      When calculating the index storage,
 
207
   *                      each key has approximately 14 byte of 
 
208
   *                      overhead plus the size of the key. 
 
209
   *                      Each key attribute takes up at least 4 bytes 
 
210
   *                      of storage. 
 
211
   *                      Thus a mixed key consisting of a 
 
212
   *                      64 bit attribute, a 32 bit attribute
 
213
   *                      and a 16 bit attribute will 
 
214
   *                      consume approx. 30 bytes per key.
 
215
   *                      Thus, the if initial size is to be 1 million rows,
 
216
   *                      then aNrOfPages should be set to 
 
217
   *                      30 M / 8k = 2670 pages.
 
218
   *                      <br><em>
 
219
   *                      This parameter is currently not used.
 
220
   *                       </em>
 
221
   * @param aFragmentType Type of fragmentation.<br>
 
222
   *                      <b>All</b> (default) means that the 
 
223
   *                      table fragments are automatically 
 
224
   *                      distributed on all nodes in the system.<br>
 
225
   *                      <b>DistributionGroup</b> and 
 
226
   *                      <b>DistributionKey</b> are 
 
227
   *                      also supported. For further details about
 
228
   *                      these types see the documentation of 
 
229
   *                      Ndb::startTransaction.
 
230
   * @param aKValue       (Hash parameter.)
 
231
   *                      Only allowed value is 6.
 
232
   *                      Later implementations might add flexibility
 
233
   *                      in this parameter.
 
234
   * @param aMinLoadFactor  (Hash parameter.)
 
235
   *                        This value specifies the load factor when 
 
236
   *                        starting to shrink the hash table. 
 
237
   *                        It must be smaller than aMaxLoadFactor.
 
238
   *                        Both these factors are given in percentage.
 
239
   * @param aMaxLoadFactor  (Hash parameter.)
 
240
   *                        This value specifies the load factor when 
 
241
   *                        starting to split the containers in the local
 
242
   *                        hash tables. 100 is the maximum which will
 
243
   *                        optimize memory usage (this is the figure 
 
244
   *                        used for the above calculations).
 
245
   *                        A lower figure will store less information in 
 
246
   *                        each container and thus
 
247
   *                        find the key faster but consume more memory.
 
248
   * @param aMemoryType     Currently only 1 is allowed which specifies 
 
249
   *                        storage of table in main memory. 
 
250
   *                        Later 2 will be added where the table is stored
 
251
   *                        completely on disk 
 
252
   *                        and 3 where the index is in main memory but
 
253
   *                        data is on disk. 
 
254
   *                        If 1 is chosen an individual attribute can
 
255
   *                        still be specified as a disk attribute.
 
256
   * @param aStoredTable    If set to false it indicates that the table is 
 
257
   *                        a temporary table and should not be logged 
 
258
   *                        to disk.
 
259
   *                        In case of a system restart the table will still
 
260
   *                        be defined and exist but will be empty. 
 
261
   *                        Thus no checkpointing and
 
262
   *                        no logging is performed on the table.
 
263
   *                        The default value is true and indicates a 
 
264
   *                        normal table with full checkpointing and 
 
265
   *                        logging activated.
 
266
   * @return                Returns 0 when successful and returns -1 otherwise.
 
267
   */
 
268
  int           createTable(    const char* aTableName, 
 
269
                                Uint32 aTableSize = 8, 
 
270
                                KeyType aTupleKey = TupleKey,
 
271
                                int aNrOfPages = 2, 
 
272
                                FragmentType aFragmentType = All, 
 
273
                                int aKValue = 6,
 
274
                                int aMinLoadFactor = 78,
 
275
                                int aMaxLoadFactor = 80,
 
276
                                int aMemoryType = 1,
 
277
                                bool aStoredTable = true);
 
278
 
 
279
  /** 
 
280
   * This is the old function declaration, don't use.
 
281
   *
 
282
   * @deprecated do not use!
 
283
   */
 
284
#ifndef NDB_WIN32
 
285
  inline int    createTable(    const char* aTableName, 
 
286
                                Uint32 aTableSize, 
 
287
                                KeyType aTupleKey,
 
288
                                int aNrOfPages, 
 
289
                                FragmentType aFragmentType, 
 
290
                                int aKValue,
 
291
                                int aMinLoadFactor,
 
292
                                int aMaxLoadFactor,
 
293
                                int aMemoryType,
 
294
                                int aStoredTable){
 
295
    return createTable(aTableName, 
 
296
                       aTableSize, 
 
297
                       aTupleKey,                        
 
298
                       aNrOfPages, 
 
299
                       aFragmentType, 
 
300
                       aKValue, 
 
301
                       aMinLoadFactor, 
 
302
                       aMaxLoadFactor, 
 
303
                       aMemoryType, 
 
304
                       (aStoredTable == 1 ? true : false));
 
305
  }
 
306
#endif
 
307
 
 
308
  /**
 
309
   * Add a new attribute to a database table.
 
310
   *
 
311
   * Attributes can only be added to a table in the same transaction
 
312
   * as the transaction creating the table.
 
313
   *
 
314
   * @note The NdbSchemaCon transaction should be closed with 
 
315
   *       Ndb::closeSchemaTransaction, even if this method fails.
 
316
   *
 
317
   * Example creating an unsigned int attribute belonging to the primary key
 
318
   * of the table it is created in:
 
319
   * @code 
 
320
   *   MySchemaOp->createAttribute("Attr1",   // Attribute name
 
321
   *                               TupleKey,  // Belongs to primary key
 
322
   *                               32,        // 32 bits
 
323
   *                               1,         // Not an array attribute
 
324
   *                               UnSigned,  // Unsigned type
 
325
   *                              );
 
326
   * @endcode 
 
327
   * 
 
328
   * Example creating a string attribute belonging to the primary key
 
329
   * of the table it is created in:
 
330
   * @code
 
331
   *   MySchemaOp->createAttribute("Attr1",       // Attribute name
 
332
   *                               TupleKey,      // Belongs to primary key
 
333
   *                               8,             // Each character is 8 bits
 
334
   *                               12,            // Max 12 chars in string
 
335
   *                               String,        // Attribute if of type string
 
336
   *                              );
 
337
   * @endcode
 
338
   *
 
339
   * A <em>distribution key</em> is a set of attributes which are used
 
340
   * to distribute the tuples onto the NDB nodes.
 
341
   * A <em>distribution group</em> is a part (currently 16 bits) 
 
342
   * of an attribute used to distribute the tuples onto the NDB nodes.
 
343
   * The distribution key uses the NDB Cluster hashing function,
 
344
   * while the distribution group uses a simpler function.
 
345
   *
 
346
   * @param  aAttrName   Attribute name.  Should not be NULL.
 
347
   * @param  aTupleKey   This parameter specifies whether the 
 
348
   *                     attribute is part of the primary key or not.
 
349
   *                     Floats are not allowed in the primary key.
 
350
   *                     <br>
 
351
   *                     Legal values: NoKey, TupleKey
 
352
   * @param  aAttrSize   Specifies the size of the elements of the 
 
353
   *                     attribute.  (An attribute can consist
 
354
   *                     of an array of elements.)
 
355
   *                     <br>
 
356
   *                     Legal values: 8, 16, 32, 64 and 128 bits.
 
357
   * @param  aArraySize  Size of array.
 
358
   *                     <br>
 
359
   *                     Legal values:
 
360
   *                     0 = variable-sized array, 
 
361
   *                     1 = no array, and
 
362
   *                     2- = fixed size array.
 
363
   *                     <br>
 
364
   *                     <em>
 
365
   *                     Variable-sized array attributes are 
 
366
   *                     not yet supported.
 
367
   *                     </em>
 
368
   *                     <br>
 
369
   *                     There is no upper limit of the array size
 
370
   *                     for a single attribute. 
 
371
   * @param  aAttrType   The attribute type.
 
372
   *                     This is only of interest if calculations are 
 
373
   *                     made within NDB.
 
374
   *                     <br>
 
375
   *                     Legal values: UnSigned, Signed, Float, String
 
376
   * @param aStorageMode    Main memory based or disk based attribute.<br>
 
377
   *                     Legal values: MMBased, DiskBased
 
378
   *                     <br>
 
379
   *                     <em>
 
380
   *                     Disk-based attributes are not yet supported.
 
381
   *                     </em>
 
382
   * @param nullable     Set to true if NULL is a correct value for
 
383
   *                     the attribute.
 
384
   *                     <br>
 
385
   *                     Legal values: true, false
 
386
   * @param aStType      Obsolete since wl-2066
 
387
   * @param aDistributionKey    Sometimes it is preferable to use a subset
 
388
   *                            of the primary key as the distribution key. 
 
389
   *                            An example is TPC-C where it might be
 
390
   *                            good to use the warehouse id and district id 
 
391
   *                            as the distribution key. 
 
392
   *                            <br>
 
393
   *                            Locally in the fragments the full primary key 
 
394
   *                            will still be used with the hashing algorithm.
 
395
   *                            Set to 1 if this attribute is part of the 
 
396
   *                            distribution key.
 
397
   *                            All distribution key attributes must be 
 
398
   *                            defined before
 
399
   *                            any other attributes are defined.
 
400
   * @param aDistributionGroup    In other applications it is desirable to use 
 
401
   *                              only a part of an attribute to create the 
 
402
   *                              distribution key.
 
403
   *                              This is applicable for some telecom
 
404
   *                              applications.
 
405
   *                              <br>
 
406
   *                              In these situations one must provide how many 
 
407
   *                              bits of the attribute that is to
 
408
   *                              be used as the distribution hash value.
 
409
   *                              <br>
 
410
   *                              This provides some control to the
 
411
   *                              application of the distribution. 
 
412
   *                              It still needs to be part of a primary key
 
413
   *                              the attribute and must be defined as the 
 
414
   *                              first attribute.
 
415
   * @param  aDistributionGroupNoOfBits
 
416
   *                              Number of bits to use of the 
 
417
   *                              distribution group attribute in the
 
418
   *                              distribution hash value.
 
419
   *                              <br>
 
420
   *                              Currently, only 16 bits is supported. It will
 
421
   *                              always be the last 16 bits in the attribute
 
422
   *                              which is used for the distribution group.
 
423
   * @param aAutoIncrement        Set to autoincrement attribute.
 
424
   * @param aDefaultValue         Set a default value of attribute.
 
425
   *
 
426
   * @return Returns 0 when successful and returns -1 otherwise.
 
427
   ****************************************************************************/
 
428
  int createAttribute(const char* aAttrName,
 
429
                      KeyType aTupleKey = NoKey,
 
430
                      int aAttrSize = 32,
 
431
                      int aArraySize = 1,
 
432
                      AttrType aAttrType = UnSigned,
 
433
                      StorageMode aStorageMode = MMBased,
 
434
                      bool nullable = false,
 
435
                      int aStType= 0, // obsolete
 
436
                      int aDistributionKey = 0,
 
437
                      int aDistributionGroup = 0,
 
438
                      int aDistributionGroupNoOfBits = 16,
 
439
                      bool aAutoIncrement = false,
 
440
                      const char* aDefaultValue = 0);
 
441
 
 
442
  /**
 
443
   * @deprecated do not use!
 
444
   */
 
445
  int createAttribute(const char* aAttrName,
 
446
                      KeyType aTupleKey,
 
447
                      int aAttrSize,
 
448
                      int aArraySize,
 
449
                      AttrType aAttrType,
 
450
                      StorageMode aStorageMode,
 
451
                      NullAttributeType aNullAttr,
 
452
                      int aStType, // obsolete
 
453
                      int aDistributionKey = 0,
 
454
                      int aDistributionGroup = 0,
 
455
                      int aDistributionGroupNoOfBits = 16){
 
456
    return createAttribute(aAttrName,
 
457
                           aTupleKey,
 
458
                           aAttrSize,
 
459
                           aArraySize,
 
460
                           aAttrType,
 
461
                           aStorageMode,
 
462
                           aNullAttr == NullAttribute,
 
463
                           aStType,
 
464
                           aDistributionKey,
 
465
                           aDistributionGroup,
 
466
                           aDistributionGroupNoOfBits);
 
467
  }
 
468
 
 
469
  const NdbError & getNdbError() const;
 
470
 
 
471
protected:
 
472
 
 
473
/*****************************************************************************
 
474
 *   These are the methods used to create and delete the NdbOperation objects.
 
475
 ****************************************************************************/
 
476
                        NdbSchemaOp(Ndb* aNdb);
 
477
 
 
478
                        ~NdbSchemaOp();     
 
479
 
 
480
/******************************************************************************
 
481
 *      These methods are service routines used by the other NDBAPI classes.
 
482
 *****************************************************************************/
 
483
 
 
484
  void                  release();      // Release all memory connected
 
485
                                              // to the operations object.          
 
486
 
 
487
/****************************************************************************
 
488
 *      The methods below is the execution part of the NdbSchemaOp class. 
 
489
 *****************************************************************************/
 
490
 
 
491
  int sendRec();        
 
492
  int sendSignals(Uint32 aNodeId, bool HaveMutex);
 
493
 
 
494
  int init(NdbSchemaCon* aSchemaCon);
 
495
 
 
496
  /**************************************************************************
 
497
   * These are the private variables that are defined in the operation 
 
498
   * objects.
 
499
   **************************************************************************/
 
500
  Ndb*                  theNdb;         // Point back to the Ndb object.      
 
501
  NdbSchemaCon*         theSchemaCon;   // Point back to the connection object.
 
502
  
 
503
 
 
504
  class NdbDictionary::Table * m_currentTable;
 
505
};
 
506
 
 
507
 
 
508
/**
 
509
 * Get old attribute type from new type
 
510
 * 
 
511
 * NOTE! attrType is deprecated, use getType instead!
 
512
 *
 
513
 * @return Type of attribute: { Signed, UnSigned, Float,a String }
 
514
 */
 
515
inline 
 
516
AttrType 
 
517
convertColumnTypeToAttrType(NdbDictionary::Column::Type _type)
 
518
{      
 
519
  
 
520
  switch(_type){
 
521
  case NdbDictionary::Column::Bigint:
 
522
  case NdbDictionary::Column::Int:
 
523
    return Signed;
 
524
  case NdbDictionary::Column::Bigunsigned:
 
525
  case NdbDictionary::Column::Unsigned:
 
526
    return UnSigned;
 
527
  case NdbDictionary::Column::Float:
 
528
  case NdbDictionary::Column::Olddecimal:
 
529
  case NdbDictionary::Column::Olddecimalunsigned:
 
530
  case NdbDictionary::Column::Decimal:
 
531
  case NdbDictionary::Column::Decimalunsigned:
 
532
  case NdbDictionary::Column::Double:
 
533
    return Float;
 
534
  case NdbDictionary::Column::Char:
 
535
  case NdbDictionary::Column::Varchar:
 
536
  case NdbDictionary::Column::Binary:
 
537
  case NdbDictionary::Column::Varbinary:
 
538
    return String;
 
539
  default:
 
540
    return NoAttrTypeDef;
 
541
  }
 
542
}
 
543
#endif
 
544
 
 
545
#endif
 
546
 
 
547