You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2006/07/19 23:35:07 UTC
svn commit: r423615 [33/44] - in /incubator/openjpa/trunk: ./
openjpa-jdbc-5/ openjpa-jdbc-5/src/ openjpa-jdbc-5/src/main/
openjpa-jdbc-5/src/main/java/ openjpa-jdbc-5/src/main/java/org/
openjpa-jdbc-5/src/main/java/org/apache/ openjpa-jdbc-5/src/main/...
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,1019 @@
+/*
+ * Copyright 2006 The 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 java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
+import org.apache.openjpa.jdbc.kernel.exps.FilterValue;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.jdbc.schema.Index;
+import org.apache.openjpa.jdbc.schema.PrimaryKey;
+import org.apache.openjpa.jdbc.schema.Sequence;
+import org.apache.openjpa.jdbc.schema.Table;
+import org.apache.openjpa.lib.jdbc.DelegatingDatabaseMetaData;
+import org.apache.openjpa.lib.jdbc.DelegatingPreparedStatement;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.util.StoreException;
+import serp.util.Numbers;
+
+/**
+ * Dictionary for Oracle.
+ */
+public class OracleDictionary
+ extends DBDictionary {
+
+ public static final String SELECT_HINT =
+ "org.apache.openjpa.hint.OracleSelectHint";
+ public static final String VENDOR_ORACLE = "oracle";
+
+ private static final int BEHAVE_OTHER = 0;
+ private static final int BEHAVE_ORACLE = 1;
+ private static final int BEHAVE_DATADIRECT31 = 2;
+
+ private static Blob EMPTY_BLOB = null;
+ private static Clob EMPTY_CLOB = null;
+
+ private static final Localizer _loc = Localizer.forPackage
+ (OracleDictionary.class);
+
+ /**
+ * If true, then simulate auto-assigned values in Oracle by
+ * using a trigger that inserts a sequence value into the
+ * primary key value when a row is inserted.
+ */
+ public boolean useTriggersForAutoAssign = false;
+
+ /**
+ * The global sequence name to use for autoassign simulation.
+ */
+ public String autoAssignSequenceName = null;
+
+ /**
+ * Flag to use OpenJPA 3 style naming for auto assign sequence name and
+ * trigger name for backwards compatibility.
+ */
+ public boolean openjpa3GeneratedKeyNames = false;
+
+ /**
+ * If true, then OpenJPA will attempt to use the special
+ * OraclePreparedStatement.setFormOfUse method to
+ * configure statements that it detects are operating on unicode fields.
+ */
+ public boolean useSetFormOfUseForUnicode = true;
+
+ // some oracle drivers have problems with select for update; warn the
+ // first time locking is attempted
+ private boolean _checkedUpdateBug = false;
+ private boolean _warnedCharColumn = false;
+ private boolean _warnedNcharColumn = false;
+ private int _driverBehavior = -1;
+
+ // cache lob methods
+ private Method _putBytes = null;
+ private Method _putString = null;
+ private Method _putChars = null;
+
+ public OracleDictionary() {
+ platform = "Oracle";
+ validationSQL = "SELECT SYSDATE FROM DUAL";
+ nextSequenceQuery = "SELECT {0}.NEXTVAL FROM DUAL";
+ stringLengthFunction = "LENGTH({0})";
+ joinSyntax = SYNTAX_DATABASE;
+ maxTableNameLength = 30;
+ maxColumnNameLength = 30;
+ maxIndexNameLength = 30;
+ maxConstraintNameLength = 30;
+ maxEmbeddedBlobSize = 4000;
+ maxEmbeddedClobSize = 4000;
+
+ supportsDeferredConstraints = true;
+ supportsLockingWithDistinctClause = false;
+ supportsSelectStartIndex = true;
+ supportsSelectEndIndex = true;
+
+ systemSchemaSet.addAll(Arrays.asList(new String[]{
+ "CTXSYS", "MDSYS", "SYS", "SYSTEM", "WKSYS", "WMSYS", "XDB",
+ }));
+
+ bigintTypeName = "NUMBER{0}";
+ bitTypeName = "NUMBER{0}";
+ decimalTypeName = "NUMBER{0}";
+ doubleTypeName = "NUMBER{0}";
+ integerTypeName = "NUMBER{0}";
+ numericTypeName = "NUMBER{0}";
+ smallintTypeName = "NUMBER{0}";
+ tinyintTypeName = "NUMBER{0}";
+ longVarcharTypeName = "LONG";
+ timeTypeName = "DATE";
+ varcharTypeName = "VARCHAR2{0}";
+ fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{
+ "LONG RAW", "RAW", "LONG", "REF",
+ }));
+ reservedWordSet.addAll(Arrays.asList(new String[]{
+ "ACCESS", "AUDIT", "CLUSTER", "COMMENT", "COMPRESS", "EXCLUSIVE",
+ "FILE", "IDENTIFIED", "INCREMENT", "INDEX", "INITIAL", "LOCK",
+ "LONG", "MAXEXTENTS", "MINUS", "MODE", "NOAUDIT", "NOCOMPRESS",
+ "NOWAIT", "OFFLINE", "ONLINE", "PCTFREE", "ROW",
+ }));
+ }
+
+ public void endConfiguration() {
+ super.endConfiguration();
+ if (useTriggersForAutoAssign)
+ supportsAutoAssign = true;
+ }
+
+ public void connectedConfiguration(Connection conn)
+ throws SQLException {
+ super.connectedConfiguration(conn);
+ if (driverVendor == null) {
+ DatabaseMetaData meta = conn.getMetaData();
+ String url = (meta.getURL() == null) ? "" : meta.getURL();
+ String driverName = meta.getDriverName();
+ String metadataClassName;
+ if (meta instanceof DelegatingDatabaseMetaData)
+ metadataClassName = ((DelegatingDatabaseMetaData) meta).
+ getInnermostDelegate().getClass().getName();
+ else
+ metadataClassName = meta.getClass().getName();
+
+ // check both the driver class name and the URL for known patterns
+ if (metadataClassName.startsWith("oracle.")
+ || url.indexOf("jdbc:oracle:") != -1
+ || "Oracle JDBC driver".equals(driverName)) {
+ driverVendor = VENDOR_ORACLE + meta.getDriverMajorVersion()
+ + meta.getDriverMinorVersion();
+
+ // warn sql92
+ if (meta.getDatabaseProductVersion().indexOf("Release 8.") > 0)
+ {
+ if (joinSyntax == SYNTAX_SQL92 && log.isWarnEnabled())
+ log.warn(_loc.get("oracle-syntax"));
+ joinSyntax = SYNTAX_DATABASE;
+ dateTypeName = "DATE"; // added oracle 9
+ timestampTypeName = "DATE"; // added oracle 9
+ }
+ } else if (metadataClassName.startsWith("com.ddtek.")
+ || url.indexOf("jdbc:datadirect:oracle:") != -1
+ || "Oracle".equals(driverName)) {
+ driverVendor = VENDOR_DATADIRECT + meta.getDriverMajorVersion()
+ + meta.getDriverMinorVersion();
+ } else
+ driverVendor = VENDOR_OTHER;
+ }
+ cacheDriverBehavior(driverVendor);
+ }
+
+ /**
+ * Cache constant for drivers with behaviors we have to deal with.
+ */
+ private void cacheDriverBehavior(String driverVendor) {
+ if (_driverBehavior != -1)
+ return;
+
+ driverVendor = driverVendor.toLowerCase();
+ if (driverVendor.startsWith(VENDOR_ORACLE))
+ _driverBehavior = BEHAVE_ORACLE;
+ else if (driverVendor.equals(VENDOR_DATADIRECT + "30")
+ || driverVendor.equals(VENDOR_DATADIRECT + "31"))
+ _driverBehavior = BEHAVE_DATADIRECT31;
+ else
+ _driverBehavior = BEHAVE_OTHER;
+ }
+
+ /**
+ * Ensure that the driver vendor has been set, and if not, set it now.
+ */
+ public void ensureDriverVendor() {
+ if (driverVendor != null) {
+ cacheDriverBehavior(driverVendor);
+ return;
+ }
+
+ if (log.isInfoEnabled())
+ log.info(_loc.get("oracle-connecting-for-driver"));
+ Connection conn = null;
+ try {
+ conn = conf.getDataSource2(null).getConnection();
+ connectedConfiguration(conn);
+ } catch (SQLException se) {
+ throw SQLExceptions.getStore(se, this);
+ } finally {
+ if (conn != null)
+ try {
+ conn.close();
+ } catch (SQLException se) {
+ }
+ }
+ }
+
+ public boolean supportsLocking(Select sel) {
+ if (!super.supportsLocking(sel))
+ return false;
+ return !requiresSubselectForRange(sel.getStartIndex(),
+ sel.getEndIndex(), sel.isDistinct(), sel.getOrdering());
+ }
+
+ protected SQLBuffer getSelects(Select sel, boolean distinctIdentifiers,
+ boolean forUpdate) {
+ // if range doesn't require a subselect can use super
+ if (!requiresSubselectForRange(sel.getStartIndex(),
+ sel.getEndIndex(), sel.isDistinct(), sel.getOrdering()))
+ return super.getSelects(sel, distinctIdentifiers, forUpdate);
+
+ // if there are no joins involved or we're using a from select so
+ // that all cols already have unique aliases, can use super
+ if (sel.getFromSelect() != null || sel.getTableAliases().size() < 2)
+ return super.getSelects(sel, distinctIdentifiers, forUpdate);
+
+ // since none of the conditions above were met, we're dealing with
+ // a select that uses joins and requires subselects to select the
+ // proper range; alias all column values so that they are unique within
+ // the subselect
+ SQLBuffer selectSQL = new SQLBuffer(this);
+ List aliases;
+ if (distinctIdentifiers)
+ aliases = sel.getIdentifierAliases();
+ else
+ aliases = sel.getSelectAliases();
+
+ Object alias;
+ int i = 0;
+ for (Iterator itr = aliases.iterator(); itr.hasNext(); i++) {
+ alias = itr.next();
+ if (alias instanceof SQLBuffer)
+ selectSQL.append((SQLBuffer) alias);
+ else
+ selectSQL.append(alias.toString());
+ selectSQL.append(" AS c").append(String.valueOf(i));
+ if (itr.hasNext())
+ selectSQL.append(", ");
+ }
+ return selectSQL;
+ }
+
+ public boolean canOuterJoin(int syntax, ForeignKey fk) {
+ if (!super.canOuterJoin(syntax, fk))
+ return false;
+ if (fk != null && syntax == SYNTAX_DATABASE) {
+ if (fk.getConstants().length > 0)
+ return false;
+ if (fk.getPrimaryKeyConstants().length > 0)
+ return false;
+ }
+ return true;
+ }
+
+ public SQLBuffer toNativeJoin(Join join) {
+ if (join.getType() != Join.TYPE_OUTER)
+ return toTraditionalJoin(join);
+
+ ForeignKey fk = join.getForeignKey();
+ if (fk == null)
+ return null;
+
+ boolean inverse = join.isForeignKeyInversed();
+ Column[] from = (inverse) ? fk.getPrimaryKeyColumns()
+ : fk.getColumns();
+ Column[] to = (inverse) ? fk.getColumns()
+ : fk.getPrimaryKeyColumns();
+
+ // do column joins
+ SQLBuffer buf = new SQLBuffer(this);
+ int count = 0;
+ for (int i = 0; i < from.length; i++, count++) {
+ if (count > 0)
+ buf.append(" AND ");
+ buf.append(join.getAlias1()).append(".").append(from[i]);
+ buf.append(" = ");
+ buf.append(join.getAlias2()).append(".").append(to[i]);
+ buf.append("(+)");
+ }
+
+ // check constant joins
+ if (fk.getConstantColumns().length > 0)
+ throw new StoreException(_loc.get("oracle-constant",
+ join.getTable1(), join.getTable2())).setFatal(true);
+
+ if (fk.getConstantPrimaryKeyColumns().length > 0)
+ throw new StoreException(_loc.get("oracle-constant",
+ join.getTable1(), join.getTable2())).setFatal(true);
+ return buf;
+ }
+
+ public SQLBuffer toSelect(SQLBuffer select, JDBCFetchConfiguration fetch,
+ SQLBuffer tables, SQLBuffer where, SQLBuffer group,
+ SQLBuffer having, SQLBuffer order,
+ boolean distinct, boolean forUpdate, long start, long end) {
+ if (!_checkedUpdateBug) {
+ ensureDriverVendor();
+ if (forUpdate && _driverBehavior == BEHAVE_DATADIRECT31)
+ log.warn(_loc.get("dd-lock-bug"));
+ _checkedUpdateBug = true;
+ }
+
+ // if no range, use standard select
+ if (start == 0 && end == Long.MAX_VALUE)
+ return super.toSelect(select, fetch, tables, where, group, having,
+ order, distinct, forUpdate, 0, Long.MAX_VALUE);
+
+ // if no skip, ordering, or distinct can use rownum directly
+ SQLBuffer buf = new SQLBuffer(this);
+ if (!requiresSubselectForRange(start, end, distinct, order)) {
+ if (where != null && !where.isEmpty())
+ buf.append(where).append(" AND ");
+ buf.append("ROWNUM <= ").appendValue(end);
+ return super.toSelect(select, fetch, tables, buf, group, having,
+ order, distinct, forUpdate, 0, Long.MAX_VALUE);
+ }
+
+ // if there is ordering, skip, or distinct we have to use subselects
+ SQLBuffer sel = super.toSelect(select, fetch, tables, where,
+ group, having, order, distinct, forUpdate, 0, Long.MAX_VALUE);
+
+ // if no skip, can use single nested subselect
+ if (start == 0) {
+ buf.append(getSelectOperation(fetch) + " * FROM (");
+ buf.append(sel);
+ buf.append(") WHERE ROWNUM <= ").appendValue(end);
+ return buf;
+ }
+
+ // with a skip, we have to use a double-nested subselect to put
+ // where conditions on the rownum
+ buf.append(getSelectOperation(fetch)
+ + " * FROM (SELECT r.*, ROWNUM RNUM FROM (");
+ buf.append(sel);
+ buf.append(") r");
+ if (end != Long.MAX_VALUE)
+ buf.append(" WHERE ROWNUM <= ").appendValue(end);
+ buf.append(") WHERE RNUM > ").appendValue(start);
+ return buf;
+ }
+
+ /**
+ * Return true if the select with the given parameters needs a
+ * subselect to apply a range.
+ */
+ private boolean requiresSubselectForRange(long start, long end,
+ boolean distinct, SQLBuffer order) {
+ if (start == 0 && end == Long.MAX_VALUE)
+ return false;
+ return start != 0 || distinct || (order != null && !order.isEmpty());
+ }
+
+ /**
+ * Check to see if we have set the {@link #SELECT_HINT} in the
+ * fetch configuraiton, and if so, append the Orache hint after the
+ * "SELECT" part of the query.
+ */
+ public String getSelectOperation(JDBCFetchConfiguration fetch) {
+ Object hint = fetch == null ? null : fetch.getHint(SELECT_HINT);
+ String select = "SELECT";
+ if (hint != null)
+ select += " " + hint;
+ return select;
+ }
+
+ public void substring(SQLBuffer buf, FilterValue str, FilterValue start,
+ FilterValue end) {
+ buf.append("SUBSTR(");
+ str.appendTo(buf);
+ buf.append(", (");
+ start.appendTo(buf);
+ buf.append(" + 1)");
+ if (end != null) {
+ buf.append(", (");
+ end.appendTo(buf);
+ buf.append(" - ");
+ start.appendTo(buf);
+ buf.append(")");
+ }
+ buf.append(")");
+ }
+
+ public void setString(PreparedStatement stmnt, int idx, String val,
+ Column col)
+ throws SQLException {
+ // oracle NCHAR/NVARCHAR/NCLOB unicode columns require some
+ // special handling to configure them correctly; see:
+ // http://www.oracle.com/technology/sample_code/tech/java/
+ // sqlj_jdbc/files/9i_jdbc/NCHARsupport4UnicodeSample/Readme.html
+ String typeName = (col == null) ? null : col.getTypeName();
+ if (useSetFormOfUseForUnicode && typeName != null &&
+ (typeName.toLowerCase().startsWith("nvarchar") ||
+ typeName.toLowerCase().startsWith("nchar") ||
+ typeName.toLowerCase().startsWith("nclob"))) {
+ Statement inner = stmnt;
+ if (inner instanceof DelegatingPreparedStatement)
+ inner = ((DelegatingPreparedStatement) inner).
+ getInnermostDelegate();
+ if (isOraclePreparedStatement(inner)) {
+ try {
+ inner.getClass().getMethod("setFormOfUse",
+ new Class[]{ int.class, short.class }).
+ invoke(inner,
+ new Object[]{
+ new Integer(idx),
+ Class.forName
+ ("oracle.jdbc.OraclePreparedStatement").
+ getField("FORM_NCHAR").get(null)
+ });
+ return;
+ } catch (Exception e) {
+ log.warn(e);
+ }
+ } else if (!_warnedNcharColumn && log.isWarnEnabled()) {
+ _warnedNcharColumn = true;
+ log.warn(_loc.get("unconfigured-nchar-cols"));
+ }
+ }
+
+ // call setFixedCHAR for fixed width character columns to get padding
+ // semantics
+ if (col != null && col.getType() == Types.CHAR
+ && val != null && val.length() != col.getSize()) {
+ Statement inner = stmnt;
+ if (inner instanceof DelegatingPreparedStatement)
+ inner = ((DelegatingPreparedStatement) inner).
+ getInnermostDelegate();
+ if (isOraclePreparedStatement(inner)) {
+ try {
+ inner.getClass().getMethod("setFixedCHAR",
+ new Class[]{ int.class, String.class }).
+ invoke(inner, new Object[]{ new Integer(idx), val });
+ return;
+ } catch (Exception e) {
+ log.warn(e);
+ }
+ }
+
+ if (!_warnedCharColumn && log.isWarnEnabled()) {
+ _warnedCharColumn = true;
+ log.warn(_loc.get("unpadded-char-cols"));
+ }
+ }
+ super.setString(stmnt, idx, val, col);
+ }
+
+ public void setNull(PreparedStatement stmnt, int idx, int colType,
+ Column col)
+ throws SQLException {
+ if (colType == Types.BLOB && _driverBehavior == BEHAVE_ORACLE)
+ stmnt.setBlob(idx, getEmptyBlob());
+ else if (colType == Types.CLOB && _driverBehavior == BEHAVE_ORACLE)
+ stmnt.setClob(idx, getEmptyClob());
+ else if ((colType == Types.STRUCT || colType == Types.OTHER)
+ && col != null && col.getTypeName() != null)
+ stmnt.setNull(idx, Types.STRUCT, col.getTypeName());
+ // some versions of the Oracle JDBC driver will fail if calling
+ // setNull with DATE; see bug #1171
+ else if (colType == Types.DATE)
+ super.setNull(stmnt, idx, Types.TIMESTAMP, col);
+ else
+ super.setNull(stmnt, idx, colType, col);
+ }
+
+ public String getClobString(ResultSet rs, int column)
+ throws SQLException {
+ if (_driverBehavior != BEHAVE_ORACLE)
+ return super.getClobString(rs, column);
+
+ Clob clob = getClob(rs, column);
+ if (clob == null)
+ return null;
+ if (clob.getClass().getName().equals("oracle.sql.CLOB")) {
+ try {
+ if (((Boolean) Class.forName("oracle.sql.CLOB").
+ getMethod("isEmptyLob", new Class[0]).
+ invoke(clob, new Object[0])).
+ booleanValue())
+ return null;
+ } catch (Exception e) {
+ // possibly different version of the driver
+ }
+ }
+ if (clob.length() == 0)
+ return null;
+
+ // unlikely that we'll have strings over 4 billion chars
+ return clob.getSubString(1, (int) clob.length());
+ }
+
+ public Timestamp getTimestamp(ResultSet rs, int column, Calendar cal)
+ throws SQLException {
+ if (cal == null)
+ return super.getTimestamp(rs, column, cal);
+
+ // handle Oracle bug where nanos not returned from call with Calendar
+ // parameter
+ Timestamp ts = rs.getTimestamp(column, cal);
+ if (ts != null && ts.getNanos() == 0)
+ ts.setNanos(rs.getTimestamp(column).getNanos());
+ return ts;
+ }
+
+ public Object getObject(ResultSet rs, int column, Map map)
+ throws SQLException {
+ // recent oracle dirvers return oracle-specific types for timestamps
+ // and dates
+ Object obj = super.getObject(rs, column, map);
+ if (obj == null)
+ return null;
+ if ("oracle.sql.DATE".equals(obj.getClass().getName()))
+ obj = convertFromOracleType(obj, "dateValue");
+ else if ("oracle.sql.TIMESTAMP".equals(obj.getClass().getName()))
+ obj = convertFromOracleType(obj, "timestampValue");
+ return obj;
+ }
+
+ /**
+ * Convert an object from its proprietary Oracle type to the standard
+ * Java type.
+ */
+ private static Object convertFromOracleType(Object obj,
+ String convertMethod)
+ throws SQLException {
+ try {
+ Method m = obj.getClass().getMethod(convertMethod, (Class[]) null);
+ return m.invoke(obj, (Object[]) null);
+ } catch (Throwable t) {
+ if (t instanceof InvocationTargetException)
+ t = ((InvocationTargetException) t).getTargetException();
+ if (t instanceof SQLException)
+ throw(SQLException) t;
+ throw new SQLException(t.getMessage());
+ }
+ }
+
+ public Column[] getColumns(DatabaseMetaData meta, String catalog,
+ String schemaName, String tableName, String columnName, Connection conn)
+ throws SQLException {
+ Column[] cols = super.getColumns(meta, catalog, schemaName, tableName,
+ columnName, conn);
+
+ for (int i = 0; cols != null && i < cols.length; i++) {
+ if (cols[i].getTypeName() == null)
+ continue;
+ if (cols[i].getTypeName().toUpperCase().startsWith("TIMESTAMP"))
+ cols[i].setType(Types.TIMESTAMP);
+ else if ("BLOB".equalsIgnoreCase(cols[i].getTypeName()))
+ cols[i].setType(Types.BLOB);
+ else if ("CLOB".equalsIgnoreCase(cols[i].getTypeName())
+ || "NCLOB".equalsIgnoreCase(cols[i].getTypeName()))
+ cols[i].setType(Types.CLOB);
+ else if ("FLOAT".equalsIgnoreCase(cols[i].getTypeName()))
+ cols[i].setType(Types.FLOAT);
+ else if ("NVARCHAR".equalsIgnoreCase(cols[i].getTypeName()))
+ cols[i].setType(Types.VARCHAR);
+ else if ("NCHAR".equalsIgnoreCase(cols[i].getTypeName()))
+ cols[i].setType(Types.CHAR);
+ }
+ return cols;
+ }
+
+ public PrimaryKey[] getPrimaryKeys(DatabaseMetaData meta,
+ String catalog, String schemaName, String tableName, Connection conn)
+ throws SQLException {
+ StringBuffer buf = new StringBuffer();
+ buf.append("SELECT t0.OWNER AS TABLE_SCHEM, ").
+ append("t0.TABLE_NAME AS TABLE_NAME, ").
+ append("t0.COLUMN_NAME AS COLUMN_NAME, ").
+ append("t0.CONSTRAINT_NAME AS PK_NAME ").
+ append("FROM ALL_CONS_COLUMNS t0, ALL_CONSTRAINTS t1 ").
+ append("WHERE t0.OWNER = t1.OWNER ").
+ append("AND t0.CONSTRAINT_NAME = t1.CONSTRAINT_NAME ").
+ append("AND t1.CONSTRAINT_TYPE = 'P'");
+ if (schemaName != null)
+ buf.append(" AND t0.OWNER = ?");
+ if (tableName != null)
+ buf.append(" AND t0.TABLE_NAME = ?");
+
+ PreparedStatement stmnt = conn.prepareStatement(buf.toString());
+ ResultSet rs = null;
+ try {
+ int idx = 1;
+ if (schemaName != null)
+ setString(stmnt, idx++, schemaName.toUpperCase(), null);
+ if (tableName != null)
+ setString(stmnt, idx++, tableName.toUpperCase(), null);
+
+ rs = stmnt.executeQuery();
+ List pkList = new ArrayList();
+ while (rs != null && rs.next())
+ pkList.add(newPrimaryKey(rs));
+ return (PrimaryKey[]) pkList.toArray
+ (new PrimaryKey[pkList.size()]);
+ } finally {
+ if (rs != null)
+ try {
+ rs.close();
+ } catch (Exception e) {
+ }
+ try {
+ stmnt.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ public Index[] getIndexInfo(DatabaseMetaData meta, String catalog,
+ String schemaName, String tableName, boolean unique, boolean approx,
+ Connection conn)
+ throws SQLException {
+ StringBuffer buf = new StringBuffer();
+ buf.append("SELECT t0.INDEX_OWNER AS TABLE_SCHEM, ").
+ append("t0.TABLE_NAME AS TABLE_NAME, ").
+ append("DECODE(t1.UNIQUENESS, 'UNIQUE', 0, 'NONUNIQUE', 1) ").
+ append("AS NON_UNIQUE, ").
+ append("t0.INDEX_NAME AS INDEX_NAME, ").
+ append("t0.COLUMN_NAME AS COLUMN_NAME ").
+ append("FROM ALL_IND_COLUMNS t0, ALL_INDEXES t1 ").
+ append("WHERE t0.INDEX_OWNER = t1.OWNER ").
+ append("AND t0.INDEX_NAME = t1.INDEX_NAME");
+ if (schemaName != null)
+ buf.append(" AND t0.TABLE_OWNER = ?");
+ if (tableName != null)
+ buf.append(" AND t0.TABLE_NAME = ?");
+
+ PreparedStatement stmnt = conn.prepareStatement(buf.toString());
+ ResultSet rs = null;
+ try {
+ int idx = 1;
+ if (schemaName != null)
+ setString(stmnt, idx++, schemaName.toUpperCase(), null);
+ if (tableName != null)
+ setString(stmnt, idx++, tableName.toUpperCase(), null);
+
+ rs = stmnt.executeQuery();
+ List idxList = new ArrayList();
+ while (rs != null && rs.next())
+ idxList.add(newIndex(rs));
+ return (Index[]) idxList.toArray(new Index[idxList.size()]);
+ } finally {
+ if (rs != null)
+ try {
+ rs.close();
+ } catch (Exception e) {
+ }
+ try {
+ stmnt.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ public ForeignKey[] getImportedKeys(DatabaseMetaData meta, String catalog,
+ String schemaName, String tableName, Connection conn)
+ throws SQLException {
+ StringBuffer delAction = new StringBuffer("DECODE(t1.DELETE_RULE").
+ append(", 'NO ACTION', ").append(meta.importedKeyNoAction).
+ append(", 'RESTRICT', ").append(meta.importedKeyRestrict).
+ append(", 'CASCADE', ").append(meta.importedKeyCascade).
+ append(", 'SET NULL', ").append(meta.importedKeySetNull).
+ append(", 'SET DEFAULT', ").append(meta.importedKeySetDefault).
+ append(")");
+
+ StringBuffer buf = new StringBuffer();
+ buf.append("SELECT t2.OWNER AS PKTABLE_SCHEM, ").
+ append("t2.TABLE_NAME AS PKTABLE_NAME, ").
+ append("t2.COLUMN_NAME AS PKCOLUMN_NAME, ").
+ append("t0.OWNER AS FKTABLE_SCHEM, ").
+ append("t0.TABLE_NAME AS FKTABLE_NAME, ").
+ append("t0.COLUMN_NAME AS FKCOLUMN_NAME, ").
+ append("t0.POSITION AS KEY_SEQ, ").
+ append(delAction).append(" AS DELETE_RULE, ").
+ append("t0.CONSTRAINT_NAME AS FK_NAME, ").
+ append("DECODE(t1.DEFERRED, 'DEFERRED', ").
+ append(meta.importedKeyInitiallyDeferred).
+ append(", 'IMMEDIATE', ").
+ append(meta.importedKeyInitiallyImmediate).
+ append(") AS DEFERRABILITY ").
+ append("FROM ALL_CONS_COLUMNS t0, ALL_CONSTRAINTS t1, ").
+ append("ALL_CONS_COLUMNS t2 ").
+ append("WHERE t0.OWNER = t1.OWNER ").
+ append("AND t0.CONSTRAINT_NAME = t1.CONSTRAINT_NAME ").
+ append("AND t1.CONSTRAINT_TYPE = 'R' ").
+ append("AND t1.R_OWNER = t2.OWNER ").
+ append("AND t1.R_CONSTRAINT_NAME = t2.CONSTRAINT_NAME ").
+ append("AND t0.POSITION = t2.POSITION");
+ if (schemaName != null)
+ buf.append(" AND t0.OWNER = ?");
+ if (tableName != null)
+ buf.append(" AND t0.TABLE_NAME = ?");
+ buf.append(" ORDER BY t2.OWNER, t2.TABLE_NAME, t0.POSITION");
+
+ PreparedStatement stmnt = conn.prepareStatement(buf.toString());
+ ResultSet rs = null;
+ try {
+ int idx = 1;
+ if (schemaName != null)
+ setString(stmnt, idx++, schemaName.toUpperCase(), null);
+ if (tableName != null)
+ setString(stmnt, idx++, tableName.toUpperCase(), null);
+
+ rs = stmnt.executeQuery();
+ List fkList = new ArrayList();
+ while (rs != null && rs.next())
+ fkList.add(newForeignKey(rs));
+ return (ForeignKey[]) fkList.toArray
+ (new ForeignKey[fkList.size()]);
+ } finally {
+ if (rs != null)
+ try {
+ rs.close();
+ } catch (Exception e) {
+ }
+ try {
+ stmnt.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ public String[] getCreateTableSQL(Table table) {
+ // only override if we are simulating auto-incremenet with triggers
+ String[] create = super.getCreateTableSQL(table);
+ if (!useTriggersForAutoAssign)
+ return create;
+
+ Column[] cols = table.getColumns();
+ List seqs = null;
+ String seq, trig;
+ for (int i = 0; cols != null && i < cols.length; i++) {
+ if (!cols[i].isAutoAssigned())
+ continue;
+ if (seqs == null)
+ seqs = new ArrayList(4);
+
+ seq = autoAssignSequenceName;
+ if (seq == null) {
+ if (openjpa3GeneratedKeyNames)
+ seq = getOpenJPA3GeneratedKeySequenceName(cols[i]);
+ else
+ seq = getGeneratedKeySequenceName(cols[i]);
+ seqs.add("CREATE SEQUENCE " + seq + " START WITH 1");
+ }
+ if (openjpa3GeneratedKeyNames)
+ trig = getOpenJPA3GeneratedKeyTriggerName(cols[i]);
+ else
+ trig = getGeneratedKeyTriggerName(cols[i]);
+
+ // create the trigger that will insert new values into
+ // the table whenever a row is created
+ seqs.add("CREATE OR REPLACE TRIGGER " + trig
+ + " BEFORE INSERT ON " + table.getName()
+ + " FOR EACH ROW BEGIN SELECT " + seq + ".nextval INTO "
+ + ":new." + cols[i].getName() + " FROM DUAL; "
+ + "END " + trig + ";");
+ }
+ if (seqs == null)
+ return create;
+
+ // combine create table sql and create seqences sql
+ String[] sql = new String[create.length + seqs.size()];
+ System.arraycopy(create, 0, sql, 0, create.length);
+ for (int i = 0; i < seqs.size(); i++)
+ sql[create.length + i] = (String) seqs.get(i);
+ return sql;
+ }
+
+ public String[] getCreateSequenceSQL(Sequence seq) {
+ String[] sql = super.getCreateSequenceSQL(seq);
+ if (seq.getAllocate() > 1)
+ sql[0] += " CACHE " + seq.getAllocate();
+ return sql;
+ }
+
+ protected String getSequencesSQL(String schemaName, String sequenceName) {
+ StringBuffer buf = new StringBuffer();
+ buf.append("SELECT SEQUENCE_OWNER AS SEQUENCE_SCHEMA, ").
+ append("SEQUENCE_NAME FROM ALL_SEQUENCES");
+ if (schemaName != null || sequenceName != null)
+ buf.append(" WHERE ");
+ if (schemaName != null) {
+ buf.append("SEQUENCE_OWNER = ?");
+ if (sequenceName != null)
+ buf.append(" AND ");
+ }
+ if (sequenceName != null)
+ buf.append("SEQUENCE_NAME = ?");
+ return buf.toString();
+ }
+
+ public boolean isSystemSequence(String name, String schema,
+ boolean targetSchema) {
+ if (super.isSystemSequence(name, schema, targetSchema))
+ return true;
+
+ // filter out generated sequences used for auto-assign
+ return (autoAssignSequenceName != null
+ && name.equalsIgnoreCase(autoAssignSequenceName))
+ || (autoAssignSequenceName == null
+ && name.toUpperCase().startsWith("ST_"));
+ }
+
+ public Object getGeneratedKey(Column col, Connection conn)
+ throws SQLException {
+ if (!useTriggersForAutoAssign)
+ return Numbers.valueOf(0L);
+
+ // if we simulate auto-assigned columns using triggers and
+ // sequences, then return the current value of the sequence
+ // from autoAssignSequenceName
+ String seq = autoAssignSequenceName;
+ if (seq == null && openjpa3GeneratedKeyNames)
+ seq = getOpenJPA3GeneratedKeySequenceName(col);
+ else if (seq == null)
+ seq = getGeneratedKeySequenceName(col);
+ PreparedStatement stmnt = conn.prepareStatement("SELECT " + seq
+ + ".currval FROM DUAL");
+ ResultSet rs = null;
+ try {
+ rs = stmnt.executeQuery();
+ rs.next();
+ return Numbers.valueOf(rs.getLong(1));
+ } finally {
+ if (rs != null)
+ try {
+ rs.close();
+ } catch (SQLException se) {
+ }
+ try {
+ stmnt.close();
+ } catch (SQLException se) {
+ }
+ }
+ }
+
+ /**
+ * Trigger name for simulating auto-assign values on the given column.
+ */
+ protected String getGeneratedKeyTriggerName(Column col) {
+ // replace trailing _SEQ with _TRG
+ String seqName = getGeneratedKeySequenceName(col);
+ return seqName.substring(0, seqName.length() - 3) + "TRG";
+ }
+
+ /**
+ * Returns a OpenJPA 3-compatible name for an auto-assign sequence.
+ */
+ private String getOpenJPA3GeneratedKeySequenceName(Column col) {
+ Table table = col.getTable();
+ return makeNameValid("SEQ_" + table.getName(), table.getSchema().
+ getSchemaGroup(), maxTableNameLength, NAME_ANY);
+ }
+
+ /**
+ * Returns a OpenJPA 3-compatible name for an auto-assign trigger.
+ */
+ private String getOpenJPA3GeneratedKeyTriggerName(Column col) {
+ Table table = col.getTable();
+ return makeNameValid("TRIG_" + table.getName(), table.getSchema().
+ getSchemaGroup(), maxTableNameLength, NAME_ANY);
+ }
+
+ /**
+ * Invoke Oracle's <code>putBytes</code> method on the given BLOB object.
+ * Uses reflection in case the blob is wrapped in another
+ * vendor-specific class; for example Weblogic wraps oracle thin driver
+ * lobs in its own interfaces with the same methods.
+ */
+ public void putBytes(Object blob, byte[] data)
+ throws SQLException {
+ if (blob == null)
+ return;
+ if (_putBytes == null) {
+ try {
+ _putBytes = blob.getClass().getMethod("putBytes",
+ new Class[]{ long.class, byte[].class });
+ } catch (Exception e) {
+ throw new StoreException(e);
+ }
+ }
+ invokePutLobMethod(_putBytes, blob, data);
+ }
+
+ /**
+ * Invoke Oracle's <code>putString</code> method on the given CLOB object.
+ * Uses reflection in case the clob is wrapped in another
+ * vendor-specific class; for example Weblogic wraps oracle thin driver
+ * lobs in its own interfaces with the same methods.
+ */
+ public void putString(Object clob, String data)
+ throws SQLException {
+ if (_putString == null) {
+ try {
+ _putString = clob.getClass().getMethod("putString",
+ new Class[]{ long.class, String.class });
+ } catch (Exception e) {
+ throw new StoreException(e);
+ }
+ }
+ invokePutLobMethod(_putString, clob, data);
+ }
+
+ /**
+ * Invoke Oracle's <code>putChars</code> method on the given CLOB
+ * object. Uses reflection in case the clob is wrapped in another
+ * vendor-specific class; for example Weblogic wraps oracle thin driver
+ * lobs in its own interfaces with the same methods.
+ */
+ public void putChars(Object clob, char[] data)
+ throws SQLException {
+ if (_putChars == null) {
+ try {
+ _putChars = clob.getClass().getMethod("putChars",
+ new Class[]{ long.class, char[].class });
+ } catch (Exception e) {
+ throw new StoreException(e);
+ }
+ }
+ invokePutLobMethod(_putChars, clob, data);
+ }
+
+ /**
+ * Invoke the given LOB method on the given target with the given data.
+ */
+ private static void invokePutLobMethod(Method method, Object target,
+ Object data)
+ throws SQLException {
+ try {
+ method.invoke(target, new Object[]{ Numbers.valueOf(1L), data });
+ } catch (InvocationTargetException ite) {
+ Throwable t = ite.getTargetException();
+ if (t instanceof SQLException)
+ throw(SQLException) t;
+ throw new StoreException(t);
+ } catch (Exception e) {
+ throw new StoreException(e);
+ }
+ }
+
+ private static Clob getEmptyClob()
+ throws SQLException {
+ if (EMPTY_CLOB != null)
+ return EMPTY_CLOB;
+ try {
+ return EMPTY_CLOB = (Clob) Class.forName("oracle.sql.CLOB").
+ getMethod("empty_lob", new Class[0]).
+ invoke(null, new Object[0]);
+ } catch (Exception e) {
+ throw new SQLException(e.getMessage());
+ }
+ }
+
+ private static Blob getEmptyBlob()
+ throws SQLException {
+ if (EMPTY_BLOB != null)
+ return EMPTY_BLOB;
+ try {
+ return EMPTY_BLOB = (Blob) Class.forName("oracle.sql.BLOB").
+ getMethod("empty_lob", new Class[0]).
+ invoke(null, new Object[0]);
+ } catch (Exception e) {
+ throw new SQLException(e.getMessage());
+ }
+ }
+
+ private static boolean isOraclePreparedStatement(Statement stmnt) {
+ try {
+ return Class.forName("oracle.jdbc.OraclePreparedStatement").
+ isInstance(stmnt);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PointbaseDictionary.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PointbaseDictionary.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PointbaseDictionary.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PointbaseDictionary.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2006 The 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 java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.apache.openjpa.jdbc.kernel.exps.FilterValue;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.Index;
+
+/**
+ * Dictionary for Pointbase Embedded.
+ */
+public class PointbaseDictionary
+ extends DBDictionary {
+
+ public PointbaseDictionary() {
+ platform = "Pointbase Embedded";
+ supportsDeferredConstraints = false;
+ supportsMultipleNontransactionalResultSets = false;
+ requiresAliasForSubselect = true;
+
+ supportsLockingWithDistinctClause = false;
+ supportsLockingWithMultipleTables = false;
+ supportsLockingWithDistinctClause = false;
+
+ bitTypeName = "TINYINT";
+ blobTypeName = "BLOB(1M)";
+ longVarbinaryTypeName = "BLOB(1M)";
+ charTypeName = "CHARACTER{0}";
+ clobTypeName = "CLOB(1M)";
+ doubleTypeName = "DOUBLE PRECISION";
+ floatTypeName = "FLOAT";
+ bigintTypeName = "BIGINT";
+ integerTypeName = "INTEGER";
+ realTypeName = "REAL";
+ smallintTypeName = "SMALLINT";
+ tinyintTypeName = "TINYINT";
+
+ // there is no build-in function for getting the last generated
+ // key in Pointbase; using MAX will have to suffice
+ supportsAutoAssign = true;
+ lastGeneratedKeyQuery = "SELECT MAX({0}) FROM {1}";
+ autoAssignTypeName = "BIGINT IDENTITY";
+ }
+
+ public int getPreferredType(int type) {
+ switch (type) {
+ case Types.LONGVARCHAR:
+ return Types.CLOB;
+ default:
+ return super.getPreferredType(type);
+ }
+ }
+
+ public Column[] getColumns(DatabaseMetaData meta, String catalog,
+ String schemaName, String tableName, String columnName, Connection conn)
+ throws SQLException {
+ Column[] cols = super.getColumns(meta, catalog, schemaName, tableName,
+ columnName, conn);
+
+ // pointbase reports the type for a CLOB field as VARCHAR: override it
+ for (int i = 0; cols != null && i < cols.length; i++)
+ if (cols[i].getTypeName().toUpperCase().startsWith("CLOB"))
+ cols[i].setType(Types.CLOB);
+ return cols;
+ }
+
+ public String getFullName(Index index) {
+ return getFullName(index.getTable(), false) + "." + index.getName();
+ }
+
+ public void substring(SQLBuffer buf, FilterValue str, FilterValue start,
+ FilterValue end) {
+ // SUBSTRING in Pointbase is of the form:
+ // SELECT SUBSTRING(SOME_COLUMN FROM 1 FOR 5)
+ buf.append("SUBSTRING(");
+ str.appendTo(buf);
+ buf.append(" FROM ");
+ start.appendTo(buf);
+ buf.append(" + 1");
+ if (end != null) {
+ buf.append(" FOR ");
+ end.appendTo(buf);
+ buf.append(" - (");
+ start.appendTo(buf);
+ buf.append(")");
+ }
+ buf.append(")");
+ }
+
+ public void indexOf(SQLBuffer buf, FilterValue str, FilterValue find,
+ FilterValue start) {
+ buf.append("(POSITION(");
+ find.appendTo(buf);
+ buf.append(" IN ");
+ if (start != null)
+ substring(buf, str, start, null);
+ else
+ str.appendTo(buf);
+ buf.append(") - 1");
+ if (start != null) {
+ buf.append(" + ");
+ start.appendTo(buf);
+ }
+ buf.append(")");
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PointbaseDictionary.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2006 The 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 java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+
+import org.apache.openjpa.jdbc.kernel.exps.FilterValue;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.Sequence;
+import org.apache.openjpa.jdbc.schema.Table;
+import org.apache.openjpa.lib.jdbc.DelegatingConnection;
+import org.apache.openjpa.lib.jdbc.DelegatingPreparedStatement;
+import org.apache.openjpa.lib.util.Localizer;
+
+/**
+ * Dictionary for Postgres.
+ */
+public class PostgresDictionary
+ extends DBDictionary {
+
+ private static final Localizer _loc = Localizer.forPackage
+ (PostgresDictionary.class);
+
+ /**
+ * SQL statement to load all sequence schema,name pairs from all schemas.
+ */
+ public String allSequencesSQL = "SELECT NULL AS SEQUENCE_SCHEMA, relname " +
+ "AS SEQUENCE_NAME FROM pg_class WHERE relkind='S'";
+
+ /**
+ * SQL statement to load schema,name pairs for all sequences with a
+ * certain name from all schemas.
+ */
+ public String namedSequencesFromAllSchemasSQL = "SELECT NULL AS " +
+ "SEQUENCE_SCHEMA, relname AS SEQUENCE_NAME FROM pg_class " +
+ "WHERE relkind='S' AND relname = ?";
+
+ /**
+ * SQL statement to load schema,name pairs from a named schema.
+ */
+ public String allSequencesFromOneSchemaSQL = "SELECT NULL AS " +
+ "SEQUENCE_SCHEMA, relname AS SEQUENCE_NAME FROM pg_class, " +
+ "pg_namespace WHERE relkind='S' AND pg_class.relnamespace = " +
+ "pg_namespace.oid AND nspname = ?";
+
+ /**
+ * SQL statement to load a sequence's schema,name pair from one schema.
+ */
+ public String namedSequenceFromOneSchemaSQL = "SELECT NULL AS " +
+ "SEQUENCE_SCHEMA, relname AS SEQUENCE_NAME FROM pg_class, " +
+ "pg_namespace WHERE relkind='S' AND pg_class.relnamespace = " +
+ "pg_namespace.oid AND relname = ? AND nspname = ?";
+
+ /**
+ * Some Postgres drivers do not support the {@link Statement#setFetchSize}
+ * method.
+ */
+ public boolean supportsSetFetchSize = true;
+
+ public PostgresDictionary() {
+ platform = "PostgreSQL";
+ validationSQL = "SELECT NOW()";
+ datePrecision = CENTI;
+ supportsAlterTableWithDropColumn = false;
+ supportsDeferredConstraints = true;
+ supportsSelectStartIndex = true;
+ supportsSelectEndIndex = true;
+
+ // PostgreSQL requires double-escape for strings
+ searchStringEscape = "\\\\";
+
+ maxTableNameLength = 31;
+ maxColumnNameLength = 31;
+ maxIndexNameLength = 31;
+ maxConstraintNameLength = 31;
+ schemaCase = SCHEMA_CASE_LOWER;
+ requiresAliasForSubselect = true;
+ allowsAliasInBulkClause = false;
+
+ // {2} is the result of getGeneratedKeySequenceName; the
+ // single-quote escape will result in SELECT CURVAL('mysequence')
+ lastGeneratedKeyQuery = "SELECT CURRVAL(''{2}'')";
+ supportsAutoAssign = true;
+ autoAssignTypeName = "BIGSERIAL";
+ nextSequenceQuery = "SELECT NEXTVAL(''{0}'')";
+
+ useGetBytesForBlobs = true;
+ useSetBytesForBlobs = true;
+ useGetStringForClobs = true;
+ useSetStringForClobs = true;
+ bitTypeName = "BOOL";
+ smallintTypeName = "SMALLINT";
+ realTypeName = "FLOAT8";
+ tinyintTypeName = "SMALLINT";
+ binaryTypeName = "BYTEA";
+ blobTypeName = "BYTEA";
+ longVarbinaryTypeName = "BYTEA";
+ varbinaryTypeName = "BYTEA";
+ clobTypeName = "TEXT";
+ longVarcharTypeName = "TEXT";
+ doubleTypeName = "DOUBLE PRECISION";
+ varcharTypeName = "VARCHAR{0}";
+ timestampTypeName = "ABSTIME";
+ fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{
+ "BOOL", "BYTEA", "NAME", "INT8", "INT2", "INT2VECTOR", "INT4",
+ "REGPROC", "TEXT", "OID", "TID", "XID", "CID", "OIDVECTOR",
+ "SET", "FLOAT4", "FLOAT8", "ABSTIME", "RELTIME", "TINTERVAL",
+ "MONEY",
+ }));
+
+ supportsLockingWithDistinctClause = false;
+ supportsLockingWithOuterJoin = false;
+ supportsNullTableForGetImportedKeys = true;
+
+ reservedWordSet.addAll(Arrays.asList(new String[]{
+ "ABORT", "ACL", "AGGREGATE", "APPEND", "ARCHIVE", "ARCH_STORE",
+ "BACKWARD", "BINARY", "CHANGE", "CLUSTER", "COPY", "DATABASE",
+ "DELIMITER", "DELIMITERS", "DO", "EXPLAIN", "EXTEND",
+ "FORWARD", "HEAVY", "INDEX", "INHERITS", "ISNULL", "LIGHT",
+ "LISTEN", "LOAD", "MERGE", "NOTHING", "NOTIFY", "NOTNULL",
+ "OID", "OIDS", "PURGE", "RECIPE", "RENAME", "REPLACE",
+ "RETRIEVE", "RETURNS", "RULE", "SETOF", "STDIN", "STDOUT",
+ "STORE", "VACUUM", "VERBOSE", "VERSION",
+ }));
+ }
+
+ public Date getDate(ResultSet rs, int column)
+ throws SQLException {
+ try {
+ return super.getDate(rs, column);
+ } catch (StringIndexOutOfBoundsException sioobe) {
+ // there is a bug in some versions of the postgres JDBC
+ // driver such that a date with not enough numbers in it
+ // will throw a parsing exception: this tries to work
+ // around it. The bug only occurs when there is a trailing
+ // millisecond missing from the end. E.g., when the date is
+ // like:
+ // 2066-10-19 22:08:32.83
+ // rather than what the driver expects:
+ // 2066-10-19 22:08:32.830
+ String dateStr = rs.getString(column);
+ SimpleDateFormat fmt = new SimpleDateFormat(
+ "yyyy-MM-dd hh:mm:ss.SS");
+ try {
+ return fmt.parse(dateStr);
+ } catch (ParseException pe) {
+ throw new SQLException(pe.toString());
+ }
+ }
+ }
+
+ public byte getByte(ResultSet rs, int column)
+ throws SQLException {
+ // postgres does not perform automatic conversions, so attempting to
+ // get a whole number out of a decimal will throw an exception.
+ // fall back to performing manual conversion if the initial get fails
+ try {
+ return super.getByte(rs, column);
+ } catch (SQLException sqle) {
+ return super.getBigDecimal(rs, column).byteValue();
+ }
+ }
+
+ public short getShort(ResultSet rs, int column)
+ throws SQLException {
+ // postgres does not perform automatic conversions, so attempting to
+ // get a whole number out of a decimal will throw an exception.
+ // fall back to performing manual conversion if the initial get fails
+ try {
+ return super.getShort(rs, column);
+ } catch (SQLException sqle) {
+ return super.getBigDecimal(rs, column).shortValue();
+ }
+ }
+
+ public int getInt(ResultSet rs, int column)
+ throws SQLException {
+ // postgres does not perform automatic conversions, so attempting to
+ // get a whole number out of a decimal will throw an exception.
+ // fall back to performing manual conversion if the initial get fails
+ try {
+ return super.getInt(rs, column);
+ } catch (SQLException sqle) {
+ return super.getBigDecimal(rs, column).intValue();
+ }
+ }
+
+ public long getLong(ResultSet rs, int column)
+ throws SQLException {
+ // postgres does not perform automatic conversions, so attempting to
+ // get a whole number out of a decimal will throw an exception.
+ // fall back to performing manual conversion if the initial get fails
+ try {
+ return super.getLong(rs, column);
+ } catch (SQLException sqle) {
+ return super.getBigDecimal(rs, column).longValue();
+ }
+ }
+
+ public void setBoolean(PreparedStatement stmnt, int idx, boolean val,
+ Column col)
+ throws SQLException {
+ // postgres actually requires that a boolean be set: it cannot
+ // handle a numeric argument.
+ stmnt.setBoolean(idx, val);
+ }
+
+ protected void appendSelectRange(SQLBuffer buf, long start, long end) {
+ if (end != Long.MAX_VALUE)
+ buf.append(" LIMIT ").appendValue(end - start);
+ if (start != 0)
+ buf.append(" OFFSET ").appendValue(start);
+ }
+
+ public void indexOf(SQLBuffer buf, FilterValue str, FilterValue find,
+ FilterValue start) {
+ buf.append("(POSITION(");
+ find.appendTo(buf);
+ buf.append(" IN ");
+ if (start != null)
+ substring(buf, str, start, null);
+ else
+ str.appendTo(buf);
+ buf.append(") - 1");
+ if (start != null) {
+ buf.append(" + ");
+ start.appendTo(buf);
+ }
+ buf.append(")");
+ }
+
+ public String[] getCreateSequenceSQL(Sequence seq) {
+ String[] sql = super.getCreateSequenceSQL(seq);
+ if (seq.getAllocate() > 1)
+ sql[0] += " CACHE " + seq.getAllocate();
+ return sql;
+ }
+
+ protected String getSequencesSQL(String schemaName, String sequenceName) {
+ if (schemaName == null && sequenceName == null)
+ return allSequencesSQL;
+ else if (schemaName == null)
+ return namedSequencesFromAllSchemasSQL;
+ else if (sequenceName == null)
+ return allSequencesFromOneSchemaSQL;
+ else
+ return namedSequenceFromOneSchemaSQL;
+ }
+
+ public boolean isSystemSequence(String name, String schema,
+ boolean targetSchema) {
+ if (super.isSystemSequence(name, schema, targetSchema))
+ return true;
+
+ // filter out generated sequences used for bigserial cols, which are
+ // of the form <table>_<col>_seq
+ int idx = name.indexOf('_');
+ return idx != -1 && idx != name.length() - 4
+ && name.toUpperCase().endsWith("_SEQ");
+ }
+
+ public boolean isSystemTable(String name, String schema,
+ boolean targetSchema) {
+ // names starting with "pg_" are reserved for Postgresql internal use
+ return super.isSystemTable(name, schema, targetSchema)
+ || (name != null && name.toLowerCase().startsWith("pg_"));
+ }
+
+ public boolean isSystemIndex(String name, Table table) {
+ // names starting with "pg_" are reserved for Postgresql internal use
+ return super.isSystemIndex(name, table)
+ || (name != null && name.toLowerCase().startsWith("pg_"));
+ }
+
+ public Connection decorate(Connection conn)
+ throws SQLException {
+ return new PostgresConnection(super.decorate(conn), this);
+ }
+
+ /**
+ * Connection wrapper to work around the postgres empty result set bug.
+ */
+ private static class PostgresConnection
+ extends DelegatingConnection {
+
+ private final PostgresDictionary _dict;
+
+ public PostgresConnection(Connection conn, PostgresDictionary dict) {
+ super(conn);
+ _dict = dict;
+ }
+
+ protected PreparedStatement prepareStatement(String sql, boolean wrap)
+ throws SQLException {
+ return new PostgresPreparedStatement(super.prepareStatement
+ (sql, false), this, _dict);
+ }
+
+ protected PreparedStatement prepareStatement(String sql, int rsType,
+ int rsConcur, boolean wrap)
+ throws SQLException {
+ return new PostgresPreparedStatement(super.prepareStatement
+ (sql, rsType, rsConcur, false), this, _dict);
+ }
+ }
+
+ /**
+ * Statement wrapper to work around the postgres empty result set bug.
+ */
+ private static class PostgresPreparedStatement
+ extends DelegatingPreparedStatement {
+
+ private final PostgresDictionary _dict;
+
+ public PostgresPreparedStatement(PreparedStatement ps,
+ Connection conn, PostgresDictionary dict) {
+ super(ps, conn);
+ _dict = dict;
+ }
+
+ protected ResultSet executeQuery(boolean wrap)
+ throws SQLException {
+ try {
+ return super.executeQuery(wrap);
+ } catch (SQLException se) {
+ // we need to make our best guess whether this is the empty
+ // ResultSet bug, since this exception could occur
+ // for other reasons (like an invalid query string). Note
+ // that Postgres error messages are localized, so we
+ // cannot just parse the exception String.
+ ResultSet rs = getResultSet(wrap);
+
+ // ResultSet should be empty: if not, then maybe an
+ // actual error occured
+ if (rs == null)
+ throw se;
+
+ return rs;
+ }
+ }
+
+ public void setFetchSize(int i)
+ throws SQLException {
+ // some postgres drivers do not support the setFetchSize method
+ try {
+ if (_dict.supportsSetFetchSize)
+ super.setFetchSize(i);
+ } catch (SQLException e) {
+ _dict.supportsSetFetchSize = false;
+ if (_dict.log.isWarnEnabled())
+ _dict.log.warn(_loc.get("psql-no-set-fetch-size"), e);
+ }
+ }
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PrimaryRow.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PrimaryRow.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PrimaryRow.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PrimaryRow.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,432 @@
+/*
+ * Copyright 2006 The 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 java.sql.SQLException;
+
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.RelationId;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.ColumnIO;
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.jdbc.schema.Table;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.util.InvalidStateException;
+
+/**
+ * Primary table row that tracks foreign keys and auto-inc columns.
+ *
+ * @author Abe White
+ * @nojavadoc
+ */
+public class PrimaryRow
+ extends RowImpl {
+
+ // VALID flag in superclass uses 2 << 0
+ private static final byte PK_SET = 2 << 1;
+ private static final byte PK_WHERE = 2 << 2;
+ private static final byte PK_FLUSHED = 2 << 3;
+ private static final byte DEPENDENT = 2 << 4;
+
+ private static final Localizer _loc = Localizer.forPackage
+ (PrimaryRow.class);
+
+ private OpenJPAStateManager _pk = null;
+ private ColumnIO _pkIO = null;
+ private OpenJPAStateManager[] _fkSet = null;
+ private ColumnIO[] _fkIO = null;
+ private OpenJPAStateManager[] _fkWhere = null;
+ private OpenJPAStateManager[] _relSet = null;
+ private RelationId[] _callbacks = null;
+ private Object _failed = null;
+ private int _idx = -1;
+
+ /**
+ * Constructor; supply table and action.
+ */
+ public PrimaryRow(Table table, int action, OpenJPAStateManager owner) {
+ super(table, action);
+ _pk = owner;
+ }
+
+ /**
+ * Mark this row as dependent on some other row.
+ */
+ public boolean isDependent() {
+ return (flags & DEPENDENT) > 0;
+ }
+
+ /**
+ * Mark this row as dependent on some other row.
+ */
+ public void setDependent(boolean dependent) {
+ if (dependent)
+ flags |= DEPENDENT;
+ else
+ flags &= ~DEPENDENT;
+ }
+
+ /**
+ * The index of this row in ordered row list.
+ */
+ public int getIndex() {
+ return _idx;
+ }
+
+ /**
+ * The index of this row in ordered row list.
+ */
+ public void setIndex(int idx) {
+ _idx = idx;
+ }
+
+ public Object getFailedObject() {
+ return _failed;
+ }
+
+ public void setFailedObject(Object failed) {
+ _failed = failed;
+ }
+
+ public OpenJPAStateManager getPrimaryKey() {
+ return _pk;
+ }
+
+ public void setPrimaryKey(OpenJPAStateManager sm)
+ throws SQLException {
+ setPrimaryKey(null, sm);
+ }
+
+ public void setPrimaryKey(ColumnIO io, OpenJPAStateManager sm) {
+ _pk = sm;
+ flags |= PK_SET;
+ _pkIO = io;
+
+ // force valid
+ setValid(true);
+ }
+
+ public void wherePrimaryKey(OpenJPAStateManager sm)
+ throws SQLException {
+ _pk = sm;
+ flags |= PK_WHERE;
+
+ // force valid
+ if (getAction() == ACTION_DELETE)
+ setValid(true);
+ }
+
+ /**
+ * Return the I/O information for the given set foreign key.
+ */
+ public ColumnIO getForeignKeyIO(ForeignKey fk) {
+ return (_fkIO == null) ? null : _fkIO[fk.getIndex()];
+ }
+
+ /**
+ * Return the value for the given foreign key. Values not needed for
+ * constraint analyses are not recorded.
+ */
+ public OpenJPAStateManager getForeignKeySet(ForeignKey fk) {
+ return (_fkSet == null) ? null : _fkSet[fk.getIndex()];
+ }
+
+ /**
+ * Return the value for the given foreign key. Values not needed for
+ * constraint analyses are not recorded.
+ */
+ public OpenJPAStateManager getForeignKeyWhere(ForeignKey fk) {
+ return (_fkWhere == null) ? null : _fkWhere[fk.getIndex()];
+ }
+
+ public void setForeignKey(ForeignKey fk, OpenJPAStateManager sm)
+ throws SQLException {
+ setForeignKey(fk, null, sm);
+ }
+
+ public void setForeignKey(ForeignKey fk, ColumnIO io,
+ OpenJPAStateManager sm)
+ throws SQLException {
+ if (!delayForeignKey(fk, sm, true))
+ super.setForeignKey(fk, io, sm);
+ else
+ recordForeignKey(fk, io, sm, true);
+ }
+
+ public void whereForeignKey(ForeignKey fk, OpenJPAStateManager sm)
+ throws SQLException {
+ if (!delayForeignKey(fk, sm, false))
+ super.whereForeignKey(fk, sm);
+ else
+ recordForeignKey(fk, null, sm, false);
+ }
+
+ public void clearForeignKey(ForeignKey fk)
+ throws SQLException {
+ super.clearForeignKey(fk);
+ if (_fkSet != null)
+ _fkSet[fk.getIndex()] = null;
+ if (_fkIO != null)
+ _fkIO[fk.getIndex()] = null;
+ }
+
+ /**
+ * If this is a delete, delay foreign keys to other deleted objects if
+ * the key is restricted. If this is an update or insert, delay foreign
+ * keys to other inserts if the key is not logical. If the foreign key
+ * is to a new record and the columns are auto-inc, record it.
+ */
+ private boolean delayForeignKey(ForeignKey fk, OpenJPAStateManager sm,
+ boolean set) {
+ if (sm == null)
+ return false;
+
+ if (getAction() == ACTION_DELETE)
+ return sm.isDeleted() && !fk.isDeferred()
+ && fk.getDeleteAction() == ForeignKey.ACTION_RESTRICT;
+
+ if (!sm.isNew() || sm.isFlushed())
+ return false;
+ if (!fk.isDeferred() && !fk.isLogical())
+ return true;
+ if (fk.isPrimaryKeyAutoAssigned())
+ return true;
+ return false;
+ }
+
+ /**
+ * Record a delayed foreign key.
+ */
+ private void recordForeignKey(ForeignKey fk, ColumnIO io,
+ OpenJPAStateManager sm, boolean set) {
+ OpenJPAStateManager[] arr = null;
+ if (set) {
+ // force valid
+ if (canSetAny(io, fk.getColumns().length
+ + fk.getConstantColumns().length, false))
+ setValid(true);
+
+ if (_fkSet == null)
+ _fkSet = new OpenJPAStateManager[getTable().
+ getForeignKeys().length];
+ _fkSet[fk.getIndex()] = sm;
+
+ if (_fkIO != null)
+ _fkIO[fk.getIndex()] = io;
+ else if (io != null && ((getAction() == ACTION_INSERT
+ && !io.isAllInsertable(fk, false))
+ || (getAction() != ACTION_INSERT
+ && !io.isAllUpdatable(fk, false)))) {
+ _fkIO = new ColumnIO[_fkSet.length];
+ _fkIO[fk.getIndex()] = io;
+ }
+ } else {
+ // force valid
+ if (getAction() == ACTION_DELETE)
+ setValid(true);
+
+ if (_fkWhere == null)
+ _fkWhere = new OpenJPAStateManager[getTable().
+ getForeignKeys().length];
+ _fkWhere[fk.getIndex()] = sm;
+ }
+ }
+
+ /**
+ * Return the recorded value for the given relation id column. Only
+ * values that are dependent on a new, unflushed auto-assigned instance
+ * are recorded.
+ */
+ public OpenJPAStateManager getRelationIdSet(Column col) {
+ return (_relSet == null) ? null : _relSet[getRelationIdIndex(col)];
+ }
+
+ /**
+ * Return the recorded callbacks for the given relation id column. Only
+ * values that are dependent on a new, unflushed auto-assigned instance
+ * are recorded.
+ */
+ public RelationId getRelationIdCallback(Column col) {
+ return (_callbacks == null) ? null
+ : _callbacks[getRelationIdIndex(col)];
+ }
+
+ public void setRelationId(Column col, OpenJPAStateManager sm,
+ RelationId rel)
+ throws SQLException {
+ if (sm == null || sm.getObjectId() != null || !sm.isNew()
+ || sm.isFlushed() || !isPrimaryKeyAutoAssigned(sm))
+ super.setRelationId(col, sm, rel);
+ else {
+ if (_relSet == null) {
+ Column[] cols = getTable().getRelationIdColumns();
+ _relSet = new OpenJPAStateManager[cols.length];
+ _callbacks = new RelationId[cols.length];
+ }
+ int idx = getRelationIdIndex(col);
+ _relSet[idx] = sm;
+ _callbacks[idx] = rel;
+ }
+ }
+
+ public void clearRelationId(Column col)
+ throws SQLException {
+ super.clearRelationId(col);
+ if (_relSet != null) {
+ int idx = getRelationIdIndex(col);
+ _relSet[idx] = null;
+ _callbacks[idx] = null;
+ }
+ }
+
+ /**
+ * Return the index into our relation id array of the value for the
+ * given column.
+ */
+ private int getRelationIdIndex(Column col) {
+ Column[] cols = getTable().getRelationIdColumns();
+ for (int i = 0; i < cols.length; i++)
+ if (cols[i] == col)
+ return i;
+ return -1;
+ }
+
+ /**
+ * Return true if any primary key columns of the given instance are
+ * auto-assigned.
+ */
+ private static boolean isPrimaryKeyAutoAssigned(OpenJPAStateManager sm) {
+ ClassMapping cls = (ClassMapping) sm.getMetaData();
+ while (cls.getJoinablePCSuperclassMapping() != null)
+ cls = cls.getJoinablePCSuperclassMapping();
+ Column[] cols = cls.getPrimaryKeyColumns();
+ for (int i = 0; i < cols.length; i++)
+ if (cols[i].isAutoAssigned())
+ return true;
+ return false;
+ }
+
+ protected void setObject(Column col, Object val, int metaType,
+ boolean overrideDefault)
+ throws SQLException {
+ // make sure we're not setting two different values
+ Object prev = getSet(col);
+ if (prev != null) {
+ if (prev == NULL)
+ prev = null;
+ if (!rowValueEquals(prev, val)) {
+ throw new InvalidStateException(_loc.get("diff-values",
+ new Object[]{ col.getFullName(),
+ (prev == null) ? null : prev.getClass(), prev,
+ (val == null) ? null : val.getClass(), val })).
+ setFatal(true);
+ }
+ }
+ super.setObject(col, val, metaType, overrideDefault);
+ }
+
+ /**
+ * Return true if the two values should be considered equal.
+ */
+ private static boolean rowValueEquals(Object o1, Object o2) {
+ if (ObjectUtils.equals(o1, o2))
+ return true;
+
+ // check for numeric equality (bug #1151)
+ return o1 instanceof Number && o2 instanceof Number
+ && ((Number) o1).doubleValue() == ((Number) o2).doubleValue();
+ }
+
+ protected String generateSQL(DBDictionary dict) {
+ try {
+ if ((flags & PK_SET) > 0)
+ super.setPrimaryKey(_pkIO, _pk);
+ if ((flags & PK_WHERE) > 0)
+ super.wherePrimaryKey(_pk);
+ if (_fkSet != null) {
+ ForeignKey[] fks = getTable().getForeignKeys();
+ ColumnIO io;
+ for (int i = 0; i < _fkSet.length; i++) {
+ if (_fkSet[i] != null) {
+ io = (_fkIO == null) ? null : _fkIO[i];
+ super.setForeignKey(fks[i], io, _fkSet[i]);
+ }
+ }
+ }
+ if (_relSet != null) {
+ Column[] cols = getTable().getRelationIdColumns();
+ for (int i = 0; i < _relSet.length; i++)
+ if (_relSet[i] != null)
+ super.setRelationId(cols[i], _relSet[i], _callbacks[i]);
+ }
+ if (_fkWhere != null) {
+ ForeignKey[] fks = getTable().getForeignKeys();
+ for (int i = 0; i < _fkWhere.length; i++)
+ if (_fkWhere[i] != null)
+ super.whereForeignKey(fks[i], _fkWhere[i]);
+ }
+ }
+ catch (SQLException se) {
+ throw SQLExceptions.getStore(se, dict);
+ }
+ return super.generateSQL(dict);
+ }
+
+ protected RowImpl newInstance(Table table, int action) {
+ return new PrimaryRow(table, action, _pk);
+ }
+
+ public void copyInto(RowImpl row, boolean whereOnly) {
+ super.copyInto(row, whereOnly);
+ if (!(row instanceof PrimaryRow))
+ return;
+
+ PrimaryRow prow = (PrimaryRow) row;
+ prow._pk = _pk;
+ prow._pkIO = _pkIO;
+ if ((flags & PK_WHERE) > 0)
+ prow.flags |= PK_WHERE;
+ if (!whereOnly && (flags & PK_SET) > 0)
+ prow.flags |= PK_SET;
+
+ if (_fkWhere != null) {
+ if (prow._fkWhere == null)
+ prow._fkWhere = new OpenJPAStateManager[_fkWhere.length];
+ System.arraycopy(_fkWhere, 0, prow._fkWhere, 0, _fkWhere.length);
+ }
+ if (!whereOnly && _fkSet != null) {
+ if (prow._fkSet == null)
+ prow._fkSet = new OpenJPAStateManager[_fkSet.length];
+ System.arraycopy(_fkSet, 0, prow._fkSet, 0, _fkSet.length);
+ if (_fkIO != null) {
+ if (prow._fkIO == null)
+ prow._fkIO = new ColumnIO[_fkIO.length];
+ System.arraycopy(_fkIO, 0, prow._fkIO, 0, _fkIO.length);
+ }
+ }
+ if (!whereOnly && _relSet != null) {
+ if (prow._relSet == null) {
+ prow._relSet = new OpenJPAStateManager[_relSet.length];
+ prow._callbacks = new RelationId[_callbacks.length];
+ }
+ System.arraycopy(_relSet, 0, prow._relSet, 0, _relSet.length);
+ System.arraycopy(_callbacks, 0, prow._callbacks, 0,
+ _callbacks.length);
+ }
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PrimaryRow.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Raw.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Raw.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Raw.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Raw.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2006 The 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;
+
+/**
+ * Represents a raw SQL string for passing to
+ * <code>Row.setObject</code>.
+ *
+ * @author Abe White
+ */
+public class Raw {
+
+ public final String sql;
+
+ public Raw(String sql) {
+ this.sql = sql;
+ }
+
+ public String toString() {
+ return sql;
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Raw.java
------------------------------------------------------------------------------
svn:executable = *