All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datanucleus.store.rdbms.table.ColumnImpl Maven / Gradle / Ivy

The newest version!
/**********************************************************************
Copyright (c) 2002 Kelly Grizzle (TJDO) and others. All rights reserved.
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.

Contributors:
2003 Erik Bengtson - added a replaceAll operation in substitution for
                    java.lang.String.replaceAll of J2SDK 1.4. Now we
                    can compile with J2SDK 1.3.1
2003 Erik Bengtson - moved replaceAll to StringUtils class
2003 Andy Jefferson - updated the DEFAULT_MAX_STRING_LENGTH to be controllable
                     via a property.
2004 Andy Jefferson - updated the NULL/NOT NULL so that we always specify the 
                     nullability of a column on creation
2004 Erik Bengtson - added columnOptions getters that can be read by
                    the SetTable or MapTable (Collection). columnOptions is
                    used for create an equivalent column on the set table
2004 Andy Jefferson - added Auto, Unique, Default, options to column SQL options
2006 Andy Jefferson - removed co, dba, lengthType, copying of ColumnMetaData
2008 Andy Jefferson - changed to implement interface, use byte "flags", cleanup
    ...
**********************************************************************/
package org.datanucleus.store.rdbms.table;

import java.sql.DatabaseMetaData;
import java.sql.Types;
import java.util.StringTokenizer;

import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.ColumnMetaData;
import org.datanucleus.metadata.JdbcType;
import org.datanucleus.store.rdbms.adapter.DatastoreAdapter;
import org.datanucleus.store.rdbms.exceptions.ColumnDefinitionException;
import org.datanucleus.store.rdbms.RDBMSPropertyNames;
import org.datanucleus.store.rdbms.RDBMSStoreManager;
import org.datanucleus.store.rdbms.exceptions.IncompatibleDataTypeException;
import org.datanucleus.store.rdbms.exceptions.WrongPrecisionException;
import org.datanucleus.store.rdbms.exceptions.WrongScaleException;
import org.datanucleus.store.rdbms.identifier.DatastoreIdentifier;
import org.datanucleus.store.rdbms.mapping.column.ColumnMapping;
import org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping;
import org.datanucleus.store.rdbms.schema.RDBMSColumnInfo;
import org.datanucleus.store.rdbms.schema.SQLTypeInfo;
import org.datanucleus.store.schema.naming.ColumnType;
import org.datanucleus.store.schema.table.MemberColumnMapping;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;

/**
 * Implementation of a Column in an RDBMS datastore. 
 * Contains the full definition of the column, its type, size, whether it is identity, nullable, part of the PK etc. 
 * The SQL column definition is generated here.
 */ 
public class ColumnImpl implements Column
{
    private static final byte PK = (byte) (1<<0); // Part of PK?
    private static final byte NULLABLE = (byte) (1<<1); // Column can store nulls?
    private static final byte UNIQUE = (byte) (1<<2); // Values are unique?
    private static final byte DEFAULTABLE = (byte) (1<<3); // Defaultable column on inserts?
    private static final byte IDENTITY = (byte) (1<<4); // AUTO_INCREMENT, SERIAL etc?

    /** Identifier for the column in the datastore. */
    protected DatastoreIdentifier identifier;

    /** ColumnMetaData for this column. */
    protected ColumnMetaData columnMetaData;

    /** Table containing this column in the datastore. */
    protected final Table table;

    /** Mapping for this column. */
    protected ColumnMapping columnMapping = null;

    /** Java type that this column is storing. (can we just get this from the mapping above ?) */
    protected final String storedJavaType;

    /** Manual override of the type name for this column (optional). */
    protected String typeName;

    /** SQL Type info for the JDBC type being stored in this column */
    protected SQLTypeInfo typeInfo;

    /** CHECK constraints to apply to this column in its SQL specification (optional). */
    protected String checkConstraints;

    /** Operational flags, for nullability, PK, autoinc, etc. */
    protected byte flags;

    /** Default value accepted by the datastore for this column, from DatabaseMetaData. */
    protected Object defaultValue;

    /** Function wrapping the column (for example, SQRT(COLUMN)). */
    protected String[] wrapperFunction;

