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

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


The following commit(s) were added to refs/heads/master by this push:
     new 3ac712d0f CAY-2898 Crypto: NPE in a ColumnQuery
3ac712d0f is described below

commit 3ac712d0fb8f2e857c3d9a7e6aaeafc8dda163b0
Author: Nikita Timofeev <[email protected]>
AuthorDate: Thu Oct 2 18:54:45 2025 +0400

    CAY-2898 Crypto: NPE in a ColumnQuery
---
 RELEASE-NOTES.txt                                  |  1 +
 .../reader/CryptoRowReaderFactoryDecorator.java    |  2 +-
 .../cayenne/crypto/Runtime_AES128_GZIP_IT.java     | 68 ++++++++++++++++++++++
 .../apache/cayenne/crypto/Runtime_AES128_IT.java   | 17 ++++++
 4 files changed, 87 insertions(+), 1 deletion(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index be179ec6b..1c6a8a33a 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -29,6 +29,7 @@ CAY-2879 Negative number for non parameterized ObjectSelect 
query not processed
 CAY-2883 License and notice templates are not processed by the Gradle build
 CAY-2885 Modeler: DbImport fails to load DB schema view
 CAY-2896 Inserting two identical objects into two datamaps stores both objects 
in the last used datamap
+CAY-2898 Crypto: NPE in a ColumnQuery
 
 ----------------------------------
 Release: 5.0-M1
diff --git 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java
 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java
index bb818a8b6..35d8aa9d4 100644
--- 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java
+++ 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java
@@ -159,7 +159,7 @@ public class CryptoRowReaderFactoryDecorator extends 
DefaultRowReaderFactory {
         @Override
         public Object readRow(ResultSet resultSet) {
             Object value = delegateReader.readRow(resultSet);
-            if(valueDecryptor == null) {
+            if(valueDecryptor == null || value == null) {
                 return value;
             }
             return valueDecryptor.decrypt(bytesDecryptor, value);
diff --git 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_GZIP_IT.java
 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_GZIP_IT.java
index 85ea2204f..a4803358d 100644
--- 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_GZIP_IT.java
+++ 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_GZIP_IT.java
@@ -190,4 +190,72 @@ public class Runtime_AES128_GZIP_IT extends 
Runtime_AES128_Base {
         assertEquals(str+"A", result.get(0).getCryptoString());
     }
 
+    @Test
+    public void test_ScalarColumnQuery() throws SQLException {
+        // make sure compression is on...
+        byte[] cryptoBytes1 = CryptoUnitUtils.bytesOfSize(GZIP_THRESHOLD + 
101);
+        byte[] cryptoBytes2 = CryptoUnitUtils.bytesOfSize(GZIP_THRESHOLD + 
102);
+
+        ObjectContext context = runtime.newContext();
+
+        Table2 t1 = context.newObject(Table2.class);
+        t1.setPlainBytes("a".getBytes());
+        t1.setCryptoBytes(cryptoBytes1);
+
+        Table2 t2 = context.newObject(Table2.class);
+        t2.setPlainBytes("b".getBytes());
+        t2.setCryptoBytes(cryptoBytes2);
+
+        Table2 t3 = context.newObject(Table2.class);
+        t3.setPlainBytes("c".getBytes());
+        t3.setCryptoBytes(null);
+
+        context.commitChanges();
+
+        List<byte[]> result = ObjectSelect.query(Table2.class)
+                .column(Table2.CRYPTO_BYTES)
+                .orderBy(Table2.PLAIN_BYTES.asc())
+                .select(runtime.newContext());
+
+        assertEquals(3, result.size());
+        assertArrayEquals(cryptoBytes1, result.get(0));
+        assertArrayEquals(cryptoBytes2, result.get(1));
+        assertArrayEquals(null, result.get(2));
+    }
+
+    @Test
+    public void test_MultipleColumnsQuery() throws SQLException {
+        // make sure compression is on...
+        byte[] cryptoBytes1 = CryptoUnitUtils.bytesOfSize(GZIP_THRESHOLD + 
101);
+        byte[] cryptoBytes2 = CryptoUnitUtils.bytesOfSize(GZIP_THRESHOLD + 
102);
+
+        ObjectContext context = runtime.newContext();
+
+        Table2 t1 = context.newObject(Table2.class);
+        t1.setPlainBytes("a".getBytes());
+        t1.setCryptoBytes(cryptoBytes1);
+
+        Table2 t2 = context.newObject(Table2.class);
+        t2.setPlainBytes("b".getBytes());
+        t2.setCryptoBytes(cryptoBytes2);
+
+        Table2 t3 = context.newObject(Table2.class);
+        t3.setPlainBytes("c".getBytes());
+        t3.setCryptoBytes(null);
+
+        context.commitChanges();
+
+        List<Object[]> result = ObjectSelect.query(Table2.class)
+                .columns(Table2.CRYPTO_BYTES, Table2.PLAIN_BYTES)
+                .orderBy(Table2.PLAIN_BYTES.asc())
+                .select(runtime.newContext());
+
+        assertEquals(3, result.size());
+        assertArrayEquals(cryptoBytes1, (byte[])result.get(0)[0]);
+        assertArrayEquals("a".getBytes(), (byte[])result.get(0)[1]);
+        assertArrayEquals(cryptoBytes2, (byte[])result.get(1)[0]);
+        assertArrayEquals("b".getBytes(), (byte[])result.get(1)[1]);
+        assertArrayEquals(null, (byte[])result.get(2)[0]);
+    }
+
 }
diff --git 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java
index ead0ca076..336d9a7a0 100644
--- 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java
+++ 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java
@@ -333,6 +333,23 @@ public class Runtime_AES128_IT extends Runtime_AES128_Base 
{
         assertEquals("test", result.get(0));
     }
 
+    @Test
+    public void test_ColumnQuerySingleScalarNull() {
+        ObjectContext context = runtime.newContext();
+
+        Table1 t1 = context.newObject(Table1.class);
+        t1.setCryptoInt(1);
+        t1.setCryptoString(null);
+        context.commitChanges();
+
+        List<String> result = ObjectSelect
+                .columnQuery(Table1.class, Table1.CRYPTO_STRING)
+                .select(context);
+
+        assertEquals(1, result.size());
+        assertNull(result.get(0));
+    }
+
     @Test
     public void test_ColumnQueryMultipleScalars() {
         ObjectContext context = runtime.newContext();

Reply via email to