Skip to content

Commit fbec912

Browse files
authored
[IcebergIO] extend table partitioning to Beam SQL (#35268)
* add sql partitioning * trigger ITs and add to CHANGES * simplify test * include emoty namespace fix * add error when using partitioning for unsupported tables * fix test
1 parent 6126b03 commit fbec912

19 files changed

Lines changed: 259 additions & 16 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"comment": "Modify this file in a trivial way to cause this test suite to run.",
3-
"modification": 6
3+
"modification": 3
44
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"comment": "Modify this file in a trivial way to cause this test suite to run",
3-
"modification": 2
3+
"modification": 3
44
}

CHANGES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
* [IcebergIO] Now available with Beam SQL! ([#34799](https://github.com/apache/beam/pull/34799))
7979
* [IcebergIO] Support reading with column pruning ([#34856](https://github.com/apache/beam/pull/34856))
8080
* [IcebergIO] Support reading with pushdown filtering ([#34827](https://github.com/apache/beam/pull/34827))
81-
* [IcebergIO] Create tables with a specified partition spec ([#34966](https://github.com/apache/beam/pull/34966))
81+
* [IcebergIO] Create tables with a specified partition spec ([#34966](https://github.com/apache/beam/pull/34966), [#35268](https://github.com/apache/beam/pull/35268))
8282
* [IcebergIO] Dynamically create namespaces if needed ([#35228](https://github.com/apache/beam/pull/35228))
8383

8484
## New Features / Improvements

sdks/java/extensions/sql/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ dependencies {
114114
provided library.java.hadoop_client
115115
permitUnusedDeclared library.java.hadoop_client
116116
provided library.java.kafka_clients
117+
118+
testImplementation "org.apache.iceberg:iceberg-api:1.6.1"
119+
testImplementation "org.apache.iceberg:iceberg-core:1.6.1"
117120
testImplementation library.java.vendored_calcite_1_28_0
118121
testImplementation library.java.vendored_guava_32_1_2_jre
119122
testImplementation library.java.junit

sdks/java/extensions/sql/src/main/codegen/config.fmpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ data: {
4545
"LOCATION"
4646
"TBLPROPERTIES"
4747
"PROPERTIES"
48+
"PARTITIONED"
4849
]
4950

5051
# List of keywords from "keywords" section that are not reserved.

sdks/java/extensions/sql/src/main/codegen/includes/parserImpls.ftl

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,12 +255,28 @@ SqlDrop SqlDropCatalog(Span s, boolean replace) :
255255
}
256256
}
257257

258+
SqlNodeList PartitionFieldList() :
259+
{
260+
final List<SqlNode> list = new ArrayList<SqlNode>();
261+
SqlNode field;
262+
}
263+
{
264+
field = StringLiteral() { list.add(field); }
265+
(
266+
<COMMA> field = StringLiteral() { list.add(field); }
267+
)*
268+
{
269+
return new SqlNodeList(list, getPos());
270+
}
271+
}
272+
258273
/**
259274
* Note: This example is probably out of sync with the code.
260275
*
261-
* CREATE TABLE ( IF NOT EXISTS )?
276+
* CREATE EXTERNAL TABLE ( IF NOT EXISTS )?
262277
* ( database_name '.' )? table_name '(' column_def ( ',' column_def )* ')'
263278
* TYPE type_name
279+
* ( PARTITIONED BY '(' partition_field ( ',' partition_field )* ')' )?
264280
* ( COMMENT comment_string )?
265281
* ( LOCATION location_string )?
266282
* ( TBLPROPERTIES tbl_properties )?
@@ -271,6 +287,7 @@ SqlCreate SqlCreateExternalTable(Span s, boolean replace) :
271287
final SqlIdentifier id;
272288
List<Schema.Field> fieldList = null;
273289
final SqlNode type;
290+
SqlNodeList partitionFields = null;
274291
SqlNode comment = null;
275292
SqlNode location = null;
276293
SqlNode tblProperties = null;
@@ -290,6 +307,7 @@ SqlCreate SqlCreateExternalTable(Span s, boolean replace) :
290307
|
291308
type = SimpleIdentifier()
292309
)
310+
[ <PARTITIONED> <BY> <LPAREN> partitionFields = PartitionFieldList() <RPAREN> ]
293311
[ <COMMENT> comment = StringLiteral() ]
294312
[ <LOCATION> location = StringLiteral() ]
295313
[ <TBLPROPERTIES> tblProperties = StringLiteral() ]
@@ -302,6 +320,7 @@ SqlCreate SqlCreateExternalTable(Span s, boolean replace) :
302320
id,
303321
fieldList,
304322
type,
323+
partitionFields,
305324
comment,
306325
location,
307326
tblProperties);

sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/parser/SqlCreateExternalTable.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919

2020
import static org.apache.beam.sdk.schemas.Schema.toSchema;
2121
import static org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.util.Static.RESOURCE;
22+
import static org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
2223
import static org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkNotNull;
2324

2425
import java.util.List;
26+
import java.util.stream.Collectors;
2527
import org.apache.beam.sdk.extensions.sql.TableUtils;
2628
import org.apache.beam.sdk.extensions.sql.impl.BeamCalciteSchema;
2729
import org.apache.beam.sdk.extensions.sql.impl.utils.CalciteUtils;
@@ -33,12 +35,14 @@
3335
import org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.sql.SqlIdentifier;
3436
import org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.sql.SqlKind;
3537
import org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.sql.SqlNode;
38+
import org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.sql.SqlNodeList;
3639
import org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.sql.SqlOperator;
3740
import org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.sql.SqlSpecialOperator;
3841
import org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.sql.SqlUtil;
3942
import org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.sql.SqlWriter;
4043
import org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.sql.parser.SqlParserPos;
4144
import org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.util.Pair;
45+
import org.checkerframework.checker.nullness.qual.Nullable;
4246

4347
/** Parse tree for {@code CREATE EXTERNAL TABLE} statement. */
4448
@SuppressWarnings({
@@ -51,6 +55,7 @@ public class SqlCreateExternalTable extends SqlCreate implements BeamSqlParser.E
5155
private final SqlNode comment;
5256
private final SqlNode location;
5357
private final SqlNode tblProperties;
58+
private final @Nullable SqlNodeList partitionFields;
5459

5560
private static final SqlOperator OPERATOR =
5661
new SqlSpecialOperator("CREATE EXTERNAL TABLE", SqlKind.OTHER_DDL);
@@ -63,13 +68,15 @@ public SqlCreateExternalTable(
6368
SqlIdentifier name,
6469
List<Schema.Field> columnList,
6570
SqlNode type,
71+
SqlNodeList partitionFields,
6672
SqlNode comment,
6773
SqlNode location,
6874
SqlNode tblProperties) {
6975
super(OPERATOR, pos, replace, ifNotExists);
7076
this.name = checkNotNull(name);
7177
this.columnList = columnList; // may be null
7278
this.type = checkNotNull(type);
79+
this.partitionFields = partitionFields;
7380
this.comment = comment; // may be null
7481
this.location = location; // may be null
7582
this.tblProperties = tblProperties; // may be null
@@ -98,6 +105,19 @@ public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
98105
}
99106
writer.keyword("TYPE");
100107
type.unparse(writer, 0, 0);
108+
if (partitionFields != null) {
109+
writer.keyword("PARTITIONED");
110+
writer.keyword("BY");
111+
writer.sep("(");
112+
for (int i = 0; i < partitionFields.size(); i++) {
113+
if (i > 0) {
114+
writer.sep(",");
115+
}
116+
SqlNode field = partitionFields.get(i);
117+
field.unparse(writer, 0, 0);
118+
}
119+
writer.sep(")");
120+
}
101121
if (comment != null) {
102122
writer.keyword("COMMENT");
103123
comment.unparse(writer, 0, 0);
@@ -130,7 +150,17 @@ public void execute(CalcitePrepare.Context context) {
130150
name.getParserPosition(),
131151
RESOURCE.internal("Schema is not instanceof BeamCalciteSchema"));
132152
}
153+
133154
BeamCalciteSchema schema = (BeamCalciteSchema) pair.left.schema;
155+
if (partitionFields != null) {
156+
checkArgument(
157+
schema.resolveMetastore().supportsPartitioning(),
158+
"Invalid use of 'PARTITIONED BY()': Table '%s' of type '%s' "
159+
+ "does not support partitioning.",
160+
SqlDdlNodes.name(name),
161+
SqlDdlNodes.getString(type));
162+
}
163+
134164
schema.resolveMetastore().createTable(toTable());
135165
}
136166

@@ -149,11 +179,19 @@ private void unparseColumn(SqlWriter writer, Schema.Field column) {
149179
}
150180
}
151181

182+
private @Nullable List<String> parsePartitionFields() {
183+
if (partitionFields == null) {
184+
return null;
185+
}
186+
return partitionFields.stream().map(SqlDdlNodes::getString).collect(Collectors.toList());
187+
}
188+
152189
private Table toTable() {
153190
return Table.builder()
154191
.type(SqlDdlNodes.getString(type))
155192
.name(SqlDdlNodes.name(name))
156193
.schema(columnList.stream().collect(toSchema()))
194+
.partitionFields(parsePartitionFields())
157195
.comment(SqlDdlNodes.getString(comment))
158196
.location(SqlDdlNodes.getString(location))
159197
.properties(

sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/meta/Table.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.fasterxml.jackson.databind.node.ObjectNode;
2121
import com.google.auto.value.AutoValue;
2222
import java.io.Serializable;
23+
import java.util.List;
2324
import org.apache.beam.sdk.extensions.sql.TableUtils;
2425
import org.apache.beam.sdk.schemas.Schema;
2526
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -34,6 +35,8 @@ public abstract class Table implements Serializable {
3435

3536
public abstract Schema getSchema();
3637

38+
public abstract @Nullable List<String> getPartitionFields();
39+
3740
public abstract @Nullable String getComment();
3841

3942
public abstract @Nullable String getLocation();
@@ -55,6 +58,8 @@ public abstract static class Builder {
5558

5659
public abstract Builder schema(Schema getSchema);
5760

61+
public abstract Builder partitionFields(List<String> fields);
62+
5863
public abstract Builder comment(String name);
5964

6065
public abstract Builder location(String location);

sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/meta/catalog/InMemoryCatalog.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
public class InMemoryCatalog implements Catalog {
2626
private final String name;
2727
private final Map<String, String> properties;
28-
protected final InMemoryMetaStore metaStore = new InMemoryMetaStore();
28+
private final InMemoryMetaStore metaStore = new InMemoryMetaStore();
2929

3030
public InMemoryCatalog(String name, Map<String, String> properties) {
3131
this.name = name;

sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/meta/provider/TableProvider.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,8 @@ default Set<String> getSubProviders() {
7979
default TableProvider getSubProvider(String name) {
8080
return null;
8181
}
82+
83+
default boolean supportsPartitioning() {
84+
return false;
85+
}
8286
}

0 commit comments

Comments
 (0)