Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-dev] questions while running JPA JUnit tests

Hi Dies,

  I have been running the tests and in general things are going well.

I am concerned about one issue. There are a number of table creators that have been altered with Symfoware specific code to switch to using a FAST_TABLE_CREATOR. I am concerned that as people add new table creators it will be hard to enforce including that kind of code for each table creator.

I have been trying to find a more generic way. I am attaching some rough changes to TableCreator and SchemaManager.(the code still needs some work)

The idea is that with these changes, we could run the Symfoware tests with the "eclipselink.toggle-table-creator" property enabled and that would allow us to remove custom code from most of the TableCreator subclasses. For instance, the method below on AdvancedTableCreator could be removed:

    @Override
    public void replaceTables(DatabaseSession session) {
        // on Symfoware, to avoid table locking issues only the first invocation
        // of an instance of this class drops & recreates the tables.
        boolean isFast = SchemaManager.FAST_TABLE_CREATOR;
        try {
            if (onlyDelete && session.getPlatform().isSymfoware()) {
                SchemaManager.FAST_TABLE_CREATOR = true;
            }
            super.replaceTables(session);
        } finally {
            SchemaManager.FAST_TABLE_CREATOR = isFast;
        }
        // subsequent instances/invocations delete the rows instead.
        onlyDelete = true;
    }

  Can you think of any issues with making these kinds of changes?

Thanks,
Tom

Dies Koper wrote:
Hi Tom,

- EmployeeSystem - why are we removing the call to dropTableConstraints()?

I believe in the next line it calls EmployeeTableCreator().replaceTables(session), which calls super.replaceTables(session), which eventually calls replaceTablesAndConstraints, which has 'schemaManager.dropConstraints', so the constraints are dropped already.

But if I misunderstood I can put it back in a SchemaManager.FAST_TABLE_CREATOR check block like the others. (or in a isSymfoware() block as Symfoware doesn't support foreign keys anyway.

- There are a number of places where a DeleteAll query has been replaces
by a native query. (e.g. EntityMappingsAdvancedTestSuite,
EntityManagerTLRJUnitTestSuite, EntityManagerJunitTestSuite) I am
concerned that this does not account for the multi-table nature of
Employee. I am also concerned that adding more native statements will
not adapt if the model changes either to remove the multi-table nature,
or to add another table) Perhaps we should isolate these calls to the
case where Symfoware is active.

Sure.
Would you like to skip the ones you are concerned about so I can pick them up easily after you've applied the rest? Then I can prepare a new patch where I do the native queries in a isSymfoware() block (and double-check for missing queries on secondary tables).

Thanks,
Dies


Dies Koper wrote:
Hi Tom,

I'm getting the same! Sorry about that.

I've prepared the patch again, confirmed there is no error when
applying it, and uploaded it again.

Thanks,
Dies


On 27/01/2010 05:55, Tom Ware wrote:
Hi Dies,

Can you try to reupload? I am getting an error that says: "An unknown
line type was found in line 1380" when I try to apply the patch.

-Tom

Dies Koper wrote:
Hi Tom,

I have attached the patch with the complete set of changes.
Please let me know if you have any questions.

https://bugs.eclipse.org/bugs/show_bug.cgi?id=288715

Thanks,
Dies


On 26/01/2010 02:06, Tom Ware wrote:
Hi Dies,

Is it possible to provide a single patch with your complete change set
based on the latest code? If you can provide that, I'll start working
integrating and testing the changes.

-Tom

Dies Koper wrote:
Hi Tom, James,

I ended up implementing this after all.
Patch (completely split settings) attached to the Symfoware issue.

The reason is that Symfoware ignores the setMaxRows setting depending on the cursor type, causing some tests to fail. At least with LIMIT I
can be sure that it works no matter what the cursor policy is.

With this patch, Andrei's (for SCHEMA table name) and the previous
one
I uploaded, I have completed all changes to common code that I need
for Symfoware to work.
With these I passed 100% of the SRG test sets, and Core LRG.
Please let me know when you can accept the final patch with the
Symfoware platform class itself, the isSymfoware() methods and the
inclusions/exclusions to the tests for Symfoware.

Thanks,
Dies


On 16/12/2009 01:51, Tom Ware wrote:
Hi Dies,

To check this in, the setting will have to be completely split. i.e.
remove the methods that affect both settings
(setIgnoreFirstResultsMaxRowsSettings and
shouldIgnoreFirstResultsMaxRowsSettings). Instead, have separate
getters
and setters for each setting, and make the appropriate changes to
classes depending on those methods.

-Tom

Dies Koper wrote:
Hi Tom,

I have started looking at your patch. I have a quick question:

- In DatabaseAccessor, what is the goal of splitting the
ignoreFirstRowSettings and the ignoreMaxResultsSettings

Symfoware supports LIMIT, but has no equivalent for OFFSET.

Currently, the flag setIgnoreFirstRowMaxResultsSettings toggles
both,
so either both JDBC absolute and setMaxRows are called, or both are
not.

To support pagination using SQL I override printSQLSelectStatement
like the other platforms do, but as I only have a LIMIT in the
query I
need EclipseLink to only call the JDBC absolute method when
FirstRow
is set.

So my implementation method looks like:

public void printSQLSelectStatement(DatabaseCall call,
ExpressionSQLPrinter printer, SQLSelectStatement statement) {
int max = 0;
int firstRow = 0;

statement.setMaximumAliasLength(getMaxFieldNameSize());
if (statement.getQuery() != null) {
max = statement.getQuery().getMaxRows();
firstRow = statement.getQuery().getFirstResult();
}

// only MaxRows can be worked into the statement
if (max > 0 && this.shouldUseRownumFiltering()) {
statement.setUseUniqueFieldAliases(true);
call.setFields(statement.printSQL(printer));
printer.printString(" WITH OPTION LIMIT (");
printer.printParameter(DatabaseCall.MAXROW_FIELD);
printer.printString(")");

call.setIgnoreMaxResultsSettings(true);
} else {
// use JDBC absolute and setMaxRows methods for pagination
super.printSQLSelectStatement(call, printer, statement);
}
}

Thanks,
Dies


Dies Koper wrote:
Hi Tom, all

Congrats on the 2.0 release!

I've been continuing running the test sets on the Symfoware
platform.
May I ask you for you help with the following three issues?

1. Review of patch

I've added a patch with changes required for a number of tests to
run. Could you review them, and if okay, commit them?
https://bugs.eclipse.org/bugs/show_bug.cgi?id=288715

2. Where is table name "SEQUENCE" defined?

I tried renaming all occurrences of the word "SEQUENCE" in the
code
I could find but still some tests try to create a sequence table
with table name SEQUENCE.
It starts with the 8th test in the JPA test set:
testTransitionToDeferedFailure

which tries a UPDATE SEQUENCE (...).

In the log I see even more:

[junit] [EL Config]:
ServerSession(1006920425)--Thread(Thread[main,5,main])--The table
generator name defined within [class
org.eclipse.persistence.testing.models.jpa.inherited.Alpine] is
being defaulted to: SEQUENCE.

and

[junit] [EL Fine]:
ServerSession(1006920425)--Connection(753758527)--Thread(Thread[main,5,main])--CREATE



TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT
DECIMAL(18), PRIMARY KEY (SEQ_NAME))
[junit] java.sql.SQLException: [SymfoWARE ODBC Driver][SymfoWARE
[...]
[junit] at
org.eclipse.persistence.testing.tests.jpa.advanced.concurrency.LifecycleJUnitTest.testSetup(LifecycleJUnitTest.java:76)





I believe these might be from the CONCURRENCYA, B and C
tables, or
ConcurrencyA, B and C entities. Each of them has
fieldID.setIsIdentity(true) resp. @GeneratedValue specified
but the
details of the sequence generators are omitted.

I though it would default to what the platform's
createPlatformDefaultSequence() method returns but apparently
not.

Many tests fail because of this issue.

3. Table locks during DROP TABLE

In the SymfowarePlatform wiki page you suggested that we should
start by trying to get the Core SRG running first. Most are
passing
now.

Only a few (the ReadAllTest tests, which rely on
AutoTableGeneratorBasicTestModel.java) failed I think because of
the
table lock issue.

The error message is:

Exception Description: 24 objects were read from the database,
but
originially there were, 12.

When I run it by itself through the test browser it passes
without
problems.

Some messages in the finest log let me believe this is because a
problem
occurred while recreating the tables: they should have been
dropped and
recreated, thereby clearing those old 12 object, but because
of the
locking issue they were not.

[junit] Internal Exception: java.sql.SQLException: [SymfoWARE
ODBC
Driver][SymfoWARE Server] JYP3913E : Table "PHONE" being used
exclusively by another user.
[junit] Error Code: -3913
[junit] Call: drop table PHONE
[junit] Query: DataModifyQuery(sql="drop table PHONE")
[junit] at
org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:324)




[..]
[junit] at
org.eclipse.persistence.testing.models.employee.relational.EmployeeSystem.dropTableConstraints(EmployeeSystem.java:86)




[junit] at
org.eclipse.persistence.testing.tests.schemaframework.AutoTableGeneratorEmployeeSystem.createTables(AutoTableGeneratorEmployeeSystem.java:32)






This table was created and accessed as follows:

1. drop (index) and table (outside transaction)
DROP TABLE PHONE

2. create table (and index) (in transaction), same connection
CREATE TABLE PHONE (EMP_ID NUMERIC(15) NOT NULL, TYPE VARCHAR(15)
NOT NULL, AREA_CODE VARCHAR(3), P_NUMBER VARCHAR(7), PRIMARY KEY
(EMP_ID, TYPE))

3. insert 30 rows (in single transaction), same connection
INSERT INTO PHONE (TYPE, AREA_CODE, P_NUMBER, EMP_ID) VALUES
(?, ?,
?, ?)
[junit] bind => [Home, 613, 5551234, 105]

4. drop (index) and table (outside transaction), different
connection
DROP TABLE PHONE
-> SQLException saying PHONE is locked.

I can reproduce this in a simple JDBC application. If I close the
connection used in steps 1-3 before step 4 it works fine.

I can see some session swapping is done in
AutoTableGeneratorBasicTestModel#addForcedRequiredSystems() so I
tried adding a reset() invocation at the start of its
addForcedRequiredSystems() method but that did not help.
How can I make it close the physical connection?
(You mentioned adding logic into TestSystem?)

The lock issue occurs much more often in the JPA test set. The
first
failing test however is because of the reserved SEQUENCE keyword
used as table name, so if you could help me in the right
direction
with the above three issues I can try move forward with this test
set again.

Thanks!
Dies


Tom Ware wrote:
Hi Dies,

FYI: I am working on integrating your initial patch. There are a
couple of items I will not be integrating at the moment:

1. Specific references to SymfowarePlatform and
isSymfowarePlatform(). These will be left until
SymfowarePlatform
becomes part of the full product

2. Changes to default SEQUENCE table name to "\"SEQUENCE\"" for
our
Sessions and project XML (in XMLSessionConfigProject,
ObjectPersistenceRuntimeXMLProject and SequencingConfig).
This is
simply to big a backward compatibility risk since I cannot test
these changes on every database platform that EclipseLink is
run on
by our customers. I am looking for some way of having this
information stored on the DatabasePlatform, but at the
moment, it
is not looking good. Assuming I do not find a good solution,
Symfoware users will simply have to explicitly set these items
when
using our proprietary configuration code (sessions.xml and
deployment.xml) We will also have to address anywhere in the
tests
we choose to run where this is an issue.

I am in the process of running testing on the other changes and
I'll let you know when it gets checked in.

-Tom

Dies Koper wrote:
Hi Tom,

I have added the outstanding issues with SQL keywords in
table/columns names, and maximum precision. The biggest open
issue
is of course the drop table restriction. I'm looking forward to
hearing what solution(s) you can come up with.

Thanks!
Dies


Tom Ware wrote:
Hi Dies,

I am moving some of the main points of this discussion to the
wiki page so I can more easily keep track of where we are:

http://wiki.eclipse.org/EclipseLink/Development/Incubator/Extensions/SymfowarePlatform#Open_Issues





I started by just adding some basics about the main issues
that
are getting in the way at the moment. I plan to add more
detail
next week. Please feel free to add other issues I have
missed, or
more detail.

As for some of the other things you were wondering about:

1. Your initial patch (including sequencing fixes etc.). I
hope
to find some time to start integrating it next week

2. EclipseLink bug 286907 (not related to Symfoware platform)-
this bug is in the queue and will be addressed in sequence
with
the other bugs. The fact that you have submitted a patch
means it
will potentially jump other bugs in the queue. At the moment
I am
going to focus on the Symfoware Platform issues with my free
time, so I can't make any promises about when it will be
included.

3. GlassFish bug 9179 (not related to Symfoware platform) -
same
thing as above, but additionally: we cannot legally make
use of
the submitted patch until it is attached to EclipseLink bug by
someone with IP rights

I'll get started looking for some solutions next week.
-Tom


_______________________________________________
eclipselink-dev mailing list
eclipselink-dev@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/eclipselink-dev
/*******************************************************************************
 * Copyright (c) 1998, 2010 Oracle. 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:
 *     Oracle - initial API and implementation from Oracle TopLink
 ******************************************************************************/  
package org.eclipse.persistence.tools.schemaframework;

import java.util.*;

import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.logging.SessionLog;

/**
 * <b>Purpose</b>: This class is reponsible for creating the tables defined in the project.
 * A specific subclass of this class is created for each project.  The specific table information
 * is defined in the subclass.
 *
 * @since TopLink 2.0
 * @author Peter Krogh
 */
public class TableCreator {
    protected Vector tableDefinitions;
    protected String name;
    protected boolean ignoreDatabaseException; //if true, DDL generation will continue even if exceptions occur
    protected boolean useFastTableCreator = false;
    
    public TableCreator() {
        this(new Vector());
    }

    public TableCreator(Vector tableDefinitions) {
        super();
        this.tableDefinitions = tableDefinitions;
    }

    /**
     * Add the table.
     */
    public void addTableDefinition(TableDefinition tableDefinition) {
        tableDefinitions.addElement(tableDefinition);
    }
    
    /**
     * Add a set of tables.
     */
    public void addTableDefinitions(Collection tableDefs) {
        tableDefinitions.addAll(tableDefs);
    }


    /**
     * Create constraints.
     */
    public void createConstraints(org.eclipse.persistence.sessions.DatabaseSession session) {
        //CR2612669
        createConstraints(session, new SchemaManager(session));
    }

    /**
     * Create constraints.
     */
    public void createConstraints(org.eclipse.persistence.sessions.DatabaseSession session, SchemaManager schemaManager) {
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            schemaManager.buildFieldTypes((TableDefinition)enumtr.nextElement());
        }

