Package org.rascalmpl.library.resource.jdbc

Source Code of org.rascalmpl.library.resource.jdbc.DriverShim

/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:

*   * Mark Hills - Mark.Hills@cwi.nl - CWI
*******************************************************************************/
package org.rascalmpl.library.resource.jdbc;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Types;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;

import org.eclipse.imp.pdb.facts.IConstructor;
import org.eclipse.imp.pdb.facts.IInteger;
import org.eclipse.imp.pdb.facts.IList;
import org.eclipse.imp.pdb.facts.IListWriter;
import org.eclipse.imp.pdb.facts.ISet;
import org.eclipse.imp.pdb.facts.ISetWriter;
import org.eclipse.imp.pdb.facts.IString;
import org.eclipse.imp.pdb.facts.IValue;
import org.eclipse.imp.pdb.facts.IValueFactory;
import org.eclipse.imp.pdb.facts.type.Type;
import org.eclipse.imp.pdb.facts.type.TypeFactory;
import org.eclipse.imp.pdb.facts.type.TypeStore;
import org.rascalmpl.interpreter.IEvaluatorContext;
import org.rascalmpl.interpreter.staticErrors.UnsupportedOperation;
import org.rascalmpl.interpreter.utils.RuntimeExceptionFactory;
import org.rascalmpl.values.ValueFactoryFactory;

public class JDBC {

  private static TypeFactory TF = TypeFactory.getInstance();
  public static final TypeStore TS = new TypeStore();
 
  public static final Type Connection = TF.abstractDataType(TS, "Connection");
  public static final Type jdbcConnection = TF.constructor(TS, Connection, "jdbcConnection", TF.integerType(), "id");

  public static final Type JDBCType = TF.abstractDataType(TS, "JDBCType");
  public static final Type jdbcArray = TF.constructor(TS, JDBCType, "array");
  public static final Type jdbcBigInt = TF.constructor(TS, JDBCType, "bigInt");
  public static final Type jdbcBinary = TF.constructor(TS, JDBCType, "binary");
  public static final Type jdbcBit = TF.constructor(TS, JDBCType, "bit");
  public static final Type jdbcBlob = TF.constructor(TS, JDBCType, "blob");
  public static final Type jdbcBoolean = TF.constructor(TS, JDBCType, "boolean");
  public static final Type jdbcChar = TF.constructor(TS, JDBCType, "char");
  public static final Type jdbcClob = TF.constructor(TS, JDBCType, "clob");
  public static final Type jdbcDataLink = TF.constructor(TS, JDBCType, "dataLink");
  public static final Type jdbcDate = TF.constructor(TS, JDBCType, "date");
  public static final Type jdbcDecimal = TF.constructor(TS, JDBCType, "decimal");
  public static final Type jdbcDistinct = TF.constructor(TS, JDBCType, "distinct");
  public static final Type jdbcDouble = TF.constructor(TS, JDBCType, "double");
  public static final Type jdbcFloat = TF.constructor(TS, JDBCType, "float");
  public static final Type jdbcInteger = TF.constructor(TS, JDBCType, "integer");
  public static final Type jdbcJavaObject = TF.constructor(TS, JDBCType, "javaObject");
  public static final Type jdbcLongNVarChar = TF.constructor(TS, JDBCType, "longNVarChar");
  public static final Type jdbcLongVarBinary = TF.constructor(TS, JDBCType, "longVarBinary");
  public static final Type jdbcLongVarChar = TF.constructor(TS, JDBCType, "longVarChar");
  public static final Type jdbcNChar = TF.constructor(TS, JDBCType, "nChar");
  public static final Type jdbcNClob = TF.constructor(TS, JDBCType, "nClob");
  public static final Type jdbcNull = TF.constructor(TS, JDBCType, "null");
  public static final Type jdbcNumeric = TF.constructor(TS, JDBCType, "numeric");
  public static final Type jdbcNVarChar = TF.constructor(TS, JDBCType, "nVarChar");
  public static final Type jdbcOther = TF.constructor(TS, JDBCType, "other");
  public static final Type jdbcReal = TF.constructor(TS, JDBCType, "real");
  public static final Type jdbcRef = TF.constructor(TS, JDBCType, "ref");
  public static final Type jdbcRowId = TF.constructor(TS, JDBCType, "rowId");
  public static final Type jdbcSmallInt = TF.constructor(TS, JDBCType, "smallInt");
  public static final Type jdbcSQLXML = TF.constructor(TS, JDBCType, "sqlXML");
  public static final Type jdbcStruct = TF.constructor(TS, JDBCType, "struct");
  public static final Type jdbcTime = TF.constructor(TS, JDBCType, "time");
  public static final Type jdbcTimeStamp = TF.constructor(TS, JDBCType, "timeStamp");
  public static final Type jdbcTinyInt = TF.constructor(TS, JDBCType, "tinyInt");
  public static final Type jdbcVarBinary = TF.constructor(TS, JDBCType, "varBinary");
  public static final Type jdbcVarChar = TF.constructor(TS, JDBCType, "varChar");

