diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfigurationImpl.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfigurationImpl.java index b0706e8f2e..ef12fbec09 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfigurationImpl.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfigurationImpl.java @@ -220,6 +220,7 @@ public JDBCConfigurationImpl(boolean derivations, boolean loadGlobals) { "sqlserver", org.apache.openjpa.jdbc.sql.SQLServerDictionary.class.getName(), "sybase", org.apache.openjpa.jdbc.sql.SybaseDictionary.class.getName(), "maxdb", MaxDBDictionary.class.getName(), + "sqlanywhere", org.apache.openjpa.jdbc.sql.SQLAnywhereDictionary.class.getName(), }; dbdictionaryPlugin.setAliases(aliases); dbdictionaryPlugin.setInstantiatingGetter("getDBDictionaryInstance"); 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 9bba079d52..34824d9c2a 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 @@ -267,6 +267,9 @@ private static String dictionaryClassForString(String prod, JDBCConfiguration co if (prod.indexOf("sapdb") != -1) { return dbdictionaryPlugin.unalias("maxdb"); } + if (prod.indexOf("sqlanywhere") != -1) { + return dbdictionaryPlugin.unalias("sqlanywhere"); + } // 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/SQLAnywhereDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLAnywhereDictionary.java new file mode 100644 index 0000000000..94ea850baa --- /dev/null +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLAnywhereDictionary.java @@ -0,0 +1,426 @@ +/* + * 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.util.Arrays; +import java.util.Calendar; +import java.util.Map; +import java.util.Set; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; + +import org.apache.openjpa.jdbc.kernel.exps.FilterValue; +import org.apache.openjpa.jdbc.identifier.DBIdentifier; +import org.apache.openjpa.jdbc.identifier.Normalizer; +import org.apache.openjpa.jdbc.schema.Column; +import org.apache.openjpa.jdbc.schema.Table; +import org.apache.openjpa.util.StoreException; + +/** + * Dictionary for SAP SQL Anywhere + */ +public class SQLAnywhereDictionary + extends DBDictionary { + + public static final String VENDOR_SQLANYWHERE = "SAP SQLAnywhere"; + + public SQLAnywhereDictionary() { + platform = "SQL Anywhere"; + + // SQLA does not support DEFERRABLE / INITIALLY DEFERRED constraints + supportsDeferredConstraints = false; + + // SQLA does not support UNIQUE constraints when one of the columns is NULLable. + supportsNullUniqueColumn = false; + + // SQLA supports comments on tables and columns using COMMENT ON syntax, but that is not used. + supportsComments = false; + + /* reservedWords; + Following the style of other DBDictionary, the reservedWords string is not set. + + The reservedWordSet is computed using the following query on SQLA V17 + (... replaced with SQL92 keywords from sql-keywords.rsrc): + + select list(reserved_word order by reserved_word ) + from ( + select UPPER(reserved_word) as reserved_word + from sa_reserved_words() + except all + select row_value as reserved_word + from sa_split_list('...',',') + ORDER BY 1 + ) DT + + Individual databases might be /onfigured differently from the default: + - reserved_keywords option -- enable the LIMIT keyword + - non_keywords option -- disable particular keywords, allowing their use as identifiers + These configurations are not considered and the reservedWordSet is + based on the maximal set of possible keywords / reserved words. + */ + reservedWordSet.addAll(Arrays.asList(new String[]{ + "ARRAY", "ATTACH", "BACKUP", "BIGINT", "BINARY", "BOTTOM", "BREAK", "CALL", "CAPABILITY", "CHAR_CONVERT", + "CHECKPOINT", "COMMENT", "COMPRESSED", "CONFLICT", "CONTAINS", "CUBE", "DATETIMEOFFSET", "DBSPACE", + "DELETING", "DETACH", "DO", "DYNAMIC", "ELSEIF", "ENCRYPTED", "ENDIF", "EXECUTING", "EXECUTING_USER", + "EXISTING", "EXTERNLOGIN", "FORCE", "FORWARD", "HOLDLOCK", "IDENTIFIED", "IF", "INDEX", "INOUT", + "INSERTING", "INSTALL", "INSTEAD", "INTEGRATED", "INVOKING", "INVOKING_USER", "JSON", "KERBEROS", + "LATERAL", "LIMIT", "LOCK", "LOGIN", "LONG", "MEMBERSHIP", "MERGE", "MESSAGE", "MODE", "MODIFY", "NEW", + "NOHOLDLOCK", "NOTIFY", "NVARCHAR", "OFF", "OPENSTRING", "OPENXML", "OPTIONS", "OTHERS", "OUT", "OVER", + "PASSTHROUGH", "PIVOT", "PRINT", "PROC", "PROCEDURE_OWNER", "PUBLICATION", "RAISERROR", "READTEXT", + "REFERENCE", "REFRESH", "RELEASE", "REMOTE", "REMOVE", "RENAME", "REORGANIZE", "RESOURCE", "RESTORE", + "RETURN", "ROLLUP", "ROW", "ROWTYPE", "SAVE", "SAVEPOINT", "SENSITIVE", "SETUSER", "SHARE", "SPATIAL", + "START", "STOP", "SUBTRANS", "SUBTRANSACTION", "SYNCHRONIZE", "TINYINT", "TOP", "TRAN", "TREAT", + "TRIGGER", "TRUNCATE", "TSEQUAL", "UNBOUNDED", "UNIQUEIDENTIFIER", "UNNEST", "UNPIVOT", "UNSIGNED", + "UPDATING", "VALIDATE", "VARBINARY", "VARBIT", "VARIABLE", "VARRAY", "WAIT", "WAITFOR", "WHILE", + "WINDOW", "WITHIN", "WRITETEXT", "XML" + })); + + /* We want to include all SQL-92 reserved words from SQLAnywhere. + Those are not yet in reservedWordSet (added in endConfiguration). + Use the precise set: + + select LIST(STRING('"',word,'"') order by word ) words + from ( + select upper(reserved_word) word + from sa_reserved_words() + ) D + */ + invalidColumnWordSet.addAll(Arrays.asList(new String[]{ + "ADD", "ALL", "ALTER", "AND", "ANY", "ARRAY", "AS", "ASC", "ATTACH", "BACKUP", "BEGIN", "BETWEEN", + "BIGINT", "BINARY", "BIT", "BOTTOM", "BREAK", "BY", "CALL", "CAPABILITY", "CASCADE", "CASE", "CAST", + "CHAR", "CHAR_CONVERT", "CHARACTER", "CHECK", "CHECKPOINT", "CLOSE", "COMMENT", "COMMIT", "COMPRESSED", + "CONFLICT", "CONNECT", "CONSTRAINT", "CONTAINS", "CONTINUE", "CONVERT", "CREATE", "CROSS", "CUBE", + "CURRENT", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "DATE", "DATETIMEOFFSET", "DBSPACE", + "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DELETE", "DELETING", "DESC", "DETACH", "DISTINCT", + "DO", "DOUBLE", "DROP", "DYNAMIC", "ELSE", "ELSEIF", "ENCRYPTED", "END", "ENDIF", "ESCAPE", "EXCEPT", + "EXCEPTION", "EXEC", "EXECUTE", "EXECUTING", "EXECUTING_USER", "EXISTING", "EXISTS", "EXTERNLOGIN", + "FETCH", "FIRST", "FLOAT", "FOR", "FORCE", "FOREIGN", "FORWARD", "FROM", "FULL", "GOTO", "GRANT", + "GROUP", "HAVING", "HOLDLOCK", "IDENTIFIED", "IF", "IN", "INDEX", "INNER", "INOUT", "INSENSITIVE", + "INSERT", "INSERTING", "INSTALL", "INSTEAD", "INT", "INTEGER", "INTEGRATED", "INTERSECT", "INTO", + "INVOKING", "INVOKING_USER", "IS", "ISOLATION", "JOIN", "JSON", "KERBEROS", "KEY", "LATERAL", "LEFT", + "LIKE", "LIMIT", "LOCK", "LOGIN", "LONG", "MATCH", "MEMBERSHIP", "MERGE", "MESSAGE", "MODE", "MODIFY", + "NATURAL", "NCHAR", "NEW", "NO", "NOHOLDLOCK", "NOT", "NOTIFY", "NULL", "NUMERIC", "NVARCHAR", "OF", + "OFF", "ON", "OPEN", "OPENSTRING", "OPENXML", "OPTION", "OPTIONS", "OR", "ORDER", "OTHERS", "OUT", + "OUTER", "OVER", "PASSTHROUGH", "PIVOT", "PRECISION", "PREPARE", "PRIMARY", "PRINT", "PRIVILEGES", + "PROC", "PROCEDURE", "PROCEDURE_OWNER", "PUBLICATION", "RAISERROR", "READTEXT", "REAL", "REFERENCE", + "REFERENCES", "REFRESH", "RELEASE", "REMOTE", "REMOVE", "RENAME", "REORGANIZE", "RESOURCE", "RESTORE", + "RESTRICT", "RETURN", "REVOKE", "RIGHT", "ROLLBACK", "ROLLUP", "ROW", "ROWTYPE", "SAVE", "SAVEPOINT", + "SCROLL", "SELECT", "SENSITIVE", "SESSION", "SESSION_USER", "SET", "SETUSER", "SHARE", "SMALLINT", + "SOME", "SPATIAL", "SQLCODE", "SQLSTATE", "START", "STOP", "SUBTRANS", "SUBTRANSACTION", "SYNCHRONIZE", + "TABLE", "TEMPORARY", "THEN", "TIME", "TIMESTAMP", "TINYINT", "TO", "TOP", "TRAN", "TREAT", "TRIGGER", + "TRUNCATE", "TSEQUAL", "UNBOUNDED", "UNION", "UNIQUE", "UNIQUEIDENTIFIER", "UNKNOWN", "UNNEST", + "UNPIVOT", "UNSIGNED", "UPDATE", "UPDATING", "USER", "USING", "VALIDATE", "VALUES", "VARBINARY", + "VARBIT", "VARCHAR", "VARIABLE", "VARRAY", "VARYING", "VIEW", "WAIT", "WAITFOR", "WHEN", "WHERE", + "WHILE", "WINDOW", "WITH", "WITHIN", "WORK", "WRITETEXT", "XML" + })); + + systemSchemaSet.addAll(Arrays.asList(new String[]{ + "SYS", "PUBLIC", "dbo", "diagnostics", "SA_DEBUG", "rs_systabgroup", "ml_server" + })); + + // A SELECT statement may start with SELECT or WITH. + selectWordSet.add("WITH"); + + /* Based on the following query with names removed if they support a size. + select list(string('"',x,'"'),',' order by x) + from ( + ( select upper(domain_name) x + from sys.sysdomain + union all + select upper(type_name) from sys.sysusertype ) + except + select UPPER(row_value) as x + from sa_split_list('BIGINT,BIT,BLOB,CLOB,DATE,DECIMAL,DISTINCT,DOUBLE,FLOAT,INTEGER,' + ||'JAVA_OBJECT,NULL,NUMERIC,OTHER,REAL,REF,SMALLINT,STRUCT,TIME,TIMESTAMP,TINYINT') + ) D + */ + fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{ + "ARRAY","DATETIME","LONG BINARY","LONG NVARCHAR","LONG VARBIT","LONG VARCHAR","MONEY","NTEXT","ROW", + "SMALLDATETIME","SMALLMONEY","ST_GEOMETRY","SYSNAME","TEXT","TIMESTAMP WITH TIME ZONE","UNIQUEIDENTIFIER", + "UNIQUEIDENTIFIERSTR","UNSIGNED BIGINT","UNSIGNED INT","UNSIGNED SMALLINT","XML", + })); + + // Set in configureNamingUtil + // schemaCase = SCHEMA_CASE_PRESERVE; + + // The SQL used to validate that a connection is still in a valid state. + validationSQL = "SELECT NOW()"; + + // SQLA can use FOR UPDATE but it requires setting ansi_update_constraints option. Use table lock hints. + forUpdateClause = null; + tableForUpdateClause = "WITH (UPDLOCK)"; + + // Use TOP n OFFSET m. The alternative LIMIT/OFFSET might be disabled due to reserved_keywords. + rangePosition = RANGE_POST_DISTINCT; + supportsSelectStartIndex = true; + supportsSelectEndIndex = true; + + // In the FROM clause, the correlation name is required according to ISO/IEC 2095-2:2011 7.6