        // Unique constraints should be generated before foreign key constraints,
        // because foreign key constraints can reference unique constraints
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            try {
                schemaManager.createUniqueConstraints((TableDefinition)enumtr.nextElement());
            } catch (DatabaseException ex) {
                if (!shouldIgnoreDatabaseException()) {
                    throw ex;
                }
            }
        }
        
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            try {
                schemaManager.createForeignConstraints((TableDefinition)enumtr.nextElement());
            } catch (DatabaseException ex) {
                if (!shouldIgnoreDatabaseException()) {
                    throw ex;
                }
            }
        }
    }

    /**
     * This creates the tables on the database.
     * If the table already exists this will fail.
     */
    public void createTables(org.eclipse.persistence.sessions.DatabaseSession session) {
        //CR2612669
        createTables(session, new SchemaManager(session));
    }

    /**
     * This creates the tables on the database.
     * If the table already exists this will fail.
     */
    public void createTables(org.eclipse.persistence.sessions.DatabaseSession session, SchemaManager schemaManager) {
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            schemaManager.buildFieldTypes((TableDefinition)enumtr.nextElement());
        }

        String sequenceTableName = getSequenceTableName(session);
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            // Must not create sequence table as done in createSequences.
            TableDefinition table = (TableDefinition)enumtr.nextElement();
            if (!table.getName().equals(sequenceTableName)) {
                try {
                    schemaManager.createObject(table);
                    session.getSessionLog().log(SessionLog.FINEST, "default_tables_created", table.getFullName());
                } catch (DatabaseException ex) {
                    session.getSessionLog().log(SessionLog.FINEST, "default_tables_already_existed", table.getFullName());
                    if (!shouldIgnoreDatabaseException()) {
                        throw ex;
                    }
                }
            }
        }

        // Unique constraints should be generated before foreign key constraints,
        // because foreign key constraints can reference unique constraints
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            try {
                schemaManager.createUniqueConstraints((TableDefinition)enumtr.nextElement());
            } catch (DatabaseException ex) {
                if (!shouldIgnoreDatabaseException()) {
                    throw ex;
                }
            }
        }
        
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            try {
                schemaManager.createForeignConstraints((TableDefinition)enumtr.nextElement());
            } catch (DatabaseException ex) {
                if (!shouldIgnoreDatabaseException()) {
                    throw ex;
                }
            }
        }

        schemaManager.createSequences();
    }

    /**
     * Drop the table constraints from the database.
     */
    public void dropConstraints(org.eclipse.persistence.sessions.DatabaseSession session) {
        //CR2612669
        dropConstraints(session, new SchemaManager(session));
    }

    /**
     * Drop the table constraints from the database.
     */
    public void dropConstraints(org.eclipse.persistence.sessions.DatabaseSession session, SchemaManager schemaManager) {
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            schemaManager.buildFieldTypes((TableDefinition)enumtr.nextElement());
        }

        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            try {
                schemaManager.dropConstraints((TableDefinition)enumtr.nextElement());
            } catch (org.eclipse.persistence.exceptions.DatabaseException dbE) {
                //ignore
            }
        }
    }

    /**
     * Drop the tables from the database.
     */
    public void dropTables(org.eclipse.persistence.sessions.DatabaseSession session) {
        //CR2612669
        dropTables(session, new SchemaManager(session));
    }

    /**
     * Drop the tables from the database.
     */
    public void dropTables(org.eclipse.persistence.sessions.DatabaseSession session, SchemaManager schemaManager) {
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            schemaManager.buildFieldTypes((TableDefinition)enumtr.nextElement());
        }

        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            try {
                schemaManager.dropConstraints((TableDefinition)enumtr.nextElement());
            } catch (org.eclipse.persistence.exceptions.DatabaseException dbE) {
                //ignore
            }
        }

        String sequenceTableName = getSequenceTableName(session);
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            // Must not create sequence table as done in createSequences.
            TableDefinition table = (TableDefinition)enumtr.nextElement();
            if (!table.getName().equals(sequenceTableName)) {
                try {
                    schemaManager.dropObject(table);
                } catch (DatabaseException ex) {
                    if (!shouldIgnoreDatabaseException()) {
                        throw ex;
                    }
                }
            }
        }
    }

    /**
     * Return the name.
     */
    public String getName() {
        return name;
    }

    /**
     * Return the tables.
     */
    public Vector getTableDefinitions() {
        return tableDefinitions;
    }

    /**
     * Recreate the tables on the database.
     * This will drop the tables if they exist and recreate them.
     */
    public void replaceTables(org.eclipse.persistence.sessions.DatabaseSession session) {
        replaceTables(session, new SchemaManager(session));
    }

    /**
     * Recreate the tables on the database.
     * This will drop the tables if they exist and recreate them.
     */
    public void replaceTables(org.eclipse.persistence.sessions.DatabaseSession session, SchemaManager schemaManager) {
        replaceTablesAndConstraints(schemaManager, session);

        schemaManager.createSequences();

    }
    
    /**
     * Recreate the tables on the database.
     * This will drop the tables if they exist and recreate them.
     */
    public void replaceTables(org.eclipse.persistence.sessions.DatabaseSession session, 
            SchemaManager schemaManager, boolean keepSequenceTable) {
        replaceTablesAndConstraints(schemaManager, session);

        schemaManager.createOrReplaceSequences(keepSequenceTable, false);
    }    
    

    private void replaceTablesAndConstraints(final SchemaManager schemaManager, 
            final org.eclipse.persistence.sessions.DatabaseSession session) {
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            schemaManager.buildFieldTypes((TableDefinition)enumtr.nextElement());
        }
        
        // CR 3870467, do not log stack
        boolean shouldLogExceptionStackTrace = session.getSessionLog().shouldLogExceptionStackTrace();
        if (shouldLogExceptionStackTrace) {
            session.getSessionLog().setShouldLogExceptionStackTrace(false);
        }
        try {
            for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
                try {
                    schemaManager.dropConstraints((TableDefinition)enumtr.nextElement());
                } catch (org.eclipse.persistence.exceptions.DatabaseException dbE) {
                    //ignore
                }
            }
        } finally {
            if (shouldLogExceptionStackTrace) {
                session.getSessionLog().setShouldLogExceptionStackTrace(true);
            }
        }

        String sequenceTableName = getSequenceTableName(session);
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            // Must not create sequence table as done in createSequences.
            TableDefinition table = (TableDefinition)enumtr.nextElement();
            if (!table.getName().equals(sequenceTableName)) {
                try {
                    schemaManager.replaceObject(table, useFastTableCreator);
                } catch (DatabaseException ex) {
                    if (!shouldIgnoreDatabaseException()) {
                        throw ex;
                    }
                }
            }
        }
        if (SchemaManager.shouldToggleTableCreatorAfterInitialCreate() == Boolean.TRUE){
            useFastTableCreator = true;
        }

        // Unique constraints should be generated before foreign key constraints,
        // because foreign key constraints can reference unique constraints
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            try {
                schemaManager.createUniqueConstraints((TableDefinition)enumtr.nextElement());
            } catch (DatabaseException ex) {
                if (!shouldIgnoreDatabaseException()) {
                    throw ex;
                }
            }
        }
        
        for (Enumeration enumtr = getTableDefinitions().elements(); enumtr.hasMoreElements();) {
            try {
                schemaManager.createForeignConstraints((TableDefinition)enumtr.nextElement());
            } catch (DatabaseException ex) {
                if (!shouldIgnoreDatabaseException()) {
                    throw ex;
                }
            }
        }
    }

    /**
     * Set the name.
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Set the tables.
     */
    public void setTableDefinitions(Vector tableDefinitions) {
        this.tableDefinitions = tableDefinitions;
    }

    /**
     * Return true if DatabaseException is to be ignored.
     */
    boolean shouldIgnoreDatabaseException() {
        return ignoreDatabaseException;
    }

    /**
     * Set flag whether DatabaseException should be ignored. 
     */
    void setIgnoreDatabaseException(boolean ignoreDatabaseException) {
        this.ignoreDatabaseException = ignoreDatabaseException;
    }

    protected String getSequenceTableName(org.eclipse.persistence.sessions.Session session) {
        String sequenceTableName = null;
        if (session.getProject().usesSequencing()) {
            org.eclipse.persistence.sequencing.Sequence sequence = session.getLogin().getDefaultSequence();
            if (sequence instanceof org.eclipse.persistence.sequencing.TableSequence) {
                sequenceTableName = ((org.eclipse.persistence.sequencing.TableSequence)sequence).getTableName();
            }
        }
        return sequenceTableName;
    }
}
/*******************************************************************************
 * Copyright (c) 1998, 2010 Oracle. 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:
 *     Oracle - initial API and implementation from Oracle TopLink
 *     Dies Koper - add support for creating indices on tables
 ******************************************************************************/  
