Bug 392109 - Pervasive PSQL JPA support
diff --git a/extensions/org.eclipse.persistence.platrom.database.pervasive/src/org/eclipse/persistence/platform/database/PervasivePlatform.java b/extensions/org.eclipse.persistence.platrom.database.pervasive/src/org/eclipse/persistence/platform/database/PervasivePlatform.java
new file mode 100644
index 0000000..1b28f77
--- /dev/null
+++ b/extensions/org.eclipse.persistence.platrom.database.pervasive/src/org/eclipse/persistence/platform/database/PervasivePlatform.java
@@ -0,0 +1,460 @@
+/*

+

+    For minimal implementation, compare with:

+         C:\PL\JPA\EclipseLink\SVN\org.eclipse.persistence\foundation\org.eclipse.persistence.core\src\org\eclipse\persistence\platform\database\CloudscapePlatform.java

+         

+    For PVSW data type mapping, see: getColumnClassName():C:\cmsynergy\psql11.20_pnl\psql\comp\sdk\jdbc\pvjdbc2\src\com\pervasive\jdbc\v2\ResultSetMetaData.java         

+    

+*/

+

+/*******************************************************************************

+ * Copyright (c) 2012 All rights reserved.

+ * This program and the accompanying materials are made available under the 

+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 

+ * which accompanies this distribution. 

+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html

+ * and the Eclipse Distribution License is available at 

+ * http://www.eclipse.org/org/documents/edl-v10.php.

+ *

+ * Contributors:

+ *     

+ ******************************************************************************/  

+package org.eclipse.persistence.platform.database;

+

+import java.util.*;

+

+import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;

+

+import java.io.*;

+import java.sql.CallableStatement;

+import java.sql.PreparedStatement;

+import java.sql.ResultSet;

+import java.sql.SQLException;

+import java.util.*;

+

+import org.eclipse.persistence.exceptions.*;

+import org.eclipse.persistence.expressions.*;

+import org.eclipse.persistence.internal.expressions.*;

+import org.eclipse.persistence.internal.helper.*;

+import org.eclipse.persistence.internal.sessions.AbstractRecord;

+import org.eclipse.persistence.internal.sessions.AbstractSession;

+import org.eclipse.persistence.internal.databaseaccess.*;

+import org.eclipse.persistence.tools.schemaframework.FieldDefinition;

+import org.eclipse.persistence.queries.*;

+import org.eclipse.persistence.sessions.SessionProfiler;

+

+/**

+ *    <p><b>Purpose</b>: Provides Pervasive DBMS specific behavior.

+ *

+ *  

+ **/

