package org.codalang.codaserver.hsqldbdriver; import org.codalang.codaserver.database.*; import org.codalang.codaserver.language.objects.CodaConstant; import java.sql.*; import java.text.SimpleDateFormat; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; /** * Created by IntelliJ IDEA. * User: mike * Date: Feb 20, 2008 * Time: 8:46:05 AM * * CodaServer and related original technologies are copyright 2008, 18th Street Software, LLC. * * Permission to use them is granted under the terms of the GNU GPLv2. */ public class HSQLDBConnection implements CodaConnection { private Connection conn = null; private java.util.logging.Logger logger; // the cached data about this database Hashtable tableData; public HSQLDBConnection (Connection conn, Logger logger) { this.conn = conn; try { this.conn.setAutoCommit(false); } catch (Exception e) { // skip it } this.logger = logger; this.refreshAllTableData(); } public boolean emptySchema() { CodaDatabaseMetadata metadata = this.getMetadata(); String[] tables = new String[0]; tables = metadata.getTables(); for (int i = 0; i < tables.length; i++) { try { this.dropTable(tables[i]); } catch (SQLException ex) { logger.log(Level.WARNING, ex.getMessage()); } } return true; } public void setLogger(Logger logger) { this.logger = logger; } public CodaDatabaseMetadata getMetadata() { return new JDBCMetadata(conn); } public CodaResultSet runQuery(String query, Vector columnHeadings) { try { Statement sql = conn.createStatement(); ResultSet rs = sql.executeQuery(query); return resultSetToCodaResultSet(rs, columnHeadings); } catch (SQLException ex) { logger.log(Level.WARNING, "Query Failed:\n"+query+"\n"); return new CodaResultSet(ex.getMessage()); } } public CodaResultSet runQuery(String query, Vector columnHeadings, long top, long startingAt) { try { Statement sql = conn.createStatement(); ResultSet rs = sql.executeQuery(query); return resultSetToCodaResultSet(rs, columnHeadings, top, startingAt); } catch (SQLException ex) { logger.log(Level.WARNING, "Query Failed:\n"+query+"\n"); return new CodaResultSet(ex.getMessage()); } } public boolean runStatement(String sqlStatement) { try { Statement sql = conn.createStatement(); sql.execute(sqlStatement); return true; } catch (SQLException ex) { logger.log(Level.WARNING, "SQL Statement Failed:\n"+sqlStatement+"\n"); return false; } } public boolean runStatementWithException(String sqlStatement) throws SQLException { Statement sql = conn.createStatement(); sql.execute(sqlStatement); return true; } public boolean createTable(String tableName, Vector columnDefinitions) throws SQLException { String sql = "CREATE CACHED TABLE " + tableName + " ( "; boolean firstFlag = true; Iterator it = columnDefinitions.iterator(); while (it.hasNext()) { ColumnDefinition cd = (ColumnDefinition)it.next(); if (firstFlag) { firstFlag = false; } else { sql += ", "; } sql = sql + cd.getName() + " " + getSQLTypeName(cd.getSqlType()) + " "; if (!cd.getNullable()) { sql += "NOT NULL"; } } sql += ") "; if (runStatementWithException(sql)) { refreshTableData(tableName); return true; } else { return false; } } public boolean alterTable (String tableName, String newTableName) throws SQLException { if (tableData.containsKey(newTableName.toUpperCase())) { return false; } else { String sql = "ALTER TABLE " + tableName + " RENAME TO " + newTableName; if (runStatementWithException(sql)) { if(tableData.containsKey(tableName)) { tableData.remove(tableName); } refreshTableData(newTableName); return true; } else { return false; } } } public boolean addColumn(String tableName, ColumnDefinition columnDefinition) throws SQLException { String sql = "ALTER TABLE " + tableName + " ADD COLUMN " + columnDefinition.getName() + " " + getSQLTypeName(columnDefinition.getSqlType()) + " "; if (!columnDefinition.getNullable()) { sql += "NOT NULL"; } else { sql += "NULL"; } if (runStatementWithException(sql)) { refreshTableData(tableName); return true; } else { return false; } } public boolean alterColumn (String tableName, String columnName, ColumnDefinition columnDefinition) throws SQLException { String sql = ""; if (!columnName.equalsIgnoreCase(columnDefinition.getName())) { tableName = tableName.toUpperCase(); columnName = columnName.toUpperCase(); if(!tableData.containsKey(tableName)) { refreshTableData(tableName); } if (tableData.containsKey(tableName)) { if (!((Hashtable)tableData.get(tableName)).containsKey(columnName)) { refreshTableData(tableName); } if (!((Hashtable)tableData.get(tableName)).containsKey(columnName)) { sql = "ALTER TABLE " + tableName + " ALTER COLUMN " + columnName + " RENAME TO "+ columnDefinition.getName(); if (runStatementWithException(sql)) { columnName = columnDefinition.getName(); refreshTableData(tableName); } } } } sql = "ALTER TABLE " + tableName + " ALTER COLUMN " + columnName + " " + getSQLTypeName(columnDefinition.getSqlType()) + " "; if (!columnDefinition.getNullable()) { sql += " NOT NULL"; } else { sql += " NULL"; } if (runStatementWithException(sql)) { refreshTableData(tableName); return true; } else { return false; } } public boolean dropColumn (String tableName, String columnName) throws SQLException { String sql = "ALTER TABLE " + tableName + " DROP COLUMN " + columnName; if (runStatementWithException(sql)) { refreshTableData(tableName); return true; } else { return false; } } public boolean dropTable(String tableName) throws SQLException { String sql = "DROP TABLE " + tableName; if (runStatementWithException(sql)) { conn.commit(); if (tableData.containsKey(tableName.toUpperCase())) { tableData.remove(tableName.toUpperCase()); } return true; } else { return false; } } public boolean setPrimaryKey(String tableName, String columnName) throws SQLException { String sql = "ALTER TABLE " + tableName + " ADD PRIMARY KEY (" + columnName + ")"; this.runStatementWithException(sql); sql = "ALTER TABLE " + tableName + " ALTER COLUMN " + columnName + " IDENTITY"; this.runStatementWithException(sql); sql = "ALTER TABLE " + tableName + " ALTER COLUMN " + columnName + " RESTART WITH 1"; return runStatementWithException(sql); } public boolean setIdentityColumns(String indexName, String tableName, Vector columnNames) throws SQLException { String sql = "CREATE UNIQUE INDEX " + indexName + " ON " + tableName + " ( "; boolean firstFlag = true; Iterator it = columnNames.iterator(); while (it.hasNext()) { if (firstFlag) { firstFlag = false; } else { sql += ", "; } sql += (String)it.next(); } sql += ")"; return runStatementWithException(sql); } public boolean createIndex(String indexName, String tableName, Vector columnNames, boolean uniqueFlag) throws SQLException { String sql = "CREATE " + (uniqueFlag ? "UNIQUE " : "") + "INDEX " + indexName + " ON " + tableName + " ( "; boolean firstFlag = true; Iterator it = columnNames.iterator(); while (it.hasNext()) { if (firstFlag) { firstFlag = false; } else { sql += ", "; } sql += (String)it.next(); } sql += ")"; return runStatementWithException(sql); } public boolean dropIndex(String indexName) throws SQLException { String sql = "DROP INDEX " + indexName; return runStatementWithException(sql); } public boolean createForeignKey (String keyName, String tableName, String columnName, String targetTableName, String targetColumnName) throws SQLException { String sql = "ALTER TABLE " + tableName + " ADD CONSTRAINT " + keyName + " FOREIGN KEY ( "+columnName+" ) REFERENCES " + targetTableName + " ( " + targetColumnName + " ) "; return runStatementWithException(sql); } public boolean dropForeignKey (String tableName, String keyName) throws SQLException { String sql = "ALTER TABLE " +tableName + " DROP CONSTRAINT " +keyName; return runStatementWithException(sql); } public long insertRow(String tableName, Hashtable values) { long id = -1; String columnList = "", valueList = ""; boolean firstFlag = true; Enumeration en = values.keys(); while(en.hasMoreElements()) { String key = (String) en.nextElement(); if (firstFlag) { firstFlag = false; } else { columnList += ", "; valueList += ", "; } columnList += key + " "; Object temp = values.get(key); if (values.get(key).getClass().getName() == "org.codalang.codaserver.language.objects.CodaConstant" && ((CodaConstant)values.get(key)).getSysvar() == CodaConstant.SYSVAR_NULL) { valueList += "null "; } else { valueList += formatStringForSQL(tableName, key, values.get(key).toString()) + " "; } } String sql = "INSERT INTO " + tableName + " ( " + columnList + ") VALUES (" + valueList + ")"; try { Statement st = conn.createStatement(); st.executeUpdate(sql); ResultSet rs = st.executeQuery("CALL IDENTITY()"); while (rs.next()) { id = rs.getLong(1); } } catch (SQLException e) { logger.log(Level.WARNING, e.getMessage()); } return id; } public boolean updateRow(String tableName, String idColumnName, long id, Hashtable values) { String sql = "UPDATE " + tableName + " SET "; String valueList = ""; boolean firstFlag = true; Enumeration en = values.keys(); while(en.hasMoreElements()) { String key = (String) en.nextElement(); if (firstFlag) { firstFlag = false; } else { valueList += ", "; } if (values.get(key).getClass().getName() == "org.codalang.codaserver.language.objects.CodaConstant" && ((CodaConstant)values.get(key)).getSysvar() == CodaConstant.SYSVAR_NULL) { valueList += key + " = null "; } else { valueList += key + " = " + formatStringForSQL(tableName, key, values.get(key).toString()) + " "; } } sql += valueList + " WHERE " + idColumnName + " = " + id; return runStatement(sql); } public boolean deleteRow(String tableName, String idColumnName, long id) { String sql = "DELETE FROM " + tableName + " WHERE " + idColumnName + " = " + id; return runStatement(sql); } public boolean updateRows(String tableName, String idColumnName, Vector<Long> ids, Hashtable values) { String sql = "UPDATE " + tableName + " SET "; String valueList = ""; boolean firstFlag = true; Enumeration en = values.keys(); while(en.hasMoreElements()) { String key = (String) en.nextElement(); if (firstFlag) { firstFlag = false; } else { valueList += ", "; } if (values.get(key).getClass().getName() == "org.codalang.codaserver.language.objects.CodaConstant" && ((CodaConstant)values.get(key)).getSysvar() == CodaConstant.SYSVAR_NULL) { valueList += key + " = null "; } else { valueList += key + " = " + formatStringForSQL(tableName, key, (String) values.get(key)) + " "; } } sql += valueList + " WHERE " + idColumnName + " IN ("; boolean start = true; for (Long id : ids) { if (start) { sql += id; start = false; } else { sql += ", " + id; } } sql += ")"; return runStatement(sql); } public boolean deleteRows(String tableName, String idColumnName, Vector<Long> ids) { String sql = "DELETE FROM " + tableName + " "; sql += " WHERE " + idColumnName + " IN ("; boolean start = true; for (Long id : ids) { if (start) { sql += id; start = false; } else { sql += ", " + id; } } sql += ")"; return runStatement(sql); } public CodaResultSet selectRow(String tableName, String idColumnName, long id) { String sql = "SELECT * FROM " + tableName + " WHERE " + idColumnName + " = " + id; return runQuery(sql, null); } public CodaResultSet selectRows(String tableName, String idColumnName, Vector<Long> ids) { String sql = "SELECT * FROM " + tableName + " WHERE " + idColumnName + " IN ("; boolean start = true; for (Long id : ids) { if (start) { sql += id; start = false; } else { sql += ", " + id; } } sql += ")"; return runQuery(sql, null); } public CodaResultSet selectRow(String tableName, long id) { return selectRow(tableName, "id", id); } private String getSQLTypeName(int javaSQLTypeID) { String retval = ""; switch (javaSQLTypeID) { case java.sql.Types.BIGINT: case java.sql.Types.INTEGER: case java.sql.Types.SMALLINT: case java.sql.Types.TINYINT: retval = "BIGINT"; break; case java.sql.Types.DOUBLE: case java.sql.Types.FLOAT: case java.sql.Types.REAL: retval = "FLOAT"; break; case java.sql.Types.DECIMAL: case java.sql.Types.NUMERIC: retval = "NUMERIC"; break; case java.sql.Types.DATE: retval = "DATE"; break; case java.sql.Types.TIME: retval = "TIME"; break; case java.sql.Types.TIMESTAMP: retval = "DATETIME"; break; case java.sql.Types.BOOLEAN: case java.sql.Types.BIT: retval = "INTEGER"; break; case java.sql.Types.VARCHAR: case java.sql.Types.CHAR: case java.sql.Types.LONGVARCHAR: case java.sql.Types.CLOB: retval = "VARCHAR(255) "; break; case java.sql.Types.BINARY: case java.sql.Types.VARBINARY: case java.sql.Types.LONGVARBINARY: case java.sql.Types.BLOB: retval = "LONGVARBINARY"; break; default: retval = "VARCHAR(255) "; break; } return retval; } private String escapeString(String val) { return val.replaceAll("'", "''"); } private CodaResultSet resultSetToCodaResultSet(ResultSet rs, Vector columnHeadings) { CodaResultSet wrs = new CodaResultSet(columnHeadings); try { if (columnHeadings == null) { ResultSetMetaData rsmd = rs.getMetaData(); columnHeadings = new Vector(); for (int i = 0; i < rsmd.getColumnCount(); i++) { columnHeadings.add(new CodaResultSetColumnHeading(rsmd.getColumnName(i+1), rsmd.getColumnType(i+1))); } wrs = new CodaResultSet(columnHeadings); } while (rs.next()) { Vector row = new Vector(); for (int j = 0; j < columnHeadings.size(); j++) { row.add(rs.getString(j+1)); } wrs.addRow(row); } } catch (SQLException ex) { logger.log(Level.WARNING, ex.getMessage()); } return wrs; } private CodaResultSet resultSetToCodaResultSet(ResultSet rs, Vector columnHeadings, long top, long startingAt) { CodaResultSet wrs = new CodaResultSet(columnHeadings); try { if (columnHeadings == null) { ResultSetMetaData rsmd = rs.getMetaData(); columnHeadings = new Vector(); for (int i = 0; i < rsmd.getColumnCount(); i++) { columnHeadings.add(new CodaResultSetColumnHeading(rsmd.getColumnName(i+1).equals("") ? "COLUMN" : rsmd.getColumnName(i+1), rsmd.getColumnType(i+1))); } wrs = new CodaResultSet(columnHeadings); } if (startingAt > 0) { for (long i = 0; i < startingAt; i++) { if (!rs.next()) { i = startingAt; } } } boolean useLimit = false; if (top > 0) { useLimit = true; } long k = 0; while (rs.next() && (!useLimit || (useLimit && top > k))) { Vector row = new Vector(); for (int j = 0; j < columnHeadings.size(); j++) { row.add(rs.getString(j+1)); } wrs.addRow(row); k++; } } catch (SQLException ex) { logger.log(Level.WARNING, ex.getMessage()); } return wrs; } private void refreshAllTableData () { this.tableData = new Hashtable(); try { Statement st = conn.createStatement(); ResultSet rs = st.executeQuery("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.SYSTEM_TABLES WHERE TABLE_SCHEM = 'PUBLIC'"); while (rs.next()) { refreshTableData(rs.getString(1)); } } catch (SQLException ex) { logger.log(Level.SEVERE, "Unable to read the schema metadata. Exiting."); System.out.println("Error reading schema metadata!"); System.exit(0); } } private void refreshTableData(String tableName) { tableName = tableName.toUpperCase(); if (tableData.containsKey(tableName)) { tableData.remove(tableName); } Hashtable columns = new Hashtable(); try { Statement st = conn.createStatement(); ResultSet rs = st.executeQuery("SELECT COLUMN_NAME, DATA_TYPE, TYPE_NAME FROM INFORMATION_SCHEMA.SYSTEM_COLUMNS WHERE TABLE_NAME = '"+ tableName +"'"); while (rs.next()) { columns.put(rs.getString(1).toUpperCase(), rs.getString(3).toUpperCase()); } } catch (SQLException ex) { logger.log(Level.SEVERE, "Unable to read the schema metadata. Exiting."); System.out.println("Error reading schema metadata!"); System.exit(0); } tableData.put(tableName, columns); } public String formatStringForSQL(String tableName, String columnName, String value) { tableName = tableName.toUpperCase(); columnName = columnName.toUpperCase(); if(!tableData.containsKey(tableName)) { refreshTableData(tableName); } if (tableData.containsKey(tableName)) { if (!((Hashtable)tableData.get(tableName)).containsKey(columnName)) { refreshTableData(tableName); } if (((Hashtable)tableData.get(tableName)).containsKey(columnName)) { int columnClass = getColumnClass((String) ((Hashtable)tableData.get(tableName)).get(columnName)); switch (columnClass) { case 1: value = value; break; case 2: SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); value = sdf.format(new java.util.Date(Long.parseLong(value))); default: value = "'" + this.escapeString(value) + "'"; break; } } } return value; } /** * 0: String * 1: Numeric * 2: Date */ private int getColumnClass(String type) { if (type.equals("NUMERIC") || type.equals("INTEGER") || type.equals("BIGINT") || type.equals("FLOAT") || type.equals("BOOLEAN")) { return 1; } else if (type.equals("DATE") || type.equals("TIMESTAMP")) { return 2; } else { return 0; } } public void setAutoCommit(boolean autoCommit) { try { conn.setAutoCommit(autoCommit); } catch (SQLException ex) { logger.log(Level.WARNING, "Autocommit on '" + this.getMetadata().getSystemTable().getApplicationName() + "' is not toggling!"); } } public boolean getAutoCommit() { try { return conn.getAutoCommit(); } catch (SQLException ex) { logger.log(Level.WARNING, "Autocommit on '" + this.getMetadata().getSystemTable().getApplicationName() + "' is not returning!"); } return false; } public void commit() { try { conn.commit(); } catch (SQLException ex) { logger.log(Level.WARNING, "Cannot commit on '" + this.getMetadata().getSystemTable().getApplicationName() + "'!"); } } public void rollback() { try { conn.rollback(); } catch (SQLException ex) { logger.log(Level.WARNING, "Cannot rollback on '" + this.getMetadata().getSystemTable().getApplicationName() + "'!"); } } public void closeConnection() { try { conn.close(); } catch (SQLException ex) { logger.log(Level.WARNING, ex.getMessage()); } } }