How can I get the Primary Keys from logical Tables in DB2? - java

So I am working on AS400, DB2 System.
I wrote a method which provides me the Primary Keys of every physical table. But on some tables the primary keys are only set on the logical table. There my method does not work.
#Override
public ArrayList<Field> getPKS(String lib) {
ArrayList<Field> pkList = new ArrayList<>();
try (Connection connection = DriverManager.getConnection("jdbc:as400://" + ConnectionData.SYSTEM + ";naming=system;libraries=*" + lib + ";",
ConnectionData.USER, ConnectionData.PASSWORD);
ResultSet rs = connection.getMetaData().getPrimaryKeys(null, connection.getSchema(), "LSAVSLA")){
while (rs.next()) {
pkList.add(new Field(rs.getString("COLUMN_NAME")));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return pkList;
}
For a pysical table it's working, but for a logical Table it is not.
Do you have any idea how to get the primary keys from the logical table.

Logical files do not contain data. They contain a description of records that are found in one or more physical files. A logical file is a view or representation of one or more physical files
says the manual. Similarly to a view in a conventional RDBMS, you cannot define a primary key for a logical file.

So basically what you have are physical files (or SQL tables) defined without a primary key and logical files or (SQL indexes) defined with a unique key.
On the IBM i, a logical file can act as either an SQL index, and SQL View or both at the same time. As Mustaccio mentions, there isn't any actual data in the object.
You best bet, might be to query the SYSTABLEINDEXS catalog view looking for primary key or unique indexes over a given table.
You could also take a look at the getIndexes() method.

I found a solution by selecting the "DBKFLD" field from "QSYS.QADBKATR"
The SQL Query: SELECT DBKFLD FROM QSYS.QADBKATR WHERE DBKLIB = "Your lib" AND DBKFIL = "Your table"
The Java Code:
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet resultSetQuery = statement.executeQuery("select DBKFLD from QSYS.QADBKATR where DBKLIB = '" + lib + "' and DBKFIL = '" + tablename + "'")) {
ResultSetMetaData metadata = resultSetQuery.getMetaData();
int columnCount = metadata.getColumnCount();
while (resultSetQuery.next()) {
for (int i = 1; i <= columnCount; i++) {
String pk = resultSetQuery.getString(i);
pk = pk.replaceAll("\\s+", "");
pkList.add(new Feld(pk));
}
}
return pkList;

Related

Java SQLite: How to load an existing database in memory to select faster

I have an sqlite database that i query from a java program, no insert, no table creation, just many many SELECT bundled with UNION (500 at a time).
How can I load the database from the .db into memory to speed up the SELECT?
Class.forName("org.sqlite.JDBC");
String urlJDBC = "jdbc:sqlite:";
String url = urlJDBC + dbPath;
Connection conn = null;
try {
conn = DriverManager.getConnection(url);
} catch (SQLException e) {
System.out.println(e.getMessage());
}
for(int i=0, i<10000, i++)
{
String multipleSELECT = "SELECT a FROM table WHERE b=\"" + b1 + "\"" + "
UNION ... + " UNION SELECT a FROM table WHERE b=\"" + b500 + "\"";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(multipleSELECT);
check(rs);
}
Obviously the 500 values of b change at every iteration of i.
Is there a way to load the entire database in memory (about 2.5 GB, so less than the 8 GB ram)?
To speed up many small queries, wrap them in a single transaction, and use a single prepared statement with a parameter that changes. Then you should be able to use a simple SELECT that checks only one number.
Alternatively, write all numbers to check into a temporary table, and return all matching names at once:
SELECT a
FROM MyTable
JOIN TempTable ON MyTable.b = TempTable.b;
In either case, you can improve the lookup speed by using a covering index, i.e., a two-column index on the number and the name, or, better, by using a clustered index.

Updating a single row database in Java

So I have a database that is made like this
{MATERIAL NAME;QUANTITY}
I also have a JTable in Java that uploads the info from the database (SQLite). As I edit a cell in the table, it automatically updates the database in this way:
//table listener
public void tableChanged(TableModelEvent e) {
//gets row and column if the table is edited
int row = e.getFirstRow();
int column = e.getColumn();
//change in sqlite
if (column == 1) {
int value = Integer.parseInt(table.getModel().getValueAt(row, column).toString());
String materialId = table.getModel().getValueAt(row, column-1).toString();
try (Connection c = DriverManager.getConnection("jdbc:sqlite:database.db"); Statement statement = c.createStatement()) {
String sql = "UPDATE MATERIALS set QUANTITY = " + value + " where MATERIAL='" + materialId +"';";
statement.executeUpdate(sql);
statement.close();
c.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
else if (column == 0) {
int value = Integer.parseInt(table.getModel().getValueAt(row, column+1).toString());
String materialId = table.getModel().getValueAt(row, column).toString();
try (Connection c = DriverManager.getConnection("jdbc:sqlite:database.db"); Statement statement = c.createStatement()) {
String sql = "UPDATE MATERIALS set MATERIAL = " + materialId + " where MATERIAL='" + materialId +"';";
statement.executeUpdate(sql);
statement.close();
c.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}
It's okay when the quantity is updated. However, once the material name is updated, I don't know how I am supposed to update it in the database.
String sql = "UPDATE MATERIALS set MATERIAL = " + materialId + " where MATERIAL='" + materialId +"';";
Maybe there is a way to save a previous value of that value? I can not use the quantity of materials as they might be repeated.
I would like to avoid adding IDs to the objects if possible.
This statement:
String sql = "UPDATE MATERIALS set MATERIAL = " + materialId + " where MATERIAL='" + materialId +"';"
Has two (three) problems.
The 'old' value and the 'new' value are the same. You have a logical problem.
WHERE part is concatenated like for string type, but SET part is not
concatenation is evil. The Correct way to do this is
String sql = "UPDATE MATERIALS set MATERIAL = ? where MATERIAL= ?";
PreparedStatement prepstmt = conn.prepareStatement(sql);
prepstmt .setString(1, newMatewrial);
prepstmt .setString(2, oldMaterial);
prepstmt .executeUpdate();
Logical problem must be resolved in your conception.
It Seems the main problem is in basically not using the ID . Every row in relational database should have stable primary key (ID like You say). Name isn't primary key, because can be changed.
WHERE part should use ID and not name.
EDIT: in my opinion good JTable model is one way to solve your problem.
Maybe automagic default table model is too poor. Class implementing Row should have one more column (ID) which can(or not) be invisible.
My English isn't too fluent, cannot teach relational database and Swing in SO post.
The second, small snippet of code doesn't show your application conception.
Read in google about custom JTable model.
Very basic example:
http://www.java2s.com/Code/Java/Swing-JFC/TablewithacustomTableModel.htm
Okay so it seems that I found the solution to the problem
JTable: Detect cell data change
However, this requires implementing another class into the project and using it. The answer for the SQL statement still stays the same.

How to insert values in a table with dynamic columns Jdbc/Mysql

I want to add values in a table that has dynamic columns.
I managed to create a table with dynamic columns but I cannot figure out how to insert data.
//Create Table
sql = "CREATE TABLE MyDB.myTable" +
"(level INTEGER(255) )";
int columnNumber = 5; //Number of columns
//Add columns
for (i=0;i<columnNumber;i++){
String columnName = "Level_" +i:
String sql = "ALTER TABLE MyDB.myTable ADD " + columnName + " INTEGER(30)";
}
//Insert Data
//How to insert data dynamically, without knowing the number of columns?
You can also use database metadata to get the column names. This has the advantage that you even don't need to know the column names, rather they are retrieved dynamically in your code.
public static List<String> getColumns(String tableName, String schemaName) throws SQLException{
ResultSet rs=null;
ResultSetMetaData rsmd=null;
PreparedStatement stmt=null;
List<String> columnNames =null;
String qualifiedName = (schemaName!=null&&!schemaName.isEmpty())?(schemaName+"."+tableName):tableName;
try{
stmt=conn.prepareStatement("select * from "+qualifiedName+" where 0=1");
rs=stmt.executeQuery();//you'll get an empty ResultSet but you'll still get the metadata
rsmd=rs.getMetaData();
columnNames = new ArrayList<String>();
for(int i=1;i<=rsmd.getColumnCount();i++)
columnNames.add(rsmd.getColumnLabel(i));
}catch(SQLException e){
throw e;//or log it
}
finally{
if(rs!=null)
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
throw e
}
if(stmt!=null)
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
throw e
}
}
return columnNames;
}
Once you have the column names, you can use it as you normally would (List.size() would of course give the number of columns).
UPDATE:
//I will assume that your values (data to be inserted) is a List of Object types and that it is already populated
List<Object> data = new ArrayList<>();
//you will populate this list
//getting the column names
List<String> columnNames = getColumns("MyTable", "MyDB");
String insertColumns = "";
String insertValues = "";
if(columnNames != null && columnNames.size() > 0){
insertColumns += columnNames.get(0);
insertValues += "?";
}
for(int i = 1; i < columnNames.size();i++){
insertColumns += ", " + columnNames.get(i) ;
insertValues += "?";
}
String insertSql = "INSERT INTO MyDB.MyTable (" + insertColumns + ") values(" + insertValues + ")";
try{
PrepareStatement ps = conn.prepareStatement(insertSql);
for(Object o : data){
ps.setObject(o); //you must pass objects of correct type
}
ps.execute(); //this inserts your data
}catch(SQLException sqle){
//do something with it
}
This code assume that you pass objects of correct types to PreparedStatement.setObject(Object o) method. It's also possible to retrieve column types using metadatabase information and then use that info to enforce type checking but that would make your code much more complicated
If you know the number of columns you want to insert, you can make your insert query same way you made your CREATE TABLE. Explicitly name the columns you want to add your data into, and make sure the columns you leave empty are allowed to be empty (NULL or default=0)
INSERT INTO MyDB.myTable (Level_1, Level_2, ...) VALUES (Val_1, Val_2, ...);
The alternative approach would be to store each inserted value in a separate row. In that way you don't vhave to change the number of columns in your table.
You need a table where you have a ID for every group of values:
- ID
- Level
- Value
You could have a separate table where you can register each ID:
- ID (bigInt, auto increment, primary key)
- info field
- timestamp
Now, for every set of data you want to insert, first insert need a Unique ID. If you use the second table, inserting a new row would give you a new ID:
INSERT INTO register_table (ID, info, timestamp) VALUES (NULL, 'some info', NOW());
This will give you a new ID (last_inserted_id).
With this ID now insert all values in the other table:
for(i=0i<columnNumber;i++){
"INSERT INTO _table (ID, Level, _Value) VALUES ("+ the_ID +", "+ i +", "+ the_VALUE +");";
}
If you want to fetch the data:
"SELECT Level, _Value FROM _table WHERE ID="+ the_ID +" ORDER BY Level;";

H2 DB: How to check whether table schema is initialized programmatically?

I have a DB schema that creates several tables and fills them with data. I want to check whether db contains corresponding tables or not during app start. I could check for db file existence, but H2 creates db if it doesn't exist. So the only way, I think, is to check for tables existence.
Here is the code of how I initialize DB:
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:database/svc", "sa", "");
Statement st = conn.createStatement();
st.execute("CREATE TABLE IF NOT EXISTS table1 (id INT PRIMARY KEY AUTO_INCREMENT NOT NULL, name VARCHAR(100), record INT, record_date DATE, UNIQUE(name))");
st.execute("CREATE TABLE IF NOT EXISTS table2 (id INT PRIMARY KEY AUTO_INCREMENT NOT NULL, name VARCHAR(100), record INT, record_date DATE, UNIQUE(name))");
st.execute("CREATE TABLE IF NOT EXISTS daily_record_stat (id INT PRIMARY KEY AUTO_INCREMENT NOT NULL, date DATE, table1_id INT, table1_record INT, table2_id INT," +
" table2_record INT, total_record INT);");
st.execute("ALTER TABLE daily_record_stat ADD FOREIGN KEY (table1_id) REFERENCES table1(id);");
st.execute("ALTER TABLE daily_record_stat ADD FOREIGN KEY (table2_id) REFERENCES table2(id);");
st.execute("INSERT INTO table1 VALUES(1, 'non_existed_stub', 0, NULL)");
st.execute("INSERT INTO table2 VALUES(1, 'non_existed_stub', 0, NULL)");
conn.close();
As you see, I check for table existence before creation using IF NOT EXISTS statement. But then I run at the problem with ALTER and INSERT - these commands don's allow IF usage.
I tried to do the following:
Connection conn = DriverManager.getConnection("jdbc:h2:database/svc", "sa", "");
ResultSet meta = conn.getMetaData().getTables(null, null, "table1", null);
if(meta.next()) {
//do something
}
But meta.next() is false.
So how to check whether table schema is initialized? Or maybe this should be done some other way?
Not sure if that's what you mean by saying to check programmatically, buy you can try to use DatabaseMetaData.getTables(). This call will return ResultSet which you'll have to check programmatically. You can see what fields are returned in this ResultSet in corresponding section here. And meta data itself can be obtained by conn.getMetaData().
Following code should return all tables which names start with 'TABLE':
ResultSet meta = conn.getMetaData().getTables(null, null, "TABLE%", new String[]{"TABLE"});
while (meta.next()) {
System.out.println(meta.getString(3));
}
Note that you have to specify table name pattern in upper case. Also it's good to pass table types that you need, although it is optional.
This is a check I used to (re)create the H2 database:
// IMPORTANT A sorted list of table names.
private static final String[] REQUIRED_TABLES = { "USER", ... };
public static final String CREATE_USER = "create table USER (...)";
private boolean schemaExists() throws SQLException {
final List<String> requiredTables = Arrays.asList(REQUIRED_TABLES);
final List<String> tableNames = new ArrayList<String>();
final Connection conn = dataSource.getConnection();
try {
final Statement st = conn.createStatement();
final ResultSet rs = st.executeQuery("show tables");
while (rs.next()) {
tableNames.add(rs.getString("TABLE_NAME"));
}
rs.close();
st.close();
}
finally {
if (conn != null) { conn.close(); }
}
Collections.sort(tableNames);
return tableNames.equals(requiredTables);
}
private void initializeDatabase() throws SQLException {
final Connection conn = dataSource.getConnection();
try {
if (schemaExists()) {
return;
}
final Statement st = conn.createStatement();
st.executeUpdate(CREATE_USER);
conn.commit();
}
finally {
if (conn != null) { conn.close(); }
}
}
And you just call:
initializeDatabase();
Notice the list of required tables has to be sorted because I use List.equals() to compare two lists. It would probably be better to also programmatically sort the required tables list too.
It's not fool-proof (not checking if any table exists and if it should be altered) but it works for me.
Take a look at the SHOW command for other uses.

Delete all tables in Derby DB

How do i delete all the tables in the schema on Apache Derby DB using JDBC?
Thanks are due to the blog:
Step 1:
Run the SQL statement, but don't forget to replace the schema name 'APP' with your your schema name in the 2 occurrences below:
SELECT
'ALTER TABLE '||S.SCHEMANAME||'.'||T.TABLENAME||' DROP CONSTRAINT '||C.CONSTRAINTNAME||';'
FROM
SYS.SYSCONSTRAINTS C,
SYS.SYSSCHEMAS S,
SYS.SYSTABLES T
WHERE
C.SCHEMAID = S.SCHEMAID
AND
C.TABLEID = T.TABLEID
AND
S.SCHEMANAME = 'APP'
UNION
SELECT 'DROP TABLE ' || schemaname ||'.' || tablename || ';'
FROM SYS.SYSTABLES
INNER JOIN SYS.SYSSCHEMAS ON SYS.SYSTABLES.SCHEMAID = SYS.SYSSCHEMAS.SCHEMAID
where schemaname='APP';
Step 2:
The result of the above execution is a set of SQL statements, copy them to the SQL editor, execute them, then the constraints and the tables are dropped.
For actual code that does this, check CleanDatabaseTestSetup.java in the Derby test suite section of the Derby distribution.
Do a little method in java in which you execute a
DROP TABLE [tablename]
tablename is passed by parameter.
And another method in which you loop over a record set formed by the query
SELECT tablename FROM SYSTABLES
calling the first method.
Derby latest documentation
I think most db providers don't allow DROP TABLE * (or similar).
I think the best way would be to SHOW TABLES and then go through each deleting in a loop via a resultset.
HTH.
JDBC allows you to solve your task in a database agnostic way:
Open the connection
Grab the DatabaseMetaData
Use it to list all tables in your database JavaDoc
Iterate over the resultset and fire the DROP TABLE for each table
you must generate schema and table name from Derby DB system catalog.
Order all tables by relation.
Generate java statement for drop all tables
Use autoCommit() method and set this method to false. for manual commit or rollback transactions when got errors.
Run you java process.
Good Luck.
A simpler solution is to use JDBC to run "drop database foo" then "create database foo". However, this will cause all objects in the DB to be deleted (i.e. not just tables).
If you're working from the command prompt rather than through JDBC, this should get you started.
SELECT 'DROP TABLE ' || schemaname ||'.' || tablename || ';'
FROM SYS.SYSTABLES
INNER JOIN SYS.SYSSCHEMAS ON SYS.SYSTABLES.SCHEMAID = SYS.SYSSCHEMAS.SCHEMAID
;
A simple solution is to do right click -> disconnect then delete the folder containing your database and reconnect it.
Download Squirrel SQL from http://squirrel-sql.sourceforge.net/
Connect to the database.
Expand the TABLE node.
Select the tables that you want to drop.
Right click and select -> Scripts -> Drop table scripts
Run the generated queries
You can even select delete records to empty the selected tables.
For those wanting to delete all schemas programmatically without having to manually copy-paste SQL each time, here's code lifted from org.apache.derbyTesting.junit.CleanDatabaseTestSetup and org.apache.derbyTesting.junit.JDBC. You just call dropAllSchemas(connection);
public static void dropAllSchemas(Connection conn) throws SQLException {
DatabaseMetaData dmd = conn.getMetaData();
SQLException sqle = null;
// Loop a number of arbitary times to catch cases
// where objects are dependent on objects in
// different schemas.
for (int count = 0; count < 5; count++) {
// Fetch all the user schemas into a list
List<String> schemas = new ArrayList<String>();
ResultSet rs = dmd.getSchemas();
while (rs.next()) {
String schema = rs.getString("TABLE_SCHEM");
if (schema.startsWith("SYS"))
continue;
if (schema.equals("SQLJ"))
continue;
if (schema.equals("NULLID"))
continue;
schemas.add(schema);
}
rs.close();
// DROP all the user schemas.
sqle = null;
for (String schema : schemas) {
try {
dropSchema(dmd, schema);
} catch (SQLException e) {
sqle = e;
}
}
// No errors means all the schemas we wanted to
// drop were dropped, so nothing more to do.
if (sqle == null)
return;
}
throw sqle;
}
/**
* Constant to pass to DatabaseMetaData.getTables() to fetch
* just tables.
*/
public static final String[] GET_TABLES_TABLE = new String[] {"TABLE"};
/**
* Constant to pass to DatabaseMetaData.getTables() to fetch
* just views.
*/
public static final String[] GET_TABLES_VIEW = new String[] {"VIEW"};
/**
* Constant to pass to DatabaseMetaData.getTables() to fetch
* just synonyms.
*/
public static final String[] GET_TABLES_SYNONYM =
new String[] {"SYNONYM"};
/**
* Drop a database schema by dropping all objects in it
* and then executing DROP SCHEMA. If the schema is
* APP it is cleaned but DROP SCHEMA is not executed.
*
* TODO: Handle dependencies by looping in some intelligent
* way until everything can be dropped.
*
*
* #param dmd DatabaseMetaData object for database
* #param schema Name of the schema
* #throws SQLException database error
*/
public static void dropSchema(DatabaseMetaData dmd, String schema) throws SQLException{
Connection conn = dmd.getConnection();
Statement s = dmd.getConnection().createStatement();
// Triggers
PreparedStatement pstr = conn.prepareStatement(
"SELECT TRIGGERNAME FROM SYS.SYSSCHEMAS S, SYS.SYSTRIGGERS T "
+ "WHERE S.SCHEMAID = T.SCHEMAID AND SCHEMANAME = ?");
pstr.setString(1, schema);
ResultSet trrs = pstr.executeQuery();
while (trrs.next()) {
String trigger = trrs.getString(1);
s.execute("DROP TRIGGER " + escape(schema, trigger));
}
trrs.close();
pstr.close();
// Functions - not supported by JDBC meta data until JDBC 4
// Need to use the CHAR() function on A.ALIASTYPE
// so that the compare will work in any schema.
PreparedStatement psf = conn.prepareStatement(
"SELECT ALIAS FROM SYS.SYSALIASES A, SYS.SYSSCHEMAS S" +
" WHERE A.SCHEMAID = S.SCHEMAID " +
" AND CHAR(A.ALIASTYPE) = ? " +
" AND S.SCHEMANAME = ?");
psf.setString(1, "F" );
psf.setString(2, schema);
ResultSet rs = psf.executeQuery();
dropUsingDMD(s, rs, schema, "ALIAS", "FUNCTION");
// Procedures
rs = dmd.getProcedures((String) null,
schema, (String) null);
dropUsingDMD(s, rs, schema, "PROCEDURE_NAME", "PROCEDURE");
// Views
rs = dmd.getTables((String) null, schema, (String) null,
GET_TABLES_VIEW);
dropUsingDMD(s, rs, schema, "TABLE_NAME", "VIEW");
// Tables
rs = dmd.getTables((String) null, schema, (String) null,
GET_TABLES_TABLE);
dropUsingDMD(s, rs, schema, "TABLE_NAME", "TABLE");
// At this point there may be tables left due to
// foreign key constraints leading to a dependency loop.
// Drop any constraints that remain and then drop the tables.
// If there are no tables then this should be a quick no-op.
ResultSet table_rs = dmd.getTables((String) null, schema, (String) null,
GET_TABLES_TABLE);
while (table_rs.next()) {
String tablename = table_rs.getString("TABLE_NAME");
rs = dmd.getExportedKeys((String) null, schema, tablename);
while (rs.next()) {
short keyPosition = rs.getShort("KEY_SEQ");
if (keyPosition != 1)
continue;
String fkName = rs.getString("FK_NAME");
// No name, probably can't happen but couldn't drop it anyway.
if (fkName == null)
continue;
String fkSchema = rs.getString("FKTABLE_SCHEM");
String fkTable = rs.getString("FKTABLE_NAME");
String ddl = "ALTER TABLE " +
escape(fkSchema, fkTable) +
" DROP FOREIGN KEY " +
escape(fkName);
s.executeUpdate(ddl);
}
rs.close();
}
table_rs.close();
conn.commit();
// Tables (again)
rs = dmd.getTables((String) null, schema, (String) null,
GET_TABLES_TABLE);
dropUsingDMD(s, rs, schema, "TABLE_NAME", "TABLE");
// drop UDTs
psf.setString(1, "A" );
psf.setString(2, schema);
rs = psf.executeQuery();
dropUsingDMD(s, rs, schema, "ALIAS", "TYPE");
// drop aggregates
psf.setString(1, "G" );
psf.setString(2, schema);
rs = psf.executeQuery();
dropUsingDMD(s, rs, schema, "ALIAS", "DERBY AGGREGATE");
psf.close();
// Synonyms - need work around for DERBY-1790 where
// passing a table type of SYNONYM fails.
rs = dmd.getTables((String) null, schema, (String) null,
GET_TABLES_SYNONYM);
dropUsingDMD(s, rs, schema, "TABLE_NAME", "SYNONYM");
// sequences
if ( sysSequencesExists( conn ) )
{
psf = conn.prepareStatement
(
"SELECT SEQUENCENAME FROM SYS.SYSSEQUENCES A, SYS.SYSSCHEMAS S" +
" WHERE A.SCHEMAID = S.SCHEMAID " +
" AND S.SCHEMANAME = ?");
psf.setString(1, schema);
rs = psf.executeQuery();
dropUsingDMD(s, rs, schema, "SEQUENCENAME", "SEQUENCE");
psf.close();
}
// Finally drop the schema if it is not APP
if (!schema.equals("APP")) {
s.executeUpdate("DROP SCHEMA " + escape(schema) + " RESTRICT");
}
conn.commit();
s.close();
}
/**
* Return true if the SYSSEQUENCES table exists.
*/
private static boolean sysSequencesExists( Connection conn ) throws SQLException
{
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement
(
"select count(*) from sys.systables t, sys.sysschemas s\n" +
"where t.schemaid = s.schemaid\n" +
"and ( cast(s.schemaname as varchar(128)))= 'SYS'\n" +
"and ( cast(t.tablename as varchar(128))) = 'SYSSEQUENCES'" );
rs = ps.executeQuery();
rs.next();
return ( rs.getInt( 1 ) > 0 );
}
finally
{
if ( rs != null ) { rs.close(); }
if ( ps != null ) { ps.close(); }
}
}
/**
* Escape a non-qualified name so that it is suitable
* for use in a SQL query executed by JDBC.
*/
public static String escape(String name)
{
StringBuffer buffer = new StringBuffer(name.length() + 2);
buffer.append('"');
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
// escape double quote characters with an extra double quote
if (c == '"') buffer.append('"');
buffer.append(c);
}
buffer.append('"');
return buffer.toString();
}
/**
* Escape a schema-qualified name so that it is suitable
* for use in a SQL query executed by JDBC.
*/
public static String escape(String schema, String name)
{
return escape(schema) + "." + escape(name);
}
/**
* DROP a set of objects based upon a ResultSet from a
* DatabaseMetaData call.
*
* TODO: Handle errors to ensure all objects are dropped,
* probably requires interaction with its caller.
*
* #param s Statement object used to execute the DROP commands.
* #param rs DatabaseMetaData ResultSet
* #param schema Schema the objects are contained in
* #param mdColumn The column name used to extract the object's
* name from rs
* #param dropType The keyword to use after DROP in the SQL statement
* #throws SQLException database errors.
*/
private static void dropUsingDMD(
Statement s, ResultSet rs, String schema,
String mdColumn,
String dropType) throws SQLException
{
String dropLeadIn = "DROP " + dropType + " ";
// First collect the set of DROP SQL statements.
ArrayList<String> ddl = new ArrayList<String>();
while (rs.next())
{
String objectName = rs.getString(mdColumn);
String raw = dropLeadIn + escape(schema, objectName);
if (
"TYPE".equals( dropType ) ||
"SEQUENCE".equals( dropType ) ||
"DERBY AGGREGATE".equals( dropType )
)
{ raw = raw + " restrict "; }
ddl.add( raw );
}
rs.close();
if (ddl.isEmpty())
return;
// Execute them as a complete batch, hoping they will all succeed.
s.clearBatch();
int batchCount = 0;
for (Iterator i = ddl.iterator(); i.hasNext(); )
{
Object sql = i.next();
if (sql != null) {
s.addBatch(sql.toString());
batchCount++;
}
}
int[] results;
boolean hadError;
try {
results = s.executeBatch();
//Assert.assertNotNull(results);
//Assert.assertEquals("Incorrect result length from executeBatch", batchCount, results.length);
hadError = false;
} catch (BatchUpdateException batchException) {
results = batchException.getUpdateCounts();
//Assert.assertNotNull(results);
//Assert.assertTrue("Too many results in BatchUpdateException", results.length <= batchCount);
hadError = true;
}
// Remove any statements from the list that succeeded.
boolean didDrop = false;
for (int i = 0; i < results.length; i++)
{
int result = results[i];
if (result == Statement.EXECUTE_FAILED)
hadError = true;
else if (result == Statement.SUCCESS_NO_INFO || result >= 0) {
didDrop = true;
ddl.set(i, null);
}
//else
//Assert.fail("Negative executeBatch status");
}
s.clearBatch();
if (didDrop) {
// Commit any work we did do.
s.getConnection().commit();
}
// If we had failures drop them as individual statements
// until there are none left or none succeed. We need to
// do this because the batch processing stops at the first
// error. This copes with the simple case where there
// are objects of the same type that depend on each other
// and a different drop order will allow all or most
// to be dropped.
if (hadError) {
do {
hadError = false;
didDrop = false;
for (ListIterator<String> i = ddl.listIterator(); i.hasNext();) {
String sql = i.next();
if (sql != null) {
try {
s.executeUpdate(sql);
i.set(null);
didDrop = true;
} catch (SQLException e) {
hadError = true;
}
}
}
if (didDrop)
s.getConnection().commit();
} while (hadError && didDrop);
}
}
PS: This code came in handy for when I migrated my database from H2 that does support DROP ALL OBJECTS, to Apache Derby which does not (headache). The only reason I migrated away from H2 is that it's a fully in-memory database and was getting too big for my server's RAM, so I decided to try Apache Derby. H2 is far easier and more user-friendly than Derby, I highly recommend it. I'm sad that I can't afford the RAM to keep using H2.
By the way, for those affected by Derby's lack of LIMIT or UPSERT, see this post about substituting FETCH NEXT instead of LIMIT and this one about correctly using MERGE INTO.

Categories

Resources