Skip to content

fix: support SUBPARTITION TEMPLATE for OceanBase composite partitioning#6630

Open
daguimu wants to merge 1 commit intoalibaba:masterfrom
daguimu:fix/subpartition-template-6160
Open

fix: support SUBPARTITION TEMPLATE for OceanBase composite partitioning#6630
daguimu wants to merge 1 commit intoalibaba:masterfrom
daguimu:fix/subpartition-template-6160

Conversation

@daguimu
Copy link
Copy Markdown

@daguimu daguimu commented Mar 26, 2026

Problem

OceanBase CREATE TABLE with SUBPARTITION TEMPLATE fails with:

syntax error, expect OPTIONS, actual IDENTIFIER TEMPLATE

Example SQL (from OceanBase V4.2.2 documentation):

CREATE TABLE t2_m_lr(col1 INT, col2 INT)
PARTITION BY LIST (col1)
SUBPARTITION BY RANGE(col2)
SUBPARTITION TEMPLATE
 (SUBPARTITION mp0 VALUES LESS THAN(100),
  SUBPARTITION mp1 VALUES LESS THAN(200),
  SUBPARTITION mp2 VALUES LESS THAN(300))
 (PARTITION p0 VALUES IN(1,3),
  PARTITION p1 VALUES IN(4,6),
  PARTITION p2 VALUES IN(7,9));

Root cause: The MySQL parser (used by OceanBase) only recognized SUBPARTITION OPTIONS after the subpartition-by clause. When encountering SUBPARTITION TEMPLATE, it tried to match OPTIONS and failed.

Fix

Add SUBPARTITION TEMPLATE parsing to MySqlCreateTableParser. When the parser sees TEMPLATE after SUBPARTITION, it parses the template subpartition list (each containing SUBPARTITION name VALUES ...), storing them in the existing SQLSubPartitionBy.subPartitionTemplate field.

The SUBPARTITION OPTIONS path remains unchanged for backward compatibility.

Tests Added

4 test cases in Issue6160.java:

  • test_subpartition_template_list_range — exact SQL from issue (LIST + RANGE template)
  • test_subpartition_template_range_hash — RANGE + HASH template
  • test_subpartition_template_with_values_in — RANGE + LIST template with VALUES IN
  • test_subpartition_options_still_works — regression: SUBPARTITIONS count still works

All 6 existing OceanBase tests pass.

Fixes #6160

Add SUBPARTITION TEMPLATE parsing to MySQL parser (used by OceanBase).
Previously only SUBPARTITION OPTIONS was recognized after the subpartition-by
clause, causing SUBPARTITION TEMPLATE to fail with "expect OPTIONS, actual
IDENTIFIER TEMPLATE".

The template contains subpartition definitions with VALUES LESS THAN or
VALUES IN clauses that get applied to each partition in composite
partitioning.

Fixes alibaba#6160
@daguimu daguimu force-pushed the fix/subpartition-template-6160 branch from 50b7dab to 5a73b72 Compare March 26, 2026 04:19
Comment on lines +1436 to +1448
for (; ; ) {
acceptIdentifier("SUBPARTITION");
SQLSubPartition subPartition = new SQLSubPartition();
subPartition.setName(this.exprParser.name());
SQLPartitionValue values = this.exprParser.parsePartitionValues();
if (values != null) {
subPartition.setValues(values);
}
subPartition.setParent(subPartitionByClause);
subPartitionByClause.getSubPartitionTemplate().add(subPartition);
if (lexer.token() == Token.COMMA) {
lexer.nextToken();
continue;
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.

[Suggestion] Template subpartitions are manually constructed with only name + VALUES, skipping per-subpartition properties (TABLESPACE, COMMENT, ENGINE, DATA DIRECTORY, etc.) that this.exprParser.parseSubPartition() handles. The Oracle parser's equivalent code correctly calls parseSubPartition().

Suggested fix: Replace the manual construction with a call to the existing parser method:

acceptIdentifier("SUBPARTITION");
SQLSubPartition subPartition = this.exprParser.parseSubPartition();
SQLPartitionValue values = this.exprParser.parsePartitionValues();
if (values != null) {
    subPartition.setValues(values);
}
subPartition.setParent(subPartitionByClause);
subPartitionByClause.getSubPartitionTemplate().add(subPartition);

List<SQLStatement> stmtList = parser.parseStatementList();
assertEquals(1, stmtList.size());
}
}
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.

