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 = *