package org.eclipse.persistence.tools.schemaframework;

import java.io.Writer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.text.StyledEditorKit.BoldAction;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.EclipseLinkException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.sequencing.Sequencing;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.DatabaseSessionImpl;
import org.eclipse.persistence.sequencing.DefaultSequence;
import org.eclipse.persistence.sequencing.NativeSequence;
import org.eclipse.persistence.sequencing.Sequence;
import org.eclipse.persistence.sequencing.TableSequence;
import org.eclipse.persistence.sequencing.UnaryTableSequence;

/**
 * <p>
 * <b>Purpose</b>: Define all user level protocol for development time database manipulation.
 * <p>
 * <b>Responsibilities</b>:
 * <ul>
 * <li> Define protocol for schema creation.
 * <li> Define any useful testing specific protocol.
 * </ul>
 */
public class SchemaManager {
    protected DatabaseSessionImpl session;
    protected Writer createSchemaWriter;
    protected Writer dropSchemaWriter;
    protected boolean createSQLFiles = true; //if true, schema writer will add terminator string.
    protected TableCreator defaultTableCreator;

    /** Allow table creator to occur "fast" by just deleting all the rows. */
    public static boolean FAST_TABLE_CREATOR = false;
    
    private static Boolean TOGGLE_TABLE_CREATOR_AFTER_INITIAL_CREATE = null;
    
    public static boolean shouldToggleTableCreatorAfterInitialCreate(){
        if (TOGGLE_TABLE_CREATOR_AFTER_INITIAL_CREATE == null){
            if (System.getProperty("eclipselink.toggle-table-creator") != null){
                TOGGLE_TABLE_CREATOR_AFTER_INITIAL_CREATE = Boolean.TRUE;
            } else {
                TOGGLE_TABLE_CREATOR_AFTER_INITIAL_CREATE = Boolean.FALSE;
            }
        }
        return TOGGLE_TABLE_CREATOR_AFTER_INITIAL_CREATE;
    }
    
    public SchemaManager(DatabaseSessionImpl session) {
        this.session = session;
    }

    public SchemaManager(org.eclipse.persistence.sessions.DatabaseSession session) {
        this.session = ((DatabaseSessionImpl)session);
    }

    protected Writer getDropSchemaWriter() {
        if (null == dropSchemaWriter) {
            return createSchemaWriter;
        } else {
            return dropSchemaWriter;
        }
    }

    /**
     * PUBLIC: If the schema manager is writing to a writer, append this string
     * to that writer.
     */
    public void appendToDDLWriter(String stringToWrite) {
        // If this method is called, we know that it is the old case and
        // it would not matter which schemaWriter we use as both the 
        // create and drop schemaWriters are essentially the same. 
        // So just pick one.
        appendToDDLWriter(createSchemaWriter, stringToWrite);
    }

    public void appendToDDLWriter(Writer schemaWriter, String stringToWrite) {
        if (schemaWriter == null) {
            return; //do nothing.  Ignore append request
        }

        try {
            schemaWriter.write(stringToWrite);
            schemaWriter.flush();
        } catch (java.io.IOException ioException) {
            throw ValidationException.fileError(ioException);
        }
    }

    /**
     * INTERNAL:
     * builds the field names based on the type read in from the builder
     */
    public void buildFieldTypes(TableDefinition tableDef) {
        tableDef.buildFieldTypes(getSession());
    }

    /**
     * PUBLIC:
     * Close the schema writer.
     */
    public void closeDDLWriter() {
        closeDDLWriter(createSchemaWriter);
        closeDDLWriter(dropSchemaWriter);
        createSchemaWriter = null;
        dropSchemaWriter = null;
    }

    public void closeDDLWriter(Writer schemaWriter) {
        if (schemaWriter == null) {
            return;
        }

        try {
            schemaWriter.flush();
            schemaWriter.close();
        } catch (java.io.IOException ioException) {
            throw ValidationException.fileError(ioException);
        }
    }

    /**
     * Use the table definition to add the constraints to the database, this is normally done
     * in two steps to avoid dependencies.
     */
    public void createConstraints(TableDefinition tableDefinition) throws EclipseLinkException {
        boolean usesBatchWriting = false;

        if (getSession().getPlatform().usesBatchWriting()) {
            usesBatchWriting = true;
            getSession().getPlatform().setUsesBatchWriting(false);
        }

        try {
            if (shouldWriteToDatabase()) {
                tableDefinition.createConstraintsOnDatabase(getSession());
            } else {
            tableDefinition.setCreateSQLFiles(createSQLFiles);
            tableDefinition.createConstraints(getSession(), createSchemaWriter);
            }
        } finally {
            if (usesBatchWriting) {
                getSession().getPlatform().setUsesBatchWriting(true);
            }
        }
    }
    
    void createUniqueConstraints(TableDefinition tableDefinition) throws EclipseLinkException {
        if (shouldWriteToDatabase()) {
            tableDefinition.createUniqueConstraintsOnDatabase(getSession());
        } else {
            tableDefinition.setCreateSQLFiles(createSQLFiles);
            tableDefinition.createUniqueConstraints(getSession(), createSchemaWriter);
        }
    }
    
    void createForeignConstraints(TableDefinition tableDefinition) throws EclipseLinkException {
        if (shouldWriteToDatabase()) {
            tableDefinition.createForeignConstraintsOnDatabase(getSession());
        } else {
            tableDefinition.setCreateSQLFiles(createSQLFiles);
            tableDefinition.createForeignConstraints(getSession(), createSchemaWriter);
        }
    }

    /**
     * Use the definition object to create the schema entity on the database.
     * This is used for creating tables, views, procedures ... etc ...
     */
    public void createObject(DatabaseObjectDefinition databaseObjectDefinition) throws EclipseLinkException {
        boolean usesBatchWriting = false;

        if (getSession().getPlatform().usesBatchWriting()) {
            usesBatchWriting = true;
            getSession().getPlatform().setUsesBatchWriting(false);
        }

        try {
            if (shouldWriteToDatabase()) {
                databaseObjectDefinition.createOnDatabase(getSession());
            } else {
                databaseObjectDefinition.createObject(getSession(), createSchemaWriter);
                if (createSQLFiles){
                    this.appendToDDLWriter(createSchemaWriter, getSession().getPlatform().getStoredProcedureTerminationToken());
                }
                this.appendToDDLWriter(createSchemaWriter, "\n");
                databaseObjectDefinition.postCreateObject(getSession(), createSchemaWriter, createSQLFiles);
            }
        } finally {
            if (usesBatchWriting) {
                getSession().getPlatform().setUsesBatchWriting(true);
            }
        }
    }

    /**
     * Create all the receiver's sequences on the database for all of the loaded descriptors.
     */
    public void createSequences() throws EclipseLinkException {
        createOrReplaceSequences(true);
    }

    public void setCreateSQLFiles(boolean genFlag) {
        this.createSQLFiles = genFlag;
    }

    /**
     * Drop and recreate all the receiver's sequences on the database for all of the loaded descriptors.
     */
    public void replaceSequences() throws EclipseLinkException {
        createOrReplaceSequences(false);
    }

    /**
     * Common implementor for createSequence and replaceSequence
     * @param create - true to create the sequences, false to replace them (dropped then create)
     */
    protected void createOrReplaceSequences(boolean create) throws EclipseLinkException {              
        createOrReplaceSequences(create, create);
    }
    
