Skip to content

Commit eb74ddb

Browse files
committed
[SYNCOPE-1930] Finally exploiting MariaDB with JSON (#1238)
1 parent 84bac9c commit eb74ddb

File tree

9 files changed

+46
-222
lines changed

9 files changed

+46
-222
lines changed

core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java

Lines changed: 1 addition & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,17 @@
2020

2121
import jakarta.persistence.EntityManager;
2222
import jakarta.persistence.EntityManagerFactory;
23-
import java.time.format.DateTimeFormatter;
24-
import java.util.List;
25-
import java.util.Optional;
2623
import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager;
2724
import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
2825
import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
2926
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
3027
import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
3128
import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO;
3229
import org.apache.syncope.core.persistence.api.dao.UserDAO;
33-
import org.apache.syncope.core.persistence.api.dao.search.AttrCond;
3430
import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
3531
import org.apache.syncope.core.persistence.api.entity.EntityFactory;
36-
import org.apache.syncope.core.persistence.api.entity.PlainAttr;
37-
import org.apache.syncope.core.persistence.api.entity.PlainSchema;
38-
import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
39-
import org.springframework.data.domain.Sort;
4032

41-
public class MariaDBJPAAnySearchDAO extends AbstractJPAAnySearchDAO {
33+
public class MariaDBJPAAnySearchDAO extends MySQLJPAAnySearchDAO {
4234

4335
public MariaDBJPAAnySearchDAO(
4436
final RealmSearchDAO realmSearchDAO,
@@ -66,96 +58,4 @@ public MariaDBJPAAnySearchDAO(
6658
entityManagerFactory,
6759
entityManager);
6860
}
69-
70-
@Override
71-
protected void parseOrderByForPlainSchema(
72-
final SearchSupport svs,
73-
final OrderBySupport obs,
74-
final OrderBySupport.Item item,
75-
final Sort.Order clause,
76-
final PlainSchema schema,
77-
final String fieldName) {
78-
79-
// keep track of involvement of non-mandatory schemas in the order by clauses
80-
obs.nonMandatorySchemas = !"true".equals(schema.getMandatoryCondition());
81-
82-
obs.views.add(svs.field());
83-
84-
item.select = new StringBuilder().
85-
append("( SELECT usa").append('.').append(key(schema.getType())).
86-
append(" FROM ").append(schema.isUniqueConstraint()
87-
? svs.asSearchViewSupport().uniqueAttr().name()
88-
: svs.asSearchViewSupport().attr().name()).
89-
append(" usa WHERE usa.any_id = ").
90-
append(defaultSV(svs).alias()).
91-
append(".any_id").
92-
append(" AND usa.schema_id ='").append(fieldName).append("'").
93-
append(" LIMIT 1").
94-
append(") AS ").append(fieldName).toString();
95-
item.where = "plainSchema = '" + fieldName + '\'';
96-
item.orderBy = fieldName + ' ' + clause.getDirection().name();
97-
}
98-
99-
@Override
100-
protected AttrCondQuery getQuery(
101-
final AttrCond cond,
102-
final boolean not,
103-
final CheckResult<AttrCond> checked,
104-
final List<Object> parameters,
105-
final SearchSupport svs) {
106-
107-
// normalize NULL / NOT NULL checks
108-
if (not) {
109-
if (cond.getType() == AttrCond.Type.ISNULL) {
110-
cond.setType(AttrCond.Type.ISNOTNULL);
111-
} else if (cond.getType() == AttrCond.Type.ISNOTNULL) {
112-
cond.setType(AttrCond.Type.ISNULL);
113-
}
114-
}
115-
116-
switch (cond.getType()) {
117-
case ISNOTNULL -> {
118-
return new AttrCondQuery(true, new AnySearchNode.Leaf(
119-
svs.field(),
120-
"JSON_SEARCH("
121-
+ "plainAttrs, 'one', '" + checked.schema().getKey() + "', NULL, '$[*].schema'"
122-
+ ") IS NOT NULL"));
123-
}
124-
125-
case ISNULL -> {
126-
return new AttrCondQuery(true, new AnySearchNode.Leaf(
127-
svs.field(),
128-
"JSON_SEARCH("
129-
+ "plainAttrs, 'one', '" + checked.schema().getKey() + "', NULL, '$[*].schema'"
130-
+ ") IS NULL"));
131-
}
132-
133-
default -> {
134-
if (!not && cond.getType() == AttrCond.Type.EQ) {
135-
PlainAttr container = new PlainAttr();
136-
container.setPlainSchema(checked.schema());
137-
if (checked.schema().isUniqueConstraint()) {
138-
container.setUniqueValue(checked.value());
139-
} else {
140-
container.add(checked.value());
141-
}
142-
143-
return new AttrCondQuery(true, new AnySearchNode.Leaf(
144-
svs.field(),
145-
"JSON_CONTAINS("
146-
+ "plainAttrs, '" + POJOHelper.serialize(List.of(container)).replace("'", "''")
147-
+ "')"));
148-
} else {
149-
Optional.ofNullable(checked.value().getDateValue()).
150-
map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::format).
151-
ifPresent(formatted -> {
152-
checked.value().setDateValue(null);
153-
checked.value().setStringValue(formatted);
154-
});
155-
156-
return super.getQuery(cond, not, checked, parameters, svs);
157-
}
158-
}
159-
}
160-
}
16161
}

core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ protected void parseOrderByForPlainSchema(
8484
obs.views.add(svs.field());
8585

8686
item.select = new StringBuilder().append(schema.isUniqueConstraint()
87-
? "( SELECT JSON_UNQUOTE(JSON_EXTRACT(usa.attrUniqueValue, '$. " + key(schema.getType()) + "')) "
87+
? "( SELECT JSON_UNQUOTE(JSON_EXTRACT(usa.attrUniqueValue, '$." + key(schema.getType()) + "')) "
8888
: "( SELECT usa." + key(schema.getType())).
8989
append(" FROM ").
9090
append(svs.field().name()).
@@ -119,7 +119,7 @@ protected AnySearchNode.Leaf filJSONAttrQuery(
119119
append(" AND ").
120120
append(lower ? "LOWER(" : "").
121121
append(schema.isUniqueConstraint()
122-
? "attrUniqueValue ->> '$." + key + '\''
122+
? "JSON_UNQUOTE(JSON_EXTRACT(attrUniqueValue, '$." + key + "'))"
123123
: key).
124124
append(lower ? ')' : "");
125125

core/persistence-jpa/src/main/resources/META-INF/mariadb/views.xml

Lines changed: 31 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -49,40 +49,19 @@ under the License.
4949
<!-- user -->
5050
<entry key="user_search">
5151
CREATE VIEW user_search AS
52-
53-
SELECT u.id as any_id, u.* FROM SyncopeUser u
54-
</entry>
55-
<entry key="user_search_unique_attr">
56-
CREATE VIEW user_search_unique_attr AS
57-
58-
SELECT u.id as any_id, attrs.*
59-
FROM SyncopeUser u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[]'), '$[*]' COLUMNS (
60-
schema_id VARCHAR(255) PATH '$.schema',
61-
NESTED PATH '$.uniqueValue' COLUMNS (
62-
booleanvalue INT PATH '$.booleanValue',
63-
datevalue VARCHAR(32) PATH '$.dateValue',
64-
doublevalue DOUBLE PATH '$.doubleValue',
65-
longvalue BIGINT(20) PATH '$.longValue',
66-
stringvalue VARCHAR(255) PATH '$.stringValue'))
67-
) AS attrs ON 1=1
68-
WHERE schema_id IS NOT NULL
69-
AND (booleanvalue IS NOT NULL OR datevalue IS NOT NULL OR doublevalue IS NOT NULL OR longvalue IS NOT NULL OR stringvalue IS NOT NULL)
70-
</entry>
71-
<entry key="user_search_attr">
72-
CREATE VIEW user_search_attr AS
73-
74-
SELECT u.id as any_id, attrs.*
75-
FROM SyncopeUser u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[]'), '$[*]' COLUMNS (
76-
schema_id VARCHAR(255) PATH '$.schema',
52+
53+
SELECT u.id as any_id, u.*, attrs.*
54+
FROM SyncopeUser u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[{}]'), '$[*]' COLUMNS (
55+
plainSchema VARCHAR(255) PATH '$.schema',
7756
NESTED PATH '$.values[*]' COLUMNS (
78-
booleanvalue INT PATH '$.booleanValue',
79-
datevalue VARCHAR(32) PATH '$.dateValue',
80-
doublevalue DOUBLE PATH '$.doubleValue',
81-
longvalue BIGINT(20) PATH '$.longValue',
82-
stringvalue VARCHAR(255) PATH '$.stringValue'))
57+
binaryValue LONGBLOB PATH '$.binaryValue',
58+
booleanValue INT PATH '$.booleanValue',
59+
dateValue VARCHAR(32) PATH '$.dateValue',
60+
doubleValue DOUBLE PATH '$.doubleValue',
61+
longValue BIGINT(20) PATH '$.longValue',
62+
stringValue VARCHAR(255) PATH '$.stringValue'),
63+
attrUniqueValue JSON PATH '$.uniqueValue')
8364
) AS attrs ON 1=1
84-
WHERE schema_id IS NOT NULL
85-
AND (booleanvalue IS NOT NULL OR datevalue IS NOT NULL OR doublevalue IS NOT NULL OR longvalue IS NOT NULL OR stringvalue IS NOT NULL)
8665
</entry>
8766
<entry key="user_search_urelationship">
8867
CREATE VIEW user_search_urelationship AS
@@ -127,39 +106,18 @@ under the License.
127106
<entry key="anyObject_search">
128107
CREATE VIEW anyObject_search AS
129108