+public class PervasivePlatform extends org.eclipse.persistence.platform.database.DatabasePlatform {

+

+    public static final int DEFAULT_CHAR_SIZE = 80;

+

+    //

+    // Cloned from AccessPlatform.java

+    //

+    protected Map<String, Class> buildClassTypes() {

+        Map<String, Class> classTypeMapping = super.buildClassTypes();

+

+        // Causes BLOB to translate to LONGVARBINARY(via java.sql.Blob) instead of BINARY (via Byte[])

+        classTypeMapping.put("BLOB", java.sql.Blob.class);

+

+        return classTypeMapping;

+    }

+

+

+    protected Hashtable buildFieldTypes() {

+        Hashtable fieldTypeMapping;

+

+        fieldTypeMapping = new Hashtable();

+        fieldTypeMapping.put(String.class, new FieldTypeDefinition("VARCHAR", DEFAULT_CHAR_SIZE));            

+        // fieldTypeMapping.put(java.math.BigDecimal.class, new FieldTypeDefinition("BIGINT", false));

+        fieldTypeMapping.put(java.math.BigInteger.class, new FieldTypeDefinition("BIGINT", false));

+        fieldTypeMapping.put(Integer.class, new FieldTypeDefinition("INTEGER", false));

+        fieldTypeMapping.put(Long.class, new FieldTypeDefinition("INTEGER", false));        

+        fieldTypeMapping.put(Short.class, new FieldTypeDefinition("SMALLINT", false));        

+        fieldTypeMapping.put(Byte.class, new FieldTypeDefinition("TINYINT", false));        

+        fieldTypeMapping.put(Float.class, new FieldTypeDefinition("REAL", false));          

+        fieldTypeMapping.put(Double.class, new FieldTypeDefinition("DOUBLE", false));         

+        fieldTypeMapping.put(Character.class, new FieldTypeDefinition("CHAR", 1));

+        fieldTypeMapping.put(java.sql.Date.class, new FieldTypeDefinition("DATE", false));              

+        fieldTypeMapping.put(java.sql.Time.class, new FieldTypeDefinition("TIME", false));              

+        fieldTypeMapping.put(java.sql.Timestamp.class, new FieldTypeDefinition("TIMESTAMP", false));      

+        fieldTypeMapping.put(byte[].class, new FieldTypeDefinition("BINARY", DEFAULT_CHAR_SIZE ));   

+        fieldTypeMapping.put(Byte[].class, new FieldTypeDefinition("BINARY", DEFAULT_CHAR_SIZE));   

+        fieldTypeMapping.put(Character[].class, new FieldTypeDefinition("CHAR", DEFAULT_CHAR_SIZE));

+        fieldTypeMapping.put(Boolean.class, new FieldTypeDefinition("BIT", false));              

+        fieldTypeMapping.put(java.sql.Blob.class, new FieldTypeDefinition("LONGVARBINARY", false));

+        fieldTypeMapping.put(java.sql.Clob.class, new FieldTypeDefinition("LONGVARCHAR", false));        

+

+        fieldTypeMapping.put(java.math.BigDecimal.class, new FieldTypeDefinition("DECIMAL",38, 0));        // From MySQL

+        fieldTypeMapping.put(Number.class, new FieldTypeDefinition("DECIMAL",38,0));                      // From MySQL

+

+

+        // fieldTypeMapping.put(java.lang.Number.class, new FieldTypeDefinition("BIGINT", false));

+        fieldTypeMapping.put(char[].class, new FieldTypeDefinition("LONGVARCHAR", false));

+        fieldTypeMapping.put(java.util.Calendar.class, new FieldTypeDefinition("TIMESTAMP"));

+        fieldTypeMapping.put(java.util.Date.class, new FieldTypeDefinition("TIMESTAMP"));

+   

+        return fieldTypeMapping;

+    }

+

+    /**

+     * 

+     * Pervasive uses the INOUT keyword, as opposed to "IN OUT".

+     */

+    @Override

+    public String getInOutputProcedureToken() {

+        return "INOUT";

+    }

+    

+    /**

+     * Pervasive uses IN prefix for INPUT parameters.

+     * 

+     */

+    @Override

+    public String getInputProcedureToken() {

+        return "IN";

+    }

+

+    /**

+     * Pervasive uses ":" as prefix for procedure arguments.

+     */

+    public String getProcedureArgumentString() {

+        return ":";

+    }

+

+    /**

+     * 

+     * Pervasive requires BEGIN in a procedure statement.

+     */

+    @Override

+    public String getProcedureBeginString() {

+        return "BEGIN ";

+    }

+

+    /**

+     * In CREATE PROCEDURE, Pervasive requires brackets after the procedure name, even if there are no arguments.

+     */

+    public boolean requiresProcedureBrackets() {

+        return true;

+    }

+

+    /**

+     * Pervasive uses CALL or EXECUTE not CALL PROCEDURE or EXECUTE PROCEDURE

+     */

+    public String getProcedureCallHeader() {

+        return "CALL ";

+    }

+

+    /**

+     * 

+     * Pervasive requires END in a procedure statement.

+     */

+    @Override

+    public String getProcedureEndString() {

+        return "END";

+    }

+    

+

+    /**

+     * Pervasive uses ":" as prefix for procedure parameters.

+     */

+    public String getStoredProcedureParameterPrefix() {

+        return ":";

+    }

+    

+    

+    /**

+     * Pervasive requires the OUTPUT keyword for output parameters

+     */

+    @Override

+    public boolean requiresProcedureCallOuputToken() {

+        return true;

+    }

+

+

+    protected void initializePlatformOperators() {

+        super.initializePlatformOperators();

+

+        addOperator(ExpressionOperator.simpleThreeArgumentFunction(ExpressionOperator.Substring, "SUBSTRING"));

+        addOperator(singleArgumentSubstringOperator());

+        addOperator(ExpressionOperator.simpleTwoArgumentFunction(ExpressionOperator.Nvl, "ISNULL"));

+        addOperator(ExpressionOperator.simpleFunction(ExpressionOperator.Ceil, "CEILING"));

+        addOperator(toNumberOperator());

+        addOperator(toCharOperator());

+        addOperator(toDateOperator());

+    }

+

+    /**

+     * Cloned from MySQLPlatform.java

+     * 

+     */

+

+    /**

+     * INTERNAL:

+     * Pervasive SQL stored procedure calls do not require the argument name be printed in the call string

+     * e.g. call MyStoredProc(?) instead of call MyStoredProc(myvariable = ?)

+     */

+    @Override

+    public boolean shouldPrintStoredProcedureArgumentNameInCall(){

+        return false;

+    }

+

+

+    protected ExpressionOperator toNumberOperator() {

+        ExpressionOperator exOperator = new ExpressionOperator();

+        exOperator.setType(ExpressionOperator.FunctionOperator);

+        exOperator.setSelector(ExpressionOperator.ToNumber);

+        Vector v = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(2);

+        v.addElement("CONVERT(");

+        v.addElement(", SQL_NUMERIC)");

+        exOperator.printsAs(v);

+        exOperator.bePrefix();

+        exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);

+        return exOperator;

+    }

