Skip to content

[SPARK-55978][SQL] Add TABLESAMPLE SYSTEM block sampling with DSv2 pushdown#54972

Open
stanyao wants to merge 1 commit intoapache:masterfrom
stanyao:spark-55978-tablesample-system
Open

[SPARK-55978][SQL] Add TABLESAMPLE SYSTEM block sampling with DSv2 pushdown#54972
stanyao wants to merge 1 commit intoapache:masterfrom
stanyao:spark-55978-tablesample-system

Conversation

@stanyao
Copy link
Copy Markdown

@stanyao stanyao commented Mar 24, 2026

What changes were proposed in this pull request?

This PR adds support for ANSI SQL TABLESAMPLE SYSTEM (block-level sampling) alongside the existing TABLESAMPLE BERNOULLI (row-level sampling). Key changes:

  • SQL grammar: Extended TABLESAMPLE to accept an optional SYSTEM or BERNOULLI qualifier before the sample method. Added both as non-reserved keywords.
  • Logical plan: Introduced SampleMethod sealed trait (Bernoulli/System) and added it to the Sample node. Default is Bernoulli for backward compatibility.
  • Parser: TABLESAMPLE SYSTEM only supports PERCENT sampling and does not support REPEATABLE. Other methods (ROWS, BUCKET, BYTES) are rejected with clear error messages.
  • DSv2 pushdown: TABLESAMPLE SYSTEM is pushed down to data sources via an extended SupportsPushDownTableSample.pushTableSample() overload with isSystemSampling flag. Sources that don't override the new method reject SYSTEM sampling by default.
  • Physical planning: SYSTEM samples that aren't pushed down to a DSv2 source raise an AnalysisException — there is no row-level fallback since block sampling is data-source dependent.

Why are the changes needed?

ANSI SQL defines two sampling methods: BERNOULLI (row-level) and SYSTEM (implementation-dependent, typically block/split-level). Block sampling is significantly faster for large tables since it avoids per-row evaluation, making it useful for approximate queries and data exploration. Many databases (PostgreSQL, Hive, Trino) support this distinction.

Does this PR introduce any user-facing change?

Yes. New SQL syntax TABLESAMPLE SYSTEM (x PERCENT) and TABLESAMPLE BERNOULLI (x PERCENT). BERNOULLI and SYSTEM are added as non-reserved keywords. Existing queries without these keywords behave identically to before.

How was this patch tested?

  • 9 new test cases in PlanParserSuite covering: basic parsing, case insensitivity, boundary fractions, unsupported methods (ROWS/BUCKET with SYSTEM), REPEATABLE rejection, fraction validation, identifier preservation, and subquery contexts.
  • Existing SQLQuerySuite tests pass.
  • Scalastyle passes with 0 errors/warnings.

Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (thoroughly refined, reviewed, and tested by human)

@stanyao stanyao changed the title [SPARK-55978][SQL] Add TABLESAMPLE SYSTEM block sampling with DSv2 pu… [SPARK-55978][SQL] Add TABLESAMPLE SYSTEM block sampling with DSv2 pushdown Mar 24, 2026
@stanyao stanyao force-pushed the spark-55978-tablesample-system branch 3 times, most recently from bd6f9cf to afbd7d5 Compare March 24, 2026 16:05
@dbtsai
Copy link
Copy Markdown
Member

dbtsai commented Mar 26, 2026

cc @szehon-ho

@stanyao stanyao force-pushed the spark-55978-tablesample-system branch from afbd7d5 to 340c545 Compare March 28, 2026 17:17
@stanyao
Copy link
Copy Markdown
Author

stanyao commented Mar 28, 2026

Rebased and resolved the new merge conflict above.

Copy link
Copy Markdown
Member

@szehon-ho szehon-ho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the idea seems reasonable to me in DSV2 API. Added some comments