130-
SELECT a.id as any_id, a.* FROM AnyObject a
131-
</entry>
132-
<entry key="anyObject_search_unique_attr">
133-
CREATE VIEW anyObject_search_unique_attr AS
134-
135-
SELECT u.id as any_id, attrs.*
136-
FROM AnyObject u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[]'), '$[*]' COLUMNS (
137-
schema_id VARCHAR(255) PATH '$.schema',
138-
NESTED PATH '$.uniqueValue' COLUMNS (
139-
booleanvalue INT PATH '$.booleanValue',
140-
datevalue VARCHAR(32) PATH '$.dateValue',
141-
doublevalue DOUBLE PATH '$.doubleValue',
142-
longvalue BIGINT(20) PATH '$.longValue',
143-
stringvalue VARCHAR(255) PATH '$.stringValue'))
144-
) AS attrs ON 1=1
145-
WHERE schema_id IS NOT NULL
146-
AND (booleanvalue IS NOT NULL OR datevalue IS NOT NULL OR doublevalue IS NOT NULL OR longvalue IS NOT NULL OR stringvalue IS NOT NULL)
147-
</entry>
148-
<entry key="anyObject_search_attr">
149-
CREATE VIEW anyObject_search_attr AS
150-
151-
SELECT u.id as any_id, attrs.*
152-
FROM AnyObject u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[]'), '$[*]' COLUMNS (
153-
schema_id VARCHAR(255) PATH '$.schema',
109+
SELECT a.id as any_id, a.*, attrs.*
110+
FROM AnyObject a LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[{}]'), '$[*]' COLUMNS (
111+
plainSchema VARCHAR(255) PATH '$.schema',
154112
NESTED PATH '$.values[*]' COLUMNS (
155-
booleanvalue INT PATH '$.booleanValue',
156-
datevalue VARCHAR(32) PATH '$.dateValue',
157-
doublevalue DOUBLE PATH '$.doubleValue',
158-
longvalue BIGINT(20) PATH '$.longValue',
159-
stringvalue VARCHAR(255) PATH '$.stringValue'))
113+
binaryValue LONGBLOB PATH '$.binaryValue',
114+
booleanValue INT PATH '$.booleanValue',
115+
dateValue VARCHAR(32) PATH '$.dateValue',
116+
doubleValue DOUBLE PATH '$.doubleValue',
117+
longValue BIGINT(20) PATH '$.longValue',
118+
stringValue VARCHAR(255) PATH '$.stringValue'),
119+
attrUniqueValue JSON PATH '$.uniqueValue')
160120
) AS attrs ON 1=1
161-
WHERE schema_id IS NOT NULL
162-
AND (booleanvalue IS NOT NULL OR datevalue IS NOT NULL OR doublevalue IS NOT NULL OR longvalue IS NOT NULL OR stringvalue IS NOT NULL)
163121
</entry>
164122
<entry key="anyObject_search_arelationship">
165123
CREATE VIEW anyObject_search_arelationship AS
@@ -198,39 +156,18 @@ under the License.
198156
<entry key="group_search">
199157
CREATE VIEW group_search AS
200158

