blob: 938c8c17fa4e1b585c78f3512327dc890d9684cc [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. 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:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.platform.database;
import java.io.*;
import java.util.*;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.expressions.*;
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.queries.ValueReadQuery;
import org.eclipse.persistence.tools.schemaframework.FieldDefinition;
/**
* <p><b>Purpose</b>: Provides Microsoft Access specific behavior.
*
* @since TOPLink/Java 1.0
*/
public class AccessPlatform extends org.eclipse.persistence.platform.database.DatabasePlatform {
/**
* Default constructor.
*/
public AccessPlatform() {
super();
}
@Override
protected Map<String, Class<?>> buildClassTypes() {
Map<String, Class<?>> classTypeMapping = super.buildClassTypes();
// In access LONG means numeric not CLOB like in Oracle
classTypeMapping.put("LONG", Long.class);
classTypeMapping.put("TEXT", String.class);
return classTypeMapping;
}
@Override
protected Hashtable<Class<?>, FieldTypeDefinition> buildFieldTypes() {
Hashtable<Class<?>, FieldTypeDefinition> fieldTypeMapping = new Hashtable<>();
fieldTypeMapping.put(Boolean.class, new FieldTypeDefinition("BIT", false));
fieldTypeMapping.put(Integer.class, new FieldTypeDefinition("LONG", false));
fieldTypeMapping.put(Long.class, new FieldTypeDefinition("DOUBLE", false));
fieldTypeMapping.put(Float.class, new FieldTypeDefinition("DOUBLE", false));
fieldTypeMapping.put(Double.class, new FieldTypeDefinition("DOUBLE", false));
fieldTypeMapping.put(Short.class, new FieldTypeDefinition("SHORT", false));
fieldTypeMapping.put(Byte.class, new FieldTypeDefinition("BYTE", false));
fieldTypeMapping.put(java.math.BigInteger.class, new FieldTypeDefinition("DOUBLE", false));
fieldTypeMapping.put(java.math.BigDecimal.class, new FieldTypeDefinition("DOUBLE", false));
fieldTypeMapping.put(Number.class, new FieldTypeDefinition("DOUBLE", false));
fieldTypeMapping.put(String.class, new FieldTypeDefinition("TEXT", DEFAULT_VARCHAR_SIZE));
fieldTypeMapping.put(Character.class, new FieldTypeDefinition("TEXT", 1));
fieldTypeMapping.put(Byte[].class, new FieldTypeDefinition("LONGBINARY", false));
fieldTypeMapping.put(Character[].class, new FieldTypeDefinition("MEMO", false));
fieldTypeMapping.put(byte[].class, new FieldTypeDefinition("LONGBINARY", false));
fieldTypeMapping.put(char[].class, new FieldTypeDefinition("MEMO", false));
fieldTypeMapping.put(java.sql.Blob.class, new FieldTypeDefinition("LONGBINARY", false));
fieldTypeMapping.put(java.sql.Clob.class, new FieldTypeDefinition("MEMO", false));
fieldTypeMapping.put(java.sql.Date.class, new FieldTypeDefinition("DATETIME", false));
fieldTypeMapping.put(java.sql.Time.class, new FieldTypeDefinition("DATETIME", false));
fieldTypeMapping.put(java.sql.Timestamp.class, new FieldTypeDefinition("DATETIME", false));
return fieldTypeMapping;
}
/**
* INTERNAL:
* returns the maximum number of characters that can be used in a field
* name on this platform.
*/
@Override
public int getMaxFieldNameSize() {
return 64;
}
/**
* Access do not support millisecond well, truncate the millisecond from the timestamp
*/
@Override
public java.sql.Timestamp getTimestampFromServer(AbstractSession session, String sessionName) {
if (getTimestampQuery() == null) {
java.sql.Timestamp currentTime = new java.sql.Timestamp(System.currentTimeMillis());
currentTime.setNanos(0);
return currentTime;
} else {
getTimestampQuery().setSessionName(sessionName);
return (java.sql.Timestamp)session.executeQuery(getTimestampQuery());
}
}
/**
* Initialize any platform-specific operators
*/
@Override
protected void initializePlatformOperators() {
super.initializePlatformOperators();
addOperator(ExpressionOperator.simpleFunction(ExpressionOperator.ToUpperCase, "UCASE"));
addOperator(ExpressionOperator.simpleFunction(ExpressionOperator.ToLowerCase, "LCASE"));
}
@Override
public boolean isAccess() {
return true;
}
/**
* Builds a table of maximum numeric values keyed on java class. This is used for type testing but
* might also be useful to end users attempting to sanitize values.
* <p><b>NOTE</b>: BigInteger {@literal &} BigDecimal maximums are dependent upon their precision {@literal &} Scale
*/
@Override
public Hashtable<Class<? extends Number>, ? super Number> maximumNumericValues() {
Hashtable<Class<? extends Number>, ? super Number> values = new Hashtable<>();
values.put(Integer.class, Integer.MAX_VALUE);
values.put(Long.class, Long.MAX_VALUE);
values.put(Double.class, Double.MAX_VALUE);
values.put(Short.class, Short.MAX_VALUE);
values.put(Byte.class, Byte.MAX_VALUE);
values.put(Float.class, 123456789F);
values.put(java.math.BigInteger.class, new java.math.BigInteger("999999999999999"));
values.put(java.math.BigDecimal.class, new java.math.BigDecimal("99999999999999999999.9999999999999999999"));
return values;
}
/**
* Builds a table of minimum numeric values keyed on java class. This is used for type testing but
* might also be useful to end users attempting to sanitize values.
* <p><b>NOTE</b>: BigInteger {@literal &} BigDecimal minimums are dependent upon their precision {@literal &} Scale
*/
@Override
public Hashtable<Class<? extends Number>, ? super Number> minimumNumericValues() {
Hashtable<Class<? extends Number>, ? super Number> values = new Hashtable<>();
values.put(Integer.class, Integer.MIN_VALUE);
values.put(Long.class, Long.MIN_VALUE);
values.put(Double.class, Double.MIN_VALUE);
values.put(Short.class, Short.MIN_VALUE);
values.put(Byte.class, Byte.MIN_VALUE);
values.put(Float.class, (float) -123456789);
values.put(java.math.BigInteger.class, new java.math.BigInteger("-999999999999999"));
values.put(java.math.BigDecimal.class, new java.math.BigDecimal("-9999999999999999999.9999999999999999999"));
return values;
}
/**
* This is used as some databases create the primary key constraint differently, i.e. Access.
*/
@Override
public boolean requiresNamedPrimaryKeyConstraints() {
return true;
}
/**
* JDBC defines and outer join syntax, many drivers do not support this. So we normally avoid it.
*/
@Override
public boolean shouldUseJDBCOuterJoinSyntax() {
return false;
}
/**
* INTERNAL: Build the identity query for native sequencing.
*/
@Override
public ValueReadQuery buildSelectQueryForIdentity() {
ValueReadQuery selectQuery = new ValueReadQuery();
StringWriter writer = new StringWriter();
writer.write("SELECT @@IDENTITY");
selectQuery.setSQLString(writer.toString());
return selectQuery;
}
/** Append the receiver's field 'identity' constraint clause to a writer. */
@Override
public void printFieldIdentityClause(Writer writer) throws ValidationException {
try {
writer.write(" COUNTER");
} catch (IOException ioException) {
throw ValidationException.fileError(ioException);
}
}
/**
* INTERNAL: Indicates whether the platform supports identity. Sybase does
* through IDENTITY field types. This method is to be used *ONLY* by
* sequencing classes
*/
@Override
public boolean supportsIdentity() {
return true;
}
@Override
public void printFieldTypeSize(Writer writer, FieldDefinition field,FieldTypeDefinition fieldType, boolean shouldPrintFieldIdentityClause) throws IOException {
if (!shouldPrintFieldIdentityClause) {
super.printFieldTypeSize(writer, field, fieldType,
shouldPrintFieldIdentityClause);
}
}
@Override
public void printFieldUnique(Writer writer, boolean shouldPrintFieldIdentityClause) throws IOException {
if (!shouldPrintFieldIdentityClause) {
super.printFieldUnique(writer, shouldPrintFieldIdentityClause);
}
}
}