[Suggestion] All 4 test cases assert stmtList.size() == 1 but never verify subPartitionTemplate contents (names, values, count). Neighboring tests (Issue5078, Issue6102) use round-trip verification via SQLParseAssertUtil.assertParseSql().

Suggested fix: Add AST-level assertions (after fixing output visitors for round-trip support):

SQLCreateTableStatement stmt = (SQLCreateTableStatement) stmtList.get(0);
SQLSubPartitionBy spBy = stmt.getPartitionBy().getSubPartitionBy();
List<SQLSubPartition> template = spBy.getSubPartitionTemplate();
assertEquals(3, template.size());
assertEquals("mp0", template.get(0).getName().getSimpleName());

Or use SQLParseAssertUtil.assertParseSql(sql, dbType) for round-trip validation once output visitors are fixed.

Copy link
Copy Markdown
Member

@wenshao wenshao left a comment

Choose a reason for hiding this comment

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

Review: fix: support SUBPARTITION TEMPLATE for OceanBase composite partitioning

Parsing logic is correct and all tests pass (49 OceanBase test classes, 0 failures). The PR successfully adds SUBPARTITION TEMPLATE parsing. However, the parsed template data has limited end-to-end functionality due to gaps in the existing AST infrastructure that this PR exposes.

Findings

1. accept0() in all SQLSubPartitionBy subclasses skips subPartitionTemplate [Suggestion]
The root cause: none of the accept0() implementations in SQLSubPartitionByHash, SQLSubPartitionByRange, SQLSubPartitionByList, MySqlSubPartitionByKey, MySqlSubPartitionByList, or MySqlSubPartitionByValue visit subPartitionTemplate. This makes template subpartitions invisible to ALL visitors — schema analysis, validation, statistics, and custom AST traversal. Fix: add acceptChild(visitor, subPartitionTemplate) to each accept0().

2. Output visitors silently drop SUBPARTITION TEMPLATE for all sub-partition types [Suggestion]
Only SQLASTOutputVisitor.visit(SQLSubPartitionByList) renders the template. visit(SQLSubPartitionByRange), visit(SQLSubPartitionByHash), and the MySQL-specific overrides (MySqlSubPartitionByList, MySqlSubPartitionByKey) all silently drop it. This means round-trip parse→output produces incorrect DDL that is missing the template. Fix: add template output blocks mirroring the existing visit(SQLSubPartitionByList) pattern.

3. Manual SQLSubPartition construction skips properties [Suggestion] (inline comment posted)
The new code manually constructs SQLSubPartition with only name + VALUES, while this.exprParser.parseSubPartition() handles TABLESPACE, COMMENT, ENGINE, etc. The Oracle parser's equivalent code calls parseSubPartition().

4. Tests only verify parse success, not AST structure [Suggestion] (inline comment posted)
All 4 test cases assert stmtList.size() == 1 without checking subPartitionTemplate contents. Neighboring tests use round-trip verification.

5. clone() for SQLSubPartitionByHash/SQLSubPartitionByRange loses template [Possibly, needs human review]
These clone() methods don't call super.cloneTo(), so cloning an AST with template subpartitions silently drops them.

Verdict: Comment

The parsing is correct for the targeted use cases. Findings 1-2 are pre-existing infrastructure gaps exposed by this PR — they should be addressed before or shortly after merge for end-to-end functionality. Finding 3 is a forward-compatibility improvement. Finding 4 strengthens test coverage.

Reviewed by glm-5.1 via Qwen Code /review

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.

OceanBase创建模板化二级分区表,SQL解析异常, 创建非模板化二级分区表 二级分区 List Range SQL解析异常

2 participants