    /**
     * Common implementor for createSequence and replaceSequence, distinguishes between sequence tables and sequence objects
     * @param createSequenceTables - true to create the sequences tables, false to replace them (dropped then create)
     * @param createSequenceObjects - true to create the sequences objects, false to replace them (dropped then create)
     */
    protected void createOrReplaceSequences(boolean createSequenceTables, boolean createSequenceObjects) throws EclipseLinkException {
        // PERF: Allow a special "fast" flag to be set on the session causes a delete from the table instead of a replace.
        boolean fast = FAST_TABLE_CREATOR;
        if (fast) {
            // Assume sequences already created.
            return;
        }
        Sequencing sequencing = getSession().getSequencing();

        if ((sequencing == null) || (sequencing.whenShouldAcquireValueForAll() == Sequencing.AFTER_INSERT)) {
            // Not required on Sybase native etc.
            return;
        }

        // Prepare table and sequence definitions
        // table name mapped to TableDefinition
        HashMap tableDefinitions = new HashMap();

        // sequence name to SequenceDefinition
        HashSet sequenceDefinitions = new HashSet();

        // remember the processed - to handle each sequence just once.
        HashSet processedSequenceNames = new HashSet();

        buildTableAndSequenceDefinitions(sequenceDefinitions, processedSequenceNames, tableDefinitions);
        processTableDefinitions(tableDefinitions, createSequenceTables);
        processSequenceDefinitions(sequenceDefinitions, createSequenceObjects);
    }    

    private void buildTableAndSequenceDefinitions(final HashSet sequenceDefinitions, 
            final HashSet processedSequenceNames, final HashMap tableDefinitions) {
        Iterator descriptors = getSession().getDescriptors().values().iterator();

        while (descriptors.hasNext()) {
            ClassDescriptor descriptor = (ClassDescriptor) descriptors.next();

            if (descriptor.usesSequenceNumbers()) {
                String seqName = descriptor.getSequenceNumberName();

                if (seqName == null) {
                    seqName = getSession().getDatasourcePlatform().getDefaultSequence().getName();
                }

                if (processedSequenceNames.contains(seqName)) {
                    continue;
                }

                processedSequenceNames.add(seqName);

                Sequence sequence = getSession().getDatasourcePlatform().getSequence(seqName);

                SequenceDefinition sequenceDefinition = buildSequenceDefinition(sequence);

                if (sequenceDefinition == null) {
                    continue;
                }

                sequenceDefinitions.add(sequenceDefinition);

                TableDefinition tableDefinition = sequenceDefinition.buildTableDefinition();

                if (tableDefinition != null) {
                    String tableName = tableDefinition.getName();
                    TableDefinition otherTableDefinition = (TableDefinition) tableDefinitions.get(tableName);

                    if (otherTableDefinition != null) {
                        // check for a conflict; if there is one - throw a ValidationException
                    } else {
                        tableDefinitions.put(tableName, tableDefinition);
                    }
                }
            }
        }
    }

    /**
     * Method creates database sequence tables.  If create is true, it will attempt to create the sequence tables and silently 
     * ignore exceptions.  If create is false, it will drop the tables ignoring any exceptions, then create it.  
     * @param tableDefinitions - HashMap of Sequence table definitions
     * @param create - true if tables should be created, false if they should be replaced (dropped then created)
     * @throws EclipseLinkException
     */
    private void processTableDefinitions(final HashMap tableDefinitions, final boolean create) throws EclipseLinkException {

        // create tables
        Iterator itTableDefinitions = tableDefinitions.values().iterator();
        
        // CR 3870467, do not log stack
        boolean shouldLogExceptionStackTrace = session.getSessionLog().shouldLogExceptionStackTrace();

        while (itTableDefinitions.hasNext()) {
            TableDefinition tableDefinition = (TableDefinition) itTableDefinitions.next();

            processDatabaseObjectDefinition(tableDefinition, create, shouldLogExceptionStackTrace);
        }
    }

    /**
     * Method creates database sequence objects.  If create is true, it will attempt to create the sequence and silently ignore
     * exceptions.  If create is false, it will drop the sequence ignoring any exceptions, then create it.  
     * @param sequenceDefinitions - HashSet of Sequence object definitions
     * @param create - true if sequenceDefinitions should be created, false if they should be replaced (dropped then created)
     * @throws EclipseLinkException
     */
    private void processSequenceDefinitions(final HashSet sequenceDefinitions, final boolean create) throws EclipseLinkException {
        
        // CR 3870467, do not log stack
        boolean shouldLogExceptionStackTrace = session.getSessionLog().shouldLogExceptionStackTrace();
        // create sequence objects
        Iterator itSequenceDefinitions = sequenceDefinitions.iterator();
        
        while (itSequenceDefinitions.hasNext()) {
            SequenceDefinition sequenceDefinition = (SequenceDefinition) itSequenceDefinitions.next();
            
            processDatabaseObjectDefinition(sequenceDefinition, create, shouldLogExceptionStackTrace);
        }
    }

    /**
     * Method creates database tables/objects.  If create is true, it will attempt to create the object and silently ignore
     * exceptions.  If create is false, it will drop the object ignoring any exceptions, then create it.  
     * @param definition -the object definition
     * @param create - true if the definition should be created, false if it should be replaced (dropped then created)
     * @throws EclipseLinkException
     */
    private void processDatabaseObjectDefinition(DatabaseObjectDefinition definition, final boolean create, final boolean shouldLogExceptionStackTrace) throws EclipseLinkException {
        if (shouldLogExceptionStackTrace) {
            session.getSessionLog().setShouldLogExceptionStackTrace(false);
        }

        if (create) {
            try {
                createObject(definition);
            } catch (DatabaseException exception) {
                // Ignore already created
            } finally {
                if (shouldLogExceptionStackTrace) {
                    session.getSessionLog().setShouldLogExceptionStackTrace(true);
                }
            }
        } else {
            try {
                dropObject(definition);
            } catch (DatabaseException exception) {
                // Ignore table not found for first creation
            } finally {
                if (shouldLogExceptionStackTrace) {
                    session.getSessionLog().setShouldLogExceptionStackTrace(true);
                }
            }

            createObject(definition);
        }
    }

    protected SequenceDefinition buildSequenceDefinition(Sequence sequence) {
        if (sequence.shouldAcquireValueAfterInsert()) {
            return null;
        }
        if (sequence instanceof TableSequence ||
            (sequence instanceof DefaultSequence && ((DefaultSequence)sequence).getDefaultSequence() instanceof TableSequence)) {
            return new TableSequenceDefinition(sequence);
        } else if (sequence instanceof UnaryTableSequence ||
                   (sequence instanceof DefaultSequence && ((DefaultSequence)sequence).getDefaultSequence() instanceof UnaryTableSequence)) {
            return new UnaryTableSequenceDefinition(sequence);
        } else if (sequence instanceof NativeSequence || 
                   (sequence instanceof DefaultSequence && ((DefaultSequence)sequence).getDefaultSequence() instanceof NativeSequence)) {
            return new SequenceObjectDefinition(sequence);
        } else {
            return null;
        }
    }

    /**
     * Use the table definition to drop the constraints from the table, this is normally done
     * in two steps to avoid dependencies.
     */
    public void dropConstraints(TableDefinition tableDefinition) throws EclipseLinkException {
        boolean usesBatchWriting = false;

        if (getSession().getPlatform().usesBatchWriting()) {
            usesBatchWriting = true;
            getSession().getPlatform().setUsesBatchWriting(false);
        }

        try {
            if (shouldWriteToDatabase()) {
                tableDefinition.dropConstraintsOnDatabase(getSession());
            } else {
                tableDefinition.setCreateSQLFiles(createSQLFiles);
                tableDefinition.dropConstraints(getSession(), getDropSchemaWriter());
            }
        } finally {
            if (usesBatchWriting) {
                getSession().getPlatform().setUsesBatchWriting(true);
            }
        }
    }