+

+    /**

+     * Cloned from MySQLPlatform.java

+     */

+    protected ExpressionOperator toDateOperator() {

+        ExpressionOperator exOperator = new ExpressionOperator();

+        exOperator.setType(ExpressionOperator.FunctionOperator);

+        exOperator.setSelector(ExpressionOperator.ToDate);

+        Vector v = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(2);

+        v.addElement("CONVERT(");

+        v.addElement(", DATETIME)");

+        exOperator.printsAs(v);

+        exOperator.bePrefix();

+        exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);

+        return exOperator;

+    }

+

+    /**

+     * Cloned from MySQLPlatform.java

+     */

+    protected ExpressionOperator toCharOperator() {

+        ExpressionOperator exOperator = new ExpressionOperator();

+        exOperator.setType(ExpressionOperator.FunctionOperator);

+        exOperator.setSelector(ExpressionOperator.ToChar);

+        Vector v = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(2);

+        v.addElement("CONVERT(");

+        v.addElement(", SQL_CHAR)");

+        exOperator.printsAs(v);

+        exOperator.bePrefix();

+        exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);

+        return exOperator;

+    }

+

+    /**

+     * 

+     * Cloned from MySQLPlatform.java

+     */

+    protected ExpressionOperator dateToStringOperator() {

+        ExpressionOperator exOperator = new ExpressionOperator();

+        exOperator.setType(ExpressionOperator.FunctionOperator);

+        exOperator.setSelector(ExpressionOperator.DateToString);

+        Vector v = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(2);

+        v.addElement("CONVERT(");

+        v.addElement(", SQL_CHAR)");

+        exOperator.printsAs(v);

+        exOperator.bePrefix();

+        exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);

+        return exOperator;

+    }

+

+

+

+    /**

+     * Answers whether platform is Pervasive

+     */

+    public boolean isPervasive() {

+        return true;

+    }

+

+    /**

+     * JDBC defines an outer join syntax which many drivers do not support. So we normally avoid it.

+     */

+    public boolean shouldUseJDBCOuterJoinSyntax() {

+        return false; // not sure about this

+    }

+    

+    /** Append the receiver's field 'identity' constraint clause to

+    *   a writer.

+    *      

+    *  Taken from

+    *  org.eclipse.persistence\foundation\org.eclipse.persistence.core\src\org\eclipse\persistence\platform\database\AccessPlatform.java

+    */

+    public void printFieldIdentityClause(Writer writer)	throws ValidationException {

+        try {

+            writer.write(" IDENTITY");

+        } catch (IOException ioException) {

+            throw ValidationException.fileError(ioException);

+        }

+    }

+

+

+    /**

+     * Override the default SubstringSingleArg operator. 

+     * Cloned from SybasePlatform.java 

+     */

+    public ExpressionOperator singleArgumentSubstringOperator() {

+        ExpressionOperator result = new ExpressionOperator();

+        result.setSelector(ExpressionOperator.SubstringSingleArg);

+        result.setType(ExpressionOperator.FunctionOperator);

+        Vector v = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance();

+        v.addElement("SUBSTRING(");

+        v.addElement(",");

+        v.addElement(", CHAR_LENGTH(");

+        v.addElement("))");

+        result.printsAs(v);

+        int[] indices = new int[3];

+        indices[0] = 0;

+        indices[1] = 1;

+        indices[2] = 0;

+

+        result.setArgumentIndices(indices);

+        result.setNodeClass(ClassConstants.FunctionExpression_Class);

+        result.bePrefix();

+        return result;

+    }

