1 /* 2 Copyright (c) 2010, 2021, Oracle and/or its affiliates. 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.math.BigDecimal; 28 import java.math.BigInteger; 29 30 import java.nio.ByteBuffer; 31 import java.nio.ByteOrder; 32 33 import java.util.List; 34 35 import com.mysql.clusterj.ClusterJFatalInternalException; 36 37 import com.mysql.clusterj.core.store.Blob; 38 import com.mysql.clusterj.core.store.Column; 39 import com.mysql.clusterj.core.store.ResultData; 40 41 import com.mysql.clusterj.core.util.I18NHelper; 42 import com.mysql.clusterj.core.util.Logger; 43 import com.mysql.clusterj.core.util.LoggerFactoryService; 44 import com.mysql.clusterj.tie.DbImpl.BufferManager; 45 46 import com.mysql.ndbjtie.ndbapi.NdbBlob; 47 import com.mysql.ndbjtie.ndbapi.NdbErrorConst; 48 import com.mysql.ndbjtie.ndbapi.NdbOperation; 49 import com.mysql.ndbjtie.ndbapi.NdbRecAttr; 50 51 /** 52 * 53 */ 54 class ResultDataImpl implements ResultData { 55 56 /** My message translator */ 57 static final I18NHelper local = I18NHelper 58 .getInstance(ResultDataImpl.class); 59 60 /** My logger */ 61 static final Logger logger = LoggerFactoryService.getFactory() 62 .getInstance(ResultDataImpl.class); 63 64 /** Flags for iterating a scan */ 65 protected final int RESULT_READY = 0; 66 protected final int SCAN_FINISHED = 1; 67 protected final int CACHE_EMPTY = 2; 68 69 /** The NdbOperation that defines the result */ 70 private NdbOperation ndbOperation = null; 71 72 /** The NdbRecAttrs that specify the columns to retrieve */ 73 private NdbRecAttr[] ndbRecAttrs = null; 74 75 /** The flag indicating that there are no more results */ 76 private boolean nextDone; 77 78 /** The ByteBuffer containing the results, possibly obtained from buffer manager */ 79 private ByteBuffer byteBuffer = null; 80 81 /** Offsets into the ByteBuffer containing the results */ 82 private int[] offsets = null; 83 84 /** Lengths of the fields in the ByteBuffer containing the results */ 85 private int[] lengths = null; 86 87 /** The Columns in this result */ 88 private final Column[] storeColumns; 89 90 /** The buffer manager */ 91 private BufferManager bufferManager; 92 93 /** The cluster connection */ 94 private ClusterConnectionImpl clusterConnection; 95 96 /** Construct the ResultDataImpl based on an NdbOperation, a list of columns 97 * to include in the result, and the pre-computed buffer layout for the result. 98 * @param ndbOperation the NdbOperation 99 * @param storeColumns the columns in the result 100 * @param maximumColumnId the largest column id 101 * @param bufferSize the size of the buffer needed 102 * @param offsets the array of offsets indexed by column id 103 * @param lengths the array of lengths indexed by column id 104 * @param bufferManager the buffer manager 105 * @param allocateNew true to allocate a new (unshared) result buffer 106 */ ResultDataImpl(NdbOperation ndbOperation, List<Column> storeColumns, int maximumColumnId, int bufferSize, int[] offsets, int[] lengths, BufferManager bufferManager, boolean allocateNew)107 public ResultDataImpl(NdbOperation ndbOperation, List<Column> storeColumns, 108 int maximumColumnId, int bufferSize, int[] offsets, int[] lengths, 109 BufferManager bufferManager, boolean allocateNew) { 110 this.ndbOperation = ndbOperation; 111 this.bufferManager = bufferManager; 112 // save the column list 113 this.storeColumns = storeColumns.toArray(new Column[storeColumns.size()]); 114 this.offsets = offsets; 115 this.lengths = lengths; 116 if (allocateNew) { 117 byteBuffer = ByteBuffer.allocateDirect(bufferSize); 118 } else { 119 byteBuffer = bufferManager.getResultDataBuffer(bufferSize); 120 } 121 byteBuffer.order(ByteOrder.nativeOrder()); 122 // iterate the list of store columns and allocate an NdbRecAttr (via getValue) for each 123 ndbRecAttrs = new NdbRecAttr[maximumColumnId + 1]; 124 for (Column storeColumn: storeColumns) { 125 NdbRecAttr ndbRecAttr = null; 126 int columnId = storeColumn.getColumnId(); 127 byteBuffer.position(offsets[columnId]); 128 if (lengths[columnId] == 0) { 129 // TODO: to help profiling 130 ndbRecAttr = ndbOperation.getValue(columnId, null); 131 // ndbRecAttr = getValue(ndbOperation, columnId, null); 132 } else { 133 ndbRecAttr = ndbOperation.getValue(columnId, byteBuffer); 134 // ndbRecAttr = getValue(ndbOperation, columnId, byteBuffer); 135 } 136 handleError(ndbRecAttr, ndbOperation); 137 ndbRecAttrs[columnId] = ndbRecAttr; 138 } 139 } 140 next()141 public boolean next() { 142 // NdbOperation has exactly zero or one result. ScanResultDataImpl handles scans... 143 NdbErrorConst error = ndbOperation.getNdbError(); 144 // if the ndbOperation reports an error there is no result 145 int errorCode = error.code(); 146 if (errorCode != 0) { 147 setNoResult(); 148 } 149 if (nextDone) { 150 return false; 151 } else { 152 nextDone = true; 153 return true; 154 } 155 } 156 getBlob(int column)157 public Blob getBlob(int column) { 158 return getBlob(storeColumns[column]); 159 } 160 getBlob(Column storeColumn)161 public Blob getBlob(Column storeColumn) { 162 NdbBlob ndbBlob = ndbOperation.getBlobHandle(storeColumn.getColumnId()); 163 handleError(ndbBlob, ndbOperation); 164 return new BlobImpl(ndbBlob, clusterConnection.getByteBufferPool()); 165 } 166 getBoolean(int column)167 public boolean getBoolean(int column) { 168 return getBoolean(storeColumns[column]); 169 } 170 getBoolean(Column storeColumn)171 public boolean getBoolean(Column storeColumn) { 172 int index = storeColumn.getColumnId(); 173 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 174 return Utility.getBoolean(storeColumn, ndbRecAttr); 175 } 176 getBooleans(int column)177 public boolean[] getBooleans(int column) { 178 return getBooleans(storeColumns[column]); 179 } 180 getBooleans(Column storeColumn)181 public boolean[] getBooleans(Column storeColumn) { 182 throw new ClusterJFatalInternalException(local.message("ERR_Not_Implemented")); 183 } 184 getByte(int column)185 public byte getByte(int column) { 186 return getByte(storeColumns[column]); 187 } 188 getByte(Column storeColumn)189 public byte getByte(Column storeColumn) { 190 int index = storeColumn.getColumnId(); 191 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 192 return Utility.getByte(storeColumn, ndbRecAttr); 193 } 194 getShort(int column)195 public short getShort(int column) { 196 return getShort(storeColumns[column]); 197 } 198 getShort(Column storeColumn)199 public short getShort(Column storeColumn) { 200 int index = storeColumn.getColumnId(); 201 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 202 return Utility.getShort(storeColumn, ndbRecAttr); 203 } 204 getInt(int column)205 public int getInt(int column) { 206 return getInt(storeColumns[column]); 207 } 208 getInt(Column storeColumn)209 public int getInt(Column storeColumn) { 210 int index = storeColumn.getColumnId(); 211 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 212 return Utility.getInt(storeColumn, ndbRecAttr); 213 } 214 getLong(int column)215 public long getLong(int column) { 216 return getLong(storeColumns[column]); 217 } 218 getLong(Column storeColumn)219 public long getLong(Column storeColumn) { 220 int index = storeColumn.getColumnId(); 221 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 222 return Utility.getLong(storeColumn, ndbRecAttr.int64_value()); 223 } 224 getFloat(int column)225 public float getFloat(int column) { 226 return getFloat(storeColumns[column]); 227 } 228 getFloat(Column storeColumn)229 public float getFloat(Column storeColumn) { 230 int index = storeColumn.getColumnId(); 231 float result = ndbRecAttrs[index].float_value(); 232 return result; 233 } 234 getDouble(int column)235 public double getDouble(int column) { 236 return getDouble(storeColumns[column]); 237 } 238 getDouble(Column storeColumn)239 public double getDouble(Column storeColumn) { 240 int index = storeColumn.getColumnId(); 241 double result = ndbRecAttrs[index].double_value(); 242 return result; 243 } 244 getString(int column)245 public String getString(int column) { 246 return getString(storeColumns[column]); 247 } 248 getString(Column storeColumn)249 public String getString(Column storeColumn) { 250 int index = storeColumn.getColumnId(); 251 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 252 if (ndbRecAttr.isNULL() == 1) return null; 253 int prefixLength = storeColumn.getPrefixLength(); 254 int actualLength; 255 int offset = offsets[index]; 256 byteBuffer.limit(byteBuffer.capacity()); 257 switch (prefixLength) { 258 case 0: 259 actualLength = lengths[index]; 260 break; 261 case 1: 262 actualLength = (byteBuffer.get(offset) + 256) % 256; 263 offset += 1; 264 break; 265 case 2: 266 actualLength = (byteBuffer.get(offset) + 256) % 256; 267 int length2 = (byteBuffer.get(offset + 1) + 256) % 256; 268 actualLength += 256 * length2; 269 offset += 2; 270 break; 271 default: 272 throw new ClusterJFatalInternalException( 273 local.message("ERR_Invalid_Prefix_Length", prefixLength)); 274 } 275 276 byteBuffer.position(offset); 277 byteBuffer.limit(offset + actualLength); 278 279 String result = Utility.decode(byteBuffer, storeColumn.getCharsetNumber(), bufferManager); 280 byteBuffer.clear(); 281 return result; 282 } 283 getBytes(int column)284 public byte[] getBytes(int column) { 285 return getBytes(storeColumns[column]); 286 } 287 getBytes(Column storeColumn)288 public byte[] getBytes(Column storeColumn) { 289 int index = storeColumn.getColumnId(); 290 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 291 if (ndbRecAttr.isNULL() == 1) return null; 292 int prefixLength = storeColumn.getPrefixLength(); 293 int actualLength = lengths[index]; 294 int offset = offsets[index]; 295 switch (prefixLength) { 296 case 0: 297 break; 298 case 1: 299 actualLength = (byteBuffer.get(offset) + 256) % 256; 300 offset += 1; 301 break; 302 case 2: 303 actualLength = (byteBuffer.get(offset) + 256) % 256; 304 int length2 = (byteBuffer.get(offset + 1) + 256) % 256; 305 actualLength += 256 * length2; 306 offset += 2; 307 break; 308 default: 309 throw new ClusterJFatalInternalException( 310 local.message("ERR_Invalid_Prefix_Length", prefixLength)); 311 } 312 byteBuffer.position(offset); 313 byte[] result = new byte[actualLength]; 314 byteBuffer.get(result); 315 return result; 316 } 317 318 getObject(int column)319 public Object getObject(int column) { 320 return getObject(storeColumns[column]); 321 } 322 getObject(Column storeColumn)323 public Object getObject(Column storeColumn) { 324 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur")); 325 } 326 wasNull(Column storeColumn)327 public boolean wasNull(Column storeColumn) { 328 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur")); 329 } 330 getObjectBoolean(int column)331 public Boolean getObjectBoolean(int column) { 332 return getObjectBoolean(storeColumns[column]); 333 } 334 getObjectBoolean(Column storeColumn)335 public Boolean getObjectBoolean(Column storeColumn) { 336 int index = storeColumn.getColumnId(); 337 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 338 if (ndbRecAttr.isNULL() == 1) { 339 return null; 340 } else { 341 byte value = ndbRecAttr.int8_value(); 342 Boolean result = (Boolean.valueOf((value & 0x01) == 0x01)); 343 return result; 344 } 345 } 346 getObjectByte(int column)347 public Byte getObjectByte(int column) { 348 return getObjectByte(storeColumns[column]); 349 } 350 getObjectByte(Column storeColumn)351 public Byte getObjectByte(Column storeColumn) { 352 int index = storeColumn.getColumnId(); 353 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 354 return (ndbRecAttr.isNULL() == 1)?null:Utility.getByte(storeColumn, ndbRecAttr); 355 } 356 getObjectShort(int column)357 public Short getObjectShort(int column) { 358 return getObjectShort(storeColumns[column]); 359 } 360 getObjectShort(Column storeColumn)361 public Short getObjectShort(Column storeColumn) { 362 int index = storeColumn.getColumnId(); 363 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 364 return (ndbRecAttr.isNULL() == 1)?null:Utility.getShort(storeColumn, ndbRecAttr); 365 } 366 getObjectInteger(int column)367 public Integer getObjectInteger(int column) { 368 return getObjectInteger(storeColumns[column]); 369 } 370 getObjectInteger(Column storeColumn)371 public Integer getObjectInteger(Column storeColumn) { 372 int index = storeColumn.getColumnId(); 373 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 374 return (ndbRecAttr.isNULL() == 1)?null:Utility.getInt(storeColumn, ndbRecAttr); 375 } 376 getObjectLong(int column)377 public Long getObjectLong(int column) { 378 return getObjectLong(storeColumns[column]); 379 } 380 getObjectLong(Column storeColumn)381 public Long getObjectLong(Column storeColumn) { 382 int index = storeColumn.getColumnId(); 383 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 384 return (ndbRecAttr.isNULL() == 1)?null:Utility.getLong(storeColumn, ndbRecAttr.int64_value()); 385 } 386 getObjectFloat(int column)387 public Float getObjectFloat(int column) { 388 return getObjectFloat(storeColumns[column]); 389 } 390 getObjectFloat(Column storeColumn)391 public Float getObjectFloat(Column storeColumn) { 392 int index = storeColumn.getColumnId(); 393 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 394 return (ndbRecAttr.isNULL() == 1)?null:getFloat(storeColumn); 395 } 396 getObjectDouble(int column)397 public Double getObjectDouble(int column) { 398 return getObjectDouble(storeColumns[column]); 399 } 400 getObjectDouble(Column storeColumn)401 public Double getObjectDouble(Column storeColumn) { 402 int index = storeColumn.getColumnId(); 403 NdbRecAttr ndbRecAttr = ndbRecAttrs[index]; 404 return (ndbRecAttr.isNULL() == 1)?null:getDouble(storeColumn); 405 } 406 getBigInteger(int column)407 public BigInteger getBigInteger(int column) { 408 return getBigInteger(storeColumns[column]); 409 } 410 getBigInteger(Column storeColumn)411 public BigInteger getBigInteger(Column storeColumn) { 412 int index = storeColumn.getColumnId(); 413 int offset = offsets[index]; 414 int precision = storeColumn.getPrecision(); 415 int scale = storeColumn.getScale(); 416 int length = Utility.getDecimalColumnSpace(precision, scale); 417 byteBuffer.position(offset); 418 return Utility.getBigInteger(byteBuffer, length, precision, scale); 419 } 420 getDecimal(int column)421 public BigDecimal getDecimal(int column) { 422 return getDecimal(storeColumns[column]); 423 } 424 getDecimal(Column storeColumn)425 public BigDecimal getDecimal(Column storeColumn) { 426 int index = storeColumn.getColumnId(); 427 int offset = offsets[index]; 428 int precision = storeColumn.getPrecision(); 429 int scale = storeColumn.getScale(); 430 int length = Utility.getDecimalColumnSpace(precision, scale); 431 byteBuffer.position(offset); 432 return Utility.getDecimal(byteBuffer, length, precision, scale); 433 } 434 handleError(Object object, NdbOperation ndbOperation)435 private void handleError(Object object, NdbOperation ndbOperation) { 436 if (object == null) { 437 Utility.throwError(object, ndbOperation.getNdbError()); 438 } 439 } 440 setNoResult()441 public void setNoResult() { 442 nextDone = true; 443 } 444 getColumns()445 public Column[] getColumns() { 446 return storeColumns; 447 } 448 449 } 450