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