activerecord-jdbc-adapter 52.1-java → 52.2-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.
Files changed (34) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +34 -15
  3. data/Gemfile +1 -2
  4. data/README.md +10 -3
  5. data/lib/arjdbc/abstract/core.rb +12 -2
  6. data/lib/arjdbc/abstract/database_statements.rb +1 -1
  7. data/lib/arjdbc/abstract/statement_cache.rb +4 -4
  8. data/lib/arjdbc/db2/adapter.rb +68 -60
  9. data/lib/arjdbc/db2/as400.rb +12 -0
  10. data/lib/arjdbc/db2/column.rb +3 -0
  11. data/lib/arjdbc/db2/connection_methods.rb +4 -0
  12. data/lib/arjdbc/jdbc.rb +3 -0
  13. data/lib/arjdbc/jdbc/adapter.rb +0 -6
  14. data/lib/arjdbc/jdbc/column.rb +4 -2
  15. data/lib/arjdbc/mysql/adapter.rb +8 -0
  16. data/lib/arjdbc/postgresql/adapter.rb +5 -72
  17. data/lib/arjdbc/postgresql/oid_types.rb +82 -14
  18. data/lib/arjdbc/version.rb +1 -1
  19. data/rakelib/rails.rake +4 -3
  20. data/src/java/arjdbc/ArJdbcModule.java +5 -15
  21. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +2 -2
  22. data/src/java/arjdbc/jdbc/ConnectionFactory.java +0 -87
  23. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +0 -1
  24. data/src/java/arjdbc/jdbc/RubyConnectionFactory.java +61 -0
  25. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +46 -18
  26. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +2 -2
  27. data/src/java/arjdbc/postgresql/PgDateTimeUtils.java +52 -0
  28. data/src/java/arjdbc/postgresql/PostgreSQLResult.java +90 -17
  29. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +68 -49
  30. data/src/java/arjdbc/util/DateTimeUtils.java +119 -0
  31. data/src/java/arjdbc/util/PG.java +8 -0
  32. data/src/java/arjdbc/util/QuotingUtils.java +6 -7
  33. metadata +6 -4
  34. data/src/java/arjdbc/postgresql/PgResultSetMetaDataWrapper.java +0 -23
@@ -32,7 +32,15 @@ module ActiveRecord
32
32
  include ArJdbc::MySQL
33
33
 
34
34
  def initialize(connection, logger, connection_parameters, config)
35
+ # workaround to skip version check on JNDI to be lazy, dummy version is high enough for Rails 5.0 - 6.0
36
+ is_jndi = ::ActiveRecord::ConnectionAdapters::JdbcConnection.jndi_config?(config)
37
+ @version = '8.1.5' if is_jndi
38
+
35
39
  super
40
+
41
+ # set to nil to have it lazy-load the real value when required
42
+ @version = nil if is_jndi
43
+
36
44
  @prepared_statements = false unless config.key?(:prepared_statements)
37
45
  # configure_connection taken care of at ArJdbc::Abstract::Core
38
46
  end
@@ -531,77 +531,6 @@ module ArJdbc
531
531
  execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
532
532
  end
533
533
 
