This is an automated email from the ASF dual-hosted git repository.

ntimofeev pushed a commit to branch STABLE-4.2
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/STABLE-4.2 by this push:
     new 336bcf16a CAY-2895 Incorrect Lazy Pagination Comparison for BigInteger 
PK
336bcf16a is described below

commit 336bcf16a46490c90257a4959fa6a5e6d5f9c551
Author: Nikita Timofeev <[email protected]>
AuthorDate: Tue Oct 7 15:40:10 2025 +0400

    CAY-2895 Incorrect Lazy Pagination Comparison for BigInteger PK
---
 .../translator/select/BaseColumnExtractor.java     |  4 +-
 .../translator/select/IdColumnExtractor.java       | 13 +++-
 .../DataContextEntityWithMeaningfulPKIT.java       | 60 +++++++++++++++
 .../testdo/meaningful_pk/MeaningfulPkBigint.java}  | 24 +-----
 .../meaningful_pk/auto/_MeaningfulPkBigint.java    | 88 ++++++++++++++++++++++
 .../src/test/resources/meaningful-pk.map.xml       |  6 ++
 6 files changed, 172 insertions(+), 23 deletions(-)

diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseColumnExtractor.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseColumnExtractor.java
index de8a1bc44..7b0fee15e 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseColumnExtractor.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseColumnExtractor.java
@@ -35,10 +35,10 @@ abstract class BaseColumnExtractor implements 
ColumnExtractor {
         this.context = context;
     }
 
-    protected void addDbAttribute(String prefix, String labelPrefix, 
DbAttribute dba) {
+    protected ResultNodeDescriptor addDbAttribute(String prefix, String 
labelPrefix, DbAttribute dba) {
         String alias = context.getTableTree().aliasForPath(prefix);
         String dataRowKey = labelPrefix != null ? labelPrefix + '.' + 
dba.getName() : dba.getName();
         Node columnNode = table(alias).column(dba).build();
-        context.addResultNode(columnNode, dataRowKey).setDbAttribute(dba);
+        return context.addResultNode(columnNode, 
dataRowKey).setDbAttribute(dba);
     }
 }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/IdColumnExtractor.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/IdColumnExtractor.java
