david-mollitor-db opened a new pull request, #58838:
URL: https://github.com/apache/spark/pull/58838

   ### What changes were proposed in this pull request?
   
   Row-based readers apply column "existence default values" (the value used 
for a column missing from
   the data but with a DEFAULT in the schema) per record via
   `ResolveDefaultColumns.applyExistenceDefaultValuesToRow(schema, row, 
bitmask)` /
   `resetExistenceDefaultsBitmask(schema, bitmask)`, which internally call:
   
   ```scala
   def existenceDefaultValues(schema: StructType): Array[Any] =
     schema.fields.map(getExistenceDefaultValue) // new Array[Any]; 
parses+evaluates each default expr
   ```
   
   This is a pure function of the schema (field metadata only, never row data) 
and is not cached, so it
   is recomputed every record. `applyExistenceDefaultValuesToRow` recomputes it 
and also calls
   `hasExistenceDefaultValues(schema)` (recomputes again); the recompute 
happens whether or not the
   schema has any defaults. The utility already documents this: "The caller 
should avoid using such
   methods in a loop for efficiency."
   
   This PR:
   - adds overloads that take the precomputed values, keeping the schema-based 
methods (still used
     one-time by the analyzer): `resetExistenceDefaultsBitmask(defaultValues: 
Array[Any], bitmask)` and
     `applyExistenceDefaultValuesToRow(defaultValues: Array[Any], row, 
bitmask)`;
   - caches the values once per reader and uses the overloads:
     - `ParquetRowConverter` and `OrcDeserializer`: add a per-instance 
`existenceDefaults` and a
       `hasExistenceDefaults` boolean; the bitmask starts all-true and is only 
corrected when defaults
       exist, so the guard is kept, then the cached array is passed to the 
overload;
     - `UnivocityParser` (CSV): caches `existenceDefaults` and indexes it in 
the parse-error fallback.
   
   `JacksonParser` (JSON) uses the same pattern but its `convertObject` is 
recursive over a different
   struct schema per call; that hoist is handled in a separate PR.
   
   ### Why are the changes needed?
   
   JFR profiling of `DataSourceReadBenchmark` showed this recompute as ~4.5% of 
sampled allocation
   (`ManifestFactory.newArray` under `ParquetRowConverter.currentRecord` and 
the ORC reader). It is the
   same "schema-derived constant recomputed per row" pattern as SPARK-59506 and 
SPARK-59543.
   
   Measuring a trimmed `DataSourceReadBenchmark` (single INT column scan) under 
JFR
   (`settings=profile`), allocation samples through 
`ResolveDefaultColumns.getExistenceDefaultValue`:
   
   - `ParquetRowConverter.currentRecord`: ~1,050 → 0.
   - `OrcDeserializer`: ~520 → 0.
   
   No new allocation is introduced (the values are computed once); the CSV path 
only touches them on a
   parse error.
   
   ### Does this PR introduce _any_ user-facing change?
   
   No. The values are a schema constant, so the applied defaults are identical.
   
   ### How was this patch tested?
   
   Existing tests, which write data, `ALTER TABLE ADD COLUMN` with a DEFAULT, 
then read the old data
   expecting the default — across Parquet/ORC/CSV, vectorized and row:
   
   ```
   build/sbt 'sql/testOnly *InsertSuite *ResolveDefaultColumnsSuite'
   build/sbt 'catalyst/testOnly *StructTypeSuite'
   ```
   
   All pass.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Isaac
   
   This pull request and its description were written by Isaac.
   


-- 
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