From 613a252552734d23d6447e7cc741b16644c0ac07 Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Thu, 4 Sep 2025 17:58:04 +0200 Subject: [PATCH 1/6] [SYNCOPE-1906] Fixing order by clause on plain attribute for mariadb and mysql --- .../jpa/dao/MariaDBJPAAnySearchDAO.java | 29 +++++++ .../jpa/dao/MySQLJPAAnySearchDAO.java | 14 +++- .../persistence/jpa/inner/AnySearchTest.java | 80 ++++++++++++++++++ .../syncope/fit/core/UserIssuesITCase.java | 84 +++++++++++++++++++ 4 files changed, 204 insertions(+), 3 deletions(-) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java index 9bef72a4aaa..d5b5f157f85 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java @@ -38,6 +38,7 @@ import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.domain.Sort; public class MariaDBJPAAnySearchDAO extends AbstractJPAAnySearchDAO { @@ -68,6 +69,34 @@ public MariaDBJPAAnySearchDAO( entityManager); } + @Override + protected void parseOrderByForPlainSchema( + final SearchSupport svs, + final OrderBySupport obs, + final OrderBySupport.Item item, + final Sort.Order clause, + final PlainSchema schema, + final String fieldName) { + + // keep track of involvement of non-mandatory schemas in the order by clauses + obs.nonMandatorySchemas = !"true".equals(schema.getMandatoryCondition()); + + obs.views.add(svs.field()); + + item.select = new StringBuilder(). + append("( SELECT usa").append('.').append(key(schema.getType())). + append(" FROM "). + append(svs.asSearchViewSupport().attr().name()). + append(" usa WHERE usa.any_id = "). + append(svs.asSearchViewSupport().attr().alias()). + append(".any_id"). + append(" AND usa.schema_id ='").append(fieldName).append("'"). + append(isOracle() ? " FETCH FIRST 1 ROWS ONLY " : " LIMIT 1"). + append(") AS ").append(fieldName).toString(); + item.where = "plainSchema = '" + fieldName + '\''; + item.orderBy = fieldName + ' ' + clause.getDirection().name(); + } + @Override protected Pair getQuery( final AttrCond cond, diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java index ca3b237fcb8..d222edc089d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java @@ -84,9 +84,17 @@ protected void parseOrderByForPlainSchema( obs.views.add(svs.field()); - item.select = svs.field().alias() + '.' - + (schema.isUniqueConstraint() ? "attrUniqueValue" : key(schema.getType())) - + " AS " + fieldName; + item.select = new StringBuilder().append("( SELECT usa"). + append('.'). + append((schema.isUniqueConstraint() ? "attrUniqueValue" : key(schema.getType()))). + append(" FROM "). + append(svs.field().name()). + append(" usa WHERE usa.any_id = "). + append(svs.field().alias()). + append(".any_id"). + append(" AND usa.plainSchema ='").append(fieldName).append("'"). + append(" LIMIT 1"). + append(") AS ").append(fieldName).toString(); item.where = "plainSchema = '" + fieldName + '\''; item.orderBy = fieldName + ' ' + clause.getDirection().name(); } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java index e1dc24f772c..efa4ca98e36 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java @@ -36,10 +36,12 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; @@ -56,6 +58,7 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.group.Group; @@ -105,6 +108,12 @@ public class AnySearchTest extends AbstractTest { @Autowired private RoleDAO roleDAO; + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private PlainAttrValidationManager validator; + @BeforeEach public void adjustLoginDateForLocalSystem() throws ParseException { User rossini = userDAO.findByUsername("rossini").orElseThrow(); @@ -948,4 +957,75 @@ public void issueSYNCOPE1419() { assertNotNull(users); assertEquals(4, users.size()); } + + @Test + public void issueSYNCOPE1906() { + User bellini = userDAO.findByUsername("bellini").orElseThrow(); + + PlainSchema ctypeSchema = plainSchemaDAO.findById("ctype").orElseThrow(); + assertNotNull(ctypeSchema); + + userDAO.save(addPlainAttr(bellini, ctypeSchema, "aa1")); + + User puccini = userDAO.findByUsername("puccini").orElseThrow(); + assertNotNull(bellini); + + userDAO.save(addPlainAttr(puccini, ctypeSchema, "aa2")); + + User verdi = userDAO.findByUsername("verdi").orElseThrow(); + assertNotNull(verdi); + + userDAO.save(addPlainAttr(verdi, ctypeSchema, "aa3")); + + User vivaldi = userDAO.findByUsername("vivaldi").orElseThrow(); + assertNotNull(vivaldi); + + userDAO.save(addPlainAttr(vivaldi, ctypeSchema, "aa4")); + + User rossini = userDAO.findByUsername("rossini").orElseThrow(); + assertNotNull(rossini); + + userDAO.save(addPlainAttr(rossini, ctypeSchema, "aa5")); + + entityManager.flush(); + + AnyCond idCond = new AnyCond(AttrCond.Type.ISNOTNULL); + idCond.setSchema("id"); + + List users = + searchDAO.search(SearchCond.of(idCond), List.of(new Sort.Order(Sort.Direction.DESC, "ctype")), + AnyTypeKind.USER); + + assertEquals("bellini", users.get(4).getUsername()); + assertEquals("puccini", users.get(3).getUsername()); + assertEquals("verdi", users.get(2).getUsername()); + assertEquals("vivaldi", users.get(1).getUsername()); + assertEquals("rossini", users.get(0).getUsername()); + + // order by ctype even not searching by it + AttrCond surnameCond = new AttrCond(AttrCond.Type.ILIKE); + surnameCond.setSchema("surname"); + surnameCond.setExpression("%ini"); + + AttrCond coolCond = new AttrCond(AttrCond.Type.ISNULL); + coolCond.setSchema("cool"); + + users = searchDAO.search(SearchCond.and(SearchCond.of(surnameCond), SearchCond.of(coolCond)), + List.of(new Sort.Order(Sort.Direction.DESC, "ctype")), AnyTypeKind.USER); + + assertFalse(users.isEmpty()); + assertEquals(2, users.size()); + } + + private User addPlainAttr(final User user, final PlainSchema plainSchema, final String value) { + user.getPlainAttr(plainSchema.getKey()) + .ifPresentOrElse(ctype -> ctype.getValues().get(0).setStringValue(value), () -> { + PlainAttr attr = new PlainAttr(); + attr.setPlainSchema(plainSchema); + attr.add(validator, value); + + user.add(attr); + }); + return user; + } } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java index e6070482743..32034e0d953 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java @@ -102,6 +102,7 @@ import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.common.lib.types.UnmatchingRule; import org.apache.syncope.common.rest.api.RESTHeaders; +import org.apache.syncope.common.rest.api.beans.AnyQuery; import org.apache.syncope.common.rest.api.beans.RealmQuery; import org.apache.syncope.common.rest.api.beans.ReconQuery; import org.apache.syncope.common.rest.api.beans.TaskQuery; @@ -1895,4 +1896,87 @@ public void issueSYNCOPE1894() { SCHEMA_SERVICE.delete(SchemaType.PLAIN, userWithDotSchema.getKey()); } } + + @Test + public void issueSYNCOPE1906() { + UserCR userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs().add(attr("ctype", "aa1")); + UserTO aa1 = createUser(userCR).getEntity(); + + userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs().add(attr("ctype", "aa2")); + UserTO aa2 = createUser(userCR).getEntity(); + + userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs().add(attr("ctype", "aa3")); + UserTO aa3 = createUser(userCR).getEntity(); + + userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs().add(attr("ctype", "aa4")); + UserTO aa4 = createUser(userCR).getEntity(); + + userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs().add(attr("ctype", "aa5")); + UserTO aa5 = createUser(userCR).getEntity(); + + // add also other users with valued ctype + userCR = UserITCase.getUniqueSample("actype@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs().add(attr("ctype", "a ctype")); + createUser(userCR); + + userCR = UserITCase.getUniqueSample("actypetwo@syncope.apache.org"); + userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs().add(attr("ctype", "a ctype 2")); + createUser(userCR); + try { + await().until(() -> USER_SERVICE.search(new AnyQuery.Builder().fiql( + SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("*issuesyncope1906*") + .query()) + .size(0) + .page(1) + .build()).getTotalCount() == 5); + List users = USER_SERVICE.search(new AnyQuery.Builder().fiql( + SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("*issuesyncope1906*") + .query()) + .size(10) + .page(1) + .orderBy("ctype DESC") + .build()).getResult(); + + assertEquals(5, users.size()); + + assertEquals(aa1.getUsername(), users.get(4).getUsername()); + assertEquals(aa2.getUsername(), users.get(3).getUsername()); + assertEquals(aa3.getUsername(), users.get(2).getUsername()); + assertEquals(aa4.getUsername(), users.get(1).getUsername()); + assertEquals(aa5.getUsername(), users.get(0).getUsername()); + + users = USER_SERVICE.search(new AnyQuery.Builder().fiql( + SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("*issuesyncope1906**") + .query()) + .size(10) + .page(1) + .orderBy("ctype ASC") + .build()).getResult(); + + assertEquals(aa5.getUsername(), users.get(4).getUsername()); + assertEquals(aa4.getUsername(), users.get(3).getUsername()); + assertEquals(aa3.getUsername(), users.get(2).getUsername()); + assertEquals(aa2.getUsername(), users.get(1).getUsername()); + assertEquals(aa1.getUsername(), users.get(0).getUsername()); + } finally { + USER_SERVICE.search(new AnyQuery.Builder().fiql( + SyncopeClient.getUserSearchConditionBuilder().is("ctype").equalTo("aa*").query()) + .size(10) + .page(1) + .orderBy("ctype DESC") + .build()).getResult().forEach(u -> deleteUser(u.getKey())); + } + } } From d9a44d63277f0e5d417ae582710583d862c07638 Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Fri, 5 Sep 2025 09:50:17 +0200 Subject: [PATCH 2/6] fixes mariadb query --- .../core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java index d5b5f157f85..7eafd740761 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java @@ -88,7 +88,7 @@ protected void parseOrderByForPlainSchema( append(" FROM "). append(svs.asSearchViewSupport().attr().name()). append(" usa WHERE usa.any_id = "). - append(svs.asSearchViewSupport().attr().alias()). + append(defaultSV(svs).alias()). append(".any_id"). append(" AND usa.schema_id ='").append(fieldName).append("'"). append(isOracle() ? " FETCH FIRST 1 ROWS ONLY " : " LIMIT 1"). From 4b3f2526386568f4e9102004dbbe524e224fdea6 Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Fri, 5 Sep 2025 11:36:51 +0200 Subject: [PATCH 3/6] fixed also order by unique attribute on mariadb --- .../jpa/dao/MariaDBJPAAnySearchDAO.java | 5 +- .../persistence/jpa/inner/AnySearchTest.java | 10 ++++ .../syncope/fit/core/UserIssuesITCase.java | 46 +++++++++++++++---- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java index 7eafd740761..34b71c3ed94 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java @@ -85,8 +85,9 @@ protected void parseOrderByForPlainSchema( item.select = new StringBuilder(). append("( SELECT usa").append('.').append(key(schema.getType())). - append(" FROM "). - append(svs.asSearchViewSupport().attr().name()). + append(" FROM ").append(schema.isUniqueConstraint() + ? svs.asSearchViewSupport().uniqueAttr().name() + : svs.asSearchViewSupport().attr().name()). append(" usa WHERE usa.any_id = "). append(defaultSV(svs).alias()). append(".any_id"). diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java index efa4ca98e36..b27d83fec63 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java @@ -1015,6 +1015,16 @@ public void issueSYNCOPE1906() { assertFalse(users.isEmpty()); assertEquals(2, users.size()); + + // order by unique attribute + users = searchDAO.search(SearchCond.of(idCond), List.of(new Sort.Order(Sort.Direction.DESC, "fullname")), + AnyTypeKind.USER); + + assertEquals("vivaldi", users.get(4).getUsername()); + assertEquals("puccini", users.get(3).getUsername()); + assertEquals("rossini", users.get(2).getUsername()); + assertEquals("verdi", users.get(1).getUsername()); + assertEquals("bellini", users.get(0).getUsername()); } private User addPlainAttr(final User user, final PlainSchema plainSchema, final String value) { diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java index 32034e0d953..c8483b884b2 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java @@ -1900,39 +1900,53 @@ public void issueSYNCOPE1894() { @Test public void issueSYNCOPE1906() { UserCR userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); - userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs() + .removeIf(attr -> "ctype".equals(attr.getSchema()) || "fullname".equals(attr.getSchema())); userCR.getPlainAttrs().add(attr("ctype", "aa1")); + userCR.getPlainAttrs().add(attr("fullname", "aa1")); UserTO aa1 = createUser(userCR).getEntity(); userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); - userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs() + .removeIf(attr -> "ctype".equals(attr.getSchema()) || "fullname".equals(attr.getSchema())); userCR.getPlainAttrs().add(attr("ctype", "aa2")); + userCR.getPlainAttrs().add(attr("fullname", "aa2")); UserTO aa2 = createUser(userCR).getEntity(); userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); - userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs() + .removeIf(attr -> "ctype".equals(attr.getSchema()) || "fullname".equals(attr.getSchema())); userCR.getPlainAttrs().add(attr("ctype", "aa3")); + userCR.getPlainAttrs().add(attr("fullname", "aa3")); UserTO aa3 = createUser(userCR).getEntity(); userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); - userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs() + .removeIf(attr -> "ctype".equals(attr.getSchema()) || "fullname".equals(attr.getSchema())); userCR.getPlainAttrs().add(attr("ctype", "aa4")); + userCR.getPlainAttrs().add(attr("fullname", "aa4")); UserTO aa4 = createUser(userCR).getEntity(); userCR = UserITCase.getUniqueSample("issuesyncope1906@syncope.apache.org"); - userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs() + .removeIf(attr -> "ctype".equals(attr.getSchema()) || "fullname".equals(attr.getSchema())); userCR.getPlainAttrs().add(attr("ctype", "aa5")); + userCR.getPlainAttrs().add(attr("fullname", "aa5")); UserTO aa5 = createUser(userCR).getEntity(); // add also other users with valued ctype userCR = UserITCase.getUniqueSample("actype@syncope.apache.org"); - userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs() + .removeIf(attr -> "ctype".equals(attr.getSchema()) || "fullname".equals(attr.getSchema())); userCR.getPlainAttrs().add(attr("ctype", "a ctype")); + userCR.getPlainAttrs().add(attr("fullname", "a fullname")); createUser(userCR); userCR = UserITCase.getUniqueSample("actypetwo@syncope.apache.org"); - userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema())); + userCR.getPlainAttrs() + .removeIf(attr -> "ctype".equals(attr.getSchema()) || "fullname".equals(attr.getSchema())); userCR.getPlainAttrs().add(attr("ctype", "a ctype 2")); + userCR.getPlainAttrs().add(attr("fullname", "a fullname 2")); createUser(userCR); try { await().until(() -> USER_SERVICE.search(new AnyQuery.Builder().fiql( @@ -1958,7 +1972,7 @@ public void issueSYNCOPE1906() { assertEquals(aa5.getUsername(), users.get(0).getUsername()); users = USER_SERVICE.search(new AnyQuery.Builder().fiql( - SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("*issuesyncope1906**") + SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("*issuesyncope1906*") .query()) .size(10) .page(1) @@ -1970,6 +1984,22 @@ public void issueSYNCOPE1906() { assertEquals(aa3.getUsername(), users.get(2).getUsername()); assertEquals(aa2.getUsername(), users.get(1).getUsername()); assertEquals(aa1.getUsername(), users.get(0).getUsername()); + + // order by unique attribute + users = USER_SERVICE.search(new AnyQuery.Builder().fiql( + SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("*issuesyncope1906*") + .query()) + .size(10) + .page(1) + .orderBy("fullname ASC") + .build()).getResult(); + + assertEquals(aa5.getUsername(), users.get(4).getUsername()); + assertEquals(aa4.getUsername(), users.get(3).getUsername()); + assertEquals(aa3.getUsername(), users.get(2).getUsername()); + assertEquals(aa2.getUsername(), users.get(1).getUsername()); + assertEquals(aa1.getUsername(), users.get(0).getUsername()); + } finally { USER_SERVICE.search(new AnyQuery.Builder().fiql( SyncopeClient.getUserSearchConditionBuilder().is("ctype").equalTo("aa*").query()) From 75ab7b6d4e8e1c2a0e55476a56c504d7500ebce3 Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Fri, 5 Sep 2025 11:39:19 +0200 Subject: [PATCH 4/6] fixes checkstyle --- .../syncope/fit/core/UserIssuesITCase.java | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java index c8483b884b2..e10c4ac3106 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java @@ -1957,11 +1957,7 @@ public void issueSYNCOPE1906() { .build()).getTotalCount() == 5); List users = USER_SERVICE.search(new AnyQuery.Builder().fiql( SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("*issuesyncope1906*") - .query()) - .size(10) - .page(1) - .orderBy("ctype DESC") - .build()).getResult(); + .query()).size(10).page(1).orderBy("ctype DESC").build()).getResult(); assertEquals(5, users.size()); @@ -1973,33 +1969,25 @@ public void issueSYNCOPE1906() { users = USER_SERVICE.search(new AnyQuery.Builder().fiql( SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("*issuesyncope1906*") - .query()) - .size(10) - .page(1) - .orderBy("ctype ASC") - .build()).getResult(); + .query()).size(10).page(1).orderBy("ctype ASC").build()).getResult(); assertEquals(aa5.getUsername(), users.get(4).getUsername()); assertEquals(aa4.getUsername(), users.get(3).getUsername()); assertEquals(aa3.getUsername(), users.get(2).getUsername()); assertEquals(aa2.getUsername(), users.get(1).getUsername()); assertEquals(aa1.getUsername(), users.get(0).getUsername()); - + // order by unique attribute users = USER_SERVICE.search(new AnyQuery.Builder().fiql( SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("*issuesyncope1906*") - .query()) - .size(10) - .page(1) - .orderBy("fullname ASC") - .build()).getResult(); + .query()).size(10).page(1).orderBy("fullname ASC").build()).getResult(); assertEquals(aa5.getUsername(), users.get(4).getUsername()); assertEquals(aa4.getUsername(), users.get(3).getUsername()); assertEquals(aa3.getUsername(), users.get(2).getUsername()); assertEquals(aa2.getUsername(), users.get(1).getUsername()); assertEquals(aa1.getUsername(), users.get(0).getUsername()); - + } finally { USER_SERVICE.search(new AnyQuery.Builder().fiql( SyncopeClient.getUserSearchConditionBuilder().is("ctype").equalTo("aa*").query()) From 179949929fba337b7b4f47177a89611aee04fc7d Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Fri, 5 Sep 2025 15:26:54 +0200 Subject: [PATCH 5/6] fixing mysql unique attribute search --- .../core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java index d222edc089d..4a5e4970677 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java @@ -84,9 +84,9 @@ protected void parseOrderByForPlainSchema( obs.views.add(svs.field()); - item.select = new StringBuilder().append("( SELECT usa"). - append('.'). - append((schema.isUniqueConstraint() ? "attrUniqueValue" : key(schema.getType()))). + item.select = new StringBuilder().append(schema.isUniqueConstraint() + ? "( SELECT JSON_UNQUOTE(JSON_EXTRACT(usa.attrUniqueValue, '$. " + key(schema.getType()) + "')) " + : "( SELECT usa." + key(schema.getType())). append(" FROM "). append(svs.field().name()). append(" usa WHERE usa.any_id = "). From 61d379f8ef9ecefeb2d43d50d3e6968972213acf Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Fri, 5 Sep 2025 16:43:25 +0200 Subject: [PATCH 6/6] mariadb is never oracle --- .../core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java index 34b71c3ed94..148f52ac002 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java @@ -92,7 +92,7 @@ protected void parseOrderByForPlainSchema( append(defaultSV(svs).alias()). append(".any_id"). append(" AND usa.schema_id ='").append(fieldName).append("'"). - append(isOracle() ? " FETCH FIRST 1 ROWS ONLY " : " LIMIT 1"). + append(" LIMIT 1"). append(") AS ").append(fieldName).toString(); item.where = "plainSchema = '" + fieldName + '\''; item.orderBy = fieldName + ' ' + clause.getDirection().name();