activerecord-jdbc-alt-adapter 60.1.0-java → 61.0.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.
@@ -35,7 +35,17 @@ ArJdbc::ConnectionMethods.module_eval do
35
35
  # * http://sqlite.org/c3ref/open.html
36
36
  # * http://sqlite.org/c3ref/c_open_autoproxy.html
37
37
  # => 0x01 = readonly, 0x40 = uri (default in JDBC)
38
- config[:properties][:open_mode] = 0x01 | 0x40
38
+ config[:properties][:open_mode] = ::SQLite3::Constants::Open::READONLY | ::SQLite3::Constants::Open::URI
39
+ end
40
+
41
+ if config[:flags]
42
+ config[:properties][:open_mode] ||= 0
43
+ config[:properties][:open_mode] |= config[:flags]
44
+
45
+ # JDBC driver has an extra flag for it
46
+ if config[:flags] & ::SQLite3::Constants::Open::SHAREDCACHE != 0
47
+ config[:properties][:shared_cache] = true
48
+ end
39
49
  end
40
50
 
41
51
  timeout = config[:timeout]
@@ -5,15 +5,17 @@ module ActiveRecord::Tasks
5
5
  DatabaseTasks.module_eval do
6
6
 
7
7
  # @override patched to adapt jdbc configuration
8
- def each_current_configuration(environment, spec_name = nil)
8
+ def each_current_configuration(environment, name = nil)
9
9
  environments = [environment]
10
10
  environments << 'test' if environment == 'development'
11
11
 
12
12
  environments.each do |env|
13
13
  ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
14
- next if spec_name && spec_name != db_config.spec_name
14
+ next if name && name != db_config.name
15
15
 
16
- yield adapt_jdbc_config(db_config.config), db_config.spec_name, env unless db_config.config['database'].blank?
16
+ if db_config.database
17
+ yield adapt_jdbc_config(db_config), db_config.name, env
18
+ end
17
19
  end
18
20
  end
19
21
  end
@@ -21,21 +23,24 @@ module ActiveRecord::Tasks
21
23
  # @override patched to adapt jdbc configuration
22
24
  def each_local_configuration
23
25
  ActiveRecord::Base.configurations.configs_for.each do |db_config|
24
- next unless db_config.config['database']
26
+ next unless db_config.database
25
27
 
26
- if local_database?(db_config.config)
27
- yield adapt_jdbc_config(db_config.config)
28
+ if local_database?(db_config)
29
+ yield adapt_jdbc_config(db_config)
28
30
  else
29
- $stderr.puts "This task only modifies local databases. #{db_config.config['database']} is on a remote host."
31
+ $stderr.puts "This task only modifies local databases. #{db_config.database} is on a remote host."
30
32
  end
31
33
  end
32
34
  end
33
35
 
34
36
  private
35
37
 
36
- def adapt_jdbc_config(config)
37
- return config unless config['adapter']
38
- config.merge 'adapter' => config['adapter'].sub(/^jdbc/, '')
38
+ def adapt_jdbc_config(db_config)
39
+ if db_config.adapter.start_with? 'jdbc'
40
+ config = db_config.configuration_hash.merge(adapter: db_config.adapter.sub(/^jdbc/, ''))
41
+ db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new(db_config.env_name, db_config.name, config)
42
+ end
43
+ db_config
39
44
  end
40
45
 
41
46
  end
@@ -1,18 +1,29 @@
1
- require 'active_record/tasks/database_tasks'
1
+ # frozen_string_literal: true
2
2
 
3
- require 'arjdbc/tasks/jdbc_database_tasks'
3
+ require 'active_record/tasks/database_tasks'
4
4
 
5
5
  module ArJdbc
6
- module Tasks
7
- class MSSQLDatabaseTasks < JdbcDatabaseTasks
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(configuration['database'])
13
- establish_connection configuration
14
- rescue ActiveRecord::StatementInvalid => error
15
- case error.message
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 configuration['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
- def structure_dump(filename)
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
- config = config_from_url_if_needed
42
- `sqlcmd -S #{config['host']} -d #{config['database']} -U #{config['username']} -P #{config['password']} -i #{filename}`
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 configuration.merge('database' => 'master')
88
+ establish_connection(configuration_hash.merge(database: 'master'))
49
89
  end
50
90
 
51
- def config_from_url_if_needed
52
- config = self.config
53
- if config['url'] && ! config.key?('database')
54
- config = config_from_url(config['url'])
55
- end
56
- config
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 deep_dup(hash)
60
- dup = hash.dup
61
- dup.each_pair do |k,v|
62
- tv = dup[k]
63
- dup[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? deep_dup(tv) : v
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 => error
78
- case error.message
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 :include, DatabaseTasksMSSQL
145
+ ActiveRecord::Tasks::DatabaseTasks.send(:include, DatabaseTasksMSSQL)
89
146
  end
90
147
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ArJdbc
2
- VERSION = '60.1.0'
4
+ VERSION = '61.0.0'
3
5
  end
@@ -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, new Callable<IRubyObject>() {
864
- public IRubyObject call(final Connection connection) throws SQLException {
865
- Statement statement = null;
866
- final String query = sqlString(sql);
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
- if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
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
- return mapGeneratedKeys(context, connection, statement);
878
- } catch (final SQLException e) {
879
- debugErrorSQL(context, query);
880
- throw e;
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, new Callable<IRubyObject>() {
907
- public IRubyObject call(final Connection connection) throws SQLException {
908
- PreparedStatement statement = null;
909
- final String query = sqlString(sql);
910
- try {
911
- if (pk == context.nil || pk == context.fals || !supportsGeneratedKeys(connection)) {
912
- statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
913
- } else {
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
- //debugMessage(context, attribute);
2406
- final int type = jdbcTypeForAttribute(context, attribute);
2407
- IRubyObject value = valueForDatabase(context, attribute);
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
- return attributeType(context, attribute).callMethod(context, "type");
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
- if (value instanceof RubyInteger) {
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