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 <[email protected]>
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