MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Utility.java
1 /*
2  * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16  */
17 
18 package com.mysql.clusterj.tie;
19 
20 import java.lang.reflect.Method;
21 import java.math.BigDecimal;
22 import java.math.BigInteger;
23 import java.math.RoundingMode;
24 import java.nio.ByteBuffer;
25 import java.nio.ByteOrder;
26 import java.nio.CharBuffer;
27 import java.nio.charset.CharacterCodingException;
28 import java.nio.charset.Charset;
29 import java.nio.charset.CharsetDecoder;
30 import java.nio.charset.CharsetEncoder;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Calendar;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.TreeMap;
39 
40 import com.mysql.ndbjtie.mysql.CharsetMap;
41 import com.mysql.ndbjtie.mysql.CharsetMapConst;
42 import com.mysql.ndbjtie.mysql.Utils;
43 import com.mysql.ndbjtie.ndbapi.NdbErrorConst;
44 import com.mysql.ndbjtie.ndbapi.NdbRecAttr;
45 import com.mysql.clusterj.ClusterJDatastoreException;
46 import com.mysql.clusterj.ClusterJFatalInternalException;
47 import com.mysql.clusterj.ClusterJUserException;
48 import com.mysql.clusterj.core.store.Column;
49 import com.mysql.clusterj.core.util.I18NHelper;
50 import com.mysql.clusterj.core.util.Logger;
51 import com.mysql.clusterj.core.util.LoggerFactoryService;
52 import com.mysql.clusterj.tie.DbImpl.BufferManager;
53 
57 public class Utility {
58 
60  static final I18NHelper local = I18NHelper
61  .getInstance(Utility.class);
62 
64  static final Logger logger = LoggerFactoryService.getFactory()
65  .getInstance(Utility.class);
66 
68  static CharsetEncoder charsetEncoder = Charset.forName("windows-1252").newEncoder();
69 
71  static CharsetDecoder charsetDecoder = Charset.forName("windows-1252").newDecoder();
72 
73  static final long ooooooooooooooff = 0x00000000000000ffL;
74  static final long ooooooooooooffoo = 0x000000000000ff00L;
75  static final long ooooooooooffoooo = 0x0000000000ff0000L;
76  static final long ooooooooffoooooo = 0x00000000ff000000L;
77  static final long ooooooffoooooooo = 0x000000ff00000000L;
78  static final long ooooffoooooooooo = 0x0000ff0000000000L;
79  static final long ooffoooooooooooo = 0x00ff000000000000L;
80  static final long ffoooooooooooooo = 0xff00000000000000L;
81  static final long ooooooooffffffff = 0x00000000ffffffffL;
82  static final int ooooooff = 0x000000ff;
83  static final int ooooffff = 0x0000ffff;
84  static final int ooooffoo = 0x0000ff00;
85  static final int ooffoooo = 0x00ff0000;
86 
87  static final char[] SPACE_PAD = new char[255];
88  static {
89  for (int i = 0; i < 255; ++i) {
90  SPACE_PAD[i] = ' ';
91  }
92  }
93 
94  static final byte[] ZERO_PAD = new byte[255];
95  static {
96  for (int i = 0; i < 255; ++i) {
97  ZERO_PAD[i] = (byte)0;
98  }
99  }
100 
101  static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
102 
103  // TODO: this is intended to investigate a class loader issue with Sparc java
104  // The idea is to force loading the CharsetMap native class prior to calling the static create method
105  static Class<?> charsetMapClass = loadClass("com.mysql.ndbjtie.mysql.CharsetMap");
106  static Class<?> loadClass(String className) {
107  try {
108  return Class.forName(className);
109  } catch (ClassNotFoundException e) {
110  // TODO Auto-generated catch block
111  throw new ClusterJUserException(local.message("ERR_Loading_Native_Class", className), e);
112  }
113  }
114 
115  // TODO: change this to a weak reference so we can call delete on it when not needed
120  static CharsetMap charsetMap = createCharsetMap();
121 
122  // TODO: this is intended to investigate a class loader issue with Sparc java
123  // The idea is to create the CharsetMap create method in a try/catch block to report the exact error
124  static CharsetMap createCharsetMap() {
125  StringBuilder builder = new StringBuilder();
126  CharsetMap result = null;
127  try {
128  return CharsetMap.create();
129  } catch (Throwable t1) {
130  builder.append("CharsetMap.create() threw " + t1.getClass().getName() + ":" + t1.getMessage());
131  try {
132  Method charsetMapCreateMethod = charsetMapClass.getMethod("create", (Class[])null);
133  result = (CharsetMap)charsetMapCreateMethod.invoke(null, (Object[])null);
134  builder.append("charsetMapCreateMethod.invoke() succeeded:" + result);
135  } catch (Throwable t2) {
136  builder.append("charsetMapCreateMethod.invoke() threw " + t2.getClass().getName() + ":" + t2.getMessage());
137  }
138  throw new ClusterJUserException(builder.toString());
139  }
140  }
141 
143  static int MAXIMUM_MYSQL_COLLATION_NUMBER = 256;
144 
146  static int collationLatin1 = charsetMap.getCharsetNumber("latin1");
147 
149  static protected final int collationUTF16 = charsetMap.getUTF16CharsetNumber();
150 
152  public static CharsetMap getCharsetMap() {
153  return charsetMap;
154  }
155 
157  private static Map<String, int[]> collationPeersMap = new TreeMap<String, int[]>();
158 
160  private static CharsetConverter charsetConverterMultibyte = new MultiByteCharsetConverter();
161 
163  private static CharsetConverter[] charsetConverters =
164  new CharsetConverter[MAXIMUM_MYSQL_COLLATION_NUMBER + 1];
165 
167  static {
168  Map<String, List<Integer>> workingCollationPeersMap = new TreeMap<String, List<Integer>>();
169  for (int collation = 1; collation <= MAXIMUM_MYSQL_COLLATION_NUMBER; ++collation) {
170  String mysqlName = charsetMap.getMysqlName(collation);
171  if (mysqlName != null) {
172  if ((isMultibyteCollation(collation))) {
173  // multibyte collations all use the multibyte charset converter
174  charsetConverters[collation] = charsetConverterMultibyte;
175  } else {
176  // find out if this charset name is already used by another (peer) collation
177  List<Integer> collations = workingCollationPeersMap.get(mysqlName);
178  if (collations == null) {
179  // this is the first collation to use this charset name
180  collations = new ArrayList<Integer>(8);
181  collations.add(collation);
182  workingCollationPeersMap.put(mysqlName, collations);
183  } else {
184  // add this collation to the list of (peer) collations
185  collations.add(collation);
186  }
187  }
188  }
189  }
190 
191  for (Map.Entry<String, List<Integer>> workingCollationPeers: workingCollationPeersMap.entrySet()) {
192  String mysqlName = workingCollationPeers.getKey();
193  List<Integer> collations = workingCollationPeers.getValue();
194  int[] collationArray = new int[collations.size()];
195  int i = 0;
196  for (Integer collation: collations) {
197  collationArray[i++] = collation;
198  }
199  collationPeersMap.put(mysqlName, collationArray);
200  }
201  if (logger.isDetailEnabled()) {
202  for (Map.Entry<String, int[]> collationEntry: collationPeersMap.entrySet()) {
203  logger.detail("Utility collationMap " + collationEntry.getKey()
204  + " collations : " + Arrays.toString(collationEntry.getValue()));
205  }
206  }
207  // initialize the charset converter for latin1 and its peer collations (after peers are known)
208  addCollation(collationLatin1);
209  }
210 
215  public static boolean isRetriable(ClusterJDatastoreException ex) {
216  return NdbErrorConst.Status.TemporaryError == ex.getStatus();
217  }
218 
219  private final static EndianManager endianManager = ByteOrder.BIG_ENDIAN.equals(ByteOrder.nativeOrder())?
220  /*
221  * Big Endian algorithms to convert NdbRecAttr buffer into primitive types
222  */
223  new EndianManager() {
224 
225  public boolean getBoolean(Column storeColumn, NdbRecAttr ndbRecAttr) {
226  switch (storeColumn.getType()) {
227  case Bit:
228  return ndbRecAttr.int32_value() == 1;
229  case Tinyint:
230  return ndbRecAttr.int8_value() == 1;
231  default:
232  throw new ClusterJUserException(
233  local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "boolean"));
234  }
235  }
236 
237  public byte getByte(Column storeColumn, NdbRecAttr ndbRecAttr) {
238  switch (storeColumn.getType()) {
239  case Bit:
240  return (byte)ndbRecAttr.int32_value();
241  case Tinyint:
242  case Year:
243  return ndbRecAttr.int8_value();
244  default:
245  throw new ClusterJUserException(
246  local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "byte"));
247  }
248  }
249  public short getShort(Column storeColumn, NdbRecAttr ndbRecAttr) {
250  switch (storeColumn.getType()) {
251  case Bit:
252  return (short)ndbRecAttr.int32_value();
253  case Smallint:
254  return ndbRecAttr.short_value();
255  default:
256  throw new ClusterJUserException(
257  local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
258  }
259  }
260  public int getInt(Column storeColumn, NdbRecAttr ndbRecAttr) {
261  switch (storeColumn.getType()) {
262  case Bit:
263  case Int:
264  case Timestamp:
265  return ndbRecAttr.int32_value();
266  case Date:
267  return ndbRecAttr.u_medium_value();
268  case Time:
269  return ndbRecAttr.medium_value();
270  default:
271  throw new ClusterJUserException(
272  local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "int"));
273  }
274  }
275  public long getLong(Column storeColumn, NdbRecAttr ndbRecAttr) {
276  switch (storeColumn.getType()) {
277  case Bit:
278  long rawValue = ndbRecAttr.int64_value();
279  return (rawValue >>> 32) | (rawValue << 32);
280  case Bigint:
281  case Bigunsigned:
282  return ndbRecAttr.int64_value();
283  case Datetime:
284  return unpackDatetime(ndbRecAttr.int64_value());
285  case Timestamp:
286  return (((long)ndbRecAttr.int32_value()) & ooooooooffffffff) * 1000L;
287  case Date:
288  return unpackDate(ndbRecAttr.u_medium_value());
289  case Time:
290  return unpackTime(ndbRecAttr.medium_value());
291  default:
292  throw new ClusterJUserException(
293  local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
294  }
295  }
302  public void put3byteInt(ByteBuffer byteBuffer, int value) {
303  byteBuffer.put((byte)(value));
304  byteBuffer.put((byte)(value >> 8));
305  byteBuffer.put((byte)(value >> 16));
306  }
307 
308  public ByteBuffer convertValue(Column storeColumn, byte value) {
309  ByteBuffer result;
310  switch (storeColumn.getType()) {
311  case Bit:
312  result = ByteBuffer.allocateDirect(4);
313  // bit fields are always stored in an int32
314  result.order(ByteOrder.BIG_ENDIAN);
315  result.putInt(value & 0xff);
316  result.flip();
317  return result;
318  case Tinyint:
319  case Year:
320  result = ByteBuffer.allocateDirect(1);
321  result.put(value);
322  result.flip();
323  return result;
324  default:
325  throw new ClusterJUserException(local.message(
326  "ERR_Unsupported_Mapping", storeColumn.getType(), "byte"));
327  }
328  }
329 
330  public ByteBuffer convertValue(Column storeColumn, short value) {
331  ByteBuffer result;
332  switch (storeColumn.getType()) {
333  case Bit:
334  result = ByteBuffer.allocateDirect(4);
335  // bit fields are always stored in an int32
336  result.order(ByteOrder.BIG_ENDIAN);
337  result.putInt(value & 0xffff);
338  result.flip();
339  return result;
340  case Smallint:
341  result = ByteBuffer.allocateDirect(2);
342  result.order(ByteOrder.BIG_ENDIAN);
343  result.putShort(value);
344  result.flip();
345  return result;
346  default:
347  throw new ClusterJUserException(local.message(
348  "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
349  }
350  }
351 
352  public ByteBuffer convertValue(Column storeColumn, int value) {
353  ByteBuffer result = ByteBuffer.allocateDirect(4);
354  switch (storeColumn.getType()) {
355  case Bit:
356  case Int:
357  result.order(ByteOrder.BIG_ENDIAN);
358  break;
359  default:
360  throw new ClusterJUserException(local.message(
361  "ERR_Unsupported_Mapping", storeColumn.getType(), "int"));
362  }
363  result.putInt(value);
364  result.flip();
365  return result;
366  }
367 
368  public ByteBuffer convertValue(Column storeColumn, long value) {
369  ByteBuffer result = ByteBuffer.allocateDirect(8);
370  return convertValue(storeColumn, value, result);
371  }
372 
373  public ByteBuffer convertValue(Column storeColumn, long value, ByteBuffer result) {
374  switch (storeColumn.getType()) {
375  case Bit:
376  // bit fields are stored in two int32 fields
377  result.order(ByteOrder.BIG_ENDIAN);
378  result.putInt((int)((value)));
379  result.putInt((int)((value >>> 32)));
380  result.flip();
381  return result;
382  case Bigint:
383  case Bigunsigned:
384  result.order(ByteOrder.BIG_ENDIAN);
385  result.putLong(value);
386  result.flip();
387  return result;
388  case Date:
389  result.order(ByteOrder.LITTLE_ENDIAN);
390  put3byteInt(result, packDate(value));
391  result.flip();
392  return result;
393  case Datetime:
394  result.order(ByteOrder.BIG_ENDIAN);
395  result.putLong(packDatetime(value));
396  result.flip();
397  return result;
398  case Time:
399  result.order(ByteOrder.LITTLE_ENDIAN);
400  put3byteInt(result, packTime(value));
401  result.flip();
402  return result;
403  case Timestamp:
404  result.order(ByteOrder.BIG_ENDIAN);
405  result.putInt((int)(value/1000L));
406  result.flip();
407  return result;
408  default:
409  throw new ClusterJUserException(local.message(
410  "ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
411  }
412  }
413 
414  public long convertLongValueForStorage(Column storeColumn, long value) {
415  long result = 0L;
416  switch (storeColumn.getType()) {
417  case Bit:
418  // bit fields are stored in two int32 fields
419  result |= (value >>> 32);
420  result |= (value << 32);
421  return result;
422  case Bigint:
423  case Bigunsigned:
424  return value;
425  case Date:
426  // the high order bytes are the little endian representation
427  // the original is 0000000000xxyyzz and the result is zzyyxx0000000000
428  long packDate = packDate(value);
429  result |= (packDate & ooooooff) << 56;
430  result |= (packDate & ooooffoo) << 40;
431  result |= (packDate & ooffoooo) << 24;
432  return result;
433  case Datetime:
434  return packDatetime(value);
435  case Time:
436  // the high order bytes are the little endian representation
437  // the original is 0000000000xxyyzz and the result is zzyyxx0000000000
438  long packTime = packTime(value);
439  result |= (packTime & ooooooff) << 56;
440  result |= (packTime & ooooffoo) << 40;
441  result |= (packTime & ooffoooo) << 24;
442  return result;
443  case Timestamp:
444  // timestamp is an int so put the value into the high bytes
445  // the original is 00000000tttttttt and the result is tttttttt00000000
446  return (value/1000L) << 32;
447  default:
448  throw new ClusterJUserException(local.message(
449  "ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
450  }
451  }
452 
453  public int convertByteValueForStorage(Column storeColumn, byte value) {
454  switch (storeColumn.getType()) {
455  case Bit:
456  // bit fields are always stored in an int32
457  return value & ooooooff;
458  case Tinyint:
459  case Year:
460  // other byte values are stored in the high byte of an int
461  return value << 24;
462  default:
463  throw new ClusterJUserException(local.message(
464  "ERR_Unsupported_Mapping", storeColumn.getType(), "byte"));
465  }
466  }
467 
468  public int convertShortValueForStorage(Column storeColumn, short value) {
469  switch (storeColumn.getType()) {
470  case Bit:
471  // bit fields are always stored in an int32
472  return value & ooooffff;
473  case Smallint:
474  // short values are in the top 16 bits of an int
475  return value << 16;
476  default:
477  throw new ClusterJUserException(local.message(
478  "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
479  }
480  }
481 
482  }:
483  /*
484  * Little Endian algorithms to convert NdbRecAttr buffer into primitive types
485  */
486  new EndianManager() {
487 
488  public boolean getBoolean(Column storeColumn, NdbRecAttr ndbRecAttr) {
489  switch (storeColumn.getType()) {
490  case Bit:
491  return ndbRecAttr.int32_value() == 1;
492  case Tinyint:
493  return ndbRecAttr.int8_value() == 1;
494  default:
495  throw new ClusterJUserException(
496  local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "boolean"));
497  }
498  }
499 
500  public byte getByte(Column storeColumn, NdbRecAttr ndbRecAttr) {
501  switch (storeColumn.getType()) {
502  case Bit:
503  case Tinyint:
504  case Year:
505  return ndbRecAttr.int8_value();
506  default:
507  throw new ClusterJUserException(
508  local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "byte"));
509  }
510  }
511  public short getShort(Column storeColumn, NdbRecAttr ndbRecAttr) {
512  switch (storeColumn.getType()) {
513  case Bit:
514  case Smallint:
515  return ndbRecAttr.short_value();
516  default:
517  throw new ClusterJUserException(
518  local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
519  }
520  }
521  public int getInt(Column storeColumn, NdbRecAttr ndbRecAttr) {
522  switch (storeColumn.getType()) {
523  case Bit:
524  case Int:
525  case Timestamp:
526  return ndbRecAttr.int32_value();
527  case Date:
528  return ndbRecAttr.u_medium_value();
529  case Time:
530  return ndbRecAttr.medium_value();
531  default:
532  throw new ClusterJUserException(
533  local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "int"));
534  }
535  }
536  public long getLong(Column storeColumn, NdbRecAttr ndbRecAttr) {
537  switch (storeColumn.getType()) {
538  case Bigint:
539  case Bigunsigned:
540  case Bit:
541  return ndbRecAttr.int64_value();
542  case Datetime:
543  return unpackDatetime(ndbRecAttr.int64_value());
544  case Timestamp:
545  return ndbRecAttr.int32_value() * 1000L;
546  case Date:
547  return unpackDate(ndbRecAttr.int32_value());
548  case Time:
549  return unpackTime(ndbRecAttr.int32_value());
550  default:
551  throw new ClusterJUserException(
552  local.message("ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
553  }
554  }
555 
562  public void put3byteInt(ByteBuffer byteBuffer, int value) {
563  byteBuffer.putInt(value);
564  byteBuffer.limit(3);
565  }
566 
567  public ByteBuffer convertValue(Column storeColumn, byte value) {
568  ByteBuffer result;
569  switch (storeColumn.getType()) {
570  case Bit:
571  // bit fields are always stored as int32
572  result = ByteBuffer.allocateDirect(4);
573  result.order(ByteOrder.nativeOrder());
574  result.putInt(value & 0xff);
575  result.flip();
576  return result;
577  case Tinyint:
578  case Year:
579  result = ByteBuffer.allocateDirect(1);
580  result.order(ByteOrder.nativeOrder());
581  result.put(value);
582  result.flip();
583  return result;
584  default:
585  throw new ClusterJUserException(local.message(
586  "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
587  }
588  }
589 
590  public ByteBuffer convertValue(Column storeColumn, short value) {
591  switch (storeColumn.getType()) {
592  case Bit:
593  case Smallint:
594  ByteBuffer result = ByteBuffer.allocateDirect(2);
595  result.order(ByteOrder.nativeOrder());
596  result.putShort(value);
597  result.flip();
598  return result;
599  default:
600  throw new ClusterJUserException(local.message(
601  "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
602  }
603  }
604 
605  public ByteBuffer convertValue(Column storeColumn, int value) {
606  switch (storeColumn.getType()) {
607  case Bit:
608  case Int:
609  ByteBuffer result = ByteBuffer.allocateDirect(4);
610  result.order(ByteOrder.nativeOrder());
611  result.putInt(value);
612  result.flip();
613  return result;
614  default:
615  throw new ClusterJUserException(local.message(
616  "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
617  }
618  }
619 
620  public ByteBuffer convertValue(Column storeColumn, long value) {
621  ByteBuffer result = ByteBuffer.allocateDirect(8);
622  return convertValue(storeColumn, value, result);
623  }
624 
625  public ByteBuffer convertValue(Column storeColumn, long value, ByteBuffer result) {
626  switch (storeColumn.getType()) {
627  case Bit:
628  case Bigint:
629  case Bigunsigned:
630  result.order(ByteOrder.LITTLE_ENDIAN);
631  result.putLong(value);
632  result.flip();
633  return result;
634  case Datetime:
635  result.order(ByteOrder.LITTLE_ENDIAN);
636  result.putLong(packDatetime(value));
637  result.flip();
638  return result;
639  case Timestamp:
640  result.order(ByteOrder.LITTLE_ENDIAN);
641  result.putInt((int)(value/1000L));
642  result.flip();
643  return result;
644  case Date:
645  result.order(ByteOrder.LITTLE_ENDIAN);
646  put3byteInt(result, packDate(value));
647  result.flip();
648  return result;
649  case Time:
650  result.order(ByteOrder.LITTLE_ENDIAN);
651  put3byteInt(result, packTime(value));
652  result.flip();
653  return result;
654  default:
655  throw new ClusterJUserException(local.message(
656  "ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
657  }
658  }
659 
660  public long convertLongValueForStorage(Column storeColumn, long value) {
661  switch (storeColumn.getType()) {
662  case Bit:
663  case Bigint:
664  case Bigunsigned:
665  return value;
666  case Datetime:
667  return packDatetime(value);
668  case Timestamp:
669  return value/1000L;
670  case Date:
671  return packDate(value);
672  case Time:
673  return packTime(value);
674  default:
675  throw new ClusterJUserException(local.message(
676  "ERR_Unsupported_Mapping", storeColumn.getType(), "long"));
677  }
678  }
679 
680  public int convertByteValueForStorage(Column storeColumn, byte value) {
681  switch (storeColumn.getType()) {
682  case Bit:
683  // bit fields are always stored as int32
684  case Tinyint:
685  case Year:
686  return value & ooooooff;
687  default:
688  throw new ClusterJUserException(local.message(
689  "ERR_Unsupported_Mapping", storeColumn.getType(), "byte"));
690  }
691  }
692 
693  public int convertShortValueForStorage(Column storeColumn, short value) {
694  switch (storeColumn.getType()) {
695  case Bit:
696  // bit fields are always stored as int32
697  case Smallint:
698  return value & ooooffff;
699  default:
700  throw new ClusterJUserException(local.message(
701  "ERR_Unsupported_Mapping", storeColumn.getType(), "short"));
702  }
703  }
704 
705  };
706 
707  /* Error codes that are not severe, and simply reflect expected conditions */
708  private static Set<Integer> NonSevereErrorCodes = new HashSet<Integer>();
709 
710  static {
711  NonSevereErrorCodes.add(4203); // Trying to set a NOT NULL attribute to NULL
712  NonSevereErrorCodes.add(4243); // Index not found
713  NonSevereErrorCodes.add(626); // Tuple did not exist
714  }
715 
716  protected static interface EndianManager {
717  public void put3byteInt(ByteBuffer byteBuffer, int value);
718  public int getInt(Column storeColumn, NdbRecAttr ndbRecAttr);
719  public short getShort(Column storeColumn, NdbRecAttr ndbRecAttr);
720  public long getLong(Column storeColumn, NdbRecAttr ndbRecAttr);
721  public byte getByte(Column storeColumn, NdbRecAttr ndbRecAttr);
722  public ByteBuffer convertValue(Column storeColumn, byte value);
723  public ByteBuffer convertValue(Column storeColumn, short value);
724  public ByteBuffer convertValue(Column storeColumn, int value);
725  public ByteBuffer convertValue(Column storeColumn, long value);
726  public boolean getBoolean(Column storeColumn, NdbRecAttr ndbRecAttr);
727  public long convertLongValueForStorage(Column storeColumn, long value);
728  public int convertByteValueForStorage(Column storeColumn, byte value);
729  public int convertShortValueForStorage(Column storeColumn, short value);
730  }
731 
737  protected static short swap(short value) {
738  return (short)((0x00ff & (value >>> 8)) |
739  (0xff00 & (value << 8)));
740  }
741 
747  protected static int swap(int value) {
748  return 0x000000ff & (value >>> 24) |
749  (0x0000ff00 & (value >>> 8)) |
750  (0x00ff0000 & (value << 8)) |
751  (0xff000000 & (value << 24));
752  }
753 
759  protected static long swap(long value) {
760  return ooooooooooooooff & (value >>> 56) |
761  (ooooooooooooffoo & (value >>> 40)) |
762  (ooooooooooffoooo & (value >>> 24)) |
763  (ooooooooffoooooo & (value >>> 8)) |
764  (ooooooffoooooooo & (value << 8)) |
765  (ooooffoooooooooo & (value << 24)) |
766  (ooffoooooooooooo & (value << 40)) |
767  (ffoooooooooooooo & (value << 56));
768  }
769 
770  protected static void throwError(Object returnCode, NdbErrorConst ndbError) {
771  throwError(returnCode, ndbError, "");
772  }
773 
774  protected static void throwError(Object returnCode, NdbErrorConst ndbError, String extra) {
775  String message = ndbError.message();
776  int code = ndbError.code();
777  int mysqlCode = ndbError.mysql_code();
778  int status = ndbError.status();
779  int classification = ndbError.classification();
780  String msg = local.message("ERR_NdbJTie", returnCode, code, mysqlCode,
781  status, classification, message, extra);
782  if (!NonSevereErrorCodes .contains(code)) {
783  logger.error(msg);
784  }
785  throw new ClusterJDatastoreException(msg, code, mysqlCode, status, classification);
786  }
787 
794  public static ByteBuffer convertValue(Column storeColumn, byte[] value) {
795  int dataLength = value.length;
796  int prefixLength = storeColumn.getPrefixLength();
797  ByteBuffer result;
798  switch (prefixLength) {
799  case 0:
800  int requiredLength = storeColumn.getColumnSpace();
801  if (dataLength > requiredLength) {
803  local.message("ERR_Data_Too_Long",
804  storeColumn.getName(), requiredLength, dataLength));
805  } else {
806  result = ByteBuffer.allocateDirect(requiredLength);
807  result.order(ByteOrder.nativeOrder());
808  result.put(value);
809  if (dataLength < requiredLength) {
810  // pad with 0x00 on right
811  result.put(ZERO_PAD, 0, requiredLength - dataLength);
812  }
813  }
814  break;
815  case 1:
816  if (dataLength > 255) {
818  local.message("ERR_Data_Too_Long",
819  storeColumn.getName(), "255", dataLength));
820  }
821  result = ByteBuffer.allocateDirect(prefixLength + dataLength);
822  result.order(ByteOrder.nativeOrder());
823  result.put((byte)dataLength);
824  result.put(value);
825  break;
826  case 2:
827  if (dataLength > 8000) {
829  local.message("ERR_Data_Too_Long",
830  storeColumn.getName(), "8000", dataLength));
831  }
832  result = ByteBuffer.allocateDirect(prefixLength + dataLength);
833  result.order(ByteOrder.nativeOrder());
834  result.put((byte)(dataLength%256));
835  result.put((byte)(dataLength/256));
836  result.put(value);
837  break;
838  default:
840  local.message("ERR_Unknown_Prefix_Length",
841  prefixLength, storeColumn.getName()));
842  }
843  result.flip();
844  return result;
845  }
846 
854  public static ByteBuffer convertValue(Column storeColumn, BigDecimal value) {
855  int precision = storeColumn.getPrecision();
856  int scale = storeColumn.getScale();
857  int bytesNeeded = getDecimalColumnSpace(precision, scale);
858  ByteBuffer result = ByteBuffer.allocateDirect(bytesNeeded);
859  // TODO this should be a policy option, perhaps an annotation to fail on truncation
860  BigDecimal scaledValue = value.setScale(scale, RoundingMode.HALF_UP);
861  // the new value has the same scale as the column
862  String stringRepresentation = scaledValue.toPlainString();
863  int length = stringRepresentation.length();
864  ByteBuffer byteBuffer = ByteBuffer.allocateDirect(length);
865  CharBuffer charBuffer = CharBuffer.wrap(stringRepresentation);
866  // basic encoding
867  charsetEncoder.encode(charBuffer, byteBuffer, true);
868  byteBuffer.flip();
869  int returnCode = Utils.decimal_str2bin(
870  byteBuffer, length, precision, scale, result, bytesNeeded);
871  byteBuffer.flip();
872  if (returnCode != 0) {
873  throw new ClusterJUserException(
874  local.message("ERR_String_To_Binary_Decimal",
875  returnCode, scaledValue, storeColumn.getName(), precision, scale));
876  }
877  return result;
878  }
879 
887  public static ByteBuffer convertValue(Column storeColumn, BigInteger value) {
888  int precision = storeColumn.getPrecision();
889  int scale = storeColumn.getScale();
890  int bytesNeeded = getDecimalColumnSpace(precision, scale);
891  ByteBuffer result = ByteBuffer.allocateDirect(bytesNeeded);
892  String stringRepresentation = value.toString();
893  int length = stringRepresentation.length();
894  ByteBuffer byteBuffer = ByteBuffer.allocateDirect(length);
895  CharBuffer charBuffer = CharBuffer.wrap(stringRepresentation);
896  // basic encoding
897  charsetEncoder.encode(charBuffer, byteBuffer, true);
898  byteBuffer.flip();
899  int returnCode = Utils.decimal_str2bin(
900  byteBuffer, length, precision, scale, result, bytesNeeded);
901  byteBuffer.flip();
902  if (returnCode != 0) {
903  throw new ClusterJUserException(
904  local.message("ERR_String_To_Binary_Decimal",
905  returnCode, stringRepresentation, storeColumn.getName(), precision, scale));
906  }
907  return result;
908  }
909 
916  public static ByteBuffer convertValue(Column storeColumn, double value) {
917  ByteBuffer result = ByteBuffer.allocateDirect(8);
918  result.order(ByteOrder.nativeOrder());
919  result.putDouble(value);
920  result.flip();
921  return result;
922  }
923 
930  public static ByteBuffer convertValue(Column storeColumn, float value) {
931  ByteBuffer result = ByteBuffer.allocateDirect(4);
932  result.order(ByteOrder.nativeOrder());
933  result.putFloat(value);
934  result.flip();
935  return result;
936  }
937 
944  public static ByteBuffer convertValue(Column storeColumn, byte value) {
945  return endianManager.convertValue(storeColumn, value);
946  }
947 
954  public static ByteBuffer convertValue(Column storeColumn, short value) {
955  return endianManager.convertValue(storeColumn, value);
956  }
957 
964  public static ByteBuffer convertValue(Column storeColumn, int value) {
965  return endianManager.convertValue(storeColumn, value);
966  }
967 
974  public static ByteBuffer convertValue(Column storeColumn, long value) {
975  return endianManager.convertValue(storeColumn, value);
976  }
977 
985  protected static ByteBuffer convertValue(Column storeColumn, String value) {
986  if (value == null) {
987  value = "";
988  }
989  CharSequence chars = value;
990  int offset = storeColumn.getPrefixLength();
991  if (offset == 0) {
992  chars = padString(value, storeColumn);
993  }
994  ByteBuffer byteBuffer = encodeToByteBuffer(chars, storeColumn.getCharsetNumber(), offset);
995  fixBufferPrefixLength(storeColumn.getName(), byteBuffer, offset);
996  if (logger.isDetailEnabled()) dumpBytesToLog(byteBuffer, byteBuffer.limit());
997  return byteBuffer;
998  }
999 
1006  protected static ByteBuffer convertValueForLikeFilter(Column storeColumn, String value) {
1007  if (value == null) {
1008  value = "";
1009  }
1010  CharSequence chars = value;
1011  ByteBuffer byteBuffer = encodeToByteBuffer(chars, storeColumn.getCharsetNumber(), 0);
1012  if (logger.isDetailEnabled()) dumpBytesToLog(byteBuffer, byteBuffer.limit());
1013  return byteBuffer;
1014  }
1015 
1022  protected static ByteBuffer convertValueForLikeFilter(Column storeColumn, byte[] value) {
1023  if (value == null) {
1024  value = EMPTY_BYTE_ARRAY;
1025  }
1026  ByteBuffer byteBuffer = ByteBuffer.allocateDirect(value.length);
1027  byteBuffer.put(value);
1028  byteBuffer.flip();
1029  if (logger.isDetailEnabled()) dumpBytesToLog(byteBuffer, byteBuffer.limit());
1030  return byteBuffer;
1031  }
1032 
1038  private static CharSequence padString(CharSequence value, Column storeColumn) {
1039  CharSequence chars = value;
1040  int suppliedLength = value.length();
1041  int requiredLength = storeColumn.getColumnSpace();
1042  if (suppliedLength > requiredLength) {
1043  throw new ClusterJUserException(local.message("ERR_Data_Too_Long",
1044  storeColumn.getName(), requiredLength, suppliedLength));
1045  } else if (suppliedLength < requiredLength) {
1046  // pad to fixed length
1047  StringBuilder buffer = new StringBuilder(requiredLength);
1048  buffer.append(value);
1049  buffer.append(SPACE_PAD, 0, requiredLength - suppliedLength);
1050  chars = buffer;
1051  }
1052  return chars;
1053  }
1054 
1060  public static void fixBufferPrefixLength(String columnName, ByteBuffer byteBuffer, int offset) {
1061  int limit = byteBuffer.limit();
1062  // go back and fill in the length field(s)
1063  // size of the output char* is current limit minus offset
1064  int length = limit - offset;
1065  byteBuffer.position(0);
1066  switch (offset) {
1067  case 0:
1068  break;
1069  case 1:
1070  if (length > 255) {
1071  throw new ClusterJUserException(local.message("ERR_Varchar_Too_Big",
1072  length, columnName));
1073  }
1074  byteBuffer.put((byte)(length % 256));
1075  break;
1076  case 2:
1077  if (length > 65535) {
1078  throw new ClusterJUserException(local.message("ERR_Varchar_Too_Big",
1079  length, columnName));
1080  }
1081  byteBuffer.put((byte)(length % 256));
1082  byteBuffer.put((byte)(length / 256));
1083  break;
1084  }
1085  // reset the position and limit for return
1086  byteBuffer.position(0);
1087  byteBuffer.limit(limit);
1088  }
1089 
1097  private static int packDate(long millis) {
1098  Calendar calendar = Calendar.getInstance();
1099  calendar.clear();
1100  calendar.setTimeInMillis(millis);
1101  int year = calendar.get(Calendar.YEAR);
1102  int month = calendar.get(Calendar.MONTH);
1103  int day = calendar.get(Calendar.DATE);
1104  int date = (year * 512) + ((month + 1) * 32) + day;
1105  return date;
1106  }
1107 
1115  private static int packTime(long millis) {
1116  Calendar calendar = Calendar.getInstance();
1117  calendar.clear();
1118  calendar.setTimeInMillis(millis);
1119  int year = calendar.get(Calendar.YEAR);
1120  int month = calendar.get(Calendar.MONTH);
1121  int day = calendar.get(Calendar.DATE);
1122  int hour = calendar.get(Calendar.HOUR);
1123  int minute = calendar.get(Calendar.MINUTE);
1124  int second = calendar.get(Calendar.SECOND);
1125  if (month != 0) {
1126  throw new ClusterJUserException(
1127  local.message("ERR_Write_Time_Domain", new java.sql.Time(millis), millis, year, month, day, hour, minute, second));
1128  }
1129  int time = ((day - 1) * 240000) + (hour * 10000) + (minute * 100) + second;
1130  return time;
1131  }
1132 
1140  protected static long packDatetime(long value) {
1141  Calendar calendar = Calendar.getInstance();
1142  calendar.clear();
1143  calendar.setTimeInMillis(value);
1144  long year = calendar.get(Calendar.YEAR);
1145  long month = calendar.get(Calendar.MONTH) + 1;
1146  long day = calendar.get(Calendar.DATE);
1147  long hour = calendar.get(Calendar.HOUR);
1148  long minute = calendar.get(Calendar.MINUTE);
1149  long second = calendar.get(Calendar.SECOND);
1150  long packedDatetime = (year * 10000000000L) + (month * 100000000L) + (day * 1000000L)
1151  + (hour * 10000L) + (minute * 100) + second;
1152  return packedDatetime;
1153  }
1154 
1160  public static String dumpBytes (byte[] bytes) {
1161  StringBuffer buffer = new StringBuffer("byte[");
1162  buffer.append(bytes.length);
1163  buffer.append("]: [");
1164  for (int i = 0; i < bytes.length; ++i) {
1165  buffer.append((int)bytes[i]);
1166  buffer.append(" ");
1167  }
1168  buffer.append("]");
1169  return buffer.toString();
1170  }
1171 
1177  public static String dumpBytes(ByteBuffer byteBuffer) {
1178  byteBuffer.mark();
1179  int length = byteBuffer.limit() - byteBuffer.position();
1180  byte[] dst = new byte[length];
1181  byteBuffer.get(dst);
1182  byteBuffer.reset();
1183  return dumpBytes(dst);
1184  }
1185 
1186  private static void dumpBytesToLog(ByteBuffer byteBuffer, int limit) {
1187  StringBuffer message = new StringBuffer("String position is: ");
1188  message.append(byteBuffer.position());
1189  message.append(" limit: ");
1190  message.append(byteBuffer.limit());
1191  message.append(" data [");
1192  while (byteBuffer.hasRemaining()) {
1193  message.append((int)byteBuffer.get());
1194  message.append(" ");
1195  }
1196  message.append("]");
1197  logger.detail(message.toString());
1198  byteBuffer.position(0);
1199  byteBuffer.limit(limit);
1200  }
1201 
1202  public static BigDecimal getDecimal(ByteBuffer byteBuffer, int length, int precision, int scale) {
1203  String decimal = null;
1204  try {
1205  decimal = getDecimalString(byteBuffer, length, precision, scale);
1206  return new BigDecimal(decimal);
1207  } catch (NumberFormatException nfe) {
1208  throw new ClusterJUserException(
1209  local.message("ERR_Number_Format", decimal, dump(decimal)));
1210  }
1211  }
1212 
1213  public static BigInteger getBigInteger(ByteBuffer byteBuffer, int length, int precision, int scale) {
1214  String decimal = null;
1215  try {
1216  decimal = getDecimalString(byteBuffer, length, precision, scale);
1217  return new BigInteger(decimal);
1218  } catch (NumberFormatException nfe) {
1219  throw new ClusterJUserException(
1220  local.message("ERR_Number_Format", decimal, dump(decimal)));
1221  }
1222  }
1223 
1232  public static String getDecimalString(ByteBuffer byteBuffer, int length, int precision, int scale) {
1233  // allow for decimal point and sign and one more for trailing null
1234  int capacity = precision + 3;
1235  ByteBuffer digits = ByteBuffer.allocateDirect(capacity);
1236  int returnCode = Utils.decimal_bin2str(byteBuffer, length, precision, scale, digits, capacity);
1237  if (returnCode != 0) {
1238  throw new ClusterJUserException(
1239  local.message("ERR_Binary_Decimal_To_String",
1240  returnCode, precision, scale, dumpBytes(byteBuffer)));
1241  }
1242  String string = null;
1243  // look for the end (null) of the result string
1244  for (int i = 0; i < digits.limit(); ++i) {
1245  if (digits.get(i) == 0) {
1246  // found the end; mark it so we only decode the answer characters
1247  digits.limit(i);
1248  break;
1249  }
1250  }
1251  try {
1252  // use basic decoding
1253  CharBuffer charBuffer = charsetDecoder.decode(digits);
1254  string = charBuffer.toString();
1255  return string;
1256  } catch (CharacterCodingException e) {
1258  local.message("ERR_Character_Encoding", string));
1259  }
1260 
1261  }
1262 
1268  public static long unpackDate(int packedDate) {
1269  int date = packedDate & 0x1f;
1270  packedDate = packedDate >>> 5;
1271  int month = (packedDate & 0x0f) - 1; // Month value is 0-based. e.g., 0 for January.
1272  int year = packedDate >>> 4;
1273  Calendar calendar = Calendar.getInstance();
1274  calendar.clear();
1275  calendar.set(year, month, date);
1276  return calendar.getTimeInMillis();
1277  }
1278 
1284  public static long unpackTime(int packedTime) {
1285  int second = packedTime % 100;
1286  packedTime /= 100;
1287  int minute = packedTime % 100;
1288  packedTime /= 100;
1289  int hour = packedTime % 24;
1290  int date = (packedTime / 24) + 1;
1291  if (date > 31) {
1292  throw new ClusterJUserException(
1293  local.message("ERR_Read_Time_Domain", packedTime, date, hour, minute, second));
1294  }
1295  Calendar calendar = Calendar.getInstance();
1296  calendar.clear();
1297  calendar.set(Calendar.DATE, date);
1298  calendar.set(Calendar.HOUR, hour);
1299  calendar.set(Calendar.MINUTE, minute);
1300  calendar.set(Calendar.SECOND, second);
1301  calendar.set(Calendar.MILLISECOND, 0);
1302  return calendar.getTimeInMillis();
1303  }
1304 
1312  protected static long unpackDatetime(long packedDatetime) {
1313  int second = (int)(packedDatetime % 100);
1314  packedDatetime /= 100;
1315  int minute = (int)(packedDatetime % 100);
1316  packedDatetime /= 100;
1317  int hour = (int)(packedDatetime % 100);
1318  packedDatetime /= 100;
1319  int day = (int)(packedDatetime % 100);
1320  packedDatetime /= 100;
1321  int month = (int)(packedDatetime % 100) - 1;
1322  int year = (int)(packedDatetime / 100);
1323  Calendar calendar = Calendar.getInstance();
1324  calendar.clear();
1325  calendar.set(Calendar.YEAR, year);
1326  calendar.set(Calendar.MONTH, month);
1327  calendar.set(Calendar.DATE, day);
1328  calendar.set(Calendar.HOUR, hour);
1329  calendar.set(Calendar.MINUTE, minute);
1330  calendar.set(Calendar.SECOND, second);
1331  calendar.set(Calendar.MILLISECOND, 0);
1332  return calendar.getTimeInMillis();
1333 
1334  }
1335 
1343  public static String decode(byte[] array, int collation) {
1344  if (array == null) return null;
1345  ByteBuffer byteBuffer = ByteBuffer.allocateDirect(array.length);
1346  byteBuffer.put(array);
1347  byteBuffer.flip();
1348  int inputLength = array.length;
1349  // TODO make this more reasonable
1350  int outputLength = inputLength * 4;
1351  ByteBuffer outputByteBuffer = ByteBuffer.allocateDirect(outputLength);
1352  int[] lengths = new int[] {inputLength, outputLength};
1353  int returnCode = charsetMap.recode(lengths, collation, collationUTF16,
1354  byteBuffer, outputByteBuffer);
1355  switch (returnCode) {
1356  case CharsetMapConst.RecodeStatus.RECODE_OK:
1357  outputByteBuffer.limit(lengths[1]);
1358  CharBuffer charBuffer = outputByteBuffer.asCharBuffer();
1359  return charBuffer.toString();
1360  case CharsetMapConst.RecodeStatus.RECODE_BAD_CHARSET:
1361  throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Charset",
1362  collation));
1363  case CharsetMapConst.RecodeStatus.RECODE_BAD_SRC:
1364  throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Source",
1365  collation, lengths[0]));
1366  case CharsetMapConst.RecodeStatus.RECODE_BUFF_TOO_SMALL:
1367  throw new ClusterJFatalInternalException(local.message("ERR_Decode_Buffer_Too_Small",
1368  collation, inputLength, outputLength, lengths[0], lengths[1]));
1369  default:
1370  throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Return_Code",
1371  returnCode));
1372  }
1373  }
1374 
1382  protected static String decode(ByteBuffer inputByteBuffer, int collation) {
1383  int inputLength = inputByteBuffer.limit() - inputByteBuffer.position();
1384  int outputLength = inputLength * 2;
1385  ByteBuffer outputByteBuffer = ByteBuffer.allocateDirect(outputLength);
1386  int[] lengths = new int[] {inputLength, outputLength};
1387  int returnCode = charsetMap.recode(lengths, collation, collationUTF16,
1388  inputByteBuffer, outputByteBuffer);
1389  switch (returnCode) {
1390  case CharsetMapConst.RecodeStatus.RECODE_OK:
1391  outputByteBuffer.limit(lengths[1]);
1392  CharBuffer charBuffer = outputByteBuffer.asCharBuffer();
1393  return charBuffer.toString();
1394  case CharsetMapConst.RecodeStatus.RECODE_BAD_CHARSET:
1395  throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Charset",
1396  collation));
1397  case CharsetMapConst.RecodeStatus.RECODE_BAD_SRC:
1398  throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Source",
1399  collation, lengths[0]));
1400  case CharsetMapConst.RecodeStatus.RECODE_BUFF_TOO_SMALL:
1401  throw new ClusterJFatalInternalException(local.message("ERR_Decode_Buffer_Too_Small",
1402  collation, inputLength, outputLength, lengths[0], lengths[1]));
1403  default:
1404  throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Return_Code",
1405  returnCode));
1406  }
1407  }
1408 
1409 
1417  public static byte[] encode(String string, int collation) {
1418  ByteBuffer encoded = encodeToByteBuffer(string, collation, 0);
1419  int length = encoded.limit();
1420  byte[] result = new byte[length];
1421  encoded.get(result);
1422  return result;
1423  }
1424 
1433  public static ByteBuffer encodeToByteBuffer(CharSequence string, int collation, int prefixLength) {
1434  if (string == null) return null;
1435  int inputLength = (string.length() * 2);
1436  ByteBuffer inputByteBuffer = ByteBuffer.allocateDirect(inputLength);
1437  CharBuffer charBuffer = inputByteBuffer.asCharBuffer();
1438  charBuffer.append(string);
1439  int outputLength = (2 * inputLength) + prefixLength;
1440  ByteBuffer outputByteBuffer = ByteBuffer.allocateDirect(outputLength);
1441  outputByteBuffer.position(prefixLength);
1442  int[] lengths = new int[] {inputLength, outputLength - prefixLength};
1443  int returnCode = charsetMap.recode(lengths, collationUTF16, collation,
1444  inputByteBuffer, outputByteBuffer);
1445 
1446  switch (returnCode) {
1447  case CharsetMapConst.RecodeStatus.RECODE_OK:
1448  outputByteBuffer.limit(prefixLength + lengths[1]);
1449  return outputByteBuffer;
1450  case CharsetMapConst.RecodeStatus.RECODE_BAD_CHARSET:
1451  throw new ClusterJFatalInternalException(local.message("ERR_Encode_Bad_Charset",
1452  collation));
1453  case CharsetMapConst.RecodeStatus.RECODE_BAD_SRC:
1454  throw new ClusterJFatalInternalException(local.message("ERR_Encode_Bad_Source",
1455  collation, lengths[0]));
1456  case CharsetMapConst.RecodeStatus.RECODE_BUFF_TOO_SMALL:
1457  throw new ClusterJFatalInternalException(local.message("ERR_Encode_Buffer_Too_Small",
1458  collation, inputLength, outputLength, lengths[0], lengths[1]));
1459  default:
1460  throw new ClusterJFatalInternalException(local.message("ERR_Encode_Bad_Return_Code",
1461  returnCode));
1462  }
1463  }
1464 
1472  public static ByteBuffer encode(String input, Column storeColumn, BufferManager bufferManager) {
1473  int collation = storeColumn.getCharsetNumber();
1474  CharsetConverter charsetConverter = getCharsetConverter(collation);
1475  CharSequence chars = input;
1476  int prefixLength = storeColumn.getPrefixLength();
1477  if (prefixLength == 0) {
1478  chars = padString(input, storeColumn);
1479  }
1480  return charsetConverter.encode(storeColumn.getName(), chars, collation, prefixLength, bufferManager);
1481  }
1482 
1491  public static String decode(ByteBuffer inputByteBuffer, int collation, BufferManager bufferManager) {
1492  CharsetConverter charsetConverter = getCharsetConverter(collation);
1493  return charsetConverter.decode(inputByteBuffer, collation, bufferManager);
1494  }
1495 
1501  private static CharsetConverter getCharsetConverter(int collation) {
1502  // must be synchronized because the charsetConverters is not synchronized
1503  // we avoid a race condition where a charset converter is in the process
1504  // of being created and it's partially visible by another thread in charsetConverters
1505  synchronized (charsetConverters) {
1506  if (collation + 1 > charsetConverters.length) {
1507  // unlikely; only if collations are added beyond existing collation number
1508  String charsetName = charsetMap.getName(collation);
1509  logger.warn(local.message("ERR_Charset_Number_Too_Big", collation, charsetName,
1510  MAXIMUM_MYSQL_COLLATION_NUMBER));
1511  return charsetConverterMultibyte;
1512  }
1513  CharsetConverter result = charsetConverters[collation];
1514  if (result == null) {
1515  result = addCollation(collation);
1516  }
1517  return result;
1518  }
1519  }
1520 
1527  private static CharsetConverter addCollation(int collation) {
1528  if (isMultibyteCollation(collation)) {
1529  return charsetConverters[collation] = charsetConverterMultibyte;
1530  }
1531  String charsetName = charsetMap.getMysqlName(collation);
1532  CharsetConverter charsetConverter = new SingleByteCharsetConverter(collation);
1533  int[] collations = collationPeersMap.get(charsetName);
1534  if (collations == null) {
1535  // unlikely; only if a new collation is added
1536  collations = new int[] {collation};
1537  collationPeersMap.put(charsetName, collations);
1538  logger.warn(local.message("WARN_Unknown_Collation", collation, charsetName));
1539  return charsetConverter;
1540  }
1541  for (int peer: collations) {
1542  // for each collation that shares the same charset name, set the charset converter
1543  logger.info("Adding charset converter " + charsetName + " for collation " + peer);
1544  charsetConverters[peer] = charsetConverter;
1545  }
1546  return charsetConverter;
1547  }
1548 
1555  private static Boolean isMultibyteCollation(int collation) {
1556  boolean[] multibyte = charsetMap.isMultibyte(collation);
1557  return (multibyte == null)?null:multibyte[0];
1558  }
1559 
1562  protected interface CharsetConverter {
1563 
1564  ByteBuffer encode(String columnName, CharSequence input, int collation, int prefixLength, BufferManager bufferManager);
1565 
1566  String decode(ByteBuffer inputByteBuffer, int collation, BufferManager bufferManager);
1567  }
1568 
1572  protected static class MultiByteCharsetConverter implements CharsetConverter {
1573 
1583  public ByteBuffer encode(String columnName, CharSequence input, int collation, int prefixLength, BufferManager bufferManager) {
1584  // input length in bytes is twice String length
1585  int inputLength = input.length() * 2;
1586  ByteBuffer inputByteBuffer = bufferManager.copyStringToByteBuffer(input);
1587  boolean done = false;
1588  // first try with output length equal input length
1589  int sizeNeeded = inputLength;
1590  while (!done) {
1591  ByteBuffer outputByteBuffer = bufferManager.getStringStorageBuffer(sizeNeeded);
1592  int outputLength = outputByteBuffer.limit();
1593  outputByteBuffer.position(prefixLength);
1594  int[] lengths = new int[] {inputLength, outputLength - prefixLength};
1595  int returnCode = charsetMap.recode(lengths, collationUTF16, collation,
1596  inputByteBuffer, outputByteBuffer);
1597  switch (returnCode) {
1598  case CharsetMapConst.RecodeStatus.RECODE_OK:
1599  outputByteBuffer.limit(prefixLength + lengths[1]);
1600  outputByteBuffer.position(0);
1601  fixBufferPrefixLength(columnName, outputByteBuffer, prefixLength);
1602  return outputByteBuffer;
1603  case CharsetMapConst.RecodeStatus.RECODE_BAD_CHARSET:
1604  throw new ClusterJFatalInternalException(local.message("ERR_Encode_Bad_Charset",
1605  collation));
1606  case CharsetMapConst.RecodeStatus.RECODE_BAD_SRC:
1607  throw new ClusterJFatalInternalException(local.message("ERR_Encode_Bad_Source",
1608  collation, lengths[0]));
1609  case CharsetMapConst.RecodeStatus.RECODE_BUFF_TOO_SMALL:
1610  // loop increasing output buffer size until success or run out of memory...
1611  sizeNeeded = sizeNeeded * 3 / 2;
1612  break;
1613  default:
1614  throw new ClusterJFatalInternalException(local.message("ERR_Encode_Bad_Return_Code",
1615  returnCode));
1616  }
1617  }
1618  return null; // to make compiler happy; we never get here
1619  }
1620 
1630  public String decode(ByteBuffer inputByteBuffer, int collation, BufferManager bufferManager) {
1631  int inputLength = inputByteBuffer.limit() - inputByteBuffer.position();
1632  int sizeNeeded = inputLength * 4;
1633  boolean done = false;
1634  while (!done) {
1635  ByteBuffer outputByteBuffer = bufferManager.getStringByteBuffer(sizeNeeded);
1636  CharBuffer outputCharBuffer = bufferManager.getStringCharBuffer();
1637  int outputLength = outputByteBuffer.capacity();
1638  outputByteBuffer.position(0);
1639  outputByteBuffer.limit(outputLength);
1640  int[] lengths = new int[] {inputLength, outputLength};
1641  int returnCode = charsetMap.recode(lengths, collation, collationUTF16,
1642  inputByteBuffer, outputByteBuffer);
1643  switch (returnCode) {
1644  case CharsetMapConst.RecodeStatus.RECODE_OK:
1645  outputCharBuffer.position(0);
1646  // each output character is two bytes for UTF16
1647  outputCharBuffer.limit(lengths[1] / 2);
1648  return outputCharBuffer.toString();
1649  case CharsetMapConst.RecodeStatus.RECODE_BAD_CHARSET:
1650  throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Charset",
1651  collation));
1652  case CharsetMapConst.RecodeStatus.RECODE_BAD_SRC:
1653  throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Source",
1654  collation, lengths[0]));
1655  case CharsetMapConst.RecodeStatus.RECODE_BUFF_TOO_SMALL:
1656  // try a bigger buffer
1657  sizeNeeded = sizeNeeded * 3 / 2;
1658  break;
1659  default:
1660  throw new ClusterJFatalInternalException(local.message("ERR_Decode_Bad_Return_Code",
1661  returnCode));
1662  }
1663  }
1664  return null; // never reached; make the compiler happy
1665  }
1666  }
1667 
1670  protected static class SingleByteCharsetConverter implements CharsetConverter {
1671 
1672  private static final int BYTE_RANGE = (1 + Byte.MAX_VALUE) - Byte.MIN_VALUE;
1673  private static byte[] allBytes = new byte[BYTE_RANGE];
1674  // The initial charToByteMap, with all char mappings mapped
1675  // to (byte) '?', so that unknown characters are mapped to '?'
1676  // instead of '\0' (which means end-of-string to MySQL).
1677  private static byte[] unknownCharsMap = new byte[65536];
1678 
1679  static {
1680  // initialize allBytes with all possible byte values
1681  for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
1682  allBytes[i - Byte.MIN_VALUE] = (byte) i;
1683  }
1684  // initialize unknownCharsMap to '?' in each position
1685  for (int i = 0; i < unknownCharsMap.length; i++) {
1686  unknownCharsMap[i] = (byte) '?'; // use something 'sane' for unknown chars
1687  }
1688  }
1689 
1691  private char[] byteToChars = new char[BYTE_RANGE];
1692 
1694  private byte[] charToBytes = new byte[65536];
1695 
1700  public SingleByteCharsetConverter(int collation) {
1701  ByteBuffer allBytesByteBuffer = ByteBuffer.allocateDirect(256);
1702  allBytesByteBuffer.put(allBytes);
1703  allBytesByteBuffer.flip();
1704  String allBytesString = Utility.decode(allBytesByteBuffer, collation);
1705  if (allBytesString.length() != 256) {
1706  String charsetName = charsetMap.getName(collation);
1707  throw new ClusterJFatalInternalException(local.message("ERR_Bad_Charset_Decode_All_Chars",
1708  collation, charsetName, allBytesString.length()));
1709  }
1710  int allBytesLen = allBytesString.length();
1711 
1712  System.arraycopy(unknownCharsMap, 0, this.charToBytes, 0,
1713  this.charToBytes.length);
1714 
1715  for (int i = 0; i < BYTE_RANGE && i < allBytesLen; i++) {
1716  char c = allBytesString.charAt(i);
1717  this.byteToChars[i] = c;
1718  this.charToBytes[c] = allBytes[i];
1719  }
1720  }
1721 
1730  public ByteBuffer encode(String columnName, CharSequence input, int collation, int prefixLength, BufferManager bufferManager) {
1731  int length = input.length();
1732  byte[] outputBytes = new byte[length];
1733  for (int position = 0; position < length; ++position) {
1734  outputBytes[position] = charToBytes[input.charAt(position)];
1735  }
1736  // input is now encoded; copy to shared output buffer
1737  ByteBuffer outputByteBuffer = bufferManager.getStringStorageBuffer(length + prefixLength);
1738  // skip over prefix
1739  outputByteBuffer.position(prefixLength);
1740  outputByteBuffer.put(outputBytes);
1741  outputByteBuffer.flip();
1742  // adjust the length prefix
1743  fixBufferPrefixLength(columnName, outputByteBuffer, prefixLength);
1744  return outputByteBuffer;
1745  }
1746 
1756  public String decode(ByteBuffer inputByteBuffer, int collation, BufferManager bufferManager) {
1757  int inputLimit = inputByteBuffer.limit();
1758  int inputPosition = inputByteBuffer.position();
1759  int inputSize = inputLimit- inputPosition;
1760  byte[] inputBytes = new byte[inputSize];
1761  inputByteBuffer.get(inputBytes);
1762  char[] outputChars = new char[inputSize];
1763  for (int position = 0; position < inputSize; ++position) {
1764  outputChars[position] = byteToChars[inputBytes[position] - Byte.MIN_VALUE];
1765  }
1766  // input is now decoded; create a new String from the output
1767  String result = new String(outputChars);
1768  return result;
1769  }
1770  }
1771 
1772  private static String dump(String string) {
1773  StringBuffer buffer = new StringBuffer("[");
1774  for (int i = 0; i < string.length(); ++i) {
1775  int theCharacter = string.charAt(i);
1776  buffer.append(theCharacter);
1777  buffer.append(" ");
1778  }
1779  buffer.append("]");
1780  return buffer.toString();
1781  }
1782 
1790  static int[] howManyBytesNeeded = new int[] {0, 1, 1, 2, 2, 3, 3, 4, 4, 4,
1791  5, 5, 6, 6, 7, 7, 8, 8, 8,
1792  9, 9, 10, 10, 11, 11, 12, 12, 12,
1793  13, 13, 14, 14, 15, 15, 16, 16, 16,
1794  17, 17, 18, 18, 19, 19, 20, 20, 20,
1795  21, 21, 22, 22, 23, 23, 24, 24, 24,
1796  25, 25, 26, 26, 27, 27, 28, 28, 28,
1797  29, 29};
1804  public static int getDecimalColumnSpace(int precision, int scale) {
1805  int howManyBytesNeededForIntegral = howManyBytesNeeded[precision - scale];
1806  int howManyBytesNeededForFraction = howManyBytesNeeded[scale];
1807  int result = howManyBytesNeededForIntegral + howManyBytesNeededForFraction;
1808  return result;
1809  }
1810 
1817  public static boolean getBoolean(Column storeColumn, NdbRecAttr ndbRecAttr) {
1818  return endianManager.getBoolean(storeColumn, ndbRecAttr);
1819  }
1820 
1827  public static byte getByte(Column storeColumn, NdbRecAttr ndbRecAttr) {
1828  return endianManager.getByte(storeColumn, ndbRecAttr);
1829  }
1830 
1837  public static short getShort(Column storeColumn, NdbRecAttr ndbRecAttr) {
1838  return endianManager.getShort(storeColumn, ndbRecAttr);
1839  }
1840 
1847  public static int getInt(Column storeColumn, NdbRecAttr ndbRecAttr) {
1848  return endianManager.getInt(storeColumn, ndbRecAttr);
1849  }
1850 
1857  public static long getLong(Column storeColumn, NdbRecAttr ndbRecAttr) {
1858  return endianManager.getLong(storeColumn, ndbRecAttr);
1859  }
1860 
1869  public static long convertLongValueForStorage(Column storeColumn, long value) {
1870  return endianManager.convertLongValueForStorage(storeColumn, value);
1871  }
1872 
1881  public static int convertByteValueForStorage(Column storeColumn, byte value) {
1882  return endianManager.convertByteValueForStorage(storeColumn, value);
1883  }
1884 
1893  public static int convertShortValueForStorage(Column storeColumn,
1894  short value) {
1895  return endianManager.convertShortValueForStorage(storeColumn, value);
1896  }
1897 
1898 }