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

pcristof pushed a commit to branch OPENJPA-2940
in repository https://gitbox.apache.org/repos/asf/openjpa.git


The following commit(s) were added to refs/heads/OPENJPA-2940 by this push:
     new 2e9b0cdd1 [WIP][OPENJPA-2940] Implements version JPQL function
2e9b0cdd1 is described below

commit 2e9b0cdd1132c241b114a634fa791a1c95bf3163
Author: Paulo Cristovão de Araújo Silva Filho <[email protected]>
AuthorDate: Mon Nov 17 20:34:16 2025 -0300

    [WIP][OPENJPA-2940] Implements version JPQL function
---
 .../jdbc/kernel/exps/JDBCExpressionFactory.java    |   5 +
 .../openjpa/jdbc/kernel/exps/VersionVal.java       | 166 +++++++++++++++++++++
 .../openjpa/kernel/exps/ExpressionFactory.java     |   5 +
 .../kernel/exps/InMemoryExpressionFactory.java     |   5 +
 .../org/apache/openjpa/kernel/exps/VersionVal.java |  64 ++++++++
 .../openjpa/kernel/jpql/JPQLExpressionBuilder.java |   3 +
 .../apache/openjpa/kernel/jpql/TestJPQLParser.java |  15 +-
 .../openjpa/persistence/common/apps/CompUser.java  |  12 ++
 .../jpql/functions/TestEJBQLFunction.java          |  18 +++
 9 files changed, 292 insertions(+), 1 deletion(-)

diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
index ded7d807b..2a2f7b816 100644
--- 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
+++ 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
@@ -660,6 +660,11 @@ public class JDBCExpressionFactory
        }
        return new GetNativeObjectId((PCPath) val);
     }