stanyao pushed a commit to stanyao/blocksampling that referenced this pull request Apr 1, 2026
Summary:
CONTEXT: Review feedback on PR apache#54972 for TABLESAMPLE SYSTEM block sampling.
WHAT:
- Add DSv2 SampleMethod Java enum to replace boolean isSystemSampling parameter
- Update SupportsPushDownTableSample Javadoc to clarify BERNOULLI vs SYSTEM
- Replace _LEGACY_ERROR_TEMP_0035 with UNSUPPORTED_FEATURE.TABLESAMPLE_SYSTEM
- Restrict SYSTEM sample pushdown to direct table scans only (no subqueries/views)
- Remove findScanBuilderHolder that incorrectly traversed through Filter/Project

Test Plan:
Existing tests. Error class added to error-conditions.json.
@stanyao stanyao force-pushed the spark-55978-tablesample-system branch from 32fd86b to 84cdd7f Compare April 2, 2026 00:01
stanyao pushed a commit to stanyao/blocksampling that referenced this pull request Apr 2, 2026
Summary:
CONTEXT: Review feedback on PR apache#54972 for TABLESAMPLE SYSTEM block sampling.
WHAT:
- Add DSv2 SampleMethod Java enum to replace boolean isSystemSampling parameter
- Update SupportsPushDownTableSample Javadoc to clarify BERNOULLI vs SYSTEM
- Replace _LEGACY_ERROR_TEMP_0035 with UNSUPPORTED_FEATURE.TABLESAMPLE_SYSTEM
- Restrict SYSTEM sample pushdown to direct table scans only (no subqueries/views)
- Remove findScanBuilderHolder that incorrectly traversed through Filter/Project

Test Plan:
Existing tests. Error class added to error-conditions.json.
@stanyao stanyao force-pushed the spark-55978-tablesample-system branch from 84cdd7f to b390671 Compare April 2, 2026 03:37
stanyao pushed a commit to stanyao/blocksampling that referenced this pull request Apr 2, 2026
Summary:
CONTEXT: Review feedback on PR apache#54972 for TABLESAMPLE SYSTEM block sampling.
WHAT:
- Add DSv2 SampleMethod Java enum to replace boolean isSystemSampling parameter
- Update SupportsPushDownTableSample Javadoc to clarify BERNOULLI vs SYSTEM
- Replace _LEGACY_ERROR_TEMP_0035 with UNSUPPORTED_FEATURE.TABLESAMPLE_SYSTEM
- Restrict SYSTEM sample pushdown to direct table scans only (no subqueries/views)
- Remove findScanBuilderHolder that incorrectly traversed through Filter/Project

Test Plan:
Existing tests. Error class added to error-conditions.json.
Copy link
Copy Markdown
Member

@szehon-ho szehon-ho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, leaving a few more comments

