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 c56c9fa87 CAY-2838 Vertical Inheritance: Problem setting db attribute 
to null via flattened path
c56c9fa87 is described below

commit c56c9fa870537bebd4623203707ac453203b59b5
Author: Nikita Timofeev <stari...@gmail.com>
AuthorDate: Thu Feb 29 17:08:19 2024 +0400

    CAY-2838 Vertical Inheritance: Problem setting db attribute to null via 
flattened path
---
 .../cayenne/dba/sqlserver/SQLServerAdapter.java    | 54 +++++++++++++++++++++-
 .../cayenne/access/VerticalInheritanceIT.java      | 29 +++++-------
 2 files changed, 65 insertions(+), 18 deletions(-)

diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java
index 0b71a2028..93efb47ea 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java
@@ -20,8 +20,12 @@
 package org.apache.cayenne.dba.sqlserver;
 
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
+import java.util.stream.Collectors;
 
+import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor;
 import org.apache.cayenne.access.types.ExtendedType;
@@ -31,6 +35,8 @@ import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.sybase.SybaseAdapter;
 import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.query.Query;
 import org.apache.cayenne.query.SQLAction;
 import org.apache.cayenne.resource.ResourceLocator;
@@ -114,7 +120,7 @@ public class SQLServerAdapter extends SybaseAdapter {
        public boolean supportsGeneratedKeysForBatchInserts() {
                return false;
        }
-       
+
        /**
         * @since 4.2
         */
@@ -157,4 +163,50 @@ public class SQLServerAdapter extends SybaseAdapter {
        public void setVersion(Integer version) {
                this.version = version;
        }
+
+    /**
+     * Generates DDL to create unique index that allows multiple NULL values 
to comply with ANSI SQL,
+     * that is default behaviour for other RDBMS.
+     * <br>
+     * Example:
+     * <pre>
+     * {@code
+     * CREATE UNIQUE NONCLUSTERED INDEX _idx_entity_attribute
+     * ON entity(attribute)
+     * WHERE attribute IS NOT NULL
+     * }
+     * </pre>
+     *
+     * @param source  entity for the index
+     * @param columns source columns for the index
+     * @return DDL to create unique index
+     *
+     * @since 4.2.1
+     */
+    @Override
+    public String createUniqueConstraint(DbEntity source, 
Collection<DbAttribute> columns) {
+        if (columns == null || columns.isEmpty()) {
+            throw new CayenneRuntimeException("Can't create UNIQUE constraint 
- no columns specified.");
+        }
+
+        return "CREATE UNIQUE NONCLUSTERED INDEX " + uniqueIndexName(source, 
columns) + " ON " +
+                quotingStrategy.quotedFullyQualifiedName(source) +
+                "(" +
+                
columns.stream().map(quotingStrategy::quotedName).collect(Collectors.joining(", 
")) +
+                ") WHERE " +
+                columns.stream().map(quotingStrategy::quotedName)
+                        .map(n -> n + " IS NOT NULL")
+                        .collect(Collectors.joining(" AND "));
+    }
+
+    private String uniqueIndexName(DbEntity source, Collection<DbAttribute> 
columns) {
+        return "_idx_unique_"
+                + source.getName().replace(' ', '_').toLowerCase()
+                + "_"
+                + columns.stream()
+                .map(DbAttribute::getName)
+                .map(String::toLowerCase)
+                .map(n -> n.replace(' ', '_'))
+                .collect(Collectors.joining("_"));
+    }
 }
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
index eba76fb85..6f17d11c8 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
@@ -33,6 +33,7 @@ import org.apache.cayenne.testdo.inheritance_vertical.*;
 import org.apache.cayenne.unit.di.server.CayenneProjects;
 import org.apache.cayenne.unit.di.server.ServerCase;
 import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -72,6 +73,15 @@ public class VerticalInheritanceIT extends ServerCase {
                                .setColumnTypes(Types.INTEGER, Types.VARCHAR, 
Types.INTEGER);
        }
 
+       @After
+       public void cleanUpConcrete() throws SQLException {
+               ivConcreteTable.deleteAll();
+               ivAbstractTable.deleteAll();
+
+               assertEquals(0, ivAbstractTable.getRowCount());
+               assertEquals(0, ivConcreteTable.getRowCount());
+       }
+
     @Test
        public void testInsert_Root() throws Exception {
 
@@ -630,8 +640,8 @@ public class VerticalInheritanceIT extends ServerCase {
                assertEquals(parent2, child.getParent());
 
                // Manually delete child to prevent a foreign key constraint 
failure while cleaning MySQL db
-               ivConcreteTable.deleteAll();
-               ivAbstractTable.deleteAll();
+               context.deleteObject(child);
+               context.commitChanges();
        }
 
        /**
@@ -654,9 +664,6 @@ public class VerticalInheritanceIT extends ServerCase {
                        IvConcrete concreteFetched = 
SelectById.query(IvConcrete.class, id).selectOne(cleanContext);
                        assertNull(concreteFetched.getName());
                }
-
-               ivConcreteTable.deleteAll();
-               ivAbstractTable.deleteAll();
        }
 
        @Test
@@ -739,14 +746,6 @@ public class VerticalInheritanceIT extends ServerCase {
 
        @Test
        public void testNullifyFlattenedRelationshipConcreteToAbstract() throws 
SQLException {
-               TableHelper ivAbstractTable = new TableHelper(dbHelper, 
"IV_ABSTRACT");
-               ivAbstractTable.setColumns("ID", "PARENT_ID", "TYPE")
-                               .setColumnTypes(Types.INTEGER, Types.INTEGER, 
Types.CHAR);
-
-               TableHelper ivConcreteTable = new TableHelper(dbHelper, 
"IV_CONCRETE");
-               ivConcreteTable.setColumns("ID", "NAME", "RELATED_ABSTRACT_ID")
-                               .setColumnTypes(Types.INTEGER, Types.VARCHAR, 
Types.INTEGER);
-
                ivAbstractTable.insert(1, null, "S");
                ivConcreteTable.insert(1, "One", null);
                ivAbstractTable.insert(2, null, "S");
@@ -764,10 +763,6 @@ public class VerticalInheritanceIT extends ServerCase {
                        assertEquals("Two", concreteFetched.getName());
                        assertNull(concreteFetched.getRelatedAbstract());
                }
-
-               // must clean these tables manually
-               ivConcreteTable.deleteAll();
-               ivAbstractTable.deleteAll();
        }
 
        @Test//(expected = ValidationException.class) // other2 is not 
mandatory for now

Reply via email to