  public static final Type Column = TF.abstractDataType(TS, "Column");
  public static final Type column = TF.constructor(TS, Column, "column", TF.stringType(), "columnName", JDBCType, "columnType", TF.boolType(), "nullable");
 
  public static final Type Table = TF.abstractDataType(TS, "Table");
  public static final Type table = TF.constructor(TS, Table, "table", TF.stringType(), "tableName", TF.listType(Column), "columns");

  public static final Type nullableT = TF.parameterType("T");
  public static final Type Nullable = TF.abstractDataType(TS, "Nullable", nullableT);

  private final IValueFactory vf;
  private int connectionCounter = 0;
  private HashMap<IInteger,Connection> connectionMap;

  public JDBC(IValueFactory vf) {
    this.vf = vf;
    this.connectionMap = new HashMap<IInteger,Connection>();
  }
 
  public void registerJDBCClass(IString className, IEvaluatorContext eval) {
    List<ClassLoader> loaders = eval.getEvaluator().getClassLoaders();
    Class<?> driverClass = null;
    Throwable ex = null;
   
    for (ClassLoader loader : loaders) {
      try {
        driverClass = loader.loadClass(className.getValue());
        if (driverClass != null) {
          Driver driver = (Driver)driverClass.newInstance();
          DriverManager.registerDriver(new DriverShim(driver));
        }
      } catch (ClassNotFoundException e) {
        ex = e;
        continue;
      } catch (InstantiationException e) {
        driverClass = null;
        ex = e;
        continue;
      } catch (IllegalAccessException e) {
        driverClass = null;
        ex = e;
        continue;
      } catch (SQLException e) {
        driverClass = null;
        ex = e;
        continue;
      }
      break;
    }
   
    if (driverClass == null) {
      RuntimeExceptionFactory.javaException(new RuntimeException("An appropriate class loader to load class " + className.getValue() + " could not be found, either this class does not exist or the jar containing this class has not been added to the classpath.", ex), eval.getCurrentAST(), null);
    }
  }
 
  public IConstructor createConnection(IString connectString) {
    try {
      Connection conn = DriverManager.getConnection(connectString.getValue());
      IInteger newKey = vf.integer(++connectionCounter);
      connectionMap.put(newKey, conn);
      return vf.constructor(JDBC.jdbcConnection, newKey);
    } catch (SQLException sqle) {
      throw RuntimeExceptionFactory.illegalArgument(connectString, null, null, addMessage("Could not connect with given connect string", sqle));
    }
  }
 
  public void closeConnection(IConstructor connection) {
    try {
      IInteger connectionId = (IInteger) connection.get(0);
      if (connectionMap.containsKey(connectionId)) {
        Connection conn = connectionMap.get(connectionId);
        conn.close();
        connectionMap.remove(connectionId);
      } else {
        throw RuntimeExceptionFactory.illegalArgument(connection, null, null, "Connection does not exist.");
      }
    } catch (SQLException sqle) {
      throw RuntimeExceptionFactory.illegalArgument(connection, null, null, addMessage("Could not close the given connection", sqle));
    }
  }

  public IList getTableTypes(IConstructor connection) {
    try {
      IInteger connectionId = (IInteger) connection.get(0);
      if (connectionMap.containsKey(connectionId)) {
        Connection conn = connectionMap.get(connectionId);
        DatabaseMetaData dmd = conn.getMetaData();
        ResultSet rs = dmd.getTableTypes();
        IListWriter resultWriter = this.vf.listWriter(TF.stringType());
        while (rs.next()) resultWriter.append(this.vf.string(rs.getString(1)));
        rs.close();
        return resultWriter.done();
      } else {
        throw RuntimeExceptionFactory.illegalArgument(connection, null, null, "Connection does not exist.");
      }
    } catch (SQLException sqle) {
      throw RuntimeExceptionFactory.illegalArgument(connection, null, null, addMessage("Could not close the given connection", sqle));
    }
  }

