This is an automated email from the ASF dual-hosted git repository.
jshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new a583e6ba4a [#9731] feat(paimon): support hash distribution in Paimon
catalog (#9753)
a583e6ba4a is described below
commit a583e6ba4a0810608ad5c6abded8b279417cce86
Author: FANNG <[email protected]>
AuthorDate: Thu Jan 29 02:32:45 2026 +0900
[#9731] feat(paimon): support hash distribution in Paimon catalog (#9753)
### What changes were proposed in this pull request?
Support hash distribution in Paimon catalog
### Why are the changes needed?
Fix: #9731
### Does this PR introduce _any_ user-facing change?
no
### How was this patch tested?
existing tests
---
.../catalog/lakehouse/paimon/PaimonConstants.java | 1 +
.../lakehouse/paimon/GravitinoPaimonTable.java | 73 +++++++++++++
.../lakehouse/paimon/PaimonCatalogOperations.java | 76 +++++++++++++-
.../paimon/PaimonTablePropertiesMetadata.java | 2 +
.../lakehouse/paimon/TestGravitinoPaimonTable.java | 114 +++++++++++++++++++++
docs/lakehouse-paimon-catalog.md | 4 +
6 files changed, 267 insertions(+), 3 deletions(-)
diff --git
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonConstants.java
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonConstants.java
index 291a7ea969..04e8988f49 100644
---
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonConstants.java
+++
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonConstants.java
@@ -49,6 +49,7 @@ public class PaimonConstants {
public static final String COMMENT = "comment";
public static final String OWNER = "owner";
public static final String BUCKET_KEY = "bucket-key";
+ public static final String BUCKET_NUM = "bucket";
public static final String MERGE_ENGINE = "merge-engine";
public static final String SEQUENCE_FIELD = "sequence.field";
public static final String ROWKIND_FIELD = "rowkind.field";
diff --git
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/GravitinoPaimonTable.java
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/GravitinoPaimonTable.java
index 2853abbbe3..d09d58cdd9 100644
---
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/GravitinoPaimonTable.java
+++
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/GravitinoPaimonTable.java
@@ -18,6 +18,8 @@
*/
package org.apache.gravitino.catalog.lakehouse.paimon;
+import static
org.apache.gravitino.catalog.lakehouse.paimon.PaimonTablePropertiesMetadata.BUCKET_KEY;
+import static
org.apache.gravitino.catalog.lakehouse.paimon.PaimonTablePropertiesMetadata.BUCKET_NUM;
import static
org.apache.gravitino.catalog.lakehouse.paimon.PaimonTablePropertiesMetadata.COMMENT;
import static
org.apache.gravitino.dto.rel.partitioning.Partitioning.EMPTY_PARTITIONING;
import static org.apache.gravitino.meta.AuditInfo.EMPTY;
@@ -34,9 +36,14 @@ import java.util.Map;
import java.util.stream.Collectors;
import lombok.Getter;
import lombok.ToString;
+import org.apache.commons.lang3.StringUtils;
import org.apache.gravitino.connector.BaseTable;
import org.apache.gravitino.connector.TableOperations;
+import org.apache.gravitino.rel.expressions.Expression;
import org.apache.gravitino.rel.expressions.NamedReference;
+import org.apache.gravitino.rel.expressions.distributions.Distribution;
+import org.apache.gravitino.rel.expressions.distributions.Distributions;
+import org.apache.gravitino.rel.expressions.distributions.Strategy;
import org.apache.gravitino.rel.expressions.transforms.Transform;
import org.apache.gravitino.rel.expressions.transforms.Transforms;
import org.apache.gravitino.rel.indexes.Index;
@@ -79,6 +86,9 @@ public class GravitinoPaimonTable extends BaseTable {
Map<String, String> normalizedProperties = new HashMap<>(properties);
normalizedProperties.remove(COMMENT);
+ normalizedProperties.remove(BUCKET_KEY);
+ normalizedProperties.remove(BUCKET_NUM);
+ applyDistribution(normalizedProperties, distribution);
List<String> partitionKeys = getPartitionKeys(partitioning);
List<String> primaryKeys = getPrimaryKeysFromIndexes(indexes);
@@ -106,6 +116,7 @@ public class GravitinoPaimonTable extends BaseTable {
GravitinoPaimonColumn.fromPaimonRowType(table.rowType())
.toArray(new GravitinoPaimonColumn[0]))
.withPartitioning(toGravitinoPartitioning(table.partitionKeys()))
+ .withDistribution(getDistribution(table.options()))
.withComment(table.comment().orElse(null))
.withProperties(table.options())
.withIndexes(constructIndexesFromPrimaryKeys(table))
@@ -188,6 +199,67 @@ public class GravitinoPaimonTable extends BaseTable {
}
}
+ private static void applyDistribution(Map<String, String> properties,
Distribution distribution) {
+ if (distribution == null || distribution.strategy() ==
Distributions.NONE.strategy()) {
+ return;
+ }
+
+ List<String> bucketKeys = getBucketKeys(distribution);
+ if (!bucketKeys.isEmpty()) {
+ properties.put(BUCKET_KEY, String.join(",", bucketKeys));
+ }
+
+ properties.put(BUCKET_NUM, String.valueOf(distribution.number()));
+ }
+
+ private static List<String> getBucketKeys(Distribution distribution) {
+ return Arrays.stream(distribution.expressions())
+ .map(
+ expression -> {
+ Preconditions.checkArgument(
+ expression instanceof NamedReference.FieldReference,
+ "Paimon bucket keys must be plain column references.");
+ NamedReference.FieldReference reference =
(NamedReference.FieldReference) expression;
+ String[] fieldName = reference.fieldName();
+ Preconditions.checkArgument(
+ fieldName.length == 1, "Paimon bucket keys must be single
columns.");
+ return fieldName[0];
+ })
+ .collect(Collectors.toList());
+ }
+
+ static Distribution getDistribution(Map<String, String> properties) {
+ if (properties == null) {
+ return Distributions.NONE;
+ }
+ String bucketKeys = properties.get(BUCKET_KEY);
+ if (StringUtils.isBlank(bucketKeys)) {
+ return Distributions.NONE;
+ }
+ List<String> bucketKeyList =
+ Arrays.stream(bucketKeys.split(","))
+ .map(String::trim)
+ .filter(StringUtils::isNotBlank)
+ .collect(Collectors.toList());
+ if (bucketKeyList.isEmpty()) {
+ return Distributions.NONE;
+ }
+ Expression[] expressions =
+
bucketKeyList.stream().map(NamedReference::field).toArray(Expression[]::new);
+ String bucketValue = properties.get(BUCKET_NUM);
+ if (StringUtils.isBlank(bucketValue)) {
+ return Distributions.auto(Strategy.HASH, expressions);
+ }
+ String trimmedBucketValue = bucketValue.trim();
+ try {
+ return Distributions.hash(Integer.parseInt(trimmedBucketValue),
expressions);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(
+ String.format("Paimon bucket number must be a valid integer, but was
'%s'.", bucketValue),
+ e);
+ }
+ }
+
/** A builder class for constructing {@link GravitinoPaimonTable} instance.
*/
public static class Builder extends BaseTableBuilder<Builder,
GravitinoPaimonTable> {
@@ -206,6 +278,7 @@ public class GravitinoPaimonTable extends BaseTable {
paimonTable.comment = comment;
paimonTable.columns = columns;
paimonTable.partitioning = partitioning;
+ paimonTable.distribution = distribution;
paimonTable.properties = properties == null ? Maps.newHashMap() :
Maps.newHashMap(properties);
paimonTable.indexes = indexes;
paimonTable.auditInfo = auditInfo;
diff --git
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonCatalogOperations.java
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonCatalogOperations.java
index cbcc024170..c1e7f08a30 100644
---
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonCatalogOperations.java
+++
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonCatalogOperations.java
@@ -29,6 +29,7 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import java.time.Instant;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -60,6 +61,7 @@ import org.apache.gravitino.rel.TableChange.RenameTable;
import org.apache.gravitino.rel.expressions.NamedReference;
import org.apache.gravitino.rel.expressions.distributions.Distribution;
import org.apache.gravitino.rel.expressions.distributions.Distributions;
+import org.apache.gravitino.rel.expressions.distributions.Strategy;
import org.apache.gravitino.rel.expressions.sorts.SortOrder;
import org.apache.gravitino.rel.expressions.transforms.Transform;
import org.apache.gravitino.rel.indexes.Index;
@@ -348,13 +350,11 @@ public class PaimonCatalogOperations implements
CatalogOperations, SupportsSchem
&& references[0] instanceof
NamedReference.FieldReference;
}),
"Paimon partition columns should not be nested.");
- Preconditions.checkArgument(
- distribution == null || distribution.strategy() ==
Distributions.NONE.strategy(),
- "Distribution is not supported for Paimon in Gravitino now.");
Preconditions.checkArgument(
sortOrders == null || sortOrders.length == 0,
"Sort orders are not supported for Paimon in Gravitino.");
checkPaimonIndexes(indexes);
+ validateDistribution(distribution, columns, indexes);
String currentUser = currentUser();
GravitinoPaimonTable createdTable =
GravitinoPaimonTable.builder()
@@ -378,6 +378,7 @@ public class PaimonCatalogOperations implements
CatalogOperations, SupportsSchem
.withPartitioning(partitioning)
.withComment(comment)
.withProperties(properties)
+ .withDistribution(distribution)
.withIndexes(indexes)
.withAuditInfo(
AuditInfo.builder().withCreator(currentUser).withCreateTime(Instant.now()).build())
@@ -501,6 +502,75 @@ public class PaimonCatalogOperations implements
CatalogOperations, SupportsSchem
"Paimon only supports primary key Index."));
}
+ private void validateDistribution(Distribution distribution, Column[]
columns, Index[] indexes) {
+ if (distribution == null || distribution.strategy() ==
Distributions.NONE.strategy()) {
+ return;
+ }
+
+ Preconditions.checkArgument(
+ distribution.strategy() == Strategy.HASH,
+ "Paimon only supports HASH distribution strategy.");
+
+ Preconditions.checkArgument(
+ distribution.expressions() != null &&
distribution.expressions().length > 0,
+ "Paimon bucket keys must be specified for HASH distribution.");
+
+ int bucketNumber = distribution.number();
+ Preconditions.checkArgument(
+ bucketNumber == Distributions.AUTO || bucketNumber > 0,
+ "Paimon bucket number must be positive or AUTO.");
+
+ List<String> bucketKeys = extractBucketKeys(distribution);
+ List<String> columnNames =
+ Arrays.stream(columns).map(Column::name).collect(Collectors.toList());
+ bucketKeys.forEach(
+ bucketKey ->
+ Preconditions.checkArgument(
+ columnNames.stream().anyMatch(name -> name.equals(bucketKey)),
+ "Distribution column %s does not exist in table columns.",
+ bucketKey));
+
+ List<String> primaryKeys = extractPrimaryKeys(indexes);
+ if (!primaryKeys.isEmpty()) {
+ Preconditions.checkArgument(
+ primaryKeys.containsAll(bucketKeys),
+ "Paimon bucket keys must be a subset of primary key columns for
primary key tables.");
+ }
+ }
+
+ private static List<String> extractBucketKeys(Distribution distribution) {
+ return Arrays.stream(distribution.expressions())
+ .map(
+ expression -> {
+ Preconditions.checkArgument(
+ expression instanceof NamedReference.FieldReference,
+ "Paimon bucket keys must be plain column references.");
+ NamedReference.FieldReference reference =
(NamedReference.FieldReference) expression;
+ String[] fieldNames = reference.fieldName();
+ Preconditions.checkArgument(
+ fieldNames.length == 1, "Paimon bucket keys must be single
columns.");
+ return fieldNames[0];
+ })
+ .collect(Collectors.toList());
+ }
+
+ private static List<String> extractPrimaryKeys(Index[] indexes) {
+ if (indexes == null || indexes.length == 0) {
+ return Collections.emptyList();
+ }
+ // Paimon supports at most one index; this is enforced in {@code
checkPaimonIndexes()}.
+ Index primaryKeyIndex = indexes[0];
+ return Arrays.stream(primaryKeyIndex.fieldNames())
+ .map(
+ fieldName -> {
+ Preconditions.checkArgument(
+ fieldName != null && fieldName.length == 1,
+ "Paimon primary keys must be single columns.");
+ return fieldName[0];
+ })
+ .collect(Collectors.toList());
+ }
+
/**
* Performs rename table change with the provided identifier.
*
diff --git
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonTablePropertiesMetadata.java
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonTablePropertiesMetadata.java
index ad63df6783..e56e6ea6f3 100644
---
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonTablePropertiesMetadata.java
+++
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonTablePropertiesMetadata.java
@@ -38,6 +38,7 @@ public class PaimonTablePropertiesMetadata extends
BasePropertiesMetadata {
public static final String COMMENT = PaimonConstants.COMMENT;
public static final String OWNER = PaimonConstants.OWNER;
public static final String BUCKET_KEY = PaimonConstants.BUCKET_KEY;
+ public static final String BUCKET_NUM = PaimonConstants.BUCKET_NUM;
public static final String MERGE_ENGINE = PaimonConstants.MERGE_ENGINE;
public static final String SEQUENCE_FIELD = PaimonConstants.SEQUENCE_FIELD;
public static final String ROWKIND_FIELD = PaimonConstants.ROWKIND_FIELD;
@@ -52,6 +53,7 @@ public class PaimonTablePropertiesMetadata extends
BasePropertiesMetadata {
stringReservedPropertyEntry(COMMENT, "The table comment", true),
stringReservedPropertyEntry(OWNER, "The table owner", false),
stringReservedPropertyEntry(BUCKET_KEY, "The table bucket key",
false),
+ stringReservedPropertyEntry(BUCKET_NUM, "The table bucket number",
false),
stringImmutablePropertyEntry(
MERGE_ENGINE, "The table merge engine", false, null, false,
false),
stringImmutablePropertyEntry(
diff --git
a/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/TestGravitinoPaimonTable.java
b/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/TestGravitinoPaimonTable.java
index 466ecb3459..ae1ecc2707 100644
---
a/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/TestGravitinoPaimonTable.java
+++
b/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/TestGravitinoPaimonTable.java
@@ -23,6 +23,7 @@ import static
org.apache.gravitino.catalog.lakehouse.paimon.GravitinoPaimonTable
import static
org.apache.gravitino.catalog.lakehouse.paimon.TestPaimonCatalog.PAIMON_PROPERTIES_METADATA;
import static
org.apache.gravitino.catalog.lakehouse.paimon.utils.TableOpsUtils.checkColumnCapability;
import static org.apache.gravitino.rel.Column.DEFAULT_VALUE_NOT_SET;
+import static org.apache.gravitino.rel.expressions.distributions.Strategy.HASH;
import static
org.apache.gravitino.rel.expressions.transforms.Transforms.identity;
import static org.apache.gravitino.rel.indexes.Indexes.primary;
@@ -51,6 +52,7 @@ import org.apache.gravitino.rel.Column;
import org.apache.gravitino.rel.Table;
import org.apache.gravitino.rel.TableCatalog;
import org.apache.gravitino.rel.expressions.NamedReference;
+import org.apache.gravitino.rel.expressions.distributions.Distribution;
import org.apache.gravitino.rel.expressions.distributions.Distributions;
import org.apache.gravitino.rel.expressions.sorts.SortOrder;
import org.apache.gravitino.rel.expressions.transforms.Transform;
@@ -335,6 +337,118 @@ public class TestGravitinoPaimonTable {
}
}
+ @Test
+ void testCreatePaimonTableWithDistribution() {
+ String paimonTableName = "test_paimon_table_with_distribution";
+ NameIdentifier tableIdentifier = NameIdentifier.of(paimonSchema.name(),
paimonTableName);
+ Map<String, String> properties = Maps.newHashMap();
+
+ Column[] columns =
+ new Column[] {
+ fromPaimonColumn(new DataField(0, "col_1",
DataTypes.INT().notNull(), PAIMON_COMMENT)),
+ fromPaimonColumn(new DataField(1, "col_2",
DataTypes.STRING().nullable(), PAIMON_COMMENT))
+ };
+
+ Table table =
+ paimonCatalogOperations.createTable(
+ tableIdentifier,
+ columns,
+ PAIMON_COMMENT,
+ properties,
+ new Transform[0],
+ Distributions.hash(4, NamedReference.field("col_1")),
+ new SortOrder[0]);
+
+ Assertions.assertEquals(HASH, table.distribution().strategy());
+ Assertions.assertEquals(4, table.distribution().number());
+ Assertions.assertEquals(
+ "col_1", ((NamedReference)
table.distribution().expressions()[0]).fieldName()[0]);
+
+ Table loadedTable = paimonCatalogOperations.loadTable(tableIdentifier);
+ Assertions.assertEquals(HASH, loadedTable.distribution().strategy());
+ Assertions.assertEquals(4, loadedTable.distribution().number());
+ Assertions.assertEquals(
+ "col_1", ((NamedReference)
loadedTable.distribution().expressions()[0]).fieldName()[0]);
+ Assertions.assertEquals(
+ "4",
loadedTable.properties().get(PaimonTablePropertiesMetadata.BUCKET_NUM));
+ Assertions.assertEquals(
+ "col_1",
loadedTable.properties().get(PaimonTablePropertiesMetadata.BUCKET_KEY));
+ }
+
+ @Test
+ void testGetDistributionWithAutoBucketNumber() {
+ Map<String, String> options = Maps.newHashMap();
+ options.put(PaimonTablePropertiesMetadata.BUCKET_KEY, "col_1");
+
+ Distribution distribution = GravitinoPaimonTable.getDistribution(options);
+ Assertions.assertEquals(HASH, distribution.strategy());
+ Assertions.assertEquals(Distributions.AUTO, distribution.number());
+ Assertions.assertEquals(
+ "col_1", ((NamedReference)
distribution.expressions()[0]).fieldName()[0]);
+ }
+
+ @Test
+ void testGetDistributionWithMultipleBucketKeys() {
+ Map<String, String> options = Maps.newHashMap();
+ options.put(PaimonTablePropertiesMetadata.BUCKET_KEY, "col_1,col_2");
+ options.put(PaimonTablePropertiesMetadata.BUCKET_NUM, "4");
+
+ Distribution distribution = GravitinoPaimonTable.getDistribution(options);
+ Assertions.assertEquals(HASH, distribution.strategy());
+ Assertions.assertEquals(4, distribution.number());
+ Assertions.assertEquals(2, distribution.expressions().length);
+ Assertions.assertEquals(
+ "col_1", ((NamedReference)
distribution.expressions()[0]).fieldName()[0]);
+ Assertions.assertEquals(
+ "col_2", ((NamedReference)
distribution.expressions()[1]).fieldName()[0]);
+ }
+
+ @Test
+ void testGetDistributionWithInvalidBucketNumber() {
+ Map<String, String> options = Maps.newHashMap();
+ options.put(PaimonTablePropertiesMetadata.BUCKET_KEY, "col_1");
+ options.put(PaimonTablePropertiesMetadata.BUCKET_NUM, "not_a_number");
+
+ IllegalArgumentException exception =
+ Assertions.assertThrows(
+ IllegalArgumentException.class, () ->
GravitinoPaimonTable.getDistribution(options));
+ Assertions.assertTrue(
+ exception.getMessage().contains("Paimon bucket number must be a valid
integer"));
+ }
+
+ @Test
+ void testCreatePaimonPrimaryKeyTableWithInvalidBucketKey() {
+ String paimonTableName = "test_paimon_primary_key_table_invalid_bucket";
+ NameIdentifier tableIdentifier = NameIdentifier.of(paimonSchema.name(),
paimonTableName);
+
+ Column[] columns =
+ new Column[] {
+ fromPaimonColumn(new DataField(0, "col_1",
DataTypes.INT().notNull(), PAIMON_COMMENT)),
+ fromPaimonColumn(new DataField(1, "col_2",
DataTypes.STRING().notNull(), PAIMON_COMMENT))
+ };
+
+ Index[] indexes =
+ Collections.singletonList(
+ primary(PAIMON_PRIMARY_KEY_INDEX_NAME, new String[][] {new
String[] {"col_2"}}))
+ .toArray(new Index[0]);
+
+ IllegalArgumentException exception =
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ paimonCatalogOperations.createTable(
+ tableIdentifier,
+ columns,
+ PAIMON_COMMENT,
+ Maps.newHashMap(),
+ new Transform[0],
+ Distributions.hash(2, NamedReference.field("col_1")),
+ new SortOrder[0],
+ indexes));
+ Assertions.assertTrue(
+ exception.getMessage().contains("bucket keys must be a subset of
primary key columns"));
+ }
+
@Test
void testDropPaimonTable() {
NameIdentifier tableIdentifier = NameIdentifier.of(paimonSchema.name(),
genRandomName());
diff --git a/docs/lakehouse-paimon-catalog.md b/docs/lakehouse-paimon-catalog.md
index 8bb9b0ca2b..20945fe44f 100644
--- a/docs/lakehouse-paimon-catalog.md
+++ b/docs/lakehouse-paimon-catalog.md
@@ -192,6 +192,9 @@ You can pass [Paimon table
properties](https://paimon.apache.org/docs/0.8/mainte
**Immutable**: Fields that cannot be modified once set.
:::
+Bucket settings are defined via Gravitino table distribution (HASH strategy).
The `bucket` and
+`bucket-key` options are reserved and derived from the distribution instead of
being set directly.
+
| Configuration item | Description | Default Value | Required |
Reserved | Immutable | Since version |
|--------------------|---------------------------|---------------|-----------|----------|-----------|-------------------|
| `merge-engine` | The table merge-engine. | (none) | No |
No | Yes | 0.6.0-incubating |
@@ -200,6 +203,7 @@ You can pass [Paimon table
properties](https://paimon.apache.org/docs/0.8/mainte
| `comment` | The table comment. | (none) | No |
Yes | No | 0.6.0-incubating |
| `owner` | The table owner. | (none) | No |
Yes | No | 0.6.0-incubating |
| `bucket-key` | The table bucket-key. | (none) | No |
Yes | No | 0.6.0-incubating |
+| `bucket` | The table bucket number. | (none) | No |
Yes | No | 1.2.0 |
| `primary-key` | The table primary-key. | (none) | No |
Yes | No | 0.6.0-incubating |
| `partition` | The table partition. | (none) | No |
Yes | No | 0.6.0-incubating |