You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by il...@apache.org on 2020/07/21 12:43:50 UTC

[openjpa] branch master updated: OPENJPA-2818 DBDictionary > delimitIdentifiers = true does not work (#69)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new e3bb1f1  OPENJPA-2818 DBDictionary > delimitIdentifiers = true does not work (#69)
e3bb1f1 is described below

commit e3bb1f16575c53f35969968b259a20d16056747c
Author: Enrico Olivelli <eo...@gmail.com>
AuthorDate: Tue Jul 21 14:43:40 2020 +0200

    OPENJPA-2818 DBDictionary > delimitIdentifiers = true does not work (#69)
---
 openjpa-jdbc/nbproject/project.properties          |   0
 .../openjpa/jdbc/identifier/DBIdentifier.java      |  42 +-
 .../org/apache/openjpa/jdbc/schema/SchemaTool.java |   7 +-
 .../java/org/apache/openjpa/jdbc/schema/Table.java |  38 +-
 .../openjpa/jdbc/sql/DBDictionaryFactory.java      |   5 +-
 .../apache/openjpa/jdbc/sql/HerdDBDictionary.java  |   2 +
 .../org/apache/openjpa/jdbc/sql/SelectImpl.java    |   6 +-
 .../openjpa/jdbc/sql/DBDictionaryFactoryTest.java  |  61 +++
 .../apache/openjpa/jdbc/sql/TestSelectImpl.java    |  92 ++++
 .../openjpa/jdbc/sql/TestDelimitIdentifiers.java   | 536 +++++++++++++++++++++
 10 files changed, 759 insertions(+), 30 deletions(-)

diff --git a/openjpa-jdbc/nbproject/project.properties b/openjpa-jdbc/nbproject/project.properties
new file mode 100644
index 0000000..e69de29
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java
index 9b8b467..aa1f888 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/identifier/DBIdentifier.java
@@ -30,7 +30,7 @@ import org.apache.openjpa.lib.util.StringUtil;
  */
 public class DBIdentifier extends IdentifierImpl implements Cloneable, Identifier, Serializable {
 
-    
+
     private static final long serialVersionUID = 1L;
 
     /**
@@ -754,16 +754,17 @@ public class DBIdentifier extends IdentifierImpl implements Cloneable, Identifie
         if (DBIdentifier.isNull(name)) {
             return name;
         }
-        DBIdentifier sName = name.clone();
-        if (sName.getNameInternal() == null) {
-            return sName;
+        if (name.getNameInternal() == null) {
+            return name;
         }
         // Do not convert delimited names to upper case.  They may have
         // been delimited to preserve case.
-        if (force || !Normalizer.isDelimited(sName.getNameInternal())) {
+        if (force || !Normalizer.isDelimited(name.getNameInternal())) {
+            DBIdentifier sName = name.clone();
             sName.setNameInternal(sName.getNameInternal().toUpperCase());
+            return sName;
         }
-        return sName;
+        return name;
     }
 
     /**
@@ -861,17 +862,36 @@ public class DBIdentifier extends IdentifierImpl implements Cloneable, Identifie
         if (DBIdentifier.isNull(name)) {
             return name;
         }
-        DBIdentifier sName = name.clone();
-        if (isEmpty(sName)) {
-            return sName;
+        if (!name.isDelimited()) {
+            return name;
         }
-        String strName = sName.getNameInternal();
-        strName = Normalizer.removeDelimiters(strName);
+        String strName = Normalizer.removeDelimiters(name.getNameInternal());
+        DBIdentifier sName = name.clone();
         sName.setNameInternal(strName);
         return sName;
     }
 
     /**
+     * Combine {@link #removeDelimiters(org.apache.openjpa.jdbc.identifier.DBIdentifier) }
+     * with {@link #toUpper(org.apache.openjpa.jdbc.identifier.DBIdentifier, boolean) }
+     * in order to save allocations and CPU cycles.
+     * @param name
+     * @return
+     */
+    public static DBIdentifier removeDelimitersAndMakeUpper(DBIdentifier name) {
+        if (DBIdentifier.isNull(name) || name.getNameInternal() == null) {
+            return name;
+        }
+        if (!name.isDelimited()) {
+            return toUpper(name, true);
+        }
+        String strName = Normalizer.removeDelimiters(name.getNameInternal());
+        DBIdentifier sName = name.clone();
+        sName.setNameInternal(strName.toUpperCase());
+        return sName;
+    }
+
+    /**
      * Returns a new delimiter with leading and trailing spaces removed.
      * @param name
      */
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java
index a3c22be..f7d528f 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java
@@ -40,6 +40,8 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import javax.sql.DataSource;
 
 import org.apache.openjpa.conf.OpenJPAConfiguration;
@@ -1030,10 +1032,7 @@ public class SchemaTool {
                             continue;
 
                         if (dropColumn(cols[k])) {
-                            if (dbTable != null)
-                                dbTable.removeColumn(col);
-                            else
-                                _log.warn(_loc.get("drop-col", cols[k], tabs[j]));
+                           dbTable.removeColumn(col);
                         }
                     }
                 }
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java
index ab00073..34f4327 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java
@@ -38,6 +38,7 @@ import org.apache.openjpa.lib.meta.SourceTracker;
  * @author Abe White
  * @author Stephen Kim
  */
+
 public class Table
     extends NameSet
     implements Comparable<Object>, SourceTracker {
@@ -45,6 +46,7 @@ public class Table
     private static final long serialVersionUID = 1L;
     private DBIdentifier _name = DBIdentifier.NULL;
     private DBIdentifier _schemaName = DBIdentifier.NULL;
+    // all keys must be normalized with normalizeColumnKey
     private Map<DBIdentifier, Column> _colMap = null;
     private Map<DBIdentifier, Index> _idxMap = null;
     private Collection<ForeignKey> _fkList = null;
@@ -312,12 +314,20 @@ public class Table
         return _rels;
     }
 
+    /**
+     * Return the list of column names, used only for informative (error) messages.
+     * @return
+     */
     public String[] getColumnNames() {
         if (_colMap == null) {
             return new String[0];
         }
-        DBIdentifier[] sNames = _colMap.keySet().toArray(new DBIdentifier[_colMap.size()]);
-        return DBIdentifier.toStringArray(sNames);
+        return _colMap
+                .values()
+                .stream()
+                .map(Column::getIdentifier)
+                .map(DBIdentifier::getName)
+                .toArray(String[]::new);
     }
 
     /**
@@ -329,10 +339,19 @@ public class Table
         return getColumn(DBIdentifier.newIdentifier(name, DBIdentifierType.COLUMN, true));
     }
 
-    public Column getColumn(DBIdentifier name) {
+    private Column internalGetColumn(DBIdentifier name) {
         if (DBIdentifier.isNull(name) || _colMap == null)
             return null;
-        return _colMap.get(DBIdentifier.toUpper(name));
+        DBIdentifier key = normalizeColumnKey(name);
+        return _colMap.get(key);
+    }
+
+    public Column getColumn(DBIdentifier name) {
+        return internalGetColumn(name);
+    }
+
+    private static DBIdentifier normalizeColumnKey(DBIdentifier name) {
+        return DBIdentifier.removeDelimitersAndMakeUpper(name);
     }
 
     public Column getColumn(DBIdentifier name, boolean create) {
@@ -375,8 +394,7 @@ public class Table
         if (DBIdentifier.isNull(name) || _colMap == null) {
             return false;
         }
-        DBIdentifier sName = DBIdentifier.toUpper(name);
-        return _colMap.containsKey(sName);
+        return _colMap.containsKey(normalizeColumnKey(name));
     }
 
     public boolean containsColumn(Column col) {
@@ -414,8 +432,7 @@ public class Table
         }
         if (_colMap == null)
             _colMap = new LinkedHashMap<>();
-        DBIdentifier sName = DBIdentifier.toUpper(name);
-        _colMap.put(sName, col);
+        _colMap.put(normalizeColumnKey(name), col);
         _cols = null;
         return col;
     }
@@ -440,8 +457,7 @@ public class Table
             col = new Column(validName, this);
         if (_colMap == null)
             _colMap = new LinkedHashMap<>();
-        DBIdentifier sName = DBIdentifier.toUpper(name);
-        _colMap.put(sName, col);
+        _colMap.put(normalizeColumnKey(name), col);
         _cols = null;
         return col;
     }
@@ -469,7 +485,7 @@ public class Table
         if (col == null || _colMap == null)
             return false;
 
-        DBIdentifier sName = DBIdentifier.toUpper(col.getIdentifier());
+        DBIdentifier sName = normalizeColumnKey(col.getIdentifier());
         Column cur = _colMap.get(sName);
         if (!col.equals(cur))
             return false;
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java
index bdc302d..cd24841 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java
@@ -217,7 +217,7 @@ public class DBDictionaryFactory {
     /**
      * Guess the dictionary class name to use based on the product string.
      */
-    private static String dictionaryClassForString(String prod, JDBCConfiguration conf) {
+    static String dictionaryClassForString(String prod, JDBCConfiguration conf) {
         if (StringUtil.isEmpty(prod))
             return null;
         prod = prod.toLowerCase(Locale.ENGLISH);
@@ -270,6 +270,9 @@ public class DBDictionaryFactory {
         if (prod.indexOf("sapdb") != -1) {
             return dbdictionaryPlugin.unalias("maxdb");
         }
+        if (prod.indexOf("herddb") != -1) {
+            return dbdictionaryPlugin.unalias("herddb");
+        }
         // test h2 in a special way, because there's a decent chance the string
         // h2 could appear in the URL of another database
         if (prod.indexOf("jdbc:h2:") != -1)
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HerdDBDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HerdDBDictionary.java
index 31986e8..63bfa18 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HerdDBDictionary.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HerdDBDictionary.java
@@ -32,6 +32,8 @@ public class HerdDBDictionary
         supportsForeignKeys = false;
         supportsUniqueConstraints = false;
         supportsCascadeDeleteAction = false;
+        schemaCase = SCHEMA_CASE_LOWER;
+        delimitedCase = SCHEMA_CASE_PRESERVE;
 
         // make OpenJPA escape everything, because Apache Calcite has a lot of reserved words, like 'User', 'Value'...
         setDelimitIdentifiers(true);
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
index 611e730..1c10959 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
@@ -2154,7 +2154,7 @@ public class SelectImpl
      * Return the alias for the given table under the given joins.
      * NOTE: WE RELY ON THESE INDEXES BEING MONOTONICALLY INCREASING FROM 0
      */
-    private int getTableIndex(Table table, PathJoins pj, boolean create) {
+    int getTableIndex(Table table, PathJoins pj, boolean create) {
         // if we have a from select, then there are no table aliases
         if (_from != null)
             return -1;
@@ -2699,7 +2699,7 @@ public class SelectImpl
          * Return the alias used to key on the column data, considering the
          * given joins.
          */
-        private String getColumnAlias(Column col, PathJoins pj) {
+        String getColumnAlias(Column col, PathJoins pj) {
             String alias;
             if (_sel._from != null) {
                 alias = SelectImpl.toAlias(_sel._from.getTableIndex
@@ -2711,7 +2711,7 @@ public class SelectImpl
                 return alias + "_" + col;
             }
             alias = SelectImpl.toAlias(_sel.getTableIndex(col.getTable(), pj, false));
-            return (alias == null) ? null : alias + "." + col;
+            return (alias == null) ? null : alias + "." + _sel._dict.getNamingUtil().toDBName(col.toString());
         }
 
         ////////////////////////////
diff --git a/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactoryTest.java b/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactoryTest.java
new file mode 100644
index 0000000..29782bf
--- /dev/null
+++ b/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactoryTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 Apache Software Foundation.
+ *
+ * Licensed 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.sql;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
+
+public class DBDictionaryFactoryTest {
+
+    @Test
+    public void testDictionaryClassForString() {
+        JDBCConfiguration conf = new JDBCConfigurationImpl();
+
+        String[] aliases = new String[]{
+            "access", org.apache.openjpa.jdbc.sql.AccessDictionary.class.getName(),
+            "db2", org.apache.openjpa.jdbc.sql.DB2Dictionary.class.getName(),
+            "derby", org.apache.openjpa.jdbc.sql.DerbyDictionary.class.getName(),
+            "empress", org.apache.openjpa.jdbc.sql.EmpressDictionary.class.getName(),
+            "foxpro", org.apache.openjpa.jdbc.sql.FoxProDictionary.class.getName(),
+            "h2", org.apache.openjpa.jdbc.sql.H2Dictionary.class.getName(),
+            "hsql", org.apache.openjpa.jdbc.sql.HSQLDictionary.class.getName(),
+            "informix", org.apache.openjpa.jdbc.sql.InformixDictionary.class.getName(),
+            "ingres", org.apache.openjpa.jdbc.sql.IngresDictionary.class.getName(),
+            "jdatastore", org.apache.openjpa.jdbc.sql.JDataStoreDictionary.class.getName(),
+            "mariadb", org.apache.openjpa.jdbc.sql.MariaDBDictionary.class.getName(),
+            "mysql", org.apache.openjpa.jdbc.sql.MySQLDictionary.class.getName(),
+            "herddb", org.apache.openjpa.jdbc.sql.HerdDBDictionary.class.getName(),
+            "oracle", org.apache.openjpa.jdbc.sql.OracleDictionary.class.getName(),
+            "pointbase", org.apache.openjpa.jdbc.sql.PointbaseDictionary.class.getName(),
+            "postgres", org.apache.openjpa.jdbc.sql.PostgresDictionary.class.getName(),
+            "soliddb", org.apache.openjpa.jdbc.sql.SolidDBDictionary.class.getName(),
+            "sqlserver", org.apache.openjpa.jdbc.sql.SQLServerDictionary.class.getName(),
+            "sybase", org.apache.openjpa.jdbc.sql.SybaseDictionary.class.getName(),
+            "maxdb", MaxDBDictionary.class.getName(),
+            "jdbc:h2:", H2Dictionary.class.getName(),
+            "h2 database", H2Dictionary.class.getName()
+        };
+
+        for (int i = 0; i < aliases.length; i++) {
+            String key = aliases[i++];
+            String expected = aliases[i];
+            assertEquals(expected, DBDictionaryFactory.dictionaryClassForString(key, conf));
+        }
+    }
+
+}
diff --git a/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestSelectImpl.java b/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestSelectImpl.java
new file mode 100644
index 0000000..21571c8
--- /dev/null
+++ b/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestSelectImpl.java
@@ -0,0 +1,92 @@
+/*
+ * 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.sql;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
+import org.apache.openjpa.jdbc.identifier.DBIdentifier;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.Table;
+import static org.apache.openjpa.jdbc.sql.Select.FROM_SELECT_ALIAS;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Unit tests about SelectImpl.
+ */
+public class TestSelectImpl {
+
+    @Test
+    public void testSelectResultGetColumnAlias() {
+        verifySelectResultGetColumnAlias(true, false, false, -1, "Col", 56, "t56.\"Col\"");
+        verifySelectResultGetColumnAlias(false, false, false, -1, "Col", 18, "t18.Col");
+
+        // tests with additional select in from
+        verifySelectResultGetColumnAlias(false, true /* fromSelect */, false, -1, "Col", 18, null);
+        verifySelectResultGetColumnAlias(false, true, false, 92, "Col", 18, "t92_Col");
+        verifySelectResultGetColumnAlias(false, true, true /* requiresAliasForSubselect */, 92, "Col", 18, FROM_SELECT_ALIAS + ".t92_Col");
+
+        verifySelectResultGetColumnAlias(true, true, false, -1, "Col", 18, null);
+        verifySelectResultGetColumnAlias(true, true, false, 92, "Col", 18, "t92_Col");
+        verifySelectResultGetColumnAlias(true, true, true /* requiresAliasForSubselect */, 92, "Col", 18, FROM_SELECT_ALIAS + ".t92_Col");
+    }
+
+    private void verifySelectResultGetColumnAlias(boolean delimitIdentifiers, boolean fromSelect, boolean requiresAliasForSubselect,
+            int fromSelectTableIndex, String colName, int tableIndex, String expected) {
+        DBDictionary dict = new DBDictionary();
+        dict.setDelimitIdentifiers(delimitIdentifiers);
+        dict.requiresAliasForSubselect = requiresAliasForSubselect;
+        dict.setSupportsDelimitedIdentifiers(delimitIdentifiers);
+        dict.configureNamingRules();
+        DBIdentifier columnName = DBIdentifier.newColumn(colName, false);
+
+        JDBCConfiguration conf = new JDBCConfigurationImpl();
+        dict.setConfiguration(conf);
+        conf.setDBDictionary(dict);
+        SelectImpl selectImpl = new SelectImpl(conf) {
+            @Override
+            int getTableIndex(Table table, PathJoins pj, boolean create) {
+                return tableIndex;
+            }
+        };
+        Connection conn = null;
+        Statement stmnt = null;
+        ResultSet rs = null;
+        SelectImpl.SelectResult result = new SelectImpl.SelectResult(conn, stmnt, rs, dict);
+        result.setSelect(selectImpl);
+        PathJoins pj = null;
+        Column column = new Column(columnName, null);
+        if (fromSelect) {
+            SelectImpl fromSelectImpl = new SelectImpl(conf) {
+                @Override
+                int getTableIndex(Table table, PathJoins pj, boolean create) {
+                    return fromSelectTableIndex;
+                }
+            };
+            selectImpl.setFromSelect(fromSelectImpl);
+        } else {
+            assertEquals(-1, fromSelectTableIndex);
+        }
+        String res = result.getColumnAlias(column, pj);
+        assertEquals(expected, res);
+    }
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestDelimitIdentifiers.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestDelimitIdentifiers.java
new file mode 100644
index 0000000..505c8f0
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestDelimitIdentifiers.java
@@ -0,0 +1,536 @@
+/*
+ * 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.sql;
+
+import static junit.framework.TestCase.assertEquals;
+import org.apache.commons.dbcp2.BasicDataSource;
+import org.apache.derby.jdbc.EmbeddedDriver;
+import org.apache.openjpa.persistence.PersistenceProviderImpl;
+import org.apache.openjpa.persistence.PersistenceUnitInfoImpl;
+import org.junit.Test;
+
+import javax.persistence.Entity;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.junit.Assert.assertNotNull;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.Statement;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import org.apache.openjpa.persistence.PersistentCollection;
+
+public class TestDelimitIdentifiers {
+
+    public static class LowercaseSchemaDerbyDBDictionary extends DerbyDictionary {
+
+        public LowercaseSchemaDerbyDBDictionary() {
+            super();
+            schemaCase = SCHEMA_CASE_LOWER;
+            delimitedCase = SCHEMA_CASE_PRESERVE;
+            setDelimitIdentifiers(true);
+            setSupportsDelimitedIdentifiers(true);
+        }
+    }
+    
+    @Test
+    public void testDelimitIdentifiers() throws SQLException {
+        final PersistenceUnitInfoImpl persistenceUnitInfo = new PersistenceUnitInfoImpl();
+        persistenceUnitInfo.setExcludeUnlistedClasses(true);
+        persistenceUnitInfo.addManagedClassName(AllFieldTypes.class.getName());
+        final BasicDataSource ds = new BasicDataSource();
+        ds.setDriver(new EmbeddedDriver());
+        ds.setUrl("jdbc:derby:memory:TestDelimitIdentifiers;create=true");
+        persistenceUnitInfo.setNonJtaDataSource(ds);
+        // reproducer for OPENJPA-2818 delimitIdentifiers=true,delimitedCase=lower,schemaCase=lower
+        persistenceUnitInfo.setProperty("openjpa.jdbc.DBDictionary", LowercaseSchemaDerbyDBDictionary.class.getName());        
+        new PersistenceProviderImpl().generateSchema(persistenceUnitInfo, new HashMap<>());
+        // try rebuild the schema
+        new PersistenceProviderImpl().generateSchema(persistenceUnitInfo, new HashMap<>());
+        
+        final Map<String, Collection<String>> columns = new HashMap<>();
+        final Collection<String> createdTables = new HashSet<>();
+        try (final Connection connection = ds.getConnection()) {
+            try (final ResultSet tables = connection.getMetaData()
+                    .getTables(null, null, "TestDelimitIdentifiers$AllFieldTypes%", null)) {
+                while (tables.next()) {
+                    final String table = tables.getString(3);
+                    createdTables.add(table);
+                }
+            }
+            for (final String table : createdTables) {
+                try (final Statement statement = connection.createStatement()) {
+                    try (final ResultSet rs = statement.executeQuery("select * from \"" + table + "\"")) {
+                        final ResultSetMetaData metaData = rs.getMetaData();
+                        final Set<String> columnNames = new HashSet<>();
+                        columns.put(table, columnNames);
+                        for (int i = 1; i <= metaData.getColumnCount(); i++) {
+                            columnNames.add(metaData.getColumnName(i));
+                        }
+                    }
+                }
+            }
+        }
+
+        final EntityManagerFactory entityManagerFactory = new PersistenceProviderImpl()
+                .createContainerEntityManagerFactory(persistenceUnitInfo, new HashMap());
+        try {
+            final AllFieldTypes entity = new AllFieldTypes();
+            final AllFieldTypes entity2 = new AllFieldTypes();
+            {
+                final EntityManager em = entityManagerFactory.createEntityManager();
+                em.getTransaction().begin();
+                try {
+                    em.persist(entity2);
+                    entity.setArrayOfStrings(new String[]{"a", "b"});
+                    entity.setStringField("foo");
+                    entity.setIntField(10);
+                    entity.setSelfOneOne(entity2);
+                    em.persist(entity);
+                    em.getTransaction().commit();
+                } catch (final RuntimeException re) {
+                    if (em.getTransaction().isActive()) {
+                        em.getTransaction().rollback();
+                    }
+                    throw re;
+                } finally {
+                    em.close();
+                }
+            }
+            {
+                final EntityManager em = entityManagerFactory.createEntityManager();
+                try {
+                    assertEquals(2, em.createQuery("select x from TestDelimitIdentifiers$AllFieldTypes x").
+                            getResultList().size());
+                    assertEquals(1, em.createQuery("select x from TestDelimitIdentifiers$AllFieldTypes x where x.stringField = 'foo'").
+                            getResultList().size());
+                    assertEquals(0, em.createQuery("select x from TestDelimitIdentifiers$AllFieldTypes x where x.stringField = 'bar'").
+                            getResultList().size());
+                    assertEquals(1, em.createQuery("select x from TestDelimitIdentifiers$AllFieldTypes x where x.intField >= 10").
+                            getResultList().size());
+                } finally {
+                    em.close();
+                }
+            }
+        } finally {
+            entityManagerFactory.close();
+        }
+        ds.close();
+    }
+
+    @Entity
+    public static class AllFieldTypes {
+
+        public enum EnumType {
+            Value1, Value2
+        };
+
+        // @Basic types
+        private short shortField;
+        private int intField;
+        private boolean booleanField;
+        private long longField;
+        private float floatField;
+        private char charField;
+        private double doubleField;
+        private byte byteField;
+        private Short wShortField;
+        private Integer wIntegerField;
+        private Boolean wBooleanField;
+        private Long wLongField;
+        private Float wFloatField;
+        private Character wCharacterField;
+        private Double wDoubleField;
+        private Byte wByteField;
+        private BigInteger bigIntegerField;
+        private BigDecimal bigDecimalField;
+        private String stringField;
+        private Date dateField;
+        private Calendar calendarField;
+        private java.sql.Date sqlDateField;
+        private java.sql.Time sqlTimeField;
+        private java.sql.Timestamp sqlTimestampField;
+        private byte[] byteLob;
+        private Byte[] wByteLob;
+        private char[] charLob;
+        private Character[] wCharacterLob;
+        private EnumType enumField;
+        private Serializable serializableField;
+
+        // Additional types
+        private Set<String> setOfStrings = new HashSet<>();
+        private String[] arrayOfStrings;
+
+        @PersistentCollection
+        private int[] arrayOfInts;
+
+        // one-to-one and one-to-many relations to self
+        @OneToOne
+        private AllFieldTypes selfOneOne;
+        @OneToMany
+        private List<AllFieldTypes> selfOneMany = new ArrayList<>();
+
+        // Java8 DateTime types which are required by the JPA-2.2 spec
+        private LocalDate localDateField;
+        private LocalTime localTimeField;
+        private LocalDateTime localDateTimeField;
+        private OffsetTime offsetTimeField;
+        private OffsetDateTime offsetDateTimeField;
+
+        public void setShortField(short shortField) {
+            this.shortField = shortField;
+        }
+
+        public short getShortField() {
+            return this.shortField;
+        }
+
+        public void setIntField(int intField) {
+            this.intField = intField;
+        }
+
+        public int getIntField() {
+            return this.intField;
+        }
+
+        public void setBooleanField(boolean booleanField) {
+            this.booleanField = booleanField;
+        }
+
+        public boolean getBooleanField() {
+            return this.booleanField;
+        }
+
+        public void setLongField(long longField) {
+            this.longField = longField;
+        }
+
+        public long getLongField() {
+            return this.longField;
+        }
+
+        public void setFloatField(float floatField) {
+            this.floatField = floatField;
+        }
+
+        public float getFloatField() {
+            return this.floatField;
+        }
+
+        public void setCharField(char charField) {
+            this.charField = charField;
+        }
+
+        public char getCharField() {
+            return this.charField;
+        }
+
+        public void setDoubleField(double doubleField) {
+            this.doubleField = doubleField;
+        }
+
+        public double getDoubleField() {
+            return this.doubleField;
+        }
+
+        public void setByteField(byte byteField) {
+            this.byteField = byteField;
+        }
+
+        public byte getByteField() {
+            return this.byteField;
+        }
+
+        public void setStringField(String stringField) {
+            this.stringField = stringField;
+        }
+
+        public String getStringField() {
+            return this.stringField;
+        }
+
+        public void setDateField(Date dateField) {
+            this.dateField = dateField;
+        }
+
+        public Date getDateField() {
+            return this.dateField;
+        }
+
+        public void setSetOfStrings(Set<String> setOfStrings) {
+            this.setOfStrings = setOfStrings;
+        }
+
+        public Set<String> getSetOfStrings() {
+            return this.setOfStrings;
+        }
+
+        public void setArrayOfStrings(String[] arrayOfStrings) {
+            this.arrayOfStrings = arrayOfStrings;
+        }
+
+        public String[] getArrayOfStrings() {
+            return this.arrayOfStrings;
+        }
+
+        public void setArrayOfInts(int[] arrayOfInts) {
+            this.arrayOfInts = arrayOfInts;
+        }
+
+        public int[] getArrayOfInts() {
+            return arrayOfInts;
+        }
+
+        public BigDecimal getBigDecimalField() {
+            return bigDecimalField;
+        }
+
+        public void setBigDecimalField(BigDecimal bigDecimalField) {
+            this.bigDecimalField = bigDecimalField;
+        }
+
+        public BigInteger getBigIntegerField() {
+            return bigIntegerField;
+        }
+
+        public void setBigIntegerField(BigInteger bigIntegerField) {
+            this.bigIntegerField = bigIntegerField;
+        }
+
+        public byte[] getByteLob() {
+            return byteLob;
+        }
+
+        public void setByteLob(byte[] byteLob) {
+            this.byteLob = byteLob;
+        }
+
+        public Calendar getCalendarField() {
+            return calendarField;
+        }
+
+        public void setCalendarField(Calendar calendarField) {
+            this.calendarField = calendarField;
+        }
+
+        public char[] getCharLob() {
+            return charLob;
+        }
+
+        public void setCharLob(char[] charLob) {
+            this.charLob = charLob;
+        }
+
+        public EnumType getEnumField() {
+            return enumField;
+        }
+
+        public void setEnumField(EnumType enumField) {
+            this.enumField = enumField;
+        }
+
+        public Serializable getSerializableField() {
+            return serializableField;
+        }
+
+        public void setSerializableField(Serializable serializableField) {
+            this.serializableField = serializableField;
+        }
+
+        public java.sql.Date getSqlDateField() {
+            return sqlDateField;
+        }
+
+        public void setSqlDateField(java.sql.Date sqlDateField) {
+            this.sqlDateField = sqlDateField;
+        }
+
+        public java.sql.Time getSqlTimeField() {
+            return sqlTimeField;
+        }
+
+        public void setSqlTimeField(java.sql.Time sqlTimeField) {
+            this.sqlTimeField = sqlTimeField;
+        }
+
+        public java.sql.Timestamp getSqlTimestampField() {
+            return sqlTimestampField;
+        }
+
+        public void setSqlTimestampField(java.sql.Timestamp sqlTimestampField) {
+            this.sqlTimestampField = sqlTimestampField;
+        }
+
+        public Boolean getWBooleanField() {
+            return wBooleanField;
+        }
+
+        public void setWBooleanField(Boolean booleanField) {
+            wBooleanField = booleanField;
+        }
+
+        public Byte getWByteField() {
+            return wByteField;
+        }
+
+        public void setWByteField(Byte byteField) {
+            wByteField = byteField;
+        }
+
+        public Byte[] getWByteLob() {
+            return wByteLob;
+        }
+
+        public void setWByteLob(Byte[] byteLob) {
+            wByteLob = byteLob;
+        }
+
+        public Character getWCharacterField() {
+            return wCharacterField;
+        }
+
+        public void setWCharacterField(Character characterField) {
+            wCharacterField = characterField;
+        }
+
+        public Character[] getWCharacterLob() {
+            return wCharacterLob;
+        }
+
+        public void setWCharacterLob(Character[] characterLob) {
+            wCharacterLob = characterLob;
+        }
+
+        public Double getWDoubleField() {
+            return wDoubleField;
+        }
+
+        public void setWDoubleField(Double doubleField) {
+            wDoubleField = doubleField;
+        }
+
+        public Float getWFloatField() {
+            return wFloatField;
+        }
+
+        public void setWFloatField(Float floatField) {
+            wFloatField = floatField;
+        }
+
+        public Integer getWIntegerField() {
+            return wIntegerField;
+        }
+
+        public void setWIntegerField(Integer integerField) {
+            wIntegerField = integerField;
+        }
+
+        public Long getWLongField() {
+            return wLongField;
+        }
+
+        public void setWLongField(Long longField) {
+            wLongField = longField;
+        }
+
+        public Short getWShortField() {
+            return wShortField;
+        }
+
+        public void setWShortField(Short shortField) {
+            wShortField = shortField;
+        }
+
+        public AllFieldTypes getSelfOneOne() {
+            return selfOneOne;
+        }
+
+        public void setSelfOneOne(AllFieldTypes selfOneOne) {
+            this.selfOneOne = selfOneOne;
+        }
+
+        public List<AllFieldTypes> getSelfOneMany() {
+            return selfOneMany;
+        }
+
+        public void setSelfOneMany(List<AllFieldTypes> selfOneMany) {
+            this.selfOneMany = selfOneMany;
+        }
+
+        public LocalDate getLocalDateField() {
+            return localDateField;
+        }
+
+        public void setLocalDateField(LocalDate localDateField) {
+            this.localDateField = localDateField;
+        }
+
+        public LocalTime getLocalTimeField() {
+            return localTimeField;
+        }
+
+        public void setLocalTimeField(LocalTime localTimeField) {
+            this.localTimeField = localTimeField;
+        }
+
+        public LocalDateTime getLocalDateTimeField() {
+            return localDateTimeField;
+        }
+
+        public void setLocalDateTimeField(LocalDateTime localDateTimeField) {
+            this.localDateTimeField = localDateTimeField;
+        }
+
+        public OffsetTime getOffsetTimeField() {
+            return offsetTimeField;
+        }
+
+        public void setOffsetTimeField(OffsetTime offsetTimeField) {
+            this.offsetTimeField = offsetTimeField;
+        }
+
+        public OffsetDateTime getOffsetDateTimeField() {
+            return offsetDateTimeField;
+        }
+
+        public void setOffsetDateTimeField(OffsetDateTime offsetDateTimeField) {
+            this.offsetDateTimeField = offsetDateTimeField;
+        }
+    }
+}