    /**
     * Constructor.
     * @param table The table in the datastore that this column belongs to.
     * @param javaType The type of data being stored in this column
     * @param identifier The identifier of the column (in the datastore).
     * @param colmd The ColumnMetaData for this column
     */
    public ColumnImpl(Table table, String javaType, DatastoreIdentifier identifier, ColumnMetaData colmd)
    {
        this.table = table;
        this.storedJavaType = javaType;

		typeInfo = null;
		checkConstraints = null;
		flags = 0;

        setIdentifier(identifier);
        if (colmd == null)
        {
            // Create a default ColumnMetaData since none provided
            columnMetaData = new ColumnMetaData();
        }
        else
        {
            // TODO Consider making a copy here
            columnMetaData = colmd;
        }

        // Nullability
        if (columnMetaData.getAllowsNull() != null && columnMetaData.isAllowsNull())
        {
            // MetaData requires it to be nullable
            setNullable(true);
        }

        // Uniqueness
        if (columnMetaData.getUnique())
        {
            // MetaData requires it to be unique
            setUnique(true);
        }

        wrapperFunction = new String[3];
		wrapperFunction[WRAPPER_FUNCTION_SELECT]= "?";
		wrapperFunction[WRAPPER_FUNCTION_INSERT]= "?";
		wrapperFunction[WRAPPER_FUNCTION_UPDATE]= "?";
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#getName()
     */
    @Override
    public String getName()
    {
        return identifier.toString();
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#getMemberColumnMapping()
     */
    @Override
    public MemberColumnMapping getMemberColumnMapping()
    {
        throw new UnsupportedOperationException("Not supported on this Column");
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#getColumnType()
     */
    @Override
    public ColumnType getColumnType()
    {
        throw new UnsupportedOperationException("Not supported on this Column");
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#setJdbcType(org.datanucleus.metadata.JdbcType)
     */
    @Override
    public Column setJdbcType(JdbcType jdbcType)
    {
        throw new UnsupportedOperationException("Not supported on this Column");
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#getJdbcType()
     */
    @Override
    public JdbcType getJdbcType()
    {
        return typeInfo != null ? JdbcType.getEnumByValue(typeInfo.getDataType()) : null;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#setTypeName(java.lang.String)
     */
    @Override
    public Column setTypeName(String type)
    {
        this.typeName = type;
        return this;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#getTypeName()
     */
    @Override
    public String getTypeName()
    {
        return (typeName != null) ? typeName : typeInfo.getTypeName();
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#setPosition(int)
     */
    @Override
    public Column setPosition(int pos)
    {
        throw new UnsupportedOperationException("Not supported on this Column");
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#getPosition()
     */
    @Override
    public int getPosition()
    {
        throw new UnsupportedOperationException("Not supported on this Column");
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#isUnlimitedLength()
     */
    public boolean isUnlimitedLength()
    {
        // TODO Enable the two commented out lines so that we can allow people to have "BLOB(1024)" etc
        if (columnMetaData.getJdbcType() != null && (columnMetaData.getJdbcType() == JdbcType.BLOB || columnMetaData.getJdbcType() == JdbcType.CLOB)/* &&
            !typeInfo.isAllowsPrecisionSpec()*/)
        {
            // Allow for jdbc-type=BLOB/CLOB
            return true;
        }
        else if (columnMetaData.getSqlType() != null && columnMetaData.getSqlType().toLowerCase().indexOf("lob") > 0/* &&
            !typeInfo.isAllowsPrecisionSpec()*/)
        {
            // Allow for sql-type=BLOB/CLOB
            return true;
        }
        return false;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getIdentifier()
     */
    public DatastoreIdentifier getIdentifier()
    {
        return identifier;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#setIdentifier(org.datanucleus.store.rdbms.DatastoreIdentifier)
     */
    public void setIdentifier(DatastoreIdentifier identifier)
    {
        this.identifier = identifier;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getTable()
     */
    public Table getTable()
    {
        return table;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getColumnMapping()
     */
    public ColumnMapping getColumnMapping()
    {
        return columnMapping;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#setColumnMapping(org.datanucleus.store.rdbms.mapping.column.ColumnMapping)
     */
    public void setColumnMapping(ColumnMapping mapping)
    {
        columnMapping = mapping;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getJavaTypeMapping()
     */
    public JavaTypeMapping getJavaTypeMapping()
    {
        return columnMapping.getJavaTypeMapping();
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getStoredJavaType()
     */
    public String getStoredJavaType()
    {
        return storedJavaType;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#setTypeInfo(org.datanucleus.store.rdbms.schema.SQLTypeInfo)
     */
    public final Column setTypeInfo(SQLTypeInfo typeInfo)
    {
        if (this.typeInfo == null)
        {
            this.typeInfo = typeInfo;
        }
        return this;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getTypeInfo()
     */
    public final SQLTypeInfo getTypeInfo()
    {
        return typeInfo;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getStoreManager()
     */
    public RDBMSStoreManager getStoreManager()
    {
        return table.getStoreManager();
    }

    /**
     * Accessor for the precision of data in the column.
     * @return The precision of data in the column
     */
    private int getSQLPrecision()
    {
        int sqlPrecision = - 1;
        if (columnMetaData.getLength() != null && columnMetaData.getLength().intValue() > 0)
        {
            // User-provided length, so use it
            sqlPrecision = columnMetaData.getLength().intValue();
        }
        else if (isUnlimitedLength())
        {
            // Use the precision for "unlimited length" if defined
            int ulpv = getStoreManager().getDatastoreAdapter().getUnlimitedLengthPrecisionValue(typeInfo);
            if (ulpv > 0)
            {
                sqlPrecision = ulpv;
            }
        }

        // Databases like Derby that use BIT types for binary need to have the length expressed in bits, not bytes.
        if (typeInfo.getTypeName().toLowerCase().startsWith("bit"))
        {
            return sqlPrecision * 8;
        }
        return sqlPrecision;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getSQLDefinition()
     */
    public String getSQLDefinition()
    {
        StringBuilder def = new StringBuilder(identifier.toString());

        if (!StringUtils.isWhitespace(columnMetaData.getColumnDdl()))
        {
            // User-defined DDL, so assume they set the type etc to something sensible
            // Note that the JPA spec doesn't explicitly specify if this has to include the type or not
            def.append(" ").append(columnMetaData.getColumnDdl());
            return def.toString();
        }

        DatastoreAdapter adapter = getStoreManager().getDatastoreAdapter();

        // Add any type specification.
        if (adapter.supportsOption(DatastoreAdapter.IDENTITY_COLUMNS) && isIdentity() && !adapter.supportsOption(DatastoreAdapter.IDENTITY_COLUMN_TYPE_SPECIFICATION))
        {
            // Don't add type
        }
        else if (typeName != null)
        {
            // Allow for manual override of "type" for things like MySQL ENUM where we want to define the type with its options
            def.append(" " + typeName);
        }
        else
        {
            StringBuilder typeSpec = new StringBuilder(typeInfo.getTypeName());

            // Parse and append createParams to the typeName if it looks like it's supposed to be appended,
            // i.e. if it contains parentheses, and the type name itself doesn't. createParams is mighty ill-defined by the JDBC spec, but attempt to interpret it.
            if (typeInfo.getCreateParams() != null && typeInfo.getCreateParams().indexOf('(') >= 0 && typeInfo.getTypeName().indexOf('(') < 0)
            {
                StringTokenizer toks = new StringTokenizer(typeInfo.getCreateParams());
                
                while (toks.hasMoreTokens())
                {
                    String tok = toks.nextToken();
                    if (tok.startsWith("[") && tok.endsWith("]"))
                    {
                        // The brackets look like they indicate an optional param so
                        // skip
                        continue;
                    }

                    typeSpec.append(" " + tok);
                }
            }

            // Add any precision. We use the "allowsPrecisionSpec" flag for this
            StringBuilder precSpec = new StringBuilder();
            int sqlPrecision = getSQLPrecision();
            if (sqlPrecision > 0 && typeInfo.isAllowsPrecisionSpec())
            {
                precSpec.append(sqlPrecision);

                // Optional "scale" for the precision e.g BIGDECIMAL(8,3)
                if (columnMetaData.getScale() != null)
                {
                    precSpec.append("," + columnMetaData.getScale());
                }

                // Optional "type" for the precision, where the database supports it
                if (adapter.supportsOption(DatastoreAdapter.COLUMN_LENGTH_SEMANTICS))
                {
                    if (columnMetaData.hasExtension("column-length-semantic"))
                    {
                        precSpec.append(" " + columnMetaData.getValueForExtension("column-length-semantic"));
                    }
                }
            }
            else if (sqlPrecision > 0 && !typeInfo.isAllowsPrecisionSpec())
            {
                NucleusLogger.DATASTORE_SCHEMA.warn(Localiser.msg("020183", this.toString()));
            }

            int lParenIdx = typeSpec.toString().indexOf('(');
            int rParenIdx = typeSpec.toString().indexOf(')', lParenIdx);
            if (lParenIdx > 0 && rParenIdx > 0)
            {
                // Some databases (like DB2) give you typeNames with ()'s already
                // present ready for you to insert the values instead of appending them.
                if (precSpec.length() > 0)
                {
                    typeSpec.replace(lParenIdx + 1, rParenIdx, precSpec.toString());
                }
                else if (rParenIdx == lParenIdx + 1)
                {
                    throw new ColumnDefinitionException(Localiser.msg("020184", this.toString()));
                }
            }
            else if (precSpec.length() > 0)
            {
                typeSpec.append('(');
                typeSpec.append(precSpec.toString());
                typeSpec.append(')');
            }
            def.append(" " + typeSpec.toString());
        }

        // Add DEFAULT (if specifiable before NULL)
        if (adapter.supportsOption(DatastoreAdapter.DEFAULT_BEFORE_NULL_IN_COLUMN_OPTIONS) && 
            adapter.supportsOption(DatastoreAdapter.DEFAULT_KEYWORD_IN_COLUMN_OPTIONS) &&
            columnMetaData.getDefaultValue() != null)
        {
            def.append(" ").append(getDefaultDefinition());
        }

        if (isIdentity() && isPrimaryKey() && adapter.supportsOption(DatastoreAdapter.IDENTITY_PK_IN_CREATE_TABLE_COLUMN_DEF))
        {
            def.append(" PRIMARY KEY");
        }

        // Nullability
        if (adapter.supportsOption(DatastoreAdapter.IDENTITY_COLUMNS) && isIdentity() && !adapter.supportsOption(DatastoreAdapter.IDENTITY_KEYS_NULL_SPECIFICATION))
        {
            // Do nothing since the adapter doesn't allow NULL specifications with autoincrement/identity
        }
        else
        {
            if (!isNullable())
            {
                if (columnMetaData.getDefaultValue() == null || adapter.supportsOption(DatastoreAdapter.DEFAULT_KEYWORD_WITH_NOT_NULL_IN_COLUMN_OPTIONS))
                {
                    def.append(" NOT NULL");
                }
            }
            else if (typeInfo.getNullable() == DatabaseMetaData.columnNullable)
            {
                if (adapter.supportsOption(DatastoreAdapter.NULLS_KEYWORD_IN_COLUMN_OPTIONS))
                {
                    def.append(" NULL");
                }
            }
        }

        // Add DEFAULT (if specifiable after NULL)
        if (!adapter.supportsOption(DatastoreAdapter.DEFAULT_BEFORE_NULL_IN_COLUMN_OPTIONS) && 
            adapter.supportsOption(DatastoreAdapter.DEFAULT_KEYWORD_IN_COLUMN_OPTIONS) &&
            columnMetaData.getDefaultValue() != null)
        {
            def.append(" ").append(getDefaultDefinition());
        }

        // CHECK Constraints
        if (adapter.supportsOption(DatastoreAdapter.CHECK_IN_CREATE_STATEMENTS) && checkConstraints != null)
        {
            def.append(" " + checkConstraints.toString());
        }

        // Auto Increment
        if (adapter.supportsOption(DatastoreAdapter.IDENTITY_COLUMNS) && isIdentity())
        {
            def.append(" " + adapter.getIdentityKeyword(table.getStoreManager(), columnMapping));
        }

        // Uniqueness
        if (isUnique() && !adapter.supportsOption(DatastoreAdapter.UNIQUE_IN_END_CREATE_STATEMENTS))
        {
            def.append(" UNIQUE");
        }

        return def.toString();
    }

    /**
     * Convenience method to return the "DEFAULT" part of the column definition.
     * @return The default part of the column definition.
     */
    private String getDefaultDefinition()
    {
        if (columnMetaData.getDefaultValue().equalsIgnoreCase("#NULL"))
        {
            // Special case
            // Note that this really ought to be coordinated with "DEFAULT_KEYWORD_WITH_NOT_NULL_IN_COLUMN_OPTIONS"
            this.defaultValue = null;
            return "DEFAULT NULL";
        }

        // Quote any character types (CHAR, VARCHAR, BLOB, CLOB)
        if (typeInfo.getTypeName().toUpperCase().indexOf("CHAR") >= 0 || typeInfo.getTypeName().toUpperCase().indexOf("LOB") >= 0)
        {
            // NOTE We use single quote here but would be better to take
            // some character from the DatabaseMetaData. The "identifierQuoteString"
            // does not work for string quoting here for Postgres
            this.defaultValue = columnMetaData.getDefaultValue();
            return "DEFAULT '" + columnMetaData.getDefaultValue() + "'";
        }
        else if (typeInfo.getTypeName().toUpperCase().indexOf("BIT") == 0)
        {
            if (columnMetaData.getDefaultValue().equalsIgnoreCase("true") || columnMetaData.getDefaultValue().equalsIgnoreCase("false"))
            {
                // Quote any "true"/"false" values for BITs
                this.defaultValue = columnMetaData.getDefaultValue();
                return "DEFAULT '" + columnMetaData.getDefaultValue() + "'";
            }
        }

        this.defaultValue = columnMetaData.getDefaultValue();
        return "DEFAULT " + columnMetaData.getDefaultValue();
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#initializeColumnInfoFromDatastore(org.datanucleus.store.rdbms.schema.RDBMSColumnInfo)
     */
    public void initializeColumnInfoFromDatastore(RDBMSColumnInfo ci)
    {
        String column_default = ci.getColumnDef();
        if (!StringUtils.isWhitespace(column_default))
        {
            // Set column defaultValue using defaultValue defined on the datastore column
            if (column_default.startsWith("'") && column_default.endsWith("'"))
            {
                // JDBC javadoc, if starts with single quotes then is a string
                String colDefString = column_default.replace("'", "");
                setDefaultable(colDefString);
            }
            else
            {
                if (!column_default.equalsIgnoreCase("null"))
                {
                    // Non-null value so use it (null is the default so don't need that)
                    String columnDef = column_default.replace("'", "").replace("\"", "").replace(")", "").replace("(", "");
                    if (!columnDef.equalsIgnoreCase("null"))
                    {
                        setDefaultable(columnDef);
                    }
                }
            }
        }

        // TODO Make sure that this lines up with the defaultValue when set.
        try
        {
            setIdentity(getStoreManager().getDatastoreAdapter().isIdentityFieldDataType(ci.getColumnDef()));
        }
        catch( UnsupportedOperationException ex)
        {
            //do nothing, or maybe log
        }
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#validate(org.datanucleus.store.rdbms.schema.RDBMSColumnInfo)
     */
    public void validate(RDBMSColumnInfo ci)
    {
        if (!typeInfo.isCompatibleWith(ci))
        {
            throw new IncompatibleDataTypeException(this, typeInfo.getDataType(), ci.getDataType());
        }
        if (ci.getDataType() == Types.OTHER)
        {
            return;
        }

        if (table instanceof TableImpl)
        {
            // Only validate precision/scale/nullability for tables
            if (typeInfo.isAllowsPrecisionSpec())
            {
                int actualPrecision = ci.getColumnSize();
                int actualScale = ci.getDecimalDigits();
                int sqlPrecision = getSQLPrecision();
                
                if (sqlPrecision > 0 && actualPrecision > 0)
                {
                    if (sqlPrecision != actualPrecision)
                    {
                        if (this.columnMetaData != null && this.columnMetaData.getParent() != null && 
                            (this.columnMetaData.getParent() instanceof AbstractMemberMetaData))
                        {
                            //includes the field name in error msg
                            throw new WrongPrecisionException(this.toString(), sqlPrecision, 
                                actualPrecision, 
                                ((AbstractMemberMetaData)this.columnMetaData.getParent()).getFullFieldName());
                        }

                        throw new WrongPrecisionException(this.toString(), sqlPrecision, actualPrecision);
                    }
                }

                if (this.columnMetaData != null && columnMetaData.getScale() != null && actualScale >= 0)
                {
                    if (columnMetaData.getScale().intValue() != actualScale)
                    {
                        if (this.columnMetaData.getParent() != null && this.columnMetaData.getParent() instanceof AbstractMemberMetaData)
                        {
                            //includes the field name in error msg
                            throw new WrongScaleException(this.toString(), columnMetaData.getScale().intValue(), actualScale, 
                                ((AbstractMemberMetaData)this.columnMetaData.getParent()).getFullFieldName());
                        }
                        throw new WrongScaleException(this.toString(), columnMetaData.getScale().intValue(), actualScale);
                    }
                }
            }

            String actualIsNullable = ci.getIsNullable();
            if (actualIsNullable.length() > 0)
            {
                switch (Character.toUpperCase(actualIsNullable.charAt(0)))
                {
                    case 'Y' :
                        if (!isNullable())
                        {
                            NucleusLogger.DATASTORE.warn(Localiser.msg("020025", this));
                        }
                        break;

                    case 'N' :
                        // TODO convert to default value somewhere at runtime if
                        // column has a default value and "default"
                        break;

                    default :
                        break;
                }
            }
            
            try
            {
                if (isIdentity() != getStoreManager().getDatastoreAdapter().isIdentityFieldDataType(ci.getColumnDef()))
                {
                    //TODO localise
                    if (isIdentity())
                    {
                        throw new NucleusException("Expected an auto increment column ("+getIdentifier()+
                            ") in the database, but it is not").setFatal();
                    }

                    throw new NucleusException("According to the user metadata, the column ("+
                            getIdentifier()+") is not auto incremented, but the database says it is.").setFatal();
                }
            }
            catch (UnsupportedOperationException ex)
            {
                //ignore this validation step
            }
        }
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#setCheckConstraints(java.lang.String)
     */
    public final Column setCheckConstraints(String constraints)
    {
        this.checkConstraints = constraints;
        return this;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#setPrimaryKey()
     */
    public final Column setPrimaryKey()
    {
        flags |= PK;
        //primary keys cannot be null
        flags &= ~NULLABLE;
        return this;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#setNullable(boolean)
     */
    public final Column setNullable(boolean flag)
    {
        if (flag)
        {
            flags |= NULLABLE;
        }
        else
        {
            flags &= ~NULLABLE;
        }
        return this;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#setDefaultable(java.lang.Object)
     */
    public final Column setDefaultable(Object defaultValue)
    {
        if (!getStoreManager().getBooleanProperty(RDBMSPropertyNames.PROPERTY_RDBMS_COLUMN_DEFAULT_WHEN_NULL))
        {
            return this;
        }

        flags |= DEFAULTABLE;
        this.defaultValue = defaultValue;
        return this;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#setUnique(boolean)
     */
    public final Column setUnique(boolean flag)
    {
        if (flag)
        {
            flags |= UNIQUE;
        }
        else
        {
            flags &= ~UNIQUE;
        }
        return this;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#setIdentity(boolean)
     */
    public Column setIdentity(boolean identity)
    {
        if (identity)
        {
            flags |= IDENTITY;
        }
        else
        {
            flags &= ~IDENTITY;
        }
        return this;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#isPrimaryKey()
     */
    public final boolean isPrimaryKey()
    {
        return ((flags & PK) != 0);
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#isNullable()
     */
    public final boolean isNullable()
    {
        return ((flags & NULLABLE) != 0);
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#isDefaultable()
     */
    public final boolean isDefaultable()
    {
        return ((flags & DEFAULTABLE) != 0);
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#isUnique()
     */
    public final boolean isUnique()
    {
        return ((flags & UNIQUE) != 0);
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#isIdentity()
     */
    public boolean isIdentity()
    {
        return ((flags & IDENTITY) != 0);
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getDefaultValue()
     */
    public Object getDefaultValue()
    {
        return defaultValue;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getColumnMetaData()
     */
    public final ColumnMetaData getColumnMetaData()
    {
        return columnMetaData;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getFieldMetaData()
     */
    public AbstractMemberMetaData getMemberMetaData()
    {
        if (columnMetaData != null && columnMetaData.getParent() instanceof AbstractMemberMetaData)
        {
            return (AbstractMemberMetaData)columnMetaData.getParent();
        }
        return null;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.schema.table.Column#setColumnMetaData(org.datanucleus.metadata.ColumnMetaData)
     */
    public Column setColumnMetaData(ColumnMetaData colmd)
    {
        if (colmd == null)
        {
            // Nothing to do since no definition of requirements
            return this;
        }

        if (colmd.getJdbcType() != null)
        {
            columnMetaData.setJdbcType(colmd.getJdbcType());
        }
        if (colmd.getSqlType() != null)
        {
            columnMetaData.setSqlType(colmd.getSqlType());
        }
        if (colmd.getName() != null)
        {
            columnMetaData.setName(colmd.getName());
        }
        if (colmd.getAllowsNull() != null)
        {
            columnMetaData.setAllowsNull(Boolean.valueOf(colmd.isAllowsNull()));
        }
        if (colmd.getLength() != null)
        {
            columnMetaData.setLength(colmd.getLength());
        }
        if (colmd.getScale() != null)
        {
            columnMetaData.setScale(colmd.getScale());
        }

        if (colmd.getAllowsNull() != null && colmd.isAllowsNull())
        {
            // MetaData requires it to be nullable
            setNullable(true);
        }
        if (colmd.getUnique())
        {
            // MetaData requires it to be unique
            setUnique(true);
        }
        return this;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getCheckConstraints()
     */
    public String getCheckConstraints()
    {
        return checkConstraints;
    }
    
    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#checkPrimitive()
     */
    public final void checkPrimitive() throws ColumnDefinitionException
    {
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#checkInteger()
     */
    public final void checkInteger() throws ColumnDefinitionException
    {
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#checkDecimal()
     */
    public final void checkDecimal() throws ColumnDefinitionException
    {
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#checkString()
     */
    public final void checkString() throws ColumnDefinitionException
    {
        if (columnMetaData.getJdbcType() == null)
        {
            columnMetaData.setJdbcType(JdbcType.VARCHAR);
        }
        if (columnMetaData.getLength() == null)
        {
            // Use the default string length
            columnMetaData.setLength(getStoreManager().getIntProperty(RDBMSPropertyNames.PROPERTY_RDBMS_STRING_DEFAULT_LENGTH));
        }
    }

	/* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#copyConfigurationTo(org.datanucleus.store.rdbms.table.Column)
     */
	public void copyConfigurationTo(Column colIn)
    {
        ColumnImpl col = (ColumnImpl) colIn;
        col.typeInfo = this.typeInfo;
        col.typeName = this.typeName;
        col.flags |= this.flags;
        col.flags &= ~PK;
        col.flags &= ~UNIQUE;
        col.flags &= ~NULLABLE;
        col.flags &= ~IDENTITY;
        col.flags &= ~DEFAULTABLE;
        col.defaultValue = this.defaultValue;
        col.wrapperFunction = this.wrapperFunction;

        // Copy key aspects of ColumnMetaData across. May need to copy other parts in future
        if (this.columnMetaData.getJdbcType() != null)
        {
            col.columnMetaData.setJdbcType(this.columnMetaData.getJdbcType());
        }
        if (this.columnMetaData.getSqlType() != null)
        {
            col.columnMetaData.setSqlType(this.columnMetaData.getSqlType());
        }
        if (this.columnMetaData.getLength() != null)
        {
            col.getColumnMetaData().setLength(this.columnMetaData.getLength());
        }
        if (this.columnMetaData.getScale() != null)
        {
            col.getColumnMetaData().setScale(this.getColumnMetaData().getScale());
        }
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#applySelectFunction(java.lang.String)
     */
    public String applySelectFunction(String replacementValue)
    {
        if (replacementValue == null)
        {
            return wrapperFunction[WRAPPER_FUNCTION_SELECT];
        }
        if (wrapperFunction[WRAPPER_FUNCTION_SELECT] != null)
        {
            return wrapperFunction[WRAPPER_FUNCTION_SELECT].replace("?", replacementValue);
        }
        return replacementValue;
    }    

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#setWrapperFunction(java.lang.String, int)
     */
    public void setWrapperFunction(String wrapperFunction, int wrapperMode)
    {
        if (wrapperFunction != null && wrapperMode == WRAPPER_FUNCTION_SELECT && wrapperFunction.indexOf("?") < 0)
        {
            throw new NucleusUserException("Wrapping function must have one '?'. e.g. SQRT(?)");
        }
        this.wrapperFunction[wrapperMode] = wrapperFunction;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.table.Column#getWrapperFunction(int)
     */
    public String getWrapperFunction(int wrapperMode)
    {
        return wrapperFunction[wrapperMode];
    }

    public boolean equals(Object obj)
    {
        if (obj == this)
        {
            return true;
        }
        if (!(obj instanceof ColumnImpl))
        {
            return false;
        }

        ColumnImpl col = (ColumnImpl)obj;
        return table.equals(col.table) && identifier.equals(col.identifier);
    }

    public int hashCode()
    {
        return table.hashCode() ^ identifier.hashCode();
    }

    public String toString()
    {
        return table.toString() + "." + identifier;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy