1 /*
2  *  Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved.
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License, version 2.0,
6  *  as published by the Free Software Foundation.
7  *
8  *  This program is also distributed with certain software (including
9  *  but not limited to OpenSSL) that is licensed under separate terms,
10  *  as designated in a particular file or component or in included license
11  *  documentation.  The authors of MySQL hereby grant you an additional
12  *  permission to link the program and your derivative works with the
13  *  separately licensed software that they have included with MySQL.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License, version 2.0, for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23  */
24 
25 package com.mysql.clusterj.tie;
26 
27 import java.lang.reflect.Method;
28 import java.math.BigDecimal;
29 import java.math.BigInteger;
30 import java.math.RoundingMode;
31 import java.nio.ByteBuffer;
32 import java.nio.ByteOrder;
33 import java.nio.CharBuffer;
34 import java.nio.charset.CharacterCodingException;
35 import java.nio.charset.Charset;
36 import java.nio.charset.CharsetDecoder;
37 import java.nio.charset.CharsetEncoder;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.Calendar;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Set;
45 import java.util.TreeMap;
46 
47 import com.mysql.ndbjtie.mysql.CharsetMap;
48 import com.mysql.ndbjtie.mysql.CharsetMapConst;
49 import com.mysql.ndbjtie.mysql.Utils;
50 import com.mysql.ndbjtie.ndbapi.NdbErrorConst;
51 import com.mysql.ndbjtie.ndbapi.NdbRecAttr;
52 import com.mysql.clusterj.ClusterJDatastoreException;
53 import com.mysql.clusterj.ClusterJFatalInternalException;
54 import com.mysql.clusterj.ClusterJUserException;
55 import com.mysql.clusterj.core.store.Column;
56 import com.mysql.clusterj.core.util.I18NHelper;
57 import com.mysql.clusterj.core.util.Logger;
58 import com.mysql.clusterj.core.util.LoggerFactoryService;
59 import com.mysql.clusterj.tie.DbImpl.BufferManager;
60 
61 /** This class provides utility methods.
62  *
63  */
64 public class Utility {
65 
66     /** My message translator */
67     static final I18NHelper local = I18NHelper
68             .getInstance(Utility.class);
69 
70     /** My logger */
71     static final Logger logger = LoggerFactoryService.getFactory()
72             .getInstance(Utility.class);
73 
74     /** Standard Java charset */
75     static Charset charset = Charset.forName("windows-1252");
76 
77     static final long ooooooooooooooff = 0x00000000000000ffL;
78     static final long ooooooooooooffoo = 0x000000000000ff00L;
79     static final long ooooooooooffoooo = 0x0000000000ff0000L;
80     static final long ooooooooffoooooo = 0x00000000ff000000L;
81     static final long ooooooffoooooooo = 0x000000ff00000000L;
82     static final long ooooffoooooooooo = 0x0000ff0000000000L;
83     static final long ooffoooooooooooo = 0x00ff000000000000L;
84     static final long ffoooooooooooooo = 0xff00000000000000L;
85     static final long ooooooooffffffff = 0x00000000ffffffffL;
86     static final int ooooooff = 0x000000ff;
87     static final int ooooffff = 0x0000ffff;
88     static final int ooooffoo = 0x0000ff00;
89     static final int ooffoooo = 0x00ff0000;
90     static final int ooffffff = 0x00ffffff;
91     static final int ffoooooo = 0xff000000;
92 
93     static final char[] SPACE_PAD = new char[255];
94     static {
95         for (int i = 0; i < 255; ++i) {
96             SPACE_PAD[i] = ' ';
97         }
98     }
99 
100     static final byte[] ZERO_PAD = new byte[255];
101     static {
102         for (int i = 0; i < 255; ++i) {
103             ZERO_PAD[i] = (byte)0;
104         }
105     }
106 
107     static final byte[] BLANK_PAD = new byte[255];
108     static {
109         for (int i = 0; i < 255; ++i) {
110             BLANK_PAD[i] = (byte)' ';
111         }
112     }
113 
114     static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
115 
116     static int MAX_MEDIUMINT_VALUE = (int)(Math.pow(2, 23) - 1);
117     static int MAX_MEDIUMUNSIGNED_VALUE = (int)(Math.pow(2, 24) - 1);
118     static int MIN_MEDIUMINT_VALUE = (int) (- Math.pow(2, 23));
119 
120     /** Scratch buffer pool used for decimal conversions; 65 digits of precision, sign, decimal, null terminator */
121     static final FixedByteBufferPoolImpl decimalByteBufferPool = new FixedByteBufferPoolImpl(68, "Decimal Pool");
122 
123     /* Error codes that are not severe, and simply reflect expected conditions */
124     private static Set<Integer> NonSevereErrorCodes = new HashSet<Integer>();
125 
126     public static final int SET_NOT_NULL_TO_NULL = 4203;
127     public static final int INDEX_NOT_FOUND = 4243;
128     public static final int ROW_NOT_FOUND = 626;
129     public static final int DUPLICATE_PRIMARY_KEY = 630;
130     public static final int DUPLICATE_UNIQUE_KEY = 893;
131     public static final int FOREIGN_KEY_NO_PARENT = 255;
132     public static final int FOREIGN_KEY_REFERENCED_ROW_EXISTS = 256;
133 
134     static {
135         NonSevereErrorCodes.add(SET_NOT_NULL_TO_NULL); // Attempt to set a NOT NULL attribute to NULL
136         NonSevereErrorCodes.add(INDEX_NOT_FOUND); // Index not found
137         NonSevereErrorCodes.add(ROW_NOT_FOUND); // Tuple did not exist
138         NonSevereErrorCodes.add(DUPLICATE_PRIMARY_KEY); // Duplicate primary key on insert
139         NonSevereErrorCodes.add(DUPLICATE_UNIQUE_KEY); // Duplicate unique key on insert
140         NonSevereErrorCodes.add(FOREIGN_KEY_NO_PARENT); // Foreign key violation; no parent exists
141         NonSevereErrorCodes.add(FOREIGN_KEY_REFERENCED_ROW_EXISTS); // Foreign key violation; referenced row exists
142     }
143 
144     // TODO: this is intended to investigate a class loader issue with Sparc java
145     // The idea is to force loading the CharsetMap native class prior to calling the static create method
146     // First, make sure that the native library is loaded because CharsetMap depends on it
147     static {
148         ClusterConnectionServiceImpl.loadSystemLibrary("ndbclient");
149     }
150     static Class<?> charsetMapClass = loadClass("com.mysql.ndbjtie.mysql.CharsetMap");
loadClass(String className)151     static Class<?> loadClass(String className) {
152         try {
153             return Class.forName(className);
154         } catch (ClassNotFoundException e) {
155             throw new ClusterJUserException(local.message("ERR_Loading_Native_Class", className), e);
156         }
157     }
158 
159     // TODO: change this to a weak reference so we can call delete on it when not needed
160     /** Note that mysql refers to charset number and charset name, but the number is
161     * actually a collation number. The CharsetMap interface thus has methods like
162     * getCharsetNumber(String charsetName) but what is returned is actually a collation number.
163     */
164     static CharsetMap charsetMap = createCharsetMap();
165 
166     // TODO: this is intended to investigate a class loader issue with Sparc java
167     // The idea is to create the CharsetMap create method in a try/catch block to report the exact error
createCharsetMap()168     static CharsetMap createCharsetMap() {
169         StringBuilder builder = new StringBuilder();
170         CharsetMap result = null;
171         try {
172             return CharsetMap.create();
173         } catch (Throwable t1) {
174             builder.append("CharsetMap.create() threw " + t1.getClass().getName() + ":" + t1.getMessage());
175             try {
176                 Method charsetMapCreateMethod = charsetMapClass.getMethod("create", (Class[])null);
177                 result = (CharsetMap)charsetMapCreateMethod.invoke(null, (Object[])null);
178                 builder.append("charsetMapCreateMethod.invoke() succeeded:" + result);
179             } catch (Throwable t2) {
180                 builder.append("charsetMapCreateMethod.invoke() threw " + t2.getClass().getName() + ":" + t2.getMessage());
181             }
182             throw new ClusterJUserException(builder.toString());
183         }
184     }
185 
186     /** The maximum mysql collation (charset) number. This is hard coded in <mysql>/include/my_sys.h */
187     static int MAXIMUM_MYSQL_COLLATION_NUMBER = 256;
188 
189     /** The mysql collation number for the standard charset */
190     static int collationLatin1 = charsetMap.getCharsetNumber("latin1");
191 
192     /** The mysql collation number for UTF16 */
193     static protected final int collationUTF16 = charsetMap.getUTF16CharsetNumber();
194 
195     /** The mysql charset map */
getCharsetMap()196     public static CharsetMap getCharsetMap() {
197         return charsetMap;
198     }
199 
200     /** The map of charset name to collations that share the charset name */
201     private static Map<String, int[]> collationPeersMap = new TreeMap<String, int[]>();
202 
203     /** The ClusterJ charset converter for all multibyte charsets */
204     private static CharsetConverter charsetConverterMultibyte = new MultiByteCharsetConverter();
205 
206     /** Charset converters */
207     private static CharsetConverter[] charsetConverters =
208         new CharsetConverter[MAXIMUM_MYSQL_COLLATION_NUMBER + 1];
209 
210     /** Initialize the the array of charset converters and the map of known collations that share the same charset */
211     static {
212         Map<String, List<Integer>> workingCollationPeersMap = new TreeMap<String, List<Integer>>();
213         for (int collation = 1; collation <= MAXIMUM_MYSQL_COLLATION_NUMBER; ++collation) {
214             String mysqlName = charsetMap.getMysqlName(collation);
215             if (mysqlName != null) {
216                 if ((isMultibyteCollation(collation))) {
217                     // multibyte collations all use the multibyte charset converter
218                     charsetConverters[collation] = charsetConverterMultibyte;
219                 } else {
220                     // find out if this charset name is already used by another (peer) collation
221                     List<Integer> collations = workingCollationPeersMap.get(mysqlName);
222                     if (collations == null) {
223                         // this is the first collation to use this charset name
224                         collations = new ArrayList<Integer>(8);
225                         collations.add(collation);
workingCollationPeersMap.put(mysqlName, collations)226                         workingCollationPeersMap.put(mysqlName, collations);
227                     } else {
228                         // add this collation to the list of (peer) collations
229                         collations.add(collation);
230                     }
231                 }
232             }
233         }
234 
235         for (Map.Entry<String, List<Integer>> workingCollationPeers: workingCollationPeersMap.entrySet()) {
236             String mysqlName = workingCollationPeers.getKey();
237             List<Integer> collations = workingCollationPeers.getValue();
238             int[] collationArray = new int[collations.size()];
239             int i = 0;
240             for (Integer collation: collations) {
241                 collationArray[i++] = collation;
242             }
collationPeersMap.put(mysqlName, collationArray)243             collationPeersMap.put(mysqlName, collationArray);
244         }
245         if (logger.isDetailEnabled()) {
246             for (Map.Entry<String, int[]> collationEntry: collationPeersMap.entrySet()) {
247                 logger.detail("Utility collationMap " + collationEntry.getKey()
248                         + " collations : " + Arrays.toString(collationEntry.getValue()));
249             }
250         }
251         // initialize the charset converter for latin1 and its peer collations (after peers are known)
252         addCollation(collationLatin1);
253     }
254 
255     /** Determine if the exception is retriable
256      * @param ex the exception
257      * @return if the status is retriable
258      */
isRetriable(ClusterJDatastoreException ex)259     public static boolean isRetriable(ClusterJDatastoreException ex) {
260         return NdbErrorConst.Status.TemporaryError == ex.getStatus();
261     }
262 
263     private final static EndianManager endianManager = ByteOrder.BIG_ENDIAN.equals(ByteOrder.nativeOrder())?
264         /*
265          * Big Endian algorithms to convert NdbRecAttr buffer into primitive types
266          */
267         new EndianManager() {
268 
269         public boolean getBoolean(Column storeColumn, NdbRecAttr ndbRecAttr) {
270             switch (storeColumn.getType()) {
271                 case Bit:
272                     return ndbRecAttr.int32_value() == 1;
273                 case Tinyint:
274                     return ndbRecAttr.int8_value() == 1;
275                 default:
276                     throw new ClusterJUserException(
277                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "boolean"));
278             }
279         }
280 
281         public boolean getBoolean(Column storeColumn, int value) {
282             switch (storeColumn.getType()) {
283                 case Bit:
284                     return value == 1;
285                 case Tinyint:
286                     // the value is stored in the top 8 bits
287                     return (value >>> 24) == 1;
288                 default:
289                     throw new ClusterJUserException(
290                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "boolean"));
291             }
292         }
293 
294         public byte getByte(Column storeColumn, NdbRecAttr ndbRecAttr) {
295             switch (storeColumn.getType()) {
296                 case Bit:
297                     return (byte)ndbRecAttr.int32_value();
298                 case Tinyint:
299                 case Year:
300                     return ndbRecAttr.int8_value();
301                 default:
302                     throw new ClusterJUserException(
303                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "byte"));
304             }
305         }
306 
307         public short getShort(Column storeColumn, NdbRecAttr ndbRecAttr) {
308             switch (storeColumn.getType()) {
309                 case Bit:
310                     return (short)ndbRecAttr.int32_value();
311                 case Smallint:
312                     return ndbRecAttr.short_value();
313                 default:
314                     throw new ClusterJUserException(
315                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
316             }
317         }
318 
319         public int getInt(Column storeColumn, NdbRecAttr ndbRecAttr) {
320             switch (storeColumn.getType()) {
321                 case Bit:
322                 case Int:
323                 case Timestamp:
324                     return ndbRecAttr.int32_value();
325                 case Date:
326                 case Mediumunsigned:
327                     return ndbRecAttr.u_medium_value();
328                 case Time:
329                 case Mediumint:
330                     return ndbRecAttr.medium_value();
331                 default:
332                     throw new ClusterJUserException(
333                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "int"));
334             }
335         }
336 
337         public int getInt(Column storeColumn, int value) {
338             int result = 0;
339             switch (storeColumn.getType()) {
340                 case Bit:
341                 case Int:
342                 case Timestamp:
343                     return value;
344                 case Date:
345                     // the unsigned value is stored in the top 3 bytes
346                     return value >>> 8;
347                 case Time:
348                     // the signed value is stored in the top 3 bytes
349                     return value >> 8;
350                 case Mediumint:
351                     // the three high order bytes are the little endian representation
352                     // the original is zzyyax00 and the result is aaaxyyzz
353                     result |= (value & ffoooooo) >>> 24;
354                     result |= (value & ooffoooo) >>> 8;
355                     // the ax byte is signed, so shift left 16 and arithmetic shift right 8
356                     result |= ((value & ooooffoo) << 16) >> 8;
357                     return result;
358                 case Mediumunsigned:
359                     // the three high order bytes are the little endian representation
360                     // the original is zzyyxx00 and the result is 00xxyyzz
361                     result |= (value & ffoooooo) >>> 24;
362                     result |= (value & ooffoooo) >>> 8;
363                     result |= (value & ooooffoo) << 8;
364                     return result;
365                 default:
366                     throw new ClusterJUserException(
367                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "int"));
368             }
369         }
370 
371         public long getLong(Column storeColumn, NdbRecAttr ndbRecAttr) {
372             switch (storeColumn.getType()) {
373                 case Bit:
374                     long rawValue = ndbRecAttr.int64_value();
375                     return (rawValue >>> 32) | (rawValue << 32);
376                 case Mediumint:
377                     return ndbRecAttr.medium_value();
378                 case Mediumunsigned:
379                     return ndbRecAttr.u_medium_value();
380                 case Bigint:
381                 case Bigunsigned:
382                     return ndbRecAttr.int64_value();
383                 case Datetime:
384                     return unpackDatetime(ndbRecAttr.int64_value());
385                 case Timestamp:
386                     return (((long)ndbRecAttr.int32_value()) & ooooooooffffffff) * 1000L;
387                 case Date:
388                     return unpackDate(ndbRecAttr.u_medium_value());
389                 case Time:
390                     return unpackTime(ndbRecAttr.medium_value());
391                 default:
392                     throw new ClusterJUserException(
393                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
394             }
395         }
396 
397         /** Put the higher order three bytes of the input value into the result as long value.
398          * Also preserve the sign of the MSB while shifting
399          * @param byteBuffer the byte buffer
400          * @param value the input value
401          */
402         public long get3ByteLong(long value) {
403             // the three high order bytes are the little endian representation
404             // the original is zzyyxx0000000000 and the result is 0000000000xxyyzz
405             long result = 0L;
406             result |= (value & ffoooooooooooooo) >>> 56;
407             result |= (value & ooffoooooooooooo) >>> 40;
408             // the xx byte is signed, so shift left 16 and arithmetic shift right 40
409             result |= ((value & ooooffoooooooooo) << 16) >> 40;
410             return result;
411         }
412 
413         public long getLong(Column storeColumn, long value) {
414             switch (storeColumn.getType()) {
415                 case Bit:
416                     // the data is stored as two int values
417                     return (value >>> 32) | (value << 32);
418                 case Mediumint:
419                     return get3ByteLong(value);
420                 case Mediumunsigned:
421                     // the three high order bytes are the little endian representation
422                     // the original is zzyyxx0000000000 and the result is 0000000000xxyyzz
423                     long result = 0L;
424                     result |= (value & ffoooooooooooooo) >>> 56;
425                     result |= (value & ooffoooooooooooo) >>> 40;
426                     result |= (value & ooooffoooooooooo) >>> 24;
427                     return result;
428                 case Bigint:
429                 case Bigunsigned:
430                     return value;
431                 case Datetime:
432                     return unpackDatetime(value);
433                 case Timestamp:
434                     return (value >> 32) * 1000L;
435                 case Date:
436                     long packedDate = get3ByteLong(value);
437                     return unpackDate((int)packedDate);
438                 case Time:
439                     long packedTime = get3ByteLong(value);
440                     return unpackTime((int)packedTime);
441                 case Datetime2:
442                     return unpackDatetime2(storeColumn.getPrecision(), value);
443                 case Timestamp2:
444                     return unpackTimestamp2(storeColumn.getPrecision(), value);
445                 case Time2:
446                     return unpackTime2(storeColumn.getPrecision(), value);
447                 default:
448                     throw new ClusterJUserException(
449                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
450             }
451         }
452 
453         /** Put the low order three bytes of the input value into the ByteBuffer as a medium_value.
454          * The format for medium value is always little-endian even on big-endian architectures.
455          * Do not flip the buffer, as the caller will do that if needed.
456          * @param byteBuffer the byte buffer
457          * @param value the input value
458          */
459         public void put3byteInt(ByteBuffer byteBuffer, int value) {
460             byteBuffer.put((byte)(value));
461             byteBuffer.put((byte)(value >> 8));
462             byteBuffer.put((byte)(value >> 16));
463         }
464 
465         public void convertValue(ByteBuffer result, Column storeColumn, byte value) {
466             switch (storeColumn.getType()) {
467                 case Bit:
468                     // bit fields are always stored in an int32
469                     result.order(ByteOrder.BIG_ENDIAN);
470                     result.putInt(value & 0xff);
471                     result.flip();
472                     return;
473                 case Tinyint:
474                 case Year:
475                     result.put(value);
476                     result.flip();
477                     return;
478                 default:
479                     throw new ClusterJUserException(local.message(
480                             "ERR_Unsupported_Mapping", storeColumn.getType(), "byte"));
481             }
482         }
483 
484         public ByteBuffer convertValue(Column storeColumn, byte value) {
485             ByteBuffer result;
486             switch (storeColumn.getType()) {
487                 case Bit:
488                     result = ByteBuffer.allocateDirect(4);
489                     // bit fields are always stored in an int32
490                     result.order(ByteOrder.BIG_ENDIAN);
491                     result.putInt(value & 0xff);
492                     result.flip();
493                     return result;
494                 case Tinyint:
495                 case Year:
496                     result = ByteBuffer.allocateDirect(1);
497                     result.put(value);
498                     result.flip();
499                     return result;
500                 default:
501                     throw new ClusterJUserException(local.message(
502                             "ERR_Unsupported_Mapping", storeColumn.getType(), "byte"));
503             }
504         }
505 
506         public void convertValue(ByteBuffer result, Column storeColumn, short value) {
507             switch (storeColumn.getType()) {
508                 case Bit:
509                     // bit fields are always stored in an int32
510                     result.order(ByteOrder.BIG_ENDIAN);
511                     result.putInt(value & 0xffff);
512                     result.flip();
513                     return;
514                 case Smallint:
515                     result.order(ByteOrder.BIG_ENDIAN);
516                     result.putShort(value);
517                     result.flip();
518                     return;
519                 default:
520                     throw new ClusterJUserException(local.message(
521                             "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
522             }
523         }
524 
525         public ByteBuffer convertValue(Column storeColumn, short value) {
526             ByteBuffer result;
527             switch (storeColumn.getType()) {
528                 case Bit:
529                     result = ByteBuffer.allocateDirect(4);
530                     // bit fields are always stored in an int32
531                     result.order(ByteOrder.BIG_ENDIAN);
532                     result.putInt(value & 0xffff);
533                     result.flip();
534                     return result;
535                 case Smallint:
536                     result = ByteBuffer.allocateDirect(2);
537                     result.order(ByteOrder.BIG_ENDIAN);
538                     result.putShort(value);
539                     result.flip();
540                     return result;
541                 default:
542                     throw new ClusterJUserException(local.message(
543                             "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
544             }
545         }
546 
547         public ByteBuffer convertValue(Column storeColumn, int value) {
548             ByteBuffer result = ByteBuffer.allocateDirect(4);
549             convertValue(result, storeColumn, value);
550             return result;
551         }
552 
553         public void convertValue(ByteBuffer result, Column storeColumn, int value) {
554             switch (storeColumn.getType()) {
555                 case Bit:
556                 case Int:
557                     result.order(ByteOrder.BIG_ENDIAN);
558                     result.putInt(value);
559                     result.flip();
560                     return;
561                 case Mediumint:
562                     if (value > MAX_MEDIUMINT_VALUE || value < MIN_MEDIUMINT_VALUE){
563                         throw new ClusterJUserException(local.message(
564                                 "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
565                     }
566                     result.order(ByteOrder.LITTLE_ENDIAN);
567                     put3byteInt(result, value);
568                     result.flip();
569                     return;
570                 case Mediumunsigned:
571                     if (value > MAX_MEDIUMUNSIGNED_VALUE || value < 0){
572                         throw new ClusterJUserException(local.message(
573                                 "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
574                     }
575                     result.order(ByteOrder.LITTLE_ENDIAN);
576                     put3byteInt(result, value);
577                     result.flip();
578                     return;
579                 default:
580                     throw new ClusterJUserException(local.message(
581                             "ERR_Unsupported_Mapping", storeColumn.getType(), "int"));
582             }
583         }
584 
585         public int convertIntValueForStorage(Column storeColumn, int value) {
586             int result = 0;
587             switch (storeColumn.getType()) {
588                 case Bit:
589                 case Int:
590                     return value;
591                 case Mediumint:
592                     if (value > MAX_MEDIUMINT_VALUE || value < MIN_MEDIUMINT_VALUE){
593                         throw new ClusterJUserException(local.message(
594                                 "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
595                     }
596                     // the high order bytes are the little endian representation
597                     // the the original is 00xxyyzz and the result is zzyyxx00
598                     result |= (value & ooooooff) << 24;
599                     result |= (value & ooooffoo) << 8;
600                     result |= (value & ooffoooo) >> 8;
601                     return result;
602                 case Mediumunsigned:
603                     if (value > MAX_MEDIUMUNSIGNED_VALUE || value < 0){
604                         throw new ClusterJUserException(local.message(
605                                 "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
606                     }
607                     // the high order bytes are the little endian representation
608                     // the original is 00xxyyzz and the result is zzyyxx00
609                     result |= (value & ooooooff) << 24;
610                     result |= (value & ooooffoo) << 8;
611                     result |= (value & ooffoooo) >> 8;
612                     return result;
613                 default:
614                     throw new ClusterJUserException(local.message(
615                             "ERR_Unsupported_Mapping", storeColumn.getType(), "int"));
616             }
617         }
618 
619         public ByteBuffer convertValue(Column storeColumn, long value) {
620             ByteBuffer result = ByteBuffer.allocateDirect(8);
621             return convertValue(storeColumn, value, result);
622         }
623 
624         public void convertValue(ByteBuffer result, Column storeColumn, long value) {
625             convertValue(storeColumn, value, result);
626         }
627 
628         public ByteBuffer convertValue(Column storeColumn, long value, ByteBuffer result) {
629             switch (storeColumn.getType()) {
630                 case Bit:
631                     // bit fields are stored in two int32 fields
632                     result.order(ByteOrder.BIG_ENDIAN);
633                     result.putInt((int)((value)));
634                     result.putInt((int)((value >>> 32)));
635                     result.flip();
636                     return result;
637                 case Mediumint:
638                     if (value > MAX_MEDIUMINT_VALUE || value < MIN_MEDIUMINT_VALUE){
639                         throw new ClusterJUserException(local.message(
640                             "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
641                     }
642                     result.order(ByteOrder.LITTLE_ENDIAN);
643                     put3byteInt(result, (int)value);
644                     result.flip();
645                     return result;
646                 case Mediumunsigned:
647                     if (value > MAX_MEDIUMUNSIGNED_VALUE || value < 0){
648                         throw new ClusterJUserException(local.message(
649                             "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
650                     }
651                     result.order(ByteOrder.LITTLE_ENDIAN);
652                     put3byteInt(result, (int)value);
653                     result.flip();
654                     return result;
655                 case Bigint:
656                 case Bigunsigned:
657                     result.order(ByteOrder.BIG_ENDIAN);
658                     result.putLong(value);
659                     result.flip();
660                     return result;
661                 case Date:
662                     result.order(ByteOrder.LITTLE_ENDIAN);
663                     put3byteInt(result, packDate(value));
664                     result.flip();
665                     return result;
666                 case Datetime:
667                     result.order(ByteOrder.BIG_ENDIAN);
668                     result.putLong(packDatetime(value));
669                     result.flip();
670                     return result;
671                 case Time:
672                     result.order(ByteOrder.LITTLE_ENDIAN);
673                     put3byteInt(result, packTime(value));
674                     result.flip();
675                     return result;
676                 case Timestamp:
677                     result.order(ByteOrder.BIG_ENDIAN);
678                     result.putInt((int)(value/1000L));
679                     result.flip();
680                     return result;
681                 case Datetime2:
682                     result.order(ByteOrder.BIG_ENDIAN);
683                     result.putLong(packDatetime2(storeColumn.getPrecision(), value));
684                     result.flip();
685                     return result;
686                 case Time2:
687                     result.order(ByteOrder.BIG_ENDIAN);
688                     result.putLong(packTime2(storeColumn.getPrecision(), value));
689                     result.flip();
690                     return result;
691                 case Timestamp2:
692                     result.order(ByteOrder.BIG_ENDIAN);
693                     result.putLong(packTimestamp2(storeColumn.getPrecision(), value));
694                     result.flip();
695                     return result;
696                 default:
697                     throw new ClusterJUserException(local.message(
698                             "ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
699             }
700         }
701 
702         /** Put the low order three bytes of the input value into the long as a medium_value.
703          * The format for medium value is always little-endian even on big-endian architectures.
704          * @param value the input value
705          */
706         public long put3byteLong(long value) {
707             // the high order bytes are the little endian representation
708             // the original is 0000000000xxyyzz and the result is zzyyxx0000000000
709             long result = 0L;
710             result |= (value & ooooooff) << 56;
711             result |= (value & ooooffoo) << 40;
712             result |= (value & ooffoooo) << 24;
713             return result;
714         }
715 
716         public long convertLongValueForStorage(Column storeColumn, long value) {
717             long result = 0L;
718             switch (storeColumn.getType()) {
719                 case Bit:
720                     // bit fields are stored in two int32 fields
721                     result |= (value >>> 32);
722                     result |= (value << 32);
723                     return result;
724                 case Bigint:
725                 case Bigunsigned:
726                     return value;
727                 case Mediumint:
728                     if (value > MAX_MEDIUMINT_VALUE || value < MIN_MEDIUMINT_VALUE){
729                         throw new ClusterJUserException(local.message(
730                             "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
731                     }
732                     return put3byteLong(value);
733                 case Mediumunsigned:
734                     if (value > MAX_MEDIUMUNSIGNED_VALUE || value < 0){
735                         throw new ClusterJUserException(local.message(
736                             "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
737                     }
738                     return put3byteLong(value);
739                 case Date:
740                     long packDate = packDate(value);
741                     return put3byteLong(packDate);
742                 case Datetime:
743                     return packDatetime(value);
744                 case Time:
745                     long packTime = packTime(value);
746                     return put3byteLong(packTime);
747                 case Timestamp:
748                     // timestamp is an int so put the value into the high bytes
749                     // the original is 00000000tttttttt and the result is tttttttt00000000
750                     return (value/1000L) << 32;
751                 case Datetime2:
752                     // value is in milliseconds since the epoch
753                     return packDatetime2(storeColumn.getPrecision(), value);
754                 case Time2:
755                     // value is in milliseconds since the epoch
756                     return packTime2(storeColumn.getPrecision(), value);
757                 case Timestamp2:
758                     // value is in milliseconds since the epoch
759                     return packTimestamp2(storeColumn.getPrecision(), value);
760                 default:
761                     throw new ClusterJUserException(local.message(
762                             "ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
763             }
764         }
765 
766         public int convertByteValueForStorage(Column storeColumn, byte value) {
767             switch (storeColumn.getType()) {
768                 case Bit:
769                     // bit fields are always stored in an int32
770                     return value & ooooooff;
771                 case Tinyint:
772                 case Year:
773                     // other byte values are stored in the high byte of an int
774                     return value << 24;
775                 default:
776                     throw new ClusterJUserException(local.message(
777                             "ERR_Unsupported_Mapping", storeColumn.getType(), "byte"));
778             }
779         }
780 
781         public int convertShortValueForStorage(Column storeColumn, short value) {
782             switch (storeColumn.getType()) {
783                 case Bit:
784                     // bit fields are always stored in an int32
785                     return value & ooooffff;
786                 case Smallint:
787                     // short values are in the top 16 bits of an int
788                     return value << 16;
789                 default:
790                     throw new ClusterJUserException(local.message(
791                             "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
792             }
793         }
794 
795         public long convertLongValueFromStorage(Column storeColumn,
796                 long fromStorage) {
797             // TODO Auto-generated method stub
798             return 0;
799         }
800 
801     }:
802         /*
803          * Little Endian algorithms to convert NdbRecAttr buffer into primitive types
804          */
805         new EndianManager() {
806 
807         public boolean getBoolean(Column storeColumn, NdbRecAttr ndbRecAttr) {
808             switch (storeColumn.getType()) {
809                 case Bit:
810                     return ndbRecAttr.int32_value() == 1;
811                 case Tinyint:
812                     return ndbRecAttr.int8_value() == 1;
813                 default:
814                     throw new ClusterJUserException(
815                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "boolean"));
816             }
817         }
818 
819         public boolean getBoolean(Column storeColumn, int value) {
820             switch (storeColumn.getType()) {
821                 case Bit:
822                 case Tinyint:
823                     return value == 1;
824                 default:
825                     throw new ClusterJUserException(
826                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "boolean"));
827             }
828         }
829 
830         public byte getByte(Column storeColumn, NdbRecAttr ndbRecAttr) {
831             switch (storeColumn.getType()) {
832                 case Bit:
833                 case Tinyint:
834                 case Year:
835                     return ndbRecAttr.int8_value();
836                 default:
837                     throw new ClusterJUserException(
838                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "byte"));
839             }
840         }
841 
842         public short getShort(Column storeColumn, NdbRecAttr ndbRecAttr) {
843             switch (storeColumn.getType()) {
844                 case Bit:
845                 case Smallint:
846                     return ndbRecAttr.short_value();
847                 default:
848                     throw new ClusterJUserException(
849                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
850             }
851         }
852 
853         public int getInt(Column storeColumn, NdbRecAttr ndbRecAttr) {
854             switch (storeColumn.getType()) {
855                 case Bit:
856                 case Int:
857                 case Timestamp:
858                     return ndbRecAttr.int32_value();
859                 case Date:
860                 case Mediumunsigned:
861                     return ndbRecAttr.u_medium_value();
862                 case Time:
863                 case Mediumint:
864                     return ndbRecAttr.medium_value();
865                 default:
866                     throw new ClusterJUserException(
867                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "int"));
868             }
869         }
870 
871         public int getInt(Column storeColumn, int value) {
872             switch (storeColumn.getType()) {
873                 case Bit:
874                 case Int:
875                 case Timestamp:
876                     return value;
877                 case Date:
878                 case Mediumunsigned:
879                     return value & ooffffff;
880                 case Time:
881                 case Mediumint:
882                     // propagate the sign bit from 3 byte medium_int
883                     return (value << 8) >> 8;
884                 default:
885                     throw new ClusterJUserException(
886                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "int"));
887             }
888         }
889 
890         public long getLong(Column storeColumn, NdbRecAttr ndbRecAttr) {
891             switch (storeColumn.getType()) {
892                 case Bigint:
893                 case Bigunsigned:
894                 case Bit:
895                     return ndbRecAttr.int64_value();
896                 case Mediumint:
897                     return ndbRecAttr.medium_value();
898                 case Mediumunsigned:
899                     return ndbRecAttr.u_medium_value();
900                 case Datetime:
901                     return unpackDatetime(ndbRecAttr.int64_value());
902                 case Timestamp:
903                     return ndbRecAttr.int32_value() * 1000L;
904                 case Date:
905                     return unpackDate(ndbRecAttr.int32_value());
906                 case Time:
907                     return unpackTime(ndbRecAttr.int32_value());
908                 default:
909                     throw new ClusterJUserException(
910                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
911             }
912         }
913 
914         public long getLong(Column storeColumn, long value) {
915             switch (storeColumn.getType()) {
916                 case Bigint:
917                 case Bigunsigned:
918                 case Mediumint:
919                 case Mediumunsigned:
920                 case Bit:
921                     return value;
922                 case Datetime:
923                     return unpackDatetime(value);
924                 case Timestamp:
925                     return value * 1000L;
926                 case Date:
927                     return unpackDate((int)(value));
928                 case Time:
929                     return unpackTime((int)(value));
930                 case Datetime2:
931                     // datetime2 is stored in big endian format so need to swap the input
932                     return unpackDatetime2(storeColumn.getPrecision(), swap(value));
933                 case Timestamp2:
934                     // timestamp2 is stored in big endian format so need to swap the input
935                     return unpackTimestamp2(storeColumn.getPrecision(), swap(value));
936                 case Time2:
937                     // time2 is stored in big endian format so need to swap the input
938                     return unpackTime2(storeColumn.getPrecision(), swap(value));
939                 default:
940                     throw new ClusterJUserException(
941                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
942             }
943         }
944 
945         /** Put the low order three bytes of the input value into the ByteBuffer as a medium_value.
946          * The format for medium value is always little-endian even on big-endian architectures.
947          * Do not flip the buffer, as the caller will do that if needed.
948          * @param byteBuffer the byte buffer
949          * @param value the input value
950          */
951         public void put3byteInt(ByteBuffer byteBuffer, int value) {
952             byteBuffer.putInt(value);
953             byteBuffer.limit(3);
954         }
955 
956         public void convertValue(ByteBuffer result, Column storeColumn, byte value) {
957             switch (storeColumn.getType()) {
958                 case Bit:
959                     // bit fields are always stored as int32
960                     result.order(ByteOrder.nativeOrder());
961                     result.putInt(value & 0xff);
962                     result.flip();
963                     return;
964                 case Tinyint:
965                 case Year:
966                     result.order(ByteOrder.nativeOrder());
967                     result.put(value);
968                     result.flip();
969                     return;
970                 default:
971                     throw new ClusterJUserException(local.message(
972                             "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
973             }
974         }
975 
976         public ByteBuffer convertValue(Column storeColumn, byte value) {
977             ByteBuffer result;
978             switch (storeColumn.getType()) {
979                 case Bit:
980                     // bit fields are always stored as int32
981                     result = ByteBuffer.allocateDirect(4);
982                     result.order(ByteOrder.nativeOrder());
983                     result.putInt(value & 0xff);
984                     result.flip();
985                     return result;
986                 case Tinyint:
987                 case Year:
988                     result = ByteBuffer.allocateDirect(1);
989                     result.order(ByteOrder.nativeOrder());
990                     result.put(value);
991                     result.flip();
992                     return result;
993                 default:
994                     throw new ClusterJUserException(local.message(
995                             "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
996             }
997         }
998 
999         public ByteBuffer convertValue(Column storeColumn, short value) {
1000             ByteBuffer result = ByteBuffer.allocateDirect(2);
1001             convertValue(result, storeColumn, value);
1002             return result;
1003         }
1004 
1005         public void convertValue(ByteBuffer result, Column storeColumn, short value) {
1006             switch (storeColumn.getType()) {
1007                 case Bit:
1008                 case Smallint:
1009                     result.order(ByteOrder.nativeOrder());
1010                     result.putShort(value);
1011                     result.flip();
1012                     return;
1013                 default:
1014                     throw new ClusterJUserException(local.message(
1015                             "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
1016             }
1017         }
1018 
1019         public ByteBuffer convertValue(Column storeColumn, int value) {
1020             ByteBuffer result = ByteBuffer.allocateDirect(4);
1021             convertValue(result, storeColumn, value);
1022             return result;
1023         }
1024 
1025         public void convertValue(ByteBuffer result, Column storeColumn, int value) {
1026             switch (storeColumn.getType()) {
1027                 case Bit:
1028                 case Int:
1029                     result.order(ByteOrder.nativeOrder());
1030                     result.putInt(value);
1031                     result.flip();
1032                     return;
1033                 case Mediumint:
1034                     if (value > MAX_MEDIUMINT_VALUE || value < MIN_MEDIUMINT_VALUE){
1035                         throw new ClusterJUserException(local.message(
1036                                 "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
1037                     }
1038                     result.order(ByteOrder.LITTLE_ENDIAN);
1039                     put3byteInt(result, value);
1040                     result.flip();
1041                     return;
1042                 case Mediumunsigned:
1043                     if (value > MAX_MEDIUMUNSIGNED_VALUE || value < 0){
1044                         throw new ClusterJUserException(local.message(
1045                                 "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
1046                     }
1047                     result.order(ByteOrder.LITTLE_ENDIAN);
1048                     put3byteInt(result, value);
1049                     result.flip();
1050                     return;
1051                 default:
1052                     throw new ClusterJUserException(local.message(
1053                             "ERR_Unsupported_Mapping", storeColumn.getType(), "int"));
1054             }
1055         }
1056 
1057         public int convertIntValueForStorage(Column storeColumn, int value) {
1058             switch (storeColumn.getType()) {
1059                 case Bit:
1060                 case Int:
1061                     return value;
1062                 case Mediumint:
1063                     if (value > MAX_MEDIUMINT_VALUE || value < MIN_MEDIUMINT_VALUE){
1064                         throw new ClusterJUserException(local.message(
1065                             "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
1066                     }
1067                     return value;
1068                 case Mediumunsigned:
1069                     if (value > MAX_MEDIUMUNSIGNED_VALUE || value < 0){
1070                         throw new ClusterJUserException(local.message(
1071                             "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
1072                     }
1073                     return value;
1074                 default:
1075                     throw new ClusterJUserException(local.message(
1076                             "ERR_Unsupported_Mapping", storeColumn.getType(), "int"));
1077             }
1078         }
1079 
1080         public void convertValue(ByteBuffer result, Column storeColumn, long value) {
1081             convertValue(storeColumn, value, result);
1082         }
1083 
1084         public ByteBuffer convertValue(Column storeColumn, long value) {
1085             ByteBuffer result = ByteBuffer.allocateDirect(8);
1086             return convertValue(storeColumn, value, result);
1087         }
1088 
1089         public ByteBuffer convertValue(Column storeColumn, long value, ByteBuffer result) {
1090             switch (storeColumn.getType()) {
1091                 case Bit:
1092                 case Bigint:
1093                 case Bigunsigned:
1094                     result.order(ByteOrder.LITTLE_ENDIAN);
1095                     result.putLong(value);
1096                     result.flip();
1097                     return result;
1098                 case Mediumint:
1099                     if (value > MAX_MEDIUMINT_VALUE || value < MIN_MEDIUMINT_VALUE){
1100                         throw new ClusterJUserException(local.message(
1101                             "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
1102                     }
1103                     result.order(ByteOrder.LITTLE_ENDIAN);
1104                     put3byteInt(result, (int)value);
1105                     result.flip();
1106                     return result;
1107                 case Mediumunsigned:
1108                     if (value > MAX_MEDIUMUNSIGNED_VALUE || value < 0){
1109                         throw new ClusterJUserException(local.message(
1110                             "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
1111                     }
1112                     result.order(ByteOrder.LITTLE_ENDIAN);
1113                     put3byteInt(result, (int)value);
1114                     result.flip();
1115                     return result;
1116                 case Datetime:
1117                     result.order(ByteOrder.LITTLE_ENDIAN);
1118                     result.putLong(packDatetime(value));
1119                     result.flip();
1120                     return result;
1121                 case Timestamp:
1122                     result.order(ByteOrder.LITTLE_ENDIAN);
1123                     result.putInt((int)(value/1000L));
1124                     result.flip();
1125                     return result;
1126                 case Date:
1127                     result.order(ByteOrder.LITTLE_ENDIAN);
1128                     put3byteInt(result, packDate(value));
1129                     result.flip();
1130                     return result;
1131                 case Time:
1132                     result.order(ByteOrder.LITTLE_ENDIAN);
1133                     put3byteInt(result, packTime(value));
1134                     result.flip();
1135                     return result;
1136                 case Datetime2:
1137                     result.order(ByteOrder.BIG_ENDIAN);
1138                     result.putLong(packDatetime2(storeColumn.getPrecision(), value));
1139                     result.flip();
1140                     return result;
1141                 case Time2:
1142                     result.order(ByteOrder.BIG_ENDIAN);
1143                     result.putLong(packTime2(storeColumn.getPrecision(), value));
1144                     result.flip();
1145                     return result;
1146                 case Timestamp2:
1147                     result.order(ByteOrder.BIG_ENDIAN);
1148                     result.putLong(packTimestamp2(storeColumn.getPrecision(), value));
1149                     result.flip();
1150                     return result;
1151                 default:
1152                     throw new ClusterJUserException(local.message(
1153                             "ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
1154             }
1155         }
1156 
1157         public long convertLongValueForStorage(Column storeColumn, long value) {
1158             switch (storeColumn.getType()) {
1159                 case Bit:
1160                 case Bigint:
1161                 case Bigunsigned:
1162                     return value;
1163                 case Mediumint:
1164                     if (value > MAX_MEDIUMINT_VALUE || value < MIN_MEDIUMINT_VALUE){
1165                         throw new ClusterJUserException(local.message(
1166                             "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
1167                     }
1168                     return value;
1169                 case Mediumunsigned:
1170                     if (value > MAX_MEDIUMUNSIGNED_VALUE || value < 0){
1171                         throw new ClusterJUserException(local.message(
1172                             "ERR_Bounds", value, storeColumn.getName(), storeColumn.getType()));
1173                     }
1174                     return value;
1175                 case Datetime:
1176                     return packDatetime(value);
1177                case Timestamp:
1178                     return value/1000L;
1179                 case Date:
1180                     return packDate(value);
1181                 case Time:
1182                     return packTime(value);
1183                 case Datetime2:
1184                     // value is in milliseconds since the epoch
1185                     // datetime2 is in big endian format so need to swap the result
1186                     return swap(packDatetime2(storeColumn.getPrecision(), value));
1187                 case Time2:
1188                     // value is in milliseconds since the epoch
1189                     // time2 is in big endian format so need to swap the result
1190                     return swap(packTime2(storeColumn.getPrecision(), value));
1191                 case Timestamp2:
1192                     // value is in milliseconds since the epoch
1193                     // timestamp2 is in big endian format so need to swap the result
1194                     return swap(packTimestamp2(storeColumn.getPrecision(), value));
1195                 default:
1196                     throw new ClusterJUserException(local.message(
1197                             "ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
1198             }
1199         }
1200 
1201         public int convertByteValueForStorage(Column storeColumn, byte value) {
1202             switch (storeColumn.getType()) {
1203                 case Bit:
1204                     // bit fields are always stored as int32
1205                 case Tinyint:
1206                 case Year:
1207                     return  value & ooooooff;
1208                 default:
1209                     throw new ClusterJUserException(local.message(
1210                             "ERR_Unsupported_Mapping", storeColumn.getType(), "byte"));
1211             }
1212         }
1213 
1214         public int convertShortValueForStorage(Column storeColumn, short value) {
1215             switch (storeColumn.getType()) {
1216                 case Bit:
1217                     // bit fields are always stored as int32
1218                 case Smallint:
1219                     return value & ooooffff;
1220                 default:
1221                     throw new ClusterJUserException(local.message(
1222                             "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
1223             }
1224         }
1225 
1226         public long convertLongValueFromStorage(Column storeColumn, long fromStorage) {
1227             switch (storeColumn.getType()) {
1228                 case Bigint:
1229                 case Bigunsigned:
1230                 case Bit:
1231                 case Mediumint:
1232                 case Mediumunsigned:
1233                     return fromStorage;
1234                 case Datetime:
1235                     return unpackDatetime(fromStorage);
1236                 case Timestamp:
1237                     return fromStorage * 1000L;
1238                 case Date:
1239                     return unpackDate((int)fromStorage);
1240                 case Time:
1241                     return unpackTime((int)fromStorage);
1242                 default:
1243                     throw new ClusterJUserException(
1244                             local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
1245             }
1246         }
1247 
1248     };
1249 
1250     protected static interface EndianManager {
put3byteInt(ByteBuffer byteBuffer, int value)1251         public void put3byteInt(ByteBuffer byteBuffer, int value);
getInt(Column storeColumn, int value)1252         public int getInt(Column storeColumn, int value);
getInt(Column storeColumn, NdbRecAttr ndbRecAttr)1253         public int getInt(Column storeColumn, NdbRecAttr ndbRecAttr);
getShort(Column storeColumn, NdbRecAttr ndbRecAttr)1254         public short getShort(Column storeColumn, NdbRecAttr ndbRecAttr);
getLong(Column storeColumn, NdbRecAttr ndbRecAttr)1255         public long getLong(Column storeColumn, NdbRecAttr ndbRecAttr);
getLong(Column storeColumn, long value)1256         public long getLong(Column storeColumn, long value);
getByte(Column storeColumn, NdbRecAttr ndbRecAttr)1257         public byte getByte(Column storeColumn, NdbRecAttr ndbRecAttr);
convertValue(Column storeColumn, byte value)1258         public ByteBuffer convertValue(Column storeColumn, byte value);
convertValue(Column storeColumn, short value)1259         public ByteBuffer convertValue(Column storeColumn, short value);
convertValue(Column storeColumn, int value)1260         public ByteBuffer convertValue(Column storeColumn, int value);
convertValue(Column storeColumn, long value)1261         public ByteBuffer convertValue(Column storeColumn, long value);
convertValue(ByteBuffer buffer, Column storeColumn, byte value)1262         public void convertValue(ByteBuffer buffer, Column storeColumn, byte value);
convertValue(ByteBuffer buffer, Column storeColumn, short value)1263         public void convertValue(ByteBuffer buffer, Column storeColumn, short value);
convertValue(ByteBuffer buffer, Column storeColumn, int value)1264         public void convertValue(ByteBuffer buffer, Column storeColumn, int value);
convertValue(ByteBuffer buffer, Column storeColumn, long value)1265         public void convertValue(ByteBuffer buffer, Column storeColumn, long value);
getBoolean(Column storeColumn, NdbRecAttr ndbRecAttr)1266         public boolean getBoolean(Column storeColumn, NdbRecAttr ndbRecAttr);
getBoolean(Column storeColumn, int value)1267         public boolean getBoolean(Column storeColumn, int value);
convertIntValueForStorage(Column storeColumn, int value)1268         public int convertIntValueForStorage(Column storeColumn, int value);
convertLongValueForStorage(Column storeColumn, long value)1269         public long convertLongValueForStorage(Column storeColumn, long value);
convertLongValueFromStorage(Column storeColumn, long fromStorage)1270         public long convertLongValueFromStorage(Column storeColumn, long fromStorage);
convertByteValueForStorage(Column storeColumn, byte value)1271         public int convertByteValueForStorage(Column storeColumn, byte value);
convertShortValueForStorage(Column storeColumn, short value)1272         public int convertShortValueForStorage(Column storeColumn, short value);
1273     }
1274 
1275     /** Convert the integer to a value that can be printed
1276      */
hex(int n)1277     protected static String hex(int n) {
1278         return String.format("0x%08X", n);
1279     }
1280 
1281     /** Convert the long to a value that can be printed
1282      */
hex(long n)1283     protected static String hex(long n) {
1284         return String.format("0x%016X", n);
1285     }
1286 
1287     /** Swap the bytes in the value, thereby converting a big-endian value
1288      * into a little-endian value (or vice versa).
1289      * @param value the value to be swapped
1290      * @return the swapped value
1291      */
swap(short value)1292     protected static short swap(short value) {
1293         return (short)((0x00ff & (value >>> 8)) |
1294                        (0xff00 & (value  << 8)));
1295     }
1296 
1297     /** Swap the bytes in the value, thereby converting a big-endian value
1298      * into a little-endian value (or vice versa).
1299      * @param value the value to be swapped
1300      * @return the swapped value
1301      */
swap(int value)1302     protected static int swap(int value) {
1303         return   0x000000ff & (value >>> 24) |
1304                 (0x0000ff00 & (value >>> 8)) |
1305                 (0x00ff0000 & (value  << 8)) |
1306                 (0xff000000 & (value  << 24));
1307     }
1308 
1309     /** Swap the bytes in the value, thereby converting a big-endian value
1310      * into a little-endian value (or vice versa).
1311      * @param value the value to be swapped
1312      * @return the swapped value
1313      */
swap(long value)1314     protected static long swap(long value) {
1315         return   ooooooooooooooff & (value >>> 56) |
1316                 (ooooooooooooffoo & (value >>> 40)) |
1317                 (ooooooooooffoooo & (value >>> 24)) |
1318                 (ooooooooffoooooo & (value >>> 8)) |
1319                 (ooooooffoooooooo & (value  << 8)) |
1320                 (ooooffoooooooooo & (value  << 24)) |
1321                 (ooffoooooooooooo & (value  << 40)) |
1322                 (ffoooooooooooooo & (value  << 56));
1323     }
1324 
throwError(Object returnCode, NdbErrorConst ndbError)1325     protected static void throwError(Object returnCode, NdbErrorConst ndbError) {
1326         throwError(returnCode, ndbError, "");
1327     }
1328 
throwError(Object returnCode, NdbErrorConst ndbError, String extra)1329     protected static void throwError(Object returnCode, NdbErrorConst ndbError, String extra) {
1330         String message = ndbError.message();
1331         int code = ndbError.code();
1332         int mysqlCode = ndbError.mysql_code();
1333         int status = ndbError.status();
1334         int classification = ndbError.classification();
1335         String msg = local.message("ERR_NdbJTie", returnCode, code, mysqlCode,
1336                 status, classification, message, extra);
1337         if (!NonSevereErrorCodes .contains(code)) {
1338             logger.error(msg);
1339         }
1340         throw new ClusterJDatastoreException(msg, code, mysqlCode, status, classification);
1341     }
1342 
1343     /** Convert the parameter value to a ByteBuffer that can be passed to ndbjtie.
1344      *
1345      * @param storeColumn the column definition
1346      * @param value the value to be converted
1347      * @return the ByteBuffer
1348      */
convertValue(Column storeColumn, byte[] value)1349     public static ByteBuffer convertValue(Column storeColumn, byte[] value) {
1350         int requiredLength = storeColumn.getColumnSpace();
1351         ByteBuffer result = ByteBuffer.allocateDirect(requiredLength);
1352         convertValue(result, storeColumn, value);
1353         return result;
1354     }
1355 
1356     /** Convert the parameter value and store it in a given ByteBuffer that can be passed to ndbjtie.
1357      *
1358      * @param buffer the buffer, positioned at the location to store the value
1359      * @param storeColumn the column definition
1360      * @param value the value to be converted
1361      */
convertValue(ByteBuffer buffer, Column storeColumn, byte[] value)1362     public static void convertValue(ByteBuffer buffer, Column storeColumn, byte[] value) {
1363         int dataLength = value.length;
1364         int maximumLength = storeColumn.getLength();
1365         if (dataLength > maximumLength) {
1366             throw new ClusterJUserException(
1367                     local.message("ERR_Data_Too_Long",
1368                     storeColumn.getName(), maximumLength, dataLength));
1369         }
1370         int prefixLength = storeColumn.getPrefixLength();
1371         switch (prefixLength) {
1372             case 0:
1373                 buffer.put(value);
1374                 if (dataLength < maximumLength) {
1375                     // pad with 0x00 on right
1376                     buffer.put(ZERO_PAD, 0, maximumLength - dataLength);
1377                 }
1378                 break;
1379             case 1:
1380                 buffer.put((byte)dataLength);
1381                 buffer.put(value);
1382                 break;
1383             case 2:
1384                 buffer.put((byte)(dataLength%256));
1385                 buffer.put((byte)(dataLength/256));
1386                 buffer.put(value);
1387                 break;
1388             default:
1389                     throw new ClusterJFatalInternalException(
1390                             local.message("ERR_Unknown_Prefix_Length",
1391                             prefixLength, storeColumn.getName()));
1392         }
1393         buffer.flip();
1394     }
1395 
1396     /** Convert a BigDecimal value to the binary decimal form used by MySQL.
1397      * Store the result in the given ByteBuffer that is already positioned.
1398      * Use the precision and scale of the column to convert. Values that don't fit
1399      * into the column throw a ClusterJUserException.
1400      * @param result the buffer, positioned at the location to store the value
1401      * @param storeColumn the column metadata
1402      * @param value the value to be converted
1403      * @return the ByteBuffer
1404      */
convertValue(ByteBuffer result, Column storeColumn, BigDecimal value)1405     public static void convertValue(ByteBuffer result, Column storeColumn, BigDecimal value) {
1406         int precision = storeColumn.getPrecision();
1407         int scale = storeColumn.getScale();
1408         int bytesNeeded = getDecimalColumnSpace(precision, scale);
1409         // TODO this should be a policy option, perhaps an annotation to fail on truncation
1410         BigDecimal scaledValue = value.setScale(scale, RoundingMode.HALF_UP);
1411         // the new value has the same scale as the column
1412         String stringRepresentation = scaledValue.toPlainString();
1413         int length = stringRepresentation.length();
1414         ByteBuffer byteBuffer = decimalByteBufferPool.borrowBuffer();
1415         CharBuffer charBuffer = CharBuffer.wrap(stringRepresentation);
1416         // basic encoding
1417         charset.newEncoder().encode(charBuffer, byteBuffer, true);
1418         byteBuffer.flip();
1419         int returnCode = Utils.decimal_str2bin(
1420                 byteBuffer, length, precision, scale, result, bytesNeeded);
1421         decimalByteBufferPool.returnBuffer(byteBuffer);
1422         if (returnCode != 0) {
1423             throw new ClusterJUserException(
1424                     local.message("ERR_String_To_Binary_Decimal",
1425                     returnCode, scaledValue, storeColumn.getName(), precision, scale));
1426         }
1427     }
1428 
1429     /** Convert a BigDecimal value to the binary decimal form used by MySQL.
1430      * Use the precision and scale of the column to convert. Values that don't fit
1431      * into the column throw a ClusterJUserException.
1432      * @param storeColumn the column metadata
1433      * @param value the value to be converted
1434      * @return the ByteBuffer
1435      */
convertValue(Column storeColumn, BigDecimal value)1436     public static ByteBuffer convertValue(Column storeColumn, BigDecimal value) {
1437         int precision = storeColumn.getPrecision();
1438         int scale = storeColumn.getScale();
1439         int bytesNeeded = getDecimalColumnSpace(precision, scale);
1440         ByteBuffer result = ByteBuffer.allocateDirect(bytesNeeded);
1441         // TODO this should be a policy option, perhaps an annotation to fail on truncation
1442         BigDecimal scaledValue = value.setScale(scale, RoundingMode.HALF_UP);
1443         // the new value has the same scale as the column
1444         String stringRepresentation = scaledValue.toPlainString();
1445         int length = stringRepresentation.length();
1446         ByteBuffer byteBuffer = ByteBuffer.allocateDirect(length);
1447         CharBuffer charBuffer = CharBuffer.wrap(stringRepresentation);
1448         // basic encoding
1449         charset.newEncoder().encode(charBuffer, byteBuffer, true);
1450         byteBuffer.flip();
1451         int returnCode = Utils.decimal_str2bin(
1452                 byteBuffer, length, precision, scale, result, bytesNeeded);
1453         byteBuffer.flip();
1454         if (returnCode != 0) {
1455             throw new ClusterJUserException(
1456                     local.message("ERR_String_To_Binary_Decimal",
1457                     returnCode, scaledValue, storeColumn.getName(), precision, scale));
1458         }
1459         return result;
1460     }
1461 
1462     /** Convert a BigInteger value to the binary decimal form used by MySQL.
1463      * Store the result in the given ByteBuffer that is already positioned.
1464      * Use the precision and scale of the column to convert. Values that don't fit
1465      * into the column throw a ClusterJUserException.
1466      * @param result the buffer, positioned at the location to store the value
1467      * @param storeColumn the column metadata
1468      * @param value the value to be converted
1469      * @return the ByteBuffer
1470      */
convertValue(ByteBuffer result, Column storeColumn, BigInteger value)1471     public static ByteBuffer convertValue(ByteBuffer result, Column storeColumn, BigInteger value) {
1472         int precision = storeColumn.getPrecision();
1473         int scale = storeColumn.getScale();
1474         int bytesNeeded = getDecimalColumnSpace(precision, scale);
1475         String stringRepresentation = value.toString();
1476         int length = stringRepresentation.length();
1477         ByteBuffer byteBuffer = decimalByteBufferPool.borrowBuffer();
1478         CharBuffer charBuffer = CharBuffer.wrap(stringRepresentation);
1479         // basic encoding
1480         charset.newEncoder().encode(charBuffer, byteBuffer, true);
1481         byteBuffer.flip();
1482         int returnCode = Utils.decimal_str2bin(
1483                 byteBuffer, length, precision, scale, result, bytesNeeded);
1484         decimalByteBufferPool.returnBuffer(byteBuffer);
1485         byteBuffer.flip();
1486         if (returnCode != 0) {
1487             throw new ClusterJUserException(
1488                     local.message("ERR_String_To_Binary_Decimal",
1489                     returnCode, stringRepresentation, storeColumn.getName(), precision, scale));
1490         }
1491         return result;
1492     }
1493 
1494     /** Convert a BigInteger value to the binary decimal form used by MySQL.
1495      * Use the precision and scale of the column to convert. Values that don't fit
1496      * into the column throw a ClusterJUserException.
1497      * @param storeColumn the column metadata
1498      * @param value the value to be converted
1499      * @return the ByteBuffer
1500      */
convertValue(Column storeColumn, BigInteger value)1501     public static ByteBuffer convertValue(Column storeColumn, BigInteger value) {
1502         int precision = storeColumn.getPrecision();
1503         int scale = storeColumn.getScale();
1504         int bytesNeeded = getDecimalColumnSpace(precision, scale);
1505         ByteBuffer result = ByteBuffer.allocateDirect(bytesNeeded);
1506         String stringRepresentation = value.toString();
1507         int length = stringRepresentation.length();
1508         ByteBuffer byteBuffer = ByteBuffer.allocateDirect(length);
1509         CharBuffer charBuffer = CharBuffer.wrap(stringRepresentation);
1510         // basic encoding
1511         charset.newEncoder().encode(charBuffer, byteBuffer, true);
1512         byteBuffer.flip();
1513         int returnCode = Utils.decimal_str2bin(
1514                 byteBuffer, length, precision, scale, result, bytesNeeded);
1515         byteBuffer.flip();
1516         if (returnCode != 0) {
1517             throw new ClusterJUserException(
1518                     local.message("ERR_String_To_Binary_Decimal",
1519                     returnCode, stringRepresentation, storeColumn.getName(), precision, scale));
1520         }
1521         return result;
1522     }
1523 
1524     /** Convert the parameter value to a ByteBuffer that can be passed to ndbjtie.
1525      *
1526      * @param storeColumn the column definition
1527      * @param value the value to be converted
1528      * @return the ByteBuffer
1529      */
convertValue(Column storeColumn, double value)1530     public static ByteBuffer convertValue(Column storeColumn, double value) {
1531         ByteBuffer result = ByteBuffer.allocateDirect(8);
1532         result.order(ByteOrder.nativeOrder());
1533         result.putDouble(value);
1534         result.flip();
1535         return result;
1536     }
1537 
1538     /** Convert the parameter value into a ByteBuffer that can be passed to ndbjtie.
1539      *
1540      * @param buffer the ByteBuffer
1541      * @param storeColumn the column definition
1542      * @param value the value to be converted
1543      */
convertValue(ByteBuffer buffer, Column storeColumn, double value)1544     public static void convertValue(ByteBuffer buffer, Column storeColumn, double value) {
1545         buffer.order(ByteOrder.nativeOrder());
1546         buffer.putDouble(value);
1547         buffer.flip();
1548         return;
1549     }
1550 
1551     /** Convert the parameter value to a ByteBuffer that can be passed to ndbjtie.
1552      *
1553      * @param storeColumn the column definition
1554      * @param value the value to be converted
1555      * @return the ByteBuffer
1556      */
convertValue(Column storeColumn, float value)1557     public static ByteBuffer convertValue(Column storeColumn, float value) {
1558         ByteBuffer result = ByteBuffer.allocateDirect(4);
1559         result.order(ByteOrder.nativeOrder());
1560         result.putFloat(value);
1561         result.flip();
1562         return result;
1563     }
1564 
1565     /** Convert the parameter value into a ByteBuffer that can be passed to ndbjtie.
1566      *
1567      * @param buffer the ByteBuffer
1568      * @param storeColumn the column definition
1569      * @param value the value to be converted
1570      */
convertValue(ByteBuffer buffer, Column storeColumn, float value)1571     public static void convertValue(ByteBuffer buffer, Column storeColumn, float value) {
1572         buffer.order(ByteOrder.nativeOrder());
1573         buffer.putFloat(value);
1574         buffer.flip();
1575         return;
1576     }
1577 
1578     /** Convert the parameter value to a ByteBuffer that can be passed to ndbjtie.
1579      *
1580      * @param storeColumn the column definition
1581      * @param value the value to be converted
1582      * @return the ByteBuffer
1583      */
convertValue(Column storeColumn, byte value)1584     public static ByteBuffer convertValue(Column storeColumn, byte value) {
1585         return endianManager.convertValue(storeColumn, value);
1586     }
1587 
1588     /** Convert the parameter value to a ByteBuffer that can be passed to ndbjtie.
1589      *
1590      * @param storeColumn the column definition
1591      * @param value the value to be converted
1592      * @return the ByteBuffer
1593      */
convertValue(Column storeColumn, short value)1594     public static ByteBuffer convertValue(Column storeColumn, short value) {
1595         return endianManager.convertValue(storeColumn, value);
1596     }
1597 
1598     /** Convert the parameter value to a ByteBuffer that can be passed to ndbjtie.
1599      *
1600      * @param storeColumn the column definition
1601      * @param value the value to be converted
1602      * @return the ByteBuffer
1603      */
convertValue(Column storeColumn, int value)1604     public static ByteBuffer convertValue(Column storeColumn, int value) {
1605         return endianManager.convertValue(storeColumn, value);
1606     }
1607 
1608     /** Convert the parameter value to a ByteBuffer that can be passed to ndbjtie.
1609      *
1610      * @param storeColumn the column definition
1611      * @param value the value to be converted
1612      * @return the ByteBuffer
1613      */
convertValue(Column storeColumn, long value)1614     public static ByteBuffer convertValue(Column storeColumn, long value) {
1615         return endianManager.convertValue(storeColumn, value);
1616     }
1617 
1618     /** Convert the parameter value and copy it into a ByteBuffer parameter that can be passed to ndbjtie.
1619      *
1620      * @param storeColumn the column definition
1621      * @param value the value to be converted
1622      * @param buffer the ByteBuffer
1623      */
convertValue(ByteBuffer buffer, Column storeColumn, byte value)1624     public static void convertValue(ByteBuffer buffer, Column storeColumn, byte value) {
1625         endianManager.convertValue(buffer, storeColumn, value);
1626     }
1627 
1628     /** Convert the parameter value and copy it into a ByteBuffer parameter that can be passed to ndbjtie.
1629      *
1630      * @param storeColumn the column definition
1631      * @param value the value to be converted
1632      * @param buffer the ByteBuffer
1633      */
convertValue(ByteBuffer buffer, Column storeColumn, short value)1634     public static void convertValue(ByteBuffer buffer, Column storeColumn, short value) {
1635         endianManager.convertValue(buffer, storeColumn, value);
1636     }
1637 
1638     /** Convert the parameter value and copy it into a ByteBuffer parameter that can be passed to ndbjtie.
1639      *
1640      * @param storeColumn the column definition
1641      * @param value the value to be converted
1642      * @param buffer the ByteBuffer
1643      */
convertValue(ByteBuffer buffer, Column storeColumn, int value)1644     public static void convertValue(ByteBuffer buffer, Column storeColumn, int value) {
1645         endianManager.convertValue(buffer, storeColumn, value);
1646     }
1647 
1648     /** Convert the parameter value and copy it into a ByteBuffer parameter that can be passed to ndbjtie.
1649      *
1650      * @param storeColumn the column definition
1651      * @param value the value to be converted
1652      * @param buffer the ByteBuffer
1653      */
convertValue(ByteBuffer buffer, Column storeColumn, long value)1654     public static void convertValue(ByteBuffer buffer, Column storeColumn, long value) {
1655         endianManager.convertValue(buffer, storeColumn, value);
1656     }
1657 
1658     /** Encode a String as a ByteBuffer that can be passed to ndbjtie.
1659      * Put the length information in the beginning of the buffer.
1660      * Pad fixed length strings with blanks.
1661      * @param storeColumn the column definition
1662      * @param value the value to be converted
1663      * @return the ByteBuffer
1664      */
convertValue(Column storeColumn, String value)1665     protected static ByteBuffer convertValue(Column storeColumn, String value) {
1666         if (value == null) {
1667             value = "";
1668         }
1669         CharSequence chars = value;
1670         int offset = storeColumn.getPrefixLength();
1671         if (offset == 0) {
1672             chars = padString(value, storeColumn);
1673         }
1674         ByteBuffer byteBuffer = encodeToByteBuffer(chars, storeColumn.getCharsetNumber(), offset);
1675         fixBufferPrefixLength(storeColumn.getName(), byteBuffer, offset);
1676         if (logger.isDetailEnabled()) dumpBytesToLog(byteBuffer, byteBuffer.limit());
1677         return byteBuffer;
1678     }
1679 
1680     /** Encode a String as a ByteBuffer that can be passed to ndbjtie in a COND_LIKE filter.
1681      * There is no length information in the beginning of the buffer.
1682      * @param storeColumn the column definition
1683      * @param value the value to be converted
1684      * @return the ByteBuffer
1685      */
convertValueForLikeFilter(Column storeColumn, String value)1686     protected static ByteBuffer convertValueForLikeFilter(Column storeColumn, String value) {
1687         if (value == null) {
1688             value = "";
1689         }
1690         CharSequence chars = value;
1691         ByteBuffer byteBuffer = encodeToByteBuffer(chars, storeColumn.getCharsetNumber(), 0);
1692         if (logger.isDetailEnabled()) dumpBytesToLog(byteBuffer, byteBuffer.limit());
1693         return byteBuffer;
1694     }
1695 
1696     /** Encode a byte[] as a ByteBuffer that can be passed to ndbjtie in a COND_LIKE filter.
1697      * There is no length information in the beginning of the buffer.
1698      * @param storeColumn the column definition
1699      * @param value the value to be converted
1700      * @return the ByteBuffer
1701      */
convertValueForLikeFilter(Column storeColumn, byte[] value)1702     protected static ByteBuffer convertValueForLikeFilter(Column storeColumn, byte[] value) {
1703         if (value == null) {
1704             value = EMPTY_BYTE_ARRAY;
1705         }
1706         ByteBuffer byteBuffer = ByteBuffer.allocateDirect(value.length);
1707         byteBuffer.put(value);
1708         byteBuffer.flip();
1709         if (logger.isDetailEnabled()) dumpBytesToLog(byteBuffer, byteBuffer.limit());
1710         return byteBuffer;
1711     }
1712 
1713     /** Encode a byte[] into a ByteBuffer that can be passed to ndbjtie in a COND_LIKE filter.
1714      * There is no length information in the beginning of the buffer.
1715      * @param buffer the ByteBuffer
1716      * @param storeColumn the column definition
1717      * @param value the value to be converted
1718      */
convertValueForLikeFilter(ByteBuffer buffer, Column storeColumn, byte[] value)1719     protected static void convertValueForLikeFilter(ByteBuffer buffer, Column storeColumn, byte[] value) {
1720         if (value == null) {
1721             value = EMPTY_BYTE_ARRAY;
1722         }
1723         buffer.put(value);
1724         buffer.flip();
1725         return;
1726     }
1727 
1728     /** Pad the value with blanks on the right.
1729      * @param value the input value
1730      * @param storeColumn the store column
1731      * @return the value padded with blanks on the right
1732      */
padString(CharSequence value, Column storeColumn)1733     private static CharSequence padString(CharSequence value, Column storeColumn) {
1734         CharSequence chars = value;
1735         int suppliedLength = value.length();
1736         int requiredLength = storeColumn.getColumnSpace();
1737         if (suppliedLength > requiredLength) {
1738             throw new ClusterJUserException(local.message("ERR_Data_Too_Long",
1739                     storeColumn.getName(), requiredLength, suppliedLength));
1740         } else if (suppliedLength < requiredLength) {
1741             // pad to fixed length
1742             StringBuilder buffer = new StringBuilder(requiredLength);
1743             buffer.append(value);
1744             buffer.append(SPACE_PAD, 0, requiredLength - suppliedLength);
1745             chars = buffer;
1746         }
1747         return chars;
1748     }
1749 
1750     /** Pad the value with blanks on the right.
1751      * @param buffer the input value
1752      * @param storeColumn the store column
1753      * @return the buffer padded with blanks on the right
1754      */
padString(ByteBuffer buffer, Column storeColumn)1755     private static ByteBuffer padString(ByteBuffer buffer, Column storeColumn) {
1756         int suppliedLength = buffer.limit();
1757         int requiredLength = storeColumn.getColumnSpace();
1758         if (suppliedLength > requiredLength) {
1759             throw new ClusterJUserException(local.message("ERR_Data_Too_Long",
1760                     storeColumn.getName(), requiredLength, suppliedLength));
1761         } else if (suppliedLength < requiredLength) {
1762             //reset buffer's limit
1763             buffer.limit(requiredLength);
1764             //pad to fixed length
1765             buffer.position(suppliedLength);
1766             buffer.put(BLANK_PAD, 0, requiredLength - suppliedLength);
1767             buffer.position(0);
1768         }
1769         return buffer;
1770     }
1771 
1772     /** Fix the length information in a buffer based on the length prefix,
1773      * either 0, 1, or 2 bytes that hold the length information.
1774      * @param byteBuffer the byte buffer to fix
1775      * @param offset the size of the length prefix
1776      */
fixBufferPrefixLength(String columnName, ByteBuffer byteBuffer, int offset)1777     public static void fixBufferPrefixLength(String columnName, ByteBuffer byteBuffer, int offset) {
1778         int limit = byteBuffer.limit();
1779         // go back and fill in the length field(s)
1780         // size of the output char* is current limit minus offset
1781         int length = limit - offset;
1782         byteBuffer.position(0);
1783         switch (offset) {
1784             case 0:
1785                 break;
1786             case 1:
1787                 byteBuffer.put((byte)(length % 256));
1788                 break;
1789             case 2:
1790                 byteBuffer.put((byte)(length % 256));
1791                 byteBuffer.put((byte)(length / 256));
1792                 break;
1793         }
1794         // reset the position and limit for return
1795         byteBuffer.position(0);
1796         byteBuffer.limit(limit);
1797     }
1798 
1799     /** Pack milliseconds since the Epoch into an int in database Date format.
1800      * The date is converted into a three-byte value encoded as
1801      * YYYYx16x32 + MMx32 + DD.
1802      * Add one to the month since Calendar month is 0-origin.
1803      * @param millis milliseconds since the Epoch
1804      * @return the int in packed Date format
1805      */
packDate(long millis)1806     private static int packDate(long millis) {
1807         Calendar calendar = Calendar.getInstance();
1808         calendar.clear();
1809         calendar.setTimeInMillis(millis);
1810         int year = calendar.get(Calendar.YEAR);
1811         int month = calendar.get(Calendar.MONTH);
1812         int day = calendar.get(Calendar.DATE);
1813         int date = (year * 512) + ((month + 1) * 32) + day;
1814         return date;
1815     }
1816 
1817     /** Pack milliseconds since the Epoch into an int in database Time format.
1818      * Subtract one from date to get number of days (date is 1 origin).
1819      * The time is converted into a three-byte value encoded as
1820      * DDx240000 + HHx10000 + MMx100 + SS.
1821      * @param millis milliseconds since the Epoch
1822      * @return the int in packed Time format
1823      */
packTime(long millis)1824     private static int packTime(long millis) {
1825         Calendar calendar = Calendar.getInstance();
1826         calendar.clear();
1827         calendar.setTimeInMillis(millis);
1828         int year = calendar.get(Calendar.YEAR);
1829         int month = calendar.get(Calendar.MONTH);
1830         int day = calendar.get(Calendar.DATE);
1831         int hour = calendar.get(Calendar.HOUR);
1832         int minute = calendar.get(Calendar.MINUTE);
1833         int second = calendar.get(Calendar.SECOND);
1834         if (month != 0) {
1835             throw new ClusterJUserException(
1836                     local.message("ERR_Write_Time_Domain", new java.sql.Time(millis), millis, year, month, day, hour, minute, second));
1837         }
1838         int time = ((day - 1) * 240000) + (hour * 10000) + (minute * 100) + second;
1839         return time;
1840     }
1841 
1842     /** Pack milliseconds since the Epoch into a long in database Datetime format.
1843      * The Datetime contains a eight-byte date and time packed as
1844      * YYYYx10000000000 + MMx100000000 + DDx1000000 + HHx10000 + MMx100 + SS
1845      * Calendar month is 0 origin so add 1 to get packed month
1846      * @param value milliseconds since the Epoch
1847      * @return the long in packed Datetime format
1848      */
packDatetime(long value)1849     protected static long packDatetime(long value) {
1850         Calendar calendar = Calendar.getInstance();
1851         calendar.clear();
1852         calendar.setTimeInMillis(value);
1853         long year = calendar.get(Calendar.YEAR);
1854         long month = calendar.get(Calendar.MONTH) + 1;
1855         long day = calendar.get(Calendar.DATE);
1856         long hour = calendar.get(Calendar.HOUR);
1857         long minute = calendar.get(Calendar.MINUTE);
1858         long second = calendar.get(Calendar.SECOND);
1859         long packedDatetime = (year * 10000000000L) + (month * 100000000L) + (day * 1000000L)
1860                 + (hour * 10000L) + (minute * 100) + second;
1861         return packedDatetime;
1862     }
1863 
1864     /** Pack milliseconds since the Epoch into a long in database Datetime2 format.
1865      * Reference: http://dev.mysql.com/doc/internals/en/date-and-time-data-type-representation.html
1866      * The packed datetime2 integer part is:
1867      *  1 bit  sign (1= non-negative, 0= negative) [ALWAYS POSITIVE IN MYSQL 5.6]
1868      * 17 bits year*13+month (year 0-9999, month 1-12)
1869      *  5 bits day           (0-31)
1870      *  5 bits hour          (0-23)
1871      *  6 bits minute        (0-59)
1872      *  6 bits second        (0-59)
1873      *  ---------------------------
1874      * 40 bits = 5 bytes
1875      * Calendar month is 0 origin so add 1 to get packed month
1876      * @param millis milliseconds since the Epoch
1877      * @return the long in big endian packed Datetime2 format
1878      */
packDatetime2(int precision, long millis)1879     protected static long packDatetime2(int precision, long millis) {
1880         Calendar calendar = Calendar.getInstance();
1881         calendar.clear();
1882         calendar.setTimeInMillis(millis);
1883         long year = calendar.get(Calendar.YEAR);
1884         long month = calendar.get(Calendar.MONTH);
1885         long day = calendar.get(Calendar.DATE);
1886         long hour = calendar.get(Calendar.HOUR);
1887         long minute = calendar.get(Calendar.MINUTE);
1888         long second = calendar.get(Calendar.SECOND);
1889         long milliseconds = calendar.get(Calendar.MILLISECOND);
1890         long packedMillis = packFractionalSeconds(precision, milliseconds);
1891         long result =         0x8000000000000000L  // 1 bit sign
1892                 + (year     * 0x0003400000000000L) // 17 bits year * 13
1893                 + ((month+1)* 0x0000400000000000L) //         + month
1894                 + (day      * 0x0000020000000000L) // 5 bits day
1895                 + (hour     * 0x0000001000000000L) // 5 bits hour
1896                 + (minute   * 0x0000000040000000L) // 6 bits minute
1897                 + (second   * 0x0000000001000000L) // 6 bits second
1898                 + packedMillis;        // fractional microseconds
1899         return result;
1900     }
1901 
unpackDatetime2(int precision, long packedDatetime2)1902     protected static long unpackDatetime2(int precision, long packedDatetime2) {
1903         int yearMonth = (int)((packedDatetime2 & 0x7FFFC00000000000L) >>> 46); // 17 bits year * 13 + month
1904         int year = yearMonth / 13;
1905         int month= (yearMonth % 13) - 1; // calendar month is 0-11
1906         int day =       (int)((packedDatetime2 & 0x00003E0000000000L) >>> 41); // 5 bits day
1907         int hour =      (int)((packedDatetime2 & 0x000001F000000000L) >>> 36); // 5 bits hour
1908         int minute =    (int)((packedDatetime2 & 0x0000000FC0000000L) >>> 30); // 6 bits minute
1909         int second =    (int)((packedDatetime2 & 0x000000003F000000L) >>> 24); // 6 bits second
1910         int milliseconds = unpackFractionalSeconds(precision, (int)(packedDatetime2 & 0x0000000000FFFFFFL));
1911         Calendar calendar = Calendar.getInstance();
1912         calendar.clear();
1913         calendar.set(Calendar.YEAR, year);
1914         calendar.set(Calendar.MONTH, month);
1915         calendar.set(Calendar.DATE, day);
1916         calendar.set(Calendar.HOUR, hour);
1917         calendar.set(Calendar.MINUTE, minute);
1918         calendar.set(Calendar.SECOND, second);
1919         calendar.set(Calendar.MILLISECOND, milliseconds);
1920         return calendar.getTimeInMillis();
1921     }
1922 
1923     /** Convert milliseconds to packed time2 format
1924      * Fractional format
1925 
1926      * FSP      nBytes MaxValue MaxValueHex
1927      * ---      -----  -------- -----------
1928      * FSP=1    1byte  90               5A
1929      * FSP=2    1byte  99               63
1930 
1931      * FSP=3    2bytes 9990           2706
1932      -------------------------------------
1933      * Current algorithm does not support FSP=4 to FSP=6
1934      * These will be truncated to FSP=3
1935      * FSP=4    2bytes 9999           270F
1936 
1937      * FSP=5    3bytes 999990        F4236
1938      * FSP=6    3bytes 999999        F423F
1939 
1940      * @param precision number of digits of precision, 0 to 6
1941      * @param milliseconds
1942      * @return packed fractional seconds in low order 3 bytes
1943      */
packFractionalSeconds(int precision, long milliseconds)1944     protected static long packFractionalSeconds(int precision, long milliseconds) {
1945         switch (precision) {
1946             case 0:
1947                 if (milliseconds > 0) throwOnTruncation();
1948                 return 0L;
1949             case 1: // possible truncation
1950                 if (milliseconds % 100 != 0) throwOnTruncation();
1951                 return (milliseconds / 100L)  * 0x00000000000A0000L;
1952             case 2: // possible truncation
1953                 if (milliseconds % 10 != 0) throwOnTruncation();
1954                 return (milliseconds / 10L)  * 0x0000000000010000L;
1955             case 3:
1956             case 4:
1957                 return milliseconds * 0x0000000000000A00L; // milliseconds * 10
1958             case 5:
1959             case 6:
1960                 return milliseconds * 1000L; // milliseconds * 1000
1961             default:
1962                 return 0L;
1963         }
1964     }
1965 
1966     /** Unpack fractional seconds to milliseconds
1967      * @param precision number of digits of precision, 0 to 6
1968      * @param fraction packed seconds in low order 3 bytes
1969      * @return number of milliseconds
1970      */
unpackFractionalSeconds(int precision, int fraction)1971     protected static int unpackFractionalSeconds(int precision, int fraction) {
1972         switch (precision) {
1973             case 0:
1974                 return 0;
1975             case 1:
1976                 return ((fraction & 0x00FF0000) >>> 16) * 10;
1977             case 2:
1978                 return ((fraction & 0x00FF0000) >>> 16) * 10;
1979             case 3:
1980             case 4:
1981                 return ((fraction & 0x00FFFF00) >>> 8) / 10;
1982             case 5:
1983             case 6:
1984                 return (fraction & 0x00FFFFFF) / 1000;
1985             default:
1986                 return 0;
1987         }
1988     }
1989 
1990     /** Throw an exception if truncation is not allowed
1991      * Not currently implemented.
1992      */
throwOnTruncation()1993     protected static void throwOnTruncation() {
1994     }
1995 
1996     /** Pack milliseconds since the Epoch into a long in database Time2 format.
1997      *       1 bit sign (1= non-negative, 0= negative)
1998      *       1 bit unused (reserved for INTERVAL type)
1999      *      10 bits hour   (0-838)
2000      *       6 bits minute (0-59)
2001      *       6 bits second (0-59)
2002      *       --------------------
2003      *       24 bits = 3 bytes
2004 
2005      * @param millis milliseconds since the Epoch
2006      * @return the long in big endian packed Time format
2007      */
packTime2(int precision, long millis)2008     protected static long packTime2(int precision, long millis) {
2009         Calendar calendar = Calendar.getInstance();
2010         calendar.clear();
2011         calendar.setTimeInMillis(millis);
2012         long hour = calendar.get(Calendar.HOUR);
2013         long minute = calendar.get(Calendar.MINUTE);
2014         long second = calendar.get(Calendar.SECOND);
2015         long milliseconds = calendar.get(Calendar.MILLISECOND);
2016         long packedMillis = packFractionalSeconds(precision, milliseconds);
2017         long result =     0x8000000000000000L |
2018                 (hour   * 0x0010000000000000L) |
2019                 (minute * 0x0000400000000000L) |
2020                 (second * 0x0000010000000000L) |
2021                 packedMillis << 16;
2022         return result;
2023     }
2024 
unpackTime2(int precision, long value)2025     protected static long unpackTime2(int precision, long value) {
2026         int hour =     (int)((value & 0x3FF0000000000000L) >>> 52);
2027         int minute =   (int)((value & 0x000FC00000000000L) >>> 46);
2028         int second =   (int)((value & 0x00003F0000000000L) >>> 40);
2029         int fraction = (int)((value & 0x000000FFFFFF0000L) >>> 16);
2030 
2031         int milliseconds = unpackFractionalSeconds(precision, fraction);
2032         Calendar calendar = Calendar.getInstance();
2033         calendar.clear();
2034         calendar.set(Calendar.HOUR, hour);
2035         calendar.set(Calendar.MINUTE, minute);
2036         calendar.set(Calendar.SECOND, second);
2037         calendar.set(Calendar.MILLISECOND, milliseconds);
2038         return calendar.getTimeInMillis();
2039     }
2040 
2041     /** Pack milliseconds since the Epoch into a long in database Timestamp2 format.
2042      * First four bytes are Unix time format: seconds since the epoch;
2043      * Fractional part is 0-3 bytes
2044      * @param millis milliseconds since the Epoch
2045      * @return the long in big endian packed Timestamp2 format
2046      */
packTimestamp2(int precision, long millis)2047     protected static long packTimestamp2(int precision, long millis) {
2048         long milliseconds = millis % 1000; // extract milliseconds
2049         long seconds = millis/1000; // truncate to seconds
2050         long packedMillis = packFractionalSeconds(precision, milliseconds);
2051         long result = (seconds << 32) +
2052                 (packedMillis << 8);
2053         if (logger.isDetailEnabled()) logger.detail(
2054                 "packTimestamp2 precision: " + precision + " millis: " + millis + " result: " + hex(result));
2055         return result;
2056     }
2057 
unpackTimestamp2(int precision, long value)2058     protected static long unpackTimestamp2(int precision, long value) {
2059         int fraction = (int)((value & 0x00000000FFFFFF00) >>> 8);
2060         long result = ((value >>> 32) * 1000) +
2061                 unpackFractionalSeconds(precision, fraction);
2062         if (logger.isDetailEnabled()) logger.detail(
2063                 "unpackTimestamp2 precision: " + precision + " value: " + hex(value)
2064                 + " fraction: " + hex(fraction) + " result: " + result);
2065         return result;
2066     }
2067 
2068     /** Convert the byte[] into a String to be used for logging and debugging.
2069      *
2070      * @param bytes the byte[] to be dumped
2071      * @return the String representation
2072      */
dumpBytes(byte[] bytes)2073     public static String dumpBytes (byte[] bytes) {
2074         StringBuffer buffer = new StringBuffer("byte[");
2075         buffer.append(bytes.length);
2076         buffer.append("]: [");
2077         for (int i = 0; i < bytes.length; ++i) {
2078             buffer.append((int)bytes[i]);
2079             buffer.append(" ");
2080         }
2081         buffer.append("]");
2082         return buffer.toString();
2083     }
2084 
2085     /** Convert the byteBuffer into a String to be used for logging and debugging.
2086      *
2087      * @param byteBuffer the byteBuffer to be dumped
2088      * @return the String representation
2089      */
dumpBytes(ByteBuffer byteBuffer)2090     public static String dumpBytes(ByteBuffer byteBuffer) {
2091         byteBuffer.mark();
2092         int length = byteBuffer.limit() - byteBuffer.position();
2093         byte[] dst = new byte[length];
2094         byteBuffer.get(dst);
2095         byteBuffer.reset();
2096         return dumpBytes(dst);
2097     }
2098 
dumpBytesToLog(ByteBuffer byteBuffer, int limit)2099     private static void dumpBytesToLog(ByteBuffer byteBuffer, int limit) {
2100         StringBuffer message = new StringBuffer("String position is: ");
2101         message.append(byteBuffer.position());
2102         message.append(" limit: ");
2103         message.append(byteBuffer.limit());
2104         message.append(" data [");
2105         while (byteBuffer.hasRemaining()) {
2106             message.append((int)byteBuffer.get());
2107             message.append(" ");
2108         }
2109         message.append("]");
2110         logger.detail(message.toString());
2111         byteBuffer.position(0);
2112         byteBuffer.limit(limit);
2113     }
2114 
getDecimal(ByteBuffer byteBuffer, int length, int precision, int scale)2115     public static BigDecimal getDecimal(ByteBuffer byteBuffer, int length, int precision, int scale) {
2116         String decimal = null;
2117         try {
2118             decimal = getDecimalString(byteBuffer, length, precision, scale);
2119             return new BigDecimal(decimal);
2120         } catch (NumberFormatException nfe) {
2121             throw new ClusterJUserException(
2122                     local.message("ERR_Number_Format", decimal, dump(decimal)));
2123         }
2124     }
2125 
getBigInteger(ByteBuffer byteBuffer, int length, int precision, int scale)2126     public static BigInteger getBigInteger(ByteBuffer byteBuffer, int length, int precision, int scale) {
2127         String decimal = null;
2128         try {
2129             decimal = getDecimalString(byteBuffer, length, precision, scale);
2130             return new BigInteger(decimal);
2131         } catch (NumberFormatException nfe) {
2132             throw new ClusterJUserException(
2133                     local.message("ERR_Number_Format", decimal, dump(decimal)));
2134         }
2135     }
2136 
2137     /** Get a Decimal String from the byte buffer.
2138      *
2139      * @param byteBuffer the byte buffer with the raw data, starting at position()
2140      * @param length the length of the data
2141      * @param precision the precision of the data
2142      * @param scale the scale of the data
2143      * @return the Decimal String representation of the value
2144      */
getDecimalString(ByteBuffer byteBuffer, int length, int precision, int scale)2145     public static String getDecimalString(ByteBuffer byteBuffer, int length, int precision, int scale) {
2146         // allow for decimal point and sign and one more for trailing null
2147         int capacity = precision + 3;
2148         ByteBuffer digits = decimalByteBufferPool.borrowBuffer();
2149         int returnCode = Utils.decimal_bin2str(byteBuffer, length, precision, scale, digits, capacity);
2150         if (returnCode != 0) {
2151             decimalByteBufferPool.returnBuffer(digits);
2152             throw new ClusterJUserException(
2153                     local.message("ERR_Binary_Decimal_To_String",
2154                     returnCode, precision, scale, dumpBytes(byteBuffer)));
2155         }
2156         String string = null;
2157         // look for the end (null) of the result string
2158         for (int i = 0; i < digits.limit(); ++i) {
2159             if (digits.get(i) == 0) {
2160                 // found the end; mark it so we only decode the answer characters
2161                 digits.limit(i);
2162                 break;
2163             }
2164         }
2165         try {
2166             // use basic decoding
2167             CharBuffer charBuffer;
2168             charBuffer = charset.decode(digits);
2169             string = charBuffer.toString();
2170             return string;
2171         } finally {
2172             decimalByteBufferPool.returnBuffer(digits);
2173         }
2174 
2175     }
2176 
2177     /** Unpack a Date from its packed int representation.
2178      * Date is a three-byte integer packed as YYYYx16x32 + MMx32 + DD
2179      * @param packedDate the packed representation
2180      * @return the long value as milliseconds since the Epoch
2181      */
unpackDate(int packedDate)2182     public static long unpackDate(int packedDate) {
2183         int date = packedDate & 0x1f;
2184         packedDate = packedDate >>> 5;
2185         int month = (packedDate & 0x0f) - 1; // Month value is 0-based. e.g., 0 for January.
2186         int year = (packedDate >>> 4) & 0x7FFF;
2187         Calendar calendar = Calendar.getInstance();
2188         calendar.clear();
2189         calendar.set(year, month, date);
2190         return calendar.getTimeInMillis();
2191     }
2192 
2193     /** Unpack a Time from its packed int representation.
2194      * Time is a three-byte integer packed as DDx240000 + HHx10000 + MMx100 + SS
2195      * @param packedTime the packed representation
2196      * @return the long value as milliseconds since the Epoch
2197      */
unpackTime(int packedTime)2198     public static long unpackTime(int packedTime) {
2199         int second = packedTime % 100;
2200         packedTime /= 100;
2201         int minute = packedTime % 100;
2202         packedTime /= 100;
2203         int hour = packedTime % 24;
2204         int date = (packedTime / 24) + 1;
2205         if (date > 31) {
2206             throw new ClusterJUserException(
2207                     local.message("ERR_Read_Time_Domain", packedTime, date, hour, minute, second));
2208         }
2209         Calendar calendar = Calendar.getInstance();
2210         calendar.clear();
2211         calendar.set(Calendar.DATE, date);
2212         calendar.set(Calendar.HOUR, hour);
2213         calendar.set(Calendar.MINUTE, minute);
2214         calendar.set(Calendar.SECOND, second);
2215         calendar.set(Calendar.MILLISECOND, 0);
2216         return calendar.getTimeInMillis();
2217     }
2218 
2219     /** Unpack a Datetime from its packed long representation.
2220      * The Datetime contains a long packed as
2221      * YYYYx10000000000 + MMx100000000 + DDx1000000 + HHx10000 + MMx100 + SS
2222      * Calendar month is 0 origin so subtract 1 from packed month
2223      * @param packedDatetime the packed representation
2224      * @return the value as milliseconds since the Epoch
2225      */
unpackDatetime(long packedDatetime)2226     protected static long unpackDatetime(long packedDatetime) {
2227         int second = (int)(packedDatetime % 100);
2228         packedDatetime /= 100;
2229         int minute = (int)(packedDatetime % 100);
2230         packedDatetime /= 100;
2231         int hour = (int)(packedDatetime % 100);
2232         packedDatetime /= 100;
2233         int day = (int)(packedDatetime % 100);
2234         packedDatetime /= 100;
2235         int month = (int)(packedDatetime % 100) - 1;
2236         int year = (int)(packedDatetime / 100);
2237         Calendar calendar = Calendar.getInstance();
2238         calendar.clear();
2239         calendar.set(Calendar.YEAR, year);
2240         calendar.set(Calendar.MONTH, month);
2241         calendar.set(Calendar.DATE, day);
2242         calendar.set(Calendar.HOUR, hour);
2243         calendar.set(Calendar.MINUTE, minute);
2244         calendar.set(Calendar.SECOND, second);
2245         calendar.set(Calendar.MILLISECOND, 0);
2246         return calendar.getTimeInMillis();
2247 
2248     }
2249 
2250     /** Decode a byte[] into a String using the charset. The return value
2251      * is in UTF16 format.
2252      *
2253      * @param array the byte[] to be decoded
2254      * @param collation the collation
2255      * @return the decoded String
2256      */
decode(byte[] array, int collation)2257     public static String decode(byte[] array, int collation) {
2258         if (array == null) return null;
2259         ByteBuffer byteBuffer = ByteBuffer.allocateDirect(array.length);
2260         byteBuffer.put(array);
2261         byteBuffer.flip();
2262         int inputLength = array.length;
2263         // TODO make this more reasonable
2264         int outputLength = inputLength * 4;
2265         ByteBuffer outputByteBuffer = ByteBuffer.allocateDirect(outputLength);
2266         int[] lengths = new int[] {inputLength, outputLength};
2267         int returnCode = charsetMap.recode(lengths, collation, collationUTF16,
2268                 byteBuffer, outputByteBuffer);
2269         switch (returnCode) {
2270             case CharsetMapConst.RecodeStatus.RECODE_OK:
2271                 outputByteBuffer.limit(lengths[1]);
2272                 CharBuffer charBuffer = outputByteBuffer.asCharBuffer();
2273                 return charBuffer.toString();
2274             case CharsetMapConst.RecodeStatus.RECODE_BAD_CHARSET:
2275                 throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Charset",
2276                         collation));
2277             case CharsetMapConst.RecodeStatus.RECODE_BAD_SRC:
2278                 throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Source",
2279                         collation, lengths[0]));
2280             case CharsetMapConst.RecodeStatus.RECODE_BUFF_TOO_SMALL:
2281                 throw new ClusterJFatalInternalException(local.message("ERR_Decode_Buffer_Too_Small",
2282                         collation, inputLength, outputLength, lengths[0], lengths[1]));
2283             default:
2284                 throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Return_Code",
2285                         returnCode));
2286         }
2287     }
2288 
2289     /** Decode a ByteBuffer into a String using the charset. The return value
2290      * is in UTF16 format.
2291      *
2292      * @param inputByteBuffer the byte buffer to be decoded
2293      * @param collation the collation
2294      * @return the decoded String
2295      */
decode(ByteBuffer inputByteBuffer, int collation)2296     protected static String decode(ByteBuffer inputByteBuffer, int collation) {
2297         int inputLength = inputByteBuffer.limit() - inputByteBuffer.position();
2298         int outputLength = inputLength * 2;
2299         ByteBuffer outputByteBuffer = ByteBuffer.allocateDirect(outputLength);
2300         int[] lengths = new int[] {inputLength, outputLength};
2301         int returnCode = charsetMap.recode(lengths, collation, collationUTF16,
2302                 inputByteBuffer, outputByteBuffer);
2303         switch (returnCode) {
2304             case CharsetMapConst.RecodeStatus.RECODE_OK:
2305                 outputByteBuffer.limit(lengths[1]);
2306                 CharBuffer charBuffer = outputByteBuffer.asCharBuffer();
2307                 return charBuffer.toString();
2308             case CharsetMapConst.RecodeStatus.RECODE_BAD_CHARSET:
2309                 throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Charset",
2310                         collation));
2311             case CharsetMapConst.RecodeStatus.RECODE_BAD_SRC:
2312                 throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Source",
2313                         collation, lengths[0]));
2314             case CharsetMapConst.RecodeStatus.RECODE_BUFF_TOO_SMALL:
2315                 throw new ClusterJFatalInternalException(local.message("ERR_Decode_Buffer_Too_Small",
2316                         collation, inputLength, outputLength, lengths[0], lengths[1]));
2317             default:
2318                 throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Return_Code",
2319                         returnCode));
2320         }
2321     }
2322 
2323 
2324     /** Encode a String into a byte[] for storage.
2325      * This is used by character large objects when mapping text columns.
2326      *
2327      * @param string the String to encode
2328      * @param collation the collation
2329      * @return the encoded byte[]
2330      */
encode(String string, int collation)2331     public static byte[] encode(String string, int collation) {
2332         ByteBuffer encoded = encodeToByteBuffer(string, collation, 0);
2333         int length = encoded.limit();
2334         byte[] result = new byte[length];
2335         encoded.get(result);
2336         return result;
2337     }
2338 
2339     /** Encode a String into a ByteBuffer
2340      * using the mysql native encoding method.
2341      * @param string the String to encode
2342      * @param collation the collation
2343      * @param prefixLength the length of the length prefix
2344      * @return the encoded ByteBuffer with position set to prefixLength
2345      * and limit one past the last converted byte
2346      */
encodeToByteBuffer(CharSequence string, int collation, int prefixLength)2347     private static ByteBuffer encodeToByteBuffer(CharSequence string, int collation, int prefixLength) {
2348         if (string == null) return null;
2349         int inputLength = (string.length() * 2);
2350         ByteBuffer inputByteBuffer = ByteBuffer.allocateDirect(inputLength);
2351         CharBuffer charBuffer = inputByteBuffer.asCharBuffer();
2352         charBuffer.append(string);
2353         int outputLength = (2 * inputLength) + prefixLength;
2354         ByteBuffer outputByteBuffer = ByteBuffer.allocateDirect(outputLength);
2355         outputByteBuffer.position(prefixLength);
2356         int[] lengths = new int[] {inputLength, outputLength - prefixLength};
2357         int returnCode = charsetMap.recode(lengths, collationUTF16, collation,
2358                 inputByteBuffer, outputByteBuffer);
2359 
2360         switch (returnCode) {
2361             case CharsetMapConst.RecodeStatus.RECODE_OK:
2362                 outputByteBuffer.limit(prefixLength + lengths[1]);
2363                 return outputByteBuffer;
2364             case CharsetMapConst.RecodeStatus.RECODE_BAD_CHARSET:
2365                 throw new ClusterJFatalInternalException(local.message("ERR_Encode_Bad_Charset",
2366                         collation));
2367             case CharsetMapConst.RecodeStatus.RECODE_BAD_SRC:
2368                 throw new ClusterJFatalInternalException(local.message("ERR_Encode_Bad_Source",
2369                         collation, lengths[0]));
2370             case CharsetMapConst.RecodeStatus.RECODE_BUFF_TOO_SMALL:
2371                 throw new ClusterJFatalInternalException(local.message("ERR_Encode_Buffer_Too_Small",
2372                         collation, inputLength, outputLength, lengths[0], lengths[1]));
2373             default:
2374                 throw new ClusterJFatalInternalException(local.message("ERR_Encode_Bad_Return_Code",
2375                         returnCode));
2376         }
2377     }
2378 
2379     /** Encode a String into a ByteBuffer for storage.
2380      *
2381      * @param input the input String
2382      * @param storeColumn the store column
2383      * @param bufferManager the buffer manager with shared buffers
2384      * @return a byte buffer with prefix length
2385      */
encode(String input, Column storeColumn, BufferManager bufferManager)2386     public static ByteBuffer encode(String input, Column storeColumn, BufferManager bufferManager) {
2387         int collation = storeColumn.getCharsetNumber();
2388         if (logger.isDetailEnabled()) logger.detail("Utility.encode storeColumn: " + storeColumn.getName() +
2389                 " charsetName " + storeColumn.getCharsetName() +
2390                 " charsetNumber " + collation +
2391                 " input '" + input + "'");
2392         CharsetConverter charsetConverter = getCharsetConverter(collation);
2393         CharSequence chars = input;
2394         int prefixLength = storeColumn.getPrefixLength();
2395         ByteBuffer byteBuffer = charsetConverter.encode(storeColumn.getName(), chars, collation, prefixLength, bufferManager);
2396         if (prefixLength == 0) {
2397             padString(byteBuffer, storeColumn);
2398         }
2399         return byteBuffer;
2400     }
2401 
2402     /** Decode a ByteBuffer into a String using the charset. The return value
2403      * is in UTF16 format.
2404      *
2405      * @param inputByteBuffer the byte buffer to be decoded positioned past the length prefix
2406      * @param collation the collation
2407      * @param bufferManager the buffer manager with shared buffers
2408      * @return the decoded String
2409      */
decode(ByteBuffer inputByteBuffer, int collation, BufferManager bufferManager)2410     public static String decode(ByteBuffer inputByteBuffer, int collation, BufferManager bufferManager) {
2411         CharsetConverter charsetConverter = getCharsetConverter(collation);
2412         return charsetConverter.decode(inputByteBuffer, collation, bufferManager);
2413     }
2414 
2415     /** Get the charset converter for the given collation.
2416      * This is in the inner loop and must be highly optimized for performance.
2417      * @param collation the collation
2418      * @return the charset converter for the collation
2419      */
getCharsetConverter(int collation)2420     private static CharsetConverter getCharsetConverter(int collation) {
2421         // must be synchronized because the charsetConverters is not synchronized
2422         // we avoid a race condition where a charset converter is in the process
2423         // of being created and it's partially visible by another thread in charsetConverters
2424         synchronized (charsetConverters) {
2425             if (collation + 1 > charsetConverters.length) {
2426                 // unlikely; only if collations are added beyond existing collation number
2427                 String charsetName = charsetMap.getName(collation);
2428                 logger.warn(local.message("ERR_Charset_Number_Too_Big", collation, charsetName,
2429                         MAXIMUM_MYSQL_COLLATION_NUMBER));
2430                 return charsetConverterMultibyte;
2431             }
2432             CharsetConverter result = charsetConverters[collation];
2433             if (result == null) {
2434                 result = addCollation(collation);
2435             }
2436             return result;
2437         }
2438     }
2439 
2440     /** Create a new charset converter and add it to the collection of charset converters
2441      * for all collations that share the same charset.
2442      *
2443      * @param collation the collation to add
2444      * @return the charset converter for the collation
2445      */
addCollation(int collation)2446     private static CharsetConverter addCollation(int collation) {
2447         if (isMultibyteCollation(collation)) {
2448             return charsetConverters[collation] = charsetConverterMultibyte;
2449         }
2450         String charsetName = charsetMap.getMysqlName(collation);
2451         CharsetConverter charsetConverter = new SingleByteCharsetConverter(collation);
2452         int[] collations = collationPeersMap.get(charsetName);
2453         if (collations == null) {
2454             // unlikely; only if a new collation is added
2455             collations = new int[] {collation};
2456             collationPeersMap.put(charsetName, collations);
2457             logger.warn(local.message("WARN_Unknown_Collation", collation, charsetName));
2458             return charsetConverter;
2459         }
2460         for (int peer: collations) {
2461             // for each collation that shares the same charset name, set the charset converter
2462             logger.info("Adding charset converter " + charsetName + " for collation " + peer);
2463             charsetConverters[peer] = charsetConverter;
2464         }
2465         return charsetConverter;
2466     }
2467 
2468     /** Is the collation multibyte?
2469      *
2470      * @param collation the collation number
2471      * @return true if the collation uses a multibyte charset; false if the collation uses a single byte charset;
2472      * and null if the collation is not a valid collation
2473      */
isMultibyteCollation(int collation)2474     private static Boolean isMultibyteCollation(int collation) {
2475         boolean[] multibyte = charsetMap.isMultibyte(collation);
2476         return (multibyte == null)?null:multibyte[0];
2477     }
2478 
2479     /** Utility methods for encoding and decoding Strings.
2480      */
2481     protected interface CharsetConverter {
2482 
encode(String columnName, CharSequence input, int collation, int prefixLength, BufferManager bufferManager)2483         ByteBuffer encode(String columnName, CharSequence input, int collation, int prefixLength, BufferManager bufferManager);
2484 
decode(ByteBuffer inputByteBuffer, int collation, BufferManager bufferManager)2485         String decode(ByteBuffer inputByteBuffer, int collation, BufferManager bufferManager);
2486     }
2487 
2488     /** Class for encoding and decoding multibyte charsets. A single instance of this class
2489      * can be shared among all multibyte charsets.
2490      */
2491     protected static class MultiByteCharsetConverter implements CharsetConverter {
2492 
2493         /** Encode a String into a ByteBuffer. The input String is copied into a shared byte buffer.
2494          * The buffer is encoded via the mysql recode method to a shared String storage buffer.
2495          * If the output buffer is too small, a new buffer is allocated and the encoding is repeated.
2496          * @param input the input String
2497          * @param collation the charset number
2498          * @param prefixLength the prefix length (0, 1, or 2 depending on the type)
2499          * @param bufferManager the buffer manager with shared buffers
2500          * @return a byte buffer positioned at zero with the data ready to send to the database
2501          */
encode(String columnName, CharSequence input, int collation, int prefixLength, BufferManager bufferManager)2502         public ByteBuffer encode(String columnName, CharSequence input, int collation, int prefixLength, BufferManager bufferManager) {
2503             // input length in bytes is twice String length
2504             int inputLength = input.length() * 2;
2505             ByteBuffer inputByteBuffer = bufferManager.copyStringToByteBuffer(input);
2506             boolean done = false;
2507             // first try with output length equal input length
2508             int sizeNeeded = inputLength;
2509             while (!done) {
2510                 ByteBuffer outputByteBuffer = bufferManager.getStringStorageBuffer(sizeNeeded);
2511                 int outputLength = outputByteBuffer.limit();
2512                 outputByteBuffer.position(prefixLength);
2513                 int[] lengths = new int[] {inputLength, outputLength - prefixLength};
2514                 int returnCode = charsetMap.recode(lengths, collationUTF16, collation,
2515                         inputByteBuffer, outputByteBuffer);
2516                 switch (returnCode) {
2517                     case CharsetMapConst.RecodeStatus.RECODE_OK:
2518                         outputByteBuffer.limit(prefixLength + lengths[1]);
2519                         outputByteBuffer.position(0);
2520                         fixBufferPrefixLength(columnName, outputByteBuffer, prefixLength);
2521                         return outputByteBuffer;
2522                     case CharsetMapConst.RecodeStatus.RECODE_BAD_CHARSET:
2523                         throw new ClusterJFatalInternalException(local.message("ERR_Encode_Bad_Charset",
2524                                 collation));
2525                     case CharsetMapConst.RecodeStatus.RECODE_BAD_SRC:
2526                         throw new ClusterJFatalInternalException(local.message("ERR_Encode_Bad_Source",
2527                                 collation, lengths[0]));
2528                     case CharsetMapConst.RecodeStatus.RECODE_BUFF_TOO_SMALL:
2529                         // loop increasing output buffer size until success or run out of memory...
2530                         sizeNeeded = sizeNeeded * 3 / 2;
2531                         break;
2532                     default:
2533                         throw new ClusterJFatalInternalException(local.message("ERR_Encode_Bad_Return_Code",
2534                                 returnCode));
2535                 }
2536             }
2537             return null; // to make compiler happy; we never get here
2538         }
2539 
2540         /** Decode a byte buffer into a String. The input is decoded by the mysql charset recode method
2541          * into a shared buffer. Then the shared buffer is used to create the result String.
2542          * The input byte buffer is positioned just past the length, and its limit is set to one past the
2543          * characters to decode.
2544          * @param inputByteBuffer the input byte buffer
2545          * @param collation the charset number
2546          * @param bufferManager the buffer manager with shared buffers
2547          * @return the decoded String
2548          */
decode(ByteBuffer inputByteBuffer, int collation, BufferManager bufferManager)2549         public String decode(ByteBuffer inputByteBuffer, int collation, BufferManager bufferManager) {
2550             int inputLength = inputByteBuffer.limit() - inputByteBuffer.position();
2551             int sizeNeeded = inputLength * 4;
2552             boolean done = false;
2553             while (!done) {
2554                 ByteBuffer outputByteBuffer = bufferManager.getStringByteBuffer(sizeNeeded);
2555                 CharBuffer outputCharBuffer = bufferManager.getStringCharBuffer();
2556                 int outputLength = outputByteBuffer.limit();
2557                 outputByteBuffer.position(0);
2558                 outputByteBuffer.limit(outputLength);
2559                 int[] lengths = new int[] {inputLength, outputLength};
2560                 int returnCode = charsetMap.recode(lengths, collation, collationUTF16,
2561                         inputByteBuffer, outputByteBuffer);
2562                 switch (returnCode) {
2563                     case CharsetMapConst.RecodeStatus.RECODE_OK:
2564                         outputCharBuffer.position(0);
2565                         // each output character is two bytes for UTF16
2566                         outputCharBuffer.limit(lengths[1] / 2);
2567                         return outputCharBuffer.toString();
2568                     case CharsetMapConst.RecodeStatus.RECODE_BAD_CHARSET:
2569                         throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Charset",
2570                                 collation));
2571                     case CharsetMapConst.RecodeStatus.RECODE_BAD_SRC:
2572                         throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Source",
2573                                 collation, lengths[0]));
2574                     case CharsetMapConst.RecodeStatus.RECODE_BUFF_TOO_SMALL:
2575                         // try a bigger buffer
2576                         sizeNeeded = sizeNeeded * 3 / 2;
2577                         break;
2578                     default:
2579                         throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Return_Code",
2580                                 returnCode));
2581                 }
2582             }
2583             return null; // never reached; make the compiler happy
2584         }
2585     }
2586 
2587     /** Class for encoding and decoding single byte collations.
2588      */
2589     protected static class SingleByteCharsetConverter implements CharsetConverter {
2590 
2591         private static final int BYTE_RANGE = (1 + Byte.MAX_VALUE) - Byte.MIN_VALUE;
2592         private static byte[] allBytes = new byte[BYTE_RANGE];
2593         // The initial charToByteMap, with all char mappings mapped
2594         // to (byte) '?', so that unknown characters are mapped to '?'
2595         // instead of '\0' (which means end-of-string to MySQL).
2596         private static byte[] unknownCharsMap = new byte[65536];
2597 
2598         static {
2599             // initialize allBytes with all possible byte values
2600             for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
2601                 allBytes[i - Byte.MIN_VALUE] = (byte) i;
2602             }
2603             // initialize unknownCharsMap to '?' in each position
2604             for (int i = 0; i < unknownCharsMap.length; i++) {
2605                 unknownCharsMap[i] = (byte) '?'; // use something 'sane' for unknown chars
2606             }
2607         }
2608 
2609         /** The byte to char array */
2610         private char[] byteToChars = new char[BYTE_RANGE];
2611 
2612         /** The char to byte array */
2613         private byte[] charToBytes = new byte[65536];
2614 
2615         /** Construct a new single byte charset converter. This converter is only used for
2616          * charsets that encode to a single byte for any input character.
2617          * @param collation
2618          */
SingleByteCharsetConverter(int collation)2619         public SingleByteCharsetConverter(int collation) {
2620             ByteBuffer allBytesByteBuffer = ByteBuffer.allocateDirect(256);
2621             allBytesByteBuffer.put(allBytes);
2622             allBytesByteBuffer.flip();
2623             String allBytesString = Utility.decode(allBytesByteBuffer, collation);
2624             if (allBytesString.length() != 256) {
2625                 String charsetName = charsetMap.getName(collation);
2626                 throw new ClusterJFatalInternalException(local.message("ERR_Bad_Charset_Decode_All_Chars",
2627                         collation, charsetName, allBytesString.length()));
2628             }
2629             int allBytesLen = allBytesString.length();
2630 
2631             System.arraycopy(unknownCharsMap, 0, this.charToBytes, 0,
2632                     this.charToBytes.length);
2633 
2634             for (int i = 0; i < BYTE_RANGE && i < allBytesLen; i++) {
2635                 char c = allBytesString.charAt(i);
2636                 this.byteToChars[i] = c;
2637                 this.charToBytes[c] = allBytes[i];
2638             }
2639         }
2640 
2641         /** Encode a String into a ByteBuffer. The input String is encoded, character by character,
2642          * into an output byte[]. Then the output is copied into a shared byte buffer.
2643          * @param input the input String
2644          * @param collation the charset number
2645          * @param prefixLength the prefix length (0, 1, or 2 depending on the type)
2646          * @param bufferManager the buffer manager with shared buffers
2647          * @return a byte buffer positioned at zero with the data ready to send to the database
2648          */
encode(String columnName, CharSequence input, int collation, int prefixLength, BufferManager bufferManager)2649         public ByteBuffer encode(String columnName, CharSequence input, int collation, int prefixLength, BufferManager bufferManager) {
2650             int length = input.length();
2651             byte[] outputBytes = new byte[length];
2652             for (int position = 0; position < length; ++position) {
2653                 outputBytes[position] = charToBytes[input.charAt(position)];
2654             }
2655             // input is now encoded; copy to shared output buffer
2656             ByteBuffer outputByteBuffer = bufferManager.getStringStorageBuffer(length + prefixLength);
2657             // skip over prefix
2658             outputByteBuffer.position(prefixLength);
2659             outputByteBuffer.put(outputBytes);
2660             outputByteBuffer.flip();
2661             // adjust the length prefix
2662             fixBufferPrefixLength(columnName, outputByteBuffer, prefixLength);
2663             return outputByteBuffer;
2664         }
2665 
2666         /** Decode a byte buffer into a String. The input byte buffer is copied into a byte[],
2667          * then encoded byte by byte into an output char[]. Then the result String is created from the char[].
2668          * The input byte buffer is positioned just past the length, and its limit is set to one past the
2669          * characters to decode.
2670          * @param inputByteBuffer the input byte buffer
2671          * @param collation the charset number
2672          * @param bufferManager the buffer manager with shared buffers
2673          * @return the decoded String
2674          */
decode(ByteBuffer inputByteBuffer, int collation, BufferManager bufferManager)2675         public String decode(ByteBuffer inputByteBuffer, int collation, BufferManager bufferManager) {
2676             int inputLimit = inputByteBuffer.limit();
2677             int inputPosition = inputByteBuffer.position();
2678             int inputSize = inputLimit- inputPosition;
2679             byte[] inputBytes = new byte[inputSize];
2680             inputByteBuffer.get(inputBytes);
2681             char[] outputChars = new char[inputSize];
2682             for (int position = 0; position < inputSize; ++position) {
2683                 outputChars[position] = byteToChars[inputBytes[position] - Byte.MIN_VALUE];
2684             }
2685             // input is now decoded; create a new String from the output
2686             String result = new String(outputChars);
2687             return result;
2688         }
2689     }
2690 
dump(String string)2691     private static String dump(String string) {
2692         StringBuffer buffer = new StringBuffer("[");
2693         for (int i = 0; i < string.length(); ++i) {
2694             int theCharacter = string.charAt(i);
2695             buffer.append(theCharacter);
2696             buffer.append(" ");
2697         }
2698         buffer.append("]");
2699         return buffer.toString();
2700     }
2701 
2702     /** For each group of 9 decimal digits, the number of bytes needed
2703      * to represent that group of digits:
2704      * 10, 100 -> 1; 256
2705      * 1,000, 10,000 -> 2; 65536
2706      * 100,000, 1,000,000 -> 3 16,777,216
2707      * 10,000,000, 100,000,000, 1,000,000,000 -> 4
2708      */
2709     static int[] howManyBytesNeeded = new int[] {0,  1,  1,  2,  2,  3,  3,  4,  4,  4,
2710                                                      5,  5,  6,  6,  7,  7,  8,  8,  8,
2711                                                      9,  9, 10, 10, 11, 11, 12, 12, 12,
2712                                                     13, 13, 14, 14, 15, 15, 16, 16, 16,
2713                                                     17, 17, 18, 18, 19, 19, 20, 20, 20,
2714                                                     21, 21, 22, 22, 23, 23, 24, 24, 24,
2715                                                     25, 25, 26, 26, 27, 27, 28, 28, 28,
2716                                                     29, 29};
2717     /** Get the number of bytes needed in memory to represent the decimal number.
2718      *
2719      * @param precision the precision of the number
2720      * @param scale the scale
2721      * @return the number of bytes needed for the binary representation of the number
2722      */
getDecimalColumnSpace(int precision, int scale)2723     public static int getDecimalColumnSpace(int precision, int scale) {
2724         int howManyBytesNeededForIntegral = howManyBytesNeeded[precision - scale];
2725         int howManyBytesNeededForFraction = howManyBytesNeeded[scale];
2726         int result = howManyBytesNeededForIntegral + howManyBytesNeededForFraction;
2727         return result;
2728     }
2729 
2730     /** Get a boolean from this ndbRecAttr.
2731      *
2732      * @param storeColumn the Column
2733      * @param ndbRecAttr the NdbRecAttr
2734      * @return the boolean
2735      */
getBoolean(Column storeColumn, NdbRecAttr ndbRecAttr)2736     public static boolean getBoolean(Column storeColumn, NdbRecAttr ndbRecAttr) {
2737         return endianManager.getBoolean(storeColumn, ndbRecAttr);
2738     }
2739 
getBoolean(Column storeColumn, int value)2740     public static boolean getBoolean(Column storeColumn, int value) {
2741         return endianManager.getBoolean(storeColumn, value);
2742     }
2743 
2744     /** Get a byte from this ndbRecAttr.
2745      *
2746      * @param storeColumn the Column
2747      * @param ndbRecAttr the NdbRecAttr
2748      * @return the byte
2749      */
getByte(Column storeColumn, NdbRecAttr ndbRecAttr)2750     public static byte getByte(Column storeColumn, NdbRecAttr ndbRecAttr) {
2751         return endianManager.getByte(storeColumn, ndbRecAttr);
2752     }
2753 
2754     /** Get a short from this ndbRecAttr.
2755      *
2756      * @param storeColumn the Column
2757      * @param ndbRecAttr the NdbRecAttr
2758      * @return the short
2759      */
getShort(Column storeColumn, NdbRecAttr ndbRecAttr)2760     public static short getShort(Column storeColumn, NdbRecAttr ndbRecAttr) {
2761         return endianManager.getShort(storeColumn, ndbRecAttr);
2762     }
2763 
2764     /** Get an int from this ndbRecAttr.
2765      *
2766      * @param storeColumn the Column
2767      * @param ndbRecAttr the NdbRecAttr
2768      * @return the int
2769      */
getInt(Column storeColumn, NdbRecAttr ndbRecAttr)2770     public static int getInt(Column storeColumn, NdbRecAttr ndbRecAttr) {
2771         return endianManager.getInt(storeColumn, ndbRecAttr);
2772     }
2773 
getInt(Column storeColumn, int value)2774     public static int getInt(Column storeColumn, int value) {
2775         return endianManager.getInt(storeColumn, value);
2776     }
2777 
2778     /** Get a long from this ndbRecAttr.
2779      *
2780      * @param storeColumn the Column
2781      * @param ndbRecAttr the NdbRecAttr
2782      * @return the long
2783      */
2784 
2785     /** Convert a long value from storage.
2786      * The value stored in the database might be a time, timestamp, date, bit array,
2787      * or simply a long value. The converted value can be converted into a
2788      * time, timestamp, date, bit array, or long value.
2789      */
getLong(Column storeColumn, long value)2790     public static long getLong(Column storeColumn, long value) {
2791         return endianManager.getLong(storeColumn, value);
2792     }
2793 
2794     /** Convert a long value into a long for storage. The value parameter
2795      * may be a date (milliseconds since the epoch), a bit array, or simply a long value.
2796      * The storage format depends on the type of the column and the endian-ness of
2797      * the host.
2798      * @param storeColumn the column
2799      * @param value the java value
2800      * @return the storage value
2801      */
convertLongValueForStorage(Column storeColumn, long value)2802     public static long convertLongValueForStorage(Column storeColumn, long value) {
2803         return endianManager.convertLongValueForStorage(storeColumn, value);
2804     }
2805 
2806     /** Convert a byte value into an int for storage. The value parameter
2807      * may be a bit, a bit array (BIT(1..8) needs to be stored as an int) or a byte value.
2808      * The storage format depends on the type of the column and the endian-ness of
2809      * the host.
2810      * @param storeColumn the column
2811      * @param value the java value
2812      * @return the storage value
2813      */
convertByteValueForStorage(Column storeColumn, byte value)2814     public static int convertByteValueForStorage(Column storeColumn, byte value) {
2815         return endianManager.convertByteValueForStorage(storeColumn, value);
2816     }
2817 
2818     /** Convert a short value into an int for storage. The value parameter
2819      * may be a bit array (BIT(1..16) needs to be stored as an int) or a short value.
2820      * The storage format depends on the type of the column and the endian-ness of
2821      * the host.
2822      * @param storeColumn the column
2823      * @param value the java value
2824      * @return the storage value
2825      */
convertShortValueForStorage(Column storeColumn, short value)2826     public static int convertShortValueForStorage(Column storeColumn,
2827             short value) {
2828         return endianManager.convertShortValueForStorage(storeColumn, value);
2829     }
2830 
convertIntValueForStorage(Column storeColumn, int value)2831     public static int convertIntValueForStorage(Column storeColumn, int value) {
2832         return endianManager.convertIntValueForStorage(storeColumn, value);
2833     }
2834 
2835 }
2836