activerecord-jdbc-alt-adapter 72.0.0.alpha1-java → 72.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/README.md +2 -2
- data/lib/arjdbc/abstract/core.rb +10 -1
- data/lib/arjdbc/abstract/transaction_support.rb +3 -1
- data/lib/arjdbc/jdbc/adapter.rb +0 -1
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/mssql/adapter.rb +12 -13
- data/lib/arjdbc/mssql/column.rb +16 -7
- data/lib/arjdbc/mssql/database_statements.rb +5 -0
- data/lib/arjdbc/mssql/schema_creation.rb +16 -0
- data/lib/arjdbc/mssql/schema_statements.rb +43 -1
- data/lib/arjdbc/mysql/adapter.rb +1 -10
- data/lib/arjdbc/mysql/adapter_hash_config.rb +12 -2
- data/lib/arjdbc/postgresql/adapter.rb +14 -2
- data/lib/arjdbc/sqlite3/adapter.rb +164 -66
- data/lib/arjdbc/sqlite3/column.rb +17 -3
- data/lib/arjdbc/sqlite3/pragmas.rb +105 -0
- data/lib/arjdbc/version.rb +1 -1
- data/lib/arjdbc.rb +9 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +5 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +12 -2
- metadata +4 -4
- data/lib/arjdbc/jdbc/base_ext.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f7f249466042cd80343c394b99718ff78c5754a2105f06eaf87672f25d2d54e
|
4
|
+
data.tar.gz: cc61ff60b8f74d3fa100156f59c4cdd198865e87d9be401bf833a2e0ed623291
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25c0368cbbeecad8536caa0ffadff34c28aacb0ff4a66f2085016224322645add68206976dbcef42475bdc39c1ffd47da761eacd919292ae15e6711c44935317
|
7
|
+
data.tar.gz: 9beed2af43699a9349a9284c252fd97db3c0110510892ac6937b5ace94df6be02269fb7360caa653fb864a4326d6582e958bea1afe4c69d8adaabec96af2a671
|
data/README.md
CHANGED
@@ -175,7 +175,7 @@ adapters are available:
|
|
175
175
|
|
176
176
|
```yml
|
177
177
|
development:
|
178
|
-
adapter: mysql2
|
178
|
+
adapter: mysql2
|
179
179
|
database: blog_development
|
180
180
|
username: blog
|
181
181
|
password: 1234
|
@@ -199,7 +199,7 @@ or preferably using the *properties:* syntax:
|
|
199
199
|
|
200
200
|
```yml
|
201
201
|
production:
|
202
|
-
adapter:
|
202
|
+
adapter: mysql2
|
203
203
|
username: blog
|
204
204
|
password: blog
|
205
205
|
url: "jdbc:mysql://localhost:3306/blog?profileSQL=true"
|
data/lib/arjdbc/abstract/core.rb
CHANGED
@@ -60,7 +60,16 @@ module ArJdbc
|
|
60
60
|
# this version of log() automatically fills type_casted_binds from binds if necessary
|
61
61
|
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false)
|
62
62
|
if binds.any? && (type_casted_binds.nil? || type_casted_binds.empty?)
|
63
|
-
type_casted_binds =
|
63
|
+
type_casted_binds = lambda {
|
64
|
+
# extract_raw_bind_values
|
65
|
+
binds.map do |bind|
|
66
|
+
if bind.respond_to?(:value_for_database)
|
67
|
+
bind.value_for_database
|
68
|
+
else
|
69
|
+
bind
|
70
|
+
end
|
71
|
+
end
|
72
|
+
}
|
64
73
|
end
|
65
74
|
super
|
66
75
|
end
|
data/lib/arjdbc/jdbc/adapter.rb
CHANGED
Binary file
|
data/lib/arjdbc/mssql/adapter.rb
CHANGED
@@ -54,6 +54,7 @@ module ActiveRecord
|
|
54
54
|
include MSSQL::ExplainSupport
|
55
55
|
include MSSQL::DatabaseLimits
|
56
56
|
|
57
|
+
# Latin1-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-sensitive
|
57
58
|
@cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS'
|
58
59
|
|
59
60
|
class << self
|
@@ -265,29 +266,25 @@ module ActiveRecord
|
|
265
266
|
|
266
267
|
alias_method :current_schema=, :default_schema=
|
267
268
|
|
268
|
-
# FIXME: This needs to be fixed when we implement the collation per
|
269
|
-
|
270
|
-
def default_uniqueness_comparison(attribute, value) # :nodoc:
|
269
|
+
# FIXME: This needs to be fixed when we implement the collation per column
|
270
|
+
def case_sensitive_comparison(attribute, value)
|
271
271
|
column = column_for_attribute(attribute)
|
272
272
|
|
273
|
-
|
274
|
-
|
275
|
-
|
273
|
+
case_sensitive = collation && collation.match(/_CS/)
|
274
|
+
|
275
|
+
if %i[string text].include?(column.type) && !case_sensitive && !value.nil?
|
276
276
|
attribute.eq(Arel::Nodes::Bin.new(value))
|
277
277
|
else
|
278
278
|
super
|
279
279
|
end
|
280
280
|
end
|
281
281
|
|
282
|
-
def
|
283
|
-
|
282
|
+
def can_perform_case_insensitive_comparison_for?(column)
|
283
|
+
case_sensitive = collation && collation.match(/_CS/)
|
284
284
|
|
285
|
-
|
286
|
-
attribute.eq(Arel::Nodes::Bin.new(value))
|
287
|
-
else
|
288
|
-
super
|
289
|
-
end
|
285
|
+
%i[string text].include?(column.type) && !case_sensitive
|
290
286
|
end
|
287
|
+
private :can_perform_case_insensitive_comparison_for?
|
291
288
|
|
292
289
|
def configure_connection
|
293
290
|
# Here goes initial settings per connection
|
@@ -485,6 +482,8 @@ module ActiveRecord
|
|
485
482
|
ConnectionNotEstablished.new(exception)
|
486
483
|
when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
|
487
484
|
RecordNotUnique.new(message, sql: sql, binds: binds)
|
485
|
+
when /Violation of PRIMARY KEY constraint .* Cannot insert duplicate key in object .* The duplicate key value is/i
|
486
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
488
487
|
when /Lock request time out period exceeded/i
|
489
488
|
LockTimeout.new(message, sql: sql, binds: binds)
|
490
489
|
when /The .* statement conflicted with the FOREIGN KEY constraint/
|
data/lib/arjdbc/mssql/column.rb
CHANGED
@@ -9,17 +9,26 @@ module ActiveRecord
|
|
9
9
|
def initialize(name, raw_default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil)
|
10
10
|
@table_name = table_name
|
11
11
|
|
12
|
-
|
12
|
+
default_val, default_fun = extract_default(raw_default)
|
13
13
|
|
14
|
-
super(name,
|
14
|
+
super(name, default_val, sql_type_metadata, null, default_fun, collation: collation, comment: comment)
|
15
15
|
end
|
16
16
|
|
17
17
|
def extract_default(value)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
return [nil, nil] unless value
|
19
|
+
|
20
|
+
case value
|
21
|
+
when /\A\(N?'(.*)'\)\Z/m
|
22
|
+
[unquote_string(Regexp.last_match[1]), nil]
|
23
|
+
when /\A\(\((.*)\)\)\Z/
|
24
|
+
[unquote_string(Regexp.last_match[1]), nil]
|
25
|
+
when /\A\((\w+\(\))\)\Z/
|
26
|
+
[nil, unquote_string(Regexp.last_match[1])]
|
27
|
+
else
|
28
|
+
# return nil if default does not match the patterns to avoid
|
29
|
+
# any unexpected errors.
|
30
|
+
[nil, nil]
|
31
|
+
end
|
23
32
|
end
|
24
33
|
|
25
34
|
def unquote_string(string)
|
@@ -31,6 +31,8 @@ module ActiveRecord
|
|
31
31
|
|
32
32
|
def write_query?(sql) # :nodoc:
|
33
33
|
!READ_QUERY.match?(sql)
|
34
|
+
rescue ArgumentError # Invalid encoding
|
35
|
+
!READ_QUERY.match?(sql.b)
|
34
36
|
end
|
35
37
|
|
36
38
|
# Internal method to test different isolation levels supported by this
|
@@ -176,6 +178,9 @@ module ActiveRecord
|
|
176
178
|
log(sql, name, binds) do
|
177
179
|
with_raw_connection do |conn|
|
178
180
|
result = conditional_indentity_insert(sql) do
|
181
|
+
# DEPRECATION WARNING: to_time will always preserve the timezone offset of the receiver in Rails 8.0.
|
182
|
+
# To opt in to the new behavior, set `ActiveSupport.to_time_preserves_timezone = true`.
|
183
|
+
# (called from block in execute_insert_pk
|
179
184
|
conn.execute_insert_pk(sql, binds, pk)
|
180
185
|
end
|
181
186
|
verified!
|
@@ -38,6 +38,22 @@ module ActiveRecord
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
def visit_CreateIndexDefinition(o)
|
42
|
+
index = o.index
|
43
|
+
|
44
|
+
sql = []
|
45
|
+
sql << "IF NOT EXISTS (SELECT name FROM sysindexes WHERE name = '#{o.index.name}')" if o.if_not_exists
|
46
|
+
sql << "CREATE"
|
47
|
+
sql << "UNIQUE" if index.unique
|
48
|
+
sql << index.type.upcase if index.type
|
49
|
+
sql << "INDEX"
|
50
|
+
sql << "#{quote_column_name(index.name)} ON #{quote_table_name(index.table)}"
|
51
|
+
sql << "(#{quoted_columns(index)})"
|
52
|
+
sql << "WHERE #{index.where}" if index.where
|
53
|
+
|
54
|
+
sql.join(" ")
|
55
|
+
end
|
56
|
+
|
41
57
|
def add_column_options!(sql, options)
|
42
58
|
sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
|
43
59
|
|
@@ -80,8 +80,50 @@ module ActiveRecord
|
|
80
80
|
valid_raw_connection.primary_keys(table_name)
|
81
81
|
end
|
82
82
|
|
83
|
+
def build_change_column_definition(table_name, column_name, type, **options) # :nodoc:
|
84
|
+
td = create_table_definition(table_name)
|
85
|
+
cd = td.new_column_definition(column_name, type, **options)
|
86
|
+
ChangeColumnDefinition.new(cd, column_name)
|
87
|
+
end
|
88
|
+
|
89
|
+
def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
|
90
|
+
column = column_for(table_name, column_name)
|
91
|
+
return unless column
|
92
|
+
|
93
|
+
default = extract_new_default_value(default_or_changes)
|
94
|
+
ChangeColumnDefaultDefinition.new(column, default)
|
95
|
+
end
|
96
|
+
|
83
97
|
def foreign_keys(table_name)
|
84
|
-
valid_raw_connection.foreign_keys(table_name)
|
98
|
+
# valid_raw_connection.foreign_keys(table_name)
|
99
|
+
fk_info = execute_procedure(:sp_fkeys, nil, nil, nil, table_name, nil)
|
100
|
+
|
101
|
+
grouped_fk = fk_info.group_by { |row| row["FK_NAME"] }.values.each { |group| group.sort_by! { |row| row["KEY_SEQ"] } }
|
102
|
+
grouped_fk.map do |group|
|
103
|
+
row = group.first
|
104
|
+
options = {
|
105
|
+
name: row["FK_NAME"],
|
106
|
+
on_update: extract_foreign_key_action("update", row["FK_NAME"]),
|
107
|
+
on_delete: extract_foreign_key_action("delete", row["FK_NAME"])
|
108
|
+
}
|
109
|
+
|
110
|
+
if group.one?
|
111
|
+
options[:column] = row["FKCOLUMN_NAME"]
|
112
|
+
options[:primary_key] = row["PKCOLUMN_NAME"]
|
113
|
+
else
|
114
|
+
options[:column] = group.map { |row| row["FKCOLUMN_NAME"] }
|
115
|
+
options[:primary_key] = group.map { |row| row["PKCOLUMN_NAME"] }
|
116
|
+
end
|
117
|
+
|
118
|
+
ForeignKeyDefinition.new(table_name, row["PKTABLE_NAME"], options)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def extract_foreign_key_action(action, fk_name)
|
123
|
+
case select_value("SELECT #{action}_referential_action_desc FROM sys.foreign_keys WHERE name = '#{fk_name}'")
|
124
|
+
when "CASCADE" then :cascade
|
125
|
+
when "SET_NULL" then :nullify
|
126
|
+
end
|
85
127
|
end
|
86
128
|
|
87
129
|
def charset
|
data/lib/arjdbc/mysql/adapter.rb
CHANGED
@@ -84,15 +84,6 @@ module ActiveRecord
|
|
84
84
|
@connection_parameters = conn_params
|
85
85
|
end
|
86
86
|
|
87
|
-
def self.database_exists?(config)
|
88
|
-
conn = ActiveRecord::Base.mysql2_connection(config)
|
89
|
-
conn && conn.really_valid?
|
90
|
-
rescue ActiveRecord::NoDatabaseError
|
91
|
-
false
|
92
|
-
ensure
|
93
|
-
conn.disconnect! if conn
|
94
|
-
end
|
95
|
-
|
96
87
|
def supports_json?
|
97
88
|
!mariadb? && database_version >= '5.7.8'
|
98
89
|
end
|
@@ -250,7 +241,7 @@ module ActiveRecord
|
|
250
241
|
|
251
242
|
# e.g. "5.7.20-0ubuntu0.16.04.1"
|
252
243
|
def full_version
|
253
|
-
|
244
|
+
database_version.full_version_string
|
254
245
|
end
|
255
246
|
|
256
247
|
def get_full_version
|
@@ -7,7 +7,9 @@ module ArJdbc
|
|
7
7
|
|
8
8
|
load_jdbc_driver
|
9
9
|
|
10
|
-
|
10
|
+
# don't set driver if it's explicitly set to false
|
11
|
+
# allow Java's service discovery mechanism (with connector/j 8.0)
|
12
|
+
config[:driver] ||= database_driver_name if config[:driver] != false
|
11
13
|
|
12
14
|
host = (config[:host] ||= "localhost")
|
13
15
|
port = (config[:port] ||= 3306)
|
@@ -40,7 +42,7 @@ module ArJdbc
|
|
40
42
|
def build_properties(config)
|
41
43
|
properties = config[:properties] || {}
|
42
44
|
|
43
|
-
properties["zeroDateTimeBehavior"] ||=
|
45
|
+
properties["zeroDateTimeBehavior"] ||= default_zero_date_time_behavior(config[:driver])
|
44
46
|
|
45
47
|
properties["jdbcCompliantTruncation"] ||= false
|
46
48
|
|
@@ -88,6 +90,14 @@ module ArJdbc
|
|
88
90
|
properties
|
89
91
|
end
|
90
92
|
|
93
|
+
def default_zero_date_time_behavior(driver)
|
94
|
+
return "CONVERT_TO_NULL" if driver == false
|
95
|
+
|
96
|
+
return "CONVERT_TO_NULL" if driver.start_with?("com.mysql.cj.")
|
97
|
+
|
98
|
+
"convertToNull"
|
99
|
+
end
|
100
|
+
|
91
101
|
# See https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-charsets.html
|
92
102
|
# to charset-name (characterEncoding=...)
|
93
103
|
def convert_mysql_encoding(config)
|
@@ -345,7 +345,7 @@ module ArJdbc
|
|
345
345
|
type.typname AS name,
|
346
346
|
type.OID AS oid,
|
347
347
|
n.nspname AS schema,
|
348
|
-
|
348
|
+
array_agg(enum.enumlabel ORDER BY enum.enumsortorder) AS value
|
349
349
|
FROM pg_enum AS enum
|
350
350
|
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
|
351
351
|
JOIN pg_namespace n ON type.typnamespace = n.oid
|
@@ -842,6 +842,15 @@ module ActiveRecord::ConnectionAdapters
|
|
842
842
|
# setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
|
843
843
|
class_attribute :datetime_type, default: :timestamp
|
844
844
|
|
845
|
+
##
|
846
|
+
# :singleton-method:
|
847
|
+
# Toggles automatic decoding of date columns.
|
848
|
+
#
|
849
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> String
|
850
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.decode_dates = true
|
851
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> Date
|
852
|
+
class_attribute :decode_dates, default: false
|
853
|
+
|
845
854
|
# Try to use as much of the built in postgres logic as possible
|
846
855
|
# maybe someday we can extend the actual adapter
|
847
856
|
include ActiveRecord::ConnectionAdapters::PostgreSQL::ReferentialIntegrity
|
@@ -855,9 +864,12 @@ module ActiveRecord::ConnectionAdapters
|
|
855
864
|
include ArJdbc::Abstract::DatabaseStatements
|
856
865
|
include ArJdbc::Abstract::StatementCache
|
857
866
|
include ArJdbc::Abstract::TransactionSupport
|
858
|
-
include ArJdbc::PostgreSQL
|
859
867
|
include ArJdbc::PostgreSQLConfig
|
860
868
|
|
869
|
+
# NOTE: after AR refactor quote_column_name became class and instance method
|
870
|
+
include ArJdbc::PostgreSQL
|
871
|
+
extend ArJdbc::PostgreSQL
|
872
|
+
|
861
873
|
require 'arjdbc/postgresql/oid_types'
|
862
874
|
include ::ArJdbc::PostgreSQL::OIDTypes
|
863
875
|
include ::ArJdbc::PostgreSQL::DatabaseStatements
|
@@ -18,6 +18,7 @@ require "active_record/connection_adapters/sqlite3/schema_statements"
|
|
18
18
|
require "active_support/core_ext/class/attribute"
|
19
19
|
require "arjdbc/sqlite3/column"
|
20
20
|
require "arjdbc/sqlite3/adapter_hash_config"
|
21
|
+
require "arjdbc/sqlite3/pragmas"
|
21
22
|
|
22
23
|
require "arjdbc/abstract/relation_query_attribute_monkey_patch"
|
23
24
|
|
@@ -59,6 +60,7 @@ module ArJdbc
|
|
59
60
|
# DIFFERENCE: Some common constant names to reduce differences in rest of this module from AR5 version
|
60
61
|
ConnectionAdapters = ::ActiveRecord::ConnectionAdapters
|
61
62
|
IndexDefinition = ::ActiveRecord::ConnectionAdapters::IndexDefinition
|
63
|
+
ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
|
62
64
|
Quoting = ::ActiveRecord::ConnectionAdapters::SQLite3::Quoting
|
63
65
|
RecordNotUnique = ::ActiveRecord::RecordNotUnique
|
64
66
|
SchemaCreation = ConnectionAdapters::SQLite3::SchemaCreation
|
@@ -79,6 +81,15 @@ module ArJdbc
|
|
79
81
|
json: { name: "json" },
|
80
82
|
}
|
81
83
|
|
84
|
+
DEFAULT_PRAGMAS = {
|
85
|
+
"foreign_keys" => true,
|
86
|
+
"journal_mode" => :wal,
|
87
|
+
"synchronous" => :normal,
|
88
|
+
"mmap_size" => 134217728, # 128 megabytes
|
89
|
+
"journal_size_limit" => 67108864, # 64 megabytes
|
90
|
+
"cache_size" => 2000
|
91
|
+
}
|
92
|
+
|
82
93
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
83
94
|
private
|
84
95
|
def dealloc(stmt)
|
@@ -154,8 +165,23 @@ module ArJdbc
|
|
154
165
|
!@memory_database
|
155
166
|
end
|
156
167
|
|
168
|
+
def supports_virtual_columns?
|
169
|
+
database_version >= "3.31.0"
|
170
|
+
end
|
171
|
+
|
172
|
+
def connected?
|
173
|
+
!(@raw_connection.nil? || @raw_connection.closed?)
|
174
|
+
end
|
175
|
+
|
157
176
|
def active?
|
158
|
-
|
177
|
+
if connected?
|
178
|
+
@lock.synchronize do
|
179
|
+
if @raw_connection&.active?
|
180
|
+
verified!
|
181
|
+
true
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end || false
|
159
185
|
end
|
160
186
|
|
161
187
|
def return_value_after_insert?(column) # :nodoc:
|
@@ -167,10 +193,11 @@ module ArJdbc
|
|
167
193
|
# Disconnects from the database if already connected. Otherwise, this
|
168
194
|
# method does nothing.
|
169
195
|
def disconnect!
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
196
|
+
@lock.synchronize do
|
197
|
+
super
|
198
|
+
@raw_connection&.close rescue nil
|
199
|
+
@raw_connection = nil
|
200
|
+
end
|
174
201
|
end
|
175
202
|
|
176
203
|
def supports_index_sort_order?
|
@@ -235,7 +262,6 @@ module ArJdbc
|
|
235
262
|
internal_exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
236
263
|
end
|
237
264
|
|
238
|
-
|
239
265
|
# Renames a table.
|
240
266
|
#
|
241
267
|
# Example:
|
@@ -324,15 +350,31 @@ module ArJdbc
|
|
324
350
|
end
|
325
351
|
alias :add_belongs_to :add_reference
|
326
352
|
|
353
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("([^"]+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
354
|
+
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
327
355
|
def foreign_keys(table_name)
|
328
356
|
# SQLite returns 1 row for each column of composite foreign keys.
|
329
357
|
fk_info = internal_exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
|
358
|
+
# Deferred or immediate foreign keys can only be seen in the CREATE TABLE sql
|
359
|
+
fk_defs = table_structure_sql(table_name)
|
360
|
+
.select do |column_string|
|
361
|
+
column_string.start_with?("CONSTRAINT") &&
|
362
|
+
column_string.include?("FOREIGN KEY")
|
363
|
+
end
|
364
|
+
.to_h do |fk_string|
|
365
|
+
_, from, table, to = fk_string.match(FK_REGEX).to_a
|
366
|
+
_, mode = fk_string.match(DEFERRABLE_REGEX).to_a
|
367
|
+
deferred = mode&.downcase&.to_sym || false
|
368
|
+
[[table, from, to], deferred]
|
369
|
+
end
|
370
|
+
|
330
371
|
grouped_fk = fk_info.group_by { |row| row["id"] }.values.each { |group| group.sort_by! { |row| row["seq"] } }
|
331
372
|
grouped_fk.map do |group|
|
332
373
|
row = group.first
|
333
374
|
options = {
|
334
375
|
on_delete: extract_foreign_key_action(row["on_delete"]),
|
335
|
-
on_update: extract_foreign_key_action(row["on_update"])
|
376
|
+
on_update: extract_foreign_key_action(row["on_update"]),
|
377
|
+
deferrable: fk_defs[[row["table"], row["from"], row["to"]]]
|
336
378
|
}
|
337
379
|
|
338
380
|
if group.one?
|
@@ -342,8 +384,7 @@ module ArJdbc
|
|
342
384
|
options[:column] = group.map { |row| row["from"] }
|
343
385
|
options[:primary_key] = group.map { |row| row["to"] }
|
344
386
|
end
|
345
|
-
|
346
|
-
::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(table_name, row["table"], options)
|
387
|
+
ForeignKeyDefinition.new(table_name, row["table"], options)
|
347
388
|
end
|
348
389
|
end
|
349
390
|
|
@@ -390,7 +431,14 @@ module ArJdbc
|
|
390
431
|
|
391
432
|
type_metadata = fetch_type_metadata(field["type"])
|
392
433
|
default_value = extract_value_from_default(default)
|
393
|
-
|
434
|
+
generated_type = extract_generated_type(field)
|
435
|
+
|
436
|
+
if generated_type.present?
|
437
|
+
default_function = default
|
438
|
+
else
|
439
|
+
default_function = extract_default_function(default_value, default)
|
440
|
+
end
|
441
|
+
|
394
442
|
rowid = is_column_the_rowid?(field, definitions)
|
395
443
|
|
396
444
|
ActiveRecord::ConnectionAdapters::SQLite3Column.new(
|
@@ -401,7 +449,8 @@ module ArJdbc
|
|
401
449
|
default_function,
|
402
450
|
collation: field["collation"],
|
403
451
|
auto_increment: field["auto_increment"],
|
404
|
-
rowid: rowid
|
452
|
+
rowid: rowid,
|
453
|
+
generated_type: generated_type
|
405
454
|
)
|
406
455
|
end
|
407
456
|
|
@@ -413,7 +462,12 @@ module ArJdbc
|
|
413
462
|
end
|
414
463
|
|
415
464
|
def table_structure(table_name)
|
416
|
-
structure =
|
465
|
+
structure = if supports_virtual_columns?
|
466
|
+
internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA")
|
467
|
+
else
|
468
|
+
internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
469
|
+
end
|
470
|
+
|
417
471
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
418
472
|
table_structure_with_collation(table_name, structure)
|
419
473
|
end
|
@@ -453,8 +507,9 @@ module ArJdbc
|
|
453
507
|
# See: https://www.sqlite.org/lang_altertable.html
|
454
508
|
# SQLite has an additional restriction on the ALTER TABLE statement
|
455
509
|
def invalid_alter_table_type?(type, options)
|
456
|
-
type
|
457
|
-
options[:null] == false && options[:default].nil?
|
510
|
+
type == :primary_key || options[:primary_key] ||
|
511
|
+
options[:null] == false && options[:default].nil? ||
|
512
|
+
(type == :virtual && options[:stored])
|
458
513
|
end
|
459
514
|
|
460
515
|
def alter_table(
|
@@ -510,12 +565,6 @@ module ArJdbc
|
|
510
565
|
options[:rename][column.name.to_sym] ||
|
511
566
|
column.name) : column.name
|
512
567
|
|
513
|
-
if column.has_default?
|
514
|
-
type = lookup_cast_type_from_column(column)
|
515
|
-
default = type.deserialize(column.default)
|
516
|
-
default = -> { column.default_function } if default.nil?
|
517
|
-
end
|
518
|
-
|
519
568
|
column_options = {
|
520
569
|
limit: column.limit,
|
521
570
|
precision: column.precision,
|
@@ -525,19 +574,31 @@ module ArJdbc
|
|
525
574
|
primary_key: column_name == from_primary_key
|
526
575
|
}
|
527
576
|
|
528
|
-
|
529
|
-
column_options[:
|
577
|
+
if column.virtual?
|
578
|
+
column_options[:as] = column.default_function
|
579
|
+
column_options[:stored] = column.virtual_stored?
|
580
|
+
column_options[:type] = column.type
|
581
|
+
elsif column.has_default?
|
582
|
+
type = lookup_cast_type_from_column(column)
|
583
|
+
default = type.deserialize(column.default)
|
584
|
+
default = -> { column.default_function } if default.nil?
|
585
|
+
|
586
|
+
unless column.auto_increment?
|
587
|
+
column_options[:default] = default
|
588
|
+
end
|
530
589
|
end
|
531
590
|
|
532
|
-
column_type = column.bigint? ? :bigint : column.type
|
591
|
+
column_type = column.virtual? ? :virtual : (column.bigint? ? :bigint : column.type)
|
533
592
|
@definition.column(column_name, column_type, **column_options)
|
534
593
|
end
|
535
594
|
|
536
595
|
yield @definition if block_given?
|
537
596
|
end
|
538
597
|
copy_table_indexes(from, to, options[:rename] || {})
|
598
|
+
|
599
|
+
columns_to_copy = @definition.columns.reject { |col| col.options.key?(:as) }.map(&:name)
|
539
600
|
copy_table_contents(from, to,
|
540
|
-
|
601
|
+
columns_to_copy,
|
541
602
|
options[:rename] || {})
|
542
603
|
end
|
543
604
|
|
@@ -611,32 +672,22 @@ module ArJdbc
|
|
611
672
|
|
612
673
|
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
|
613
674
|
PRIMARY_KEY_AUTOINCREMENT_REGEX = /.*\"(\w+)\".+PRIMARY KEY AUTOINCREMENT/i
|
675
|
+
GENERATED_ALWAYS_AS_REGEX = /.*"(\w+)".+GENERATED ALWAYS AS \((.+)\) (?:STORED|VIRTUAL)/i
|
614
676
|
|
615
677
|
def table_structure_with_collation(table_name, basic_structure)
|
616
678
|
collation_hash = {}
|
617
679
|
auto_increments = {}
|
618
|
-
|
619
|
-
SELECT sql FROM
|
620
|
-
(SELECT * FROM sqlite_master UNION ALL
|
621
|
-
SELECT * FROM sqlite_temp_master)
|
622
|
-
WHERE type = 'table' AND name = #{quote(table_name)}
|
623
|
-
SQL
|
624
|
-
|
625
|
-
# Result will have following sample string
|
626
|
-
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
627
|
-
# "password_digest" varchar COLLATE "NOCASE");
|
628
|
-
result = query_value(sql, "SCHEMA")
|
680
|
+
generated_columns = {}
|
629
681
|
|
630
|
-
|
631
|
-
# Splitting with left parentheses and discarding the first part will return all
|
632
|
-
# columns separated with comma(,).
|
633
|
-
columns_string = result.split("(", 2).last
|
682
|
+
column_strings = table_structure_sql(table_name, basic_structure.map { |column| column["name"] })
|
634
683
|
|
635
|
-
|
684
|
+
if column_strings.any?
|
685
|
+
column_strings.each do |column_string|
|
636
686
|
# This regex will match the column name and collation type and will save
|
637
687
|
# the value in $1 and $2 respectively.
|
638
688
|
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
639
689
|
auto_increments[$1] = true if PRIMARY_KEY_AUTOINCREMENT_REGEX =~ column_string
|
690
|
+
generated_columns[$1] = $2 if GENERATED_ALWAYS_AS_REGEX =~ column_string
|
640
691
|
end
|
641
692
|
|
642
693
|
basic_structure.map do |column|
|
@@ -650,6 +701,10 @@ module ArJdbc
|
|
650
701
|
column["auto_increment"] = true
|
651
702
|
end
|
652
703
|
|
704
|
+
if generated_columns.has_key?(column_name)
|
705
|
+
column["dflt_value"] = generated_columns[column_name]
|
706
|
+
end
|
707
|
+
|
653
708
|
column
|
654
709
|
end
|
655
710
|
else
|
@@ -657,6 +712,50 @@ module ArJdbc
|
|
657
712
|
end
|
658
713
|
end
|
659
714
|
|
715
|
+
UNQUOTED_OPEN_PARENS_REGEX = /\((?![^'"]*['"][^'"]*$)/
|
716
|
+
FINAL_CLOSE_PARENS_REGEX = /\);*\z/
|
717
|
+
|
718
|
+
def table_structure_sql(table_name, column_names = nil)
|
719
|
+
unless column_names
|
720
|
+
column_info = table_info(table_name)
|
721
|
+
column_names = column_info.map { |column| column["name"] }
|
722
|
+
end
|
723
|
+
|
724
|
+
sql = <<~SQL
|
725
|
+
SELECT sql FROM
|
726
|
+
(SELECT * FROM sqlite_master UNION ALL
|
727
|
+
SELECT * FROM sqlite_temp_master)
|
728
|
+
WHERE type = 'table' AND name = #{quote(table_name)}
|
729
|
+
SQL
|
730
|
+
|
731
|
+
# Result will have following sample string
|
732
|
+
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
733
|
+
# "password_digest" varchar COLLATE "NOCASE",
|
734
|
+
# "o_id" integer,
|
735
|
+
# CONSTRAINT "fk_rails_78146ddd2e" FOREIGN KEY ("o_id") REFERENCES "os" ("id"));
|
736
|
+
result = query_value(sql, "SCHEMA")
|
737
|
+
|
738
|
+
return [] unless result
|
739
|
+
|
740
|
+
# Splitting with left parentheses and discarding the first part will return all
|
741
|
+
# columns separated with comma(,).
|
742
|
+
result.partition(UNQUOTED_OPEN_PARENS_REGEX)
|
743
|
+
.last
|
744
|
+
.sub(FINAL_CLOSE_PARENS_REGEX, "")
|
745
|
+
# column definitions can have a comma in them, so split on commas followed
|
746
|
+
# by a space and a column name in quotes or followed by the keyword CONSTRAINT
|
747
|
+
.split(/,(?=\s(?:CONSTRAINT|"(?:#{Regexp.union(column_names).source})"))/i)
|
748
|
+
.map(&:strip)
|
749
|
+
end
|
750
|
+
|
751
|
+
def table_info(table_name)
|
752
|
+
if supports_virtual_columns?
|
753
|
+
internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA")
|
754
|
+
else
|
755
|
+
internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
660
759
|
def arel_visitor
|
661
760
|
Arel::Visitors::SQLite.new(self)
|
662
761
|
end
|
@@ -686,29 +785,17 @@ module ArJdbc
|
|
686
785
|
end
|
687
786
|
end
|
688
787
|
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
raw_execute("PRAGMA synchronous = NORMAL", "SCHEMA")
|
701
|
-
# Set the global memory map so all processes can share some data
|
702
|
-
# https://www.sqlite.org/pragma.html#pragma_mmap_size
|
703
|
-
# https://www.sqlite.org/mmap.html
|
704
|
-
raw_execute("PRAGMA mmap_size = #{128.megabytes}", "SCHEMA")
|
705
|
-
end
|
706
|
-
# Impose a limit on the WAL file to prevent unlimited growth
|
707
|
-
# https://www.sqlite.org/pragma.html#pragma_journal_size_limit
|
708
|
-
raw_execute("PRAGMA journal_size_limit = #{64.megabytes}", "SCHEMA")
|
709
|
-
# Set the local connection cache to 2000 pages
|
710
|
-
# https://www.sqlite.org/pragma.html#pragma_cache_size
|
711
|
-
raw_execute("PRAGMA cache_size = 2000", "SCHEMA")
|
788
|
+
super
|
789
|
+
|
790
|
+
pragmas = @config.fetch(:pragmas, {}).stringify_keys
|
791
|
+
DEFAULT_PRAGMAS.merge(pragmas).each do |pragma, value|
|
792
|
+
if ::SQLite3::Pragmas.respond_to?(pragma)
|
793
|
+
stmt = ::SQLite3::Pragmas.public_send(pragma, value)
|
794
|
+
raw_execute(stmt, "SCHEMA")
|
795
|
+
else
|
796
|
+
warn "Unknown SQLite pragma: #{pragma}"
|
797
|
+
end
|
798
|
+
end
|
712
799
|
end
|
713
800
|
end
|
714
801
|
# DIFFERENCE: A registration here is moved down to concrete class so we are not registering part of an adapter.
|
@@ -745,6 +832,12 @@ module ActiveRecord::ConnectionAdapters
|
|
745
832
|
end
|
746
833
|
end
|
747
834
|
|
835
|
+
# NOTE: include these modules before all then override some methods with the
|
836
|
+
# ones defined in ArJdbc::SQLite3 java part and ArJdbc::Abstract
|
837
|
+
include ::ActiveRecord::ConnectionAdapters::SQLite3::Quoting
|
838
|
+
include ::ActiveRecord::ConnectionAdapters::SQLite3::SchemaStatements
|
839
|
+
include ::ActiveRecord::ConnectionAdapters::SQLite3::DatabaseStatements
|
840
|
+
|
748
841
|
include ArJdbc::Abstract::Core
|
749
842
|
include ArJdbc::SQLite3
|
750
843
|
include ArJdbc::SQLite3Config
|
@@ -754,9 +847,6 @@ module ActiveRecord::ConnectionAdapters
|
|
754
847
|
include ArJdbc::Abstract::StatementCache
|
755
848
|
include ArJdbc::Abstract::TransactionSupport
|
756
849
|
|
757
|
-
include ::ActiveRecord::ConnectionAdapters::SQLite3::Quoting
|
758
|
-
include ::ActiveRecord::ConnectionAdapters::SQLite3::SchemaStatements
|
759
|
-
include ::ActiveRecord::ConnectionAdapters::SQLite3::DatabaseStatements
|
760
850
|
|
761
851
|
##
|
762
852
|
# :singleton-method:
|
@@ -771,6 +861,14 @@ module ActiveRecord::ConnectionAdapters
|
|
771
861
|
def initialize(...)
|
772
862
|
super
|
773
863
|
|
864
|
+
@memory_database = false
|
865
|
+
case @config[:database].to_s
|
866
|
+
when ""
|
867
|
+
raise ArgumentError, "No database file specified. Missing argument: database"
|
868
|
+
when ":memory:"
|
869
|
+
@memory_database = true
|
870
|
+
end
|
871
|
+
|
774
872
|
# assign arjdbc extra connection params
|
775
873
|
conn_params = build_connection_config(@config.compact)
|
776
874
|
|
@@ -4,12 +4,14 @@ module ActiveRecord::ConnectionAdapters
|
|
4
4
|
class SQLite3Column < JdbcColumn
|
5
5
|
|
6
6
|
attr_reader :rowid
|
7
|
-
|
8
|
-
def initialize(
|
7
|
+
|
8
|
+
def initialize(*, auto_increment: nil, rowid: false, generated_type: nil, **)
|
9
9
|
super
|
10
|
+
|
10
11
|
@auto_increment = auto_increment
|
11
12
|
@default = nil if default =~ /NULL/
|
12
13
|
@rowid = rowid
|
14
|
+
@generated_type = generated_type
|
13
15
|
end
|
14
16
|
|
15
17
|
def self.string_to_binary(value)
|
@@ -39,6 +41,18 @@ module ActiveRecord::ConnectionAdapters
|
|
39
41
|
auto_increment? || rowid
|
40
42
|
end
|
41
43
|
|
44
|
+
def virtual?
|
45
|
+
!@generated_type.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
def virtual_stored?
|
49
|
+
virtual? && @generated_type == :stored
|
50
|
+
end
|
51
|
+
|
52
|
+
def has_default?
|
53
|
+
super && !virtual?
|
54
|
+
end
|
55
|
+
|
42
56
|
def init_with(coder)
|
43
57
|
@auto_increment = coder["auto_increment"]
|
44
58
|
super
|
@@ -100,4 +114,4 @@ module ActiveRecord::ConnectionAdapters
|
|
100
114
|
end
|
101
115
|
end
|
102
116
|
end
|
103
|
-
end
|
117
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SQLite3
|
4
|
+
# defines methods to de generate pragma statements
|
5
|
+
module Pragmas
|
6
|
+
class << self
|
7
|
+
# The enumeration of valid synchronous modes.
|
8
|
+
SYNCHRONOUS_MODES = [["full", 2], ["normal", 1], ["off", 0]].freeze
|
9
|
+
|
10
|
+
# The enumeration of valid temp store modes.
|
11
|
+
TEMP_STORE_MODES = [["default", 0], ["file", 1], ["memory", 2]].freeze
|
12
|
+
|
13
|
+
# The enumeration of valid auto vacuum modes.
|
14
|
+
AUTO_VACUUM_MODES = [["none", 0], ["full", 1], ["incremental", 2]].freeze
|
15
|
+
|
16
|
+
# The list of valid journaling modes.
|
17
|
+
JOURNAL_MODES = [["delete"], ["truncate"], ["persist"], ["memory"], ["wal"], ["off"]].freeze
|
18
|
+
|
19
|
+
# The list of valid locking modes.
|
20
|
+
LOCKING_MODES = [["normal"], ["exclusive"]].freeze
|
21
|
+
|
22
|
+
# The list of valid encodings.
|
23
|
+
ENCODINGS = [["utf-8"], ["utf-16"], ["utf-16le"], ["utf-16be"]].freeze
|
24
|
+
|
25
|
+
# The list of valid WAL checkpoints.
|
26
|
+
WAL_CHECKPOINTS = [["passive"], ["full"], ["restart"], ["truncate"]].freeze
|
27
|
+
|
28
|
+
# Enforce foreign key constraints
|
29
|
+
# https://www.sqlite.org/pragma.html#pragma_foreign_keys
|
30
|
+
# https://www.sqlite.org/foreignkeys.html
|
31
|
+
def foreign_keys(value)
|
32
|
+
gen_boolean_pragma(:foreign_keys, value)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Journal mode WAL allows for greater concurrency (many readers + one writer)
|
36
|
+
# https://www.sqlite.org/pragma.html#pragma_journal_mode
|
37
|
+
def journal_mode(value)
|
38
|
+
gen_enum_pragma(:journal_mode, value, JOURNAL_MODES)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Set more relaxed level of database durability
|
42
|
+
# 2 = "FULL" (sync on every write), 1 = "NORMAL" (sync every 1000 written pages) and 0 = "NONE"
|
43
|
+
# https://www.sqlite.org/pragma.html#pragma_synchronous
|
44
|
+
def synchronous(value)
|
45
|
+
gen_enum_pragma(:synchronous, value, SYNCHRONOUS_MODES)
|
46
|
+
end
|
47
|
+
|
48
|
+
def temp_store(value)
|
49
|
+
gen_enum_pragma(:temp_store, value, TEMP_STORE_MODES)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Set the global memory map so all processes can share some data
|
53
|
+
# https://www.sqlite.org/pragma.html#pragma_mmap_size
|
54
|
+
# https://www.sqlite.org/mmap.html
|
55
|
+
def mmap_size(value)
|
56
|
+
"PRAGMA mmap_size = #{value.to_i}"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Impose a limit on the WAL file to prevent unlimited growth
|
60
|
+
# https://www.sqlite.org/pragma.html#pragma_journal_size_limit
|
61
|
+
def journal_size_limit(value)
|
62
|
+
"PRAGMA journal_size_limit = #{value.to_i}"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Set the local connection cache to 2000 pages
|
66
|
+
# https://www.sqlite.org/pragma.html#pragma_cache_size
|
67
|
+
def cache_size(value)
|
68
|
+
"PRAGMA cache_size = #{value.to_i}"
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def gen_boolean_pragma(name, mode)
|
74
|
+
case mode
|
75
|
+
when String
|
76
|
+
case mode.downcase
|
77
|
+
when "on", "yes", "true", "y", "t" then mode = "'ON'"
|
78
|
+
when "off", "no", "false", "n", "f" then mode = "'OFF'"
|
79
|
+
else
|
80
|
+
raise ActiveRecord::JDBCError, "unrecognized pragma parameter #{mode.inspect}"
|
81
|
+
end
|
82
|
+
when true, 1
|
83
|
+
mode = "ON"
|
84
|
+
when false, 0, nil
|
85
|
+
mode = "OFF"
|
86
|
+
else
|
87
|
+
raise ActiveRecord::JDBCError, "unrecognized pragma parameter #{mode.inspect}"
|
88
|
+
end
|
89
|
+
|
90
|
+
"PRAGMA #{name} = #{mode}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def gen_enum_pragma(name, mode, enums)
|
94
|
+
match = enums.find { |p| p.find { |i| i.to_s.downcase == mode.to_s.downcase } }
|
95
|
+
|
96
|
+
unless match
|
97
|
+
# Unknown pragma value
|
98
|
+
raise ActiveRecord::JDBCError, "unrecognized #{name} #{mode.inspect}"
|
99
|
+
end
|
100
|
+
|
101
|
+
"PRAGMA #{name} = '#{match.first.upcase}'"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/arjdbc/version.rb
CHANGED
data/lib/arjdbc.rb
CHANGED
@@ -17,6 +17,15 @@ if defined?(JRUBY_VERSION)
|
|
17
17
|
ActiveRecord::ConnectionAdapters.register(
|
18
18
|
"sqlserver", "ActiveRecord::ConnectionAdapters::MSSQLAdapter", "active_record/connection_adapters/mssql_adapter"
|
19
19
|
)
|
20
|
+
ActiveRecord::ConnectionAdapters.register(
|
21
|
+
"sqlite3", "ActiveRecord::ConnectionAdapters::SQLite3Adapter", "arjdbc/sqlite3/adapter"
|
22
|
+
)
|
23
|
+
ActiveRecord::ConnectionAdapters.register(
|
24
|
+
"postgresql", "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter", "arjdbc/postgresql/adapter"
|
25
|
+
)
|
26
|
+
ActiveRecord::ConnectionAdapters.register(
|
27
|
+
"mysql2", "ActiveRecord::ConnectionAdapters::Mysql2Adapter", "arjdbc/mysql/adapter"
|
28
|
+
)
|
20
29
|
end
|
21
30
|
else
|
22
31
|
warn "activerecord-jdbc-adapter is for use with JRuby only"
|
@@ -126,6 +126,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
126
126
|
private IRubyObject adapter; // the AbstractAdapter instance we belong to
|
127
127
|
private volatile boolean connected = true;
|
128
128
|
private RubyClass attributeClass;
|
129
|
+
private RubyClass timeZoneClass;
|
129
130
|
|
130
131
|
private boolean lazy = false; // final once set on initialize
|
131
132
|
private boolean jndi; // final once set on initialize
|
@@ -135,6 +136,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
135
136
|
protected RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
|
136
137
|
super(runtime, metaClass);
|
137
138
|
attributeClass = runtime.getModule("ActiveModel").getClass("Attribute");
|
139
|
+
timeZoneClass = runtime.getModule("ActiveSupport").getClass("TimeWithZone");
|
138
140
|
}
|
139
141
|
|
140
142
|
private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
@@ -2441,6 +2443,9 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2441
2443
|
if (attributeClass.isInstance(attribute)) {
|
2442
2444
|
type = jdbcTypeForAttribute(context, attribute);
|
2443
2445
|
value = valueForDatabase(context, attribute);
|
2446
|
+
} else if (timeZoneClass.isInstance(attribute)) {
|
2447
|
+
type = jdbcTypeFor("timestamp");
|
2448
|
+
value = attribute;
|
2444
2449
|
} else {
|
2445
2450
|
type = jdbcTypeForPrimitiveAttribute(context, attribute);
|
2446
2451
|
value = attribute;
|
@@ -473,7 +473,10 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
473
473
|
// Assume we will only call this with an array.
|
474
474
|
final RubyArray statements = (RubyArray) statementsArg;
|
475
475
|
return withConnection(context, connection -> {
|
476
|
+
final Ruby runtime = context.runtime;
|
477
|
+
|
476
478
|
Statement statement = null;
|
479
|
+
|
477
480
|
try {
|
478
481
|
statement = createStatement(context, connection);
|
479
482
|
|
@@ -481,8 +484,15 @@ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
481
484
|
for (int i = 0; i < length; i++) {
|
482
485
|
statement.addBatch(sqlString(statements.eltOk(i)));
|
483
486
|
}
|
484
|
-
|
485
|
-
|
487
|
+
|
488
|
+
int[] rows = statement.executeBatch();
|
489
|
+
|
490
|
+
RubyArray rowsAffected = runtime.newArray();
|
491
|
+
|
492
|
+
for (int i = 0; i < rows.length; i++) {
|
493
|
+
rowsAffected.append(runtime.newFixnum(rows[i]));
|
494
|
+
}
|
495
|
+
return rowsAffected;
|
486
496
|
} catch (final SQLException e) {
|
487
497
|
// Generate list semicolon list of statements which should match AR error formatting more.
|
488
498
|
debugErrorSQL(context, sqlString(statements.join(context, context.runtime.newString(";\n"))));
|
metadata
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-jdbc-alt-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 72.0.0.
|
4
|
+
version: 72.0.0.rc1
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Nick Sieger, Ola Bini, Karol Bucek, Jesse Chavez, and JRuby contributors
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
14
15
|
requirement: !ruby/object:Gem::Requirement
|
15
16
|
requirements:
|
16
17
|
- - "~>"
|
17
18
|
- !ruby/object:Gem::Version
|
18
19
|
version: 7.2.2
|
19
|
-
name: activerecord
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -95,7 +95,6 @@ files:
|
|
95
95
|
- lib/arjdbc/jdbc/adapter.rb
|
96
96
|
- lib/arjdbc/jdbc/adapter_java.jar
|
97
97
|
- lib/arjdbc/jdbc/adapter_require.rb
|
98
|
-
- lib/arjdbc/jdbc/base_ext.rb
|
99
98
|
- lib/arjdbc/jdbc/callbacks.rb
|
100
99
|
- lib/arjdbc/jdbc/column.rb
|
101
100
|
- lib/arjdbc/jdbc/connection.rb
|
@@ -157,6 +156,7 @@ files:
|
|
157
156
|
- lib/arjdbc/sqlite3/adapter_hash_config.rb
|
158
157
|
- lib/arjdbc/sqlite3/column.rb
|
159
158
|
- lib/arjdbc/sqlite3/connection_methods.rb
|
159
|
+
- lib/arjdbc/sqlite3/pragmas.rb
|
160
160
|
- lib/arjdbc/tasks.rb
|
161
161
|
- lib/arjdbc/tasks/database_tasks.rb
|
162
162
|
- lib/arjdbc/tasks/databases.rake
|
data/lib/arjdbc/jdbc/base_ext.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveRecord
|
4
|
-
class << Base
|
5
|
-
m = Module.new do
|
6
|
-
# Allow adapters to provide their own {#reset_column_information} method.
|
7
|
-
# @note This only affects the current thread's connection.
|
8
|
-
def reset_column_information # :nodoc:
|
9
|
-
# invoke the adapter-specific reset_column_information method
|
10
|
-
connection.reset_column_information if connection.respond_to?(:reset_column_information)
|
11
|
-
super
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
self.prepend(m)
|
16
|
-
end
|
17
|
-
end
|