534
- # Returns an array of indexes for the given table.
535
- def indexes(table_name)
536
-
537
- # FIXME: AR version => table = Utils.extract_schema_qualified_name(table_name.to_s)
538
- schema, table = extract_schema_and_table(table_name.to_s)
539
-
540
- result = query(<<-SQL, 'SCHEMA')
541
- SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
542
- pg_catalog.obj_description(i.oid, 'pg_class') AS comment
543
- FROM pg_class t
544
- INNER JOIN pg_index d ON t.oid = d.indrelid
545
- INNER JOIN pg_class i ON d.indexrelid = i.oid
546
- LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
547
- WHERE i.relkind = 'i'
548
- AND d.indisprimary = 'f'
549
- AND t.relname = '#{table}'
550
- AND n.nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
551
- ORDER BY i.relname
552
- SQL
553
-
554
- result.map do |row|
555
- index_name = row[0]
556
- # FIXME: These values [1,2] are returned in a different format than AR expects, maybe we could update it on the Java side to be more accurate
557
- unique = row[1].is_a?(String) ? row[1] == 't' : row[1] # JDBC gets us a boolean
558
- indkey = row[2].is_a?(Java::OrgPostgresqlUtil::PGobject) ? row[2].value : row[2]
559
- indkey = indkey.split(" ").map(&:to_i)
560
- inddef = row[3]
561
- oid = row[4]
562
- comment = row[5]
563
-
564
- using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
565
-
566
- orders = {}
567
- opclasses = {}
568
-
569
- if indkey.include?(0)
570
- columns = expressions
571
- else
572
- columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
573
- SELECT a.attnum, a.attname
574
- FROM pg_attribute a
575
- WHERE a.attrelid = #{oid}
576
- AND a.attnum IN (#{indkey.join(",")})
577
- SQL
578
-
579
- # add info on sort order (only desc order is explicitly specified, asc is the default)
580
- # and non-default opclasses
581
- expressions.scan(/(?<column>\w+)\s?(?<opclass>\w+_ops)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
582
- opclasses[column] = opclass.to_sym if opclass
583
- if nulls
584
- orders[column] = [desc, nulls].compact.join(' ')
585
- elsif desc
586
- orders[column] = :desc
587
- end
588
- end
589
- end
590
-
591
- IndexDefinition.new(
592
- table_name,
593
- index_name,
594
- unique,
595
- columns,
596
- orders: orders,
597
- opclasses: opclasses,
598
- where: where,
599
- using: using.to_sym,
600
- comment: comment.presence
601
- )
602
- end
603
- end
604
-
605
534
  # @private
606
535
  def column_name_for_operation(operation, node)
607
536
  case operation
@@ -690,6 +619,10 @@ module ArJdbc
690
619
  @local_tz ||= execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
691
620
  end
692
621
 
622
+ def bind_params_length
623
+ 32767
624
+ end
625
+
693
626
  end
694
627
  end
695
628
 
@@ -766,7 +699,7 @@ module ActiveRecord::ConnectionAdapters
766
699
 
767
700
  # Prepared statements aren't schema aware so we need to make sure we
768
701
  # store different PreparedStatement objects for different schemas
769
- def cached_statement_key(sql)
702
+ def sql_key(sql)
770
703
  "#{schema_search_path}-#{sql}"
771
704
  end
772
705
 
@@ -9,6 +9,61 @@ module ArJdbc
9
9
  # @private
10
10
  OID = ::ActiveRecord::ConnectionAdapters::PostgreSQL::OID
11
11
 
12
+ # this version makes sure to register the types by name as well
13
+ # we still need to version with OID since it's used from SchemaStatements as well
14
+ class ArjdbcTypeMapInitializer < OID::TypeMapInitializer
15
+ private
16
+
17
+ def name_with_ns(row)
18
+ if row['in_ns']
19
+ row['typname']
20
+ else
21
+ %Q("#{row['nspname']}"."#{row['typname']}")
22
+ end
23
+ end
24
+
25
+ def register_enum_type(row)
26
+ super
27
+ register name_with_ns(row), OID::Enum.new
28
+ end
29
+
30
+ def register_array_type(row)
31
+ super
32
+ register_with_subtype(name_with_ns(row), row['typelem'].to_i) do |subtype|
33
+ OID::Array.new(subtype, row['typdelim'])
34
+ end
35
+ end
36
+
37
+ def register_range_type(row)
38
+ super
39
+ name = name_with_ns(row)
40
+ register_with_subtype(name, row['rngsubtype'].to_i) do |subtype|
41
+ OID::Range.new(subtype, name.to_sym)
42
+ end
43
+ end
44
+
45
+ def register_domain_type(row)
46
+ if base_type = @store.lookup(row['typbasetype'].to_i)
47
+ register row['oid'], base_type
48
+ register name_with_ns(row), base_type
49
+ else
50
+ warn "unknown base type (OID: #{row['typbasetype']}) for domain #{row['typname']}."
51
+ end
52
+ end
53
+
54
+ def register_composite_type(row)
55
+ if subtype = @store.lookup(row['typelem'].to_i)
56
+ register row['oid'], OID::Vector.new(row['typdelim'], subtype)
57
+ register name_with_ns(row), OID::Vector.new(row['typdelim'], subtype)
58
+ end
59
+ end
60
+
61
+ def assert_valid_registration(oid, oid_type)
62
+ ret = super
63
+ ret == 0 ? oid : ret
64
+ end
65
+ end
66
+
12
67
  # @private
13
68
  module OIDTypes
14
69
 
@@ -33,15 +88,9 @@ module ArJdbc
33
88
  @extensions ||= super
34
89
  end
35
90
 
36
- # @override
37
- def lookup_cast_type(sql_type)
38
- oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA")
39
- super oid.first['oid'].to_i
40
- end
41
-
42
91
  def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
43
92
  if !type_map.key?(oid)
44
- load_additional_types([oid])
93
+ load_additional_types(type_map, oid)
45
94
  end
46
95
 
47
96
  type_map.fetch(oid, fmod, sql_type) {
@@ -132,27 +181,46 @@ module ArJdbc
132
181
  end
133
182
  end
134
183
 
135
- load_additional_types
184
+ load_additional_types(m)
185
+
186
+ # pgjdbc returns these if the column is auto-incrmenting
187
+ m.alias_type 'serial', 'int4'
188
+ m.alias_type 'bigserial', 'int8'
136
189
  end
137
190
 
138
- def load_additional_types(oids = nil) # :nodoc:
139
- initializer = OID::TypeMapInitializer.new(type_map)
191
+ def load_additional_types(type_map, oid = nil) # :nodoc:
192
+ initializer = ArjdbcTypeMapInitializer.new(type_map)
140
193
 
141
194
  if supports_ranges?
142
195
  query = <<-SQL
143
- SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
196
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype,
197
+ ns.nspname, ns.nspname = ANY(current_schemas(true)) in_ns
144
198
  FROM pg_type as t
145
199
  LEFT JOIN pg_range as r ON oid = rngtypid
200
+ JOIN pg_namespace AS ns ON t.typnamespace = ns.oid
146
201
  SQL
147
202
  else
148
203
  query = <<-SQL
149
- SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
204
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype,
205
+ ns.nspname, ns.nspname = ANY(current_schemas(true)) in_ns
150
206
  FROM pg_type as t
207
+ JOIN pg_namespace AS ns ON t.typnamespace = ns.oid
151
208
  SQL
152
209
  end
153
210
 
154
- if oids
155
- query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
211
+ if oid
212
+ if oid.is_a? Numeric || oid.match(/^\d+$/)
213
+ # numeric OID
214
+ query += "WHERE t.oid::integer = %s" % oid
215
+
216
+ elsif m = oid.match(/"?(\w+)"?\."?(\w+)"?/)
217
+ # namespace and type name
218
+ query += "WHERE ns.nspname = '%s' AND t.typname = '%s'" % [m[1], m[2]]
219
+
220
+ else
221
+ # only type name
222
+ query += "WHERE t.typname = '%s' AND ns.nspname = ANY(current_schemas(true))" % oid
223
+ end
156
224
  else
157
225
  query += initializer.query_conditions_for_initial_load
158
226
  end
@@ -1,3 +1,3 @@
1
1
  module ArJdbc
2
- VERSION = '52.1'
2
+ VERSION = '52.2'
3
3
  end
@@ -9,14 +9,15 @@ namespace :rails do
9
9
  if ENV['RAILS']
10
10
  ar_path = File.join(ENV['RAILS'], 'activerecord')
11
11
  end
12
-
12
+
13
13
  unless ar_path && File.exist?(ar_path)
14
- ar_path = `bundle info --path activerecord`.chomp
14
+ ar_path = `bundle info --path activerecord`.lines.last.chomp
15
15
  end
16
16
 
17
17
  unless File.exist? ar_test_dir = File.join(ar_path, 'test')
18
18
  raise "can not directly load Rails tests;" +
19
- " try setting a local repository path e.g. export RAILS=`pwd`/../rails"
19
+ " try setting a local repository path e.g. export RAILS=`pwd`/../rails;" +
20
+ " failed guess: #{ar_path}"
20
21
  end
21
22
 
22
23
  driver = "jdbc-#{ENV['DRIVER'] ? ENV['DRIVER'].downcase : (adapter =~ /postgres/i ? 'postgres' : adapter)}"
@@ -29,7 +29,6 @@ import java.util.HashMap;
29
29
  import java.util.Map;
30
30
  import java.util.WeakHashMap;
31
31
 
32
- import org.jruby.NativeException;
33
32
  import org.jruby.Ruby;
34
33
  import org.jruby.RubyArray;
35
34
  import org.jruby.RubyClass;
@@ -148,13 +147,7 @@ public class ArJdbcModule {
148
147
  }
149
148
  }
150
149
  catch (ClassNotFoundException e) { /* ignored */ }
151
- catch (NoSuchMethodException e) {
152
- throw newNativeException(runtime, e);
153
- }
154
- catch (IllegalAccessException e) {
155
- throw newNativeException(runtime, e);
156
- }
157
- catch (InvocationTargetException e) {
150
+ catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
158
151
  throw newNativeException(runtime, e);
159
152
  }
160
153
 
@@ -263,18 +256,15 @@ public class ArJdbcModule {
263
256
  try {
264
257
  return klass.getMethod(name, argType).invoke(null, arg);
265
258
  }
266
- catch (IllegalAccessException e) {
267
- throw newNativeException(runtime, e);
268
- }
269
- catch (InvocationTargetException e) {
259
+ catch (IllegalAccessException | InvocationTargetException e) {
270
260
  throw newNativeException(runtime, e);
271
261
  }
272
262
  }
273
263
 
274
264
  private static RaiseException newNativeException(final Ruby runtime, final Throwable cause) {
275
- RubyClass nativeClass = runtime.getClass(NativeException.CLASS_NAME);
276
- NativeException nativeException = new NativeException(runtime, nativeClass, cause);
277
- return new RaiseException(cause, nativeException);
265
+ final RaiseException error = runtime.newRuntimeError(cause.toString());
266
+ error.initCause(cause);
267
+ return error;
278
268
  }
279
269
 
280
270
  @JRubyMethod(meta = true)
@@ -83,8 +83,8 @@ public class DerbyRubyJdbcConnection extends RubyJdbcConnection {
83
83
  final int minor = jdbcDriver.getMinorVersion();
84
84
  if ( major < 10 || ( major == 10 && minor < 5 ) ) {
85
85
  final RubyClass errorClass = getConnectionNotEstablished(context.runtime);
86
- throw new RaiseException(context.runtime, errorClass,
87
- "adapter requires Derby >= 10.5 got: " + major + "." + minor + "", false);
86
+ throw context.runtime.newRaiseException(errorClass,
87
+ "adapter requires Derby >= 10.5 got: " + major + "." + minor + "");
88
88
  }
89
89
  if ( major == 10 && minor < 8 ) { // 10.8 ~ supports JDBC 4.1
90
90
  // config[:connection_alive_sql] ||= 'SELECT 1 FROM SYS.SYSSCHEMAS FETCH FIRST 1 ROWS ONLY'
@@ -27,12 +27,6 @@ package arjdbc.jdbc;
27
27
 
28
28
  import java.sql.Connection;
29
29
  import java.sql.SQLException;
30
- import javax.sql.DataSource;
31
-
32
- import org.jruby.RubyObject;
33
- import org.jruby.RubyString;
34
- import org.jruby.runtime.ThreadContext;
35
- import org.jruby.runtime.builtin.IRubyObject;
36
30
 
37
31
  /**
38
32
  * Interface to be implemented in Ruby for retrieving a new connection.
@@ -49,84 +43,3 @@ public interface ConnectionFactory {
49
43
  Connection newConnection() throws SQLException;
50
44
 
51
45
  }
52
-
53
- class DataSourceConnectionFactoryImpl implements ConnectionFactory {
54
-
55
- private final DataSource dataSource;
56
- final String username, password; // optional
57
-
58
- public DataSourceConnectionFactoryImpl(final DataSource dataSource) {
59
- this.dataSource = dataSource;
60
- this.username = null; this.password = null;
61
- }
62
-
63
- public DataSourceConnectionFactoryImpl(final DataSource dataSource,
64
- final String username, final String password) {
65
- this.dataSource = dataSource;
66
- this.username = username; this.password = password;
67
- }
68
-
69
- @Override
70
- public Connection newConnection() throws SQLException {
71
- if ( username != null ) {
72
- dataSource.getConnection(username, password);
73
- }
74
- return dataSource.getConnection();
75
- }
76
-
77
- DataSource getDataSource() { return dataSource; } /* for tests */
78
-
79
- }
80
-
81
- class DriverConnectionFactoryImpl implements ConnectionFactory {
82
-
83
- private final DriverWrapper driverWrapper;
84
- final String url;
85
- final String username, password; // null allowed
86
-
87
- public DriverConnectionFactoryImpl(final DriverWrapper driver, final String url) {
88
- this.driverWrapper = driver; this.url = url;
89
- this.username = null; this.password = null;
90
- }
91
-
92
- public DriverConnectionFactoryImpl(final DriverWrapper driver, final String url,
93
- final String username, final String password) {
94
- this.driverWrapper = driver; this.url = url;
95
- this.username = username; this.password = password;
96
- }
97
-
98
- @Override
99
- public Connection newConnection() throws SQLException {
100
- return driverWrapper.connect(url, username, password);
101
- }
102
-
103
- DriverWrapper getDriverWrapper() { return driverWrapper; } /* for tests */
104
-
105
- }
106
-
107
- // @legacy ActiveRecord::ConnectionAdapters::JdbcDriver
108
- class RubyConnectionFactoryImpl implements ConnectionFactory {
109
-
110
- private final IRubyObject driver;
111
- final RubyString url;
112
- final IRubyObject username, password; // null allowed
113
-
114
- private final RubyObject contextProvider;
115
-
116
- public RubyConnectionFactoryImpl(final IRubyObject driver, final RubyString url,
117
- final IRubyObject username, final IRubyObject password) {
118
- this.driver = driver; this.url = url;
119
- this.username = username; this.password = password;
120
- contextProvider = (RubyObject) driver;
121
- }
122
-
123
- @Override
124
- public Connection newConnection() throws SQLException {
125
- final ThreadContext context = contextProvider.getRuntime().getCurrentContext();
126
- final IRubyObject connection = driver.callMethod(context, "connection", new IRubyObject[] { url, username, password });
127
- return (Connection) connection.toJava(Connection.class);
128
- }
129
-
130
- IRubyObject getDriver() { return driver; } /* for tests */
131
-
132
- }
@@ -68,7 +68,6 @@ final class DataSourceConnectionFactory implements ConnectionFactory {
68
68
 
69
69
  @Override
70
70
  public Connection newConnection() throws SQLException {
71
- DataSource dataSource = this.dataSource;
72
71
  // in case DS failed previously look it up again from JNDI :
73
72
  if (dataSource == null) {
74
73
  lookupDataSource();
@@ -0,0 +1,61 @@
1
+ /***** BEGIN LICENSE BLOCK *****
2
+ * Copyright (c) 2012-2014 Karol Bucek <self@kares.org>
3
+ * Copyright (c) 2006-2010 Nick Sieger <nick@nicksieger.com>
4
+ * Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining
7
+ * a copy of this software and associated documentation files (the
8
+ * "Software"), to deal in the Software without restriction, including
9
+ * without limitation the rights to use, copy, modify, merge, publish,
10
+ * distribute, sublicense, and/or sell copies of the Software, and to
11
+ * permit persons to whom the Software is furnished to do so, subject to
12
+ * the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be
15
+ * included in all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ ***** END LICENSE BLOCK *****/
25
+
26
+ package arjdbc.jdbc;
27
+
28
+ import java.sql.Connection;
29
+ import java.sql.SQLException;
30
+
31
+ import org.jruby.RubyObject;
32
+ import org.jruby.RubyString;
33
+ import org.jruby.runtime.ThreadContext;
34
+ import org.jruby.runtime.builtin.IRubyObject;
35
+
36
+ // @legacy ActiveRecord::ConnectionAdapters::JdbcDriver
37
+ class RubyConnectionFactory implements ConnectionFactory {
38
+
39
+ private final IRubyObject driver;
40
+ final RubyString url;
41
+ final IRubyObject username, password; // null allowed
42
+
43
+ private final RubyObject contextProvider;
44
+
45
+ public RubyConnectionFactory(final IRubyObject driver, final RubyString url,
46
+ final IRubyObject username, final IRubyObject password) {
47
+ this.driver = driver; this.url = url;
48
+ this.username = username; this.password = password;
49
+ contextProvider = (RubyObject) driver;
50
+ }
51
+
52
+ @Override
53
+ public Connection newConnection() throws SQLException {
54
+ final ThreadContext context = contextProvider.getRuntime().getCurrentContext();
55
+ final IRubyObject connection = driver.callMethod(context, "connection", new IRubyObject[] { url, username, password });
56
+ return (Connection) connection.toJava(Connection.class);
57
+ }
58
+
59
+ IRubyObject getDriver() { return driver; } /* for tests */
60
+
61
+ }