예제 #1
0
파일: Result.java 프로젝트: jeremyevans/do
  /**
   * @param runtime
   * @param driver
   * @return
   */
  public static RubyClass createResultClass(final Ruby runtime, DriverDefinition driver) {
    RubyModule doModule = runtime.getModule(DATA_OBJECTS_MODULE_NAME);
    RubyClass superClass = doModule.getClass(RUBY_CLASS_NAME);
    RubyModule driverModule = (RubyModule) doModule.getConstant(driver.getModuleName());
    RubyClass resultClass =
        driverModule.defineClassUnder(RUBY_CLASS_NAME, superClass, RESULT_ALLOCATOR);

    resultClass.defineAnnotatedMethods(Result.class);
    return resultClass;
  }
예제 #2
0
 /**
  * @param runtime
  * @param factory
  * @return
  */
 public static RubyClass createCommandClass(final Ruby runtime, DriverDefinition factory) {
   RubyModule doModule = runtime.getModule(DATA_OBJECTS_MODULE_NAME);
   RubyClass superClass = doModule.getClass(RUBY_CLASS_NAME);
   RubyModule driverModule = (RubyModule) doModule.getConstant(factory.getModuleName());
   RubyClass commandClass =
       runtime.defineClassUnder("Command", superClass, COMMAND_ALLOCATOR, driverModule);
   commandClass.setInstanceVariable("@__factory", JavaEmbedUtils.javaToRuby(runtime, factory));
   commandClass.defineAnnotatedMethods(Command.class);
   setDriverDefinition(commandClass, runtime, factory);
   return commandClass;
 }
