activerecord-jdbc-alt-adapter 61.0.0-java → 70.0.0.rc1-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +273 -0
- data/.gitignore +1 -0
- data/Gemfile +8 -6
- data/README.md +2 -1
- data/Rakefile +1 -1
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/activerecord-jdbc-alt-adapter.gemspec +2 -2
- data/lib/arel/visitors/compat.rb +5 -33
- data/lib/arel/visitors/h2.rb +1 -13
- data/lib/arel/visitors/hsqldb.rb +1 -21
- data/lib/arel/visitors/sql_server.rb +2 -103
- data/lib/arjdbc/abstract/core.rb +8 -9
- data/lib/arjdbc/abstract/database_statements.rb +4 -4
- data/lib/arjdbc/discover.rb +0 -67
- data/lib/arjdbc/hsqldb/adapter.rb +2 -2
- data/lib/arjdbc/jdbc/adapter.rb +3 -3
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
- data/lib/arjdbc/jdbc/column.rb +1 -26
- data/lib/arjdbc/jdbc/type_cast.rb +2 -2
- data/lib/arjdbc/jdbc.rb +0 -7
- data/lib/arjdbc/mssql/adapter.rb +134 -105
- data/lib/arjdbc/mssql/quoting.rb +26 -27
- data/lib/arjdbc/mssql/schema_creation.rb +1 -1
- data/lib/arjdbc/mssql/schema_definitions.rb +32 -17
- data/lib/arjdbc/mssql/schema_dumper.rb +13 -1
- data/lib/arjdbc/mssql/schema_statements.rb +61 -36
- data/lib/arjdbc/mssql/transaction.rb +2 -2
- data/lib/arjdbc/mssql/types/date_and_time_types.rb +6 -6
- data/lib/arjdbc/mssql/types/numeric_types.rb +2 -2
- data/lib/arjdbc/mssql.rb +1 -1
- data/lib/arjdbc/mysql/adapter.rb +2 -1
- data/lib/arjdbc/oracle/adapter.rb +4 -23
- data/lib/arjdbc/postgresql/adapter.rb +64 -1
- data/lib/arjdbc/postgresql/oid_types.rb +68 -47
- data/lib/arjdbc/sqlite3/adapter.rb +132 -88
- data/lib/arjdbc/tasks/database_tasks.rb +0 -12
- data/lib/arjdbc/util/serialized_attributes.rb +0 -22
- data/lib/arjdbc/util/table_copier.rb +2 -1
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/02-test.rake +3 -18
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +17 -2
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +14 -1
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +33 -0
- metadata +8 -40
- data/lib/active_record/connection_adapters/as400_adapter.rb +0 -2
- data/lib/active_record/connection_adapters/db2_adapter.rb +0 -1
- data/lib/active_record/connection_adapters/derby_adapter.rb +0 -1
- data/lib/active_record/connection_adapters/informix_adapter.rb +0 -1
- data/lib/arel/visitors/db2.rb +0 -137
- data/lib/arel/visitors/derby.rb +0 -112
- data/lib/arel/visitors/firebird.rb +0 -79
- data/lib/arjdbc/db2/adapter.rb +0 -808
- data/lib/arjdbc/db2/as400.rb +0 -142
- data/lib/arjdbc/db2/column.rb +0 -131
- data/lib/arjdbc/db2/connection_methods.rb +0 -48
- data/lib/arjdbc/db2.rb +0 -4
- data/lib/arjdbc/derby/active_record_patch.rb +0 -13
- data/lib/arjdbc/derby/adapter.rb +0 -521
- data/lib/arjdbc/derby/connection_methods.rb +0 -20
- data/lib/arjdbc/derby/schema_creation.rb +0 -15
- data/lib/arjdbc/derby.rb +0 -3
- data/lib/arjdbc/firebird/adapter.rb +0 -413
- data/lib/arjdbc/firebird/connection_methods.rb +0 -23
- data/lib/arjdbc/firebird.rb +0 -4
- data/lib/arjdbc/informix/adapter.rb +0 -139
- data/lib/arjdbc/informix/connection_methods.rb +0 -9
- data/lib/arjdbc/sybase/adapter.rb +0 -47
- data/lib/arjdbc/sybase.rb +0 -2
- data/lib/arjdbc/tasks/db2_database_tasks.rb +0 -104
- data/lib/arjdbc/tasks/derby_database_tasks.rb +0 -95
- data/src/java/arjdbc/derby/DerbyModule.java +0 -178
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +0 -152
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +0 -174
- data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +0 -75
@@ -10,6 +10,7 @@ require "active_record/connection_adapters/abstract_adapter"
|
|
10
10
|
require "active_record/connection_adapters/statement_pool"
|
11
11
|
require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
|
12
12
|
require "active_record/connection_adapters/sqlite3/quoting"
|
13
|
+
require "active_record/connection_adapters/sqlite3/database_statements"
|
13
14
|
require "active_record/connection_adapters/sqlite3/schema_creation"
|
14
15
|
require "active_record/connection_adapters/sqlite3/schema_definitions"
|
15
16
|
require "active_record/connection_adapters/sqlite3/schema_dumper"
|
@@ -64,6 +65,7 @@ module ArJdbc
|
|
64
65
|
# DIFFERENCE: FQN
|
65
66
|
include ::ActiveRecord::ConnectionAdapters::SQLite3::Quoting
|
66
67
|
include ::ActiveRecord::ConnectionAdapters::SQLite3::SchemaStatements
|
68
|
+
include ::ActiveRecord::ConnectionAdapters::SQLite3::DatabaseStatements
|
67
69
|
|
68
70
|
NATIVE_DATABASE_TYPES = {
|
69
71
|
primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
|
@@ -79,15 +81,30 @@ module ArJdbc
|
|
79
81
|
boolean: { name: "boolean" },
|
80
82
|
json: { name: "json" },
|
81
83
|
}
|
82
|
-
|
83
|
-
|
84
|
-
|
84
|
+
|
85
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
86
|
+
private
|
87
|
+
def dealloc(stmt)
|
88
|
+
stmt.close unless stmt.closed?
|
89
|
+
end
|
90
|
+
end
|
85
91
|
|
86
92
|
def initialize(connection, logger, connection_options, config)
|
93
|
+
@memory_database = config[:database] == ":memory:"
|
87
94
|
super(connection, logger, config)
|
88
95
|
configure_connection
|
89
96
|
end
|
90
97
|
|
98
|
+
def self.database_exists?(config)
|
99
|
+
config = config.symbolize_keys
|
100
|
+
if config[:database] == ":memory:"
|
101
|
+
true
|
102
|
+
else
|
103
|
+
database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
|
104
|
+
File.exist?(database_file)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
91
108
|
def supports_ddl_transactions?
|
92
109
|
true
|
93
110
|
end
|
@@ -101,7 +118,7 @@ module ArJdbc
|
|
101
118
|
end
|
102
119
|
|
103
120
|
def supports_partial_index?
|
104
|
-
|
121
|
+
true
|
105
122
|
end
|
106
123
|
|
107
124
|
def supports_expression_index?
|
@@ -144,6 +161,25 @@ module ArJdbc
|
|
144
161
|
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
145
162
|
|
146
163
|
# DIFFERENCE: active?, reconnect!, disconnect! handles by arjdbc core
|
164
|
+
def supports_concurrent_connections?
|
165
|
+
!@memory_database
|
166
|
+
end
|
167
|
+
|
168
|
+
def active?
|
169
|
+
!@raw_connection.closed?
|
170
|
+
end
|
171
|
+
|
172
|
+
def reconnect!
|
173
|
+
super
|
174
|
+
connect if @connection.closed?
|
175
|
+
end
|
176
|
+
|
177
|
+
# Disconnects from the database if already connected. Otherwise, this
|
178
|
+
# method does nothing.
|
179
|
+
def disconnect!
|
180
|
+
super
|
181
|
+
@connection.close rescue nil
|
182
|
+
end
|
147
183
|
|
148
184
|
def supports_index_sort_order?
|
149
185
|
true
|
@@ -182,48 +218,8 @@ module ArJdbc
|
|
182
218
|
end
|
183
219
|
end
|
184
220
|
|
185
|
-
|
186
|
-
|
187
|
-
#++
|
188
|
-
|
189
|
-
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
190
|
-
:pragma
|
191
|
-
) # :nodoc:
|
192
|
-
private_constant :READ_QUERY
|
193
|
-
|
194
|
-
def write_query?(sql) # :nodoc:
|
195
|
-
!READ_QUERY.match?(sql)
|
196
|
-
end
|
197
|
-
|
198
|
-
def explain(arel, binds = [])
|
199
|
-
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
200
|
-
# DIFFERENCE: FQN
|
201
|
-
::ActiveRecord::ConnectionAdapters::SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
202
|
-
end
|
203
|
-
|
204
|
-
# DIFFERENCE: implemented in ArJdbc::Abstract::DatabaseStatements
|
205
|
-
#def exec_query(sql, name = nil, binds = [], prepare: false)
|
206
|
-
|
207
|
-
# DIFFERENCE: implemented in ArJdbc::Abstract::DatabaseStatements
|
208
|
-
#def exec_delete(sql, name = "SQL", binds = [])
|
209
|
-
|
210
|
-
def last_inserted_id(result)
|
211
|
-
@connection.last_insert_row_id
|
212
|
-
end
|
213
|
-
|
214
|
-
# DIFFERENCE: implemented in ArJdbc::Abstract::DatabaseStatements
|
215
|
-
#def execute(sql, name = nil) #:nodoc:
|
216
|
-
|
217
|
-
def begin_db_transaction #:nodoc:
|
218
|
-
log("begin transaction", 'TRANSACTION') { @connection.transaction }
|
219
|
-
end
|
220
|
-
|
221
|
-
def commit_db_transaction #:nodoc:
|
222
|
-
log("commit transaction", 'TRANSACTION') { @connection.commit }
|
223
|
-
end
|
224
|
-
|
225
|
-
def exec_rollback_db_transaction #:nodoc:
|
226
|
-
log("rollback transaction", 'TRANSACTION') { @connection.rollback }
|
221
|
+
def all_foreign_keys_valid? # :nodoc:
|
222
|
+
execute("PRAGMA foreign_key_check").blank?
|
227
223
|
end
|
228
224
|
|
229
225
|
# SCHEMA STATEMENTS ========================================
|
@@ -233,14 +229,15 @@ module ArJdbc
|
|
233
229
|
pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
|
234
230
|
end
|
235
231
|
|
236
|
-
|
237
|
-
|
232
|
+
def remove_index(table_name, column_name = nil, **options) # :nodoc:
|
233
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
238
234
|
|
239
235
|
index_name = index_name_for_remove(table_name, column_name, options)
|
240
236
|
|
241
237
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
242
238
|
end
|
243
239
|
|
240
|
+
|
244
241
|
# Renames a table.
|
245
242
|
#
|
246
243
|
# Example:
|
@@ -265,9 +262,17 @@ module ArJdbc
|
|
265
262
|
def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
|
266
263
|
alter_table(table_name) do |definition|
|
267
264
|
definition.remove_column column_name
|
268
|
-
definition.foreign_keys.delete_if
|
269
|
-
|
265
|
+
definition.foreign_keys.delete_if { |fk| fk.column == column_name.to_s }
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def remove_columns(table_name, *column_names, type: nil, **options) # :nodoc:
|
270
|
+
alter_table(table_name) do |definition|
|
271
|
+
column_names.each do |column_name|
|
272
|
+
definition.remove_column column_name
|
270
273
|
end
|
274
|
+
column_names = column_names.map(&:to_s)
|
275
|
+
definition.foreign_keys.delete_if { |fk| column_names.include?(fk.column) }
|
271
276
|
end
|
272
277
|
end
|
273
278
|
|
@@ -291,8 +296,8 @@ module ArJdbc
|
|
291
296
|
def change_column(table_name, column_name, type, **options) #:nodoc:
|
292
297
|
alter_table(table_name) do |definition|
|
293
298
|
definition[column_name].instance_eval do
|
294
|
-
self.type
|
295
|
-
|
299
|
+
self.type = aliased_types(type.to_s, type)
|
300
|
+
self.options.merge!(options)
|
296
301
|
end
|
297
302
|
end
|
298
303
|
end
|
@@ -322,18 +327,6 @@ module ArJdbc
|
|
322
327
|
end
|
323
328
|
end
|
324
329
|
|
325
|
-
def insert_fixtures_set(fixture_set, tables_to_delete = [])
|
326
|
-
disable_referential_integrity do
|
327
|
-
transaction(requires_new: true) do
|
328
|
-
tables_to_delete.each { |table| delete "DELETE FROM #{quote_table_name(table)}", "Fixture Delete" }
|
329
|
-
|
330
|
-
fixture_set.each do |table_name, rows|
|
331
|
-
rows.each { |row| insert_fixture(row, table_name) }
|
332
|
-
end
|
333
|
-
end
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
330
|
def build_insert_sql(insert) # :nodoc:
|
338
331
|
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
339
332
|
|
@@ -341,23 +334,23 @@ module ArJdbc
|
|
341
334
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
342
335
|
elsif insert.update_duplicates?
|
343
336
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
344
|
-
|
345
|
-
|
337
|
+
if insert.raw_update_sql?
|
338
|
+
sql << insert.raw_update_sql
|
339
|
+
else
|
340
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
|
341
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
342
|
+
end
|
346
343
|
end
|
347
344
|
|
348
345
|
sql
|
349
346
|
end
|
350
347
|
|
351
|
-
def shared_cache?
|
352
|
-
config
|
348
|
+
def shared_cache? # :nodoc:
|
349
|
+
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
|
353
350
|
end
|
354
351
|
|
355
352
|
def get_database_version # :nodoc:
|
356
|
-
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
357
|
-
end
|
358
|
-
|
359
|
-
def build_truncate_statement(table_name)
|
360
|
-
"DELETE FROM #{quote_table_name(table_name)}"
|
353
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
|
361
354
|
end
|
362
355
|
|
363
356
|
def check_version
|
@@ -380,6 +373,34 @@ module ArJdbc
|
|
380
373
|
end
|
381
374
|
alias column_definitions table_structure
|
382
375
|
|
376
|
+
def extract_value_from_default(default)
|
377
|
+
case default
|
378
|
+
when /^null$/i
|
379
|
+
nil
|
380
|
+
# Quoted types
|
381
|
+
when /^'(.*)'$/m
|
382
|
+
$1.gsub("''", "'")
|
383
|
+
# Quoted types
|
384
|
+
when /^"(.*)"$/m
|
385
|
+
$1.gsub('""', '"')
|
386
|
+
# Numeric types
|
387
|
+
when /\A-?\d+(\.\d*)?\z/
|
388
|
+
$&
|
389
|
+
else
|
390
|
+
# Anything else is blank or some function
|
391
|
+
# and we can't know the value of that, so return nil.
|
392
|
+
nil
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
def extract_default_function(default_value, default)
|
397
|
+
default if has_default_function?(default_value, default)
|
398
|
+
end
|
399
|
+
|
400
|
+
def has_default_function?(default_value, default)
|
401
|
+
!default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
402
|
+
end
|
403
|
+
|
383
404
|
# See: https://www.sqlite.org/lang_altertable.html
|
384
405
|
# SQLite has an additional restriction on the ALTER TABLE statement
|
385
406
|
def invalid_alter_table_type?(type, options)
|
@@ -440,8 +461,13 @@ module ArJdbc
|
|
440
461
|
options[:rename][column.name.to_sym] ||
|
441
462
|
column.name) : column.name
|
442
463
|
|
464
|
+
if column.has_default?
|
465
|
+
type = lookup_cast_type_from_column(column)
|
466
|
+
default = type.deserialize(column.default)
|
467
|
+
end
|
468
|
+
|
443
469
|
@definition.column(column_name, column.type,
|
444
|
-
limit: column.limit, default:
|
470
|
+
limit: column.limit, default: default,
|
445
471
|
precision: column.precision, scale: column.scale,
|
446
472
|
null: column.null, collation: column.collation,
|
447
473
|
primary_key: column_name == from_primary_key
|
@@ -459,9 +485,6 @@ module ArJdbc
|
|
459
485
|
def copy_table_indexes(from, to, rename = {})
|
460
486
|
indexes(from).each do |index|
|
461
487
|
name = index.name
|
462
|
-
# indexes sqlite creates for internal use start with `sqlite_` and
|
463
|
-
# don't need to be copied
|
464
|
-
next if name.start_with?("sqlite_")
|
465
488
|
if to == "a#{from}"
|
466
489
|
name = "t#{name}"
|
467
490
|
elsif from == "a#{to}"
|
@@ -481,6 +504,7 @@ module ArJdbc
|
|
481
504
|
options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
|
482
505
|
options[:unique] = true if index.unique
|
483
506
|
options[:where] = index.where if index.where
|
507
|
+
options[:order] = index.orders if index.orders
|
484
508
|
add_index(to, columns, **options)
|
485
509
|
end
|
486
510
|
end
|
@@ -500,20 +524,22 @@ module ArJdbc
|
|
500
524
|
end
|
501
525
|
|
502
526
|
def translate_exception(exception, message:, sql:, binds:)
|
503
|
-
case exception.message
|
504
527
|
# SQLite 3.8.2 returns a newly formatted error message:
|
505
528
|
# UNIQUE constraint failed: *table_name*.*column_name*
|
506
529
|
# Older versions of SQLite return:
|
507
530
|
# column *column_name* is not unique
|
508
|
-
|
531
|
+
if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
|
509
532
|
# DIFFERENCE: FQN
|
510
533
|
::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds)
|
511
|
-
|
534
|
+
elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
|
512
535
|
# DIFFERENCE: FQN
|
513
536
|
::ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds)
|
514
|
-
|
537
|
+
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
515
538
|
# DIFFERENCE: FQN
|
516
539
|
::ActiveRecord::InvalidForeignKey.new(message, sql: sql, binds: binds)
|
540
|
+
elsif exception.message.match?(/called on a closed database/i)
|
541
|
+
# DIFFERENCE: FQN
|
542
|
+
::ActiveRecord::ConnectionNotEstablished.new(exception)
|
517
543
|
else
|
518
544
|
super
|
519
545
|
end
|
@@ -533,12 +559,12 @@ module ArJdbc
|
|
533
559
|
# Result will have following sample string
|
534
560
|
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
535
561
|
# "password_digest" varchar COLLATE "NOCASE");
|
536
|
-
result =
|
562
|
+
result = query_value(sql, "SCHEMA")
|
537
563
|
|
538
564
|
if result
|
539
565
|
# Splitting with left parentheses and discarding the first part will return all
|
540
566
|
# columns separated with comma(,).
|
541
|
-
columns_string = result
|
567
|
+
columns_string = result.split("(", 2).last
|
542
568
|
|
543
569
|
columns_string.split(",").each do |column_string|
|
544
570
|
# This regex will match the column name and collation type and will save
|
@@ -564,7 +590,21 @@ module ArJdbc
|
|
564
590
|
Arel::Visitors::SQLite.new(self)
|
565
591
|
end
|
566
592
|
|
593
|
+
def build_statement_pool
|
594
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
595
|
+
end
|
596
|
+
|
597
|
+
def connect
|
598
|
+
@connection = ::SQLite3::Database.new(
|
599
|
+
@config[:database].to_s,
|
600
|
+
@config.merge(results_as_hash: true)
|
601
|
+
)
|
602
|
+
end
|
603
|
+
|
567
604
|
def configure_connection
|
605
|
+
# FIXME: missing from adapter
|
606
|
+
# @connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
|
607
|
+
|
568
608
|
execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
569
609
|
end
|
570
610
|
|
@@ -720,11 +760,15 @@ module ActiveRecord::ConnectionAdapters
|
|
720
760
|
|
721
761
|
# because the JDBC driver doesn't like multiple SQL statements in one JDBC statement
|
722
762
|
def combine_multi_statements(total_sql)
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
763
|
+
total_sql
|
764
|
+
end
|
765
|
+
|
766
|
+
# combine
|
767
|
+
def write_query?(sql) # :nodoc:
|
768
|
+
return sql.any? { |stmt| super(stmt) } if sql.kind_of? Array
|
769
|
+
!READ_QUERY.match?(sql)
|
770
|
+
rescue ArgumentError # Invalid encoding
|
771
|
+
!READ_QUERY.match?(sql.b)
|
728
772
|
end
|
729
773
|
|
730
774
|
def initialize_type_map(m = type_map)
|
@@ -11,18 +11,6 @@ module ArJdbc
|
|
11
11
|
require 'arjdbc/tasks/jdbc_database_tasks'
|
12
12
|
require 'arjdbc/tasks/sqlite_database_tasks_patch'
|
13
13
|
require 'arjdbc/tasks/mssql_database_tasks'
|
14
|
-
#require 'arjdbc/tasks/db2_database_tasks'
|
15
|
-
#require 'arjdbc/tasks/derby_database_tasks'
|
16
|
-
#require 'arjdbc/tasks/h2_database_tasks'
|
17
|
-
#require 'arjdbc/tasks/hsqldb_database_tasks'
|
18
|
-
|
19
|
-
# re-invent built-in (but deprecated on 4.0) tasks :
|
20
|
-
# tasks for custom (JDBC) adapters :
|
21
|
-
#register_tasks(/db2/, DB2DatabaseTasks)
|
22
|
-
#register_tasks(/derby/, DerbyDatabaseTasks)
|
23
|
-
#register_tasks(/h2/, H2DatabaseTasks)
|
24
|
-
#register_tasks(/hsqldb/, HSQLDBDatabaseTasks)
|
25
|
-
# (default) generic JDBC task :
|
26
14
|
register_tasks(/^jdbc$/, JdbcDatabaseTasks)
|
27
15
|
register_tasks(/sqlserver/, MSSQLDatabaseTasks)
|
28
16
|
|
@@ -31,33 +31,11 @@ module ArJdbc
|
|
31
31
|
SerializedAttributes.dump_column_value(self, column)
|
32
32
|
end
|
33
33
|
|
34
|
-
if defined? ActiveRecord::Type::Serialized # ArJdbc::AR42
|
35
|
-
|
36
34
|
def self.dump_column_value(record, column)
|
37
35
|
value = record[ column.name.to_s ]
|
38
36
|
column.cast_type.type_cast_for_database(value)
|
39
37
|
end
|
40
38
|
|
41
|
-
else
|
42
|
-
|
43
|
-
def self.dump_column_value(record, column)
|
44
|
-
value = record[ name = column.name.to_s ]
|
45
|
-
if record.class.respond_to?(:serialized_attributes)
|
46
|
-
if coder = record.class.serialized_attributes[name]
|
47
|
-
value = coder.respond_to?(:dump) ? coder.dump(value) : value.to_yaml
|
48
|
-
end
|
49
|
-
else
|
50
|
-
if record.respond_to?(:unserializable_attribute?)
|
51
|
-
value = value.to_yaml if record.unserializable_attribute?(name, column)
|
52
|
-
else
|
53
|
-
value = value.to_yaml if value.is_a?(Hash)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
value
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
39
|
def self.setup(lob_type = nil, after_save_alias = nil)
|
62
40
|
ActiveRecord::Base.send :include, self # include SerializedAttributes
|
63
41
|
ActiveRecord::Base.lob_type = lob_type unless lob_type.nil?
|
@@ -4,7 +4,8 @@ module ArJdbc
|
|
4
4
|
module Util
|
5
5
|
module TableCopier
|
6
6
|
|
7
|
-
# taken from SQLite adapter, code loosely based on
|
7
|
+
# taken from SQLite adapter, code loosely based on
|
8
|
+
# https://github.com/rails/rails/blob/d3e5118/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
|
8
9
|
|
9
10
|
# Performs changes for table by first copying (and preserving contents)
|
10
11
|
# into another (temporary) table, than alters and copies all data back.
|
data/lib/arjdbc/version.rb
CHANGED
data/rakelib/02-test.rake
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
test_tasks = [ 'test_mysql', 'test_sqlite3', 'test_postgresql_with_hint' ]
|
3
3
|
if defined?(JRUBY_VERSION)
|
4
|
-
test_tasks.push :
|
4
|
+
test_tasks.push :test_hsqldb, :test_h2
|
5
5
|
test_tasks.push :test_jndi, :test_jdbc
|
6
6
|
end
|
7
7
|
|
@@ -55,7 +55,6 @@ def test_task_for(adapter, options = {})
|
|
55
55
|
test_task
|
56
56
|
end
|
57
57
|
|
58
|
-
test_task_for :Derby, :desc => 'Run tests against (embedded) DerbyDB'
|
59
58
|
test_task_for :H2, :desc => 'Run tests against H2 database engine'
|
60
59
|
test_task_for :HSQLDB, :desc => 'Run tests against HyperSQL (Java) database'
|
61
60
|
test_task_for :MSSQL, :driver => :jtds, :database_name => 'MS-SQL (SQLServer)'
|
@@ -65,29 +64,22 @@ test_task_for :PostgreSQL, :driver => ENV['JDBC_POSTGRES_VERSION'] || 'postgres'
|
|
65
64
|
task :test_postgres => :test_postgresql # alias
|
66
65
|
test_task_for :SQLite3, :driver => ENV['JDBC_SQLITE_VERSION']
|
67
66
|
task :test_sqlite => :test_sqlite3 # alias
|
68
|
-
test_task_for :Firebird
|
69
67
|
|
70
68
|
test_task_for :MariaDB, :files => FileList["test/db/mysql/*_test.rb"] do |test_task| #, :prereqs => 'db:mysql'
|
71
69
|
test_task.ruby_opts << '-rdb/mariadb_config'
|
72
70
|
end
|
73
71
|
|
74
72
|
# ensure driver for these DBs is on your class-path
|
75
|
-
[ :Oracle
|
73
|
+
[ :Oracle ].each do |adapter|
|
76
74
|
test_task_for adapter, :desc => "Run tests against #{adapter} (ensure driver is on class-path)"
|
77
75
|
end
|
78
76
|
|
79
|
-
test_task_for :AS400, :desc => "Run tests against AS400 (DB2) (ensure driver is on class-path)",
|
80
|
-
:files => FileList["test/db2*_test.rb"] + FileList["test/db/db2/*_test.rb"]
|
81
|
-
|
82
|
-
#task :test_jdbc => [ :test_jdbc_mysql, :test_jdbc_derby ] if defined?(JRUBY_VERSION)
|
83
|
-
|
84
77
|
test_task_for 'JDBC', :desc => 'Run tests against plain JDBC adapter (uses MySQL and Derby)',
|
85
78
|
:prereqs => [ 'db:mysql' ], :files => FileList['test/*jdbc_*test.rb'] do |test_task|
|
86
|
-
test_task.libs << 'jdbc-mysql/lib'
|
79
|
+
test_task.libs << 'jdbc-mysql/lib'
|
87
80
|
end
|
88
81
|
|
89
82
|
# TODO since Derby AR 5.x support is not implemented we only run JNDI with MySQL :
|
90
|
-
#task :test_jndi => [ :test_jndi_mysql, :test_jndi_derby ] if defined?(JRUBY_VERSION)
|
91
83
|
task :test_jndi => [ :test_jndi_mysql ] if defined?(JRUBY_VERSION)
|
92
84
|
|
93
85
|
jndi_classpath = [ 'test/jars/tomcat-juli.jar', 'test/jars/tomcat-catalina.jar' ]
|
@@ -101,13 +93,6 @@ get_jndi_classpath_opt = lambda do
|
|
101
93
|
"-J-cp \"#{cp.join(File::PATH_SEPARATOR)}\""
|
102
94
|
end
|
103
95
|
|
104
|
-
test_task_for 'JNDI_Derby', :desc => 'Run tests against a Derby JNDI connection',
|
105
|
-
:prereqs => jndi_classpath, :files => FileList['test/*jndi_derby*test.rb'] do |test_task|
|
106
|
-
test_task.libs << 'jdbc-derby/lib'
|
107
|
-
test_task.ruby_opts << get_jndi_classpath_opt.call
|
108
|
-
#test_task.verbose = true
|
109
|
-
end
|
110
|
-
|
111
96
|
test_task_for 'JNDI_MySQL', :desc => 'Run tests against a MySQL JNDI connection',
|
112
97
|
:prereqs => [ 'db:mysql' ] + jndi_classpath, :files => FileList['test/*jndi_mysql*test.rb'] do |test_task|
|
113
98
|
test_task.libs << 'jdbc-mysql/lib'
|
@@ -720,6 +720,21 @@ public class RubyJdbcConnection extends RubyObject {
|
|
720
720
|
}
|
721
721
|
}
|
722
722
|
|
723
|
+
@JRubyMethod(name = "closed?")
|
724
|
+
public IRubyObject closed_p(ThreadContext context) {
|
725
|
+
try {
|
726
|
+
final Connection connection = getConnectionInternal(false);
|
727
|
+
|
728
|
+
if (connection == null) return context.fals;
|
729
|
+
|
730
|
+
// NOTE: isClosed method generally cannot be called to determine
|
731
|
+
// whether a connection to a database is valid or invalid ...
|
732
|
+
return context.runtime.newBoolean(connection.isClosed());
|
733
|
+
} catch (SQLException e) {
|
734
|
+
return handleException(context, e);
|
735
|
+
}
|
736
|
+
}
|
737
|
+
|
723
738
|
@JRubyMethod(name = "close")
|
724
739
|
public IRubyObject close(final ThreadContext context) {
|
725
740
|
final Connection connection = getConnection(false);
|
@@ -2642,8 +2657,8 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2642
2657
|
}
|
2643
2658
|
|
2644
2659
|
private String default_timezone(final ThreadContext context) {
|
2645
|
-
final
|
2646
|
-
return default_timezone.call(context,
|
2660
|
+
final RubyModule activeRecord = ActiveRecord(context);
|
2661
|
+
return default_timezone.call(context, activeRecord, activeRecord).asJavaString(); // :utc (or :local)
|
2647
2662
|
}
|
2648
2663
|
|
2649
2664
|
// ActiveRecord::Base.default_timezone
|
@@ -387,7 +387,20 @@ 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
|
-
|
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);
|
391
404
|
return;
|
392
405
|
}
|
393
406
|
|
@@ -402,6 +402,11 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
402
402
|
finally { close(statement); }
|
403
403
|
}
|
404
404
|
|
405
|
+
@JRubyMethod
|
406
|
+
public IRubyObject filename(ThreadContext context) {
|
407
|
+
return getConfigValue(context, "database");
|
408
|
+
}
|
409
|
+
|
405
410
|
@Override
|
406
411
|
@JRubyMethod(name = "rollback_savepoint", required = 1)
|
407
412
|
public IRubyObject rollback_savepoint(final ThreadContext context, final IRubyObject name) {
|
@@ -460,6 +465,34 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
460
465
|
return context.runtime.newBoolean(connection.isReadOnly());
|
461
466
|
}
|
462
467
|
|
468
|
+
// note: sqlite3 cext uses this same method but we do not combine all our statements
|
469
|
+
// into a single ; delimited string but leave it as an array of statements. This is
|
470
|
+
// because the JDBC way of handling batches is to use addBatch().
|
471
|
+
@JRubyMethod(name = "execute_batch2")
|
472
|
+
public IRubyObject execute_batch2(ThreadContext context, IRubyObject statementsArg) {
|
473
|
+
// Assume we will only call this with an array.
|
474
|
+
final RubyArray statements = (RubyArray) statementsArg;
|
475
|
+
return withConnection(context, connection -> {
|
476
|
+
Statement statement = null;
|
477
|
+
try {
|
478
|
+
statement = createStatement(context, connection);
|
479
|
+
|
480
|
+
int length = statements.getLength();
|
481
|
+
for (int i = 0; i < length; i++) {
|
482
|
+
statement.addBatch(sqlString(statements.eltOk(i)));
|
483
|
+
}
|
484
|
+
statement.executeBatch();
|
485
|
+
return context.nil;
|
486
|
+
} catch (final SQLException e) {
|
487
|
+
// Generate list semicolon list of statements which should match AR error formatting more.
|
488
|
+
debugErrorSQL(context, sqlString(statements.join(context, context.runtime.newString(";\n"))));
|
489
|
+
throw e;
|
490
|
+
} finally {
|
491
|
+
close(statement);
|
492
|
+
}
|
493
|
+
});
|
494
|
+
}
|
495
|
+
|
463
496
|
@Override
|
464
497
|
protected void setDecimalParameter(final ThreadContext context,
|
465
498
|
final Connection connection, final PreparedStatement statement,
|