val metadata = conn.getMetaData
// scalastyle:off line.size.limit
assert(metadata.getSQLKeywords === "ADD,AFTER,AGGREGATE,ALWAYS,ANALYZE,ANTI,ANY_VALUE,ARCHIVE,ASC,BINDING,BUCKET,BUCKETS,BYTE,CACHE,CACHED,CASCADE,CATALOG,CATALOGS,CHANGE,CHANGES,CLEAR,CLUSTER,CLUSTERED,CODEGEN,COLLATION,COLLATIONS,COLLECTION,COLUMNS,COMMENT,COMPACT,COMPACTIONS,COMPENSATION,COMPUTE,CONCATENATE,CONTAINS,CONTINUE,COST,DATA,DATABASE,DATABASES,DATEADD,DATEDIFF,DATE_ADD,DATE_DIFF,DAYOFYEAR,DAYS,DBPROPERTIES,DEFINED,DEFINER,DELAY,DELIMITED,DESC,DFS,DIRECTORIES,DIRECTORY,DISTRIBUTE,DIV,DO,ELSEIF,ENFORCED,ESCAPED,EVOLUTION,EXCHANGE,EXCLUDE,EXCLUSIVE,EXIT,EXPLAIN,EXPORT,EXTEND,EXTENDED,FIELDS,FILEFORMAT,FIRST,FLOW,FOLLOWING,FORMAT,FORMATTED,FOUND,FUNCTIONS,GENERATED,GEOGRAPHY,GEOMETRY,HANDLER,HOURS,IDENTIFIED,IDENTIFIER,IF,IGNORE,ILIKE,IMMEDIATE,INCLUDE,INCLUSIVE,INCREMENT,INDEX,INDEXES,INPATH,INPUT,INPUTFORMAT,INVOKER,ITEMS,ITERATE,JSON,KEY,KEYS,LAST,LAZY,LEAVE,LEVEL,LIMIT,LINES,LIST,LOAD,LOCATION,LOCK,LOCKS,LOGICAL,LONG,LOOP,MACRO,MAP,MATCHED,MATERIALIZED,MEASURE,METRICS,MICROSECOND,MICROSECONDS,MILLISECOND,MILLISECONDS,MINUS,MINUTES,MONTHS,MSCK,NAME,NAMESPACE,NAMESPACES,NANOSECOND,NANOSECONDS,NORELY,NULLS,OFFSET,OPTION,OPTIONS,OUTPUTFORMAT,OVERWRITE,PARTITIONED,PARTITIONS,PERCENT,PIVOT,PLACING,PRECEDING,PRINCIPALS,PROCEDURES,PROPERTIES,PURGE,QUARTER,QUERY,RECORDREADER,RECORDWRITER,RECOVER,RECURSION,REDUCE,REFRESH,RELY,RENAME,REPAIR,REPEAT,REPEATABLE,REPLACE,RESET,RESPECT,RESTRICT,ROLE,ROLES,SCHEMA,SCHEMAS,SECONDS,SECURITY,SEMI,SEPARATED,SERDE,SERDEPROPERTIES,SETS,SHORT,SHOW,SINGLE,SKEWED,SORT,SORTED,SOURCE,STATISTICS,STORED,STRATIFY,STREAM,STREAMING,STRING,STRUCT,SUBSTR,SYNC,SYSTEM_TIME,SYSTEM_VERSION,TABLES,TARGET,TBLPROPERTIES,TERMINATED,TIMEDIFF,TIMESTAMPADD,TIMESTAMPDIFF,TIMESTAMP_LTZ,TIMESTAMP_NTZ,TINYINT,TOUCH,TRANSACTION,TRANSACTIONS,TRANSFORM,TRUNCATE,TRY_CAST,TYPE,UNARCHIVE,UNBOUNDED,UNCACHE,UNLOCK,UNPIVOT,UNSET,UNTIL,USE,VAR,VARIABLE,VARIANT,VERSION,VIEW,VIEWS,VOID,WATERMARK,WEEK,WEEKS,WHILE,X,YEARS,ZONE")
assert(metadata.getSQLKeywords === "ADD,AFTER,AGGREGATE,ALWAYS,ANALYZE,ANTI,ANY_VALUE,ARCHIVE,ASC,BERNOULLI,BINDING,BUCKET,BUCKETS,BYTE,CACHE,CACHED,CASCADE,CATALOG,CATALOGS,CHANGE,CHANGES,CLEAR,CLUSTER,CLUSTERED,CODEGEN,COLLATION,COLLATIONS,COLLECTION,COLUMNS,COMMENT,COMPACT,COMPACTIONS,COMPENSATION,COMPUTE,CONCATENATE,CONTAINS,CONTINUE,COST,DATA,DATABASE,DATABASES,DATEADD,DATEDIFF,DATE_ADD,DATE_DIFF,DAYOFYEAR,DAYS,DBPROPERTIES,DEFINED,DEFINER,DELAY,DELIMITED,DESC,DFS,DIRECTORIES,DIRECTORY,DISTRIBUTE,DIV,DO,ELSEIF,ENFORCED,ESCAPED,EVOLUTION,EXCHANGE,EXCLUDE,EXCLUSIVE,EXIT,EXPLAIN,EXPORT,EXTEND,EXTENDED,FIELDS,FILEFORMAT,FIRST,FLOW,FOLLOWING,FORMAT,FORMATTED,FOUND,FUNCTIONS,GENERATED,GEOGRAPHY,GEOMETRY,HANDLER,HOURS,IDENTIFIED,IDENTIFIER,IF,IGNORE,ILIKE,IMMEDIATE,INCLUDE,INCLUSIVE,INCREMENT,INDEX,INDEXES,INPATH,INPUT,INPUTFORMAT,INVOKER,ITEMS,ITERATE,JSON,KEY,KEYS,LAST,LAZY,LEAVE,LEVEL,LIMIT,LINES,LIST,LOAD,LOCATION,LOCK,LOCKS,LOGICAL,LONG,LOOP,MACRO,MAP,MATCHED,MATERIALIZED,MEASURE,METRICS,MICROSECOND,MICROSECONDS,MILLISECOND,MILLISECONDS,MINUS,MINUTES,MONTHS,MSCK,NAME,NAMESPACE,NAMESPACES,NANOSECOND,NANOSECONDS,NORELY,NULLS,OFFSET,OPTION,OPTIONS,OUTPUTFORMAT,OVERWRITE,PARTITIONED,PARTITIONS,PERCENT,PIVOT,PLACING,PRECEDING,PRINCIPALS,PROCEDURES,PROPERTIES,PURGE,QUARTER,QUERY,RECORDREADER,RECORDWRITER,RECOVER,RECURSION,REDUCE,REFRESH,RELY,RENAME,REPAIR,REPEAT,REPEATABLE,REPLACE,RESET,RESPECT,RESTRICT,ROLE,ROLES,SCHEMA,SCHEMAS,SECONDS,SECURITY,SEMI,SEPARATED,SERDE,SERDEPROPERTIES,SETS,SHORT,SHOW,SINGLE,SKEWED,SORT,SORTED,SOURCE,STATISTICS,STORED,STRATIFY,STREAM,STREAMING,STRING,STRUCT,SUBSTR,SYNC,SYSTEM_TIME,SYSTEM_VERSION,TABLES,TARGET,TBLPROPERTIES,TERMINATED,TIMEDIFF,TIMESTAMPADD,TIMESTAMPDIFF,TIMESTAMP_LTZ,TIMESTAMP_NTZ,TINYINT,TOUCH,TRANSACTION,TRANSACTIONS,TRANSFORM,TRUNCATE,TRY_CAST,TYPE,UNARCHIVE,UNBOUNDED,UNCACHE,UNLOCK,UNPIVOT,UNSET,UNTIL,USE,VAR,VARIABLE,VARIANT,VERSION,VIEW,VIEWS,VOID,WATERMARK,WEEK,WEEKS,WHILE,X,YEARS,ZONE")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by the way, this doesnt support for Spark connect yet? (proto Sample message should include sample_method)

