morrySnow commented on code in PR #65100:
URL: https://github.com/apache/doris/pull/65100#discussion_r3510965500
##########
fe/fe-core/src/test/java/org/apache/doris/catalog/CreateFunctionTest.java:
##########
@@ -170,6 +176,229 @@ public void testCreatePythonFunctionRejectsObjectTypes()
throws Exception {
"ARRAY unsupported sub-type: bitmap");
}
+ @Test
+ public void testCreateFunctionRollbackOnlyFailedOverload() throws
Exception {
+ ConnectContext ctx = UtFrameUtils.createDefaultCtx();
+ createDatabase(ctx, "create database rollback_function_db;");
+ Database db =
Env.getCurrentInternalCatalog().getDbNullable("rollback_function_db");
+ Assert.assertNotNull(db);
+
+ EditLog editLog = Env.getCurrentEnv().getEditLog();
+ EditLog spyEditLog = Mockito.spy(editLog);
+
Mockito.doNothing().when(spyEditLog).logAddFunction(Mockito.any(Function.class));
+ Env.getCurrentEnv().setEditLog(spyEditLog);
+ try (MockedStatic<FunctionUtil> mockedFunctionUtil =
Mockito.mockStatic(FunctionUtil.class,
+ Mockito.CALLS_REAL_METHODS)) {
+ Function existingFunction = createJavaUdf("rollback_function_db",
"rollback_fn", Type.INT);
+ db.addFunction(existingFunction, false);
+ Assert.assertSame(existingFunction,
db.getFunction(searchDesc(existingFunction)));
+
+ Mockito.clearInvocations(spyEditLog);
+ Function failedFunction = createJavaUdf("rollback_function_db",
"rollback_fn", Type.BIGINT);
+ mockedFunctionUtil.when(() ->
FunctionUtil.translateToNereidsThrows(
+ "rollback_function_db", failedFunction)).thenThrow(new
RuntimeException("translate failed"));
+
+ RuntimeException exception =
Assert.assertThrows(RuntimeException.class,
+ () -> db.addFunction(failedFunction, false));
+ Assert.assertEquals("translate failed", exception.getMessage());
+ Mockito.verify(spyEditLog,
Mockito.never()).logAddFunction(Mockito.any(Function.class));
+ Assert.assertSame(existingFunction,
db.getFunction(searchDesc(existingFunction)));
+ Assert.assertThrows(AnalysisException.class, () ->
db.getFunction(searchDesc(failedFunction)));
+ } finally {
+ Env.getCurrentEnv().setEditLog(editLog);
+ }
+ }
+
+ @Test
+ public void testCreateTableFunctionRollbackWhenOuterFunctionFails() throws
Exception {
+ ConnectContext ctx = UtFrameUtils.createDefaultCtx();
+ createDatabase(ctx, "create database rollback_table_function_db;");
+ Database db =
Env.getCurrentInternalCatalog().getDbNullable("rollback_table_function_db");
+ Assert.assertNotNull(db);
+
+ EditLog editLog = Env.getCurrentEnv().getEditLog();
+ EditLog spyEditLog = Mockito.spy(editLog);
+
Mockito.doNothing().when(spyEditLog).logAddFunction(Mockito.any(Function.class));
+
Mockito.doNothing().when(spyEditLog).logAddFunctions(Mockito.anyList());
+ Env.getCurrentEnv().setEditLog(spyEditLog);
+ try (MockedStatic<FunctionUtil> mockedFunctionUtil =
Mockito.mockStatic(FunctionUtil.class,
+ Mockito.CALLS_REAL_METHODS)) {
+ Function tableFunction =
createJavaUdtf("rollback_table_function_db", "rollback_table_fn", Type.INT);
+ mockedFunctionUtil.when(() ->
FunctionUtil.translateToNereidsThrows(
+ Mockito.eq("rollback_table_function_db"),
Mockito.any(Function.class)))
+ .thenAnswer(invocation -> {
+ Function function = invocation.getArgument(1);
+ if
("rollback_table_fn_outer".equals(function.functionName())) {
+ throw new RuntimeException("outer translate
failed");
+ }
+ return true;
+ });
+
+ RuntimeException exception =
Assert.assertThrows(RuntimeException.class,
+ () -> db.addTableFunction(tableFunction, false));
+ Assert.assertEquals("outer translate failed",
exception.getMessage());
+ Mockito.verify(spyEditLog,
Mockito.never()).logAddFunction(Mockito.any(Function.class));
+ Mockito.verify(spyEditLog,
Mockito.never()).logAddFunctions(Mockito.anyList());
+ Assert.assertThrows(AnalysisException.class, () ->
db.getFunction(searchDesc(tableFunction)));
+ Assert.assertThrows(AnalysisException.class,
+ () ->
db.getFunction(searchDesc("rollback_table_function_db",
"rollback_table_fn_outer",
+ Type.INT)));
+ mockedFunctionUtil.verify(() ->
FunctionUtil.dropFromNereids(Mockito.eq("rollback_table_function_db"),
+ Mockito.argThat(function ->
"rollback_table_fn".equals(function.getName().getFunction()))));
+ mockedFunctionUtil.verify(() ->
FunctionUtil.dropFromNereids(Mockito.eq("rollback_table_function_db"),
+ Mockito.argThat(function ->
"rollback_table_fn_outer".equals(function.getName().getFunction()))));
+ } finally {
+ Env.getCurrentEnv().setEditLog(editLog);
+ }
+ }
+
+ @Test
+ public void testCreateTableFunctionRollbackWhenOuterFunctionConflicts()
throws Exception {
+ ConnectContext ctx = UtFrameUtils.createDefaultCtx();
+ createDatabase(ctx, "create database
rollback_table_function_conflict_db;");
+ Database db =
Env.getCurrentInternalCatalog().getDbNullable("rollback_table_function_conflict_db");
+ Assert.assertNotNull(db);
+
+ EditLog editLog = Env.getCurrentEnv().getEditLog();
+ EditLog spyEditLog = Mockito.spy(editLog);
+
Mockito.doNothing().when(spyEditLog).logAddFunction(Mockito.any(Function.class));
+
Mockito.doNothing().when(spyEditLog).logAddFunctions(Mockito.anyList());
+ Env.getCurrentEnv().setEditLog(spyEditLog);
+ try (MockedStatic<FunctionUtil> mockedFunctionUtil =
Mockito.mockStatic(FunctionUtil.class,
+ Mockito.CALLS_REAL_METHODS)) {
+ mockedFunctionUtil.when(() ->
FunctionUtil.translateToNereidsThrows(
+ Mockito.eq("rollback_table_function_conflict_db"),
Mockito.any(Function.class)))
+ .thenReturn(true);
+ Function existingOuterFunction = createJavaUdtf(
+ "rollback_table_function_conflict_db",
"rollback_table_conflict_fn_outer", Type.INT);
+ db.addFunction(existingOuterFunction, false);
+ Assert.assertSame(existingOuterFunction,
db.getFunction(searchDesc(existingOuterFunction)));
+
+ Mockito.clearInvocations(spyEditLog);
+ Function tableFunction = createJavaUdtf(
+ "rollback_table_function_conflict_db",
"rollback_table_conflict_fn", Type.INT);
+ UserException exception = Assert.assertThrows(UserException.class,
+ () -> db.addTableFunction(tableFunction, true));
+ Assert.assertEquals("function already exists",
exception.getDetailMessage());
+ Mockito.verify(spyEditLog,
Mockito.never()).logAddFunction(Mockito.any(Function.class));
+ Mockito.verify(spyEditLog,
Mockito.never()).logAddFunctions(Mockito.anyList());
+ Assert.assertThrows(AnalysisException.class, () ->
db.getFunction(searchDesc(tableFunction)));
+ Assert.assertSame(existingOuterFunction,
db.getFunction(searchDesc(existingOuterFunction)));
+ } finally {
+ Env.getCurrentEnv().setEditLog(editLog);
+ }
+ }
+
+ @Test
+ public void testCreateTableFunctionIfNotExistsSkipsExistingPair() throws
Exception {
+ ConnectContext ctx = UtFrameUtils.createDefaultCtx();
+ createDatabase(ctx, "create database
existing_table_function_pair_db;");
+ Database db =
Env.getCurrentInternalCatalog().getDbNullable("existing_table_function_pair_db");
+ Assert.assertNotNull(db);
+
+ EditLog editLog = Env.getCurrentEnv().getEditLog();
+ EditLog spyEditLog = Mockito.spy(editLog);
+
Mockito.doNothing().when(spyEditLog).logAddFunction(Mockito.any(Function.class));
+
Mockito.doNothing().when(spyEditLog).logAddFunctions(Mockito.anyList());
+ Env.getCurrentEnv().setEditLog(spyEditLog);
+ try (MockedStatic<FunctionUtil> mockedFunctionUtil =
Mockito.mockStatic(FunctionUtil.class,
+ Mockito.CALLS_REAL_METHODS)) {
+ mockedFunctionUtil.when(() ->
FunctionUtil.translateToNereidsThrows(
+ Mockito.eq("existing_table_function_pair_db"),
Mockito.any(Function.class)))
+ .thenReturn(true);
+ Function existingFunction = createJavaUdtf(
+ "existing_table_function_pair_db",
"existing_table_pair_fn", Type.INT);
+ Function existingOuterFunction = createJavaUdtf(
+ "existing_table_function_pair_db",
"existing_table_pair_fn_outer", Type.INT);
+ db.addFunction(existingFunction, false);
+ db.addFunction(existingOuterFunction, false);
+ Assert.assertSame(existingFunction,
db.getFunction(searchDesc(existingFunction)));
+ Assert.assertSame(existingOuterFunction,
db.getFunction(searchDesc(existingOuterFunction)));
+
+ Mockito.clearInvocations(spyEditLog);
+ Function tableFunction = createJavaUdtf(
+ "existing_table_function_pair_db",
"existing_table_pair_fn", Type.INT);
+ db.addTableFunction(tableFunction, true);
+
+ Mockito.verify(spyEditLog,
Mockito.never()).logAddFunction(Mockito.any(Function.class));
+ Mockito.verify(spyEditLog,
Mockito.never()).logAddFunctions(Mockito.anyList());
+ Assert.assertSame(existingFunction,
db.getFunction(searchDesc(existingFunction)));
+ Assert.assertSame(existingOuterFunction,
db.getFunction(searchDesc(existingOuterFunction)));
+ } finally {
+ Env.getCurrentEnv().setEditLog(editLog);
+ }
+ }
+
+ @Test
+ public void testCreateTableFunctionLogsPairAtomically() throws Exception {
+ ConnectContext ctx = UtFrameUtils.createDefaultCtx();
+ createDatabase(ctx, "create database atomic_table_function_db;");
+ Database db =
Env.getCurrentInternalCatalog().getDbNullable("atomic_table_function_db");
+ Assert.assertNotNull(db);
+
+ EditLog editLog = Env.getCurrentEnv().getEditLog();
+ EditLog spyEditLog = Mockito.spy(editLog);
+
Mockito.doNothing().when(spyEditLog).logAddFunction(Mockito.any(Function.class));
+
Mockito.doNothing().when(spyEditLog).logAddFunctions(Mockito.anyList());
+ Env.getCurrentEnv().setEditLog(spyEditLog);
+ try (MockedStatic<FunctionUtil> mockedFunctionUtil =
Mockito.mockStatic(FunctionUtil.class,
+ Mockito.CALLS_REAL_METHODS)) {
+ mockedFunctionUtil.when(() ->
FunctionUtil.translateToNereidsThrows(
+ Mockito.eq("atomic_table_function_db"),
Mockito.any(Function.class)))
+ .thenReturn(true);
+ Function tableFunction =
createJavaUdtf("atomic_table_function_db", "atomic_table_fn", Type.INT);
+
+ db.addTableFunction(tableFunction, false);
+
+ Mockito.verify(spyEditLog,
Mockito.never()).logAddFunction(Mockito.any(Function.class));
+
Mockito.verify(spyEditLog).logAddFunctions(Mockito.argThat(functions ->
Review Comment:
Fixed in commit 8b7b3418b773. Added
CreateFunctionTest.testCreateTableFunctionJournalReplayRestoresPair. It builds
the base and _outer functions, writes a JournalEntity with
OP_ADD_FUNCTIONS/CreateFunctionInfo, reads it back through
JournalEntity.readFields(), verifies the replay payload contains both
functions, calls EditLog.loadJournal(), and asserts both functions are restored
in the catalog.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]