index 3f237c08a..3c3f536ab 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/IdColumnExtractor.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/IdColumnExtractor.java
@@ -22,6 +22,7 @@ package org.apache.cayenne.access.translator.select;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.EntityResult;
+import org.apache.cayenne.map.ObjAttribute;
 import org.apache.cayenne.map.ObjEntity;
 
 /**
@@ -30,15 +31,18 @@ import org.apache.cayenne.map.ObjEntity;
 class IdColumnExtractor extends BaseColumnExtractor {
 
     private final DbEntity dbEntity;
+    private ObjEntity objEntity;
     private EntityResult result;
 
     IdColumnExtractor(TranslatorContext context, DbEntity dbEntity) {
         super(context);
         this.dbEntity = dbEntity;
+        this.objEntity = null;
     }
 
     IdColumnExtractor(TranslatorContext context, ObjEntity objEntity) {
         this(context, objEntity.getDbEntity());
+        this.objEntity = objEntity;
         if(context.getQuery().needsResultSetMapping()) {
             this.result = new EntityResult(objEntity.getName());
         }
@@ -47,10 +51,17 @@ class IdColumnExtractor extends BaseColumnExtractor {
     @Override
     public void extract(String prefix) {
         for (DbAttribute dba : dbEntity.getPrimaryKeys()) {
-            addDbAttribute(prefix, prefix, dba);
+            ResultNodeDescriptor resultNodeDescriptor = addDbAttribute(prefix, 
prefix, dba);
             if(result != null) {
                 result.addDbField(dba.getName(), prefix + dba.getName());
             }
+            if(objEntity != null) {
+                // redefine PK type if there's a corresponding ObjAttribute 
for it
+                ObjAttribute meaningfulPK = 
objEntity.getAttributeForDbAttribute(dba);
+                if(meaningfulPK != null) {
+                    resultNodeDescriptor.setJavaType(meaningfulPK.getType());
+                }
+            }
         }
         if(result != null) {
             context.getSqlResult().addEntityResult(result);
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKIT.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKIT.java
index 770072f7c..16198a384 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKIT.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextEntityWithMeaningfulPKIT.java
@@ -31,6 +31,7 @@ import org.apache.cayenne.query.ObjectSelect;
 import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPKDep;
 import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPKTest1;
 import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPk;
+import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkBigint;
 import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkDep2;
 import org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkTest2;
 import org.apache.cayenne.unit.di.server.CayenneProjects;
@@ -39,6 +40,7 @@ import org.apache.cayenne.unit.di.server.UseServerRuntime;
 import org.junit.Ignore;
 import org.junit.Test;
 
+import java.math.BigInteger;
 import java.util.List;
 import java.util.Map;
 
@@ -270,4 +272,62 @@ public class DataContextEntityWithMeaningfulPKIT extends 
ServerCase {
         obj.setPkAttribute(1002);
         context.commitChanges();
     }
+
+    @Test
+    public void testPaginatedQuery() {
+        MeaningfulPk pkObj = context.newObject(MeaningfulPk.class);
+        pkObj.setPk("123");
+        context.commitChanges();
+
+        MeaningfulPk pkObj2 = context.newObject(MeaningfulPk.class);
+        pkObj2.setPk("124");
+        context.commitChanges();
+
+        MeaningfulPk pkObj3 = context.newObject(MeaningfulPk.class);
+        pkObj3.setPk("125");
+        context.commitChanges();
+
+        ObjectContext cleanContext = runtime.newContext();
+
+        List<MeaningfulPk> select = ObjectSelect.query(MeaningfulPk.class)
+                .orderBy(MeaningfulPk.PK.asc())
+                .pageSize(1)
+                .select(cleanContext);
+
+        assertEquals(3, select.size());
+        for(MeaningfulPk pk : select) {
+            assertNotNull(pk.getPk());
+            assertTrue(pk.getPk().startsWith("12"));
+        }
+    }
+
+
+    @Test
+    public void testPaginatedQueryBigInteger() {
+        MeaningfulPkBigint pkObj = context.newObject(MeaningfulPkBigint.class);
+        pkObj.setPk(BigInteger.valueOf(123));
+        context.commitChanges();
+
+        MeaningfulPkBigint pkObj2 = 
context.newObject(MeaningfulPkBigint.class);
+        pkObj2.setPk(BigInteger.valueOf(124));
+        context.commitChanges();
+
+        MeaningfulPkBigint pkObj3 = 
context.newObject(MeaningfulPkBigint.class);
+        pkObj3.setPk(BigInteger.valueOf(125));
+        context.commitChanges();
+
+        ObjectContext cleanContext = runtime.newContext();
+
+        List<MeaningfulPkBigint> select = 
ObjectSelect.query(MeaningfulPkBigint.class)
+                .orderBy(MeaningfulPk.PK.asc())
+                .pageSize(1)
+                .select(cleanContext);
+
+        assertEquals(3, select.size());
+        for(MeaningfulPkBigint pk : select) {
+            assertNotNull(pk.getPk());
+            assertTrue(pk.getPk().compareTo(BigInteger.valueOf(120)) > 0);
+        }
+    }
+
 }
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseColumnExtractor.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/meaningful_pk/MeaningfulPkBigint.java
similarity index 53%
copy from 
cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseColumnExtractor.java
copy to 
cayenne-server/src/test/java/org/apache/cayenne/testdo/meaningful_pk/MeaningfulPkBigint.java
index de8a1bc44..fbc51cbfc 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/BaseColumnExtractor.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/meaningful_pk/MeaningfulPkBigint.java
@@ -17,28 +17,12 @@
  *  under the License.
  ****************************************************************/
 
-package org.apache.cayenne.access.translator.select;
+package org.apache.cayenne.testdo.meaningful_pk;
 
-import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
-import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.testdo.meaningful_pk.auto._MeaningfulPkBigint;
 
