@@ -118,7 +118,7 @@ public io.questdb.client.std.bytes.DirectByteSequence getBinaryB(int col, int ro
118118 public boolean getBool (int col , int row ) {
119119 QwpColumnLayout l = columnLayouts .getQuick (col );
120120 if (isLayoutNull (l , row )) return false ;
121- int denseIdx = l .nonNullIdx [ row ] ;
121+ int denseIdx = l .denseIndex ( row ) ;
122122 // Bit-packed: 8 values per byte, LSB-first
123123 byte b = Unsafe .getUnsafe ().getByte (l .valuesAddr + (denseIdx >>> 3 ));
124124 return (b & (1 << (denseIdx & 7 ))) != 0 ;
@@ -131,7 +131,7 @@ public boolean getBool(int col, int row) {
131131 public byte getByteValue (int col , int row ) {
132132 QwpColumnLayout l = columnLayouts .getQuick (col );
133133 if (isLayoutNull (l , row )) return 0 ;
134- return Unsafe .getUnsafe ().getByte (l .valuesAddr + l .nonNullIdx [ row ] );
134+ return Unsafe .getUnsafe ().getByte (l .valuesAddr + l .denseIndex ( row ) );
135135 }
136136
137137 /**
@@ -140,7 +140,7 @@ public byte getByteValue(int col, int row) {
140140 public char getCharValue (int col , int row ) {
141141 QwpColumnLayout l = columnLayouts .getQuick (col );
142142 if (isLayoutNull (l , row )) return 0 ;
143- return (char ) Unsafe .getUnsafe ().getShort (l .valuesAddr + 2L * l .nonNullIdx [ row ] );
143+ return (char ) Unsafe .getUnsafe ().getShort (l .valuesAddr + 2L * l .denseIndex ( row ) );
144144 }
145145
146146 public int getColumnCount () {
@@ -161,7 +161,7 @@ public byte getColumnWireType(int col) {
161161 public long getDecimal128High (int col , int row ) {
162162 QwpColumnLayout l = columnLayouts .getQuick (col );
163163 if (isLayoutNull (l , row )) return 0L ;
164- return Unsafe .getUnsafe ().getLong (l .valuesAddr + 16L * l .nonNullIdx [ row ] + 8L );
164+ return Unsafe .getUnsafe ().getLong (l .valuesAddr + 16L * l .denseIndex ( row ) + 8L );
165165 }
166166
167167 /**
@@ -170,7 +170,7 @@ public long getDecimal128High(int col, int row) {
170170 public long getDecimal128Low (int col , int row ) {
171171 QwpColumnLayout l = columnLayouts .getQuick (col );
172172 if (isLayoutNull (l , row )) return 0L ;
173- return Unsafe .getUnsafe ().getLong (l .valuesAddr + 16L * l .nonNullIdx [ row ] );
173+ return Unsafe .getUnsafe ().getLong (l .valuesAddr + 16L * l .denseIndex ( row ) );
174174 }
175175
176176 public int getDecimalScale (int col ) {
@@ -180,7 +180,7 @@ public int getDecimalScale(int col) {
180180 public double getDouble (int col , int row ) {
181181 QwpColumnLayout l = columnLayouts .getQuick (col );
182182 if (isLayoutNull (l , row )) return Double .NaN ;
183- return Unsafe .getUnsafe ().getDouble (l .valuesAddr + 8L * l .nonNullIdx [ row ] );
183+ return Unsafe .getUnsafe ().getDouble (l .valuesAddr + 8L * l .denseIndex ( row ) );
184184 }
185185
186186 /**
@@ -209,7 +209,7 @@ public double[] getDoubleArrayElements(int col, int row) {
209209 public float getFloat (int col , int row ) {
210210 QwpColumnLayout l = columnLayouts .getQuick (col );
211211 if (isLayoutNull (l , row )) return Float .NaN ;
212- return Unsafe .getUnsafe ().getFloat (l .valuesAddr + 4L * l .nonNullIdx [ row ] );
212+ return Unsafe .getUnsafe ().getFloat (l .valuesAddr + 4L * l .denseIndex ( row ) );
213213 }
214214
215215 public int getGeohashPrecisionBits (int col ) {
@@ -223,7 +223,7 @@ public int getGeohashPrecisionBits(int col) {
223223 public int getIntValue (int col , int row ) {
224224 QwpColumnLayout l = columnLayouts .getQuick (col );
225225 if (isLayoutNull (l , row )) return 0 ;
226- return Unsafe .getUnsafe ().getInt (l .valuesAddr + 4L * l .nonNullIdx [ row ] );
226+ return Unsafe .getUnsafe ().getInt (l .valuesAddr + 4L * l .denseIndex ( row ) );
227227 }
228228
229229 /**
@@ -238,7 +238,7 @@ public long getLong(int col, int row) {
238238 QwpColumnLayout l = columnLayouts .getQuick (col );
239239 if (isLayoutNull (l , row )) return 0L ;
240240 byte wt = l .info .wireType ;
241- int denseIdx = l .nonNullIdx [ row ] ;
241+ int denseIdx = l .denseIndex ( row ) ;
242242 if (wt == QwpConstants .TYPE_LONG || wt == QwpConstants .TYPE_DATE
243243 || wt == QwpConstants .TYPE_TIMESTAMP || wt == QwpConstants .TYPE_TIMESTAMP_NANOS
244244 || wt == QwpConstants .TYPE_DECIMAL64 ) {
@@ -274,7 +274,7 @@ public long getLong(int col, int row) {
274274 public long getLong256Word (int col , int row , int wordIndex ) {
275275 QwpColumnLayout l = columnLayouts .getQuick (col );
276276 if (isLayoutNull (l , row )) return 0L ;
277- return Unsafe .getUnsafe ().getLong (l .valuesAddr + 32L * l .nonNullIdx [ row ] + 8L * wordIndex );
277+ return Unsafe .getUnsafe ().getLong (l .valuesAddr + 32L * l .denseIndex ( row ) + 8L * wordIndex );
278278 }
279279
280280 // Raw column-address API -- for zero-branch hot inner loops.
@@ -299,7 +299,7 @@ public long[] getLongArray(int col, int row) {
299299 QwpColumnLayout l = columnLayouts .getQuick (col );
300300 if (isLayoutNull (l , row )) return null ;
301301 byte wt = l .info .wireType ;
302- int denseIdx = l .nonNullIdx [ row ] ;
302+ int denseIdx = l .denseIndex ( row ) ;
303303 if (wt == QwpConstants .TYPE_UUID || wt == QwpConstants .TYPE_DECIMAL128 ) {
304304 long base = l .valuesAddr + 16L * denseIdx ;
305305 return new long []{Unsafe .getUnsafe ().getLong (base ), Unsafe .getUnsafe ().getLong (base + 8 )};
@@ -324,7 +324,7 @@ public long[] getLongArray(int col, int row) {
324324 public long getLongValue (int col , int row ) {
325325 QwpColumnLayout l = columnLayouts .getQuick (col );
326326 if (isLayoutNull (l , row )) return 0L ;
327- return Unsafe .getUnsafe ().getLong (l .valuesAddr + 8L * l .nonNullIdx [ row ] );
327+ return Unsafe .getUnsafe ().getLong (l .valuesAddr + 8L * l .denseIndex ( row ) );
328328 }
329329
330330 public int getRowCount () {
@@ -337,7 +337,7 @@ public int getRowCount() {
337337 public short getShortValue (int col , int row ) {
338338 QwpColumnLayout l = columnLayouts .getQuick (col );
339339 if (isLayoutNull (l , row )) return 0 ;
340- return Unsafe .getUnsafe ().getShort (l .valuesAddr + 2L * l .nonNullIdx [ row ] );
340+ return Unsafe .getUnsafe ().getShort (l .valuesAddr + 2L * l .denseIndex ( row ) );
341341 }
342342
343343 /**
@@ -379,7 +379,7 @@ public String getString(int col, int row) {
379379 public long getUuidHi (int col , int row ) {
380380 QwpColumnLayout l = columnLayouts .getQuick (col );
381381 if (isLayoutNull (l , row )) return 0L ;
382- return Unsafe .getUnsafe ().getLong (l .valuesAddr + 16L * l .nonNullIdx [ row ] + 8L );
382+ return Unsafe .getUnsafe ().getLong (l .valuesAddr + 16L * l .denseIndex ( row ) + 8L );
383383 }
384384
385385 /**
@@ -388,7 +388,7 @@ public long getUuidHi(int col, int row) {
388388 public long getUuidLo (int col , int row ) {
389389 QwpColumnLayout l = columnLayouts .getQuick (col );
390390 if (isLayoutNull (l , row )) return 0L ;
391- return Unsafe .getUnsafe ().getLong (l .valuesAddr + 16L * l .nonNullIdx [ row ] );
391+ return Unsafe .getUnsafe ().getLong (l .valuesAddr + 16L * l .denseIndex ( row ) );
392392 }
393393
394394 /**
@@ -468,9 +468,23 @@ public int nonNullCount(int col) {
468468 * column's non-null values, or -1 if the row is NULL. Array length equals
469469 * {@link #getRowCount()}. Valid only during the current {@code onBatch}
470470 * callback; do not retain.
471+ * <p>
472+ * For columns with no nulls in this batch the decoder skips populating
473+ * this array (saves O(rowCount * columnCount) per batch on the typical
474+ * no-nulls hot path). This accessor lazy-materialises an identity mapping
475+ * on demand for raw-API callers; the result is cached on the layout so
476+ * repeated calls within the same batch reuse the allocation. The typed
477+ * accessors ({@link #getLong}, {@link #getDouble}, etc.) avoid this
478+ * allocation entirely via {@link QwpColumnLayout#denseIndex}.
471479 */
472480 public int [] nonNullIndex (int col ) {
473- return columnLayouts .getQuick (col ).nonNullIdx ;
481+ QwpColumnLayout l = columnLayouts .getQuick (col );
482+ if (l .nullBitmapAddr == 0 && l .nonNullIdx == null ) {
483+ int [] arr = new int [rowCount ];
484+ for (int i = 0 ; i < rowCount ; i ++) arr [i ] = i ;
485+ l .nonNullIdx = arr ;
486+ }
487+ return l .nonNullIdx ;
474488 }
475489
476490 /**
@@ -532,7 +546,7 @@ private io.questdb.client.std.bytes.DirectByteSequence lookupBinaryBytes(
532546 if (isNull (col , row )) return null ;
533547 QwpColumnLayout l = columnLayouts .getQuick (col );
534548 if (l .info .wireType != QwpConstants .TYPE_BINARY ) return null ;
535- int denseIdx = l .nonNullIdx [ row ] ;
549+ int denseIdx = l .denseIndex ( row ) ;
536550 int startOff = Unsafe .getUnsafe ().getInt (l .valuesAddr + 4L * denseIdx );
537551 int endOff = Unsafe .getUnsafe ().getInt (l .valuesAddr + 4L * (denseIdx + 1 ));
538552 return view .of (l .stringBytesAddr + startOff , endOff - startOff );
@@ -547,7 +561,7 @@ private DirectUtf8Sequence lookupStringBytes(int col, int row, DirectUtf8String
547561 if (isNull (col , row )) return null ;
548562 QwpColumnLayout l = columnLayouts .getQuick (col );
549563 byte wt = l .info .wireType ;
550- int denseIdx = l .nonNullIdx [ row ] ;
564+ int denseIdx = l .denseIndex ( row ) ;
551565 if (wt == QwpConstants .TYPE_STRING || wt == QwpConstants .TYPE_VARCHAR ) {
552566 int startOff = Unsafe .getUnsafe ().getInt (l .valuesAddr + 4L * denseIdx );
553567 int endOff = Unsafe .getUnsafe ().getInt (l .valuesAddr + 4L * (denseIdx + 1 ));
0 commit comments