package org.codalang.codaserver.mysqldriver;
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 MySQLConnection implements CodaConnection {
private Connection conn = null;
private java.util.logging.Logger logger;
private String schemaName;
public MySQLConnection (Connection conn, String schemaName, Logger logger) {
this.conn = conn;
this.schemaName = schemaName;
try {
this.conn.setAutoCommit(false);
} catch (Exception e) {
// skip it
}
this.logger = logger;
}
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 TABLE " + tableName.toLowerCase() + " ( ";
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().toLowerCase() + " " + getSQLTypeName(cd.getSqlType()) + " ";
if (!cd.getNullable()) {
sql += "NOT NULL";
} else {
sql += "NULL";
}
}
sql += ") ";
if (runStatementWithException(sql)) {
return true;
} else {
return false;
}
}
public boolean alterTable (String tableName, String newTableName) throws SQLException {
if (tableExists(newTableName.toUpperCase())) {
return false;
} else {
String sql = "ALTER TABLE " + tableName.toLowerCase() + " RENAME TO " + newTableName.toLowerCase();
if (runStatementWithException(sql)) {
return true;
} else {
return false;
}
}
}
public boolean addColumn(String tableName, ColumnDefinition columnDefinition) throws SQLException {
String sql = "ALTER TABLE " + tableName.toLowerCase() + " ADD COLUMN " + columnDefinition.getName().toLowerCase() + " " + getSQLTypeName(columnDefinition.getSqlType()) + " ";
if (!columnDefinition.getNullable()) {
sql += "NOT NULL";
} else {
sql += "NULL";
}
if (runStatementWithException(sql)) {
return true;
} else {
return false;
}
}
public boolean alterColumn (String tableName, String columnName, ColumnDefinition columnDefinition) throws SQLException {
String sql = "";
sql = "ALTER TABLE " + tableName.toLowerCase() + " CHANGE COLUMN " + columnName + " " + columnDefinition.getName().toLowerCase() + " " + getSQLTypeName(columnDefinition.getSqlType()) + " ";
if (!columnDefinition.getNullable()) {
sql += " NOT NULL";
} else {
sql += " NULL";
}
if (runStatementWithException(sql)) {
return true;
} else {
return false;
}
}
public boolean dropColumn (String tableName, String columnName) throws SQLException {
String sql = "ALTER TABLE " + tableName.toLowerCase() + " DROP COLUMN " + columnName;
if (runStatementWithException(sql)) {
return true;
} else {
return false;
}
}
public boolean dropTable(String tableName) throws SQLException {
String sql = "DROP TABLE " + tableName;
if (runStatementWithException(sql)) {
conn.commit();
return true;
} else {
return false;
}
}
public boolean setPrimaryKey(String tableName, String columnName) throws SQLException {
String sql = "ALTER TABLE " + tableName.toLowerCase() + " MODIFY COLUMN " + columnName.toLowerCase() + " BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY";
return this.runStatementWithException(sql);
}
public boolean setIdentityColumns(String indexName, String tableName, Vector columnNames) throws SQLException {
String sql = "CREATE UNIQUE INDEX " + indexName + " ON " + tableName.toLowerCase() + " ( ";
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.toLowerCase() + " ON " + tableName.toLowerCase() + " ( ";
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 tableName = null;
CodaResultSet rs = this.runQuery("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE table_schema = '" + schemaName.toUpperCase() +"' and constraint_name = '" + indexName.toUpperCase() + "'", null);
while (rs.next()) {
tableName = rs.getData(0);
}
if (tableName != null) {
String sql = "DROP INDEX " + indexName + " ON " + tableName;
return runStatementWithException(sql);
} else {
return false;
}
}
public boolean createForeignKey (String keyName, String tableName, String columnName, String targetTableName, String targetColumnName) throws SQLException {
String sql = "ALTER TABLE " + tableName.toLowerCase() + " ADD FOREIGN KEY " + keyName + " ( "+columnName+" ) REFERENCES " + targetTableName + " ( " + targetColumnName + " ) ";
return runStatementWithException(sql);
}
public boolean dropForeignKey (String tableName, String keyName) throws SQLException {
String sql = "ALTER TABLE " +tableName.toLowerCase() + " DROP KEY " +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("SELECT LAST_INSERT_ID()");
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, values.get(key).toString()) + " ";
}
}
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:
retval = "VARCHAR(255) ";
break;
case java.sql.Types.LONGVARCHAR:
case java.sql.Types.CLOB:
retval = "LONGTEXT ";
break;
case java.sql.Types.BINARY:
case java.sql.Types.VARBINARY:
case java.sql.Types.LONGVARBINARY:
case java.sql.Types.BLOB:
retval = "LONGBLOB";
break;
default:
retval = "VARCHAR(255) ";
break;
}
return retval;
}
private String escapeString(String val) {
return val.replace("'", "''").replace("\\", "\\\\");
}
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.getColumnLabel(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.getColumnLabel(i+1).equals("") ? "COLUMN" : rsmd.getColumnLabel(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 boolean tableExists(String tableName) {
boolean retval = false;
try {
DatabaseMetaData metadata = conn.getMetaData();
ResultSet rs = metadata.getTables(null, null, tableName.toLowerCase(), null);
while (rs.next()) {
if (rs.getString(3).equalsIgnoreCase(tableName)) {
retval = true;
}
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Unable to read the schema metadata.");
}
return retval;
}
private boolean columnExists(String tableName, String columnName) {
boolean retval = false;
try {
DatabaseMetaData metadata = conn.getMetaData();
ResultSet rs = metadata.getColumns(null, null, tableName.toLowerCase(), columnName.toLowerCase());
while (rs.next()) {
if (rs.getString(4).equalsIgnoreCase(columnName)) {
retval = true;
}
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Unable to read the schema metadata.");
}
return retval;
}
private String getColumnType(String tableName, String columnName) {
CodaResultSet rs = runQuery("select upper(data_type) from information_schema.columns where upper(table_schema) = upper('"+schemaName+"') and upper(table_name) = upper('"+tableName+"') and upper(column_name) = upper('"+columnName+"')", null);
while(rs.next()) {
return rs.getData(0);
}
return "VARCHAR";
}
public String formatStringForSQL(String tableName, String columnName, String value) {
tableName = tableName.toUpperCase();
columnName = columnName.toUpperCase();
if (tableExists(tableName)) {
if (columnExists(tableName, columnName)) {
int columnClass = getColumnClass(getColumnType(tableName, 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")|| type.equals("DATETIME")) {
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());
}
}
}