201-
SELECT r.id as any_id, r.* FROM SyncopeGroup r
202-
</entry>
203-
<entry key="group_search_unique_attr">
204-
CREATE VIEW group_search_unique_attr AS
205-
206-
SELECT u.id as any_id, attrs.*
207-
FROM SyncopeGroup u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[]'), '$[*]' COLUMNS (
208-
schema_id VARCHAR(255) PATH '$.schema',
209-
NESTED PATH '$.uniqueValue' COLUMNS (
210-
booleanvalue INT PATH '$.booleanValue',
211-
datevalue VARCHAR(32) PATH '$.dateValue',
212-
doublevalue DOUBLE PATH '$.doubleValue',
213-
longvalue BIGINT(20) PATH '$.longValue',
214-
stringvalue VARCHAR(255) PATH '$.stringValue'))
215-
) AS attrs ON 1=1
216-
WHERE schema_id IS NOT NULL
217-
AND (booleanvalue IS NOT NULL OR datevalue IS NOT NULL OR doublevalue IS NOT NULL OR longvalue IS NOT NULL OR stringvalue IS NOT NULL)
218-
</entry>
219-
<entry key="group_search_attr">
220-
CREATE VIEW group_search_attr AS
221-
222-
SELECT u.id as any_id, attrs.*
223-
FROM SyncopeGroup u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[]'), '$[*]' COLUMNS (
224-
schema_id VARCHAR(255) PATH '$.schema',
159+
SELECT g.id as any_id, g.*, attrs.*
160+
FROM SyncopeGroup g LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[{}]'), '$[*]' COLUMNS (
161+
plainSchema VARCHAR(255) PATH '$.schema',
225162
NESTED PATH '$.values[*]' COLUMNS (
226-
booleanvalue INT PATH '$.booleanValue',
227-
datevalue VARCHAR(32) PATH '$.dateValue',
228-
doublevalue DOUBLE PATH '$.doubleValue',
229-
longvalue BIGINT(20) PATH '$.longValue',
230-
stringvalue VARCHAR(255) PATH '$.stringValue'))
163+
binaryValue LONGBLOB PATH '$.binaryValue',
164+
booleanValue INT PATH '$.booleanValue',
165+
dateValue VARCHAR(32) PATH '$.dateValue',
166+
doubleValue DOUBLE PATH '$.doubleValue',
167+
longValue BIGINT(20) PATH '$.longValue',
168+
stringValue VARCHAR(255) PATH '$.stringValue'),
169+
attrUniqueValue JSON PATH '$.uniqueValue')
231170
) AS attrs ON 1=1
232-
WHERE schema_id IS NOT NULL
233-
AND (booleanvalue IS NOT NULL OR datevalue IS NOT NULL OR doublevalue IS NOT NULL OR longvalue IS NOT NULL OR stringvalue IS NOT NULL)
234171
</entry>
235172
<entry key="group_search_grelationship">
236173
CREATE VIEW group_search_grelationship AS