Copy link
Copy Markdown
Author

@stanyao stanyao Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@szehon-ho , Spark Connect is outside the scope of this PR. It will require changes in wire protocol and all language clients, which is worth a focused review. Is it OK I log a JIRA item to track a follow up addition of Spark Connect support? I also plan to log another JIRA item to add DataFrame API support too.

stanyao pushed a commit to stanyao/blocksampling that referenced this pull request Apr 8, 2026
Summary:
CONTEXT: Addressing code review feedback on PR apache#54972.
WHAT:
- Use PhysicalOperation extractor in pushDownSample for System sampling,
  consistent with other pushdown rules
- Remove unnecessary SubqueryAlias match (eliminated before this rule runs)
- Guard pushDownJoin against composing with pushed samples to prevent
  silent sample loss
- Use user-friendly error message for TABLESAMPLE_SYSTEM
- Include sample method in EXPLAIN pushed sample metadata
- Add 'as x' alias to BERNOULLI REPEATABLE parser test for consistency

Test Plan:
Existing tests. No golden file or EXPLAIN string assertions affected.
@stanyao stanyao force-pushed the spark-55978-tablesample-system branch from 3437a06 to ee53951 Compare April 8, 2026 00:29
stanyao pushed a commit to stanyao/blocksampling that referenced this pull request Apr 8, 2026
Summary:
CONTEXT: Review feedback on PR apache#54972 for TABLESAMPLE SYSTEM block sampling.
WHAT:
- Add DSv2 SampleMethod Java enum to replace boolean isSystemSampling parameter
- Update SupportsPushDownTableSample Javadoc to clarify BERNOULLI vs SYSTEM
- Replace _LEGACY_ERROR_TEMP_0035 with UNSUPPORTED_FEATURE.TABLESAMPLE_SYSTEM
- Restrict SYSTEM sample pushdown to direct table scans only (no subqueries/views)
- Remove findScanBuilderHolder that incorrectly traversed through Filter/Project

