blob: d96a59b20e7beb45ca4d41d32c8d481365775c78 [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.tools.schemaframework;
import java.io.IOException;
import java.io.Serializable;
import java.io.Writer;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform;
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.logging.SessionLog;
/**
* <p>
* <b>Purpose</b>: Define a database field definition for creation within a table.
* This differs from DatabaseField in that it is used only table creation not a runtime.
* <p>
* <b>Responsibilities</b>:
* <ul>
* <li> Store the name, java type, size and sub-size.
* The sizes are optional and the name of the java class is used for the type.
* </ul>
*/
public class FieldDefinition implements Serializable, Cloneable {
protected String name;
/**
* Java type class for the field.
* Particular database type is generated based on platform from this.
*/
protected Class<?> type;
/**
* Generic database type name for the field, which can be used instead of the Java class 'type'.
* This is translated to a particular database type based on platform.
*/
protected String typeName;
/**
* DatabaseField stores the field name with case and delimiting information.
* Used if the field needs to be found in the table metadata, for extending tables.
* if null, name is used for comparison to determine if this field already exists.
*/
protected DatabaseField field;
/**
* Database-specific complete type definition like "VARCHAR2(50) UNIQUE NOT NULL".
* If this is given, other additional type constraint fields(size, unique, null) are meaningless.
*/
protected String typeDefinition;
protected int size;
protected int subSize;
protected boolean shouldAllowNull;
protected boolean isIdentity;
protected boolean isPrimaryKey;
protected boolean isUnique;
protected String additional;
protected String constraint;
protected String foreignKeyFieldName;
public FieldDefinition() {
this.name = "";
this.size = 0;
this.subSize = 0;
this.shouldAllowNull = true;
this.isIdentity = false;
this.isPrimaryKey = false;
this.isUnique = false;
}
public FieldDefinition(String name, Class<?> type) {
this.name = name;
this.type = type;
this.size = 0;
this.subSize = 0;
shouldAllowNull = true;
isIdentity = false;
isPrimaryKey = false;
isUnique = false;
}
public FieldDefinition(String name, Class<?> type, int size) {
this();
this.name = name;
this.type = type;
this.size = size;
}
public FieldDefinition(String name, Class<?> type, int size, int subSize) {
this();
this.name = name;
this.type = type;
this.size = size;
this.subSize = subSize;
}
public FieldDefinition(String name, String typeName) {
this();
this.name = name;
this.typeName = typeName;
}
/**
* INTERNAL:
* Append the database field definition string to the table creation statement.
* @param writer Target writer where to write field definition string.
* @param session Current session context.
* @param table Database table being processed.
* @throws ValidationException When invalid or inconsistent data were found.
*/
public void appendDBString(final Writer writer, final AbstractSession session,
final TableDefinition table) throws ValidationException {
try {
writer.write(name);
writer.write(" ");
if (getTypeDefinition() != null) { //apply user-defined complete type definition
writer.write(typeDefinition);
} else {
final DatabasePlatform platform = session.getPlatform();
// compose type definition - type name, size, unique, identity, constraints...
final FieldTypeDefinition fieldType
= DatabaseObjectDefinition.getFieldTypeDefinition(platform, type, typeName);
String qualifiedName = table.getFullName() + '.' + name;
boolean shouldPrintFieldIdentityClause = isIdentity && platform.shouldPrintFieldIdentityClause(session, qualifiedName);
platform.printFieldTypeSize(writer, this, fieldType, shouldPrintFieldIdentityClause);
if (shouldPrintFieldIdentityClause) {
platform.printFieldIdentityClause(writer);
}
if (shouldAllowNull && fieldType.shouldAllowNull()) {
platform.printFieldNullClause(writer);
} else {
platform.printFieldNotNullClause(writer);
}
if (isUnique) {
if (platform.supportsUniqueColumns()) {
// #282751: do not add UNIQUE if the field is also simple primary key
if (!isPrimaryKey || table.getPrimaryKeyFieldNames().size() > 1) {
platform.printFieldUnique(writer, shouldPrintFieldIdentityClause);
} else {
setUnique(false);
session.log(SessionLog.WARNING, SessionLog.DDL, "removing_unique_constraint", qualifiedName);
}
} else {
// Need to move the unique column to be a constraint.
setUnique(false);
String constraintName = table.buildUniqueKeyConstraintName(table.getName(), table.getFields().indexOf(this), platform.getMaxUniqueKeyNameSize());
table.addUniqueKeyConstraint(constraintName, name);
}
}
if (constraint != null) {
writer.write(" " + constraint);
}
if (additional != null) {
writer.write(" " + additional);
}
}
} catch (IOException ioException) {
throw ValidationException.fileError(ioException);
}
}
/**
* INTERNAL:
* Append the database field definition string to the type creation statement.
* Types do not support constraints.
* @param writer Target writer where to write field definition string.
* @param session Current session context.
* @throws ValidationException When invalid or inconsistent data were found.
*/
public void appendTypeString(final Writer writer, final AbstractSession session)
throws ValidationException {
final FieldTypeDefinition fieldType
= DatabaseObjectDefinition.getFieldTypeDefinition(session, type, typeName);
try {
writer.write(name);
writer.write(" ");
writer.write(fieldType.getName());
if ((fieldType.isSizeAllowed()) && ((size != 0) || (fieldType.isSizeRequired()))) {
writer.write("(");
if (size == 0) {
writer.write(Integer.toString(fieldType.getDefaultSize()));
} else {
writer.write(Integer.toString(size));
}
if (subSize != 0) {
writer.write(",");
writer.write(Integer.toString(subSize));
} else if (fieldType.getDefaultSubSize() != 0) {
writer.write(",");
writer.write(Integer.toString(fieldType.getDefaultSubSize()));
}
writer.write(")");
}
if (additional != null) {
writer.write(" " + additional);
}
} catch (IOException ioException) {
throw ValidationException.fileError(ioException);
}
}
/**
* PUBLIC:
*/
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException impossible) {
return null;
}
}
/**
* PUBLIC:
* Return any additional information about this field to be given when the table is created.
*/
public String getAdditional() {
return additional;
}
/**
* PUBLIC:
* Return any constraint of this field.
* i.e. "BETWEEN 0 AND 1000000".
*/
public String getConstraint() {
return constraint;
}
public String getForeignKeyFieldName() {
return foreignKeyFieldName;
}
/**
* PUBLIC:
* Return the name of the field.
*/
public String getName() {
return name;
}
/**
* INTERNAL:
* Return the databasefield.
*/
public DatabaseField getDatabaseField() {
return field;
}
/**
* PUBLIC:
* Return the size of the field, this is only required for some field types.
*/
public int getSize() {
return size;
}
/**
* PUBLIC:
* Return the sub-size of the field.
* This is used as the decimal precision for numeric values only.
*/
public int getSubSize() {
return subSize;
}
/**
* PUBLIC:
* Return the type of the field.
* This should be set to a java class, such as String.class, Integer.class or Date.class.
*/
public Class<?> getType() {
return type;
}
/**
* PUBLIC:
* Return the type name of the field.
* This is the generic database type name, which can be used instead of the Java class 'type'.
* This is translated to a particular database type based on platform.
*/
public String getTypeName() {
return typeName;
}
/**
* PUBLIC:
* Return the type definition of the field.
* This is database-specific complete type definition like "VARCHAR2(50) UNIQUE NOT NULL".
* If this is given, other additional type constraint fields(size, unique, null) are meaningless.
*/
public String getTypeDefinition() {
return typeDefinition;
}
/**
* PUBLIC:
* Answer whether the receiver is an identity field.
* Identity fields are Sybase specific,
* they insure that on insert a unique sequential value is stored in the row.
*/
public boolean isIdentity() {
return isIdentity;
}
/**
* PUBLIC:
* Answer whether the receiver is a primary key.
* If the table has a multipart primary key this should be set in each field.
*/
public boolean isPrimaryKey() {
return isPrimaryKey;
}
/**
* PUBLIC:
* Answer whether the receiver is a unique constraint field.
*/
public boolean isUnique() {
return isUnique;
}
/**
* PUBLIC:
* Set any additional information about this field to be given when the table is created.
*/
public void setAdditional(String string) {
additional = string;
}
/**
* PUBLIC:
* Set any constraint of this field.
* i.e. "BETWEEN 0 AND 1000000".
*/
public void setConstraint(String string) {
constraint = string;
}
public void setForeignKeyFieldName(String foreignKeyFieldName) {
this.foreignKeyFieldName = foreignKeyFieldName;
}
/**
* PUBLIC:
* Set whether the receiver is an identity field.
* Identity fields are Sybase specific,
* they insure that on insert a unique sequential value is stored in the row.
*/
public void setIsIdentity(boolean value) {
isIdentity = value;
if (value) {
setShouldAllowNull(false);
}
}
/**
* PUBLIC:
* Set whether the receiver is a primary key.
* If the table has a multipart primary key this should be set in each field.
*/
public void setIsPrimaryKey(boolean value) {
isPrimaryKey = value;
if (value) {
setShouldAllowNull(false);
}
}
/**
* PUBLIC:
* Set the name of the field.
*/
public void setName(String name) {
this.name = name;
}
/**
* INTERNAL:
* Set the DatabaseField that is associated to this FieldDefinition object.
* The databaesField is used when extending tables to see if this field already exists.
*/
public void setDatabaseField(DatabaseField field) {
this.field = field;
}
/**
* PUBLIC:
* Set whether the receiver should allow null values.
*/
public void setShouldAllowNull(boolean value) {
shouldAllowNull = value;
}
/**
* PUBLIC:
* Set the size of the field, this is only required for some field types.
*/
public void setSize(int size) {
this.size = size;
}
/**
* PUBLIC:
* Set the sub-size of the field.
* This is used as the decimal precision for numeric values only.
*/
public void setSubSize(int subSize) {
this.subSize = subSize;
}
/**
* PUBLIC:
* Set the type of the field.
* This should be set to a java class, such as String.class, Integer.class or Date.class.
*/
public void setType(Class<?> type) {
this.type = type;
}
/**
* PUBLIC:
* Set the type name of the field.
* This is the generic database type name, which can be used instead of the Java class 'type'.
* This is translated to a particular database type based on platform.
*/
public void setTypeName(String typeName) {
this.typeName = typeName;
}
/**
* PUBLIC:
* Set the type definition of the field.
* This is database-specific complete type definition like "VARCHAR2(50) UNIQUE NOT NULL".
* If this is given, other additional type constraint fields(size, unique, null) are meaningless.
*/
public void setTypeDefinition(String typeDefinition) {
this.typeDefinition = typeDefinition;
}
/**
* PUBLIC:
* Set whether the receiver is a unique constraint field.
*/
public void setUnique(boolean value) {
isUnique = value;
}
/**
* PUBLIC:
* Return whether the receiver should allow null values.
*/
public boolean shouldAllowNull() {
return shouldAllowNull;
}
@Override
public String toString() {
return Helper.getShortClassName(getClass()) + "(" + getName() + "(" + getType() + "))";
}
}