/**********************************************************************
Copyright (c) 2003 Erik Bengtson and others. All rights reserved.
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.
Contributors:
2003 Andy Jefferson - coding standards
2004 Erik Bengtson - added sequence handling
...
**********************************************************************/
package org.jpox.store.rdbms.adapter;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.sql.DataSource;
import org.jpox.store.mapped.DatastoreContainerObject;
import org.jpox.store.mapped.DatastoreIdentifier;
import org.jpox.store.mapped.expression.LogicSetExpression;
import org.jpox.store.mapped.expression.NumericExpression;
import org.jpox.store.mapped.expression.QueryExpression;
import org.jpox.store.mapped.expression.StringExpression;
import org.jpox.store.mapped.expression.TableExprAsJoins;
import org.jpox.store.rdbms.Column;
import org.jpox.store.rdbms.ConnectionProvider;
import org.jpox.store.rdbms.typeinfo.McKoiTypeInfo;
import org.jpox.store.rdbms.typeinfo.TypeInfo;
/**
* Provides methods for adapting SQL language elements to the McKoi database
* Server database.
*
* @version $Revision: 1.19 $
*/
public class McKoiAdapter extends DatabaseAdapter
{
private static final String MCKOI_RESERVED_WORDS =
"ACCOUNT,ACTION,ADD,AFTER,ALL,ALTER," +
"AND,ANY,AS,ASC,AUTO,BEFORE," +
"BETWEEN,BIGINT,BINARY,BIT,BLOB,BOOLEAN," +
"BOTH,BY,CACHE,CALL,CALLBACK,CANONICAL_DECOMPOSITION," +
"CASCADE,CAST,CHAR,CHARACTER,CHECK,CLOB," +
"COLLATE,COLUMN,COMMIT,COMMITTED,COMPACT,CONSTRAINT," +
"COUNT,CREATE,CROSS,CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP," +
"CYCLE,DATE,DECIMAL,DEFAULT,DEFERRABLE,DEFERRED," +
"DELETE,DESC,DESCRIBE,DISTINCT,DOUBLE,DROP," +
"EACH,EXCEPT,EXECUTE,EXISTS,EXPLAIN,FLOAT," +
"FOR,FOREIGN,FROM,FULL_DECOMPOSITION,FUNCTION,GRANT," +
"GROUP,GROUPS,HAVING,IDENTICAL_STRENGTH,IF,IGNORE," +
"IMMEDIATE,IN,INCREMENT,INDEX,INDEX_BLIST,INDEX_NONE," +
"INITIALLY,INNER,INSERT,INT,INTEGER,INTERSECT," +
"INTO,IS,ISOLATION,JAVA,JAVA_OBJECT,JOIN," +
"KEY,LANGUAGE,LEADING,LEFT,LEVEL,LIKE," +
"LIMIT,LOCK,LONG,LONGVARBINARY,LONGVARCHAR,MAX," +
"MAXVALUE,MINVALUE,NAME,NATURAL,NEW,NO," +
"NO_DECOMPOSITION,NOT,NUMERIC,OLD,ON,OPTIMIZE," +
"OPTION,OR,ORDER,OUTER,PASSWORD,PRIMARY," +
"PRIMARY_STRENGTH,PRIVILEGES,PROCEDURE,PUBLIC,READ,REAL," +
"REFERENCES,REGEX,REPEATABLE,RESTRICT,RETURN,RETURNS," +
"REVOKE,RIGHT,ROLLBACK,ROW,SCHEMA,SECONDARY_STRENGTH," +
"SELECT,SEQUENCE,SERIALIZABLE,SET,SHOW,SHUTDOWN," +
"SMALLINT,SOME,START,STRING,TABLE,TEMPORARY," +
"TERTIARY_STRENGTH,TEXT,TIME,TIMESTAMP,TINYINT,TO," +
"TRAILING,TRANSACTION,TRIGGER,TRIM,UNCOMMITTED,UNION," +
"UNIQUE,UNLOCK,UPDATE,USAGE,USE,USER," +
"USING,VALUES,VARBINARY,VARCHAR,VARYING,VIEW," +
"WHERE,WITH";
/**
* Constructs a Hypersonic SQL adapter based on the given JDBC metadata.
* @param metadata the database metadata.
*/
public McKoiAdapter(DatabaseMetaData metadata)
{
super(metadata);
reservedKeywords.addAll(parseKeywordList(MCKOI_RESERVED_WORDS));
}
public String getVendorID()
{
return "mckoi";
}
public boolean isSQLKeyword(String word)
{
/*
* mcKoi does not support this "AAA".COL or AAA."COL"
* mcKoi only works like this "AAA"."COL". For this
* reason we will say that everything is a reserved word
* McKoi 1.0.3
*/
return true;
}
/**
* Accessor for the maximum table name length permitted on this
* datastore.
* @return Max table name length
**/
public int getMaxTableNameLength()
{
return SQLConstants.MAX_IDENTIFIER_LENGTH;
}
/**
* Accessor for the maximum constraint name length permitted on this
* datastore.
* @return Max constraint name length
**/
public int getMaxConstraintNameLength()
{
return SQLConstants.MAX_IDENTIFIER_LENGTH;
}
/**
* Accessor for the maximum index name length permitted on this datastore.
* @return Max index name length
**/
public int getMaxIndexNameLength()
{
return SQLConstants.MAX_IDENTIFIER_LENGTH;
}
/**
* Accessor for the maximum column name length permitted on this datastore.
* @return Max column name length
**/
public int getMaxColumnNameLength()
{
return SQLConstants.MAX_IDENTIFIER_LENGTH;
}
/**
* Accessor for the SQL statement to add a column to a table.
* @param table The table
* @param col The column
* @return The SQL necessary to add the column
*/
public String getAddColumnStatement(DatastoreContainerObject table, Column col)
{
return "ALTER TABLE " + table.toString() + " ADD COLUMN " + col.getSQLDefinition();
}
/**
* Factory for TypeInfo objects.
* @param rs The ResultSet from DatabaseMetaData.getTypeInfo().
* @return A TypeInfo object.
**/
public TypeInfo newTypeInfo(ResultSet rs)
{
return new McKoiTypeInfo(rs);
}
/**
* Whether this datastore supports the use of the escape expression
* in like predicates
* @return whether we can use the escape expression in like predicates
**/
public boolean supportsEscapeExpressionInLikePredicate()
{
return false;
}
/**
* Accessor for whether the adapter supports the transaction isolation level
* @param isolationLevel the isolation level
* @return Whether the transaction isolation level setting is supported.
*/
public boolean supportsTransactionIsolationLevel(int isolationLevel)
{
if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE)
{
return true;
}
return false;
}
/**
* Accessor for a Connection to the datastore.
* @param connProvider the ConnectionProvider
* @param ds The data source. Possible to have more than one datasource for failover
* @param isolationLevel The level of transaction isolation
* @return The Connection
* @throws SQLException Thrown when an error occurs in the creation.
*
* Up to and including 1.0.2, McKoi supports only Connection.TRANSACTION_SERIALIZABLE.
**/
public Connection getConnection(ConnectionProvider connProvider, DataSource[] ds, int isolationLevel)
throws SQLException
{
Connection conn;
conn = connProvider.getConnection(ds);
boolean succeeded = false;
try
{
conn.setAutoCommit(true);
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
succeeded = true;
}
finally
{
if (!succeeded)
{
conn.close();
}
}
return conn;
}
/**
* Returns the appropriate SQL to drop the given table.
* It should return something like:
* <p>
* <blockquote><pre>
* DROP TABLE FOO
* </pre></blockquote>
*
* @param table The table to drop.
* @return The text of the SQL statement.
*/
public String getDropTableStatement(DatastoreContainerObject table)
{
return "DROP TABLE " + table.toString();
}
/**
* Return a new TableExpression appropriate to MySQL. MySQL does not
* support the TableExprAsSubjoins so instead we use TableExprAsJoins.
* @param qs The QueryStatement to add the expression to
* @param table The table in the expression
* @param rangeVar range variable to assign to the expression.
* @return The expression.
**/
public LogicSetExpression newTableExpression(QueryExpression qs, DatastoreContainerObject table, DatastoreIdentifier rangeVar)
{
return new TableExprAsJoins(qs, table, rangeVar);
}
/**
* Union combines the results of two or more queries into a single result
* set. Union include only distinct rows and Union all may include
* duplicates.
* @return Whether to use UNION ALL instead of UNION
*/
public boolean useUnionAll()
{
return true;
}
public NumericExpression lengthMethod(StringExpression str)
{
ArrayList args = new ArrayList();
args.add(str);
return new NumericExpression("LENGTH", args);
}
public StringExpression substringMethod(StringExpression str, NumericExpression begin)
{
ArrayList args = new ArrayList();
args.add(str);
args.add(begin.add(getMapping(Integer.class, str).newLiteral(str.getQueryExpression(), BigInteger.ONE)));
args.add(lengthMethod(str).sub(begin));
return new StringExpression("SUBSTRING", args);
}
public StringExpression substringMethod(StringExpression str, NumericExpression begin, NumericExpression end)
{
ArrayList args = new ArrayList();
args.add(str);
args.add(begin.add(getMapping(BigInteger.class, str).newLiteral(str.getQueryExpression(), BigInteger.ONE)));
args.add(end.sub(begin));
return new StringExpression("SUBSTRING", args);
}
// ---------------------------- Sequence Support ---------------------------
/**
* Whether we support sequences.
* @return whether we support sequences.
**/
public boolean supportsSequences()
{
return true;
}
/**
* Accessor for the sequence statement to create the sequence.
* @param sequence_name Name of the sequence
* @param min Minimum value for the sequence
* @param max Maximum value for the sequence
* @param start Start value for the sequence
* @param increment Increment value for the sequence
* @param cache_size Cache size for the sequence
* @return The statement for getting the next id from the sequence
**/
public String getSequenceCreateStmt(String sequence_name,
String min,String max,
String start,String increment,
String cache_size)
{
if (sequence_name == null)
{
throw new UnsupportedOperationException("Adapter.SequenceNameNullNotSupported");
}
StringBuffer stmt = new StringBuffer("CREATE SEQUENCE ");
stmt.append(sequence_name);
if (increment != null && increment.length() > 0)
{
stmt.append(" INCREMENT " + increment);
}
if (min != null && min.length() > 0)
{
stmt.append(" MINVALUE " + min);
}
if (max != null && max.length() > 0)
{
stmt.append(" MAXVALUE " + max);
}
if (start != null && start.length() > 0)
{
stmt.append(" START " + start);
}
if (cache_size != null && cache_size.length() > 0)
{
stmt.append(" CACHE " + cache_size);
}
return stmt.toString();
}
/**
* Accessor for the statement for getting the next id from the sequence
* for this datastore.
* @param sequence_name Name of the sequence
* @return The statement for getting the next id for the sequence
**/
public String getSequenceNextStmt(String sequence_name)
{
if (sequence_name == null)
{
throw new UnsupportedOperationException("Adapter.SequenceNameNullNotSupported");
}
StringBuffer stmt=new StringBuffer("SELECT ");
stmt.append(" NEXTVAL('"+sequence_name+"') ");
return stmt.toString();
}
}