uros-b commented on code in PR #56942:
URL: https://github.com/apache/spark/pull/56942#discussion_r3524935695
##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/parquet/types/ops/TimestampNanosParquetOps.scala:
##########
@@ -167,3 +186,69 @@ private[ops] object TimestampNanosParquetOps {
case _ => false
})
}
+
+/**
+ * Vectorized (batch) updater for nanosecond-precision timestamps: reads an
INT64 epoch-nanos
+ * column and decomposes each value into the two-child column vector
(epochMicros: Long,
+ * nanosWithinMicro: Short). Sub-microsecond digits are truncated to the
requested precision.
+ * Mirrors the row-based `newConverter` path which calls
+ * `DateTimeUtils.epochNanosToTimestampNanos(value, precision)`.
+ *
+ * No datetime rebase is applied (TIMESTAMP(NANOS) postdates the proleptic
Gregorian switch).
+ * No timezone conversion is applied at the storage level.
+ *
+ * Replaces the former `ParquetVectorUpdaterFactory.TimestampNanosUpdater`,
now owned by the
+ * type's ops and routed through the types framework's `getVectorUpdater` hook.
+ */
+private[ops] class TimestampNanosVectorUpdater(precision: Int) extends
ParquetVectorUpdater {
+
+ // Pre-computed truncation divisor for the nanosWithinMicro component.
+ // precision 7 -> divisor 100, precision 8 -> divisor 10, precision 9 ->
divisor 1
+ private val nanosTruncationDivisor: Int = precision match {
+ case 7 => 100
+ case 8 => 10
+ case 9 => 1
+ case _ => throw new IllegalArgumentException(
+ s"Invalid nanosecond timestamp precision: $precision")
+ }
+
+ private def putTimestampNanos(
+ offset: Int, values: WritableColumnVector, epochNanos: Long): Unit = {
+ val epochMicros = Math.floorDiv(epochNanos, 1000L)
+ val rawNanosWithinMicro = Math.floorMod(epochNanos, 1000L).toInt
+ val nanosWithinMicro =
+ ((rawNanosWithinMicro / nanosTruncationDivisor) *
nanosTruncationDivisor).toShort
+ values.getChild(0).putLong(offset, epochMicros)
+ values.getChild(1).putShort(offset, nanosWithinMicro)
+ }
+
+ override def readValues(
+ total: Int,
+ offset: Int,
+ values: WritableColumnVector,
+ valuesReader: VectorizedValuesReader): Unit = {
+ var i = 0
+ while (i < total) {
+ putTimestampNanos(offset + i, values, valuesReader.readLong())
+ i += 1
+ }
+ }
+
+ override def skipValues(total: Int, valuesReader: VectorizedValuesReader):
Unit =
+ valuesReader.skipLongs(total)
+
+ override def readValue(
+ offset: Int,
+ values: WritableColumnVector,
+ valuesReader: VectorizedValuesReader): Unit =
+ putTimestampNanos(offset, values, valuesReader.readLong())
+
+ override def decodeSingleDictionaryId(
Review Comment:
decodeSingleDictionaryId is implemented, but no test forces dictionary
encoding for TIMESTAMP(NANOS) (ParquetIOSuite has this for TIME(NANOS)). The
row-vs-vectorized parity test is good coverage; a targeted dictionary test
would close the last gap.
--
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]