예제 #3
0
파일: Command.java 프로젝트: gryn/do
 /**
  * @param ps the PreparedStatement for which the parameter should be set
  * @param recv
  * @param arg a parameter value
  * @param idx the index of the parameter
  * @throws java.sql.SQLException
  */
 private static void setPreparedStatementParam(
     PreparedStatement ps, IRubyObject recv, IRubyObject arg, int idx) throws SQLException {
   Integer jdbcTypeId = null;
   try {
     jdbcTypeId = ps.getMetaData().getColumnType(idx);
   } catch (Exception ex) {
   }
   String rubyTypeName = arg.getType().getName();
   if ("Fixnum".equals(rubyTypeName)) {
     ps.setInt(idx, Integer.parseInt(arg.toString()));
   } else if ("Bignum".equals(rubyTypeName)) {
     ps.setLong(idx, ((RubyBignum) arg).getLongValue());
   } else if ("Float".equals(rubyTypeName)) {
     ps.setDouble(idx, RubyNumeric.num2dbl(arg));
   } else if ("BigDecimal".equals(rubyTypeName)) {
     ps.setBigDecimal(idx, ((RubyBigDecimal) arg).getValue());
   } else if ("NilClass".equals(rubyTypeName)) {
     // XXX In fact this should looks like ps.setNull(idx, Types.YYY);
     // where YYY is a JDBC type of column i.e. Types.VARCHAR
     // but this code works for MySQL :)
     ps.setNull(idx, Types.NULL);
   } else if ("TrueClass".equals(rubyTypeName) || "FalseClass".equals(rubyTypeName)) {
     ps.setBoolean(idx, arg.toString().equals("true"));
   } else if ("Class".equals(rubyTypeName)) {
     ps.setString(idx, arg.toString());
   } else if ("Extlib::ByteArray".equals(rubyTypeName)) {
     ps.setBytes(idx, ((RubyString) arg).getBytes());
     // TODO: add support for ps.setBlob();
   } else if ("Date".equals(rubyTypeName)) {
     ps.setDate(idx, java.sql.Date.valueOf(arg.toString()));
   } else if ("Time".equals(rubyTypeName)) {
     RubyTime rubyTime = (RubyTime) arg;
     java.util.Date date = rubyTime.getJavaDate();
     GregorianCalendar cal = new GregorianCalendar();
     cal.setTime(date);
     cal.setTimeZone(
         TimeZone.getTimeZone("UTC")); // XXX works only if driver suports Calendars in PS
     java.sql.Timestamp ts;
     if (driver.supportsCalendarsInJDBCPreparedStatement() == true) {
       ts = new java.sql.Timestamp(cal.getTime().getTime());
       ts.setNanos(cal.get(GregorianCalendar.MILLISECOND) * 100000);
     } else {
       // XXX ugly workaround for MySQL and Hsqldb
       ts =
           new Timestamp(
               cal.get(GregorianCalendar.YEAR) - 1900,
               cal.get(GregorianCalendar.MONTH),
               cal.get(GregorianCalendar.DAY_OF_MONTH),
               cal.get(GregorianCalendar.HOUR_OF_DAY),
               cal.get(GregorianCalendar.MINUTE),
               cal.get(GregorianCalendar.SECOND),
               cal.get(GregorianCalendar.MILLISECOND) * 100000);
     }
     ps.setTimestamp(idx, ts, cal);
   } else if ("DateTime".equals(rubyTypeName)) {
     ps.setTimestamp(
         idx,
         java.sql.Timestamp.valueOf(
             arg.toString().replace('T', ' ').replaceFirst("[-+]..:..$", "")));
   } else if (arg.toString().indexOf("-") != -1 && arg.toString().indexOf(":") != -1) {
     // TODO: improve the above string pattern checking
     // Handle date patterns in strings
     java.util.Date parsedDate;
     try {
       parsedDate = FORMAT.parse(arg.asJavaString().replace('T', ' '));
       java.sql.Timestamp timestamp = new java.sql.Timestamp(parsedDate.getTime());
       ps.setTimestamp(idx, timestamp);
     } catch (ParseException ex) {
       ps.setString(idx, api.convertToRubyString(arg).getUnicodeValue());
     }
   } else if (arg.toString().indexOf(":") != -1 && arg.toString().length() == 8) {
     // Handle time patterns in strings
     ps.setTime(idx, java.sql.Time.valueOf(arg.asJavaString()));
   } else {
     if (jdbcTypeId == null) {
       ps.setString(idx, api.convertToRubyString(arg).getUnicodeValue());
     } else {
       // TODO: Here comes conversions like '.execute_reader("2")'
       // It definitly needs to be refactored...
       try {
         if (jdbcTypeId == Types.VARCHAR) {
           ps.setString(idx, api.convertToRubyString(arg).getUnicodeValue());
         } else if (jdbcTypeId == Types.INTEGER) {
           ps.setObject(idx, Integer.valueOf(arg.toString()), jdbcTypeId);
         } else {
           // I'm not sure is it correct in 100%
           ps.setString(idx, api.convertToRubyString(arg).getUnicodeValue());
         }
       } catch (NumberFormatException ex) { // i.e Integer.valueOf
         ps.setString(idx, api.convertToRubyString(arg).getUnicodeValue());
       }
     }
   }
 }
