activerecord-jdbc-alt-adapter 72.0.0.alpha1-java → 72.0.0.rc1-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.
- 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
|