This is an automated email from the ASF dual-hosted git repository.

Jackie-Jiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new c396f1e40f0 Fix unsafe publication of DataSchema stored column data 
types (#19117)
c396f1e40f0 is described below

commit c396f1e40f02a61b234680bd8bfd9689ad8e4e30
Author: Xiaotian (Jackie) Jiang <[email protected]>
AuthorDate: Wed Jul 29 11:35:16 2026 -0700

    Fix unsafe publication of DataSchema stored column data types (#19117)
---
 .../org/apache/pinot/common/utils/DataSchema.java  | 30 ++++++++++++++++------
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git 
a/pinot-common/src/main/java/org/apache/pinot/common/utils/DataSchema.java 
b/pinot-common/src/main/java/org/apache/pinot/common/utils/DataSchema.java
index 978b33aed80..775838725af 100644
--- a/pinot-common/src/main/java/org/apache/pinot/common/utils/DataSchema.java
+++ b/pinot-common/src/main/java/org/apache/pinot/common/utils/DataSchema.java
@@ -44,7 +44,6 @@ import java.util.UUID;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.sql.type.SqlTypeName;
-import org.apache.pinot.common.datatable.DataTable;
 import org.apache.pinot.segment.spi.memory.PinotInputStream;
 import org.apache.pinot.spi.data.FieldSpec.DataType;
 import org.apache.pinot.spi.utils.ByteArray;
@@ -57,14 +56,27 @@ import org.apache.pinot.spi.utils.UuidUtils;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 
-/**
- * The <code>DataSchema</code> class describes the schema of {@link DataTable}.
- */
+/// Describes the schema of a [org.apache.pinot.common.datatable.DataTable].
+///
+/// Fields are never reassigned after construction, but [#getColumnNames] and 
[#getColumnDataTypes] hand out the
+/// internal arrays without copying, so instances are not immutable: 
`BaseGapfillProcessor` rewrites column names in
+/// place.
+///
+/// Read-only sharing across threads is common and must stay safe: in the 
multi-stage engine a stage plan is
+/// deserialized once per stage and then handed to every worker of that stage, 
so the `DataSchema` of each plan node is
+/// read concurrently by all worker threads on the server.
 @JsonPropertyOrder({"columnNames", "columnDataTypes"})
 public class DataSchema {
   private final String[] _columnNames;
   private final ColumnDataType[] _columnDataTypes;
-  private ColumnDataType[] _storedColumnDataTypes;
+
+  /// Lazily computed cache of [#getStoredColumnDataTypes].
+  ///
+  /// `volatile` is required, not just for the null check in 
[#getStoredColumnDataTypes]: without it the array
+  /// contents are published unsafely, and a racing thread can read the 
non-null array reference while still seeing
+  /// `null` for its elements. Downstream code (e.g. `TypeUtils.convert`, 
`DataBlockExtractUtils.extractValue`) then
+  /// switches on a `null` stored type and fails with a `NullPointerException`.
+  private volatile ColumnDataType[] _storedColumnDataTypes;
 
   /**
    * Used by both Broker and Server to generate results for EXPLAIN PLAN 
queries.
@@ -101,9 +113,11 @@ public class DataSchema {
     return _columnDataTypes;
   }
 
-  /**
-   * Lazy compute the _storeColumnDataTypes field.
-   */
+  /// Returns the stored type of each column, lazily computing and caching it 
on first access.
+  ///
+  /// Uses the racy-single-check idiom: two threads may each compute the 
array, but both compute the same values, so
+  /// the duplicate work is harmless. Correctness relies on 
`_storedColumnDataTypes` being `volatile`; see the field
+  /// for why.
   @JsonIgnore
   public ColumnDataType[] getStoredColumnDataTypes() {
     ColumnDataType[] storedColumnDataTypes = _storedColumnDataTypes;


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

Reply via email to