예제 #4
0
파일: Command.java 프로젝트: gryn/do
  @JRubyMethod(optional = 1, rest = true)
  public static IRubyObject execute_non_query(IRubyObject recv, IRubyObject[] args) {
    Ruby runtime = recv.getRuntime();
    IRubyObject connection_instance = api.getInstanceVariable(recv, "@connection");
    IRubyObject wrapped_jdbc_connection =
        api.getInstanceVariable(connection_instance, "@connection");
    if (wrapped_jdbc_connection.isNil()) {
      throw DataObjectsUtils.newDriverError(
          runtime, errorName, "This connection has already been closed.");
    }
    java.sql.Connection conn = getConnection(wrapped_jdbc_connection);

    IRubyObject insert_key = runtime.getNil();
    RubyClass resultClass = Result.createResultClass(runtime, moduleName, errorName, driver);
    // affectedCount == 1 means 1 updated row
    // or 1 row in result set that represents returned key (insert...returning),
    // other values represents numer of updated rows
    int affectedCount = 0;
    PreparedStatement sqlStatement = null;
    java.sql.ResultSet keys = null;

    // String sqlText = prepareSqlTextForPs(api.getInstanceVariable(recv, "@text").asJavaString(),
    // recv, args);
    String doSqlText =
        api.convertToRubyString(api.getInstanceVariable(recv, "@text")).getUnicodeValue();
    String sqlText = prepareSqlTextForPs(doSqlText, recv, args);

    try {
      if (driver.supportsConnectionPrepareStatementMethodWithGKFlag()) {
        sqlStatement =
            conn.prepareStatement(
                sqlText,
                driver.supportsJdbcGeneratedKeys()
                    ? Statement.RETURN_GENERATED_KEYS
                    : Statement.NO_GENERATED_KEYS);
      } else {
        // If java.sql.PreparedStatement#getGeneratedKeys() is not supported,
        // then it is important to call java.sql.Connection#prepareStatement(String)
        // -- with just a single parameter -- rather java.sql.Connection#
        // prepareStatement(String, int) (and passing in Statement.NO_GENERATED_KEYS).
        // Some less-than-complete JDBC drivers do not implement all of
        // the overloaded prepareStatement methods: the main culprit
        // being SQLiteJDBC which currently throws an ugly (and cryptic)
        // "NYI" SQLException if Connection#prepareStatement(String, int)
        // is called.
        sqlStatement = conn.prepareStatement(sqlText);
      }

      prepareStatementFromArgs(sqlStatement, recv, args);

      // javaConn.setAutoCommit(true); // hangs with autocommit set to false
      // sqlStatement.setMaxRows();
      long startTime = System.currentTimeMillis();
      try {
        if (sqlText.contains("RETURNING")) {
          keys = sqlStatement.executeQuery();
        } else {
          affectedCount = sqlStatement.executeUpdate();
        }
      } catch (SQLException sqle) {
        // This is to handle the edge case of SELECT sleep(1):
        // an executeUpdate() will throw a SQLException if a SELECT
        // is passed, so we try the same query again with execute()
        affectedCount = 0;
        sqlStatement.execute();
      }
      long endTime = System.currentTimeMillis();

      debug(recv.getRuntime(), driver.toString(sqlStatement), Long.valueOf(endTime - startTime));

      if (keys == null) {
        if (driver.supportsJdbcGeneratedKeys()) {
          // Derby, H2, and MySQL all support getGeneratedKeys(), but only
          // to varying extents.
          //
          // However, javaConn.getMetaData().supportsGetGeneratedKeys()
          // currently returns FALSE for the Derby driver, as its support
          // is limited. As such, we use supportsJdbcGeneratedKeys() from
          // our own driver definition.
          //
          // See http://issues.apache.org/jira/browse/DERBY-242
          // See http://issues.apache.org/jira/browse/DERBY-2631
          // (Derby only supplies getGeneratedKeys() for auto-incremented
          // columns)
          //

          // apparently the prepared statements always provide the
          // generated keys
          keys = sqlStatement.getGeneratedKeys();

        } else {
          // If there is no support, then a custom method can be defined
          // to return a ResultSet with keys
          keys = driver.getGeneratedKeys(conn);
        }
      }
      if (keys != null) {
        insert_key = unmarshal_id_result(runtime, keys);
        affectedCount = (affectedCount > 0) ? affectedCount : 1;
      }

      // not needed as it will be closed in the finally clause
      //            sqlStatement.close();
      //            sqlStatement = null;
    } catch (SQLException sqle) {
      // TODO: log
      // sqle.printStackTrace();
      throw newQueryError(runtime, sqle, sqlStatement);
    } finally {
      if (sqlStatement != null) {
        try {
          sqlStatement.close();
        } catch (SQLException sqle2) {
        }
      }
    }

    // return nil if no updates are made
    if (affectedCount <= 0) {
      return runtime.getNil();
    }

    IRubyObject affected_rows = runtime.newFixnum(affectedCount);

    IRubyObject result =
        api.callMethod(resultClass, "new", new IRubyObject[] {recv, affected_rows, insert_key});
    return result;
  }
