activerecord-jdbc-adapter 60.0-java → 61.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.travis.yml +14 -11
- data/Gemfile +1 -1
- data/README.md +8 -7
- data/activerecord-jdbc-adapter.gemspec +1 -1
- data/lib/arel/visitors/postgresql_jdbc.rb +1 -1
- data/lib/arjdbc/abstract/core.rb +1 -0
- data/lib/arjdbc/abstract/database_statements.rb +6 -2
- data/lib/arjdbc/abstract/transaction_support.rb +20 -7
- data/lib/arjdbc/mysql/adapter.rb +14 -5
- data/lib/arjdbc/mysql/connection_methods.rb +6 -1
- data/lib/arjdbc/postgresql/adapter.rb +94 -63
- data/lib/arjdbc/postgresql/column.rb +1 -1
- data/lib/arjdbc/postgresql/connection_methods.rb +1 -0
- data/lib/arjdbc/postgresql/oid_types.rb +5 -4
- data/lib/arjdbc/sqlite3/adapter.rb +106 -56
- data/lib/arjdbc/sqlite3/connection_methods.rb +12 -1
- data/lib/arjdbc/tasks/databases.rake +15 -10
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/01-tomcat.rake +2 -2
- data/rakelib/rails.rake +1 -1
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +124 -41
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +51 -0
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +101 -36
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +3 -4
- data/src/java/arjdbc/util/DateTimeUtils.java +12 -4
- metadata +6 -7
    
        data/lib/arjdbc/version.rb
    CHANGED
    
    
    
        data/rakelib/01-tomcat.rake
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            namespace :'tomcat-jndi' do # contains a FS JNDI impl (for tests)
         | 
| 2 2 |  | 
| 3 | 
            -
              TOMCAT_MAVEN_REPO = ' | 
| 3 | 
            +
              TOMCAT_MAVEN_REPO = 'https://repo1.maven.org/maven2/org/apache/tomcat'
         | 
| 4 4 | 
             
              TOMCAT_VERSION = '7.0.54'
         | 
| 5 5 |  | 
| 6 6 | 
             
              DOWNLOAD_DIR = File.expand_path('../test/jars', File.dirname(__FILE__))
         | 
| @@ -48,4 +48,4 @@ namespace :'tomcat-jndi' do # contains a FS JNDI impl (for tests) | |
| 48 48 | 
             
                rm jar_path if File.exist?(jar_path)
         | 
| 49 49 | 
             
              end
         | 
| 50 50 |  | 
| 51 | 
            -
            end
         | 
| 51 | 
            +
            end
         | 
    
        data/rakelib/rails.rake
    CHANGED
    
    | @@ -51,7 +51,7 @@ namespace :rails do | |
| 51 51 | 
             
                  ruby_opts_string += " -C \"#{ar_path}\""
         | 
| 52 52 | 
             
                  ruby_opts_string += " -rbundler/setup"
         | 
| 53 53 | 
             
                  ruby_opts_string += " -rminitest -rminitest/excludes" unless ENV['NO_EXCLUDES'].eql?('true')
         | 
| 54 | 
            -
                  file_list = ENV["TEST"] ? FileList[ ENV["TEST"] ] : test_files_finder.call
         | 
| 54 | 
            +
                  file_list = ENV["TEST"] ? FileList[ ENV["TEST"].split(',') ] : test_files_finder.call
         | 
| 55 55 | 
             
                  file_list_string = file_list.map { |fn| "\"#{fn}\"" }.join(' ')
         | 
| 56 56 | 
             
                  # test_loader_code = "-e \"ARGV.each{|f| require f}\"" # :direct
         | 
| 57 57 | 
             
                  option_list = ( ENV["TESTOPTS"] || ENV["TESTOPT"] || ENV["TEST_OPTS"] || '' )
         | 
| @@ -86,6 +86,7 @@ import org.jruby.anno.JRubyMethod; | |
| 86 86 | 
             
            import org.jruby.exceptions.RaiseException;
         | 
| 87 87 | 
             
            import org.jruby.ext.bigdecimal.RubyBigDecimal;
         | 
| 88 88 | 
             
            import org.jruby.ext.date.RubyDate;
         | 
| 89 | 
            +
            import org.jruby.ext.date.RubyDateTime;
         | 
| 89 90 | 
             
            import org.jruby.javasupport.JavaEmbedUtils;
         | 
| 90 91 | 
             
            import org.jruby.javasupport.JavaUtil;
         | 
| 91 92 | 
             
            import org.jruby.runtime.Block;
         | 
