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