| /* |
| * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All Rights Reserved. |
| * Copyright (c) 2012, 2018 Pervasive Software Inc. All Rights Reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Peter Lohman - initial implementation |
| // |
| |
| /* |
| |
| 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 |
| |
| */ |
| package org.eclipse.persistence.platform.database; |
| |
| import java.io.IOException; |
| import java.io.Writer; |
| import java.util.ArrayList; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.persistence.exceptions.ValidationException; |
| import org.eclipse.persistence.expressions.ExpressionOperator; |
| import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition; |
| import org.eclipse.persistence.internal.helper.ClassConstants; |
| import org.eclipse.persistence.internal.helper.DatabaseTable; |
| import org.eclipse.persistence.queries.ValueReadQuery; |
| import org.eclipse.persistence.tools.schemaframework.FieldDefinition; |
| |
| /** <p><b>Purpose</b>: Provides Pervasive SQL DBMS specific behavior. |
| * |
| * <br><br> |
| * Pervasive SQL Platform file <br> |
| * Contributed by: Pervasive Software, Inc.<br> |
| * Contributed under bug: 392109 |
| * <p> |
| * |
| * <u><b>Developed on Pervasive PSQL Server 11.30 </b></u> |
| * <blockquote> |
| * <ul> |
| * <li>Eclipselink Core SRG Test passes with known limitations. |
| * <li>Eclipselink JPA SRG Test passes with known limitations. |
| * <li>Eclipselink stored procedure tests "CustomSQLTestModel", "StoredProcedureGeneratorModel" pass with known limitations. |
| * </ul> |
| * </blockquote> |
| * |
| * <p><u><b>Limitations</b></u> |
| * <ul> |
| * <li> Updates are not supported on joined queries or queries with group by. |
| * <li> The platform method getSelectForUpdateString() currently returns an empty string. This is |
| * to avoid avoid joined queries with FOR UPDATE in them, which Pervasive does not support. |
| * <li> Columns used in indexes must total no more than 255 bytes in length. |
| * <li> Pervasive SQL does not support dynamic parameters in the SELECT list. |
| * <li> IDENTITY columns are either 2- or 4-byte integers. Foreign keys referencing such columns must use the same datatypes. |
| * </ul> |
| * |
| **/ |
| |
| |
| public class PervasivePlatform extends org.eclipse.persistence.platform.database.DatabasePlatform { |
| |
| public static final int DEFAULT_CHAR_SIZE = 80; |
| |
| /** |
| * Default constructor. |
| */ |
| public PervasivePlatform() { |
| super(); |
| } |
| |
| // |
| // Cloned from AccessPlatform.java |
| // |
| @Override |
| 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; |
| } |
| |
| @Override |
| protected Hashtable<Class<?>, FieldTypeDefinition> buildFieldTypes() { |
| Hashtable<Class<?>, FieldTypeDefinition> 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("LONGVARBINARY", false)); |
| 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. |
| */ |
| @Override |
| 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. |
| */ |
| @Override |
| public boolean requiresProcedureBrackets() { |
| return true; |
| } |
| |
| /** |
| * Pervasive uses CALL or EXECUTE not CALL PROCEDURE or EXECUTE PROCEDURE |
| */ |
| @Override |
| 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. |
| */ |
| @Override |
| public String getStoredProcedureParameterPrefix() { |
| return ":"; |
| } |
| |
| |
| /** |
| * Pervasive requires the OUTPUT keyword for output parameters |
| */ |
| @Override |
| public boolean requiresProcedureCallOuputToken() { |
| return true; |
| } |
| |
| |
| @Override |
| 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); |
| List<String> v = new ArrayList<>(2); |
| v.add("CONVERT("); |
| v.add(", 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); |
| List<String> v = new ArrayList<>(2); |
| v.add("CONVERT("); |
| v.add(", 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); |
| List<String> v = new ArrayList<>(2); |
| v.add("CONVERT("); |
| v.add(", 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); |
| List<String> v = new ArrayList<>(2); |
| v.add("CONVERT("); |
| v.add(", SQL_CHAR)"); |
| exOperator.printsAs(v); |
| exOperator.bePrefix(); |
| exOperator.setNodeClass(ClassConstants.FunctionExpression_Class); |
| return exOperator; |
| } |
| |
| |
| |
| /** |
| * Answers whether platform is Pervasive |
| */ |
| @Override |
| public boolean isPervasive() { |
| return true; |
| } |
| |
| /** |
| * JDBC defines an outer join syntax which many drivers do not support. So we normally avoid it. |
| */ |
| @Override |
| 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 |
| */ |
| @Override |
| 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); |
| List<String> v = new ArrayList<>(); |
| v.add("SUBSTRING("); |
| v.add(","); |
| v.add(", CHAR_LENGTH("); |
| v.add("))"); |
| 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. |
| * |
| */ |
| @Override |
| public boolean supportsIdentity() { |
| return true; |
| } |
| |
| // |
| // Most Temp Table settings cloned from SQLServerPlatform.java |
| // |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public boolean supportsLocalTempTables() { |
| return true; |
| } |
| |
| |
| @Override |
| public boolean supportsGlobalTempTables() { |
| return true; |
| } |
| |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| protected String getCreateTempTableSqlPrefix() { |
| return "CREATE TABLE "; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| 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 |
| */ |
| @Override |
| 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.toString(fieldType.getDefaultSize())); |
| } else { |
| writer.write(Integer.toString(field.getSize())); |
| } |
| writer.write(","); |
| if (field.getSubSize() != 0) { |
| writer.write(Integer.toString(field.getSubSize())); |
| } else { |
| writer.write(Integer.toString(fieldType.getDefaultSubSize())); |
| } |
| 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 |
| * |
| */ |
| @Override |
| public String getSelectForUpdateString() { |
| return ""; |
| } |
| |
| |
| |
| |
| /** |
| * INTERNAL: |
| * Indicates whether SELECT DISTINCT ... FOR UPDATE is allowed by the platform (Oracle doesn't allow this). |
| */ |
| @Override |
| 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 |
| */ |
| @Override |
| public boolean supportsLockingQueriesWithMultipleTables() { |
| return false; |
| } |
| |
| |
| |
| |
| } |