This is an automated email from the ASF dual-hosted git repository.
roryqi 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 08472d438c [#11069] feat(authn): Add built-in IdP user group relation
storage model (#11140)
08472d438c is described below
commit 08472d438cc1f417876c524d7709d6e4ce8a1340
Author: MaSai <[email protected]>
AuthorDate: Wed May 20 15:38:55 2026 +0800
[#11069] feat(authn): Add built-in IdP user group relation storage model
(#11140)
### What changes were proposed in this pull request?
Add built-in IdP group-user relation storage for the `idp-basic` plugin
on top of the merged user/group meta mappers:
- `IdpGroupUserRelPO`, `IdpGroupUserRelMapper`, and SQL provider factory
with H2/PostgreSQL/MySQL backends
- Register `IdpGroupUserRelMapper` in `IdpBasicMapperPackageProvider`
- Unit tests for PO, SQL providers, and JDBC-backed storage
(`TestIdpGroupUserRelStorage`)
Code follows the `org.apache.gravitino.idp.storage` package layout
introduced for user/group meta on `main`.
### Why are the changes needed?
Built-in IdP needs persistent group-user membership (join table) to
support authorization and user management flows.
Fix: #11069
### Does this PR introduce _any_ user-facing change?
No user-facing API changes. This adds internal relational storage/mapper
support only.
### How was this patch tested?
- `./gradlew :plugins:idp-basic:spotlessApply`
- `./gradlew :plugins:idp-basic:test -PskipITs`
---
design-docs/gravitino-local-authentication.md | 34 +-
.../idp/storage/mapper/IdpUserGroupRelMapper.java | 77 ++++
.../mapper/IdpUserGroupRelSQLProviderFactory.java | 88 +++++
.../provider/IdpBasicMapperPackageProvider.java | 4 +-
.../base/IdpUserGroupRelBaseSQLProvider.java | 130 +++++++
.../provider/h2/IdpUserGroupRelH2Provider.java | 87 +++++
.../IdpUserGroupRelPostgreSQLProvider.java | 98 +++++
.../idp/storage/po/IdpUserGroupRelPO.java} | 35 +-
.../storage/mapper/AbstractIdpMetaStorageTest.java | 11 +-
.../mapper/TestIdpBasicMapperPackageProvider.java | 6 +-
.../storage/mapper/TestIdpGroupMetaStorage.java | 42 ---
.../storage/mapper/TestIdpUserGroupRelStorage.java | 401 +++++++++++++++++++++
.../idp/storage/mapper/TestIdpUserMetaStorage.java | 59 ---
.../base/TestIdpGroupMetaBaseSQLProvider.java | 163 ---------
.../base/TestIdpUserMetaBaseSQLProvider.java | 169 ---------
.../provider/h2/TestIdpUserMetaH2Provider.java | 35 --
.../TestIdpGroupMetaPostgreSQLProvider.java | 45 ---
.../TestIdpUserMetaPostgreSQLProvider.java | 45 ---
.../idp/storage/po/TestIdpUserGroupRelPO.java | 89 +++++
scripts/h2/schema-1.3.0-h2.sql | 10 +-
scripts/h2/upgrade-1.2.0-to-1.3.0-h2.sql | 10 +-
scripts/mysql/schema-1.3.0-mysql.sql | 12 +-
scripts/mysql/upgrade-1.2.0-to-1.3.0-mysql.sql | 12 +-
scripts/postgresql/schema-1.3.0-postgresql.sql | 26 +-
.../upgrade-1.2.0-to-1.3.0-postgresql.sql | 24 +-
25 files changed, 1062 insertions(+), 650 deletions(-)
diff --git a/design-docs/gravitino-local-authentication.md
b/design-docs/gravitino-local-authentication.md
index fe80d10e2f..498ff153ba 100644
--- a/design-docs/gravitino-local-authentication.md
+++ b/design-docs/gravitino-local-authentication.md
@@ -168,7 +168,7 @@ Local authentication requires three new tables:
1. `idp_user_meta` — IdP user records
2. `idp_group_meta` — IdP group records
-3. `idp_group_user_rel` — user/group membership mapping
+3. `idp_user_group_rel` — user/group membership mapping
These tables follow Gravitino's existing metadata table conventions:
@@ -179,7 +179,7 @@ These tables follow Gravitino's existing metadata table
conventions:
Soft-deleted rows in `idp_user_meta` and `idp_group_meta` should be cleaned
asynchronously by
Gravitino's GC thread, following the same lifecycle management pattern used by
other metadata
tables. When a local user or local group is physically removed by the GC
thread, the implementation
-should also clean the corresponding soft-deleted rows in `idp_group_user_rel`
to avoid leaving
+should also clean the corresponding soft-deleted rows in `idp_user_group_rel`
to avoid leaving
orphaned membership records.
Unlike Gravitino's existing `user_meta` and `group_meta` tables,
`idp_user_meta` and
@@ -195,7 +195,6 @@ CREATE TABLE IF NOT EXISTS `idp_user_meta` (
`user_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'user id',
`user_name` VARCHAR(128) NOT NULL COMMENT 'username',
`password_hash` VARCHAR(1024) NOT NULL COMMENT 'hashed password',
- `audit_info` MEDIUMTEXT NOT NULL COMMENT 'user audit info',
`current_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'user current
version',
`last_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'user last version',
`deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'user deleted
at',
@@ -210,7 +209,6 @@ CREATE TABLE IF NOT EXISTS `idp_user_meta` (
CREATE TABLE IF NOT EXISTS `idp_group_meta` (
`group_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'group id',
`group_name` VARCHAR(128) NOT NULL COMMENT 'group name',
- `audit_info` MEDIUMTEXT NOT NULL COMMENT 'group audit info',
`current_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'group current
version',
`last_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'group last
version',
`deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'group deleted
at',
@@ -219,21 +217,21 @@ CREATE TABLE IF NOT EXISTS `idp_group_meta` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'IdP group
metadata';
```
-### 5.3 `idp_group_user_rel`
+### 5.3 `idp_user_group_rel`
```sql
-CREATE TABLE IF NOT EXISTS `idp_group_user_rel` (
+CREATE TABLE IF NOT EXISTS `idp_user_group_rel` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
- `group_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'IdP group id',
`user_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'IdP user id',
- `audit_info` MEDIUMTEXT NOT NULL COMMENT 'relation audit info',
+ `group_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'IdP group id',
`current_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'relation
current version',
`last_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'relation last
version',
`deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'relation
deleted at',
PRIMARY KEY (`id`),
- UNIQUE KEY `uk_gi_ui_del` (`group_id`, `user_id`, `deleted_at`),
- KEY `idx_uid` (`user_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'IdP group
user relation';
+ UNIQUE KEY `uk_iuig_del` (`user_id`, `group_id`, `deleted_at`),
+ KEY `idx_iuig_uid` (`user_id`),
+ KEY `idx_iuig_gid` (`group_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'IdP user
group relation';
```
### 5.4 Relationship Model
@@ -242,7 +240,7 @@ The logical entity relationship is straightforward:
```text
idp_user_meta
- └──< idp_group_user_rel >── idp_group_meta
+ └──< idp_user_group_rel >── idp_group_meta
```
For integration with Gravitino's existing access control model, the local
authentication tables are also
@@ -269,7 +267,7 @@ idp_user_meta --(user_name, logical mapping)--> user_meta[*]
idp_group_meta --(group_name, logical mapping)--> group_meta[*]
idp_user_meta
- └──< idp_group_user_rel >── idp_group_meta
+ └──< idp_user_group_rel >── idp_group_meta
```
This supports direct username lookup for authentication, group resolution for
authorization, and a
@@ -380,7 +378,7 @@ The password verification flow is:
The local user's groups are resolved by:
1. Query `idp_user_meta` by username and `deleted_at = 0` to get `user_id`.
-2. Query `idp_group_user_rel` by `user_id` to get all active `group_id` values.
+2. Query `idp_user_group_rel` by `user_id` to get all active `group_id` values.
3. Query `idp_group_meta` by those `group_id` values to load the full group
set.
This keeps the model aligned with Gravitino's existing authorization
architecture, where user-group
@@ -479,7 +477,7 @@ At a high level:
5. **Get group**: read the local group information and its current user
memberships.
6. **Add group**: create a new local group.
7. **Remove group**: soft-delete the group record.
-8. **Add user to group**: create a row in `idp_group_user_rel`.
+8. **Add user to group**: create a row in `idp_user_group_rel`.
9. **Remove user from group**: soft-delete the corresponding relation row.
### 9.1 HTTP Interface Design
@@ -743,10 +741,10 @@ curl -X PUT -H "Accept:
application/vnd.gravitino.v1+json" \
|---|---|---|---|
| 1 | Authenticator module wiring | `settings.gradle.kts`,
`server/build.gradle.kts`, `plugins:idp-basic` | Add the new module and make
the server load it when `gravitino.authenticators=basic`. |
| 2 | Password hashing support | `PasswordHasher`, `Argon2idPasswordHasher`,
related tests | Use Argon2id as the only supported password hashing algorithm
and store PHC-style hash strings. |
-| 3 | IdP metadata schema | JDBC schema files, mapper definitions, store layer
| Create `idp_user_meta`, `idp_group_meta`, and `idp_group_user_rel` with
soft-delete support. |
+| 3 | IdP metadata schema | JDBC schema files, mapper definitions, store layer
| Create `idp_user_meta`, `idp_group_meta`, and `idp_user_group_rel` with
soft-delete support. |
| 4 | Service admin initialization | startup initialization logic, validation
logic | Validate `GRAVITINO_INITIAL_ADMIN_PASSWORD`, initialize missing
configured service admins during startup, and fail startup when required
credentials are absent. |
| 5 | Basic authentication flow | `BasicAuthenticator`, auth manager, filter
integration | Verify Basic credentials against `idp_user_meta` and resolve the
authenticated principal. |
-| 6 | Group resolution | store layer, auth manager | Load the user's active
groups from `idp_group_user_rel` and `idp_group_meta` for later authorization. |
+| 6 | Group resolution | store layer, auth manager | Load the user's active
groups from `idp_user_group_rel` and `idp_group_meta` for later authorization. |
| 7 | Local IdP management APIs | REST resources, DTOs, request/response
classes | Implement user CRUD, group CRUD, and group membership management
under `/api/idp`. |
| 8 | Tests and documentation | unit tests, integration tests, design and user
docs | Cover initialization flow, authentication, metadata persistence, REST
APIs, and doc/config alignment. |
@@ -756,7 +754,7 @@ curl -X PUT -H "Accept: application/vnd.gravitino.v1+json" \
|---|---|
| Module wiring | The design, module name, and server wiring all consistently
use `plugins:idp-basic`, while the authenticator mode remains `basic`. |
| Configuration | All examples use `gravitino.authenticators=basic`, and no
obsolete configuration keys remain in the document. |
-| Schema design | The document consistently uses `idp_user_meta`,
`idp_group_meta`, and `idp_group_user_rel`, and the soft-delete lifecycle is
clearly described. |
+| Schema design | The document consistently uses `idp_user_meta`,
`idp_group_meta`, and `idp_user_group_rel`, and the soft-delete lifecycle is
clearly described. |
| Security constraints | The document states that passwords are never stored
in plaintext, Basic authentication should be used only over HTTPS, and
initialization must enforce password policy. |
| Initialization flow | The document explains how configured service admins
are initialized during startup, when `GRAVITINO_INITIAL_ADMIN_PASSWORD` is
required, and that only hashed passwords are written. |
| Authentication flow | The Basic authentication flow is described end-to-end,
including username/password parsing, user lookup, hash verification, and group
resolution. |
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/IdpUserGroupRelMapper.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/IdpUserGroupRelMapper.java
new file mode 100644
index 0000000000..d610d76ed0
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/IdpUserGroupRelMapper.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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 org.apache.gravitino.idp.storage.mapper;
+
+import java.util.List;
+import org.apache.gravitino.idp.storage.po.IdpUserGroupRelPO;
+import org.apache.ibatis.annotations.DeleteProvider;
+import org.apache.ibatis.annotations.InsertProvider;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.SelectProvider;
+import org.apache.ibatis.annotations.UpdateProvider;
+
+/**
+ * A MyBatis mapper for built-in IdP user-group relation operations.
+ *
+ * <p>This interface defines the SQL statements MyBatis executes for the
built-in IdP user-group
+ * relation store. The SQLs are provided through {@code *Provider} annotations
on this mapper
+ * interface. See the <a
href="https://mybatis.org/mybatis-3/getting-started.html">MyBatis getting
+ * started guide</a>.
+ */
+public interface IdpUserGroupRelMapper {
+ String IDP_USER_GROUP_REL_TABLE_NAME = "idp_user_group_rel";
+
+ @SelectProvider(
+ type = IdpUserGroupRelSQLProviderFactory.class,
+ method = "selectGroupNamesByUsername")
+ List<String> selectGroupNamesByUsername(@Param("username") String username);
+
+ @SelectProvider(
+ type = IdpUserGroupRelSQLProviderFactory.class,
+ method = "selectUsernamesByGroupName")
+ List<String> selectUsernamesByGroupName(@Param("groupName") String
groupName);
+
+ @InsertProvider(type = IdpUserGroupRelSQLProviderFactory.class, method =
"batchInsertRelations")
+ void batchInsertRelations(@Param("relations") List<IdpUserGroupRelPO>
relations);
+
+ /**
+ * Soft-deletes user-group relations in a group by user name. An empty list
soft-deletes all
+ * active relations in the group; pass null for an explicit error.
+ */
+ @UpdateProvider(type = IdpUserGroupRelSQLProviderFactory.class, method =
"softDeleteRelations")
+ Integer softDeleteRelations(
+ @Param("groupName") String groupName, @Param("usernames") List<String>
usernames);
+
+ @UpdateProvider(
+ type = IdpUserGroupRelSQLProviderFactory.class,
+ method = "softDeleteRelationsByUsername")
+ Integer softDeleteRelationsByUsername(@Param("username") String username);
+
+ @UpdateProvider(
+ type = IdpUserGroupRelSQLProviderFactory.class,
+ method = "softDeleteRelationsByGroupName")
+ Integer softDeleteRelationsByGroupName(@Param("groupName") String groupName);
+
+ @DeleteProvider(
+ type = IdpUserGroupRelSQLProviderFactory.class,
+ method = "deleteIdpUserGroupRelMetasByLegacyTimeline")
+ Integer deleteIdpUserGroupRelMetasByLegacyTimeline(
+ @Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit);
+}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/IdpUserGroupRelSQLProviderFactory.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/IdpUserGroupRelSQLProviderFactory.java
new file mode 100644
index 0000000000..b5f1fab08c
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/IdpUserGroupRelSQLProviderFactory.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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 org.apache.gravitino.idp.storage.mapper;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.List;
+import java.util.Map;
+import
org.apache.gravitino.idp.storage.mapper.provider.base.IdpUserGroupRelBaseSQLProvider;
+import
org.apache.gravitino.idp.storage.mapper.provider.h2.IdpUserGroupRelH2Provider;
+import
org.apache.gravitino.idp.storage.mapper.provider.postgresql.IdpUserGroupRelPostgreSQLProvider;
+import org.apache.gravitino.idp.storage.po.IdpUserGroupRelPO;
+import org.apache.gravitino.storage.relational.JDBCBackend.JDBCBackendType;
+import org.apache.ibatis.annotations.Param;
+
+public class IdpUserGroupRelSQLProviderFactory {
+ private static final IdpUserGroupRelBaseSQLProvider MYSQL_PROVIDER =
+ new IdpUserGroupRelBaseSQLProvider();
+ private static final IdpUserGroupRelBaseSQLProvider H2_PROVIDER = new
IdpUserGroupRelH2Provider();
+ private static final IdpUserGroupRelBaseSQLProvider POSTGRESQL_PROVIDER =
+ new IdpUserGroupRelPostgreSQLProvider();
+ private static final Map<JDBCBackendType, IdpUserGroupRelBaseSQLProvider>
PROVIDER_MAP =
+ ImmutableMap.of(
+ JDBCBackendType.MYSQL,
+ MYSQL_PROVIDER,
+ JDBCBackendType.H2,
+ H2_PROVIDER,
+ JDBCBackendType.POSTGRESQL,
+ POSTGRESQL_PROVIDER);
+
+ private IdpUserGroupRelSQLProviderFactory() {}
+
+ private static IdpUserGroupRelBaseSQLProvider currentProvider() {
+ return SQLProviderFactoryHelper.currentProvider(
+ PROVIDER_MAP, IdpUserGroupRelSQLProviderFactory.class);
+ }
+
+ static IdpUserGroupRelBaseSQLProvider getProvider(String databaseId) {
+ return SQLProviderFactoryHelper.getProvider(
+ databaseId, PROVIDER_MAP, IdpUserGroupRelSQLProviderFactory.class);
+ }
+
+ public static String selectGroupNamesByUsername(@Param("username") String
username) {
+ return currentProvider().selectGroupNamesByUsername(username);
+ }
+
+ public static String selectUsernamesByGroupName(@Param("groupName") String
groupName) {
+ return currentProvider().selectUsernamesByGroupName(groupName);
+ }
+
+ public static String batchInsertRelations(@Param("relations")
List<IdpUserGroupRelPO> relations) {
+ return currentProvider().batchInsertRelations(relations);
+ }
+
+ public static String softDeleteRelations(
+ @Param("groupName") String groupName, @Param("usernames") List<String>
usernames) {
+ return currentProvider().softDeleteRelations(groupName, usernames);
+ }
+
+ public static String softDeleteRelationsByUsername(@Param("username") String
username) {
+ return currentProvider().softDeleteRelationsByUsername(username);
+ }
+
+ public static String softDeleteRelationsByGroupName(@Param("groupName")
String groupName) {
+ return currentProvider().softDeleteRelationsByGroupName(groupName);
+ }
+
+ public static String deleteIdpUserGroupRelMetasByLegacyTimeline(
+ @Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit)
{
+ return
currentProvider().deleteIdpUserGroupRelMetasByLegacyTimeline(legacyTimeline,
limit);
+ }
+}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/IdpBasicMapperPackageProvider.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/IdpBasicMapperPackageProvider.java
index 075c994067..c4b5aef35f 100644
---
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/IdpBasicMapperPackageProvider.java
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/IdpBasicMapperPackageProvider.java
@@ -21,6 +21,7 @@ package org.apache.gravitino.idp.storage.mapper.provider;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.apache.gravitino.idp.storage.mapper.IdpGroupMetaMapper;
+import org.apache.gravitino.idp.storage.mapper.IdpUserGroupRelMapper;
import org.apache.gravitino.idp.storage.mapper.IdpUserMetaMapper;
import
org.apache.gravitino.storage.relational.mapper.provider.MapperPackageProvider;
@@ -29,6 +30,7 @@ public class IdpBasicMapperPackageProvider implements
MapperPackageProvider {
@Override
public List<Class<?>> getMapperClasses() {
- return ImmutableList.of(IdpUserMetaMapper.class, IdpGroupMetaMapper.class);
+ return ImmutableList.of(
+ IdpUserMetaMapper.class, IdpGroupMetaMapper.class,
IdpUserGroupRelMapper.class);
}
}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/base/IdpUserGroupRelBaseSQLProvider.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/base/IdpUserGroupRelBaseSQLProvider.java
new file mode 100644
index 0000000000..f7608bc1d7
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/base/IdpUserGroupRelBaseSQLProvider.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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 org.apache.gravitino.idp.storage.mapper.provider.base;
+
+import java.util.List;
+import org.apache.gravitino.idp.storage.mapper.IdpGroupMetaMapper;
+import org.apache.gravitino.idp.storage.mapper.IdpUserGroupRelMapper;
+import org.apache.gravitino.idp.storage.mapper.IdpUserMetaMapper;
+import org.apache.gravitino.idp.storage.po.IdpUserGroupRelPO;
+import org.apache.ibatis.annotations.Param;
+
+public class IdpUserGroupRelBaseSQLProvider {
+
+ public String selectGroupNamesByUsername(@Param("username") String username)
{
+ return "SELECT g.group_name"
+ + " FROM "
+ + IdpUserMetaMapper.IDP_USER_TABLE_NAME
+ + " u JOIN "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " r ON r.user_id = u.user_id AND r.deleted_at = 0"
+ + " JOIN "
+ + IdpGroupMetaMapper.IDP_GROUP_TABLE_NAME
+ + " g ON g.group_id = r.group_id AND g.deleted_at = 0"
+ + " WHERE u.user_name = #{username}"
+ + " AND u.deleted_at = 0"
+ + " ORDER BY g.group_name";
+ }
+
+ public String selectUsernamesByGroupName(@Param("groupName") String
groupName) {
+ return "SELECT u.user_name"
+ + " FROM "
+ + IdpGroupMetaMapper.IDP_GROUP_TABLE_NAME
+ + " g JOIN "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " r ON r.group_id = g.group_id AND r.deleted_at = 0"
+ + " JOIN "
+ + IdpUserMetaMapper.IDP_USER_TABLE_NAME
+ + " u ON u.user_id = r.user_id AND u.deleted_at = 0"
+ + " WHERE g.group_name = #{groupName}"
+ + " AND g.deleted_at = 0"
+ + " ORDER BY u.user_name";
+ }
+
+ public String batchInsertRelations(@Param("relations")
List<IdpUserGroupRelPO> relations) {
+ return "<script>"
+ + "INSERT INTO "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " (id, user_id, group_id, current_version, last_version, deleted_at)"
+ + " VALUES "
+ + "<foreach item='item' collection='relations' separator=','>"
+ + "(#{item.id}, #{item.userId}, #{item.groupId},
#{item.currentVersion},"
+ + " #{item.lastVersion}, #{item.deletedAt})"
+ + "</foreach>"
+ + "</script>";
+ }
+
+ public String softDeleteRelations(
+ @Param("groupName") String groupName, @Param("usernames") List<String>
usernames) {
+ return "<script>"
+ + "UPDATE "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " r INNER JOIN "
+ + IdpGroupMetaMapper.IDP_GROUP_TABLE_NAME
+ + " g ON g.group_id = r.group_id AND g.deleted_at = 0"
+ + " INNER JOIN "
+ + IdpUserMetaMapper.IDP_USER_TABLE_NAME
+ + " u ON u.user_id = r.user_id AND u.deleted_at = 0"
+ + " SET r.deleted_at = "
+ + currentTimeMillisExpression()
+ + " WHERE g.group_name = #{groupName}"
+ + " AND r.deleted_at = 0"
+ + "<foreach collection='usernames' item='username'"
+ + " open=' AND u.user_name IN (' separator=',' close=')'>"
+ + "#{username}"
+ + "</foreach>"
+ + "</script>";
+ }
+
+ public String softDeleteRelationsByUsername(@Param("username") String
username) {
+ return "UPDATE "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " r INNER JOIN "
+ + IdpUserMetaMapper.IDP_USER_TABLE_NAME
+ + " u ON u.user_id = r.user_id AND u.deleted_at = 0"
+ + " SET r.deleted_at = "
+ + currentTimeMillisExpression()
+ + " WHERE u.user_name = #{username}"
+ + " AND r.deleted_at = 0";
+ }
+
+ public String softDeleteRelationsByGroupName(@Param("groupName") String
groupName) {
+ return "UPDATE "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " r INNER JOIN "
+ + IdpGroupMetaMapper.IDP_GROUP_TABLE_NAME
+ + " g ON g.group_id = r.group_id AND g.deleted_at = 0"
+ + " SET r.deleted_at = "
+ + currentTimeMillisExpression()
+ + " WHERE g.group_name = #{groupName}"
+ + " AND r.deleted_at = 0";
+ }
+
+ public String deleteIdpUserGroupRelMetasByLegacyTimeline(
+ @Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit)
{
+ return "DELETE FROM "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeline} LIMIT
#{limit}";
+ }
+
+ protected String currentTimeMillisExpression() {
+ return "(UNIX_TIMESTAMP() * 1000.0)";
+ }
+}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/h2/IdpUserGroupRelH2Provider.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/h2/IdpUserGroupRelH2Provider.java
new file mode 100644
index 0000000000..1f9d1dc7aa
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/h2/IdpUserGroupRelH2Provider.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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 org.apache.gravitino.idp.storage.mapper.provider.h2;
+
+import java.util.List;
+import org.apache.gravitino.idp.storage.mapper.IdpGroupMetaMapper;
+import org.apache.gravitino.idp.storage.mapper.IdpUserGroupRelMapper;
+import org.apache.gravitino.idp.storage.mapper.IdpUserMetaMapper;
+import
org.apache.gravitino.idp.storage.mapper.provider.base.IdpUserGroupRelBaseSQLProvider;
+import org.apache.ibatis.annotations.Param;
+
+/** SQL provider for IdP user-group relation statements on H2 backends. */
+public class IdpUserGroupRelH2Provider extends IdpUserGroupRelBaseSQLProvider {
+
+ @Override
+ protected String currentTimeMillisExpression() {
+ return "DATEDIFF('MILLISECOND', TIMESTAMP '1970-01-01 00:00:00',
CURRENT_TIMESTAMP())";
+ }
+
+ @Override
+ public String softDeleteRelations(
+ @Param("groupName") String groupName, @Param("usernames") List<String>
usernames) {
+ return "<script>"
+ + "UPDATE "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " r SET deleted_at = "
+ + currentTimeMillisExpression()
+ + " WHERE r.deleted_at = 0"
+ + " AND r.group_id IN (SELECT g.group_id FROM "
+ + IdpGroupMetaMapper.IDP_GROUP_TABLE_NAME
+ + " g WHERE g.group_name = #{groupName} AND g.deleted_at = 0)"
+ + " AND r.user_id IN (SELECT u.user_id FROM "
+ + IdpUserMetaMapper.IDP_USER_TABLE_NAME
+ + " u WHERE u.deleted_at = 0"
+ + "<foreach collection='usernames' item='username'"
+ + " open=' AND u.user_name IN (' separator=',' close=')'>"
+ + "#{username}"
+ + "</foreach>"
+ + ")"
+ + "</script>";
+ }
+
+ @Override
+ public String softDeleteRelationsByUsername(@Param("username") String
username) {
+ return "MERGE INTO "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " r USING "
+ + IdpUserMetaMapper.IDP_USER_TABLE_NAME
+ + " u ON r.user_id = u.user_id"
+ + " AND u.user_name = #{username}"
+ + " AND u.deleted_at = 0"
+ + " AND r.deleted_at = 0"
+ + " WHEN MATCHED THEN UPDATE SET r.deleted_at = "
+ + currentTimeMillisExpression();
+ }
+
+ @Override
+ public String softDeleteRelationsByGroupName(@Param("groupName") String
groupName) {
+ return "MERGE INTO "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " r USING "
+ + IdpGroupMetaMapper.IDP_GROUP_TABLE_NAME
+ + " g ON r.group_id = g.group_id"
+ + " AND g.group_name = #{groupName}"
+ + " AND g.deleted_at = 0"
+ + " AND r.deleted_at = 0"
+ + " WHEN MATCHED THEN UPDATE SET r.deleted_at = "
+ + currentTimeMillisExpression();
+ }
+}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/postgresql/IdpUserGroupRelPostgreSQLProvider.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/postgresql/IdpUserGroupRelPostgreSQLProvider.java
new file mode 100644
index 0000000000..03cbeb6635
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/mapper/provider/postgresql/IdpUserGroupRelPostgreSQLProvider.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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 org.apache.gravitino.idp.storage.mapper.provider.postgresql;
+
+import java.util.List;
+import org.apache.gravitino.idp.storage.mapper.IdpGroupMetaMapper;
+import org.apache.gravitino.idp.storage.mapper.IdpUserGroupRelMapper;
+import org.apache.gravitino.idp.storage.mapper.IdpUserMetaMapper;
+import
org.apache.gravitino.idp.storage.mapper.provider.base.IdpUserGroupRelBaseSQLProvider;
+import org.apache.ibatis.annotations.Param;
+
+public class IdpUserGroupRelPostgreSQLProvider extends
IdpUserGroupRelBaseSQLProvider {
+
+ @Override
+ protected String currentTimeMillisExpression() {
+ return "CAST(EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) * 1000 AS BIGINT)";
+ }
+
+ @Override
+ public String softDeleteRelations(
+ @Param("groupName") String groupName, @Param("usernames") List<String>
usernames) {
+ return "<script>"
+ + "UPDATE "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " r SET deleted_at = "
+ + currentTimeMillisExpression()
+ + " FROM "
+ + IdpGroupMetaMapper.IDP_GROUP_TABLE_NAME
+ + " g, "
+ + IdpUserMetaMapper.IDP_USER_TABLE_NAME
+ + " u WHERE r.group_id = g.group_id"
+ + " AND r.user_id = u.user_id"
+ + " AND g.group_name = #{groupName}"
+ + " AND g.deleted_at = 0"
+ + " AND u.deleted_at = 0"
+ + " AND r.deleted_at = 0"
+ + "<foreach collection='usernames' item='username'"
+ + " open=' AND u.user_name IN (' separator=',' close=')'>"
+ + "#{username}"
+ + "</foreach>"
+ + "</script>";
+ }
+
+ @Override
+ public String softDeleteRelationsByUsername(@Param("username") String
username) {
+ return "UPDATE "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " r SET deleted_at = "
+ + currentTimeMillisExpression()
+ + " FROM "
+ + IdpUserMetaMapper.IDP_USER_TABLE_NAME
+ + " u WHERE r.user_id = u.user_id"
+ + " AND u.user_name = #{username}"
+ + " AND u.deleted_at = 0"
+ + " AND r.deleted_at = 0";
+ }
+
+ @Override
+ public String softDeleteRelationsByGroupName(@Param("groupName") String
groupName) {
+ return "UPDATE "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " r SET deleted_at = "
+ + currentTimeMillisExpression()
+ + " FROM "
+ + IdpGroupMetaMapper.IDP_GROUP_TABLE_NAME
+ + " g WHERE r.group_id = g.group_id"
+ + " AND g.group_name = #{groupName}"
+ + " AND g.deleted_at = 0"
+ + " AND r.deleted_at = 0";
+ }
+
+ @Override
+ public String deleteIdpUserGroupRelMetasByLegacyTimeline(
+ @Param("legacyTimeline") Long legacyTimeline, @Param("limit") int limit)
{
+ return "DELETE FROM "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " WHERE id IN (SELECT id FROM "
+ + IdpUserGroupRelMapper.IDP_USER_GROUP_REL_TABLE_NAME
+ + " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeline} LIMIT
#{limit})";
+ }
+}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/h2/TestIdpGroupMetaH2Provider.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/po/IdpUserGroupRelPO.java
similarity index 57%
rename from
plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/h2/TestIdpGroupMetaH2Provider.java
rename to
plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/po/IdpUserGroupRelPO.java
index fb6584795a..ecf168f9f7 100644
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/h2/TestIdpGroupMetaH2Provider.java
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/po/IdpUserGroupRelPO.java
@@ -16,20 +16,27 @@
* specific language governing permissions and limitations
* under the License.
*/
+package org.apache.gravitino.idp.storage.po;
-package org.apache.gravitino.idp.storage.mapper.provider.h2;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-class TestIdpGroupMetaH2Provider {
-
- @Test
- void testCurrentTimeMillisExpression() {
- IdpGroupMetaH2Provider provider = new IdpGroupMetaH2Provider();
-
- Assertions.assertEquals(
- "DATEDIFF('MILLISECOND', TIMESTAMP '1970-01-01 00:00:00',
CURRENT_TIMESTAMP())",
- provider.currentTimeMillisExpression());
- }
+@Getter
+@EqualsAndHashCode
+@ToString
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@Builder(setterPrefix = "with")
+public class IdpUserGroupRelPO {
+ private Long id;
+ private Long userId;
+ private Long groupId;
+ private Long currentVersion;
+ private Long lastVersion;
+ private Long deletedAt;
}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/AbstractIdpMetaStorageTest.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/AbstractIdpMetaStorageTest.java
index f41f323835..047af1bb08 100644
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/AbstractIdpMetaStorageTest.java
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/AbstractIdpMetaStorageTest.java
@@ -85,15 +85,6 @@ abstract class AbstractIdpMetaStorageTest {
initializeMappers();
}
- protected void restartBackend() throws IOException {
- closeSession();
- backend.close();
- backend = new JDBCBackend();
- backend.initialize(config);
- sharedSession =
SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true);
- initializeMappers();
- }
-
protected void closeSession() {
if (sharedSession != null) {
sharedSession.close();
@@ -101,7 +92,7 @@ abstract class AbstractIdpMetaStorageTest {
}
}
- protected abstract void initializeMappers();
+ protected void initializeMappers() {}
private Config createBackendConfig(String type) throws IOException {
Config backendConfig = new Config(false) {};
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpBasicMapperPackageProvider.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpBasicMapperPackageProvider.java
index 3fcf1b6a7b..d522d36100 100644
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpBasicMapperPackageProvider.java
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpBasicMapperPackageProvider.java
@@ -35,9 +35,11 @@ public class TestIdpBasicMapperPackageProvider {
MapperPackageProvider provider = new IdpBasicMapperPackageProvider();
List<Class<?>> mapperClasses = provider.getMapperClasses();
- assertEquals(2, mapperClasses.size());
+ assertEquals(3, mapperClasses.size());
assertTrue(
- mapperClasses.containsAll(List.of(IdpUserMetaMapper.class,
IdpGroupMetaMapper.class)));
+ mapperClasses.containsAll(
+ List.of(
+ IdpUserMetaMapper.class, IdpGroupMetaMapper.class,
IdpUserGroupRelMapper.class)));
}
@Test
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpGroupMetaStorage.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpGroupMetaStorage.java
index 48ad87d48a..8d34c986ec 100644
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpGroupMetaStorage.java
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpGroupMetaStorage.java
@@ -23,13 +23,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import org.apache.gravitino.idp.storage.po.IdpGroupPO;
-import org.apache.gravitino.storage.relational.session.SqlSessionFactoryHelper;
import org.apache.ibatis.exceptions.PersistenceException;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
@@ -177,44 +175,4 @@ class TestIdpGroupMetaStorage extends
AbstractIdpMetaStorageTest {
assertNull(idpGroupMetaMapper.selectIdpGroup("legacy-group"));
assertNull(idpGroupMetaMapper.selectIdpGroup("new-group"));
}
-
- @ParameterizedTest
- @MethodSource("storageProvider")
- void testRestart(String type) throws IOException {
- init(type);
- IdpGroupPO expectedActiveGroup =
- IdpGroupPO.builder()
- .withGroupId(1L)
- .withGroupName("dev")
- .withCurrentVersion(3L)
- .withLastVersion(2L)
- .withDeletedAt(0L)
- .build();
- idpGroupMetaMapper.insertIdpGroup(expectedActiveGroup);
- idpGroupMetaMapper.insertIdpGroup(
- IdpGroupPO.builder()
- .withGroupId(2L)
- .withGroupName("ops")
- .withCurrentVersion(1L)
- .withLastVersion(0L)
- .withDeletedAt(10L)
- .build());
-
- assertPersistedGroups(expectedActiveGroup);
-
- restartBackend();
-
- assertPersistedGroups(expectedActiveGroup);
- }
-
- private void assertPersistedGroups(IdpGroupPO expectedActiveGroup) {
- assertTrue(
- SqlSessionFactoryHelper.getInstance()
- .getSqlSessionFactory()
- .getConfiguration()
- .hasMapper(IdpGroupMetaMapper.class));
-
- assertEquals(expectedActiveGroup,
idpGroupMetaMapper.selectIdpGroup("dev"));
- assertNull(idpGroupMetaMapper.selectIdpGroup("ops"));
- }
}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpUserGroupRelStorage.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpUserGroupRelStorage.java
new file mode 100644
index 0000000000..3572eb8f41
--- /dev/null
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpUserGroupRelStorage.java
@@ -0,0 +1,401 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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 org.apache.gravitino.idp.storage.mapper;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+
+import java.io.IOException;
+import java.util.List;
+import org.apache.gravitino.idp.storage.po.IdpGroupPO;
+import org.apache.gravitino.idp.storage.po.IdpUserGroupRelPO;
+import org.apache.gravitino.idp.storage.po.IdpUserPO;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+@Tag("gravitino-docker-test")
+class TestIdpUserGroupRelStorage extends AbstractIdpMetaStorageTest {
+ private IdpUserMetaMapper idpUserMetaMapper;
+ private IdpGroupMetaMapper idpGroupMetaMapper;
+ private IdpUserGroupRelMapper idpUserGroupRelMapper;
+
+ @Override
+ protected void initializeMappers() {
+ idpUserMetaMapper = sharedSession.getMapper(IdpUserMetaMapper.class);
+ idpGroupMetaMapper = sharedSession.getMapper(IdpGroupMetaMapper.class);
+ idpUserGroupRelMapper =
sharedSession.getMapper(IdpUserGroupRelMapper.class);
+ }
+
+ @ParameterizedTest
+ @MethodSource("storageProvider")
+ void testBatchInsertIdpUserGroupsAndSelectGroupNamesByUsername(String type)
throws IOException {
+ init(type);
+ idpGroupMetaMapper.insertIdpGroup(
+ IdpGroupPO.builder()
+ .withGroupId(1L)
+ .withGroupName("dev")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpGroupMetaMapper.insertIdpGroup(
+ IdpGroupPO.builder()
+ .withGroupId(2L)
+ .withGroupName("ops")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserMetaMapper.insertIdpUser(
+ IdpUserPO.builder()
+ .withUserId(1L)
+ .withUserName("alice")
+ .withPasswordHash("hash-a")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserGroupRelMapper.batchInsertRelations(
+ List.of(
+ IdpUserGroupRelPO.builder()
+ .withId(100L)
+ .withUserId(1L)
+ .withGroupId(1L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build(),
+ IdpUserGroupRelPO.builder()
+ .withId(101L)
+ .withUserId(1L)
+ .withGroupId(2L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build()));
+
+ assertIterableEquals(
+ List.of("dev", "ops"),
idpUserGroupRelMapper.selectGroupNamesByUsername("alice"));
+ assertIterableEquals(
+ List.of(),
idpUserGroupRelMapper.selectGroupNamesByUsername("missing-user"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("storageProvider")
+ void testSelectUsernamesByGroupName(String type) throws IOException {
+ init(type);
+ idpGroupMetaMapper.insertIdpGroup(
+ IdpGroupPO.builder()
+ .withGroupId(1L)
+ .withGroupName("dev")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserMetaMapper.insertIdpUser(
+ IdpUserPO.builder()
+ .withUserId(1L)
+ .withUserName("alice")
+ .withPasswordHash("hash-a")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserMetaMapper.insertIdpUser(
+ IdpUserPO.builder()
+ .withUserId(2L)
+ .withUserName("bob")
+ .withPasswordHash("hash-b")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserGroupRelMapper.batchInsertRelations(
+ List.of(
+ IdpUserGroupRelPO.builder()
+ .withId(100L)
+ .withUserId(1L)
+ .withGroupId(1L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build(),
+ IdpUserGroupRelPO.builder()
+ .withId(101L)
+ .withUserId(2L)
+ .withGroupId(1L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build()));
+
+ assertIterableEquals(
+ List.of("alice", "bob"),
idpUserGroupRelMapper.selectUsernamesByGroupName("dev"));
+ assertIterableEquals(
+ List.of(),
idpUserGroupRelMapper.selectUsernamesByGroupName("missing-group"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("storageProvider")
+ void testSoftDeleteIdpUserGroups(String type) throws IOException {
+ init(type);
+ idpGroupMetaMapper.insertIdpGroup(
+ IdpGroupPO.builder()
+ .withGroupId(1L)
+ .withGroupName("dev")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserMetaMapper.insertIdpUser(
+ IdpUserPO.builder()
+ .withUserId(1L)
+ .withUserName("alice")
+ .withPasswordHash("hash-a")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserMetaMapper.insertIdpUser(
+ IdpUserPO.builder()
+ .withUserId(2L)
+ .withUserName("bob")
+ .withPasswordHash("hash-b")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserGroupRelMapper.batchInsertRelations(
+ List.of(
+ IdpUserGroupRelPO.builder()
+ .withId(100L)
+ .withUserId(1L)
+ .withGroupId(1L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build(),
+ IdpUserGroupRelPO.builder()
+ .withId(101L)
+ .withUserId(2L)
+ .withGroupId(1L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build()));
+
+ assertEquals(1, idpUserGroupRelMapper.softDeleteRelations("dev",
List.of("alice")));
+ assertIterableEquals(List.of("bob"),
idpUserGroupRelMapper.selectUsernamesByGroupName("dev"));
+ assertEquals(0, idpUserGroupRelMapper.softDeleteRelations("dev",
List.of("alice")));
+ assertEquals(1, idpUserGroupRelMapper.softDeleteRelations("dev",
List.of()));
+ assertIterableEquals(List.of(),
idpUserGroupRelMapper.selectUsernamesByGroupName("dev"));
+ assertEquals(
+ 2,
idpUserGroupRelMapper.deleteIdpUserGroupRelMetasByLegacyTimeline(Long.MAX_VALUE,
10));
+ }
+
+ @ParameterizedTest
+ @MethodSource("storageProvider")
+ void testSoftDeleteRelationsByUsername(String type) throws IOException {
+ init(type);
+ idpGroupMetaMapper.insertIdpGroup(
+ IdpGroupPO.builder()
+ .withGroupId(1L)
+ .withGroupName("dev")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserMetaMapper.insertIdpUser(
+ IdpUserPO.builder()
+ .withUserId(1L)
+ .withUserName("alice")
+ .withPasswordHash("hash-a")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserMetaMapper.insertIdpUser(
+ IdpUserPO.builder()
+ .withUserId(2L)
+ .withUserName("bob")
+ .withPasswordHash("hash-b")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserGroupRelMapper.batchInsertRelations(
+ List.of(
+ IdpUserGroupRelPO.builder()
+ .withId(100L)
+ .withUserId(1L)
+ .withGroupId(1L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build(),
+ IdpUserGroupRelPO.builder()
+ .withId(101L)
+ .withUserId(2L)
+ .withGroupId(1L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build()));
+
+ assertEquals(1,
idpUserGroupRelMapper.softDeleteRelationsByUsername("bob"));
+ assertIterableEquals(List.of("alice"),
idpUserGroupRelMapper.selectUsernamesByGroupName("dev"));
+ assertEquals(0,
idpUserGroupRelMapper.softDeleteRelationsByUsername("bob"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("storageProvider")
+ void testSoftDeleteRelationsByGroupName(String type) throws IOException {
+ init(type);
+ idpGroupMetaMapper.insertIdpGroup(
+ IdpGroupPO.builder()
+ .withGroupId(2L)
+ .withGroupName("ops")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserMetaMapper.insertIdpUser(
+ IdpUserPO.builder()
+ .withUserId(3L)
+ .withUserName("carol")
+ .withPasswordHash("hash-c")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserGroupRelMapper.batchInsertRelations(
+ List.of(
+ IdpUserGroupRelPO.builder()
+ .withId(102L)
+ .withUserId(3L)
+ .withGroupId(2L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build()));
+
+ assertEquals(1,
idpUserGroupRelMapper.softDeleteRelationsByGroupName("ops"));
+ assertIterableEquals(List.of(),
idpUserGroupRelMapper.selectUsernamesByGroupName("ops"));
+ assertEquals(0,
idpUserGroupRelMapper.softDeleteRelationsByGroupName("ops"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("storageProvider")
+ void testDeleteIdpUserGroupRelMetasByLegacyTimeline(String type) throws
IOException {
+ init(type);
+ idpGroupMetaMapper.insertIdpGroup(
+ IdpGroupPO.builder()
+ .withGroupId(1L)
+ .withGroupName("dev")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpGroupMetaMapper.insertIdpGroup(
+ IdpGroupPO.builder()
+ .withGroupId(2L)
+ .withGroupName("ops")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpGroupMetaMapper.insertIdpGroup(
+ IdpGroupPO.builder()
+ .withGroupId(3L)
+ .withGroupName("qa")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserMetaMapper.insertIdpUser(
+ IdpUserPO.builder()
+ .withUserId(1L)
+ .withUserName("legacy-user")
+ .withPasswordHash("hash")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserMetaMapper.insertIdpUser(
+ IdpUserPO.builder()
+ .withUserId(2L)
+ .withUserName("new-user")
+ .withPasswordHash("hash")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserMetaMapper.insertIdpUser(
+ IdpUserPO.builder()
+ .withUserId(3L)
+ .withUserName("active-user")
+ .withPasswordHash("hash")
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build());
+ idpUserGroupRelMapper.batchInsertRelations(
+ List.of(
+ IdpUserGroupRelPO.builder()
+ .withId(100L)
+ .withUserId(1L)
+ .withGroupId(1L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(10L)
+ .build()));
+ idpUserGroupRelMapper.batchInsertRelations(
+ List.of(
+ IdpUserGroupRelPO.builder()
+ .withId(101L)
+ .withUserId(2L)
+ .withGroupId(2L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(30L)
+ .build()));
+ idpUserGroupRelMapper.batchInsertRelations(
+ List.of(
+ IdpUserGroupRelPO.builder()
+ .withId(102L)
+ .withUserId(3L)
+ .withGroupId(3L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build()));
+
+ assertEquals(1,
idpUserGroupRelMapper.deleteIdpUserGroupRelMetasByLegacyTimeline(20L, 10));
+ assertEquals(0,
idpUserGroupRelMapper.deleteIdpUserGroupRelMetasByLegacyTimeline(20L, 10));
+ assertEquals(1,
idpUserGroupRelMapper.deleteIdpUserGroupRelMetasByLegacyTimeline(40L, 10));
+ assertEquals(
+ 0,
idpUserGroupRelMapper.deleteIdpUserGroupRelMetasByLegacyTimeline(Long.MAX_VALUE,
10));
+ assertIterableEquals(
+ List.of("active-user"),
idpUserGroupRelMapper.selectUsernamesByGroupName("qa"));
+ assertIterableEquals(List.of(),
idpUserGroupRelMapper.selectUsernamesByGroupName("dev"));
+ assertIterableEquals(List.of(),
idpUserGroupRelMapper.selectUsernamesByGroupName("ops"));
+ }
+}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpUserMetaStorage.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpUserMetaStorage.java
index f2d87f4341..d7901ad747 100644
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpUserMetaStorage.java
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/TestIdpUserMetaStorage.java
@@ -23,13 +23,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import org.apache.gravitino.idp.storage.po.IdpUserPO;
-import org.apache.gravitino.storage.relational.session.SqlSessionFactoryHelper;
import org.apache.ibatis.exceptions.PersistenceException;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
@@ -229,61 +227,4 @@ class TestIdpUserMetaStorage extends
AbstractIdpMetaStorageTest {
assertNull(idpUserMetaMapper.selectIdpUser("legacy-user"));
assertNull(idpUserMetaMapper.selectIdpUser("new-user"));
}
-
- @ParameterizedTest
- @MethodSource("storageProvider")
- void testRestart(String type) throws IOException {
- init(type);
- IdpUserPO expectedActiveUser =
- IdpUserPO.builder()
- .withUserId(1L)
- .withUserName("alice")
- .withPasswordHash("hash-a")
- .withCurrentVersion(3L)
- .withLastVersion(2L)
- .withDeletedAt(0L)
- .build();
- idpUserMetaMapper.insertIdpUser(expectedActiveUser);
- idpUserMetaMapper.insertIdpUser(
- IdpUserPO.builder()
- .withUserId(2L)
- .withUserName("bob")
- .withPasswordHash("hash-b")
- .withCurrentVersion(1L)
- .withLastVersion(0L)
- .withDeletedAt(10L)
- .build());
- assertEquals(1, idpUserMetaMapper.updateIdpUserPassword(1L, "hash-a-2"));
-
- expectedActiveUser =
- IdpUserPO.builder()
- .withUserId(expectedActiveUser.getUserId())
- .withUserName(expectedActiveUser.getUserName())
- .withPasswordHash("hash-a-2")
- .withCurrentVersion(expectedActiveUser.getCurrentVersion())
- .withLastVersion(expectedActiveUser.getLastVersion())
- .withDeletedAt(expectedActiveUser.getDeletedAt())
- .build();
-
- assertPersistedUsers(expectedActiveUser);
-
- restartBackend();
-
- assertPersistedUsers(expectedActiveUser);
- }
-
- private void assertPersistedUsers(IdpUserPO expectedActiveUser) {
- assertTrue(
- SqlSessionFactoryHelper.getInstance()
- .getSqlSessionFactory()
- .getConfiguration()
- .hasMapper(IdpUserMetaMapper.class));
-
- assertEquals(expectedActiveUser, idpUserMetaMapper.selectIdpUser("alice"));
- assertNull(idpUserMetaMapper.selectIdpUser("bob"));
-
- List<IdpUserPO> users = idpUserMetaMapper.selectIdpUsers(List.of("bob",
"alice"));
- users.sort(Comparator.comparing(IdpUserPO::getUserId));
- assertIterableEquals(List.of(expectedActiveUser), users);
- }
}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/base/TestIdpGroupMetaBaseSQLProvider.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/base/TestIdpGroupMetaBaseSQLProvider.java
deleted file mode 100644
index 2b98776e18..0000000000
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/base/TestIdpGroupMetaBaseSQLProvider.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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
- *
- * http://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 org.apache.gravitino.idp.storage.mapper.provider.base;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import org.apache.gravitino.idp.storage.po.IdpGroupPO;
-import org.apache.ibatis.builder.BuilderException;
-import org.apache.ibatis.mapping.BoundSql;
-import org.apache.ibatis.mapping.SqlSource;
-import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
-import org.apache.ibatis.session.Configuration;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-public class TestIdpGroupMetaBaseSQLProvider {
-
- protected IdpGroupMetaBaseSQLProvider createProvider() {
- return new IdpGroupMetaBaseSQLProvider() {
- @Override
- protected String currentTimeMillisExpression() {
- return "CURRENT_TIME_MILLIS()";
- }
- };
- }
-
- protected String expectedDeleteAtClause() {
- return "deleted_at = CURRENT_TIME_MILLIS()";
- }
-
- protected String expectedDeleteIdpGroupMetasByLegacyTimelineSql() {
- return "DELETE FROM idp_group_meta WHERE deleted_at > 0 AND deleted_at <
#{legacyTimeline}"
- + " LIMIT #{limit}";
- }
-
- @Test
- void testSelectIdpGroup() {
- String normalizedSql =
createProvider().selectIdpGroup("group").replaceAll("\\s+", " ").trim();
-
- Assertions.assertTrue(normalizedSql.contains("SELECT group_id as
groupId"));
- Assertions.assertTrue(normalizedSql.contains("FROM idp_group_meta"));
- Assertions.assertTrue(
- normalizedSql.contains("WHERE group_name = #{groupName} AND deleted_at
= 0"));
- }
-
- @Test
- void testSelectIdpGroups() {
- String script = createProvider().selectIdpGroups(Arrays.asList("dev",
"ops"));
- Map<String, Object> params = new HashMap<>();
- params.put("groupNames", Arrays.asList("dev", "ops"));
-
- String normalizedSql = renderScript(script, params);
-
- Assertions.assertTrue(normalizedSql.contains("SELECT group_id as
groupId"));
- Assertions.assertTrue(normalizedSql.contains("FROM idp_group_meta"));
- Assertions.assertTrue(
- normalizedSql.matches(".*group_name
IN\\s*\\(\\s*\\?\\s*,\\s*\\?\\s*\\).*"));
- Assertions.assertFalse(normalizedSql.matches(".*\\b1\\s*=\\s*0\\b.*"));
- }
-
- @Test
- void testSelectIdpGroupsWithEmptyGroupNames() {
- String script = createProvider().selectIdpGroups(Collections.emptyList());
- Map<String, Object> params = new HashMap<>();
- params.put("groupNames", Collections.emptyList());
-
- String normalizedSql = renderScript(script, params);
-
- Assertions.assertFalse(
- normalizedSql.matches(".*\\bIN\\s*\\(\\s*\\).*"),
- "Empty groupNames should not generate invalid SQL IN (...) with no
values");
- Assertions.assertFalse(normalizedSql.matches(".*\\b1\\s*=\\s*0\\b.*"));
- Assertions.assertEquals(
- "SELECT group_id as groupId, group_name as groupName, current_version
as"
- + " currentVersion, last_version as lastVersion, deleted_at as
deletedAt FROM"
- + " idp_group_meta WHERE deleted_at = 0",
- normalizedSql);
- }
-
- @Test
- void testSelectIdpGroupsWithNullGroupNames() {
- String script = createProvider().selectIdpGroups(null);
- Map<String, Object> params = new HashMap<>();
- params.put("groupNames", null);
-
- Assertions.assertThrows(BuilderException.class, () -> renderScript(script,
params));
- }
-
- @Test
- void testInsertIdpGroup() {
- String normalizedSql =
- createProvider().insertIdpGroup(newGroupPO()).replaceAll("\\s+", "
").trim();
-
- Assertions.assertTrue(normalizedSql.contains("INSERT INTO
idp_group_meta"));
- Assertions.assertTrue(
- normalizedSql.contains(
- "(group_id, group_name, current_version, last_version,
deleted_at)"));
- Assertions.assertTrue(
- normalizedSql.contains(
- "VALUES ( #{groupMeta.groupId}, #{groupMeta.groupName},
#{groupMeta.currentVersion},"
- + " #{groupMeta.lastVersion}, #{groupMeta.deletedAt} )"));
- }
-
- @Test
- void testSoftDeleteIdpGroup() {
- String normalizedSql =
createProvider().softDeleteIdpGroup(1L).replaceAll("\\s+", " ").trim();
-
- Assertions.assertTrue(normalizedSql.contains("UPDATE idp_group_meta"));
- Assertions.assertTrue(normalizedSql.contains(expectedDeleteAtClause()));
- Assertions.assertTrue(normalizedSql.contains("WHERE group_id = #{groupId}
AND deleted_at = 0"));
- }
-
- @Test
- void testDeleteIdpGroupMetasByLegacyTimeline() {
- String normalizedSql =
- createProvider().deleteIdpGroupMetasByLegacyTimeline(1L,
2).replaceAll("\\s+", " ").trim();
-
- Assertions.assertEquals(expectedDeleteIdpGroupMetasByLegacyTimelineSql(),
normalizedSql);
- }
-
- @Test
- void testCurrentTimeMillisExpression() {
- Assertions.assertEquals(
- "(UNIX_TIMESTAMP() * 1000.0)",
- new IdpGroupMetaBaseSQLProvider().currentTimeMillisExpression());
- }
-
- private IdpGroupPO newGroupPO() {
- return IdpGroupPO.builder()
- .withGroupId(1L)
- .withGroupName("group")
- .withCurrentVersion(1L)
- .withLastVersion(1L)
- .withDeletedAt(0L)
- .build();
- }
-
- private String renderScript(String script, Map<String, Object> params) {
- SqlSource sqlSource =
- new XMLLanguageDriver().createSqlSource(new Configuration(), script,
Map.class);
- BoundSql boundSql = sqlSource.getBoundSql(params);
- return boundSql.getSql().replaceAll("\\s+", " ").trim();
- }
-}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/base/TestIdpUserMetaBaseSQLProvider.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/base/TestIdpUserMetaBaseSQLProvider.java
deleted file mode 100644
index b14e015428..0000000000
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/base/TestIdpUserMetaBaseSQLProvider.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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
- *
- * http://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 org.apache.gravitino.idp.storage.mapper.provider.base;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import org.apache.gravitino.idp.storage.po.IdpUserPO;
-import org.apache.ibatis.builder.BuilderException;
-import org.apache.ibatis.mapping.BoundSql;
-import org.apache.ibatis.mapping.SqlSource;
-import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
-import org.apache.ibatis.session.Configuration;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-public class TestIdpUserMetaBaseSQLProvider {
-
- @Test
- void testSelectIdpUser() {
- String normalizedSql =
createProvider().selectIdpUser("tom").replaceAll("\\s+", " ").trim();
-
- Assertions.assertTrue(normalizedSql.contains("SELECT user_id as userId"));
- Assertions.assertTrue(normalizedSql.contains("FROM idp_user_meta"));
- Assertions.assertTrue(
- normalizedSql.contains("WHERE user_name = #{username} AND deleted_at =
0"));
- }
-
- @Test
- void testSelectIdpUsers() {
- String script = createProvider().selectIdpUsers(Arrays.asList("tom",
"jerry"));
- Map<String, Object> params = new HashMap<>();
- params.put("usernames", Arrays.asList("tom", "jerry"));
-
- String normalizedSql = renderScript(script, params);
-
- Assertions.assertTrue(normalizedSql.contains("SELECT user_id as userId"));
- Assertions.assertTrue(normalizedSql.contains("FROM idp_user_meta"));
- Assertions.assertTrue(normalizedSql.matches(".*user_name IN \\( \\? , \\?
\\).*"));
- Assertions.assertFalse(normalizedSql.matches(".*\\b1\\s*=\\s*0\\b.*"));
- }
-
- @Test
- void testSelectIdpUsersWithEmptyUserNames() {
- String script = createProvider().selectIdpUsers(Collections.emptyList());
- Map<String, Object> params = new HashMap<>();
- params.put("usernames", Collections.emptyList());
-
- String normalizedSql = renderScript(script, params);
-
- Assertions.assertFalse(
- normalizedSql.matches(".*\\bIN\\s*\\(\\s*\\).*"),
- "Empty userNames should not generate invalid SQL IN (...) with no
values");
- Assertions.assertFalse(normalizedSql.matches(".*\\b1\\s*=\\s*0\\b.*"));
- Assertions.assertEquals(
- "SELECT user_id as userId, user_name as userName, password_hash as
passwordHash,"
- + " current_version as currentVersion, last_version as
lastVersion,"
- + " deleted_at as deletedAt FROM idp_user_meta WHERE deleted_at =
0",
- normalizedSql);
- }
-
- @Test
- void testSelectIdpUsersWithNullUserNames() {
- String script = createProvider().selectIdpUsers(null);
- Map<String, Object> params = new HashMap<>();
- params.put("usernames", null);
-
- Assertions.assertThrows(BuilderException.class, () -> renderScript(script,
params));
- }
-
- @Test
- void testInsertIdpUser() {
- String normalizedSql =
- createProvider().insertIdpUser(newUserPO()).replaceAll("\\s+", "
").trim();
-
- Assertions.assertTrue(normalizedSql.contains("INSERT INTO idp_user_meta"));
- Assertions.assertTrue(
- normalizedSql.contains(
- "(user_id, user_name, password_hash, current_version,
last_version, deleted_at)"));
- Assertions.assertTrue(
- normalizedSql.contains(
- "VALUES ( #{userMeta.userId}, #{userMeta.userName},
#{userMeta.passwordHash},"
- + " #{userMeta.currentVersion}, #{userMeta.lastVersion},"
- + " #{userMeta.deletedAt} )"));
- }
-
- @Test
- void testUpdateIdpUserPassword() {
- String normalizedSql =
- createProvider().updateIdpUserPassword(1L, "hash").replaceAll("\\s+",
" ").trim();
-
- Assertions.assertTrue(normalizedSql.contains("UPDATE idp_user_meta"));
- Assertions.assertTrue(normalizedSql.contains("SET password_hash =
#{passwordHash}"));
- Assertions.assertTrue(normalizedSql.contains("WHERE user_id = #{userId}"));
- Assertions.assertTrue(normalizedSql.contains("AND deleted_at = 0"));
- }
-
- @Test
- void testSoftDeleteIdpUser() {
- String normalizedSql =
createProvider().softDeleteIdpUser(1L).replaceAll("\\s+", " ").trim();
-
- Assertions.assertTrue(normalizedSql.contains("UPDATE idp_user_meta"));
- Assertions.assertTrue(normalizedSql.contains("CURRENT_TIME_MILLIS()"));
- Assertions.assertTrue(normalizedSql.contains("WHERE user_id = #{userId}
AND deleted_at = 0"));
- }
-
- @Test
- void testDeleteIdpUserMetasByLegacyTimeline() {
- String normalizedSql =
- createProvider().deleteIdpUserMetasByLegacyTimeline(1L,
2).replaceAll("\\s+", " ").trim();
-
- Assertions.assertEquals(
- "DELETE FROM idp_user_meta WHERE deleted_at > 0 AND deleted_at <
#{legacyTimeline}"
- + " LIMIT #{limit}",
- normalizedSql);
- }
-
- @Test
- void testCurrentTimeMillisExpression() {
- Assertions.assertEquals(
- "(UNIX_TIMESTAMP() * 1000.0)",
- new IdpUserMetaBaseSQLProvider().currentTimeMillisExpression());
- }
-
- private IdpUserMetaBaseSQLProvider createProvider() {
- return new IdpUserMetaBaseSQLProvider() {
- @Override
- protected String currentTimeMillisExpression() {
- return "CURRENT_TIME_MILLIS()";
- }
- };
- }
-
- private String renderScript(String script, Map<String, Object> params) {
- SqlSource sqlSource =
- new XMLLanguageDriver().createSqlSource(new Configuration(), script,
Map.class);
- BoundSql boundSql = sqlSource.getBoundSql(params);
- return boundSql.getSql().replaceAll("\\s+", " ").trim();
- }
-
- private IdpUserPO newUserPO() {
- return IdpUserPO.builder()
- .withUserId(1L)
- .withUserName("tom")
- .withPasswordHash("hash")
- .withCurrentVersion(1L)
- .withLastVersion(1L)
- .withDeletedAt(0L)
- .build();
- }
-}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/h2/TestIdpUserMetaH2Provider.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/h2/TestIdpUserMetaH2Provider.java
deleted file mode 100644
index ab2c5f2ea7..0000000000
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/h2/TestIdpUserMetaH2Provider.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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
- *
- * http://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 org.apache.gravitino.idp.storage.mapper.provider.h2;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-class TestIdpUserMetaH2Provider {
-
- @Test
- void testCurrentTimeMillisExpression() {
- IdpUserMetaH2Provider provider = new IdpUserMetaH2Provider();
-
- Assertions.assertEquals(
- "DATEDIFF('MILLISECOND', TIMESTAMP '1970-01-01 00:00:00',
CURRENT_TIMESTAMP())",
- provider.currentTimeMillisExpression());
- }
-}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/postgresql/TestIdpGroupMetaPostgreSQLProvider.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/postgresql/TestIdpGroupMetaPostgreSQLProvider.java
deleted file mode 100644
index bdc1b16987..0000000000
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/postgresql/TestIdpGroupMetaPostgreSQLProvider.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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
- *
- * http://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 org.apache.gravitino.idp.storage.mapper.provider.postgresql;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-public class TestIdpGroupMetaPostgreSQLProvider {
-
- @Test
- void testCurrentTimeMillisExpression() {
- IdpGroupMetaPostgreSQLProvider provider = new
IdpGroupMetaPostgreSQLProvider();
-
- Assertions.assertEquals(
- "CAST(EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) * 1000 AS BIGINT)",
- provider.currentTimeMillisExpression());
- }
-
- @Test
- void testDeleteIdpGroupMetasByLegacyTimeline() {
- IdpGroupMetaPostgreSQLProvider provider = new
IdpGroupMetaPostgreSQLProvider();
-
- Assertions.assertEquals(
- "DELETE FROM idp_group_meta WHERE group_id IN (SELECT group_id FROM
idp_group_meta"
- + " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeline} LIMIT
#{limit})",
- provider.deleteIdpGroupMetasByLegacyTimeline(1L, 2));
- }
-}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/postgresql/TestIdpUserMetaPostgreSQLProvider.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/postgresql/TestIdpUserMetaPostgreSQLProvider.java
deleted file mode 100644
index 21a16b074d..0000000000
---
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/mapper/provider/postgresql/TestIdpUserMetaPostgreSQLProvider.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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
- *
- * http://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 org.apache.gravitino.idp.storage.mapper.provider.postgresql;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-public class TestIdpUserMetaPostgreSQLProvider {
-
- @Test
- void testCurrentTimeMillisExpression() {
- IdpUserMetaPostgreSQLProvider provider = new
IdpUserMetaPostgreSQLProvider();
-
- Assertions.assertEquals(
- "CAST(EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) * 1000 AS BIGINT)",
- provider.currentTimeMillisExpression());
- }
-
- @Test
- void testDeleteIdpUserMetasByLegacyTimeline() {
- IdpUserMetaPostgreSQLProvider provider = new
IdpUserMetaPostgreSQLProvider();
-
- Assertions.assertEquals(
- "DELETE FROM idp_user_meta WHERE user_id IN (SELECT user_id FROM
idp_user_meta"
- + " WHERE deleted_at > 0 AND deleted_at < #{legacyTimeline} LIMIT
#{limit})",
- provider.deleteIdpUserMetasByLegacyTimeline(1L, 2));
- }
-}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/po/TestIdpUserGroupRelPO.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/po/TestIdpUserGroupRelPO.java
new file mode 100644
index 0000000000..4a5c34c1a7
--- /dev/null
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/storage/po/TestIdpUserGroupRelPO.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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 org.apache.gravitino.idp.storage.po;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestIdpUserGroupRelPO {
+
+ @Test
+ public void testIdpUserGroupRelPOBuilder() {
+ IdpUserGroupRelPO relPO =
+ IdpUserGroupRelPO.builder()
+ .withId(1L)
+ .withUserId(20L)
+ .withGroupId(10L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build();
+
+ Assertions.assertEquals(1L, relPO.getId());
+ Assertions.assertEquals(20L, relPO.getUserId());
+ Assertions.assertEquals(10L, relPO.getGroupId());
+ Assertions.assertEquals(1L, relPO.getCurrentVersion());
+ Assertions.assertEquals(0L, relPO.getLastVersion());
+ Assertions.assertEquals(0L, relPO.getDeletedAt());
+ }
+
+ @Test
+ public void testEqualsAndHashCode() {
+ IdpUserGroupRelPO relPO1 =
+ IdpUserGroupRelPO.builder()
+ .withId(1L)
+ .withUserId(20L)
+ .withGroupId(10L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build();
+
+ IdpUserGroupRelPO relPO2 =
+ IdpUserGroupRelPO.builder()
+ .withId(1L)
+ .withUserId(20L)
+ .withGroupId(10L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L)
+ .build();
+
+ Assertions.assertEquals(relPO1, relPO2);
+ Assertions.assertEquals(relPO1.hashCode(), relPO2.hashCode());
+ }
+
+ @Test
+ public void testBuilderReuseDoesNotMutateBuiltObject() {
+ var builder =
+ IdpUserGroupRelPO.builder()
+ .withId(1L)
+ .withUserId(20L)
+ .withGroupId(10L)
+ .withCurrentVersion(1L)
+ .withLastVersion(0L)
+ .withDeletedAt(0L);
+
+ IdpUserGroupRelPO firstRelation = builder.build();
+ IdpUserGroupRelPO secondRelation = builder.withUserId(21L).build();
+
+ Assertions.assertEquals(20L, firstRelation.getUserId());
+ Assertions.assertEquals(21L, secondRelation.getUserId());
+ }
+}
diff --git a/scripts/h2/schema-1.3.0-h2.sql b/scripts/h2/schema-1.3.0-h2.sql
index 5b11e5f882..fc6f026ec3 100644
--- a/scripts/h2/schema-1.3.0-h2.sql
+++ b/scripts/h2/schema-1.3.0-h2.sql
@@ -271,17 +271,17 @@ CREATE TABLE IF NOT EXISTS `idp_group_meta` (
CONSTRAINT `uk_ign_del` UNIQUE (`group_name`, `deleted_at`)
) ENGINE=InnoDB;
-CREATE TABLE IF NOT EXISTS `idp_group_user_rel` (
+CREATE TABLE IF NOT EXISTS `idp_user_group_rel` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
- `group_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'idp group id',
`user_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'idp user id',
+ `group_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'idp group id',
`current_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'idp relation
current version',
`last_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'idp relation last
version',
`deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'idp relation
deleted at',
PRIMARY KEY (`id`),
- CONSTRAINT `uk_igiu_del` UNIQUE (`group_id`, `user_id`, `deleted_at`),
- KEY `idx_iug_gid` (`group_id`),
- KEY `idx_iug_uid` (`user_id`)
+ CONSTRAINT `uk_iuig_del` UNIQUE (`user_id`, `group_id`, `deleted_at`),
+ KEY `idx_iuig_uid` (`user_id`),
+ KEY `idx_iuig_gid` (`group_id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `tag_meta` (
diff --git a/scripts/h2/upgrade-1.2.0-to-1.3.0-h2.sql
b/scripts/h2/upgrade-1.2.0-to-1.3.0-h2.sql
index c343bbc447..5366a16436 100644
--- a/scripts/h2/upgrade-1.2.0-to-1.3.0-h2.sql
+++ b/scripts/h2/upgrade-1.2.0-to-1.3.0-h2.sql
@@ -88,15 +88,15 @@ CREATE TABLE IF NOT EXISTS `idp_group_meta` (
CONSTRAINT `uk_ign_del` UNIQUE (`group_name`, `deleted_at`)
) ENGINE=InnoDB;
-CREATE TABLE IF NOT EXISTS `idp_group_user_rel` (
+CREATE TABLE IF NOT EXISTS `idp_user_group_rel` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
- `group_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'idp group id',
`user_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'idp user id',
+ `group_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'idp group id',
`current_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'idp relation
current version',
`last_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'idp relation last
version',
`deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'idp relation
deleted at',
PRIMARY KEY (`id`),
- CONSTRAINT `uk_igiu_del` UNIQUE (`group_id`, `user_id`, `deleted_at`),
- KEY `idx_iug_gid` (`group_id`),
- KEY `idx_iug_uid` (`user_id`)
+ CONSTRAINT `uk_iuig_del` UNIQUE (`user_id`, `group_id`, `deleted_at`),
+ KEY `idx_iuig_uid` (`user_id`),
+ KEY `idx_iuig_gid` (`group_id`)
) ENGINE=InnoDB;
diff --git a/scripts/mysql/schema-1.3.0-mysql.sql
b/scripts/mysql/schema-1.3.0-mysql.sql
index 0c1cb6b8e0..7ed2d03e17 100644
--- a/scripts/mysql/schema-1.3.0-mysql.sql
+++ b/scripts/mysql/schema-1.3.0-mysql.sql
@@ -262,18 +262,18 @@ CREATE TABLE IF NOT EXISTS `idp_group_meta` (
UNIQUE KEY `uk_ign_del` (`group_name`, `deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'local IdP
group metadata';
-CREATE TABLE IF NOT EXISTS `idp_group_user_rel` (
+CREATE TABLE IF NOT EXISTS `idp_user_group_rel` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
- `group_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'idp group id',
`user_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'idp user id',
+ `group_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'idp group id',
`current_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'idp relation
current version',
`last_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'idp relation last
version',
`deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'idp relation
deleted at',
PRIMARY KEY (`id`),
- UNIQUE KEY `uk_igiu_del` (`group_id`, `user_id`, `deleted_at`),
- KEY `idx_iug_gid` (`group_id`),
- KEY `idx_iug_uid` (`user_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'local IdP
group user relation';
+ UNIQUE KEY `uk_iuig_del` (`user_id`, `group_id`, `deleted_at`),
+ KEY `idx_iuig_uid` (`user_id`),
+ KEY `idx_iuig_gid` (`group_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'local IdP
user group relation';
CREATE TABLE IF NOT EXISTS `tag_meta` (
`tag_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'tag id',
diff --git a/scripts/mysql/upgrade-1.2.0-to-1.3.0-mysql.sql
b/scripts/mysql/upgrade-1.2.0-to-1.3.0-mysql.sql
index 623aa238f7..d31b6314b0 100644
--- a/scripts/mysql/upgrade-1.2.0-to-1.3.0-mysql.sql
+++ b/scripts/mysql/upgrade-1.2.0-to-1.3.0-mysql.sql
@@ -106,15 +106,15 @@ CREATE TABLE IF NOT EXISTS `idp_group_meta` (
UNIQUE KEY `uk_ign_del` (`group_name`, `deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'local IdP
group metadata';
-CREATE TABLE IF NOT EXISTS `idp_group_user_rel` (
+CREATE TABLE IF NOT EXISTS `idp_user_group_rel` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
- `group_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'idp group id',
`user_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'idp user id',
+ `group_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'idp group id',
`current_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'idp relation
current version',
`last_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'idp relation last
version',
`deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'idp relation
deleted at',
PRIMARY KEY (`id`),
- UNIQUE KEY `uk_igiu_del` (`group_id`, `user_id`, `deleted_at`),
- KEY `idx_iug_gid` (`group_id`),
- KEY `idx_iug_uid` (`user_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'local IdP
group user relation';
+ UNIQUE KEY `uk_iuig_del` (`user_id`, `group_id`, `deleted_at`),
+ KEY `idx_iuig_uid` (`user_id`),
+ KEY `idx_iuig_gid` (`group_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'local IdP
user group relation';
diff --git a/scripts/postgresql/schema-1.3.0-postgresql.sql
b/scripts/postgresql/schema-1.3.0-postgresql.sql
index 86a23eb5a0..b922b369da 100644
--- a/scripts/postgresql/schema-1.3.0-postgresql.sql
+++ b/scripts/postgresql/schema-1.3.0-postgresql.sql
@@ -465,26 +465,26 @@ COMMENT ON COLUMN idp_group_meta.current_version IS 'idp
group current version';
COMMENT ON COLUMN idp_group_meta.last_version IS 'idp group last version';
COMMENT ON COLUMN idp_group_meta.deleted_at IS 'idp group deleted at';
-CREATE TABLE IF NOT EXISTS idp_group_user_rel (
+CREATE TABLE IF NOT EXISTS idp_user_group_rel (
id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
- group_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
+ group_id BIGINT NOT NULL,
current_version INT NOT NULL DEFAULT 1,
last_version INT NOT NULL DEFAULT 1,
deleted_at BIGINT NOT NULL DEFAULT 0,
PRIMARY KEY (id),
- UNIQUE (group_id, user_id, deleted_at)
+ CONSTRAINT uk_iuig_del UNIQUE (user_id, group_id, deleted_at)
);
-CREATE INDEX IF NOT EXISTS idp_group_user_rel_idx_group_id ON
idp_group_user_rel (group_id);
-CREATE INDEX IF NOT EXISTS idp_group_user_rel_idx_user_id ON
idp_group_user_rel (user_id);
-COMMENT ON TABLE idp_group_user_rel IS 'local IdP group user relation';
-
-COMMENT ON COLUMN idp_group_user_rel.id IS 'auto increment id';
-COMMENT ON COLUMN idp_group_user_rel.group_id IS 'idp group id';
-COMMENT ON COLUMN idp_group_user_rel.user_id IS 'idp user id';
-COMMENT ON COLUMN idp_group_user_rel.current_version IS 'idp relation current
version';
-COMMENT ON COLUMN idp_group_user_rel.last_version IS 'idp relation last
version';
-COMMENT ON COLUMN idp_group_user_rel.deleted_at IS 'idp relation deleted at';
+CREATE INDEX IF NOT EXISTS idp_user_group_rel_idx_iuig_uid ON
idp_user_group_rel (user_id);
+CREATE INDEX IF NOT EXISTS idp_user_group_rel_idx_iuig_gid ON
idp_user_group_rel (group_id);
+COMMENT ON TABLE idp_user_group_rel IS 'local IdP user group relation';
+
+COMMENT ON COLUMN idp_user_group_rel.id IS 'auto increment id';
+COMMENT ON COLUMN idp_user_group_rel.user_id IS 'idp user id';
+COMMENT ON COLUMN idp_user_group_rel.group_id IS 'idp group id';
+COMMENT ON COLUMN idp_user_group_rel.current_version IS 'idp relation current
version';
+COMMENT ON COLUMN idp_user_group_rel.last_version IS 'idp relation last
version';
+COMMENT ON COLUMN idp_user_group_rel.deleted_at IS 'idp relation deleted at';
CREATE TABLE IF NOT EXISTS tag_meta (
tag_id BIGINT NOT NULL,
diff --git a/scripts/postgresql/upgrade-1.2.0-to-1.3.0-postgresql.sql
b/scripts/postgresql/upgrade-1.2.0-to-1.3.0-postgresql.sql
index fdcfefedb7..bf3071567d 100644
--- a/scripts/postgresql/upgrade-1.2.0-to-1.3.0-postgresql.sql
+++ b/scripts/postgresql/upgrade-1.2.0-to-1.3.0-postgresql.sql
@@ -131,23 +131,23 @@ COMMENT ON COLUMN idp_group_meta.current_version IS 'idp
group current version';
COMMENT ON COLUMN idp_group_meta.last_version IS 'idp group last version';
COMMENT ON COLUMN idp_group_meta.deleted_at IS 'idp group deleted at';
-CREATE TABLE IF NOT EXISTS idp_group_user_rel (
+CREATE TABLE IF NOT EXISTS idp_user_group_rel (
id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
- group_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
+ group_id BIGINT NOT NULL,
current_version INT NOT NULL DEFAULT 1,
last_version INT NOT NULL DEFAULT 1,
deleted_at BIGINT NOT NULL DEFAULT 0,
PRIMARY KEY (id),
- UNIQUE (group_id, user_id, deleted_at)
+ CONSTRAINT uk_iuig_del UNIQUE (user_id, group_id, deleted_at)
);
-CREATE INDEX IF NOT EXISTS idp_group_user_rel_idx_group_id ON
idp_group_user_rel (group_id);
-CREATE INDEX IF NOT EXISTS idp_group_user_rel_idx_user_id ON
idp_group_user_rel (user_id);
-COMMENT ON TABLE idp_group_user_rel IS 'local IdP group user relation';
+CREATE INDEX IF NOT EXISTS idp_user_group_rel_idx_iuig_uid ON
idp_user_group_rel (user_id);
+CREATE INDEX IF NOT EXISTS idp_user_group_rel_idx_iuig_gid ON
idp_user_group_rel (group_id);
+COMMENT ON TABLE idp_user_group_rel IS 'local IdP user group relation';
-COMMENT ON COLUMN idp_group_user_rel.id IS 'auto increment id';
-COMMENT ON COLUMN idp_group_user_rel.group_id IS 'idp group id';
-COMMENT ON COLUMN idp_group_user_rel.user_id IS 'idp user id';
-COMMENT ON COLUMN idp_group_user_rel.current_version IS 'idp relation current
version';
-COMMENT ON COLUMN idp_group_user_rel.last_version IS 'idp relation last
version';
-COMMENT ON COLUMN idp_group_user_rel.deleted_at IS 'idp relation deleted at';
+COMMENT ON COLUMN idp_user_group_rel.id IS 'auto increment id';
+COMMENT ON COLUMN idp_user_group_rel.user_id IS 'idp user id';
+COMMENT ON COLUMN idp_user_group_rel.group_id IS 'idp group id';
+COMMENT ON COLUMN idp_user_group_rel.current_version IS 'idp relation current
version';
+COMMENT ON COLUMN idp_user_group_rel.last_version IS 'idp relation last
version';
+COMMENT ON COLUMN idp_user_group_rel.deleted_at IS 'idp relation deleted at';