core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/AbstractTest.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,6 @@ private static boolean classExists(final String name) {
164164
withReuse(true);
165165
twoDomain.start();
166166
JDBC2_URL_SUPPLIER = twoDomain::getJdbcUrl;
167-
168-
// https://jira.mariadb.org/browse/MDEV-27898
169-
DB_USER_SUPPLIER = () -> "root";
170-
DB_PWD_SUPPLIER = () -> "syncope";
171-
DB2_USER_SUPPLIER = () -> "root";
172-
DB2_PWD_SUPPLIER = () -> "syncope";
173167
} else if (classExists("oracle.jdbc.OracleDriver")) {
174168
JDBC_DRIVER = "oracle.jdbc.OracleDriver";
175169
DATABASE_PLATFORM = "org.apache.openjpa.jdbc.sql.OracleDictionary";

docker/core/src/main/resources/core-mariadb.properties

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,8 @@ persistence.viewsXML=classpath:META-INF/mariadb/views.xml
2121
persistence.domain[0].key=Master
2222
persistence.domain[0].jdbcDriver=org.mariadb.jdbc.Driver
2323
persistence.domain[0].jdbcURL=${DB_URL}
24-
# keep the next two lines until https://jira.mariadb.org/browse/MDEV-27898 is fixed
25-
persistence.domain[0].dbUsername=root
26-
persistence.domain[0].dbPassword=password
27-
#persistence.domain[0].dbUsername=${DB_USER}
28-
#persistence.domain[0].dbPassword=${DB_PASSWORD}
24+
persistence.domain[0].dbUsername=${DB_USER}
25+
persistence.domain[0].dbPassword=${DB_PASSWORD}
2926
persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
3027
persistence.domain[0].orm=META-INF/mariadb/spring-orm.xml
3128
persistence.domain[0].poolMaxActive=${DB_POOL_MAX}

docker/src/main/resources/docker-compose/docker-compose-mariadb.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
services:
2121
db:
22-
image: mariadb:12
22+
image: mariadb:11
2323
restart: always
2424
command: ['--character-set-server=utf8mb4', '--collation-server=utf8mb4_bin']
2525
environment:

fit/core-reference/src/main/resources/core-mariadb.properties

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ persistence.viewsXML=classpath:META-INF/mariadb/views.xml
2323
persistence.domain[0].key=Master
2424
persistence.domain[0].jdbcDriver=org.mariadb.jdbc.Driver
2525
persistence.domain[0].jdbcURL=jdbc:mariadb://${DB_CONTAINER_IP}:3306/syncope?characterEncoding=UTF-8
26-
# keep the next two lines until https://jira.mariadb.org/browse/MDEV-27898 is fixed
27-
persistence.domain[0].dbUsername=root
28-
persistence.domain[0].dbPassword=password
26+
persistence.domain[0].dbUsername=syncope
27+
persistence.domain[0].dbPassword=syncope
2928
persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
3029
persistence.domain[0].orm=META-INF/mariadb/spring-orm.xml
3130
persistence.domain[0].poolMaxActive=10
@@ -34,9 +33,8 @@ persistence.domain[0].poolMinIdle=2
3433
persistence.domain[1].key=Two
3534
persistence.domain[1].jdbcDriver=org.mariadb.jdbc.Driver
3635
persistence.domain[1].jdbcURL=jdbc:mariadb://${DB_CONTAINER_IP}:3306/syncopetwo?characterEncoding=UTF-8
37-
# keep the next two lines until https://jira.mariadb.org/browse/MDEV-27898 is fixed
38-
persistence.domain[1].dbUsername=root
39-
persistence.domain[1].dbPassword=password
36+
persistence.domain[1].dbUsername=syncopetwo
37+
persistence.domain[1].dbPassword=syncopetwo
4038
persistence.domain[1].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
4139
persistence.domain[1].orm=META-INF/mariadb/spring-orm.xml
4240
persistence.domain[1].poolMaxActive=20

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ under the License.
530530

531531
<docker.postgresql.version>18-alpine</docker.postgresql.version>
532532
<docker.mysql.version>9.0</docker.mysql.version>
533-
<docker.mariadb.version>12</docker.mariadb.version>
533+
<docker.mariadb.version>11</docker.mariadb.version>
534534
<docker.oracle.version>23-slim-faststart</docker.oracle.version>
535535
<docker.neo4j.version>5.26</docker.neo4j.version>
536536

@@ -1847,7 +1847,7 @@ under the License.
18471847
<plugin>
18481848
<groupId>io.fabric8</groupId>
18491849
<artifactId>docker-maven-plugin</artifactId>
1850-
<version>0.47.0</version>
1850+
<version>0.48-SNAPSHOT</version>
18511851
</plugin>
18521852

18531853
<plugin>

src/main/asciidoc/reference-guide/configuration/dbms.adoc

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,8 @@ persistence.viewsXML=classpath:META-INF/mariadb/views.xml
9898
persistence.domain[0].key=Master
9999
persistence.domain[0].jdbcDriver=org.mariadb.jdbc.Driver
100100
persistence.domain[0].jdbcURL=jdbc:mariadb://localhost:3306/syncope?characterEncoding=UTF-8
101-
# keep the next two lines until https://jira.mariadb.org/browse/MDEV-27898 is fixed
102-
persistence.domain[0].dbUsername=root
103-
persistence.domain[0].dbPassword=password
101+
persistence.domain[0].dbUsername=syncope
102+
persistence.domain[0].dbPassword=syncope
104103
persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3,useSetStringForClobs=true)
105104
persistence.domain[0].orm=META-INF/mariadb/spring-orm.xml
106105
persistence.domain[0].poolMaxActive=20
@@ -129,8 +128,7 @@ https://mariadb.com/kb/en/configuring-mariadb-with-option-files/[option file^].
129128

130129
[WARNING]
131130
This assumes that you have a MariaDB instance running on localhost, listening on its default port 3306 with a database
132-
`syncope` and super-admin user `root` with password `password`. +
133-
Super-admin user is required until https://jira.mariadb.org/browse/MDEV-27898[this bug^] is fixed.
131+
`syncope` fully accessible by user `syncope` with password `syncope`.
134132

135133
==== Oracle Database
136134

0 commit comments

Comments
 (0)