    /**
     * Use the definition object to drop the schema entity from the database.
     * This is used for dropping tables, views, procedures ... etc ...
     */
    public void dropObject(DatabaseObjectDefinition databaseObjectDefinition) throws EclipseLinkException {
        boolean usesBatchWriting = false;

        if (getSession().getPlatform().usesBatchWriting()) {
            usesBatchWriting = true;
            getSession().getPlatform().setUsesBatchWriting(false);
        }

        try {
            if (shouldWriteToDatabase()) {
                // drop actual object
                databaseObjectDefinition.dropFromDatabase(getSession());
            } else {
                Writer dropSchemaWriter = getDropSchemaWriter();
                // drop actual object
                databaseObjectDefinition.dropObject(getSession(), dropSchemaWriter, createSQLFiles);
                if (createSQLFiles){
                    this.appendToDDLWriter(dropSchemaWriter, getSession().getPlatform().getStoredProcedureTerminationToken());
                }
                this.appendToDDLWriter(dropSchemaWriter, "\n");
            }
        } finally {
            if (usesBatchWriting) {
                getSession().getPlatform().setUsesBatchWriting(true);
            }
        }
    }

    /**
     * Drop (delete) the table named tableName from the database.
     */
    public void dropTable(String tableName) throws EclipseLinkException {
        TableDefinition tableDefinition;

        tableDefinition = new TableDefinition();
        tableDefinition.setName(tableName);
        dropObject(tableDefinition);
    }

    /**
     * INTERNAL:
     * Close the schema writer when the schema manger is garbage collected
     */
    public void finalize() {
        try {
            this.closeDDLWriter();
        } catch (ValidationException exception) {
            // do nothing
        }
    }

    /**
     * PUBLIC:
     * Use this method to generate stored procedures based on the dynamic SQL generated
     * for your mappings and descriptors.  This should be used with caution as it maintenance
     * will be high.  Stored procedures may be generated either directly on the database
     * or to a file.
     */
    public void generateStoredProcedures() throws EclipseLinkException {
        new StoredProcedureGenerator(this).generateStoredProcedures();
    }

    /**
     * PUBLIC:
     * Use this method to generate stored procedures based on the dynamic SQL generated
     * for your mappings and descriptors.  This should be used with caution as it maintenance
     * will be high.  Stored procedures may be generated either directly on the database
     * or to a file.
     */
    public void generateStoredProcedures(Writer writer) throws EclipseLinkException {
        new StoredProcedureGenerator(this).generateStoredProcedures(writer);
    }

    /**
     * PUBLIC:
     * Use this method to generate stored procedures based on the dynamic SQL generated
     * for your mappings and descriptors.  This should be used with caution as it maintenance
     * will be high.  Stored procedures may be generated either directly on the database
     * or to a file.
     */
    public void generateStoredProceduresAndAmendmentClass(Writer writer, String fullyQualifiedClassName) throws EclipseLinkException {
        String className = fullyQualifiedClassName.substring(fullyQualifiedClassName.lastIndexOf('.') + 1);
        String packageName = fullyQualifiedClassName.substring(0, fullyQualifiedClassName.lastIndexOf('.'));
        StoredProcedureGenerator storedProcedureGenerator = new StoredProcedureGenerator(this);
        storedProcedureGenerator.generateStoredProcedures();

        storedProcedureGenerator.generateAmendmentClass(writer, packageName, className);
    }

    /**
     * PUBLIC:
     * Use this method to generate stored procedures based on the dynamic SQL generated
     * for your mappings and descriptors.  This should be used with caution as it maintenance
     * will be high.  Stored procedures may be generated either directly on the database
     * or to a file.
     */
    public void generateStoredProceduresAndAmendmentClass(String path, String fullyQualifiedClassName) throws EclipseLinkException {
    	java.io.FileWriter fileWriter = null;
    	try {
            StoredProcedureGenerator storedProcedureGenerator = new StoredProcedureGenerator(this);

            if (!(path.endsWith("\\") || path.endsWith("/"))) {
                path = path + "\\";
            }

            String className = fullyQualifiedClassName.substring(fullyQualifiedClassName.lastIndexOf('.') + 1);
            String packageName = fullyQualifiedClassName.substring(0, fullyQualifiedClassName.lastIndexOf('.'));
            String fileName = path + className + ".java";
            fileWriter = new java.io.FileWriter(fileName);
            storedProcedureGenerator.generateStoredProcedures();
            storedProcedureGenerator.generateAmendmentClass(fileWriter, packageName, className);
            fileWriter.close();
        } catch (java.io.IOException ioException) {
            throw ValidationException.fileError(ioException);
        } finally {
        	Helper.close(fileWriter);
        }
    }

    /**
     * Return the appropriate accessor.
     * Assume we are dealing with a JDBC accessor.
     */
    protected DatabaseAccessor getAccessor() {
        return (DatabaseAccessor) getSession().getAccessor();
    }

    /**
     * Get a description of table columns available in a catalog.
     *
     * <P>Each column description has the following columns:
     *  <OL>
     *    <LI><B>TABLE_CAT</B> String => table catalog (may be null)
     *    <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
     *    <LI><B>TABLE_NAME</B> String => table name
     *    <LI><B>COLUMN_NAME</B> String => column name
     *    <LI><B>DATA_TYPE</B> short => SQL type from java.sql.Types
     *    <LI><B>TYPE_NAME</B> String => Data source dependent type name
     *    <LI><B>COLUMN_SIZE</B> int => column size.  For char or date
     *        types this is the maximum number of characters, for numeric or
     *        decimal types this is precision.
     *    <LI><B>BUFFER_LENGTH</B> is not used.
     *    <LI><B>DECIMAL_DIGITS</B> int => the number of fractional digits
     *    <LI><B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2)
     *    <LI><B>NULLABLE</B> int => is NULL allowed?
     *      <UL>
     *      <LI> columnNoNulls - might not allow NULL values
     *      <LI> columnNullable - definitely allows NULL values
     *      <LI> columnNullableUnknown - nullability unknown
     *      </UL>
     *    <LI><B>REMARKS</B> String => comment describing column (may be null)
     *     <LI><B>COLUMN_DEF</B> String => default value (may be null)
     *    <LI><B>SQL_DATA_TYPE</B> int => unused
     *    <LI><B>SQL_DATETIME_SUB</B> int => unused
     *    <LI><B>CHAR_OCTET_LENGTH</B> int => for char types the
     *       maximum number of bytes in the column
     *    <LI><B>ORDINAL_POSITION</B> int    => index of column in table
     *      (starting at 1)
     *    <LI><B>IS_NULLABLE</B> String => "NO" means column definitely
     *      does not allow NULL values; "YES" means the column might
     *      allow NULL values.  An empty string means nobody knows.
     *  </OL>
     *
     * @param tableName a table name pattern
     * @return a Vector of Records.
     */
    public Vector getAllColumnNames(String tableName) throws DatabaseException {
        return getAccessor().getColumnInfo(null, null, tableName, null, getSession());
    }