  public ISet getTableNames(IConstructor connection) {
    try {
      IInteger connectionId = (IInteger) connection.get(0);
      if (connectionMap.containsKey(connectionId)) {
        Connection conn = connectionMap.get(connectionId);
        DatabaseMetaData dmd = conn.getMetaData();
        ResultSet rs = dmd.getTables(null, null, null, new String[] { "TABLE" });
        HashSet<String> tables = new HashSet<String>();
        while (rs.next()) tables.add(rs.getString("TABLE_NAME"));
        rs.close();
       
        ISetWriter setRes = vf.setWriter(TF.stringType());
       
        for (String tableName : tables) {
          setRes.insert(vf.string(tableName));
        }
        return setRes.done();
      } else {
        throw RuntimeExceptionFactory.illegalArgument(connection, null, null, "Connection does not exist.");
      }
    } catch (SQLException sqle) {
      throw RuntimeExceptionFactory.illegalArgument(connection, null, null, addMessage("Could not close the given connection", sqle));
    }
  }

  public ISet getViewNames(IConstructor connection) {
    try {
      IInteger connectionId = (IInteger) connection.get(0);
      if (connectionMap.containsKey(connectionId)) {
        Connection conn = connectionMap.get(connectionId);
        DatabaseMetaData dmd = conn.getMetaData();
        ResultSet rs = dmd.getTables(null, null, null, new String[] { "VIEW" });
        HashSet<String> tables = new HashSet<String>();
        while (rs.next()) tables.add(rs.getString("TABLE_NAME"));
        rs.close();
       
        ISetWriter setRes = vf.setWriter(TF.stringType());
       
        for (String tableName : tables) {
          setRes.insert(vf.string(tableName));
        }
        return setRes.done();
      } else {
        throw RuntimeExceptionFactory.illegalArgument(connection, null, null, "Connection does not exist.");
      }
    } catch (SQLException sqle) {
      throw RuntimeExceptionFactory.illegalArgument(connection, null, null, addMessage("Could not close the given connection", null));
    }
  }

  public ISet getTables(IConstructor connection) {
    return getTablesOrViews(connection, new String[] { "TABLE" });
  }
 
  public ISet getViews(IConstructor connection) {
    return getTablesOrViews(connection, new String[] { "VIEW" });
  }
 
  private ISet getTablesOrViews(IConstructor connection, String[] tableTypes) {
    // TODO: Add code to check and make sure the table types are valid
    try {
      IInteger connectionId = (IInteger) connection.get(0);
      if (connectionMap.containsKey(connectionId)) {
        Connection conn = connectionMap.get(connectionId);
        DatabaseMetaData dmd = conn.getMetaData();
        ResultSet rs = dmd.getTables(null, null, null, tableTypes);
        HashSet<String> tables = new HashSet<String>();
        while (rs.next()) tables.add(rs.getString("TABLE_NAME"));
        rs.close();
       
        ISetWriter setRes = vf.setWriter(Table);
       
        for (String tableName : tables) {
          rs = dmd.getColumns(null, null, tableName, null);
          IListWriter listRes = vf.listWriter(Column);
          while (rs.next()) {
            String cn = rs.getString("COLUMN_NAME");
            int dt = rs.getInt("DATA_TYPE");
            String nullable = rs.getString("IS_NULLABLE");
            listRes.append(vf.constructor(column, vf.string(cn), vf.constructor(JDBC.jdbc2rascalType(dt)), nullable.equalsIgnoreCase("YES") ? vf.bool(true) : vf.bool(false)));
          }
          setRes.insert(vf.constructor(table, vf.string(tableName), listRes.done()));
          rs.close();
        }
        return setRes.done();
      } else {
        throw RuntimeExceptionFactory.illegalArgument(connection, null, null, "Connection does not exist.");
      }
    } catch (SQLException sqle) {
      throw RuntimeExceptionFactory.illegalArgument(connection, null, null, addMessage("Could not close the given connection", sqle));
    }   
  }
 