예제 #5
0
파일: Command.java 프로젝트: gryn/do
  @JRubyMethod(optional = 1, rest = true)
  public static IRubyObject execute_reader(IRubyObject recv, IRubyObject[] args) {
    Ruby runtime = recv.getRuntime();
    IRubyObject connection_instance = api.getInstanceVariable(recv, "@connection");
    IRubyObject wrapped_jdbc_connection =
        api.getInstanceVariable(connection_instance, "@connection");
    if (wrapped_jdbc_connection.isNil()) {
      throw DataObjectsUtils.newDriverError(
          runtime, errorName, "This connection has already been closed.");
    }
    java.sql.Connection conn = getConnection(wrapped_jdbc_connection);

    RubyClass readerClass = Reader.createReaderClass(runtime, moduleName, errorName, driver);
    boolean inferTypes = false;
    int columnCount = 0;
    PreparedStatement sqlStatement = null;
    ResultSet resultSet = null;
    ResultSetMetaData metaData = null;

    // instantiate a new reader
    IRubyObject reader =
        readerClass.newInstance(
            runtime.getCurrentContext(), new IRubyObject[] {}, Block.NULL_BLOCK);

    // execute the query
    try {
      String sqlText =
          prepareSqlTextForPs(api.getInstanceVariable(recv, "@text").asJavaString(), recv, args);

      sqlStatement =
          conn.prepareStatement(
              sqlText,
              driver.supportsJdbcScrollableResultSets()
                  ? ResultSet.TYPE_SCROLL_INSENSITIVE
                  : ResultSet.TYPE_FORWARD_ONLY,
              ResultSet.CONCUR_READ_ONLY);

      // sqlStatement.setMaxRows();
      prepareStatementFromArgs(sqlStatement, recv, args);

      long startTime = System.currentTimeMillis();
      resultSet = sqlStatement.executeQuery();
      long endTime = System.currentTimeMillis();

      debug(recv.getRuntime(), driver.toString(sqlStatement), Long.valueOf(endTime - startTime));

      metaData = resultSet.getMetaData();
      columnCount = metaData.getColumnCount();

      // pass the response to the reader
      IRubyObject wrappedResultSet =
          Java.java_to_ruby(recv, JavaObject.wrap(recv.getRuntime(), resultSet), Block.NULL_BLOCK);
      reader.getInstanceVariables().setInstanceVariable("@reader", wrappedResultSet);

      wrappedResultSet.dataWrapStruct(resultSet);

      // handle each result

      // mark the reader as opened
      api.setInstanceVariable(reader, "@opened", runtime.newBoolean(true));
      // TODO: if no response return nil

      api.setInstanceVariable(reader, "@position", runtime.newFixnum(0));

      // save the field_count in reader
      api.setInstanceVariable(reader, "@field_count", runtime.newFixnum(columnCount));

      // get the field types
      RubyArray field_names = runtime.newArray();
      IRubyObject field_types = api.getInstanceVariable(recv, "@field_types");

      // If no types are passed in, infer them
      if (field_types == null) {
        field_types = runtime.newArray();
        inferTypes = true;
      } else {
        int fieldTypesCount = field_types.convertToArray().getLength();
        if (field_types.isNil() || fieldTypesCount == 0) {
          field_types = runtime.newArray();
          inferTypes = true;
        } else if (fieldTypesCount != columnCount) {
          // Wrong number of fields passed to set_types. Close the reader
          // and raise an error.
          api.callMethod(reader, "close");
          throw runtime.newArgumentError(
              String.format(
                  "Field-count mismatch. Expected %1$d fields, but the query yielded %2$d",
                  fieldTypesCount, columnCount));
        }
      }

      // for each field
      for (int i = 0; i < columnCount; i++) {
        RubyString field_name = runtime.newString(metaData.getColumnName(i + 1));
        // infer the type if no types passed
        field_names.push_m(new IRubyObject[] {field_name});

        if (inferTypes) {
          // TODO: do something
        }
      }

      // set the reader @field_names and @types (guessed or otherwise)
      api.setInstanceVariable(reader, "@fields", field_names);
      api.setInstanceVariable(reader, "@field_types", field_types);

      // keep the statement open

      // TODO why keep it open ???

      // sqlStatement.close();
      // sqlStatement = null;
    } catch (SQLException sqle) {
      // TODO: log sqle.printStackTrace();
      throw newQueryError(runtime, sqle, sqlStatement);
    } finally {
      // if (sqlStatement != null) {
      //    try {
      //        sqlStatement.close();
      //    } catch (SQLException stsqlex) {
      //    }
      // }
    }

    // return the reader
    return reader;
  }