activerecord-jdbc-alt-adapter 60.3.0-java → 61.0.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +11 -11
- data/Gemfile +1 -1
- data/README.md +16 -12
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/activerecord-jdbc-alt-adapter.gemspec +5 -5
- data/lib/arel/visitors/postgresql_jdbc.rb +1 -1
- data/lib/arel/visitors/sqlserver.rb +16 -0
- data/lib/arjdbc/abstract/core.rb +1 -0
- data/lib/arjdbc/abstract/database_statements.rb +4 -0
- data/lib/arjdbc/abstract/transaction_support.rb +20 -7
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/mssql/adapter.rb +3 -1
- data/lib/arjdbc/mssql/column.rb +14 -0
- data/lib/arjdbc/mssql/connection_methods.rb +0 -3
- data/lib/arjdbc/mssql/database_limits.rb +7 -0
- data/lib/arjdbc/mssql/extensions/attribute_methods.rb +1 -1
- data/lib/arjdbc/mssql/schema_creation.rb +2 -2
- data/lib/arjdbc/mssql/schema_statements.rb +29 -1
- data/lib/arjdbc/mssql.rb +1 -1
- data/lib/arjdbc/mysql/adapter.rb +14 -5
- data/lib/arjdbc/mysql/connection_methods.rb +5 -1
- data/lib/arjdbc/postgresql/adapter.rb +85 -73
- data/lib/arjdbc/postgresql/column.rb +1 -1
- data/lib/arjdbc/postgresql/oid_types.rb +4 -3
- data/lib/arjdbc/sqlite3/adapter.rb +95 -58
- data/lib/arjdbc/sqlite3/connection_methods.rb +11 -1
- data/lib/arjdbc/tasks/databases.rake +15 -10
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +88 -31
- data/lib/arjdbc/version.rb +3 -1
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +106 -68
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +82 -36
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +3 -4
- metadata +8 -7
@@ -1,18 +1,29 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'active_record/tasks/database_tasks'
|
4
4
|
|
5
5
|
module ArJdbc
|
6
|
-
module Tasks
|
7
|
-
class MSSQLDatabaseTasks
|
6
|
+
module Tasks # :nodoc:
|
7
|
+
class MSSQLDatabaseTasks # :nodoc:
|
8
|
+
delegate :connection, to: ActiveRecord::Base
|
9
|
+
delegate :establish_connection, to: ActiveRecord::Base
|
8
10
|
delegate :clear_active_connections!, to: ActiveRecord::Base
|
9
11
|
|
12
|
+
def self.using_database_configurations?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(db_config)
|
17
|
+
@db_config = db_config
|
18
|
+
@configuration_hash = db_config.configuration_hash
|
19
|
+
end
|
20
|
+
|
10
21
|
def create
|
11
22
|
establish_master_connection
|
12
|
-
connection.create_database(
|
13
|
-
establish_connection
|
14
|
-
rescue ActiveRecord::StatementInvalid =>
|
15
|
-
case
|
23
|
+
connection.create_database(db_config.database, creation_options)
|
24
|
+
establish_connection(db_config)
|
25
|
+
rescue ActiveRecord::StatementInvalid => e
|
26
|
+
case e.message
|
16
27
|
when /database .* already exists/i
|
17
28
|
raise ActiveRecord::Tasks::DatabaseAlreadyExists
|
18
29
|
else
|
@@ -22,7 +33,15 @@ module ArJdbc
|
|
22
33
|
|
23
34
|
def drop
|
24
35
|
establish_master_connection
|
25
|
-
connection.drop_database
|
36
|
+
connection.drop_database(db_config.database)
|
37
|
+
end
|
38
|
+
|
39
|
+
def charset
|
40
|
+
connection.charset
|
41
|
+
end
|
42
|
+
|
43
|
+
def collation
|
44
|
+
connection.collation
|
26
45
|
end
|
27
46
|
|
28
47
|
def purge
|
@@ -31,40 +50,78 @@ module ArJdbc
|
|
31
50
|
create
|
32
51
|
end
|
33
52
|
|
53
|
+
def structure_dump(filename, _extra_flags)
|
54
|
+
args = prepare_command_options
|
55
|
+
|
56
|
+
args.concat(["-f #{filename}"])
|
34
57
|
|
35
|
-
|
36
|
-
config = config_from_url_if_needed
|
37
|
-
`smoscript -s #{config['host']} -d #{config['database']} -u #{config['username']} -p #{config['password']} -f #{filename} -A -U`
|
58
|
+
run_cmd('mssql-scripter', args, 'dumping')
|
38
59
|
end
|
39
60
|
|
40
|
-
def structure_load(filename)
|
41
|
-
|
42
|
-
|
61
|
+
def structure_load(filename, _extra_flags)
|
62
|
+
args = prepare_command_options
|
63
|
+
|
64
|
+
args.concat(["-i #{filename}"])
|
65
|
+
|
66
|
+
run_cmd('mssql-cli', args, 'loading')
|
43
67
|
end
|
44
68
|
|
45
69
|
private
|
46
70
|
|
71
|
+
attr_reader :db_config, :configuration_hash
|
72
|
+
|
73
|
+
def creation_options
|
74
|
+
{}.tap do |options|
|
75
|
+
options[:collation] = configuration_hash[:collation] if configuration_hash.include?(:collation)
|
76
|
+
|
77
|
+
# azure creation options
|
78
|
+
options[:azure_maxsize] = configuration_hash[:azure_maxsize] if configuration_hash.include?(:azure_maxsize)
|
79
|
+
options[:azure_edition] = configuration_hash[:azure_edition] if configuration_hash.include?(:azure_edition)
|
80
|
+
|
81
|
+
if configuration_hash.include?(:azure_service_objective)
|
82
|
+
options[:azure_service_objective] = configuration_hash[:azure_service_objective]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
47
87
|
def establish_master_connection
|
48
|
-
establish_connection
|
88
|
+
establish_connection(configuration_hash.merge(database: 'master'))
|
49
89
|
end
|
50
90
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
91
|
+
def prepare_command_options
|
92
|
+
{
|
93
|
+
server: '-S',
|
94
|
+
database: '-d',
|
95
|
+
username: '-U',
|
96
|
+
password: '-P'
|
97
|
+
}.map { |option, arg| "#{arg} #{config_for_cli[option]}" }
|
57
98
|
end
|
58
99
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
100
|
+
def config_for_cli
|
101
|
+
{}.tap do |options|
|
102
|
+
if configuration_hash[:host].present? && configuration_hash[:port].present?
|
103
|
+
options[:server] = "#{configuration_hash[:host]},#{configuration_hash[:port]}"
|
104
|
+
elsif configuration_hash[:host].present?
|
105
|
+
options[:server] = configuration_hash[:host]
|
106
|
+
end
|
107
|
+
|
108
|
+
options[:database] = configuration_hash[:database] if configuration_hash[:database].present?
|
109
|
+
options[:username] = configuration_hash[:username] if configuration_hash[:username].present?
|
110
|
+
options[:password] = configuration_hash[:password] if configuration_hash[:password].present?
|
64
111
|
end
|
65
|
-
dup
|
66
112
|
end
|
67
113
|
|
114
|
+
def run_cmd(cmd, args, action)
|
115
|
+
fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
|
116
|
+
end
|
117
|
+
|
118
|
+
def run_cmd_error(cmd, args, action)
|
119
|
+
msg = +"failed to execute:\n"
|
120
|
+
msg << "#{cmd} #{args.join(' ')}\n\n"
|
121
|
+
msg << "Failed #{action} structure, please check the output above for any errors"
|
122
|
+
msg << " and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
|
123
|
+
msg
|
124
|
+
end
|
68
125
|
end
|
69
126
|
|
70
127
|
module DatabaseTasksMSSQL
|
@@ -74,8 +131,8 @@ module ArJdbc
|
|
74
131
|
|
75
132
|
def check_protected_environments!
|
76
133
|
super
|
77
|
-
rescue ActiveRecord::JDBCError =>
|
78
|
-
case
|
134
|
+
rescue ActiveRecord::JDBCError => e
|
135
|
+
case e.message
|
79
136
|
when /cannot open database .* requested by the login/i
|
80
137
|
else
|
81
138
|
raise
|
@@ -85,6 +142,6 @@ module ArJdbc
|
|
85
142
|
end
|
86
143
|
end
|
87
144
|
|
88
|
-
ActiveRecord::Tasks::DatabaseTasks.send
|
145
|
+
ActiveRecord::Tasks::DatabaseTasks.send(:include, DatabaseTasksMSSQL)
|
89
146
|
end
|
90
147
|
end
|
data/lib/arjdbc/version.rb
CHANGED
@@ -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
|
}
|
@@ -860,27 +859,25 @@ public class RubyJdbcConnection extends RubyObject {
|
|
860
859
|
*/
|
861
860
|
@JRubyMethod(name = "execute_insert_pk", required = 2)
|
862
861
|
public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject pk) {
|
863
|
-
return withConnection(context,
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
try {
|
868
|
-
|
869
|
-
statement = createStatement(context, connection);
|
862
|
+
return withConnection(context, connection -> {
|
863
|
+
Statement statement = null;
|
864
|
+
final String query = sqlString(sql);
|
865
|
+
try {
|
870
866
|
|
871
|
-
|
872
|
-
statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
|
873
|
-
} else {
|
874
|
-
statement.executeUpdate(query, createStatementPk(pk));
|
875
|
-
}
|
867
|
+
statement = createStatement(context, connection);
|
876
868
|
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
} finally {
|
882
|
-
close(statement);
|
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));
|
883
873
|
}
|
874
|
+
|
875
|
+
return mapGeneratedKeys(context, connection, statement);
|
876
|
+
} catch (final SQLException e) {
|
877
|
+
debugErrorSQL(context, query);
|
878
|
+
throw e;
|
879
|
+
} finally {
|
880
|
+
close(statement);
|
884
881
|
}
|
885
882
|
});
|
886
883
|
}
|
@@ -903,26 +900,24 @@ public class RubyJdbcConnection extends RubyObject {
|
|
903
900
|
@JRubyMethod(name = "execute_insert_pk", required = 3)
|
904
901
|
public IRubyObject execute_insert_pk(final ThreadContext context, final IRubyObject sql, final IRubyObject binds,
|
905
902
|
final IRubyObject pk) {
|
906
|
-
return withConnection(context,
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
statement = connection.prepareStatement(query, createStatementPk(pk));
|
915
|
-
}
|
916
|
-
|
917
|
-
setStatementParameters(context, connection, statement, (RubyArray) binds);
|
918
|
-
statement.executeUpdate();
|
919
|
-
return mapGeneratedKeys(context, connection, statement);
|
920
|
-
} catch (final SQLException e) {
|
921
|
-
debugErrorSQL(context, query);
|
922
|
-
throw e;
|
923
|
-
} finally {
|
924
|
-
close(statement);
|
903
|
+
return withConnection(context, connection -> {
|
904
|
+
PreparedStatement statement = null;
|
905
|
+
final String query = sqlString(sql);
|
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));
|
925
911
|
}
|
912
|
+
|
913
|
+
setStatementParameters(context, connection, statement, (RubyArray) binds);
|
914
|
+
statement.executeUpdate();
|
915
|
+
return mapGeneratedKeys(context, connection, statement);
|
916
|
+
} catch (final SQLException e) {
|
917
|
+
debugErrorSQL(context, query);
|
918
|
+
throw e;
|
919
|
+
} finally {
|
920
|
+
close(statement);
|
926
921
|
}
|
927
922
|
});
|
928
923
|
}
|
@@ -1012,12 +1007,12 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1012
1007
|
binds = null;
|
1013
1008
|
} else { // (sql, binds)
|
1014
1009
|
maxRows = 0;
|
1015
|
-
binds = (RubyArray) TypeConverter.checkArrayType(args[1]);
|
1010
|
+
binds = (RubyArray) TypeConverter.checkArrayType(context, args[1]);
|
1016
1011
|
}
|
1017
1012
|
break;
|
1018
1013
|
case 3: // (sql, max_rows, binds)
|
1019
1014
|
maxRows = RubyNumeric.fix2int(args[1]);
|
1020
|
-
binds = (RubyArray) TypeConverter.checkArrayType(args[2]);
|
1015
|
+
binds = (RubyArray) TypeConverter.checkArrayType(context, args[2]);
|
1021
1016
|
break;
|
1022
1017
|
default: // (sql) 1-arg
|
1023
1018
|
maxRows = 0;
|
@@ -1106,6 +1101,28 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1106
1101
|
});
|
1107
1102
|
}
|
1108
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
|
+
|
1109
1126
|
/**
|
1110
1127
|
* Prepares a query, returns a wrapped PreparedStatement. This takes care of exception wrapping
|
1111
1128
|
* @param context which context this method is executing on.
|
@@ -2402,9 +2419,16 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2402
2419
|
final Connection connection, final PreparedStatement statement,
|
2403
2420
|
final int index, IRubyObject attribute) throws SQLException {
|
2404
2421
|
|
2405
|
-
|
2406
|
-
final int type
|
2407
|
-
|
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
|
+
}
|
2408
2432
|
|
2409
2433
|
// All the set methods were calling this first so save a method call in the nil case
|
2410
2434
|
if ( value == context.nil ) {
|
@@ -2520,6 +2544,34 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2520
2544
|
return Types.OTHER; // -1 as well as 0 are used in Types
|
2521
2545
|
}
|
2522
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
|
+
|
2523
2575
|
protected Integer jdbcTypeFor(final String type) {
|
2524
2576
|
return JDBC_TYPE_FOR.get(type);
|
2525
2577
|
}
|
@@ -2531,7 +2583,9 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2531
2583
|
}
|
2532
2584
|
|
2533
2585
|
protected static IRubyObject attributeSQLType(final ThreadContext context, final IRubyObject attribute) {
|
2534
|
-
|
2586
|
+
final IRubyObject type = attributeType(context, attribute);
|
2587
|
+
if (type != null) return type.callMethod(context, "type");
|
2588
|
+
return context.nil;
|
2535
2589
|
}
|
2536
2590
|
|
2537
2591
|
private final CachingCallSite value_site = new FunctionalCachingCallSite("value"); // AR::Attribute#value
|
@@ -2546,23 +2600,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2546
2600
|
|
2547
2601
|
final IRubyObject value = value_site.call(context, attribute, attribute);
|
2548
2602
|
|
2549
|
-
|
2550
|
-
return "integer";
|
2551
|
-
}
|
2552
|
-
|
2553
|
-
if (value instanceof RubyNumeric) {
|
2554
|
-
return "float";
|
2555
|
-
}
|
2556
|
-
|
2557
|
-
if (value instanceof RubyTime) {
|
2558
|
-
return "timestamp";
|
2559
|
-
}
|
2560
|
-
|
2561
|
-
if (value instanceof RubyBoolean) {
|
2562
|
-
return "boolean";
|
2563
|
-
}
|
2564
|
-
|
2565
|
-
return "string";
|
2603
|
+
return internedTypeForPrimitive(context, value);
|
2566
2604
|
}
|
2567
2605
|
|
2568
2606
|
// to be overriden in child class for database specific types
|
@@ -197,9 +197,9 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
197
197
|
RubyClass arrayClass = oidArray(context);
|
198
198
|
RubyBasicObject attributeType = (RubyBasicObject) attributeType(context, attribute);
|
199
199
|
// The type or its delegate is an OID::Array
|
200
|
-
if (arrayClass.isInstance(attributeType) ||
|
200
|
+
if (attributeType != null && (arrayClass.isInstance(attributeType) ||
|
201
201
|
(attributeType.hasInstanceVariable("@delegate_dc_obj") &&
|
202
|
-
arrayClass.isInstance(attributeType.getInstanceVariable("@delegate_dc_obj")))) {
|
202
|
+
arrayClass.isInstance(attributeType.getInstanceVariable("@delegate_dc_obj"))))) {
|
203
203
|
return "array";
|
204
204
|
}
|
205
205
|
|
@@ -387,20 +387,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
387
387
|
RubyDate rubyDate = (RubyDate) value;
|
388
388
|
DateTime dt = rubyDate.getDateTime();
|
389
389
|
// pgjdbc needs adjustment for default JVM timezone
|
390
|
-
|
391
|
-
// If the date is a day when Daylight savings starts (2am changed to 3am),
|
392
|
-
// And we are in a positive GMT timezone (Australia/Melbourne)
|
393
|
-
// Then removing milliseconds equal to the TimeZone (+11 GMT),
|
394
|
-
// will result in the date being the previous day 23:00, because of the missing hour.
|
395
|
-
// So we check if the date after the shift is outside of daylight time and remove an hours worth of milliseconds.
|
396
|
-
final long dateMillis = dt.getMillis();
|
397
|
-
long offset = TZ_DEFAULT.getOffset(dt.getMillis());
|
398
|
-
if (TZ_DEFAULT.inDaylightTime(new Date(dt.getMillis())) && !TZ_DEFAULT.inDaylightTime(new Date(dateMillis - offset))) {
|
399
|
-
offset -= 3600000; // 1 hour
|
400
|
-
}
|
401
|
-
Date utcShiftedDate = new Date(dateMillis - offset);
|
402
|
-
|
403
|
-
statement.setDate(index, utcShiftedDate);
|
390
|
+
statement.setDate(index, new Date(dt.getMillis() - TZ_DEFAULT.getOffset(dt.getMillis())));
|
404
391
|
return;
|
405
392
|
}
|
406
393
|
|
@@ -449,7 +436,7 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
449
436
|
break;
|
450
437
|
|
451
438
|
case "interval":
|
452
|
-
statement.setObject(index,
|
439
|
+
statement.setObject(index, stringToPGInterval(value.toString()));
|
453
440
|
break;
|
454
441
|
|
455
442
|
case "json":
|
@@ -506,6 +493,74 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
506
493
|
}
|
507
494
|
}
|
508
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
|
+
|
509
564
|
protected IRubyObject jdbcToRuby(ThreadContext context, Ruby runtime, int column, int type, ResultSet resultSet) throws SQLException {
|
510
565
|
return typeMap != null ?
|
511
566
|
convertWithTypeMap(context, runtime, column, type, resultSet) :
|
@@ -887,34 +942,25 @@ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection
|
|
887
942
|
private static String formatInterval(final Object object) {
|
888
943
|
final PGInterval interval = (PGInterval) object;
|
889
944
|
final StringBuilder str = new StringBuilder(32);
|
945
|
+
str.append("P");
|
890
946
|
|
891
947
|
final int years = interval.getYears();
|
892
|
-
if (years != 0) str.append(years).append("
|
948
|
+
if (years != 0) str.append(years).append("Y");
|
893
949
|
|
894
950
|
final int months = interval.getMonths();
|
895
|
-
if (months != 0) str.append(months).append("
|
951
|
+
if (months != 0) str.append(months).append("M");
|
896
952
|
|
897
953
|
final int days = interval.getDays();
|
898
|
-
if (days != 0) str.append(days).append("
|
954
|
+
if (days != 0) str.append(days).append("D");
|
899
955
|
|
900
956
|
final int hours = interval.getHours();
|
901
957
|
final int mins = interval.getMinutes();
|
902
|
-
final
|
903
|
-
if (hours != 0 || mins != 0 || secs != 0) {
|
904
|
-
|
905
|
-
|
906
|
-
str.append(
|
907
|
-
|
908
|
-
if (mins < 10) str.append('0');
|
909
|
-
|
910
|
-
str.append(mins).append(':');
|
911
|
-
|
912
|
-
if (secs < 10) str.append('0');
|
913
|
-
|
914
|
-
str.append(secs);
|
915
|
-
|
916
|
-
} else if (str.length() > 1) {
|
917
|
-
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");
|
918
964
|
}
|
919
965
|
|
920
966
|
return str.toString();
|
@@ -370,10 +370,9 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
370
370
|
}
|
371
371
|
|
372
372
|
@Override
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
);
|
373
|
+
protected boolean resetSavepoints(final ThreadContext context, final Connection connection) throws SQLException {
|
374
|
+
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
|
375
|
+
return super.resetSavepoints(context, connection);
|
377
376
|
}
|
378
377
|
|
379
378
|
@Override
|