    /**
     * Get a description of table columns available in a catalog.
     *
     * <P>Each column description has the following columns:
     *  <OL>
     *    <LI><B>TABLE_CAT</B> String => table catalog (may be null)
     *    <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
     *    <LI><B>TABLE_NAME</B> String => table name
     *    <LI><B>COLUMN_NAME</B> String => column name
     *    <LI><B>DATA_TYPE</B> short => SQL type from java.sql.Types
     *    <LI><B>TYPE_NAME</B> String => Data source dependent type name
     *    <LI><B>COLUMN_SIZE</B> int => column size.  For char or date
     *        types this is the maximum number of characters, for numeric or
     *        decimal types this is precision.
     *    <LI><B>BUFFER_LENGTH</B> is not used.
     *    <LI><B>DECIMAL_DIGITS</B> int => the number of fractional digits
     *    <LI><B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2)
     *    <LI><B>NULLABLE</B> int => is NULL allowed?
     *      <UL>
     *      <LI> columnNoNulls - might not allow NULL values
     *      <LI> columnNullable - definitely allows NULL values
     *      <LI> columnNullableUnknown - nullability unknown
     *      </UL>
     *    <LI><B>REMARKS</B> String => comment describing column (may be null)
     *     <LI><B>COLUMN_DEF</B> String => default value (may be null)
     *    <LI><B>SQL_DATA_TYPE</B> int => unused
     *    <LI><B>SQL_DATETIME_SUB</B> int => unused
     *    <LI><B>CHAR_OCTET_LENGTH</B> int => for char types the
     *       maximum number of bytes in the column
     *    <LI><B>ORDINAL_POSITION</B> int    => index of column in table
     *      (starting at 1)
     *    <LI><B>IS_NULLABLE</B> String => "NO" means column definitely
     *      does not allow NULL values; "YES" means the column might
     *      allow NULL values.  An empty string means nobody knows.
     *  </OL>
     *
     * @param creatorName a schema name pattern; "" retrieves those
     * without a schema
     * @param tableName a table name pattern
     * @return a Vector of Records.
     */
    public Vector getAllColumnNames(String creatorName, String tableName) throws DatabaseException {
        return getAccessor().getColumnInfo(null, creatorName, tableName, null, getSession());
    }

    /**
     * Get a description of tables available in a catalog.
     *
     * <P>Each table description has the following columns:
     *  <OL>
     *    <LI><B>TABLE_CAT</B> String => table catalog (may be null)
     *    <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
     *    <LI><B>TABLE_NAME</B> String => table name
     *    <LI><B>TABLE_TYPE</B> String => table type.  Typical types are "TABLE",
     *            "VIEW",    "SYSTEM TABLE", "GLOBAL TEMPORARY",
     *            "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
     *    <LI><B>REMARKS</B> String => explanatory comment on the table
     *  </OL>
     *
     * <P><B>Note:</B> Some databases may not return information for
     * all tables.
     *
     * @return a Vector of Records.
     */
    public Vector getAllTableNames() throws DatabaseException {
        return getAccessor().getTableInfo(null, null, null, null, getSession());
    }

    /**
     * Get a description of table columns available in a catalog.
     *
     * <P>Each column description has the following columns:
     *  <OL>
     *    <LI><B>TABLE_CAT</B> String => table catalog (may be null)
     *    <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
     *    <LI><B>TABLE_NAME</B> String => table name
     *    <LI><B>COLUMN_NAME</B> String => column name
     *    <LI><B>DATA_TYPE</B> short => SQL type from java.sql.Types
     *    <LI><B>TYPE_NAME</B> String => Data source dependent type name
     *    <LI><B>COLUMN_SIZE</B> int => column size.  For char or date
     *        types this is the maximum number of characters, for numeric or
     *        decimal types this is precision.
     *    <LI><B>BUFFER_LENGTH</B> is not used.
     *    <LI><B>DECIMAL_DIGITS</B> int => the number of fractional digits
     *    <LI><B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2)
     *    <LI><B>NULLABLE</B> int => is NULL allowed?
     *      <UL>
     *      <LI> columnNoNulls - might not allow NULL values
     *      <LI> columnNullable - definitely allows NULL values
     *      <LI> columnNullableUnknown - nullability unknown
     *      </UL>
     *    <LI><B>REMARKS</B> String => comment describing column (may be null)
     *     <LI><B>COLUMN_DEF</B> String => default value (may be null)
     *    <LI><B>SQL_DATA_TYPE</B> int => unused
     *    <LI><B>SQL_DATETIME_SUB</B> int => unused
     *    <LI><B>CHAR_OCTET_LENGTH</B> int => for char types the
     *       maximum number of bytes in the column
     *    <LI><B>ORDINAL_POSITION</B> int    => index of column in table
     *      (starting at 1)
     *    <LI><B>IS_NULLABLE</B> String => "NO" means column definitely
     *      does not allow NULL values; "YES" means the column might
     *      allow NULL values.  An empty string means nobody knows.
     *  </OL>
     *
     * @param creatorName a schema name pattern; "" retrieves those
     * without a schema
     * @return a Vector of Records.
     */
    public Vector getAllTableNames(String creatorName) throws DatabaseException {
        return getAccessor().getTableInfo(null, creatorName, null, null, getSession());
    }

    /**
     * Get a description of table columns available in a catalog.
     *
     * <P>Only column descriptions matching the catalog, schema, table
     * and column name criteria are returned.  They are ordered by
     * TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION.
     *
     * <P>Each column description has the following columns:
     *  <OL>
     *    <LI><B>TABLE_CAT</B> String => table catalog (may be null)
     *    <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
     *    <LI><B>TABLE_NAME</B> String => table name
     *    <LI><B>COLUMN_NAME</B> String => column name
     *    <LI><B>DATA_TYPE</B> short => SQL type from java.sql.Types
     *    <LI><B>TYPE_NAME</B> String => Data source dependent type name
     *    <LI><B>COLUMN_SIZE</B> int => column size.  For char or date
     *        types this is the maximum number of characters, for numeric or
     *        decimal types this is precision.
     *    <LI><B>BUFFER_LENGTH</B> is not used.
     *    <LI><B>DECIMAL_DIGITS</B> int => the number of fractional digits
     *    <LI><B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2)
     *    <LI><B>NULLABLE</B> int => is NULL allowed?
     *      <UL>
     *      <LI> columnNoNulls - might not allow NULL values
     *      <LI> columnNullable - definitely allows NULL values
     *      <LI> columnNullableUnknown - nullability unknown
     *      </UL>
     *    <LI><B>REMARKS</B> String => comment describing column (may be null)
     *     <LI><B>COLUMN_DEF</B> String => default value (may be null)
     *    <LI><B>SQL_DATA_TYPE</B> int => unused
     *    <LI><B>SQL_DATETIME_SUB</B> int => unused
     *    <LI><B>CHAR_OCTET_LENGTH</B> int => for char types the
     *       maximum number of bytes in the column
     *    <LI><B>ORDINAL_POSITION</B> int    => index of column in table
     *      (starting at 1)
     *    <LI><B>IS_NULLABLE</B> String => "NO" means column definitely
     *      does not allow NULL values; "YES" means the column might
     *      allow NULL values.  An empty string means nobody knows.
     *  </OL>
     *
     * @param catalog a catalog name; "" retrieves those without a
     * catalog; null means drop catalog name from the selection criteria
     * @param schema a schema name pattern; "" retrieves those
     * without a schema
     * @param tableName a table name pattern
     * @param columnName a column name pattern
     * @return a Vector of Records.
     */
    public Vector getColumnInfo(String catalog, String schema, String tableName, String columnName) throws DatabaseException {
        return getAccessor().getColumnInfo(catalog, schema, tableName, columnName, getSession());
    }

    public AbstractSession getSession() {
        return session;
    }

