blob: 5c139de75eaeecb2623cd5a5e93e5d4f3e1a0918 [file] [log] [blame]
/*
* 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.DriverManager;
import java.sql.SQLException;
import java.util.Arrays;
import javax.sql.DataSource;
import org.apache.openjpa.util.StoreException;
/**
* Dictionary for Apache Derby (formerly Cloudscape).
*/
public class DerbyDictionary
extends AbstractDB2Dictionary {
/**
* If true, we will shutdown the embedded database when closing
* the DataSource.
*/
public boolean shutdownOnClose = true;
public DerbyDictionary() {
platform = "Apache Derby";
validationSQL = "VALUES(1)";
stringLengthFunction = "LENGTH({0})";
substringFunctionName = "SUBSTR";
toUpperCaseFunction = "UPPER(CAST({0} AS VARCHAR(" + varcharCastLength + ")))";
toLowerCaseFunction = "LOWER(CAST({0} AS VARCHAR(" + varcharCastLength + ")))";
// Derby name length restriction has been relaxed
//http://www.archivum.info/derby-dev@db.apache.org/2004-12/msg00270.html
maxConstraintNameLength = 128;
maxIndexNameLength = 128;
maxColumnNameLength = 128;
maxTableNameLength = 128;
useGetBytesForBlobs = true;
useSetBytesForBlobs = true;
allowsAliasInBulkClause = false;
supportsDeferredConstraints = false;
supportsParameterInSelect = false;
supportsSelectForUpdate = true;
supportsDefaultDeleteAction = false;
requiresCastForMathFunctions = true;
requiresCastForComparisons = true;
supportsSimpleCaseExpression = false;
supportsNullUniqueColumn = false;
supportsComments = true;
fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{
"BIGINT", "INTEGER", "TEXT"
}));
reservedWordSet.addAll(Arrays.asList(new String[]{
"BOOLEAN", "CALL", "ENDEXEC", "EXPLAIN", "FUNCTION",
"GET_CURRENT_CONNECTION", "INOUT", "LONGINT", "LTRIM", "NONE",
"NVARCHAR", "OFF", "OUT", "RTRIM", "SUBSTR", "XML", "XMLEXISTS",
"XMLPARSE", "XMLSERIALIZE",
}));
// reservedWordSet subset that CANNOT be used as valid column names
// (i.e., without surrounding them with double-quotes)
invalidColumnWordSet.addAll(Arrays.asList(new String[] {
"ADD", "ALL", "ALLOCATE", "ALTER", "AND", "ANY", "ARE", "AS", "ASC",
"ASSERTION", "AT", "AUTHORIZATION", "AVG", "BEGIN", "BETWEEN",
"BIT", "BOOLEAN", "BOTH", "BY", "CALL", "CASCADE", "CASCADED",
"CASE", "CAST", "CHAR", "CHARACTER", "CHARACTER_LENGTH", "CHECK",
"CLOSE", "COALESCE", "COLLATE", "COLLATION", "COLUMN", "COMMIT", "CONNECT",
"CONNECTION", "CONSTRAINT", "CONSTRAINTS", "CONTINUE", "CONVERT",
"CORRESPONDING", "CREATE", "CURRENT", "CURRENT_DATE", "CURRENT_ROLE",
"CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR",
"DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFERRABLE",
"DEFERRED", "DELETE", "DESC", "DESCRIBE", "DIAGNOSTICS",
"DISCONNECT", "DISTINCT", "DOUBLE", "DROP", "ELSE", "END",
"END-EXEC", "ESCAPE", "EXCEPT", "EXCEPTION", "EXEC", "EXECUTE",
"EXISTS", "EXPLAIN", "EXTERNAL", "FALSE", "FETCH", "FIRST", "FLOAT",
"FOR", "FOREIGN", "FOUND", "FROM", "FULL", "FUNCTION", "GET",
"GETCURRENTCONNECTION", "GLOBAL", "GO", "GOTO", "GRANT", "GROUP", "HAVING", "HOUR",
"IDENTITY", "IMMEDIATE", "IN", "INDICATOR", "INITIALLY", "INNER",
"INOUT", "INPUT", "INSENSITIVE", "INSERT", "INT", "INTEGER",
"INTERSECT", "INTO", "IS", "ISOLATION", "JOIN", "KEY", "LAST",
"LEADING", "LEFT", "LIKE", "LOWER", "LTRIM", "MATCH", "MAX", "MIN",
"MINUTE", "NATIONAL", "NATURAL", "NCHAR", "NEXT", "NO", "NONE", "NOT",
"NULL", "NULLIF", "NUMERIC", "NVARCHAR", "OF", "ON", "ONLY", "OPEN",
"OPTION", "OR", "ORDER", "OUT", "OUTER", "OUTPUT", "OVER", "OVERLAPS",
"PAD", "PARTIAL", "PREPARE", "PRESERVE", "PRIMARY", "PRIOR",
"PRIVILEGES", "PROCEDURE", "PUBLIC", "READ", "REAL", "REFERENCES",
"RELATIVE", "RESTRICT", "REVOKE", "RIGHT", "ROLLBACK", "ROWS",
"ROW_NUMBER", "RTRIM", "SCHEMA", "SCROLL", "SECOND", "SELECT", "SESSION_USER",
"SET", "SMALLINT", "SOME", "SPACE", "SQL", "SQLCODE", "SQLERROR",
"SQLSTATE", "SUBSTR", "SUBSTRING", "SUM", "SYSTEM_USER", "TABLE",
"TEMPORARY", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING",
"TRANSACTION", "TRANSLATE", "TRANSLATION", "TRIM", "TRUE", "UNION",
"UNIQUE", "UNKNOWN", "UPDATE", "UPPER", "USER", "USING", "VALUES",
"VARCHAR", "VARYING", "VIEW", "WHENEVER", "WHERE", "WITH", "WORK",
"WRITE", "XML", "XMLEXISTS", "XMLPARSE", "XMLQUERY", "XMLSERIALIZE", "YEAR",
}));
}
@Override
public void connectedConfiguration(Connection conn) throws SQLException {
super.connectedConfiguration(conn);
if (versionEqualOrLaterThan(10, 5)) {
supportsSelectStartIndex = true;
supportsSelectEndIndex = true;
}
}
/**
* Appends a range to the given buffer.
* <br>
* A range query is never appended to a subselct clause.
* <br>
* If this dictionary supports {@link DBDictionary#supportsSelectStartIndex offset}
* and {@link DBDictionary#supportsSelectEndIndex limit} on queries then the
* syntax is <pre>
* [ OFFSET {start} ROWS ]
* [ FETCH NEXT {end-start} ROWS ONLY ]
* </pre>
* Otherwise, the offset is not used and the syntax is <pre>
* [ FETCH FIRST {end} ROWS ONLY ]
* </pre>
* @param buf the SQL buffer to be appended
* @param start starting offset. {@code 0} means offset is not used.
* @param end number of rows to be fetched. {@code Long.MAX_VALUE} means no limit.
* @param subselect flags if the buffer represents a SQL Subquery clause
*/
@Override
protected void appendSelectRange(SQLBuffer buf, long start, long end, boolean subselect) {
// do not generate FETCH FIRST clause for subselect
if (subselect)
return;
if (supportsSelectStartIndex && supportsSelectEndIndex) {
if (isUsingOffset(start))
buf.append(" OFFSET ").append(Long.toString(start)).append(" ROWS ");
if (isUsingLimit(end)) {
long rowCount = end - start;
buf.append(" FETCH NEXT ").append(Long.toString(rowCount)).append(" ROWS ONLY");
}
} else if (isUsingLimit(end)) {
buf.append(" FETCH FIRST ").append(Long.toString(end)).append(" ROWS ONLY");
}
}
@Override
public void closeDataSource(DataSource dataSource) {
super.closeDataSource(dataSource);
if (!shutdownOnClose)
return;
// as well as closing the DataSource, we also need to
// shut down the instance if we are using an embedded database, which
// can only be done by connecting to the same URL with the
// ";shutdown=true" string appended to the end
// see: http://db.apache.org/derby/docs/dev/devguide/tdevdvlp40464.html
if (conf != null && conf.getConnectionDriverName() != null &&
conf.getConnectionDriverName().indexOf("EmbeddedDriver") != -1) {
try {
DriverManager.getConnection(conf.getConnectionURL()
+ ";shutdown=true");
} catch (SQLException e) {
// we actually expect a SQLException to be thrown here:
// Derby strangely uses that as a mechanism to report
// a successful shutdown
}
}
}
@Override
public boolean isFatalException(int subtype, SQLException ex) {
int errorCode = ex.getErrorCode();
if ((subtype == StoreException.LOCK ||
subtype == StoreException.QUERY) && errorCode <= 30000) {
return false;
}
return super.isFatalException(subtype, ex);
}
/**
* Applies range calculation on the actual number of rows selected by a
* {@code COUNT(*)} query. A range query may use either only the limit or
* both offset and limit based on database dictionary support and
* accordingly the number of rows in the result set needs to be modified.
*
* @param select
* @param count
* @return
*/
@Override
public int applyRange(Select select, int count) {
long start = select.getStartIndex();
long end = select.getEndIndex();
if (supportsSelectStartIndex) {
if (start > 0)
count -= start;
if (end != Long.MAX_VALUE) {
long size = end - start;
count = (int) Math.min(count, size);
}
}
return count;
}
}