Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ public Pair<TableDto, Boolean> putTable(

// Special case handling
if (tableDto.isPresent() && createUpdateTableRequestBody.isStageReplace()) {
checkIfLockPoliciesUpdated(tableDto.get(), createUpdateTableRequestBody);
if (isTableLocked(tableDto.get())) {
throw new UnsupportedClientOperationException(
UnsupportedClientOperationException.Operation.LOCKED_TABLE_OPERATION,
String.format(
"Table %s.%s is in locked state and cannot be replaced.", databaseId, tableId));
}
// Check if table creator has the privilege to replace the table.
authorizationUtils.checkReplaceTablePrivilege(tableDto.get(), tableCreatorUpdater);
} else if (tableDto.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
import com.linkedin.openhouse.internal.catalog.model.SoftDeletedTableDto;
import com.linkedin.openhouse.internal.catalog.model.SoftDeletedTablePrimaryKey;
import com.linkedin.openhouse.tables.api.spec.v0.request.CreateUpdateLockRequestBody;
import com.linkedin.openhouse.tables.api.spec.v0.request.CreateUpdateTableRequestBody;
import com.linkedin.openhouse.tables.api.spec.v0.request.UpdateAclPoliciesRequestBody;
import com.linkedin.openhouse.tables.api.spec.v0.request.components.LockState;
import com.linkedin.openhouse.tables.api.spec.v0.request.components.Policies;
import com.linkedin.openhouse.tables.api.spec.v0.request.components.TimePartitionSpec;
import com.linkedin.openhouse.tables.authorization.AuthorizationHandler;
import com.linkedin.openhouse.tables.authorization.Privileges;
Expand Down Expand Up @@ -817,6 +820,65 @@ public void testFailedOpsOnLockTable() {
tableDtoCopy.getDatabaseId(), TABLE_DTO.getTableId(), TEST_USER));
}

@Test
public void testStageReplaceBlockedOnLockedTable() {
TableDto tableDtoCopy =
TABLE_DTO
.toBuilder()
.tableProperties(ImmutableMap.of(CatalogConstants.RTAS_ENABLED_TABLE_PROP, "true"))
.policies(null)
.build();
TableDto created = verifyPutTableRequest(tableDtoCopy, null, true);
tablesService.createLock(
created.getDatabaseId(),
created.getTableId(),
CreateUpdateLockRequestBody.builder().locked(true).expirationInDays(4).build(),
TEST_USER);

CreateUpdateTableRequestBody stageReplaceRequestBody =
buildCreateUpdateTableRequestBody(created).toBuilder().stageReplace(true).build();

// Stage-replace (CREATE OR REPLACE TABLE) must be rejected while the table is locked, just
// like the ordinary update path already is.
Assertions.assertThrows(
UnsupportedClientOperationException.class,
() -> tablesService.putTable(stageReplaceRequestBody, TEST_USER, false));

tablesService.deleteLock(created.getDatabaseId(), created.getTableId(), TEST_USER);
Assertions.assertDoesNotThrow(
() -> tablesService.putTable(stageReplaceRequestBody, TEST_USER, false));

tablesService.deleteTable(created.getDatabaseId(), created.getTableId(), TEST_USER);
}

@Test
public void testStageReplaceCannotSmuggleLockStateChange() {
TableDto tableDtoCopy =
TABLE_DTO
.toBuilder()
.tableProperties(ImmutableMap.of(CatalogConstants.RTAS_ENABLED_TABLE_PROP, "true"))
.policies(
Policies.builder().lockState(LockState.builder().locked(false).build()).build())
.build();
TableDto created = verifyPutTableRequest(tableDtoCopy, null, true);

// Table is not locked. A stage-replace request that also tries to flip the lock state to
// locked=true in the same call must be rejected, just like the ordinary update path already
// rejects lock-state changes via checkIfLockPoliciesUpdated.
CreateUpdateTableRequestBody stageReplaceWithLockChangeRequestBody =
buildCreateUpdateTableRequestBody(created)
.toBuilder()
.stageReplace(true)
.policies(
Policies.builder().lockState(LockState.builder().locked(true).build()).build())
.build();
Assertions.assertThrows(
IllegalArgumentException.class,
() -> tablesService.putTable(stageReplaceWithLockChangeRequestBody, TEST_USER, false));

tablesService.deleteTable(created.getDatabaseId(), created.getTableId(), TEST_USER);
}

@Test
public void testRenameTable() {
TableDto putResultCreate = verifyPutTableRequest(TABLE_DTO, null, true);
Expand Down
Loading