Test Plan:
Existing tests. Error class added to error-conditions.json.
stanyao pushed a commit to stanyao/blocksampling that referenced this pull request Apr 8, 2026
Summary:
CONTEXT: Addressing code review feedback on PR apache#54972.
WHAT:
- Use PhysicalOperation extractor in pushDownSample for System sampling,
  consistent with other pushdown rules
- Remove unnecessary SubqueryAlias match (eliminated before this rule runs)
- Guard pushDownJoin against composing with pushed samples to prevent
  silent sample loss
- Use user-friendly error message for TABLESAMPLE_SYSTEM
- Include sample method in EXPLAIN pushed sample metadata
- Add 'as x' alias to BERNOULLI REPEATABLE parser test for consistency

Test Plan:
Existing tests. No golden file or EXPLAIN string assertions affected.
@stanyao stanyao force-pushed the spark-55978-tablesample-system branch from ca7eab3 to 549db12 Compare April 8, 2026 23:18
stanyao pushed a commit to stanyao/blocksampling that referenced this pull request Apr 8, 2026
Summary:
CONTEXT: Review feedback on PR apache#54972 for TABLESAMPLE SYSTEM block sampling.
WHAT:
- Add DSv2 SampleMethod Java enum to replace boolean isSystemSampling parameter
- Update SupportsPushDownTableSample Javadoc to clarify BERNOULLI vs SYSTEM
- Replace _LEGACY_ERROR_TEMP_0035 with UNSUPPORTED_FEATURE.TABLESAMPLE_SYSTEM
- Restrict SYSTEM sample pushdown to direct table scans only (no subqueries/views)
- Remove findScanBuilderHolder that incorrectly traversed through Filter/Project

Test Plan:
Existing tests. Error class added to error-conditions.json.
stanyao pushed a commit to stanyao/blocksampling that referenced this pull request Apr 8, 2026
Summary:
CONTEXT: Addressing code review feedback on PR apache#54972.
WHAT:
- Use PhysicalOperation extractor in pushDownSample for System sampling,
  consistent with other pushdown rules
- Remove unnecessary SubqueryAlias match (eliminated before this rule runs)
- Guard pushDownJoin against composing with pushed samples to prevent
  silent sample loss
- Use user-friendly error message for TABLESAMPLE_SYSTEM
- Include sample method in EXPLAIN pushed sample metadata
- Add 'as x' alias to BERNOULLI REPEATABLE parser test for consistency

Test Plan:
Existing tests. No golden file or EXPLAIN string assertions affected.
@stanyao stanyao force-pushed the spark-55978-tablesample-system branch from 06ab21c to 447d891 Compare April 9, 2026 05:10
@stanyao
Copy link
Copy Markdown
Author

stanyao commented Apr 9, 2026

Squashed multiple commits into a single one to make code review easier.

…shdown

This PR adds support for ANSI SQL `TABLESAMPLE SYSTEM` (block-level sampling)
alongside the existing `TABLESAMPLE BERNOULLI` (row-level sampling).

**SQL grammar**: Extended `TABLESAMPLE` to accept an optional `SYSTEM` or
`BERNOULLI` qualifier before the sample method. Added both as non-reserved
keywords. `TABLESAMPLE SYSTEM` only supports `PERCENT` sampling and does
not support `REPEATABLE`.

**Logical plan**: Introduced `SampleMethod` sealed trait (`Bernoulli`/`System`)
and added it to the `Sample` node. Default is `Bernoulli` for backward
compatibility.

**DSv2 pushdown**: Added `SampleMethod` Java enum and extended
`SupportsPushDownTableSample.pushTableSample()` with a new overload.
Sources that don't override the new method reject SYSTEM sampling by default.
SYSTEM pushdown is restricted to direct table scans via `PhysicalOperation`.

**Physical planning**: SYSTEM samples that aren't pushed down to a DSv2
source raise an `AnalysisException` -- there is no row-level fallback since
block sampling is data-source dependent.
@stanyao stanyao force-pushed the spark-55978-tablesample-system branch from 447d891 to a2170d4 Compare April 10, 2026 23:49
@stanyao stanyao requested a review from szehon-ho April 14, 2026 05:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants