apache / apache/gravitino

[Improvement] Fix Lance partition stats drop filter construction for quoted partition/statistic names

Open
#10,273 3 comments 0 reactions 1 assignee Claimed by @RMarx1456 View on GitHub
good first issue improvement
Dominant language
Java
Stars
3.2k
Forks
935
Avg merge
1d 16h
Merged PRs (30d)
298

Description

### What would you like to be improved?

LancePartitionStatisticStorage.dropStatisticsImpl builds the dataset.delete(...) predicate by directly concatenating partitionName and statisticNames into SQL-like quoted strings. If a name contains a single quote (for example part'ition0), the generated predicate becomes invalid. This path is reachable from production REST flow for drop-partition-statistics requests when Lance partition stats storage is enabled.

### How should we improve?

Stop building predicates with raw string concatenation. Escape single quotes in all string literals before composing the filter (for SQL-style literals, replace ' with ''), or switch to a safe API that supports parameterized/structured predicates if available in the Lance Java API.

Here's a test to help:
```
@Test
public void testDropPartitionStatisticsWithSingleQuotePartitionName() throws Exception {
PartitionStatisticStorageFactory factory = new LancePartitionStatisticStorageFactory();

String metalakeName = "metalake";
MetadataObject metadataObject =
MetadataObjects.of(Lists.newArrayList("catalog", "schema", "table"), MetadataObject.Type.TABLE);

EntityStore entityStore = mock(EntityStore.class);
TableEntity tableEntity = mock(TableEntity.class);
when(entityStore.get(any(), any(), any())).thenReturn(tableEntity);
when(tableEntity.id()).thenReturn(202L);
FieldUtils.writeField(GravitinoEnv.getInstance(), "entityStore", entityStore, true);

String location = Files.createTempDirectory("lance_stats_quote_drop_test").toString();
Map properties = Maps.newHashMap();
properties.put("location", location);
LancePartitionStatisticStorage storage =
(LancePartitionStatisticStorage) factory.create(properties);

try {
Map> statistics = Maps.newHashMap();
statistics.put("statistic0", StatisticValues.longValue(1L));

List updates =
Lists.newArrayList(
MetadataObjectStatisticsUpdate.of(
metadataObject,
Lists.newArrayList(
PartitionStatisticsModification.update("part'ition0", statistics))));
storage.updateStatistics(metalakeName, updates);

List drops =
Lists.newArrayList(
MetadataObjectStatisticsDrop.of(
metadataObject,
Lists.newArrayList(
PartitionStatisticsModification.drop(
"part'ition0", Lists.newArrayList("statistic0")))));

Assertions.assertDoesNotThrow(() -> storage.dropStatistics(metalakeName, drops));
} finally {
FileUtils.deleteDirectory(new File(location + "/" + tableEntity.id() + ".lance"));
storage.close();
}
}

```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.