+    
+    @Override
+    public Value version(Value val) {
+       return new VersionVal((PCPath) val);
+    }
 
     @Override
     public Value getMapValue(Value map, Value arg) {
diff --git 
a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/VersionVal.java
 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/VersionVal.java
new file mode 100644
index 000000000..4c0b516dc
--- /dev/null
+++ 
b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/VersionVal.java
@@ -0,0 +1,166 @@
+/*
+ * 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.openjpa.jdbc.kernel.exps;
+
+import java.sql.SQLException;
+
+import org.apache.openjpa.jdbc.kernel.exps.PCPath.PathExpState;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.sql.Result;
+import org.apache.openjpa.jdbc.sql.SQLBuffer;
+import org.apache.openjpa.jdbc.sql.Select;
+import org.apache.openjpa.kernel.Filters;
+import org.apache.openjpa.kernel.exps.ExpressionVisitor;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.util.UserException;
+
+/**
+ * Select the version value of an object; typically used in projections.
+ *
+ * @author Abe White
+ * @author Paulo Cristovão Filho
+ */
+class VersionVal
+    extends AbstractVal {
+
+    
+    private static final long serialVersionUID = 1L;
+
+    private static final Localizer _loc = 
Localizer.forPackage(VersionVal.class);
+
+    protected final PCPath _path;
+    private ClassMetaData _meta = null;
+
+    /**
+     * Constructor. Provide the value whose version to extract.
+     */
+    public VersionVal(PCPath path) {
+        _path = path;
+    }
+
+    /**
+     * Return the version column.
+     */
+    public Column[] getColumns(ExpState state) {
+        return 
_path.getClassMapping(state).getVersionFieldMapping().getColumns();
+    }
+
+    @Override
+    public ClassMetaData getMetaData() {
+        return _meta;
+    }
+
+    @Override
+    public void setMetaData(ClassMetaData meta) {
+        _meta = meta;
+    }
+
+    @Override
+    public Class getType() {
+       FieldMetaData versionField = _path.getMetaData().getVersionField();
+       if (versionField != null) {
+               return versionField.getType();
+       }
+        return null;
+    }
+
+    @Override
+    public void setImplicitType(Class type) {
+    }
+
+    @Override
+    public ExpState initialize(Select sel, ExpContext ctx, int flags) {
+        ExpState state = _path.initialize(sel, ctx, JOIN_REL);
+
+        // it's difficult to get calls on non-pc fields to always return null
+        // without screwing up the SQL, to just don't let users call it on
+        // non-pc fields at all
+        ClassMapping cls = _path.getClassMapping(state);
+        if (cls == null || cls.getEmbeddingMapping() != null)
+            throw new UserException(_loc.get("bad-getobjectid", 
_path.getFieldMapping(state)));
+        return state;
+    }
+
+    @Override
+    public Object toDataStoreValue(Select sel, ExpContext ctx, ExpState state, 
Object val) {
+        ClassMapping mapping = _path.getClassMapping(state);
+        if (mapping.getVersion() != null) {
+               return Filters.convert(val, getType());
+        }
+        return null;
+    }
+
+    @Override
+    public void select(Select sel, ExpContext ctx, ExpState state,
+        boolean pks) {
+        selectColumns(sel, ctx, state, true);
+    }
+
+    @Override
+    public void selectColumns(Select sel, ExpContext ctx, ExpState state,
+        boolean pks) {
+       sel.setSchemaAlias(_path.getSchemaAlias());
+       sel.select(getColumns(state), ((PathExpState) state).joins);
+    }
+
+    @Override
+    public void groupBy(Select sel, ExpContext ctx, ExpState state) {
+        _path.groupBy(sel, ctx, state);
+    }
+
+    @Override
+    public void orderBy(Select sel, ExpContext ctx, ExpState state,
+        boolean asc) {
+        _path.orderBy(sel, ctx, state, asc);
+    }
+
+    @Override
+    public Object load(ExpContext ctx, ExpState state, Result res)
+        throws SQLException {
+        return res.getObject(getColumns(state)[0], null, ((PathExpState) 
state).joins);
+    }
+
+    @Override
+    public void calculateValue(Select sel, ExpContext ctx, ExpState state,
+        Val other, ExpState otherState) {
+        _path.calculateValue(sel, ctx, state, null, null);
+    }
+
+    @Override
+    public int length(Select sel, ExpContext ctx, ExpState state) {
+        return _path.length(sel, ctx, state);
+    }
+
+    @Override
+    public void appendTo(Select sel, ExpContext ctx, ExpState state,
+        SQLBuffer sql, int index) {
+        _path.appendTo(sel, ctx, state, sql, index);
+    }
+
+    @Override
+    public void acceptVisit(ExpressionVisitor visitor) {
+        visitor.enter(this);
+        _path.acceptVisit(visitor);
+        visitor.exit(this);
+    }
+}
+
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
index a4b19defd..e18c8a7f3 100644
--- 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
@@ -538,6 +538,11 @@ public interface ExpressionFactory {
         * Returns the id of the given value
         */
        Value getNativeObjectId(Value val);
+       
+       /**
+        * Returns the version of the given value
+        */
+       Value version(Value val);
 
        /**
      * Return a simple case expression
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
index 654839d46..479c308a0 100644
--- 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
@@ -820,6 +820,11 @@ public class InMemoryExpressionFactory
     public Value getNativeObjectId(Value val) {
        return new GetObjectId((Val) val);
     }
+    
+    @Override
+    public Value version(Value val) {
+       return new VersionVal((Val) val);
+    }
 
     /**
      * Key that implements hashCode and equals methods for object arrays.
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/VersionVal.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/VersionVal.java
new file mode 100644
index 000000000..3231b473b
--- /dev/null
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/VersionVal.java
@@ -0,0 +1,64 @@
+/*
+ * 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.openjpa.kernel.exps;
+
+import org.apache.openjpa.kernel.StoreContext;
+
+/**
+ * Get the version of an object.
+ *
+ * @author Abe White
+ * @author Paulo Cristovão Filho
+ */
+class VersionVal
+    extends Val {
+
+    
+    private static final long serialVersionUID = 1L;
+    private final Val _val;
+
+    /**
+     * Constructor. Provide value whose version to extract.
+     */
+    public VersionVal(Val val) {
+        _val = val;
+    }
+
+    @Override
+    public Class getType() {
+        return Object.class;
+    }
+
+    @Override
+    public void setImplicitType(Class type) {
+    }
+
+    @Override
+    protected Object eval(Object candidate, Object orig,
+        StoreContext ctx, Object[] params) {
+        return ctx.getVersion(_val.eval(candidate, orig, ctx, params));
+    }
+
+    @Override
+    public void acceptVisit(ExpressionVisitor visitor) {
+        visitor.enter(this);
+        _val.acceptVisit(visitor);
+        visitor.exit(this);
+    }
+}
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
index 91726cd44..056442343 100644
--- 
a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
@@ -1570,6 +1570,9 @@ public class JPQLExpressionBuilder
             case JJTIDFUNCTION:
                return factory.getNativeObjectId(getValue(firstChild(node)));
                
+            case JJTVERSIONFUNCTION:
+               return factory.version(getValue(firstChild(node)));
+               
             default:
                 throw parseException(EX_FATAL, "bad-tree",
                     new Object[]{ node }, null);
diff --git 
a/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
 
b/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
index 9bacdf083..39ff4e8b4 100644
--- 
a/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
+++ 
b/openjpa-kernel/src/test/java/org/apache/openjpa/kernel/jpql/TestJPQLParser.java
@@ -258,7 +258,20 @@ public class TestJPQLParser {
        }
        fail();
     }
-    
+
+    @Test
+    public void testVersionFunctionEquals() {
+       try {
+               String query = "SELECT u FROM User AS u WHERE VERSION(u) = 
:version";
+               JPQLNode node = (JPQLNode) new JPQL(query).parseQuery();
+               assertNotNull(node);
+               return;
+       } catch (ParseException ex) {
+               ex.printStackTrace();
+       }
+       fail();
+    }
+
     @Test
     public void testVersionFunctionSimple() {
        try {
diff --git 
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/common/apps/CompUser.java
 
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/common/apps/CompUser.java
index 754fb2cfd..3bd1180cf 100644
--- 
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/common/apps/CompUser.java
+++ 
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/common/apps/CompUser.java
@@ -31,6 +31,7 @@ import jakarta.persistence.InheritanceType;
 import jakarta.persistence.JoinColumn;
 import jakarta.persistence.Lob;
 import jakarta.persistence.OneToOne;
+import jakarta.persistence.Version;
 
 import org.apache.openjpa.persistence.PersistentCollection;
 
@@ -66,6 +67,9 @@ public class CompUser {
     @Enumerated
     @Basic
     private CreditRating creditRating;
+    
+    @Version
+    private int version;
 
     public CompUser() {
     }
@@ -149,4 +153,12 @@ public class CompUser {
     public void setCreditRating(CreditRating creditRating) {
         this.creditRating = creditRating;
     }
+    
+    public int getVersion() {
+               return version;
+       }
+    
+    public void setVersion(int version) {
+               this.version = version;
+       }
 }
diff --git 
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java
 
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java
index 29ef52078..d4488eba0 100644
--- 
a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java
+++ 
b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/functions/TestEJBQLFunction.java
@@ -1018,6 +1018,24 @@ public class TestEJBQLFunction extends AbstractTestCase {
        
        endEm(em);
     }
+    
+    public void testVersionFunction() {
+       EntityManager em = currentEntityManager();
+       
+       String query = "SELECT VERSION(u) FROM CompUser AS u WHERE u.name = 
:name";
+       List result = em.createQuery(query).setParameter("name", 
"Seetha").getResultList();
+       
+       assertEquals(1, result.size());
+       int currentVersion = (int) result.get(0);
+       
+       query = "SELECT u FROM CompUser AS u WHERE u.name = :name AND 
version(u) = :version";
+       result = em.createQuery(query).setParameter("name", 
"Seetha").setParameter("version", currentVersion).getResultList();
+       
+       assertEquals(1, result.size());
+       assertEquals("Seetha", ((CompUser) result.get(0)).getName());
+       
+       endEm(em);
+    }
 
     public CompUser createUser(String name, String cName, Address add, int age,
         boolean isMale) {

Reply via email to