-import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.table;
+public class MeaningfulPkBigint extends _MeaningfulPkBigint {
 
-/**
- * @since 4.2
- */
-abstract class BaseColumnExtractor implements ColumnExtractor {
+    private static final long serialVersionUID = 1L;
 
-    protected final TranslatorContext context;
-
-    BaseColumnExtractor(TranslatorContext context) {
-        this.context = context;
-    }
-
-    protected void addDbAttribute(String prefix, String labelPrefix, 
DbAttribute dba) {
-        String alias = context.getTableTree().aliasForPath(prefix);
-        String dataRowKey = labelPrefix != null ? labelPrefix + '.' + 
dba.getName() : dba.getName();
-        Node columnNode = table(alias).column(dba).build();
-        context.addResultNode(columnNode, dataRowKey).setDbAttribute(dba);
-    }
 }
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/testdo/meaningful_pk/auto/_MeaningfulPkBigint.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/meaningful_pk/auto/_MeaningfulPkBigint.java
new file mode 100644
index 000000000..5994ffa8c
--- /dev/null
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/meaningful_pk/auto/_MeaningfulPkBigint.java
@@ -0,0 +1,88 @@
+package org.apache.cayenne.testdo.meaningful_pk.auto;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+
+import org.apache.cayenne.BaseDataObject;
+import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.PropertyFactory;
+
+/**
+ * Class _MeaningfulPkBigint was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _MeaningfulPkBigint extends BaseDataObject {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final String PK_PK_COLUMN = "PK";
+
+    public static final NumericProperty<BigInteger> PK = 
PropertyFactory.createNumeric("pk", BigInteger.class);
+
+    protected BigInteger pk;
+
+
+    public void setPk(BigInteger pk) {
+        beforePropertyWrite("pk", this.pk, pk);
+        this.pk = pk;
+    }
+
+    public BigInteger getPk() {
+        beforePropertyRead("pk");
+        return this.pk;
+    }
+
+    @Override
+    public Object readPropertyDirectly(String propName) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch(propName) {
+            case "pk":
+                return this.pk;
+            default:
+                return super.readPropertyDirectly(propName);
+        }
+    }
+
+    @Override
+    public void writePropertyDirectly(String propName, Object val) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch (propName) {
+            case "pk":
+                this.pk = (BigInteger)val;
+                break;
+            default:
+                super.writePropertyDirectly(propName, val);
+        }
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        writeSerialized(out);
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, 
ClassNotFoundException {
+        readSerialized(in);
+    }
+
+    @Override
+    protected void writeState(ObjectOutputStream out) throws IOException {
+        super.writeState(out);
+        out.writeObject(this.pk);
+    }
+
+    @Override
+    protected void readState(ObjectInputStream in) throws IOException, 
ClassNotFoundException {
+        super.readState(in);
+        this.pk = (BigInteger)in.readObject();
+    }
+
+}
diff --git a/cayenne-server/src/test/resources/meaningful-pk.map.xml 
b/cayenne-server/src/test/resources/meaningful-pk.map.xml
index 2ed609d4d..4202bed9f 100644
--- a/cayenne-server/src/test/resources/meaningful-pk.map.xml
+++ b/cayenne-server/src/test/resources/meaningful-pk.map.xml
@@ -9,6 +9,9 @@
        <db-entity name="MEANINGFUL_PK">
                <db-attribute name="PK" type="VARCHAR" isPrimaryKey="true" 
isMandatory="true" length="100"/>
        </db-entity>
+       <db-entity name="MEANINGFUL_PK_BIGINT">
+               <db-attribute name="PK" type="INTEGER" isPrimaryKey="true" 
isMandatory="true"/>
+       </db-entity>
        <db-entity name="MEANINGFUL_PK_DEP">
                <db-attribute name="DESCR" type="VARCHAR" length="50"/>
                <db-attribute name="MASTER_PK" type="INTEGER"/>
@@ -42,6 +45,9 @@
        <obj-entity name="MeaningfulPk" 
className="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPk" 
clientClassName="org.apache.cayenne.testdo.meaningful_pk.ClientMeaningfulPk" 
dbEntityName="MEANINGFUL_PK">
                <obj-attribute name="pk" type="java.lang.String" 
db-attribute-path="PK"/>
        </obj-entity>
+       <obj-entity name="MeaningfulPkBigint" 
className="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkBigint" 
dbEntityName="MEANINGFUL_PK_BIGINT">
+               <obj-attribute name="pk" type="java.math.BigInteger" 
db-attribute-path="PK"/>
+       </obj-entity>
        <obj-entity name="MeaningfulPkDep2" 
className="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkDep2" 
clientClassName="org.apache.cayenne.testdo.meaningful_pk.MeaningfulPkDep2" 
dbEntityName="MEANINGFUL_PK_DEP2">
                <obj-attribute name="descr" type="java.lang.String" 
db-attribute-path="DESCR"/>
                <obj-attribute name="pk" type="java.lang.String" 
db-attribute-path="PK"/>

Reply via email to