  // TODO: Handle the case where the table name does not exist
  public IConstructor getTable(IConstructor connection, IString tableName) {
    try {
      IInteger connectionId = (IInteger) connection.get(0);
      if (connectionMap.containsKey(connectionId)) {
        Connection conn = connectionMap.get(connectionId);
        DatabaseMetaData dmd = conn.getMetaData();
        ResultSet rs = dmd.getColumns(null, null, tableName.getValue(), null);
        IListWriter listRes = vf.listWriter(Column);
        while (rs.next()) {
          String cn = rs.getString("COLUMN_NAME");
          int dt = rs.getInt("DATA_TYPE");
          String nullable = rs.getString("IS_NULLABLE");
          listRes.append(vf.constructor(column, vf.string(cn), vf.constructor(JDBC.jdbc2rascalType(dt)), nullable.equalsIgnoreCase("YES") ? vf.bool(true) : vf.bool(false)));
        }
        rs.close();
        return vf.constructor(table, tableName, listRes.done());
      } else {
        throw RuntimeExceptionFactory.illegalArgument(connection, null, null, "Connection does not exist.");
      }
    } catch (SQLException sqle) {
      throw RuntimeExceptionFactory.illegalArgument(connection, null, null, addMessage("Could not close the given connection", sqle));
    }
  }

  private static Type jdbc2rascalType(int columnType) {
    switch(columnType) {
      case Types.ARRAY:
        return JDBC.jdbcArray;
      case Types.BIGINT:
        return JDBC.jdbcBigInt;
      case Types.BINARY:
        return JDBC.jdbcBinary;
      case Types.BIT:
        return JDBC.jdbcBit;
      case Types.BLOB:
        return JDBC.jdbcBlob;
      case Types.BOOLEAN:
        return JDBC.jdbcBoolean;
      case Types.CHAR:
        return JDBC.jdbcChar;
      case Types.CLOB:
        return JDBC.jdbcClob;
      case Types.DATALINK:
        return JDBC.jdbcDataLink;
      case Types.DATE:
        return JDBC.jdbcDate;
      case Types.DECIMAL:
        return JDBC.jdbcDecimal;
      case Types.DISTINCT:
        return JDBC.jdbcDistinct;
      case Types.DOUBLE:
        return JDBC.jdbcDouble;
      case Types.FLOAT:
        return JDBC.jdbcFloat;
      case Types.INTEGER:
        return JDBC.jdbcInteger;
      case Types.JAVA_OBJECT:
        return JDBC.jdbcJavaObject;
      case Types.LONGNVARCHAR:
        return JDBC.jdbcLongNVarChar;
      case Types.LONGVARBINARY:
        return JDBC.jdbcLongVarBinary;
      case Types.LONGVARCHAR:
        return JDBC.jdbcLongVarChar;
      case Types.NCHAR:
        return JDBC.jdbcNChar;
      case Types.NCLOB:
        return JDBC.jdbcNClob;
      case Types.NULL:
        return JDBC.jdbcNull;
      case Types.NUMERIC:
        return JDBC.jdbcNumeric;
      case Types.NVARCHAR:
        return JDBC.jdbcNVarChar;
      case Types.OTHER:
        return JDBC.jdbcOther;
      case Types.REAL:
        return JDBC.jdbcReal;
      case Types.REF:
        return JDBC.jdbcRef;
      case Types.ROWID:
        return JDBC.jdbcRowId;
      case Types.SMALLINT:
        return JDBC.jdbcSmallInt;
      case Types.SQLXML:
        return JDBC.jdbcSQLXML;
      case Types.STRUCT:
        return JDBC.jdbcStruct;
      case Types.TIME:
        return JDBC.jdbcTime;
      case Types.TIMESTAMP:
        return JDBC.jdbcTimeStamp;
      case Types.TINYINT:
        return JDBC.jdbcTinyInt;
      case Types.VARBINARY:
        return JDBC.jdbcVarBinary;
      case Types.VARCHAR:
        return JDBC.jdbcVarChar;
    }
    throw RuntimeExceptionFactory.illegalArgument(ValueFactoryFactory.getValueFactory().integer(columnType), null, null, "Invalid JDBC type id given: " + columnType);
  }

  public static Type jdbc2pdbType(int columnType, boolean nullable) {
    Type res = null;

    switch(columnType) {
      case Types.ARRAY:
        throw new UnsupportedOperation("JDBC Array types are currently not supported", null);
      case Types.BIGINT:
        res = TF.integerType();
        break;
      case Types.BINARY:
        res = TF.listType(TF.integerType());
        break;
      case Types.BIT:
        res = TF.boolType();
        break;
      case Types.BLOB:
        res = TF.listType(TF.integerType());
        break;
      case Types.BOOLEAN:
        res = TF.boolType();
        break;
      case Types.CHAR:
        res = TF.stringType();
        break;
      case Types.CLOB:
        res = TF.stringType();
        break;
      case Types.DATALINK:
        throw new UnsupportedOperation("JDBC Datalink types are currently not supported", null);
      case Types.DATE:
        res = TF.dateTimeType();
        break;
      case Types.DECIMAL:
        res = TF.realType();
        break;
      case Types.DISTINCT:
        throw new UnsupportedOperation("JDBC Distinct types are currently not supported", null);
      case Types.DOUBLE:
        res = TF.realType();
        break;
      case Types.FLOAT:
        res = TF.realType();
        break;
      case Types.INTEGER:
        res = TF.integerType();
        break;
      case Types.JAVA_OBJECT:
        throw new UnsupportedOperation("JDBC JavaObject types are currently not supported", null);
      case Types.LONGNVARCHAR:
        res = TF.stringType();
        break;
      case Types.LONGVARBINARY:
        res = TF.listType(TF.integerType());
        break;
      case Types.LONGVARCHAR:
        res = TF.stringType();
        break;
      case Types.NCHAR:
        res = TF.stringType();
        break;
      case Types.NCLOB:
        res = TF.stringType();
        break;
      case Types.NULL:
        throw new UnsupportedOperation("JDBC Null types are currently not supported", null);
      case Types.NUMERIC:
        res = TF.realType();
        break;
      case Types.NVARCHAR:
        res = TF.stringType();
        break;
      case Types.OTHER:
        throw new UnsupportedOperation("JDBC Other types are currently not supported", null);
      case Types.REAL:
        res = TF.realType();
        break;
      case Types.REF:
        throw new UnsupportedOperation("JDBC Ref types are currently not supported", null);
      case Types.ROWID:
        throw new UnsupportedOperation("JDBC RowID types are currently not supported", null);
      case Types.SMALLINT:
        res = TF.integerType();
        break;
      case Types.SQLXML:
        throw new UnsupportedOperation("JDBC SQLXML types are currently not supported", null);
      case Types.STRUCT:
        throw new UnsupportedOperation("JDBC Struct types are currently not supported", null);
      case Types.TIME:
        res = TF.dateTimeType();
        break;
      case Types.TIMESTAMP:
        res = TF.dateTimeType();
        break;
      case Types.TINYINT:
        res = TF.integerType();
        break;
      case Types.VARBINARY:
        res = TF.listType(TF.integerType());
        break;
      case Types.VARCHAR:
        res = TF.stringType();
        break;
      default:
        throw new UnsupportedOperation("Unknown column type: " + columnType, null);
    }
   
    if (nullable) {
      HashMap<Type,Type> bindings = new HashMap<Type,Type>();
      bindings.put(nullableT, res);
      res = Nullable.instantiate(bindings);
    }
   
    return res;
  }
 