    /**
     * Get a description of tables available in a catalog.
     *
     * <P>Only table descriptions matching the catalog, schema, table
     * name and type criteria are returned.  They are ordered by
     * TABLE_TYPE, TABLE_SCHEM and TABLE_NAME.
     *
     * <P>Each table description has the following columns:
     *  <OL>
     *    <LI><B>TABLE_CAT</B> String => table catalog (may be null)
     *    <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
     *    <LI><B>TABLE_NAME</B> String => table name
     *    <LI><B>TABLE_TYPE</B> String => table type.  Typical types are "TABLE",
     *            "VIEW",    "SYSTEM TABLE", "GLOBAL TEMPORARY",
     *            "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
     *    <LI><B>REMARKS</B> String => explanatory comment on the table
     *  </OL>
     *
     * <P><B>Note:</B> Some databases may not return information for
     * all tables.
     *
     * @param catalog a catalog name; "" retrieves those without a
     * catalog; null means drop catalog name from the selection criteria
     * @param schema a schema name pattern; "" retrieves those
     * without a schema
     * @param tableName a table name pattern
     * @param types a list of table types to include; null returns all types
     * @return a Vector of Records.
     */
    public Vector getTableInfo(String catalog, String schema, String tableName, String[] types) throws DatabaseException {
        return getAccessor().getTableInfo(catalog, schema, tableName, types, getSession());
    }

    /**
     * PUBLIC:
     * Output all DDL statements directly to the database.
     */
    public void outputDDLToDatabase() {
        this.createSchemaWriter = null;
        this.dropSchemaWriter = null;
    }

    /**
     * PUBLIC:
     * Output all DDL statements to a file writer specified by the name in the parameter.
     */
    public void outputDDLToFile(String fileName) {
        try {
            this.createSchemaWriter = new java.io.FileWriter(fileName);
        } catch (java.io.IOException ioException) {
            throw ValidationException.fileError(ioException);
        }
    }

    public void outputCreateDDLToFile(String fileName) {
        try {
            this.createSchemaWriter = new java.io.FileWriter(fileName);
        } catch (java.io.IOException ioException) {
            throw ValidationException.fileError(ioException);
        }
    }

    public void outputDropDDLToFile(String fileName) {
        try {
            this.dropSchemaWriter = new java.io.FileWriter(fileName);
        } catch (java.io.IOException ioException) {
            throw ValidationException.fileError(ioException);
        }
    }

    /**
     * PUBLIC:
     * Output all DDL statements to a writer specified in the parameter.
     */
    public void outputDDLToWriter(Writer schemaWriter) {
        this.createSchemaWriter = schemaWriter;
    }

    public void outputCreateDDLToWriter(Writer createWriter) {
        this.createSchemaWriter = createWriter;
    }

    public void outputDropDDLToWriter(Writer dropWriter) {
        this.dropSchemaWriter = dropWriter;
    }

    /**
     * Use the definition object to drop and recreate the schema entity on the database.
     * This is used for dropping tables, views, procedures ... etc ...
     * This handles and ignore any database error while dropping in case the object did not previously exist.
     */
    public void replaceObject(DatabaseObjectDefinition databaseDefinition) throws EclipseLinkException {
        replaceObject(databaseDefinition, false);
    }
    
    /**
     * Use the definition object to drop and recreate the schema entity on the database.
     * This is used for dropping tables, views, procedures ... etc ...
     * This handles and ignore any database error while dropping in case the object did not previously exist.
     */
    public void replaceObject(DatabaseObjectDefinition databaseDefinition, boolean useFastCreation) throws EclipseLinkException {                
        // PERF: Allow a special "fast" flag to be set on the session causes a delete from the table instead of a replace.
        boolean fast = FAST_TABLE_CREATOR || useFastCreation;
        if (fast && (databaseDefinition instanceof TableDefinition)) {
            session.executeNonSelectingSQL("DELETE FROM " + databaseDefinition.getName()); 
        } else if (fast && (databaseDefinition instanceof StoredProcedureDefinition)) {
            // do nothing
        } else {
            // CR 3870467, do not log stack
            boolean shouldLogExceptionStackTrace = getSession().getSessionLog().shouldLogExceptionStackTrace();
    
            if (shouldLogExceptionStackTrace) {
                getSession().getSessionLog().setShouldLogExceptionStackTrace(false);
            }
            try {
                dropObject(databaseDefinition);
            } catch (DatabaseException exception) {
                // Ignore error
            } finally {
                if (shouldLogExceptionStackTrace) {
                    getSession().getSessionLog().setShouldLogExceptionStackTrace(true);
                }
            }

            createObject(databaseDefinition);
        }
    }

    /**
     * Construct the default TableCreator.
     * If the default TableCreator is already created, just returns it. 
     */
    protected TableCreator getDefaultTableCreator(boolean generateFKConstraints) {
        if(defaultTableCreator == null) {
            defaultTableCreator = new DefaultTableGenerator(session.getProject(),generateFKConstraints).generateDefaultTableCreator();
            defaultTableCreator.setIgnoreDatabaseException(true);
        }
        return defaultTableCreator;
    }
    
    /**
    * Create the default table schema for the TopLink project this session associated with.
    */
    public void createDefaultTables(boolean generateFKConstraints) {
        //Create each table w/o throwing exception and/or exit if some of them are already existed in the db. 
        //If a table is already existed, skip the creation.

        boolean shouldLogExceptionStackTrace = getSession().getSessionLog().shouldLogExceptionStackTrace();
        getSession().getSessionLog().setShouldLogExceptionStackTrace(false);

        try {
            TableCreator tableCreator = getDefaultTableCreator(generateFKConstraints);
            tableCreator.createTables(session, this);
        } catch (DatabaseException ex) {
            // Ignore error
        } finally {
            getSession().getSessionLog().setShouldLogExceptionStackTrace(shouldLogExceptionStackTrace);
        }
    }

    /**
     * Drop and recreate the default table schema for the TopLink project this session associated with.
     */
    public void replaceDefaultTables() throws EclipseLinkException {
        boolean shouldLogExceptionStackTrace = getSession().getSessionLog().shouldLogExceptionStackTrace();
        getSession().getSessionLog().setShouldLogExceptionStackTrace(false);

        try {
            TableCreator tableCreator = getDefaultTableCreator(true);
            tableCreator.replaceTables(session, this);
        } catch (DatabaseException exception) {
            // Ignore error
        } finally {
            getSession().getSessionLog().setShouldLogExceptionStackTrace(shouldLogExceptionStackTrace);
        }
    }
    
    /**
     * Drop and recreate the default table schema for the TopLink project this session associated with.
     */
    public void replaceDefaultTables(boolean keepSequenceTables, boolean generateFKConstraints) throws EclipseLinkException {
        boolean shouldLogExceptionStackTrace = getSession().getSessionLog().shouldLogExceptionStackTrace();
        getSession().getSessionLog().setShouldLogExceptionStackTrace(false);

        try {
            TableCreator tableCreator = getDefaultTableCreator(generateFKConstraints);
            tableCreator.replaceTables(session, this, keepSequenceTables);
        } catch (DatabaseException exception) {
            // Ignore error
        } finally {
            getSession().getSessionLog().setShouldLogExceptionStackTrace(shouldLogExceptionStackTrace);
        }
    }

    public void setSession(DatabaseSessionImpl session) {
        this.session = session;
    }

    /**
     * PUBLIC:
     * Return true if this SchemaManager should write to the database directly
     */
    public boolean shouldWriteToDatabase() {
        return ((this.createSchemaWriter == null) && (this.dropSchemaWriter == null));
    }

    /**
     * Use the definition to alter sequence.
     */
    public void alterSequence(SequenceDefinition sequenceDefinition) throws EclipseLinkException {
        if (!sequenceDefinition.isAlterSupported(getSession())) {
            return;
        }

        boolean usesBatchWriting = false;

        if (getSession().getPlatform().usesBatchWriting()) {
            usesBatchWriting = true;
            getSession().getPlatform().setUsesBatchWriting(false);
        }

        try {
            if (shouldWriteToDatabase()) {
                sequenceDefinition.alterOnDatabase(getSession());
            } else {
                sequenceDefinition.alter(getSession(), createSchemaWriter);
            }
        } finally {
            if (usesBatchWriting) {
                getSession().getPlatform().setUsesBatchWriting(true);
            }
        }
    }

}

Back to the top