andygrove commented on code in PR #6730:
URL: https://github.com/apache/datafusion-comet/pull/6730#discussion_r4212937968


##########
spark/src/main/scala/org/apache/spark/sql/comet/execution/arrow/ArrowWriters.scala:
##########
@@ -1262,6 +1436,107 @@ private[arrow] class StringWriter(val valueVector: 
VarCharVector) extends ArrowF
     super.reset()
     dictionaryCache.clear()
   }
+
+  // The value is checked before Arrow grows anything, so one that cannot be 
written changes
+  // nothing.
+  override private[arrow] def writeUnsafeRowField(row: UnsafeRow, ordinal: 
Int): Unit = {
+    if (row.isNullAt(ordinal)) {
+      setNull()
+    } else {
+      val offsetAndSize = row.getLong(ordinal)
+      val start = valueVector.getStartOffset(valueVector.getLastSet + 1)
+      val length = (checkedEnd(start.toLong, offsetAndSize) - start).toInt
+      // Grows the buffers, and gives null rows written since the last value 
their empty offsets.
+      valueVector.setValueLengthSafe(count, length)
+      copyMemory(
+        row.getBaseObject,
+        row.getBaseOffset + (offsetAndSize >> 32),
+        valueVector.getDataBuffer.memoryAddress + start,
+        length.toLong)
+      BitVectorHelper.setBit(valueVector.getValidityBuffer, count.toLong)
+    }
+    count += 1
+  }
+
+  // Every element is checked before anything changes, as one value is, so the 
buffers grow once
+  // and the bytes are copied without further checks.
+  override private[arrow] def writeArrayElements(array: UnsafeArrayData): Unit 
= {
+    val numElements = array.numElements()
+    if (numElements == 0) {
+      return
+    }
+    val start = valueVector.getStartOffset(valueVector.getLastSet + 1)
+    var end = start.toLong
+    var i = 0
+    while (i < numElements) {
+      if (!array.isNullAt(i)) {
+        end = checkedEnd(end, array.getLong(i))
+      }
+      i += 1
+    }
+    while (valueVector.getValueCapacity < count + numElements) {
+      valueVector.reallocValidityAndOffsetBuffers()
+    }
+    reserveData(valueVector, end)
+    if (valueVector.getLastSet < count - 1) {
+      valueVector.fillEmpties(count)
+    }

Review Comment:
   Done in 90c3bbaee7. `reserveValues` sits next to `reserveData`, and all 
three paths call it, with the unsafe array path calling it after its checks. 
While I checked each call site with a mutation, dropping it from the dictionary 
path failed nothing, because no test appended dictionary strings after nulls 
the row path had left, or past the initial capacity. There's a test for that 
now, which appends a dictionary-encoded field as a column after its struct took 
the row path.
   



##########
spark/src/test/scala/org/apache/spark/sql/comet/execution/arrow/CometArrowWriterSuite.scala:
##########
@@ -640,4 +685,144 @@ class CometArrowWriterSuite extends AnyFunSuite with 
Matchers {
       }
     }
   }
+
+  // Nested shapes whose unsafe forms take paths of their own: an array of 
each primitive type,
+  // whose elements are either copied in one block or converted one at a time, 
collections inside
+  // collections, and a struct wider than one word of null bits.
+  private val moreNestedTypes: Seq[DataType] = 
primitiveTypes.map(ArrayType(_)) ++ Seq(

Review Comment:
   Thanks, fixed in 90c3bbaee7 with `filterNot(nestedTypes.contains)`, so 
array<int> and array<string> each run once.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to