  public static IValue jdbc2pdbValue(ResultSet rs, int idx, IValueFactory vf) {
    IValue res = null;
   
    try {
      if (idx == 0 && rs.getInt(0) == 1219)
        System.err.println(rs.getDate(6));

      int jdbcColumnType = rs.getMetaData().getColumnType(idx);
      Calendar c = Calendar.getInstance();
      IListWriter lw = null;
      InputStream isr = null;
      int isrRes = -1;
     
      switch(jdbcColumnType) {
        case Types.ARRAY:
          throw new UnsupportedOperation("JDBC Array types are currently not supported", null);
        case Types.BIGINT:
          if (rs.getBigDecimal(idx) != null)
            res = vf.integer(rs.getBigDecimal(idx).toString());
          else
            res = vf.integer(0);
          break;
        case Types.BINARY:
          isr = rs.getBinaryStream(idx);
          lw = vf.listWriter(TypeFactory.getInstance().integerType());
          if (isr != null) {
            isrRes = isr.read();
            while (isrRes != -1) {
              lw.append(vf.integer(isrRes));
              isrRes = isr.read();
            }
          }
          res = lw.done();
          break;
        case Types.BIT:
          res = vf.bool(rs.getBoolean(idx));
          break;
        case Types.BLOB:
          lw = vf.listWriter(TypeFactory.getInstance().integerType());
          if (rs.getBlob(idx) != null) {
            isr = rs.getBlob(idx).getBinaryStream();
            if (isr != null) {
              isrRes = isr.read();
              while (isrRes != -1) {
                lw.append(vf.integer(isrRes));
                isrRes = isr.read();
              }
            }
          }
          res = lw.done();
          break;
        case Types.BOOLEAN:
          res = vf.bool(rs.getBoolean(idx));
          break;
        case Types.CHAR:
          if (rs.getString(idx) != null)
            res = vf.string(rs.getString(idx));
          else
            res = vf.string("");
          break;
        case Types.CLOB:
          lw = vf.listWriter(TypeFactory.getInstance().integerType());
          if (rs.getClob(idx) != null) {
            isr = rs.getClob(idx).getAsciiStream();
            if (isr != null) {
              isrRes = isr.read();
              while (isrRes != -1) {
                lw.append(vf.integer(isrRes));
                isrRes = isr.read();
              }
            }
          }
          res = lw.done();
          break;
        case Types.DATALINK:
          throw new UnsupportedOperation("JDBC Datalink types are currently not supported", null);
        case Types.DATE:
          if (rs.getDate(idx) != null) {
            c = Calendar.getInstance();
            c.setTimeInMillis(rs.getDate(idx).getTime());
//            c.setTime(rs.getDate(idx));
          }
          res = vf.date(c.get(Calendar.YEAR), c.get(Calendar.MONTH)+1, c.get(Calendar.DAY_OF_MONTH));
          break;
        case Types.DECIMAL:
          if (rs.getBigDecimal(idx) != null)
            res = vf.real(rs.getBigDecimal(idx).toString());
          else
            res = vf.real(0.0);
          break;
        case Types.DISTINCT:
          throw new UnsupportedOperation("JDBC Distinct types are currently not supported", null);
        case Types.DOUBLE:
          res = vf.real(rs.getDouble(idx));
          break;
        case Types.FLOAT:
          res = vf.real(rs.getFloat(idx));
          break;
        case Types.INTEGER:
          res = vf.integer(rs.getInt(idx));
          break;
        case Types.JAVA_OBJECT:
          throw new UnsupportedOperation("JDBC JavaObject types are currently not supported", null);
        case Types.LONGNVARCHAR:
          if (rs.getString(idx) != null)
            res = vf.string(rs.getString(idx));
          else
            res = vf.string("");
          break;
        case Types.LONGVARBINARY:
          lw = vf.listWriter(TypeFactory.getInstance().integerType());
          isr = rs.getBinaryStream(idx);
          if (isr != null) {
            isrRes = isr.read();
            while (isrRes != -1) {
              lw.append(vf.integer(isrRes));
              isrRes = isr.read();
            }
          }
          res = lw.done();
          break;
        case Types.LONGVARCHAR:
          if (rs.getString(idx) != null)
            res = vf.string(rs.getString(idx));
          else
            res = vf.string("");
          break;
        case Types.NCHAR:
          if (rs.getString(idx) != null)
            res = vf.string(rs.getString(idx));
          else
            res = vf.string("");
          break;
        case Types.NCLOB:
          lw = vf.listWriter(TypeFactory.getInstance().integerType());
          if (rs.getNClob(idx) != null) {
            isr = rs.getNClob(idx).getAsciiStream();
            if (isr != null) {
              isrRes = isr.read();
              while (isrRes != -1) {
                lw.append(vf.integer(isrRes));
                isrRes = isr.read();
              }
            }
          }
          res = lw.done();
          break;
        case Types.NULL:
          throw new UnsupportedOperation("JDBC Null types are currently not supported", null);
        case Types.NUMERIC:
          if (rs.getBigDecimal(idx) != null) {
            res = vf.real(rs.getBigDecimal(idx).toString());
          } else {
            res = vf.real(0);
          }
          break;
        case Types.NVARCHAR:
          if (rs.getString(idx) != null)
            res = vf.string(rs.getString(idx));
          else
            res = vf.string("");
          break;
        case Types.OTHER:
          throw new UnsupportedOperation("JDBC Other types are currently not supported", null);
        case Types.REAL:
          res = vf.real(rs.getDouble(idx));
          break;
        case Types.REF:
          throw new UnsupportedOperation("JDBC Ref types are currently not supported", null);
        case Types.ROWID:
          throw new UnsupportedOperation("JDBC RowID types are currently not supported", null);
        case Types.SMALLINT:
          res = vf.integer(rs.getInt(idx));
          break;
        case Types.SQLXML:
          throw new UnsupportedOperation("JDBC SQLXML types are currently not supported", null);
        case Types.STRUCT:
          throw new UnsupportedOperation("JDBC Struct types are currently not supported", null);
        case Types.TIME:
          if (rs.getTime(idx) != null) {
            c = Calendar.getInstance();
            c.setTimeInMillis(rs.getDate(idx).getTime());
          }
          res = vf.time(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), c.get(Calendar.SECOND), c.get(Calendar.MILLISECOND));
          break;
        case Types.TIMESTAMP:
          if (rs.getTimestamp(idx) != null) {
            c = Calendar.getInstance();
            c.setTimeInMillis(rs.getDate(idx).getTime());
          }
          res = vf.datetime(c.get(Calendar.YEAR), c.get(Calendar.MONTH)+1, c.get(Calendar.DAY_OF_MONTH), c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), c.get(Calendar.SECOND), c.get(Calendar.MILLISECOND));
          break;
        case Types.TINYINT:
          res = vf.integer(rs.getInt(idx));
          break;
        case Types.VARBINARY:
          lw = vf.listWriter(TypeFactory.getInstance().integerType());
          isr = rs.getBinaryStream(idx);
          if (isr != null) {
            isrRes = isr.read();
            while (isrRes != -1) {
              lw.append(vf.integer(isrRes));
              isrRes = isr.read();
            }
          }
          res = lw.done();
          break;
        case Types.VARCHAR:
          if (rs.getString(idx) != null)
            res = vf.string(rs.getString(idx));
          else
            res = vf.string("");
          break;
        default:
          throw new UnsupportedOperation("Unknown column type: " + jdbcColumnType, null);
      }

