[Enhancement] Support for `IF NOT EXISTS` and `IF EXISTS` clauses to the GROUP PROVIDER (#63248)

What I'm doing:
This pull request adds support for IF NOT EXISTS and IF EXISTS clauses to the CREATE GROUP PROVIDER and DROP GROUP PROVIDER SQL statements, ensuring these operations are idempotent and error-tolerant. It also introduces comprehensive unit tests to verify the correct behavior of these clauses, and updates the parser and semantic analyzer accordingly.

Enhancements to Group Provider DDL Statements:

Added support for IF NOT EXISTS to CREATE GROUP PROVIDER and IF EXISTS to DROP GROUP PROVIDER, allowing these statements to succeed silently if the provider already exists or does not exist, respectively. The implementation includes proper error handling and DDL exception messages for cases where these clauses are not specified. [1] [2]

Updated the SQL parser (AstBuilder) to recognize and propagate the IF NOT EXISTS and IF EXISTS flags in CreateGroupProviderStmt and DropGroupProviderStmt objects.

Enhanced the semantic analyzer to respect the new flags, throwing exceptions only when appropriate (i.e., when the flags are not set and the provider's existence does not match the operation). [1] [2]

Testing Improvements:

Added a new test class GroupProviderStatementTest with comprehensive unit tests covering all scenarios for the new IF NOT EXISTS and IF EXISTS functionality, including creation, dropping, legacy constructors, and sequential operations.
Error Handling:

Wrapped the execution of dropGroupProviderStatement in the DDL statement executor with error reporting to ensure runtime exceptions are properly managed.
This commit is contained in:
Harbor Liu 2025-09-19 14:01:30 +08:00 committed by GitHub
parent cfe33249d4
commit 348cd8f151
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 982 additions and 20 deletions

View File

@ -611,6 +611,16 @@ public class AuthenticationMgr {
// ---------------------------------------- Group Provider Statement --------------------------------------
public void createGroupProviderStatement(CreateGroupProviderStmt stmt, ConnectContext context) throws DdlException {
// Check if group provider already exists
if (this.nameToGroupProviderMap.containsKey(stmt.getName())) {
if (stmt.isIfNotExists()) {
// If IF NOT EXISTS is specified, silently return without error
return;
} else {
throw new DdlException("Group provider '" + stmt.getName() + "' already exists");
}
}
GroupProvider groupProvider = GroupProviderFactory.createGroupProvider(stmt.getName(), stmt.getPropertyMap());
groupProvider.init();
this.nameToGroupProviderMap.put(stmt.getName(), groupProvider);
@ -629,8 +639,18 @@ public class AuthenticationMgr {
}
}
public void dropGroupProviderStatement(DropGroupProviderStmt stmt, ConnectContext context) {
GroupProvider groupProvider = this.nameToGroupProviderMap.remove(stmt.getName());
public void dropGroupProviderStatement(DropGroupProviderStmt stmt, ConnectContext context) throws DdlException {
GroupProvider groupProvider = this.nameToGroupProviderMap.get(stmt.getName());
if (groupProvider == null) {
if (stmt.isIfExists()) {
// If IF EXISTS is specified, silently return without error
return;
} else {
throw new DdlException("Group provider '" + stmt.getName() + "' does not exist");
}
}
this.nameToGroupProviderMap.remove(stmt.getName());
groupProvider.destroy();
GlobalStateMgr.getCurrentState().getEditLog().logJsonObject(OperationType.OP_DROP_GROUP_PROVIDER,

View File

@ -657,8 +657,10 @@ public class DDLStmtExecutor {
@Override
public ShowResultSet visitDropGroupProviderStatement(DropGroupProviderStmt statement, ConnectContext context) {
AuthenticationMgr authenticationMgr = GlobalStateMgr.getCurrentState().getAuthenticationMgr();
authenticationMgr.dropGroupProviderStatement(statement, context);
ErrorReport.wrapWithRuntimeException(() -> {
AuthenticationMgr authenticationMgr = GlobalStateMgr.getCurrentState().getAuthenticationMgr();
authenticationMgr.dropGroupProviderStatement(statement, context);
});
return null;
}

View File

@ -53,7 +53,7 @@ public class GroupProviderStatementAnalyzer {
groupProvider.checkProperty();
AuthenticationMgr authenticationMgr = GlobalStateMgr.getCurrentState().getAuthenticationMgr();
if (authenticationMgr.getGroupProvider(statement.getName()) != null) {
if (authenticationMgr.getGroupProvider(statement.getName()) != null && !statement.isIfNotExists()) {
throw new SemanticException("Group Provider '" + statement.getName() + "' already exists");
}
return null;
@ -62,7 +62,7 @@ public class GroupProviderStatementAnalyzer {
@Override
public Void visitDropGroupProviderStatement(DropGroupProviderStmt statement, ConnectContext context) {
AuthenticationMgr authenticationMgr = GlobalStateMgr.getCurrentState().getAuthenticationMgr();
if (authenticationMgr.getGroupProvider(statement.getName()) == null) {
if (authenticationMgr.getGroupProvider(statement.getName()) == null && !statement.isIfExists()) {
throw new SemanticException("Group Provider '" + statement.getName() + "' not found");
}

View File

@ -7186,14 +7186,14 @@ public class AstBuilder extends com.starrocks.sql.parser.StarRocksBaseVisitor<Pa
propertyMap.put(property.getKey(), property.getValue());
}
}
return new CreateGroupProviderStmt(name, propertyMap, createPos(context));
return new CreateGroupProviderStmt(name, propertyMap, context.IF() != null, createPos(context));
}
@Override
public ParseNode visitDropGroupProviderStatement(
com.starrocks.sql.parser.StarRocksParser.DropGroupProviderStatementContext context) {
String name = ((Identifier) visit(context.identifier())).getValue();
return new DropGroupProviderStmt(name, createPos(context));
return new DropGroupProviderStmt(name, context.IF() != null, createPos(context));
}
@Override

View File

@ -0,0 +1,351 @@
// Copyright 2021-present StarRocks, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.starrocks.authentication;
import com.starrocks.catalog.UserIdentity;
import com.starrocks.common.DdlException;
import com.starrocks.persist.EditLog;
import com.starrocks.qe.ConnectContext;
import com.starrocks.server.GlobalStateMgr;
import com.starrocks.sql.ast.group.CreateGroupProviderStmt;
import com.starrocks.sql.ast.group.DropGroupProviderStmt;
import com.starrocks.sql.parser.NodePosition;
import com.starrocks.utframe.UtFrameUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyShort;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
/**
* Unit tests for Group Provider statements with IF NOT EXISTS and IF EXISTS functionality
*/
public class GroupProviderStatementTest {
private ConnectContext ctx;
private AuthenticationMgr authenticationMgr;
private static final String TEST_PROVIDER_NAME = "test_provider";
private static final String NON_EXISTENT_PROVIDER_NAME = "non_existent_provider";
@BeforeEach
public void setUp() throws Exception {
// Mock EditLog
EditLog editLog = spy(new EditLog(null));
doNothing().when(editLog).logEdit(anyShort(), any());
GlobalStateMgr.getCurrentState().setEditLog(editLog);
ctx = UtFrameUtils.initCtxForNewPrivilege(UserIdentity.ROOT);
authenticationMgr = GlobalStateMgr.getCurrentState().getAuthenticationMgr();
// Clean up any existing test providers
cleanupTestProviders();
}
@AfterEach
public void tearDown() throws Exception {
cleanupTestProviders();
}
/**
* Test case: Create Group Provider with IF NOT EXISTS when provider does not exist
* Test point: Should successfully create the provider without error
*/
@Test
public void testCreateGroupProviderIfNotExistsWhenNotExists() throws Exception {
Map<String, String> properties = createUnixGroupProviderProperties();
CreateGroupProviderStmt stmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, true, NodePosition.ZERO);
// Should not throw exception
authenticationMgr.createGroupProviderStatement(stmt, ctx);
// Verify provider was created
GroupProvider provider = authenticationMgr.getGroupProvider(TEST_PROVIDER_NAME);
Assertions.assertNotNull(provider, "Group provider should be created successfully");
Assertions.assertEquals(TEST_PROVIDER_NAME, provider.getName());
Assertions.assertEquals("unix", provider.getType());
}
/**
* Test case: Create Group Provider with IF NOT EXISTS when provider already exists
* Test point: Should silently return without error and not modify existing provider
*/
@Test
public void testCreateGroupProviderIfNotExistsWhenExists() throws Exception {
Map<String, String> properties = createUnixGroupProviderProperties();
// First create the provider
CreateGroupProviderStmt firstStmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
authenticationMgr.createGroupProviderStatement(firstStmt, ctx);
// Get the original provider for comparison
GroupProvider originalProvider = authenticationMgr.getGroupProvider(TEST_PROVIDER_NAME);
Assertions.assertNotNull(originalProvider, "Original provider should exist");
// Try to create again with IF NOT EXISTS
CreateGroupProviderStmt secondStmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, true, NodePosition.ZERO);
// Should not throw exception
authenticationMgr.createGroupProviderStatement(secondStmt, ctx);
// Verify provider still exists and is unchanged
GroupProvider provider = authenticationMgr.getGroupProvider(TEST_PROVIDER_NAME);
Assertions.assertNotNull(provider, "Group provider should still exist");
Assertions.assertEquals(originalProvider.getName(), provider.getName());
Assertions.assertEquals(originalProvider.getType(), provider.getType());
}
/**
* Test case: Create Group Provider without IF NOT EXISTS when provider already exists
* Test point: Should throw DdlException with appropriate error message
*/
@Test
public void testCreateGroupProviderWithoutIfNotExistsWhenExists() throws Exception {
Map<String, String> properties = createUnixGroupProviderProperties();
// First create the provider
CreateGroupProviderStmt firstStmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
authenticationMgr.createGroupProviderStatement(firstStmt, ctx);
// Try to create again without IF NOT EXISTS
CreateGroupProviderStmt secondStmt =
new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
// Should throw DdlException
DdlException exception = Assertions.assertThrows(DdlException.class, () -> {
authenticationMgr.createGroupProviderStatement(secondStmt, ctx);
});
Assertions.assertTrue(exception.getMessage().contains("Group provider '" + TEST_PROVIDER_NAME + "' already exists"),
"Error message should indicate provider already exists: " + exception.getMessage());
}
/**
* Test case: Drop Group Provider with IF EXISTS when provider does not exist
* Test point: Should silently return without error
*/
@Test
public void testDropGroupProviderIfExistsWhenNotExists() throws Exception {
DropGroupProviderStmt stmt = new DropGroupProviderStmt(NON_EXISTENT_PROVIDER_NAME, true, NodePosition.ZERO);
// Should not throw exception
authenticationMgr.dropGroupProviderStatement(stmt, ctx);
// Verify provider still does not exist
GroupProvider provider = authenticationMgr.getGroupProvider(NON_EXISTENT_PROVIDER_NAME);
Assertions.assertNull(provider, "Group provider should not exist");
}
/**
* Test case: Drop Group Provider with IF EXISTS when provider exists
* Test point: Should successfully drop the provider
*/
@Test
public void testDropGroupProviderIfExistsWhenExists() throws Exception {
Map<String, String> properties = createUnixGroupProviderProperties();
// First create the provider
CreateGroupProviderStmt createStmt =
new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
authenticationMgr.createGroupProviderStatement(createStmt, ctx);
// Verify provider exists
GroupProvider provider = authenticationMgr.getGroupProvider(TEST_PROVIDER_NAME);
Assertions.assertNotNull(provider, "Group provider should exist before drop");
// Drop with IF EXISTS
DropGroupProviderStmt dropStmt = new DropGroupProviderStmt(TEST_PROVIDER_NAME, true, NodePosition.ZERO);
// Should not throw exception
authenticationMgr.dropGroupProviderStatement(dropStmt, ctx);
// Verify provider was dropped
provider = authenticationMgr.getGroupProvider(TEST_PROVIDER_NAME);
Assertions.assertNull(provider, "Group provider should be dropped successfully");
}
/**
* Test case: Drop Group Provider without IF EXISTS when provider does not exist
* Test point: Should throw DdlException with appropriate error message
*/
@Test
public void testDropGroupProviderWithoutIfExistsWhenNotExists() throws Exception {
DropGroupProviderStmt stmt = new DropGroupProviderStmt(NON_EXISTENT_PROVIDER_NAME, false, NodePosition.ZERO);
// Should throw DdlException
DdlException exception = Assertions.assertThrows(DdlException.class, () -> {
authenticationMgr.dropGroupProviderStatement(stmt, ctx);
});
Assertions.assertTrue(
exception.getMessage().contains("Group provider '" + NON_EXISTENT_PROVIDER_NAME + "' does not exist"),
"Error message should indicate provider does not exist: " + exception.getMessage());
}
/**
* Test case: Test CreateGroupProviderStmt constructor and getters
* Test point: Verify proper initialization of statement properties
*/
@Test
public void testCreateGroupProviderStmtProperties() {
Map<String, String> properties = createUnixGroupProviderProperties();
CreateGroupProviderStmt stmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, true, NodePosition.ZERO);
Assertions.assertEquals(TEST_PROVIDER_NAME, stmt.getName(), "Provider name should match");
Assertions.assertEquals(properties, stmt.getPropertyMap(), "Properties should match");
Assertions.assertTrue(stmt.isIfNotExists(), "IF NOT EXISTS should be true");
}
/**
* Test case: Test DropGroupProviderStmt constructor and getters
* Test point: Verify proper initialization of statement properties
*/
@Test
public void testDropGroupProviderStmtProperties() {
DropGroupProviderStmt stmt = new DropGroupProviderStmt(TEST_PROVIDER_NAME, true, NodePosition.ZERO);
Assertions.assertEquals(TEST_PROVIDER_NAME, stmt.getName(), "Provider name should match");
Assertions.assertTrue(stmt.isIfExists(), "IF EXISTS should be true");
}
/**
* Test case: Test CreateGroupProviderStmt without IF NOT EXISTS
* Test point: Verify default behavior when IF NOT EXISTS is false
*/
@Test
public void testCreateGroupProviderStmtWithoutIfNotExists() {
Map<String, String> properties = createUnixGroupProviderProperties();
CreateGroupProviderStmt stmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
Assertions.assertEquals(TEST_PROVIDER_NAME, stmt.getName(), "Provider name should match");
Assertions.assertEquals(properties, stmt.getPropertyMap(), "Properties should match");
Assertions.assertFalse(stmt.isIfNotExists(), "IF NOT EXISTS should be false");
}
/**
* Test case: Test DropGroupProviderStmt without IF EXISTS
* Test point: Verify default behavior when IF EXISTS is false
*/
@Test
public void testDropGroupProviderStmtWithoutIfExists() {
DropGroupProviderStmt stmt = new DropGroupProviderStmt(TEST_PROVIDER_NAME, false, NodePosition.ZERO);
Assertions.assertEquals(TEST_PROVIDER_NAME, stmt.getName(), "Provider name should match");
Assertions.assertFalse(stmt.isIfExists(), "IF EXISTS should be false");
}
/**
* Test case: Test CreateGroupProviderStmt with legacy constructor
* Test point: Verify backward compatibility with existing constructor
*/
@Test
public void testCreateGroupProviderStmtLegacyConstructor() {
Map<String, String> properties = createUnixGroupProviderProperties();
CreateGroupProviderStmt stmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, NodePosition.ZERO);
Assertions.assertEquals(TEST_PROVIDER_NAME, stmt.getName(), "Provider name should match");
Assertions.assertEquals(properties, stmt.getPropertyMap(), "Properties should match");
Assertions.assertFalse(stmt.isIfNotExists(), "IF NOT EXISTS should default to false");
}
/**
* Test case: Test DropGroupProviderStmt with legacy constructor
* Test point: Verify backward compatibility with existing constructor
*/
@Test
public void testDropGroupProviderStmtLegacyConstructor() {
DropGroupProviderStmt stmt = new DropGroupProviderStmt(TEST_PROVIDER_NAME, NodePosition.ZERO);
Assertions.assertEquals(TEST_PROVIDER_NAME, stmt.getName(), "Provider name should match");
Assertions.assertFalse(stmt.isIfExists(), "IF EXISTS should default to false");
}
/**
* Test case: Test multiple Group Provider operations in sequence
* Test point: Verify proper handling of multiple create/drop operations
*/
@Test
public void testMultipleGroupProviderOperations() throws Exception {
Map<String, String> properties = createUnixGroupProviderProperties();
// Create provider
CreateGroupProviderStmt createStmt =
new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
authenticationMgr.createGroupProviderStatement(createStmt, ctx);
// Verify exists
GroupProvider provider = authenticationMgr.getGroupProvider(TEST_PROVIDER_NAME);
Assertions.assertNotNull(provider, "Provider should exist after creation");
// Try to create again with IF NOT EXISTS (should not error)
CreateGroupProviderStmt createAgainStmt =
new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, true, NodePosition.ZERO);
authenticationMgr.createGroupProviderStatement(createAgainStmt, ctx);
// Verify still exists
provider = authenticationMgr.getGroupProvider(TEST_PROVIDER_NAME);
Assertions.assertNotNull(provider, "Provider should still exist after IF NOT EXISTS create");
// Drop with IF EXISTS
DropGroupProviderStmt dropStmt = new DropGroupProviderStmt(TEST_PROVIDER_NAME, true, NodePosition.ZERO);
authenticationMgr.dropGroupProviderStatement(dropStmt, ctx);
// Verify dropped
provider = authenticationMgr.getGroupProvider(TEST_PROVIDER_NAME);
Assertions.assertNull(provider, "Provider should be dropped");
// Try to drop again with IF EXISTS (should not error)
DropGroupProviderStmt dropAgainStmt = new DropGroupProviderStmt(TEST_PROVIDER_NAME, true, NodePosition.ZERO);
authenticationMgr.dropGroupProviderStatement(dropAgainStmt, ctx);
// Verify still does not exist
provider = authenticationMgr.getGroupProvider(TEST_PROVIDER_NAME);
Assertions.assertNull(provider, "Provider should still not exist after IF EXISTS drop");
}
/**
* Helper method to create Unix Group Provider properties
*/
private Map<String, String> createUnixGroupProviderProperties() {
Map<String, String> properties = new HashMap<>();
properties.put("type", "unix");
return properties;
}
/**
* Helper method to clean up test providers
*/
private void cleanupTestProviders() {
try {
// Try to drop test providers if they exist
DropGroupProviderStmt dropStmt = new DropGroupProviderStmt(TEST_PROVIDER_NAME, true, NodePosition.ZERO);
authenticationMgr.dropGroupProviderStatement(dropStmt, ctx);
} catch (Exception e) {
// Ignore cleanup errors
}
try {
DropGroupProviderStmt dropStmt = new DropGroupProviderStmt(NON_EXISTENT_PROVIDER_NAME, true, NodePosition.ZERO);
authenticationMgr.dropGroupProviderStatement(dropStmt, ctx);
} catch (Exception e) {
// Ignore cleanup errors
}
}
}

View File

@ -0,0 +1,318 @@
// Copyright 2021-present StarRocks, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.starrocks.sql.analyzer;
import com.starrocks.authentication.AuthenticationMgr;
import com.starrocks.authentication.GroupProvider;
import com.starrocks.catalog.UserIdentity;
import com.starrocks.qe.ConnectContext;
import com.starrocks.server.GlobalStateMgr;
import com.starrocks.sql.ast.group.CreateGroupProviderStmt;
import com.starrocks.sql.ast.group.DropGroupProviderStmt;
import com.starrocks.sql.parser.NodePosition;
import com.starrocks.utframe.UtFrameUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.Mockito.when;
/**
* Unit tests for GroupProviderStatementAnalyzer with IF NOT EXISTS and IF EXISTS functionality
*/
public class GroupProviderStatementAnalyzerTest {
private ConnectContext ctx;
private AuthenticationMgr authenticationMgr;
private static final String TEST_PROVIDER_NAME = "test_provider";
private static final String NON_EXISTENT_PROVIDER_NAME = "non_existent_provider";
@Mock
private GroupProvider mockGroupProvider;
@BeforeEach
public void setUp() throws Exception {
MockitoAnnotations.openMocks(this);
UtFrameUtils.setUpForPersistTest();
ctx = UtFrameUtils.initCtxForNewPrivilege(UserIdentity.ROOT);
authenticationMgr = GlobalStateMgr.getCurrentState().getAuthenticationMgr();
// Setup mock group provider
when(mockGroupProvider.getName()).thenReturn(TEST_PROVIDER_NAME);
when(mockGroupProvider.getType()).thenReturn("unix");
// Mock checkProperty method (void method)
// when(mockGroupProvider.checkProperty()).thenReturn(null);
// Clean up any existing test providers
cleanupTestProviders();
}
@AfterEach
public void tearDown() throws Exception {
cleanupTestProviders();
UtFrameUtils.tearDownForPersisTest();
}
/**
* Test case: Analyze Create Group Provider with IF NOT EXISTS when provider does not exist
* Test point: Should pass analysis without error
*/
@Test
public void testAnalyzeCreateGroupProviderIfNotExistsWhenNotExists() throws Exception {
Map<String, String> properties = createUnixGroupProviderProperties();
CreateGroupProviderStmt stmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, true, NodePosition.ZERO);
// Should not throw exception
GroupProviderStatementAnalyzer.analyze(stmt, ctx);
}
/**
* Test case: Analyze Create Group Provider with IF NOT EXISTS when provider already exists
* Test point: Should pass analysis without error due to IF NOT EXISTS
*/
@Test
public void testAnalyzeCreateGroupProviderIfNotExistsWhenExists() throws Exception {
Map<String, String> properties = createUnixGroupProviderProperties();
// First create the provider
CreateGroupProviderStmt firstStmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
authenticationMgr.createGroupProviderStatement(firstStmt, ctx);
// Analyze create again with IF NOT EXISTS
CreateGroupProviderStmt secondStmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, true, NodePosition.ZERO);
// Should not throw exception
GroupProviderStatementAnalyzer.analyze(secondStmt, ctx);
}
/**
* Test case: Analyze Create Group Provider without IF NOT EXISTS when provider already exists
* Test point: Should throw SemanticException with appropriate error message
*/
@Test
public void testAnalyzeCreateGroupProviderWithoutIfNotExistsWhenExists() throws Exception {
Map<String, String> properties = createUnixGroupProviderProperties();
// First create the provider
CreateGroupProviderStmt firstStmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
authenticationMgr.createGroupProviderStatement(firstStmt, ctx);
// Analyze create again without IF NOT EXISTS
CreateGroupProviderStmt secondStmt =
new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
// Should throw SemanticException
SemanticException exception = Assertions.assertThrows(SemanticException.class, () -> {
GroupProviderStatementAnalyzer.analyze(secondStmt, ctx);
});
Assertions.assertTrue(exception.getMessage().contains("Group Provider '" + TEST_PROVIDER_NAME + "' already exists"),
"Error message should indicate provider already exists: " + exception.getMessage());
}
/**
* Test case: Analyze Drop Group Provider with IF EXISTS when provider does not exist
* Test point: Should pass analysis without error
*/
@Test
public void testAnalyzeDropGroupProviderIfExistsWhenNotExists() throws Exception {
DropGroupProviderStmt stmt = new DropGroupProviderStmt(NON_EXISTENT_PROVIDER_NAME, true, NodePosition.ZERO);
// Should not throw exception
GroupProviderStatementAnalyzer.analyze(stmt, ctx);
}
/**
* Test case: Analyze Drop Group Provider with IF EXISTS when provider exists
* Test point: Should pass analysis without error
*/
@Test
public void testAnalyzeDropGroupProviderIfExistsWhenExists() throws Exception {
Map<String, String> properties = createUnixGroupProviderProperties();
// First create the provider
CreateGroupProviderStmt createStmt =
new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
authenticationMgr.createGroupProviderStatement(createStmt, ctx);
// Analyze drop with IF EXISTS
DropGroupProviderStmt dropStmt = new DropGroupProviderStmt(TEST_PROVIDER_NAME, true, NodePosition.ZERO);
// Should not throw exception
GroupProviderStatementAnalyzer.analyze(dropStmt, ctx);
}
/**
* Test case: Analyze Drop Group Provider without IF EXISTS when provider does not exist
* Test point: Should throw SemanticException with appropriate error message
*/
@Test
public void testAnalyzeDropGroupProviderWithoutIfExistsWhenNotExists() throws Exception {
DropGroupProviderStmt stmt = new DropGroupProviderStmt(NON_EXISTENT_PROVIDER_NAME, false, NodePosition.ZERO);
// Should throw SemanticException
SemanticException exception = Assertions.assertThrows(SemanticException.class, () -> {
GroupProviderStatementAnalyzer.analyze(stmt, ctx);
});
Assertions.assertTrue(exception.getMessage().contains("Group Provider '" + NON_EXISTENT_PROVIDER_NAME + "' not found"),
"Error message should indicate provider not found: " + exception.getMessage());
}
/**
* Test case: Analyze Create Group Provider with missing type property
* Test point: Should throw SemanticException for missing required property
*/
@Test
public void testAnalyzeCreateGroupProviderMissingType() throws Exception {
Map<String, String> properties = new HashMap<>();
// Missing "type" property
CreateGroupProviderStmt stmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
// Should throw SemanticException
SemanticException exception = Assertions.assertThrows(SemanticException.class, () -> {
GroupProviderStatementAnalyzer.analyze(stmt, ctx);
});
Assertions.assertTrue(exception.getMessage().contains("missing required property: type"),
"Error message should indicate missing type property: " + exception.getMessage());
}
/**
* Test case: Analyze Create Group Provider with unsupported type
* Test point: Should throw SemanticException for unsupported group provider type
*/
@Test
public void testAnalyzeCreateGroupProviderUnsupportedType() throws Exception {
Map<String, String> properties = new HashMap<>();
properties.put("type", "unsupported_type");
CreateGroupProviderStmt stmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
// Should throw SemanticException
SemanticException exception = Assertions.assertThrows(SemanticException.class, () -> {
GroupProviderStatementAnalyzer.analyze(stmt, ctx);
});
Assertions.assertTrue(exception.getMessage().contains("unsupported group provider type"),
"Error message should indicate unsupported type: " + exception.getMessage());
}
/**
* Test case: Analyze Create Group Provider with valid Unix type
* Test point: Should pass analysis for valid Unix group provider
*/
@Test
public void testAnalyzeCreateGroupProviderValidUnixType() throws Exception {
Map<String, String> properties = createUnixGroupProviderProperties();
CreateGroupProviderStmt stmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
// Should not throw exception
GroupProviderStatementAnalyzer.analyze(stmt, ctx);
}
/**
* Test case: Analyze Create Group Provider with valid File type
* Test point: Should pass analysis for valid File group provider
*/
@Test
public void testAnalyzeCreateGroupProviderValidFileType() throws Exception {
Map<String, String> properties = new HashMap<>();
properties.put("type", "file");
properties.put("group_file_url", "test_file");
CreateGroupProviderStmt stmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
// Should not throw exception
GroupProviderStatementAnalyzer.analyze(stmt, ctx);
}
/**
* Test case: Analyze Create Group Provider with valid LDAP type
* Test point: Should pass analysis for valid LDAP group provider
*/
@Test
public void testAnalyzeCreateGroupProviderValidLDAPType() throws Exception {
Map<String, String> properties = new HashMap<>();
properties.put("type", "ldap");
properties.put("ldap_conn_url", "ldap://localhost:389");
properties.put("ldap_bind_root_dn", "cn=admin,dc=example,dc=com");
properties.put("ldap_bind_root_pwd", "password");
properties.put("ldap_bind_base_dn", "dc=example,dc=com");
properties.put("ldap_group_dn", "ou=groups,dc=example,dc=com");
CreateGroupProviderStmt stmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
// Should not throw exception
GroupProviderStatementAnalyzer.analyze(stmt, ctx);
}
/**
* Test case: Test analysis with null context
* Test point: Should handle null context gracefully
*/
@Test
public void testAnalyzeWithNullContext() throws Exception {
Map<String, String> properties = createUnixGroupProviderProperties();
CreateGroupProviderStmt stmt = new CreateGroupProviderStmt(TEST_PROVIDER_NAME, properties, false, NodePosition.ZERO);
// Should not throw exception even with null context
GroupProviderStatementAnalyzer.analyze(stmt, null);
}
/**
* Test case: Test analysis with empty provider name
* Test point: Should handle empty provider name appropriately
*/
@Test
public void testAnalyzeWithEmptyProviderName() throws Exception {
Map<String, String> properties = createUnixGroupProviderProperties();
CreateGroupProviderStmt stmt = new CreateGroupProviderStmt("", properties, false, NodePosition.ZERO);
// Should not throw exception during analysis (name validation happens elsewhere)
GroupProviderStatementAnalyzer.analyze(stmt, ctx);
}
/**
* Helper method to create Unix Group Provider properties
*/
private Map<String, String> createUnixGroupProviderProperties() {
Map<String, String> properties = new HashMap<>();
properties.put("type", "unix");
return properties;
}
/**
* Helper method to clean up test providers
*/
private void cleanupTestProviders() {
try {
// Try to drop test providers if they exist
DropGroupProviderStmt dropStmt = new DropGroupProviderStmt(TEST_PROVIDER_NAME, true, NodePosition.ZERO);
authenticationMgr.dropGroupProviderStatement(dropStmt, ctx);
} catch (Exception e) {
// Ignore cleanup errors
}
try {
DropGroupProviderStmt dropStmt = new DropGroupProviderStmt(NON_EXISTENT_PROVIDER_NAME, true, NodePosition.ZERO);
authenticationMgr.dropGroupProviderStatement(dropStmt, ctx);
} catch (Exception e) {
// Ignore cleanup errors
}
}
}

View File

@ -0,0 +1,251 @@
// Copyright 2021-present StarRocks, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.starrocks.sql.parser;
import com.starrocks.catalog.UserIdentity;
import com.starrocks.qe.ConnectContext;
import com.starrocks.sql.ast.group.CreateGroupProviderStmt;
import com.starrocks.sql.ast.group.DropGroupProviderStmt;
import com.starrocks.sql.parser.SqlParser;
import com.starrocks.utframe.UtFrameUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Map;
/**
* Unit tests for AstBuilder Group Provider parsing with IF NOT EXISTS and IF EXISTS functionality
*/
public class GroupProviderAstBuilderTest {
private ConnectContext ctx;
@BeforeEach
public void setUp() throws Exception {
ctx = UtFrameUtils.initCtxForNewPrivilege(UserIdentity.ROOT);
}
@AfterEach
public void tearDown() throws Exception {
}
/**
* Test case: Parse CREATE GROUP PROVIDER without IF NOT EXISTS
* Test point: Should create statement with ifNotExists = false
*/
@Test
public void testParseCreateGroupProviderWithoutIfNotExists() throws Exception {
String sql = "CREATE GROUP PROVIDER test_provider PROPERTIES(\"type\" = \"unix\")";
CreateGroupProviderStmt stmt =
(CreateGroupProviderStmt) SqlParser.parseSingleStatement(sql, ctx.getSessionVariable().getSqlMode());
Assertions.assertNotNull(stmt, "Statement should not be null");
Assertions.assertEquals("test_provider", stmt.getName(), "Provider name should match");
Assertions.assertFalse(stmt.isIfNotExists(), "IF NOT EXISTS should be false");
Map<String, String> properties = stmt.getPropertyMap();
Assertions.assertNotNull(properties, "Properties should not be null");
Assertions.assertEquals("unix", properties.get("type"), "Type should be unix");
}
/**
* Test case: Parse CREATE GROUP PROVIDER with IF NOT EXISTS
* Test point: Should create statement with ifNotExists = true
*/
@Test
public void testParseCreateGroupProviderWithIfNotExists() throws Exception {
String sql = "CREATE GROUP PROVIDER IF NOT EXISTS test_provider PROPERTIES(\"type\" = \"unix\")";
CreateGroupProviderStmt stmt =
(CreateGroupProviderStmt) SqlParser.parseSingleStatement(sql, ctx.getSessionVariable().getSqlMode());
Assertions.assertNotNull(stmt, "Statement should not be null");
Assertions.assertEquals("test_provider", stmt.getName(), "Provider name should match");
Assertions.assertTrue(stmt.isIfNotExists(), "IF NOT EXISTS should be true");
Map<String, String> properties = stmt.getPropertyMap();
Assertions.assertNotNull(properties, "Properties should not be null");
Assertions.assertEquals("unix", properties.get("type"), "Type should be unix");
}
/**
* Test case: Parse DROP GROUP PROVIDER without IF EXISTS
* Test point: Should create statement with ifExists = false
*/
@Test
public void testParseDropGroupProviderWithoutIfExists() throws Exception {
String sql = "DROP GROUP PROVIDER test_provider";
DropGroupProviderStmt stmt =
(DropGroupProviderStmt) SqlParser.parseSingleStatement(sql, ctx.getSessionVariable().getSqlMode());
Assertions.assertNotNull(stmt, "Statement should not be null");
Assertions.assertEquals("test_provider", stmt.getName(), "Provider name should match");
Assertions.assertFalse(stmt.isIfExists(), "IF EXISTS should be false");
}
/**
* Test case: Parse DROP GROUP PROVIDER with IF EXISTS
* Test point: Should create statement with ifExists = true
*/
@Test
public void testParseDropGroupProviderWithIfExists() throws Exception {
String sql = "DROP GROUP PROVIDER IF EXISTS test_provider";
DropGroupProviderStmt stmt =
(DropGroupProviderStmt) SqlParser.parseSingleStatement(sql, ctx.getSessionVariable().getSqlMode());
Assertions.assertNotNull(stmt, "Statement should not be null");
Assertions.assertEquals("test_provider", stmt.getName(), "Provider name should match");
Assertions.assertTrue(stmt.isIfExists(), "IF EXISTS should be true");
}
/**
* Test case: Parse CREATE GROUP PROVIDER with File type
* Test point: Should correctly parse File group provider properties
*/
@Test
public void testParseCreateGroupProviderFileType() throws Exception {
String sql = "CREATE GROUP PROVIDER IF NOT EXISTS file_provider PROPERTIES(\"type\" = \"file\"," +
" \"file_url\" = \"/path/to/file\")";
CreateGroupProviderStmt stmt =
(CreateGroupProviderStmt) SqlParser.parseSingleStatement(sql, ctx.getSessionVariable().getSqlMode());
Assertions.assertNotNull(stmt, "Statement should not be null");
Assertions.assertEquals("file_provider", stmt.getName(), "Provider name should match");
Assertions.assertTrue(stmt.isIfNotExists(), "IF NOT EXISTS should be true");
Map<String, String> properties = stmt.getPropertyMap();
Assertions.assertNotNull(properties, "Properties should not be null");
Assertions.assertEquals("file", properties.get("type"), "Type should be file");
Assertions.assertEquals("/path/to/file", properties.get("file_url"), "File URL should match");
}
/**
* Test case: Parse CREATE GROUP PROVIDER with LDAP type
* Test point: Should correctly parse LDAP group provider properties
*/
@Test
public void testParseCreateGroupProviderLDAPType() throws Exception {
String sql = "CREATE GROUP PROVIDER IF NOT EXISTS ldap_provider PROPERTIES(" +
"\"type\" = \"ldap\", " +
"\"ldap_conn_url\" = \"ldap://localhost:389\", " +
"\"ldap_bind_root_dn\" = \"cn=admin,dc=example,dc=com\", " +
"\"ldap_bind_root_pwd\" = \"password\", " +
"\"ldap_bind_base_dn\" = \"dc=example,dc=com\")";
CreateGroupProviderStmt stmt =
(CreateGroupProviderStmt) SqlParser.parseSingleStatement(sql, ctx.getSessionVariable().getSqlMode());
Assertions.assertNotNull(stmt, "Statement should not be null");
Assertions.assertEquals("ldap_provider", stmt.getName(), "Provider name should match");
Assertions.assertTrue(stmt.isIfNotExists(), "IF NOT EXISTS should be true");
Map<String, String> properties = stmt.getPropertyMap();
Assertions.assertNotNull(properties, "Properties should not be null");
Assertions.assertEquals("ldap", properties.get("type"), "Type should be ldap");
Assertions.assertEquals("ldap://localhost:389", properties.get("ldap_conn_url"), "LDAP URL should match");
Assertions.assertEquals("cn=admin,dc=example,dc=com", properties.get("ldap_bind_root_dn"), "Root DN should match");
Assertions.assertEquals("password", properties.get("ldap_bind_root_pwd"), "Password should match");
Assertions.assertEquals("dc=example,dc=com", properties.get("ldap_bind_base_dn"), "Base DN should match");
}
/**
* Test case: Parse CREATE GROUP PROVIDER with quoted identifier
* Test point: Should correctly handle quoted provider names
*/
@Test
public void testParseCreateGroupProviderWithQuotedIdentifier() throws Exception {
String sql = "CREATE GROUP PROVIDER IF NOT EXISTS `test-provider` PROPERTIES(\"type\" = \"unix\")";
CreateGroupProviderStmt stmt =
(CreateGroupProviderStmt) SqlParser.parseSingleStatement(sql, ctx.getSessionVariable().getSqlMode());
Assertions.assertNotNull(stmt, "Statement should not be null");
Assertions.assertEquals("test-provider", stmt.getName(), "Provider name should match");
Assertions.assertTrue(stmt.isIfNotExists(), "IF NOT EXISTS should be true");
}
/**
* Test case: Parse DROP GROUP PROVIDER with quoted identifier
* Test point: Should correctly handle quoted provider names
*/
@Test
public void testParseDropGroupProviderWithQuotedIdentifier() throws Exception {
String sql = "DROP GROUP PROVIDER IF EXISTS `test-provider`";
DropGroupProviderStmt stmt =
(DropGroupProviderStmt) SqlParser.parseSingleStatement(sql, ctx.getSessionVariable().getSqlMode());
Assertions.assertNotNull(stmt, "Statement should not be null");
Assertions.assertEquals("test-provider", stmt.getName(), "Provider name should match");
Assertions.assertTrue(stmt.isIfExists(), "IF EXISTS should be true");
}
/**
* Test case: Parse CREATE GROUP PROVIDER with complex properties
* Test point: Should correctly parse multiple properties with various types
*/
@Test
public void testParseCreateGroupProviderWithComplexProperties() throws Exception {
String sql = "CREATE GROUP PROVIDER IF NOT EXISTS complex_provider PROPERTIES(" +
"\"type\" = \"ldap\", " +
"\"ldap_conn_url\" = \"ldap://localhost:389\", " +
"\"ldap_conn_timeout\" = \"5000\", " +
"\"ldap_conn_read_timeout\" = \"3000\", " +
"\"ldap_ssl_conn_allow_insecure\" = \"true\")";
CreateGroupProviderStmt stmt =
(CreateGroupProviderStmt) SqlParser.parseSingleStatement(sql, ctx.getSessionVariable().getSqlMode());
Assertions.assertNotNull(stmt, "Statement should not be null");
Assertions.assertEquals("complex_provider", stmt.getName(), "Provider name should match");
Assertions.assertTrue(stmt.isIfNotExists(), "IF NOT EXISTS should be true");
Map<String, String> properties = stmt.getPropertyMap();
Assertions.assertNotNull(properties, "Properties should not be null");
Assertions.assertEquals("ldap", properties.get("type"), "Type should be ldap");
Assertions.assertEquals("ldap://localhost:389", properties.get("ldap_conn_url"), "LDAP URL should match");
Assertions.assertEquals("5000", properties.get("ldap_conn_timeout"), "Timeout should match");
Assertions.assertEquals("3000", properties.get("ldap_conn_read_timeout"), "Read timeout should match");
Assertions.assertEquals("true", properties.get("ldap_ssl_conn_allow_insecure"), "SSL setting should match");
}
/**
* Test case: Parse statements with case variations
* Test point: Should handle case-insensitive keywords correctly
*/
@Test
public void testParseWithCaseVariations() throws Exception {
// Test CREATE with different case
String createSql = "create group provider if not exists case_test PROPERTIES(\"type\" = \"unix\")";
CreateGroupProviderStmt createStmt = (CreateGroupProviderStmt) UtFrameUtils.parseStmtWithNewParser(createSql, ctx);
Assertions.assertNotNull(createStmt, "Create statement should not be null");
Assertions.assertEquals("case_test", createStmt.getName(), "Provider name should match");
Assertions.assertTrue(createStmt.isIfNotExists(), "IF NOT EXISTS should be true");
// Test DROP with different case
String dropSql = "drop group provider if exists case_test";
DropGroupProviderStmt dropStmt = (DropGroupProviderStmt) UtFrameUtils.parseStmtWithNewParser(dropSql, ctx);
Assertions.assertNotNull(dropStmt, "Drop statement should not be null");
Assertions.assertEquals("case_test", dropStmt.getName(), "Provider name should match");
Assertions.assertTrue(dropStmt.isIfExists(), "IF EXISTS should be true");
}
}

View File

@ -1892,11 +1892,11 @@ showCreateSecurityIntegrationStatement
// ------------------------------------------- Group Provider Statement ------------------------------------------
createGroupProviderStatement
: CREATE GROUP PROVIDER identifier properties
: CREATE GROUP PROVIDER (IF NOT EXISTS)? identifier properties
;
dropGroupProviderStatement
: DROP GROUP PROVIDER identifier
: DROP GROUP PROVIDER (IF EXISTS)? identifier
;
showGroupProvidersStatement

View File

@ -23,11 +23,17 @@ import java.util.Map;
public class CreateGroupProviderStmt extends DdlStmt {
private final String name;
private final Map<String, String> propertyMap;
private final boolean ifNotExists;
public CreateGroupProviderStmt(String name, Map<String, String> propertyMap, NodePosition pos) {
this(name, propertyMap, false, pos);
}
public CreateGroupProviderStmt(String name, Map<String, String> propertyMap, boolean ifNotExists, NodePosition pos) {
super(pos);
this.name = name;
this.propertyMap = propertyMap;
this.ifNotExists = ifNotExists;
}
public String getName() {
@ -38,6 +44,10 @@ public class CreateGroupProviderStmt extends DdlStmt {
return propertyMap;
}
public boolean isIfNotExists() {
return ifNotExists;
}
@Override
public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
return visitor.visitCreateGroupProviderStatement(this, context);

View File

@ -20,16 +20,26 @@ import com.starrocks.sql.parser.NodePosition;
public class DropGroupProviderStmt extends DdlStmt {
private final String name;
private final boolean ifExists;
public DropGroupProviderStmt(String name, NodePosition pos) {
this(name, false, pos);
}
public DropGroupProviderStmt(String name, boolean ifExists, NodePosition pos) {
super(pos);
this.name = name;
this.ifExists = ifExists;
}
public String getName() {
return name;
}
public boolean isIfExists() {
return ifExists;
}
@Override
public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
return visitor.visitDropGroupProviderStatement(this, context);

View File

@ -8,11 +8,11 @@ create user u1;
grant impersonate on user root to u1;
-- result:
-- !result
create group provider foo properties("type" = "foo");
create group provider if not exists foo properties("type" = "foo");
-- result:
E: (1064, "Getting analyzing error. Detail message: unsupported group provider type 'foo'.")
-- !result
create group provider unix_group_provider properties("type" = "unix");
create group provider if not exists unix_group_provider properties("type" = "unix");
-- result:
-- !result
show create group provider unix_group_provider;
@ -32,11 +32,11 @@ show create group provider unix_group_provider;
-- result:
E: (5203, 'Access denied; you need (at least one of) the SECURITY privilege(s) on SYSTEM for this operation. Please ask the admin to grant permission(s) or try activating existing roles using <set [default] role>. Current role(s): NONE. Inactivated role(s): NONE.')
-- !result
drop group provider unix_group_provider;
drop group provider if exists unix_group_provider;
-- result:
E: (5203, 'Access denied; you need (at least one of) the SECURITY privilege(s) on SYSTEM for this operation. Please ask the admin to grant permission(s) or try activating existing roles using <set [default] role>. Current role(s): NONE. Inactivated role(s): NONE.')
-- !result
create group provider unix_group_provider2 properties("type" = "unix");
create group provider if not exists unix_group_provider2 properties("type" = "unix");
-- result:
E: (5203, 'Access denied; you need (at least one of) the SECURITY privilege(s) on SYSTEM for this operation. Please ask the admin to grant permission(s) or try activating existing roles using <set [default] role>. Current role(s): NONE. Inactivated role(s): NONE.')
-- !result
@ -46,6 +46,6 @@ execute as root with no revert;
drop user u1;
-- result:
-- !result
drop group provider unix_group_provider;
drop group provider if exists unix_group_provider;
-- result:
-- !result

View File

@ -4,19 +4,19 @@ drop user if exists u1;
create user u1;
grant impersonate on user root to u1;
create group provider foo properties("type" = "foo");
create group provider if not exists foo properties("type" = "foo");
create group provider unix_group_provider properties("type" = "unix");
create group provider if not exists unix_group_provider properties("type" = "unix");
show create group provider unix_group_provider;
execute as u1 with no revert;
show group providers;
show create group provider unix_group_provider;
drop group provider unix_group_provider;
create group provider unix_group_provider2 properties("type" = "unix");
drop group provider if exists unix_group_provider;
create group provider if not exists unix_group_provider2 properties("type" = "unix");
execute as root with no revert;
drop user u1;
drop group provider unix_group_provider;
drop group provider if exists unix_group_provider;