| @@ -124,6 +125,7 @@ public class RubyJdbcConnection extends RubyObject { | |
| 124 125 | 
             
                private IRubyObject config;
         | 
| 125 126 | 
             
                private IRubyObject adapter; // the AbstractAdapter instance we belong to
         | 
| 126 127 | 
             
                private volatile boolean connected = true;
         | 
| 128 | 
            +
                private RubyClass attributeClass;
         | 
| 127 129 |  | 
| 128 130 | 
             
                private boolean lazy = false; // final once set on initialize
         | 
| 129 131 | 
             
                private boolean jndi; // final once set on initialize
         | 
| @@ -132,6 +134,7 @@ public class RubyJdbcConnection extends RubyObject { | |
| 132 134 |  | 
| 133 135 | 
             
                protected RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
         | 
| 134 136 | 
             
                    super(runtime, metaClass);
         | 
| 137 | 
            +
                    attributeClass = runtime.getModule("ActiveModel").getClass("Attribute");
         | 
| 135 138 | 
             
                }
         | 
| 136 139 |  | 
| 137 140 | 
             
                private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
         | 
| @@ -359,7 +362,7 @@ public class RubyJdbcConnection extends RubyObject { | |
| 359 362 | 
             
                        if ( ! connection.getAutoCommit() ) {
         | 
| 360 363 | 
             
                            try {
         | 
| 361 364 | 
             
                                connection.commit();
         | 
| 362 | 
            -
                                resetSavepoints(context); // if any
         | 
| 365 | 
            +
                                resetSavepoints(context, connection); // if any
         | 
| 363 366 | 
             
                                return context.runtime.newBoolean(true);
         | 
| 364 367 | 
             
                            }
         | 
| 365 368 | 
             
                            finally {
         | 
| @@ -380,7 +383,7 @@ public class RubyJdbcConnection extends RubyObject { | |
| 380 383 | 
             
                        if ( ! connection.getAutoCommit() ) {
         | 
| 381 384 | 
             
                            try {
         | 
| 382 385 | 
             
                                connection.rollback();
         | 
| 383 | 
            -
                                resetSavepoints(context); // if any
         | 
| 386 | 
            +
                                resetSavepoints(context, connection); // if any
         | 
| 384 387 | 
             
                                return context.tru;
         | 
| 385 388 | 
             
                            } finally {
         | 
| 386 389 | 
             
                                connection.setAutoCommit(true);
         | 
| @@ -516,7 +519,7 @@ public class RubyJdbcConnection extends RubyObject { | |
| 516 519 | 
             
                    return null;
         | 
| 517 520 | 
             
                }
         | 
| 518 521 |  | 
| 519 | 
            -
                protected boolean resetSavepoints(final ThreadContext context) {
         | 
| 522 | 
            +
                protected boolean resetSavepoints(final ThreadContext context, final Connection connection) throws SQLException {
         | 
| 520 523 | 
             
                    if ( hasInternalVariable("savepoints") ) {
         | 
| 521 524 | 
             
                        removeInternalVariable("savepoints");
         | 
| 522 525 | 
             
                        return true;
         | 
| @@ -610,11 +613,7 @@ public class RubyJdbcConnection extends RubyObject { | |
| 610 613 | 
             
                            return convertJavaToRuby( connection.unwrap(Connection.class) );
         | 
| 611 614 | 
             
                        }
         | 
| 612 615 | 
             
                    }
         | 
| 613 | 
            -
                    catch (AbstractMethodError e) {
         | 
| 614 | 
            -
                        debugStackTrace(context, e);
         | 
| 615 | 
            -
                        warn(context, "driver/pool connection does not support unwrapping: " + e);
         | 
| 616 | 
            -
                    }
         | 
| 617 | 
            -
                    catch (SQLException e) {
         | 
| 616 | 
            +
                    catch (AbstractMethodError | SQLException e) {
         | 
| 618 617 | 
             
                        debugStackTrace(context, e);
         | 
| 619 618 | 
             
                        warn(context, "driver/pool connection does not support unwrapping: " + e);
         | 
| 620 619 | 
             
                    }
         | 
| @@ -676,7 +675,10 @@ public class RubyJdbcConnection extends RubyObject { | |
| 676 675 |  | 
| 677 676 | 
             
                private void connectImpl(final boolean forceConnection) throws SQLException {
         | 
| 678 677 | 
             
                    setConnection( forceConnection ? newConnection() : null );
         | 
| 679 | 
            -
                    if ( | 
| 678 | 
            +
                    if (forceConnection) {
         | 
| 679 | 
            +
                        if (getConnectionImpl() == null) throw new SQLException("Didn't get a connection. Wrong URL?");
         | 
| 680 | 
            +
                        configureConnection();
         | 
| 681 | 
            +
                    }
         | 
| 680 682 | 
             
                }
         | 
| 681 683 |  | 
| 682 684 | 
             
                @JRubyMethod(name = "read_only?")
         | 
| @@ -832,24 +834,45 @@ public class RubyJdbcConnection extends RubyObject { | |
| 832 834 | 
             
                    return mapQueryResult(context, connection, resultSet);
         | 
| 833 835 | 
             
                }
         | 
| 834 836 |  | 
| 837 | 
            +
                private static String[] createStatementPk(IRubyObject pk) {
         | 
| 838 | 
            +
                    String[] statementPk;
         | 
| 839 | 
            +
                    if (pk instanceof RubyArray) {
         | 
| 840 | 
            +
                        RubyArray ary = (RubyArray) pk;
         | 
| 841 | 
            +
                        int size = ary.size();
         | 
| 842 | 
            +
                        statementPk = new String[size];
         | 
| 843 | 
            +
                        for (int i = 0; i < size; i++) {
         | 
| 844 | 
            +
                            statementPk[i] = sqlString(ary.eltInternal(i));
         | 
| 845 | 
            +
                        }
         | 
| 846 | 
            +
                    } else {
         | 
| 847 | 
            +
                        statementPk = new String[] { sqlString(pk) };
         | 
| 848 | 
            +
                    }
         | 
| 849 | 
            +
                    return statementPk;
         | 
| 850 | 
            +
                }
         | 
| 851 | 
            +
             | 
| 835 852 | 
             
                /**
         | 
| 836 853 | 
             
                 * Executes an INSERT SQL statement
         | 
| 837 854 | 
             
                 * @param context
         | 
| 838 855 | 
             
                 * @param sql
         | 
| 856 | 
            +
                 * @param pk Rails PK
         | 
| 839 857 | 
             
                 * @return ActiveRecord::Result
         | 
| 840 858 | 
             
                 * @throws SQLException
         | 
| 841 859 | 
             
                 */
         | 
| 842 | 
            -
                @JRubyMethod(name = " | 
| 843 | 
            -
                public IRubyObject  | 
| 860 | 
            +
                @JRubyMethod(name = "execute_insert_pk", required = 2)
         | 
| 861 | 
            +
                public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject pk) {
         | 
| 844 862 | 
             
                    return withConnection(context, connection -> {
         | 
| 845 863 | 
             
                        Statement statement = null;
         | 
| 846 864 | 
             
                        final String query = sqlString(sql);
         | 
| 847 865 | 
             
                        try {
         | 
| 848 866 |  | 
| 849 867 | 
             
                            statement = createStatement(context, connection);
         | 
| 850 | 
            -
                            statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
         | 
| 851 | 
            -
                            return mapGeneratedKeys(context, connection, statement);
         | 
| 852 868 |  | 
| 869 | 
            +
                            if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
         | 
| 870 | 
            +
                                statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
         | 
| 871 | 
            +
                            } else {
         | 
| 872 | 
            +
                                statement.executeUpdate(query, createStatementPk(pk));
         | 
| 873 | 
            +
                            }
         | 
| 874 | 
            +
             | 
| 875 | 
            +
                            return mapGeneratedKeys(context, connection, statement);
         | 
| 853 876 | 
             
                        } catch (final SQLException e) {
         | 
| 854 877 | 
             
                            debugErrorSQL(context, query);
         | 
| 855 878 | 
             
                            throw e;
         | 
| @@ -859,26 +882,37 @@ public class RubyJdbcConnection extends RubyObject { | |
| 859 882 | 
             
                    });
         | 
| 860 883 | 
             
                }
         | 
| 861 884 |  | 
| 885 | 
            +
                @Deprecated
         | 
| 886 | 
            +
                @JRubyMethod(name = "execute_insert", required = 1)
         | 
| 887 | 
            +
                public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql) {
         | 
| 888 | 
            +
                    return execute_insert_pk(context, sql, context.nil);
         | 
| 889 | 
            +
                }
         | 
| 890 | 
            +
             | 
| 862 891 | 
             
                /**
         | 
| 863 892 | 
             
                 * Executes an INSERT SQL statement using a prepared statement
         | 
| 864 893 | 
             
                 * @param context
         | 
| 865 894 | 
             
                 * @param sql
         | 
| 866 895 | 
             
                 * @param binds RubyArray of values to be bound to the query
         | 
| 896 | 
            +
                 * @param pk Rails PK
         | 
| 867 897 | 
             
                 * @return ActiveRecord::Result
         | 
| 868 898 | 
             
                 * @throws SQLException
         | 
| 869 899 | 
             
                 */
         | 
| 870 | 
            -
                @JRubyMethod(name = " | 
| 871 | 
            -
                public IRubyObject  | 
| 900 | 
            +
                @JRubyMethod(name = "execute_insert_pk", required = 3)
         | 
| 901 | 
            +
                public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject binds,
         | 
| 902 | 
            +
                                                     final IRubyObject pk) {
         | 
| 872 903 | 
             
                    return withConnection(context, connection -> {
         | 
| 873 904 | 
             
                        PreparedStatement statement = null;
         | 
| 874 905 | 
             
                        final String query = sqlString(sql);
         | 
| 875 906 | 
             
                        try {
         | 
| 907 | 
            +
                            if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
         | 
| 908 | 
            +
                                statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
         | 
| 909 | 
            +
                            } else {
         | 
| 910 | 
            +
                                statement = connection.prepareStatement(query, createStatementPk(pk));
         | 
| 911 | 
            +
                            }
         | 
| 876 912 |  | 
| 877 | 
            -
                            statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
         | 
| 878 913 | 
             
                            setStatementParameters(context, connection, statement, (RubyArray) binds);
         | 
| 879 914 | 
             
                            statement.executeUpdate();
         | 
| 880 915 | 
             
                            return mapGeneratedKeys(context, connection, statement);
         | 
| 881 | 
            -
             | 
| 882 916 | 
             
                        } catch (final SQLException e) {
         | 
| 883 917 | 
             
                            debugErrorSQL(context, query);
         | 
| 884 918 | 
             
                            throw e;
         | 
| @@ -888,6 +922,12 @@ public class RubyJdbcConnection extends RubyObject { | |
| 888 922 | 
             
                    });
         | 
| 889 923 | 
             
                }
         | 
| 890 924 |  | 
| 925 | 
            +
                @Deprecated
         | 
| 926 | 
            +
                @JRubyMethod(name = "execute_insert", required = 2)
         | 
| 927 | 
            +
                public IRubyObject execute_insert(final ThreadContext context, final IRubyObject binds, final IRubyObject sql) {
         | 
| 928 | 
            +
                    return execute_insert_pk(context, sql, binds, context.nil);
         | 
| 929 | 
            +
                }
         | 
| 930 | 
            +
             | 
| 891 931 | 
             
                /**
         | 
| 892 932 | 
             
                 * Executes an UPDATE (DELETE) SQL statement
         | 
| 893 933 | 
             
                 * @param context
         | 
| @@ -967,12 +1007,12 @@ public class RubyJdbcConnection extends RubyObject { | |
| 967 1007 | 
             
                                binds = null;
         | 
| 968 1008 | 
             
                            } else {                              // (sql, binds)
         | 
| 969 1009 | 
             
                                maxRows = 0;
         | 
| 970 | 
            -
                                binds = (RubyArray) TypeConverter.checkArrayType(args[1]);
         | 
| 1010 | 
            +
                                binds = (RubyArray) TypeConverter.checkArrayType(context, args[1]);
         | 
| 971 1011 | 
             
                            }
         | 
| 972 1012 | 
             
                            break;
         | 
| 973 1013 | 
             
                        case 3:                                   // (sql, max_rows, binds)
         | 
| 974 1014 | 
             
                            maxRows = RubyNumeric.fix2int(args[1]);
         | 
| 975 | 
            -
                            binds = (RubyArray) TypeConverter.checkArrayType(args[2]);
         | 
| 1015 | 
            +
                            binds = (RubyArray) TypeConverter.checkArrayType(context, args[2]);
         | 
| 976 1016 | 
             
                            break;
         | 
| 977 1017 | 
             
                        default:                                  // (sql) 1-arg
         | 
| 978 1018 | 
             
                            maxRows = 0;
         | 
| @@ -1061,6 +1101,28 @@ public class RubyJdbcConnection extends RubyObject { | |
| 1061 1101 | 
             
                    });
         | 
| 1062 1102 | 
             
                }
         | 
| 1063 1103 |  | 
| 1104 | 
            +
                @JRubyMethod(required = 1)
         | 
| 1105 | 
            +
                public IRubyObject get_first_value(final ThreadContext context, final IRubyObject sql) {
         | 
| 1106 | 
            +
                    return withConnection(context, connection -> {
         | 
| 1107 | 
            +
                        Statement statement = null;
         | 
| 1108 | 
            +
                        final String query = sqlString(sql);
         | 
| 1109 | 
            +
                        try {
         | 
| 1110 | 
            +
                            statement = createStatement(context, connection);
         | 
| 1111 | 
            +
                            statement.execute(query);
         | 
| 1112 | 
            +
                            ResultSet rs = statement.getResultSet();
         | 
| 1113 | 
            +
                            if (rs == null || !rs.next()) return context.nil;
         | 
| 1114 | 
            +
             | 
| 1115 | 
            +
                            return jdbcToRuby(context, context.getRuntime(), 1, rs.getMetaData().getColumnType(1), rs);
         | 
| 1116 | 
            +
             | 
| 1117 | 
            +
                        } catch (final SQLException e) {
         | 
| 1118 | 
            +
                            debugErrorSQL(context, query);
         | 
| 1119 | 
            +
                            throw e;
         | 
| 1120 | 
            +
                        } finally {
         | 
| 1121 | 
            +
                            close(statement);
         | 
| 1122 | 
            +
                        }
         | 
| 1123 | 
            +
                    });
         | 
| 1124 | 
            +
                }
         | 
| 1125 | 
            +
             | 
| 1064 1126 | 
             
                /**
         | 
| 1065 1127 | 
             
                 * Prepares a query, returns a wrapped PreparedStatement. This takes care of exception wrapping
         | 
| 1066 1128 | 
             
                 * @param context which context this method is executing on.
         | 
| @@ -2120,7 +2182,7 @@ public class RubyJdbcConnection extends RubyObject { | |
| 2120 2182 | 
             
                        return RubyString.newString(runtime, DateTimeUtils.dateToString(value));
         | 
| 2121 2183 | 
             
                    }
         | 
| 2122 2184 |  | 
| 2123 | 
            -
                    return DateTimeUtils.newDateAsTime(context, value,  | 
| 2185 | 
            +
                    return DateTimeUtils.newDateAsTime(context, value, DateTimeZone.UTC).callMethod(context, "to_date");
         | 
| 2124 2186 | 
             
                }
         | 
| 2125 2187 |  | 
| 2126 2188 | 
             
                protected IRubyObject timeToRuby(final ThreadContext context,
         | 
| @@ -2357,9 +2419,16 @@ public class RubyJdbcConnection extends RubyObject { | |
| 2357 2419 | 
             
                        final Connection connection, final PreparedStatement statement,
         | 
| 2358 2420 | 
             
                        final int index, IRubyObject attribute) throws SQLException {
         | 
| 2359 2421 |  | 
| 2360 | 
            -
                     | 
| 2361 | 
            -
                    final int type | 
| 2362 | 
            -
             | 
| 2422 | 
            +
                    final IRubyObject value;
         | 
| 2423 | 
            +
                    final int type;
         | 
| 2424 | 
            +
             | 
| 2425 | 
            +
                    if (attributeClass.isInstance(attribute)) {
         | 
| 2426 | 
            +
                        type = jdbcTypeForAttribute(context, attribute);
         | 
| 2427 | 
            +
                        value = valueForDatabase(context, attribute);
         | 
| 2428 | 
            +
                    } else {
         | 
| 2429 | 
            +
                        type = jdbcTypeForPrimitiveAttribute(context, attribute);
         | 
| 2430 | 
            +
                        value = attribute;
         | 
| 2431 | 
            +
                    }
         | 
| 2363 2432 |  | 
| 2364 2433 | 
             
                    // All the set methods were calling this first so save a method call in the nil case
         | 
| 2365 2434 | 
             
                    if ( value == context.nil ) {
         | 
| @@ -2475,6 +2544,34 @@ public class RubyJdbcConnection extends RubyObject { | |
| 2475 2544 | 
             
                    return Types.OTHER; // -1 as well as 0 are used in Types
         | 
| 2476 2545 | 
             
                }
         | 
| 2477 2546 |  | 
| 2547 | 
            +
                protected String internedTypeForPrimitive(final ThreadContext context, final IRubyObject value) throws SQLException {
         | 
| 2548 | 
            +
                    if (value instanceof RubyString) {
         | 
| 2549 | 
            +
                        return "string";
         | 
| 2550 | 
            +
                    }
         | 
| 2551 | 
            +
                    if (value instanceof RubyInteger) {
         | 
| 2552 | 
            +
                        return "integer";
         | 
| 2553 | 
            +
                    }
         | 
| 2554 | 
            +
                    if (value instanceof RubyNumeric) {
         | 
| 2555 | 
            +
                        return "float";
         | 
| 2556 | 
            +
                    }
         | 
| 2557 | 
            +
                    if (value instanceof RubyTime || value instanceof RubyDateTime) {
         | 
| 2558 | 
            +
                        return "timestamp";
         | 
| 2559 | 
            +
                    }
         | 
| 2560 | 
            +
                    if (value instanceof RubyDate) {
         | 
| 2561 | 
            +
                        return "date";
         | 
| 2562 | 
            +
                    }
         | 
| 2563 | 
            +
                    if (value instanceof RubyBoolean) {
         | 
| 2564 | 
            +
                        return "boolean";
         | 
| 2565 | 
            +
                    }
         | 
| 2566 | 
            +
                    return "string";
         | 
| 2567 | 
            +
                }
         | 
| 2568 | 
            +
             | 
| 2569 | 
            +
                protected Integer jdbcTypeForPrimitiveAttribute(final ThreadContext context,
         | 
| 2570 | 
            +
                                                                final IRubyObject attribute) throws SQLException {
         | 
| 2571 | 
            +
                    final String internedType = internedTypeForPrimitive(context, attribute);
         | 
| 2572 | 
            +
                    return jdbcTypeFor(internedType);
         | 
| 2573 | 
            +
                }
         | 
| 2574 | 
            +
             | 
| 2478 2575 | 
             
                protected Integer jdbcTypeFor(final String type) {
         | 
| 2479 2576 | 
             
                    return JDBC_TYPE_FOR.get(type);
         | 
| 2480 2577 | 
             
                }
         | 
| @@ -2486,7 +2583,9 @@ public class RubyJdbcConnection extends RubyObject { | |
| 2486 2583 | 
             
                }
         | 
| 2487 2584 |  | 
| 2488 2585 | 
             
                protected static IRubyObject attributeSQLType(final ThreadContext context, final IRubyObject attribute) {
         | 
| 2489 | 
            -
                     | 
| 2586 | 
            +
                    final IRubyObject type = attributeType(context, attribute);
         | 
| 2587 | 
            +
                    if (type != null) return type.callMethod(context, "type");
         | 
| 2588 | 
            +
                    return context.nil;
         | 
| 2490 2589 | 
             
                }
         | 
| 2491 2590 |  | 
| 2492 2591 | 
             
                private final CachingCallSite value_site = new FunctionalCachingCallSite("value"); // AR::Attribute#value
         | 
| @@ -2499,23 +2598,7 @@ public class RubyJdbcConnection extends RubyObject { | |
| 2499 2598 |  | 
| 2500 2599 | 
             
                    final IRubyObject value = value_site.call(context, attribute, attribute);
         | 
| 2501 2600 |  | 
| 2502 | 
            -
                     | 
| 2503 | 
            -
                        return "integer";
         | 
| 2504 | 
            -
                    }
         | 
| 2505 | 
            -
             | 
| 2506 | 
            -
                    if (value instanceof RubyNumeric) {
         | 
| 2507 | 
            -
                        return "float";
         | 
| 2508 | 
            -
                    }
         | 
| 2509 | 
            -
             | 
| 2510 | 
            -
                    if (value instanceof RubyTime) {
         | 
| 2511 | 
            -
                        return "timestamp";
         | 
| 2512 | 
            -
                    }
         | 
| 2513 | 
            -
             | 
| 2514 | 
            -
                    if (value instanceof RubyBoolean) {
         | 
| 2515 | 
            -
                        return "boolean";
         | 
| 2516 | 
            -
                    }
         | 
| 2517 | 
            -
             | 
| 2518 | 
            -
                    return "string";
         | 
| 2601 | 
            +
                    return internedTypeForPrimitive(context, value);
         | 
| 2519 2602 | 
             
                }
         | 
| 2520 2603 |  | 
| 2521 2604 | 
             
                protected final RubyTime timeInDefaultTimeZone(final ThreadContext context, final IRubyObject value) {
         | 
| @@ -200,6 +200,57 @@ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection { | |
| 200 200 | 
             
                    });
         | 
| 201 201 | 
             
                }
         | 
| 202 202 |  | 
| 203 | 
            +
                /**
         | 
| 204 | 
            +
                 * Executes an INSERT SQL statement
         | 
| 205 | 
            +
                 * @param context
         | 
| 206 | 
            +
                 * @param sql
         | 
| 207 | 
            +
                 * @param pk Rails PK
         | 
| 208 | 
            +
                 * @return ActiveRecord::Result
         | 
| 209 | 
            +
                 * @throws SQLException
         | 
| 210 | 
            +
                 */
         | 
| 211 | 
            +
                @Override
         | 
| 212 | 
            +
                @JRubyMethod(name = "execute_insert_pk", required = 2)
         | 
| 213 | 
            +
                public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject pk) {
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                    // MSSQL does not like composite primary keys here so chop it if there is more than one column
         | 
| 216 | 
            +
                    IRubyObject modifiedPk = pk;
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                    if (pk instanceof RubyArray) {
         | 
| 219 | 
            +
                        RubyArray ary = (RubyArray) pk;
         | 
| 220 | 
            +
                        if (ary.size() > 0) {
         | 
| 221 | 
            +
                            modifiedPk = ary.eltInternal(0);
         | 
| 222 | 
            +
                        }
         | 
| 223 | 
            +
                    }
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                    return super.execute_insert_pk(context, sql, modifiedPk);
         | 
| 226 | 
            +
                }
         | 
| 227 | 
            +
             | 
| 228 | 
            +
                /**
         | 
| 229 | 
            +
                 * Executes an INSERT SQL statement using a prepared statement
         | 
| 230 | 
            +
                 * @param context
         | 
| 231 | 
            +
                 * @param sql
         | 
| 232 | 
            +
                 * @param binds RubyArray of values to be bound to the query
         | 
| 233 | 
            +
                 * @param pk Rails PK
         | 
| 234 | 
            +
                 * @return ActiveRecord::Result
         | 
| 235 | 
            +
                 * @throws SQLException
         | 
| 236 | 
            +
                 */
         | 
| 237 | 
            +
                @Override
         | 
| 238 | 
            +
                @JRubyMethod(name = "execute_insert_pk", required = 3)
         | 
| 239 | 
            +
                public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject binds,
         | 
| 240 | 
            +
                                                     final IRubyObject pk) {
         | 
| 241 | 
            +
                    // MSSQL does not like composite primary keys here so chop it if there is more than one column
         | 
| 242 | 
            +
                    IRubyObject modifiedPk = pk;
         | 
| 243 | 
            +
             | 
| 244 | 
            +
                    if (pk instanceof RubyArray) {
         | 
| 245 | 
            +
                        RubyArray ary = (RubyArray) pk;
         | 
| 246 | 
            +
                        if (ary.size() > 0) {
         | 
| 247 | 
            +
                            modifiedPk = ary.eltInternal(0);
         | 
| 248 | 
            +
                        }
         | 
| 249 | 
            +
                    }
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                    return super.execute_insert_pk(context, sql, binds, modifiedPk);
         | 
| 252 | 
            +
                }
         | 
| 253 | 
            +
             | 
| 203 254 | 
             
                @Override
         | 
| 204 255 | 
             
                protected Integer jdbcTypeFor(final String type) {
         | 
| 205 256 |  | 
| @@ -36,26 +36,18 @@ import java.io.ByteArrayInputStream; | |
| 36 36 | 
             
            import java.lang.StringBuilder;
         | 
| 37 37 | 
             
            import java.lang.reflect.InvocationTargetException;
         | 
| 38 38 | 
             
            import java.math.BigDecimal;
         | 
| 39 | 
            -
            import java.sql | 
| 40 | 
            -
            import java.sql. | 
| 41 | 
            -
            import java. | 
| 42 | 
            -
            import java.sql.ResultSet;
         | 
| 43 | 
            -
            import java.sql.ResultSetMetaData;
         | 
| 44 | 
            -
            import java.sql.SQLException;
         | 
| 45 | 
            -
            import java.sql.Timestamp;
         | 
| 46 | 
            -
            import java.sql.Types;
         | 
| 47 | 
            -
            import java.util.ArrayList;
         | 
| 48 | 
            -
            import java.util.Collections;
         | 
| 49 | 
            -
            import java.util.HashMap;
         | 
| 50 | 
            -
            import java.util.Map;
         | 
| 51 | 
            -
            import java.util.UUID;
         | 
| 39 | 
            +
            import java.sql.*;
         | 
| 40 | 
            +
            import java.sql.Date;
         | 
| 41 | 
            +
            import java.util.*;
         | 
| 52 42 | 
             
            import java.util.regex.Pattern;
         | 
| 53 43 | 
             
            import java.util.regex.Matcher;
         | 
| 54 44 |  | 
| 45 | 
            +
            import org.joda.time.DateTime;
         | 
| 55 46 | 
             
            import org.jruby.*;
         | 
| 56 47 | 
             
            import org.jruby.anno.JRubyMethod;
         | 
| 57 48 | 
             
            import org.jruby.exceptions.RaiseException;
         | 
| 58 49 | 
             
            import org.jruby.ext.bigdecimal.RubyBigDecimal;
         | 
| 50 | 
            +
            import org.jruby.ext.date.RubyDate;
         | 
| 59 51 | 
             
            import org.jruby.javasupport.JavaUtil;
         | 
| 60 52 | 
             
            import org.jruby.runtime.ObjectAllocator;
         | 
| 61 53 | 
             
            import org.jruby.runtime.ThreadContext;
         | 
| @@ -115,6 +107,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection | |
| 115 107 |  | 
| 116 108 | 
             
                // Used to wipe trailing 0's from points (3.0, 5.6) -> (3, 5.6)
         | 
| 117 109 | 
             
                private static final Pattern pointCleanerPattern = Pattern.compile("\\.0\\b");
         | 
| 110 | 
            +
                private static final TimeZone TZ_DEFAULT = TimeZone.getDefault();
         | 
| 118 111 |  | 
| 119 112 | 
             
                private RubyClass resultClass;
         | 
| 120 113 | 
             
                private RubyHash typeMap = null;
         | 
| @@ -204,9 +197,9 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection | |
| 204 197 | 
             
                    RubyClass arrayClass = oidArray(context);
         | 
| 205 198 | 
             
                    RubyBasicObject attributeType = (RubyBasicObject) attributeType(context, attribute);
         | 
| 206 199 | 
             
                    // The type or its delegate is an OID::Array
         | 
| 207 | 
            -
                    if (arrayClass.isInstance(attributeType) ||
         | 
| 200 | 
            +
                    if (attributeType != null && (arrayClass.isInstance(attributeType) ||
         | 
| 208 201 | 
             
                            (attributeType.hasInstanceVariable("@delegate_dc_obj") &&
         | 
| 209 | 
            -
                                    arrayClass.isInstance(attributeType.getInstanceVariable("@delegate_dc_obj")))) {
         | 
| 202 | 
            +
                                    arrayClass.isInstance(attributeType.getInstanceVariable("@delegate_dc_obj"))))) {
         | 
| 210 203 | 
             
                        return "array";
         | 
| 211 204 | 
             
                    }
         | 
| 212 205 |  | 
| @@ -386,7 +379,20 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection | |
| 386 379 | 
             
                        }
         | 
| 387 380 | 
             
                    }
         | 
| 388 381 |  | 
| 389 | 
            -
                     | 
| 382 | 
            +
                    if ( ! "Date".equals(value.getMetaClass().getName()) && value.respondsTo("to_date") ) {
         | 
| 383 | 
            +
                        value = value.callMethod(context, "to_date");
         | 
| 384 | 
            +
                    }
         | 
| 385 | 
            +
             | 
| 386 | 
            +
                    if (value instanceof RubyDate) {
         | 
| 387 | 
            +
                        RubyDate rubyDate = (RubyDate) value;
         | 
| 388 | 
            +
                        DateTime dt = rubyDate.getDateTime();
         | 
| 389 | 
            +
                        // pgjdbc needs adjustment for default JVM timezone
         | 
| 390 | 
            +
                        statement.setDate(index, new Date(dt.getMillis() - TZ_DEFAULT.getOffset(dt.getMillis())));
         | 
| 391 | 
            +
                        return;
         | 
| 392 | 
            +
                    }
         | 
| 393 | 
            +
             | 
| 394 | 
            +
                    // NOTE: assuming Date#to_s does right ...
         | 
| 395 | 
            +
                    statement.setDate(index, Date.valueOf(value.toString()));
         | 
| 390 396 | 
             
                }
         | 
| 391 397 |  | 
| 392 398 | 
             
                @Override
         | 
| @@ -430,7 +436,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection | |
| 430 436 | 
             
                            break;
         | 
| 431 437 |  | 
| 432 438 | 
             
                        case "interval":
         | 
| 433 | 
            -
                            statement.setObject(index,  | 
| 439 | 
            +
                            statement.setObject(index, stringToPGInterval(value.toString()));
         | 
| 434 440 | 
             
                            break;
         | 
| 435 441 |  | 
| 436 442 | 
             
                        case "json":
         | 
| @@ -487,6 +493,74 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection | |
| 487 493 | 
             
                    }
         | 
| 488 494 | 
             
                }
         | 
| 489 495 |  | 
| 496 | 
            +
                private int lookAhead(String value, int position, String find) {
         | 
| 497 | 
            +
                    char [] tokens = find.toCharArray();
         | 
| 498 | 
            +
                    int found = -1;
         | 
| 499 | 
            +
             | 
| 500 | 
            +
                    for ( int i = 0; i < tokens.length; i++ ) {
         | 
| 501 | 
            +
                        found = value.indexOf(tokens[i], position);
         | 
| 502 | 
            +
                        if ( found > 0 ) {
         | 
| 503 | 
            +
                            return found;
         | 
| 504 | 
            +
                        }
         | 
| 505 | 
            +
                    }
         | 
| 506 | 
            +
                    return found;
         | 
| 507 | 
            +
                }
         | 
| 508 | 
            +
             | 
| 509 | 
            +
                private Object stringToPGInterval(String value) throws SQLException {
         | 
| 510 | 
            +
                    if (!value.startsWith("P")) return new PGInterval(value);
         | 
| 511 | 
            +
             | 
| 512 | 
            +
                    PGInterval interval = new PGInterval();
         | 
| 513 | 
            +
             | 
| 514 | 
            +
                    /* this is copied from pgjdbc with fixes for Rails */
         | 
| 515 | 
            +
                    int number = 0;
         | 
| 516 | 
            +
                    String dateValue;
         | 
| 517 | 
            +
                    String timeValue = null;
         | 
| 518 | 
            +
             | 
| 519 | 
            +
                    int hasTime = value.indexOf('T');
         | 
| 520 | 
            +
                    if ( hasTime > 0 ) {
         | 
| 521 | 
            +
                      /* skip over the P */
         | 
| 522 | 
            +
                      dateValue = value.substring(1,hasTime);
         | 
| 523 | 
            +
                      timeValue = value.substring(hasTime + 1);
         | 
| 524 | 
            +
                    } else {
         | 
| 525 | 
            +
                      /* skip over the P */
         | 
| 526 | 
            +
                      dateValue = value.substring(1);
         | 
| 527 | 
            +
                    }
         | 
| 528 | 
            +
             | 
| 529 | 
            +
                    for ( int i = 0; i < dateValue.length(); i++ ) {
         | 
| 530 | 
            +
                      int lookAhead = lookAhead(dateValue, i, "YMD");
         | 
| 531 | 
            +
                      if (lookAhead > 0) {
         | 
| 532 | 
            +
                        char type = dateValue.charAt(lookAhead);
         | 
| 533 | 
            +
                        number = Integer.parseInt(dateValue.substring(i, lookAhead));
         | 
| 534 | 
            +
                        if (type == 'Y') {
         | 
| 535 | 
            +
                          interval.setYears(number);
         | 
| 536 | 
            +
                        } else if (type == 'M') {
         | 
| 537 | 
            +
                          interval.setMonths(number);
         | 
| 538 | 
            +
                        } else if (type == 'D') {
         | 
| 539 | 
            +
                          interval.setDays(number);
         | 
| 540 | 
            +
                        }
         | 
| 541 | 
            +
                        i = lookAhead;
         | 
| 542 | 
            +
                      }
         | 
| 543 | 
            +
                    }
         | 
| 544 | 
            +
                    if ( timeValue != null ) {
         | 
| 545 | 
            +
                      for (int i = 0; i < timeValue.length(); i++) {
         | 
| 546 | 
            +
                        int lookAhead = lookAhead(timeValue, i, "HMS");
         | 
| 547 | 
            +
                        if (lookAhead > 0) {
         | 
| 548 | 
            +
                          char type = timeValue.charAt(lookAhead);
         | 
| 549 | 
            +
                          String part = timeValue.substring(i, lookAhead);
         | 
| 550 | 
            +
                          if (timeValue.charAt(lookAhead) == 'H') {
         | 
| 551 | 
            +
                            interval.setHours(Integer.parseInt(part));
         | 
| 552 | 
            +
                          } else if (timeValue.charAt(lookAhead) == 'M') {
         | 
| 553 | 
            +
                            interval.setMinutes(Integer.parseInt(part));
         | 
| 554 | 
            +
                          } else if (timeValue.charAt(lookAhead) == 'S') {
         | 
| 555 | 
            +
                            interval.setSeconds(Double.parseDouble(part));
         | 
| 556 | 
            +
                          }
         | 
| 557 | 
            +
                          i = lookAhead;
         | 
| 558 | 
            +
                        }
         | 
| 559 | 
            +
                      }
         | 
| 560 | 
            +
                    }
         | 
| 561 | 
            +
                    return interval;
         | 
| 562 | 
            +
                }
         | 
| 563 | 
            +
             | 
| 490 564 | 
             
                protected IRubyObject jdbcToRuby(ThreadContext context, Ruby runtime, int column, int type, ResultSet resultSet) throws SQLException {
         | 
| 491 565 | 
             
                    return typeMap != null ?
         | 
| 492 566 | 
             
                            convertWithTypeMap(context, runtime, column, type, resultSet) :
         | 
| @@ -868,34 +942,25 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection | |
| 868 942 | 
             
                private static String formatInterval(final Object object) {
         | 
| 869 943 | 
             
                    final PGInterval interval = (PGInterval) object;
         | 
| 870 944 | 
             
                    final StringBuilder str = new StringBuilder(32);
         | 
| 945 | 
            +
                    str.append("P");
         | 
| 871 946 |  | 
| 872 947 | 
             
                    final int years = interval.getYears();
         | 
| 873 | 
            -
                    if (years != 0) str.append(years).append(" | 
| 948 | 
            +
                    if (years != 0) str.append(years).append("Y");
         | 
| 874 949 |  | 
| 875 950 | 
             
                    final int months = interval.getMonths();
         | 
| 876 | 
            -
                    if (months != 0) str.append(months).append(" | 
| 951 | 
            +
                    if (months != 0) str.append(months).append("M");
         | 
| 877 952 |  | 
| 878 953 | 
             
                    final int days = interval.getDays();
         | 
| 879 | 
            -
                    if (days != 0) str.append(days).append(" | 
| 954 | 
            +
                    if (days != 0) str.append(days).append("D");
         | 
| 880 955 |  | 
| 881 956 | 
             
                    final int hours = interval.getHours();
         | 
| 882 957 | 
             
                    final int mins = interval.getMinutes();
         | 
| 883 | 
            -
                    final  | 
| 884 | 
            -
                    if (hours != 0 || mins != 0 || secs != 0) { | 
| 885 | 
            -
                         | 
| 886 | 
            -
             | 
| 887 | 
            -
                        str.append( | 
| 888 | 
            -
             | 
| 889 | 
            -
                        if (mins < 10) str.append('0');
         | 
| 890 | 
            -
             | 
| 891 | 
            -
                        str.append(mins).append(':');
         | 
| 892 | 
            -
             | 
| 893 | 
            -
                        if (secs < 10) str.append('0');
         | 
| 894 | 
            -
             | 
| 895 | 
            -
                        str.append(secs);
         | 
| 896 | 
            -
             | 
| 897 | 
            -
                    } else if (str.length() > 1) {
         | 
| 898 | 
            -
                        str.deleteCharAt(str.length() - 1); // " " at the end
         | 
| 958 | 
            +
                    final double secs = interval.getSeconds();
         | 
| 959 | 
            +
                    if (hours != 0 || mins != 0 || secs != 0) {
         | 
| 960 | 
            +
                        str.append("T");
         | 
| 961 | 
            +
                        if (hours != 0) str.append(hours).append("H");
         | 
| 962 | 
            +
                        if (mins != 0) str.append(mins).append("M");
         | 
| 963 | 
            +
                        if (secs != 0) str.append(secs).append("S");
         | 
| 899 964 | 
             
                    }
         | 
| 900 965 |  | 
| 901 966 | 
             
                    return str.toString();
         |