      if(rs.getMetaData().isNullable(idx) != ResultSetMetaData.columnNoNulls) {
        Type resType = jdbc2pdbType(jdbcColumnType, true);

        if (rs.wasNull()) {
          Type nullT = TF.constructor(TS,  resType, "null");
          res = vf.constructor(nullT);
        } else {
          Type notnullT = TF.constructor(TS, resType, "notnull", resType, "item");
          res = vf.constructor(notnullT, res);
        }
      }

    } catch (SQLException sqle) {
      // TODO: Throw here...
    } catch (IOException ioe) {
      // TODO: Throw here
    }
   

    return res;
 
 
  // TODO: Add more error handling code...
  public IValue loadTable(IValue resultType, IConstructor connection, IString tableName) {
    try {
      IInteger connectionId = (IInteger) connection.get(0);
      if (connectionMap.containsKey(connectionId)) {
        Connection conn = connectionMap.get(connectionId);
        PreparedStatement stmt = conn.prepareStatement("SELECT * FROM " + tableName.getValue());
        ResultSet rs = stmt.executeQuery();
       
        Type elementType = resultType.getType().getTypeParameters().getFieldType(0);
        int columns = elementType.getArity();

        ISetWriter sw = vf.setWriter(elementType);
        while (rs.next()) {
          IValue tupleValues[] = new IValue[columns];
          for (int idx = 0; idx < columns; ++idx) {
            tupleValues[idx] = JDBC.jdbc2pdbValue(rs, idx + 1, this.vf);
          }
          sw.insert(vf.tuple(tupleValues));
        }
       
        rs.close();
        stmt.close();
       
        return sw.done();
      } else {
        throw RuntimeExceptionFactory.illegalArgument(connection, null, null, "Connection does not exist.");
      }
    } catch (SQLException sqle) {
      throw RuntimeExceptionFactory.illegalArgument(connection, null, null, sqle.getMessage());
    }
  }

  // TODO: Add more error handling code...
  public IValue loadTable(IConstructor connection, IString tableName) {
    try {
      IInteger connectionId = (IInteger) connection.get(0);
      if (connectionMap.containsKey(connectionId)) {
        Connection conn = connectionMap.get(connectionId);
        PreparedStatement stmt = conn.prepareStatement("SELECT * FROM " + tableName.getValue());
        ResultSet rs = stmt.executeQuery();
       
        Type elementType = JDBC.TF.valueType();

        ISetWriter sw = vf.setWriter(elementType);
        int columns = rs.getMetaData().getColumnCount();
       
        while (rs.next()) {
          IValue tupleValues[] = new IValue[columns];
          for (int idx = 0; idx < columns; ++idx) {
            tupleValues[idx] = JDBC.jdbc2pdbValue(rs, idx + 1, this.vf);
          }
          sw.insert(vf.tuple(tupleValues));
        }
       
        rs.close();
        stmt.close();
       
        return sw.done();
      } else {
        throw RuntimeExceptionFactory.illegalArgument(connection, null, null, "Connection does not exist.");
      }
    } catch (SQLException sqle) {
      throw RuntimeExceptionFactory.illegalArgument(connection, null, null, sqle.getMessage());
    }
  }

  public IValue loadTableOrdered(IValue resultType, IConstructor connection, IString tableName) {
    try {
      IInteger connectionId = (IInteger) connection.get(0);
      if (connectionMap.containsKey(connectionId)) {
        Connection conn = connectionMap.get(connectionId);
        PreparedStatement stmt = conn.prepareStatement("SELECT * FROM " + tableName.getValue());
        ResultSet rs = stmt.executeQuery();
       
        Type elementType = resultType.getType().getTypeParameters().getFieldType(0);
        int columns = elementType.getArity();

        IListWriter lw = vf.listWriter(elementType);
        while (rs.next()) {
          IValue tupleValues[] = new IValue[columns];
          for (int idx = 0; idx < columns; ++idx) {
            tupleValues[idx] = JDBC.jdbc2pdbValue(rs, idx + 1, this.vf);
          }
          lw.append(vf.tuple(tupleValues));
        }
       
        rs.close();
        stmt.close();
       
        return lw.done();
      } else {
        throw RuntimeExceptionFactory.illegalArgument(connection, null, null, "Connection does not exist.");
      }
    } catch (SQLException sqle) {
      throw RuntimeExceptionFactory.illegalArgument(connection, null, null, sqle.getMessage());
    }
  }

  public IValue loadTableOrdered(IConstructor connection, IString tableName) {
    try {
      IInteger connectionId = (IInteger) connection.get(0);
      if (connectionMap.containsKey(connectionId)) {
        Connection conn = connectionMap.get(connectionId);
        PreparedStatement stmt = conn.prepareStatement("SELECT * FROM " + tableName.getValue());
        ResultSet rs = stmt.executeQuery();
       
        Type elementType = JDBC.TF.valueType();
        int columns = rs.getMetaData().getColumnCount();

        IListWriter lw = vf.listWriter(elementType);
        while (rs.next()) {
          IValue tupleValues[] = new IValue[columns];
          for (int idx = 0; idx < columns; ++idx) {
            tupleValues[idx] = JDBC.jdbc2pdbValue(rs, idx + 1, this.vf);
          }
          lw.append(vf.tuple(tupleValues));
        }
       
        rs.close();
        stmt.close();
       
        return lw.done();
      } else {
        throw RuntimeExceptionFactory.illegalArgument(connection, null, null, "Connection does not exist.");
      }
    } catch (SQLException sqle) {
      throw RuntimeExceptionFactory.illegalArgument(connection, null, null, sqle.getMessage());
    }
  }
 
  private String addMessage(String msg, SQLException sqle) {
    if(sqle.getMessage() != null) {
      return msg + ": " + sqle.getMessage();
    }
    else {
      return msg;
    }
  }
}

class DriverShim implements Driver {
  private Driver driver;
  DriverShim(Driver d) {
    this.driver = d;
  }
 
  public boolean acceptsURL(String u) throws SQLException {
    return this.driver.acceptsURL(u);
  }

  public Connection connect(String u, Properties p) throws SQLException {
    return this.driver.connect(u, p);
  }

  public int getMajorVersion() {
    return this.driver.getMajorVersion();
  }

  public int getMinorVersion() {
    return this.driver.getMinorVersion();
  }

  public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {
    return this.driver.getPropertyInfo(u, p);
  }

  public boolean jdbcCompliant() {
    return this.driver.jdbcCompliant();
  }

  public Logger getParentLogger() throws SQLFeatureNotSupportedException {
    // TODO Auto-generated method stub
    return null;
  }
}
TOP

Related Classes of org.rascalmpl.library.resource.jdbc.DriverShim

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.