+

+

+

+

+    /**

+     *  

+     *  Indicates whether the platform supports identity.

+     *    

+     */

+     public boolean supportsIdentity() {

+         return true;

+     }

+    

+     // 

+     //  Most Temp Table settings cloned from SQLServerPlatform.java

+     //    

+     /**

+      * INTERNAL:

+      */

+     public boolean supportsLocalTempTables() {

+         return true;

+     }

+

+

+     public boolean supportsGlobalTempTables() {

+         return true;

+     }

+

+

+     /**

+      * INTERNAL:

+      */

+     protected String getCreateTempTableSqlPrefix() {

+         return "CREATE TABLE ";

+     }          

+

+     /**

+      * INTERNAL:

+      */

+     public DatabaseTable getTempTableForTable(DatabaseTable table) {

+         return new DatabaseTable("#" + table.getName(), table.getTableQualifier(), table.shouldUseDelimiters(), getStartDelimiter(), getEndDelimiter());

+     }          

+

+

+     /** 

+     *      

+     * Taken from org.eclipse.persistence\foundation\org.eclipse.persistence.core\src\org\eclipse\persistence\platform\database\AccessPlatform.java  

+     */

+     public void printFieldTypeSize(Writer writer, FieldDefinition field,FieldTypeDefinition fieldType, boolean shouldPrintFieldIdentityClause) throws IOException {

+		if (!shouldPrintFieldIdentityClause) {

+            // if type requires both precision and scale: NUMERIC, DECIMAL

+            if ((fieldType.getName().equals("NUMERIC")) || (fieldType.getName().equals("DECIMAL"))) {

+                writer.write(fieldType.getName());

+                writer.write("(");

+                if (field.getSize() == 0) {

+                    writer.write(Integer.valueOf(fieldType.getDefaultSize()).toString());

+                } else {

+                    writer.write(Integer.valueOf(field.getSize()).toString());

+                }

+                writer.write(",");

+                if (field.getSubSize() != 0) {

+                    writer.write(Integer.valueOf(field.getSubSize()).toString());

+                } else {

+                    writer.write(Integer.valueOf(fieldType.getDefaultSubSize()).toString());

+                }

+                writer.write(")");

+            } else {

+                super.printFieldTypeSize(writer, field, fieldType,

+                        shouldPrintFieldIdentityClause);

+            }

+		}

+	}

+

+    /**    

+     * INTERNAL:

+     * Build the identity query for native sequencing.

+     *      

+     * Taken verbatim from org.eclipse.persistence\foundation\org.eclipse.persistence.core\src\org\eclipse\persistence\platform\database\SQLServerPlatform.java  

+     *               

+     */

+     @Override

+     public ValueReadQuery buildSelectQueryForIdentity() {

+         ValueReadQuery selectQuery = new ValueReadQuery();

+         selectQuery.setSQLString("SELECT @@IDENTITY");

+         return selectQuery;

+    }

+

+     /**

+      * Temporary workaround to avoid joined queries with FOR UPDATE 

+      * in them 

+      * 

+      */

+     public String getSelectForUpdateString() {

+         return "";

+     }

+

+

+

+

+        /**

+     * INTERNAL:

+     * Indicates whether SELECT DISTINCT ... FOR UPDATE is allowed by the platform (Oracle doesn't allow this).

+     */

+    public boolean isForUpdateCompatibleWithDistinct() {

+        return false;

+    }

+

+

+     /**

+     * Setting this to false (cf. Sybase) to work around problem 

+     * that unspecified default delete rule is RESTRICT, even when 

+     * not allowed due to self-referencing table.  

+     */

+    @Override

+    public boolean supportsDeleteOnCascade() {

+        return true;

+    }

+

+

+    /** Attempts to remove FOR UPDATE from queries */

+    @Override

+    public boolean shouldPrintLockingClauseAfterWhereClause() {

+        return false;

+    }

+

+        /**

+     * INTERNAL:

+     * Indicates whether locking clause could be applied to the query that has more than one table

+     */

+    public boolean supportsLockingQueriesWithMultipleTables() {

+        return false;

+    }

+    

+

+    

+

+}