yadavay-amzn commented on code in PR #56942:
URL: https://github.com/apache/spark/pull/56942#discussion_r3525777376
##########
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
Review Comment:
Fixed - removed the stale TimestampNanosUpdater reference (renamed in the
types-framework rework); confirmed no other occurrences remain in src.
##########
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)`.
Review Comment:
Unified it: putTimestampNanos now calls the shared
DateTimeUtils.truncateNanosWithinMicroToPrecision (primitive Int->Int, zero
allocation) so truncation stays in lock-step with the row reader. I
deliberately kept floorDiv/floorMod inline rather than routing through
epochNanosToTimestampNanos, which allocates a TimestampNanosVal per value - we
want the read hot path allocation-free. Added a comment noting that.
##########
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:
Added a dictionary-encoding test for TIMESTAMP(NANOS) in ParquetIOSuite
(nullable, dictionary on and off, both reader paths) that drives
decodeSingleDictionaryId.
--
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]