activerecord-jdbc-alt-adapter 70.1.0-java → 71.0.0.alpha1-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/.github/workflows/main.yml +135 -21
- data/.github/workflows/ruby.yml +10 -10
- data/.gitignore +1 -0
- data/.solargraph.yml +15 -0
- data/Gemfile +17 -4
- data/README.md +7 -3
- data/RUNNING_TESTS.md +36 -0
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/activerecord-jdbc-alt-adapter.gemspec +1 -1
- data/lib/arel/visitors/sqlserver.rb +10 -0
- data/lib/arjdbc/abstract/connection_management.rb +23 -10
- data/lib/arjdbc/abstract/core.rb +5 -6
- data/lib/arjdbc/abstract/database_statements.rb +35 -25
- data/lib/arjdbc/abstract/statement_cache.rb +1 -6
- data/lib/arjdbc/abstract/transaction_support.rb +37 -9
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/column.rb +0 -34
- data/lib/arjdbc/jdbc/connection_methods.rb +1 -1
- data/lib/arjdbc/mssql/adapter.rb +93 -80
- data/lib/arjdbc/mssql/column.rb +1 -0
- data/lib/arjdbc/mssql/connection_methods.rb +7 -55
- data/lib/arjdbc/mssql/database_statements.rb +182 -71
- data/lib/arjdbc/mssql/explain_support.rb +8 -5
- data/lib/arjdbc/mssql/schema_creation.rb +1 -1
- data/lib/arjdbc/mssql/schema_definitions.rb +10 -0
- data/lib/arjdbc/mssql/schema_statements.rb +19 -11
- data/lib/arjdbc/mssql/server_version.rb +56 -0
- data/lib/arjdbc/mssql/utils.rb +23 -9
- data/lib/arjdbc/mysql/adapter.rb +64 -22
- data/lib/arjdbc/mysql/connection_methods.rb +43 -42
- data/lib/arjdbc/sqlite3/adapter.rb +218 -135
- data/lib/arjdbc/sqlite3/column.rb +103 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +7 -2
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +9 -5
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/02-test.rake +1 -1
- data/rakelib/rails.rake +2 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4 -2
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +2 -1
- metadata +11 -14
- data/lib/arel/visitors/sql_server/ng42.rb +0 -294
- data/lib/arel/visitors/sql_server.rb +0 -124
- data/lib/arjdbc/mssql/limit_helpers.rb +0 -231
- data/lib/arjdbc/mssql/lock_methods.rb +0 -77
- data/lib/arjdbc/mssql/old_adapter.rb +0 -804
- data/lib/arjdbc/mssql/old_column.rb +0 -200
@@ -16,6 +16,7 @@ require "active_record/connection_adapters/sqlite3/schema_definitions"
|
|
16
16
|
require "active_record/connection_adapters/sqlite3/schema_dumper"
|
17
17
|
require "active_record/connection_adapters/sqlite3/schema_statements"
|
18
18
|
require "active_support/core_ext/class/attribute"
|
19
|
+
require "arjdbc/sqlite3/column"
|
19
20
|
|
20
21
|
module SQLite3
|
21
22
|
module Constants
|
@@ -89,20 +90,8 @@ module ArJdbc
|
|
89
90
|
end
|
90
91
|
end
|
91
92
|
|
92
|
-
def initialize(connection, logger, connection_options, config)
|
93
|
-
@memory_database = config[:database] == ":memory:"
|
94
|
-
super(connection, logger, config)
|
95
|
-
configure_connection
|
96
|
-
end
|
97
|
-
|
98
93
|
def self.database_exists?(config)
|
99
|
-
config
|
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
|
94
|
+
@config[:database] == ":memory:" || File.exist?(@config[:database].to_s)
|
106
95
|
end
|
107
96
|
|
108
97
|
def supports_ddl_transactions?
|
@@ -153,6 +142,10 @@ module ArJdbc
|
|
153
142
|
database_version >= "3.8.3"
|
154
143
|
end
|
155
144
|
|
145
|
+
def supports_insert_returning?
|
146
|
+
database_version >= "3.35.0"
|
147
|
+
end
|
148
|
+
|
156
149
|
def supports_insert_on_conflict?
|
157
150
|
database_version >= "3.24.0"
|
158
151
|
end
|
@@ -166,19 +159,22 @@ module ArJdbc
|
|
166
159
|
end
|
167
160
|
|
168
161
|
def active?
|
169
|
-
!@raw_connection.closed?
|
162
|
+
@raw_connection && !@raw_connection.closed?
|
170
163
|
end
|
171
164
|
|
172
|
-
def
|
173
|
-
|
174
|
-
connect if @connection.closed?
|
165
|
+
def return_value_after_insert?(column) # :nodoc:
|
166
|
+
column.auto_populated?
|
175
167
|
end
|
176
168
|
|
169
|
+
# MISSING: alias :reset! :reconnect!
|
170
|
+
|
177
171
|
# Disconnects from the database if already connected. Otherwise, this
|
178
172
|
# method does nothing.
|
179
173
|
def disconnect!
|
180
174
|
super
|
181
|
-
|
175
|
+
|
176
|
+
@raw_connection&.close rescue nil
|
177
|
+
@raw_connection = nil
|
182
178
|
end
|
183
179
|
|
184
180
|
def supports_index_sort_order?
|
@@ -191,7 +187,7 @@ module ArJdbc
|
|
191
187
|
|
192
188
|
# Returns the current database encoding format as a string, eg: 'UTF-8'
|
193
189
|
def encoding
|
194
|
-
|
190
|
+
any_raw_connection.encoding.to_s
|
195
191
|
end
|
196
192
|
|
197
193
|
def supports_explain?
|
@@ -218,8 +214,14 @@ module ArJdbc
|
|
218
214
|
end
|
219
215
|
end
|
220
216
|
|
221
|
-
def
|
222
|
-
|
217
|
+
def check_all_foreign_keys_valid! # :nodoc:
|
218
|
+
sql = "PRAGMA foreign_key_check"
|
219
|
+
result = execute(sql)
|
220
|
+
|
221
|
+
unless result.blank?
|
222
|
+
tables = result.map { |row| row["table"] }
|
223
|
+
raise ActiveRecord::StatementInvalid.new("Foreign key violations found: #{tables.join(", ")}", sql: sql)
|
224
|
+
end
|
223
225
|
end
|
224
226
|
|
225
227
|
# SCHEMA STATEMENTS ========================================
|
@@ -234,7 +236,7 @@ module ArJdbc
|
|
234
236
|
|
235
237
|
index_name = index_name_for_remove(table_name, column_name, options)
|
236
238
|
|
237
|
-
|
239
|
+
internal_exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
238
240
|
end
|
239
241
|
|
240
242
|
|
@@ -242,10 +244,11 @@ module ArJdbc
|
|
242
244
|
#
|
243
245
|
# Example:
|
244
246
|
# rename_table('octopuses', 'octopi')
|
245
|
-
def rename_table(table_name, new_name)
|
247
|
+
def rename_table(table_name, new_name, **options)
|
248
|
+
validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
|
246
249
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
247
250
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
248
|
-
|
251
|
+
internal_exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
249
252
|
rename_table_indexes(table_name, new_name)
|
250
253
|
end
|
251
254
|
|
@@ -285,8 +288,10 @@ module ArJdbc
|
|
285
288
|
end
|
286
289
|
|
287
290
|
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
291
|
+
validate_change_column_null_argument!(null)
|
292
|
+
|
288
293
|
unless null || default.nil?
|
289
|
-
|
294
|
+
internal_exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
290
295
|
end
|
291
296
|
alter_table(table_name) do |definition|
|
292
297
|
definition[column_name].null = null
|
@@ -295,10 +300,7 @@ module ArJdbc
|
|
295
300
|
|
296
301
|
def change_column(table_name, column_name, type, **options) #:nodoc:
|
297
302
|
alter_table(table_name) do |definition|
|
298
|
-
definition
|
299
|
-
self.type = aliased_types(type.to_s, type)
|
300
|
-
self.options.merge!(options)
|
301
|
-
end
|
303
|
+
definition.change_column(column_name, type, **options)
|
302
304
|
end
|
303
305
|
end
|
304
306
|
|
@@ -308,20 +310,42 @@ module ArJdbc
|
|
308
310
|
rename_column_indexes(table_name, column.name, new_column_name)
|
309
311
|
end
|
310
312
|
|
313
|
+
def add_timestamps(table_name, **options)
|
314
|
+
options[:null] = false if options[:null].nil?
|
315
|
+
|
316
|
+
if !options.key?(:precision)
|
317
|
+
options[:precision] = 6
|
318
|
+
end
|
319
|
+
|
320
|
+
alter_table(table_name) do |definition|
|
321
|
+
definition.column :created_at, :datetime, **options
|
322
|
+
definition.column :updated_at, :datetime, **options
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
311
326
|
def add_reference(table_name, ref_name, **options) # :nodoc:
|
312
327
|
super(table_name, ref_name, type: :integer, **options)
|
313
328
|
end
|
314
329
|
alias :add_belongs_to :add_reference
|
315
330
|
|
316
331
|
def foreign_keys(table_name)
|
317
|
-
|
318
|
-
fk_info
|
332
|
+
# SQLite returns 1 row for each column of composite foreign keys.
|
333
|
+
fk_info = internal_exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
|
334
|
+
grouped_fk = fk_info.group_by { |row| row["id"] }.values.each { |group| group.sort_by! { |row| row["seq"] } }
|
335
|
+
grouped_fk.map do |group|
|
336
|
+
row = group.first
|
319
337
|
options = {
|
320
|
-
column: row["from"],
|
321
|
-
primary_key: row["to"],
|
322
338
|
on_delete: extract_foreign_key_action(row["on_delete"]),
|
323
339
|
on_update: extract_foreign_key_action(row["on_update"])
|
324
340
|
}
|
341
|
+
|
342
|
+
if group.one?
|
343
|
+
options[:column] = row["from"]
|
344
|
+
options[:primary_key] = row["to"]
|
345
|
+
else
|
346
|
+
options[:column] = group.map { |row| row["from"] }
|
347
|
+
options[:primary_key] = group.map { |row| row["to"] }
|
348
|
+
end
|
325
349
|
# DIFFERENCE: FQN
|
326
350
|
::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(table_name, row["table"], options)
|
327
351
|
end
|
@@ -342,6 +366,7 @@ module ArJdbc
|
|
342
366
|
end
|
343
367
|
end
|
344
368
|
|
369
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
345
370
|
sql
|
346
371
|
end
|
347
372
|
|
@@ -349,6 +374,10 @@ module ArJdbc
|
|
349
374
|
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
|
350
375
|
end
|
351
376
|
|
377
|
+
def use_insert_returning?
|
378
|
+
@use_insert_returning
|
379
|
+
end
|
380
|
+
|
352
381
|
def get_database_version # :nodoc:
|
353
382
|
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
|
354
383
|
end
|
@@ -359,6 +388,27 @@ module ArJdbc
|
|
359
388
|
end
|
360
389
|
end
|
361
390
|
|
391
|
+
# DIFFERENCE: here to
|
392
|
+
def new_column_from_field(table_name, field, definitions)
|
393
|
+
default = field["dflt_value"]
|
394
|
+
|
395
|
+
type_metadata = fetch_type_metadata(field["type"])
|
396
|
+
default_value = extract_value_from_default(default)
|
397
|
+
default_function = extract_default_function(default_value, default)
|
398
|
+
rowid = is_column_the_rowid?(field, definitions)
|
399
|
+
|
400
|
+
ActiveRecord::ConnectionAdapters::SQLite3Column.new(
|
401
|
+
field["name"],
|
402
|
+
default_value,
|
403
|
+
type_metadata,
|
404
|
+
field["notnull"].to_i == 0,
|
405
|
+
default_function,
|
406
|
+
collation: field["collation"],
|
407
|
+
auto_increment: field["auto_increment"],
|
408
|
+
rowid: rowid
|
409
|
+
)
|
410
|
+
end
|
411
|
+
|
362
412
|
private
|
363
413
|
# See https://www.sqlite.org/limits.html,
|
364
414
|
# the default value is 999 when not configured.
|
@@ -367,7 +417,7 @@ module ArJdbc
|
|
367
417
|
end
|
368
418
|
|
369
419
|
def table_structure(table_name)
|
370
|
-
structure =
|
420
|
+
structure = internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
371
421
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
372
422
|
table_structure_with_collation(table_name, structure)
|
373
423
|
end
|
@@ -377,15 +427,18 @@ module ArJdbc
|
|
377
427
|
case default
|
378
428
|
when /^null$/i
|
379
429
|
nil
|
380
|
-
|
381
|
-
when /^'(
|
430
|
+
# Quoted types
|
431
|
+
when /^'([^|]*)'$/m
|
382
432
|
$1.gsub("''", "'")
|
383
|
-
|
384
|
-
when /^"(
|
433
|
+
# Quoted types
|
434
|
+
when /^"([^|]*)"$/m
|
385
435
|
$1.gsub('""', '"')
|
386
|
-
|
436
|
+
# Numeric types
|
387
437
|
when /\A-?\d+(\.\d*)?\z/
|
388
438
|
$&
|
439
|
+
# Binary columns
|
440
|
+
when /x'(.*)'/
|
441
|
+
[ $1 ].pack("H*")
|
389
442
|
else
|
390
443
|
# Anything else is blank or some function
|
391
444
|
# and we can't know the value of that, so return nil.
|
@@ -398,7 +451,7 @@ module ArJdbc
|
|
398
451
|
end
|
399
452
|
|
400
453
|
def has_default_function?(default_value, default)
|
401
|
-
!default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
454
|
+
!default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP|\|\|}.match?(default)
|
402
455
|
end
|
403
456
|
|
404
457
|
# See: https://www.sqlite.org/lang_altertable.html
|
@@ -464,14 +517,24 @@ module ArJdbc
|
|
464
517
|
if column.has_default?
|
465
518
|
type = lookup_cast_type_from_column(column)
|
466
519
|
default = type.deserialize(column.default)
|
520
|
+
default = -> { column.default_function } if default.nil?
|
467
521
|
end
|
468
522
|
|
469
|
-
|
470
|
-
limit: column.limit,
|
471
|
-
precision: column.precision,
|
472
|
-
|
523
|
+
column_options = {
|
524
|
+
limit: column.limit,
|
525
|
+
precision: column.precision,
|
526
|
+
scale: column.scale,
|
527
|
+
null: column.null,
|
528
|
+
collation: column.collation,
|
473
529
|
primary_key: column_name == from_primary_key
|
474
|
-
|
530
|
+
}
|
531
|
+
|
532
|
+
unless column.auto_increment?
|
533
|
+
column_options[:default] = default
|
534
|
+
end
|
535
|
+
|
536
|
+
column_type = column.bigint? ? :bigint : column.type
|
537
|
+
@definition.column(column_name, column_type, **column_options)
|
475
538
|
end
|
476
539
|
|
477
540
|
yield @definition if block_given?
|
@@ -519,8 +582,8 @@ module ArJdbc
|
|
519
582
|
quoted_columns = columns.map { |col| quote_column_name(col) } * ","
|
520
583
|
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
|
521
584
|
|
522
|
-
|
523
|
-
|
585
|
+
internal_exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
586
|
+
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
524
587
|
end
|
525
588
|
|
526
589
|
def translate_exception(exception, message:, sql:, binds:)
|
@@ -530,25 +593,32 @@ module ArJdbc
|
|
530
593
|
# column *column_name* is not unique
|
531
594
|
if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
|
532
595
|
# DIFFERENCE: FQN
|
533
|
-
::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds)
|
596
|
+
::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
534
597
|
elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
|
535
598
|
# DIFFERENCE: FQN
|
536
|
-
::ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds)
|
599
|
+
::ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
537
600
|
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
538
601
|
# DIFFERENCE: FQN
|
539
|
-
::ActiveRecord::InvalidForeignKey.new(message, sql: sql, binds: binds)
|
602
|
+
::ActiveRecord::InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
540
603
|
elsif exception.message.match?(/called on a closed database/i)
|
541
604
|
# DIFFERENCE: FQN
|
542
|
-
::ActiveRecord::ConnectionNotEstablished.new(exception)
|
605
|
+
::ActiveRecord::ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
606
|
+
elsif exception.message.match?(/sql error/i)
|
607
|
+
::ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
608
|
+
elsif exception.message.match?(/write a readonly database/i)
|
609
|
+
message = message.sub('org.sqlite.SQLiteException', 'SQLite3::ReadOnlyException')
|
610
|
+
::ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
543
611
|
else
|
544
612
|
super
|
545
613
|
end
|
546
614
|
end
|
547
615
|
|
548
616
|
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
|
617
|
+
PRIMARY_KEY_AUTOINCREMENT_REGEX = /.*\"(\w+)\".+PRIMARY KEY AUTOINCREMENT/i
|
549
618
|
|
550
619
|
def table_structure_with_collation(table_name, basic_structure)
|
551
620
|
collation_hash = {}
|
621
|
+
auto_increments = {}
|
552
622
|
sql = <<~SQL
|
553
623
|
SELECT sql FROM
|
554
624
|
(SELECT * FROM sqlite_master UNION ALL
|
@@ -570,6 +640,7 @@ module ArJdbc
|
|
570
640
|
# This regex will match the column name and collation type and will save
|
571
641
|
# the value in $1 and $2 respectively.
|
572
642
|
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
643
|
+
auto_increments[$1] = true if PRIMARY_KEY_AUTOINCREMENT_REGEX =~ column_string
|
573
644
|
end
|
574
645
|
|
575
646
|
basic_structure.map do |column|
|
@@ -579,6 +650,10 @@ module ArJdbc
|
|
579
650
|
column["collation"] = collation_hash[column_name]
|
580
651
|
end
|
581
652
|
|
653
|
+
if auto_increments.has_key?(column_name)
|
654
|
+
column["auto_increment"] = true
|
655
|
+
end
|
656
|
+
|
582
657
|
column
|
583
658
|
end
|
584
659
|
else
|
@@ -594,99 +669,86 @@ module ArJdbc
|
|
594
669
|
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
595
670
|
end
|
596
671
|
|
597
|
-
def connect
|
598
|
-
@connection = ::SQLite3::Database.new(
|
599
|
-
@config[:database].to_s,
|
600
|
-
@config.merge(results_as_hash: true)
|
601
|
-
)
|
602
|
-
end
|
603
|
-
|
604
672
|
def configure_connection
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
module ActiveRecord::ConnectionAdapters
|
616
|
-
class SQLite3Column < JdbcColumn
|
617
|
-
def initialize(name, *args)
|
618
|
-
if Hash === name
|
619
|
-
super
|
620
|
-
else
|
621
|
-
super(nil, name, *args)
|
622
|
-
end
|
623
|
-
end
|
624
|
-
|
625
|
-
def self.string_to_binary(value)
|
626
|
-
value
|
627
|
-
end
|
628
|
-
|
629
|
-
def self.binary_to_string(value)
|
630
|
-
if value.respond_to?(:encoding) && value.encoding != Encoding::ASCII_8BIT
|
631
|
-
value = value.force_encoding(Encoding::ASCII_8BIT)
|
673
|
+
if @config[:timeout] && @config[:retries]
|
674
|
+
raise ArgumentError, "Cannot specify both timeout and retries arguments"
|
675
|
+
elsif @config[:timeout]
|
676
|
+
# FIXME: missing from adapter
|
677
|
+
# @raw_connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout]))
|
678
|
+
elsif @config[:retries]
|
679
|
+
retries = self.class.type_cast_config_to_integer(@config[:retries])
|
680
|
+
raw_connection.busy_handler do |count|
|
681
|
+
count <= retries
|
682
|
+
end
|
632
683
|
end
|
633
|
-
value
|
634
|
-
end
|
635
684
|
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
685
|
+
# Enforce foreign key constraints
|
686
|
+
# https://www.sqlite.org/pragma.html#pragma_foreign_keys
|
687
|
+
# https://www.sqlite.org/foreignkeys.html
|
688
|
+
raw_execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
689
|
+
unless @memory_database
|
690
|
+
# Journal mode WAL allows for greater concurrency (many readers + one writer)
|
691
|
+
# https://www.sqlite.org/pragma.html#pragma_journal_mode
|
692
|
+
raw_execute("PRAGMA journal_mode = WAL", "SCHEMA")
|
693
|
+
# Set more relaxed level of database durability
|
694
|
+
# 2 = "FULL" (sync on every write), 1 = "NORMAL" (sync every 1000 written pages) and 0 = "NONE"
|
695
|
+
# https://www.sqlite.org/pragma.html#pragma_synchronous
|
696
|
+
raw_execute("PRAGMA synchronous = NORMAL", "SCHEMA")
|
697
|
+
# Set the global memory map so all processes can share some data
|
698
|
+
# https://www.sqlite.org/pragma.html#pragma_mmap_size
|
699
|
+
# https://www.sqlite.org/mmap.html
|
700
|
+
raw_execute("PRAGMA mmap_size = #{128.megabytes}", "SCHEMA")
|
642
701
|
end
|
702
|
+
# Impose a limit on the WAL file to prevent unlimited growth
|
703
|
+
# https://www.sqlite.org/pragma.html#pragma_journal_size_limit
|
704
|
+
raw_execute("PRAGMA journal_size_limit = #{64.megabytes}", "SCHEMA")
|
705
|
+
# Set the local connection cache to 2000 pages
|
706
|
+
# https://www.sqlite.org/pragma.html#pragma_cache_size
|
707
|
+
raw_execute("PRAGMA cache_size = 2000", "SCHEMA")
|
643
708
|
end
|
644
709
|
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
case type
|
657
|
-
when :string then value
|
658
|
-
when :primary_key
|
659
|
-
value.respond_to?(:to_i) ? value.to_i : ( value ? 1 : 0 )
|
660
|
-
when :float then value.to_f
|
661
|
-
when :decimal then self.class.value_to_decimal(value)
|
662
|
-
when :boolean then self.class.value_to_boolean(value)
|
663
|
-
else super
|
664
|
-
end
|
665
|
-
end
|
666
|
-
|
667
|
-
private
|
668
|
-
|
669
|
-
# @override {ActiveRecord::ConnectionAdapters::Column#extract_limit}
|
670
|
-
def extract_limit(sql_type)
|
671
|
-
return nil if sql_type =~ /^(real)\(\d+/i
|
672
|
-
super
|
673
|
-
end
|
674
|
-
|
675
|
-
def extract_precision(sql_type)
|
676
|
-
case sql_type
|
677
|
-
when /^(real)\((\d+)(,\d+)?\)/i then $2.to_i
|
678
|
-
else super
|
710
|
+
def configure_connection
|
711
|
+
if @config[:timeout] && @config[:retries]
|
712
|
+
raise ArgumentError, "Cannot specify both timeout and retries arguments"
|
713
|
+
elsif @config[:timeout]
|
714
|
+
# FIXME:
|
715
|
+
# @raw_connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout]))
|
716
|
+
elsif @config[:retries]
|
717
|
+
retries = self.class.type_cast_config_to_integer(@config[:retries])
|
718
|
+
raw_connection.busy_handler do |count|
|
719
|
+
count <= retries
|
720
|
+
end
|
679
721
|
end
|
680
|
-
end
|
681
722
|
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
723
|
+
# Enforce foreign key constraints
|
724
|
+
# https://www.sqlite.org/pragma.html#pragma_foreign_keys
|
725
|
+
# https://www.sqlite.org/foreignkeys.html
|
726
|
+
raw_execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
727
|
+
unless @memory_database
|
728
|
+
# Journal mode WAL allows for greater concurrency (many readers + one writer)
|
729
|
+
# https://www.sqlite.org/pragma.html#pragma_journal_mode
|
730
|
+
raw_execute("PRAGMA journal_mode = WAL", "SCHEMA")
|
731
|
+
# Set more relaxed level of database durability
|
732
|
+
# 2 = "FULL" (sync on every write), 1 = "NORMAL" (sync every 1000 written pages) and 0 = "NONE"
|
733
|
+
# https://www.sqlite.org/pragma.html#pragma_synchronous
|
734
|
+
raw_execute("PRAGMA synchronous = NORMAL", "SCHEMA")
|
735
|
+
# Set the global memory map so all processes can share some data
|
736
|
+
# https://www.sqlite.org/pragma.html#pragma_mmap_size
|
737
|
+
# https://www.sqlite.org/mmap.html
|
738
|
+
raw_execute("PRAGMA mmap_size = #{128.megabytes}", "SCHEMA")
|
687
739
|
end
|
740
|
+
# Impose a limit on the WAL file to prevent unlimited growth
|
741
|
+
# https://www.sqlite.org/pragma.html#pragma_journal_size_limit
|
742
|
+
raw_execute("PRAGMA journal_size_limit = #{64.megabytes}", "SCHEMA")
|
743
|
+
# Set the local connection cache to 2000 pages
|
744
|
+
# https://www.sqlite.org/pragma.html#pragma_cache_size
|
745
|
+
raw_execute("PRAGMA cache_size = 2000", "SCHEMA")
|
688
746
|
end
|
689
747
|
end
|
748
|
+
# DIFFERENCE: A registration here is moved down to concrete class so we are not registering part of an adapter.
|
749
|
+
end
|
750
|
+
|
751
|
+
module ActiveRecord::ConnectionAdapters
|
690
752
|
|
691
753
|
remove_const(:SQLite3Adapter) if const_defined?(:SQLite3Adapter)
|
692
754
|
|
@@ -702,6 +764,16 @@ module ActiveRecord::ConnectionAdapters
|
|
702
764
|
include ArJdbc::Abstract::StatementCache
|
703
765
|
include ArJdbc::Abstract::TransactionSupport
|
704
766
|
|
767
|
+
##
|
768
|
+
# :singleton-method:
|
769
|
+
# Configure the SQLite3Adapter to be used in a strict strings mode.
|
770
|
+
# This will disable double-quoted string literals, because otherwise typos can silently go unnoticed.
|
771
|
+
# For example, it is possible to create an index for a non existing column.
|
772
|
+
# If you wish to enable this mode you can add the following line to your application.rb file:
|
773
|
+
#
|
774
|
+
# config.active_record.sqlite3_adapter_strict_strings_by_default = true
|
775
|
+
class_attribute :strict_strings_by_default, default: false # Does not actually do anything right now
|
776
|
+
|
705
777
|
def self.represent_boolean_as_integer=(value) # :nodoc:
|
706
778
|
if value == false
|
707
779
|
raise "`.represent_boolean_as_integer=` is now always true, so make sure your application can work with it and remove this settings."
|
@@ -736,8 +808,9 @@ module ActiveRecord::ConnectionAdapters
|
|
736
808
|
# SQLite driver doesn't support all types of insert statements with executeUpdate so
|
737
809
|
# make it act like a regular query and the ids will be returned from #last_inserted_id
|
738
810
|
# example: INSERT INTO "aircraft" DEFAULT VALUES
|
739
|
-
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
|
740
|
-
|
811
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
|
812
|
+
sql, binds = sql_for_insert(sql, pk, binds, returning)
|
813
|
+
internal_exec_query(sql, name, binds)
|
741
814
|
end
|
742
815
|
|
743
816
|
def jdbc_column_class
|
@@ -770,6 +843,16 @@ module ActiveRecord::ConnectionAdapters
|
|
770
843
|
::ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
|
771
844
|
|
772
845
|
class << self
|
846
|
+
def dbconsole(config, options = {})
|
847
|
+
args = []
|
848
|
+
|
849
|
+
args << "-#{options[:mode]}" if options[:mode]
|
850
|
+
args << "-header" if options[:header]
|
851
|
+
args << File.expand_path(config.database, const_defined?(:Rails) && Rails.respond_to?(:root) ? Rails.root : nil)
|
852
|
+
|
853
|
+
find_cmd_and_exec("sqlite3", *args)
|
854
|
+
end
|
855
|
+
|
773
856
|
private
|
774
857
|
def initialize_type_map(m)
|
775
858
|
super
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord::ConnectionAdapters
|
4
|
+
class SQLite3Column < JdbcColumn
|
5
|
+
|
6
|
+
attr_reader :rowid
|
7
|
+
|
8
|
+
def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, auto_increment: nil, rowid: false, **)
|
9
|
+
super
|
10
|
+
@auto_increment = auto_increment
|
11
|
+
@default = nil if default =~ /NULL/
|
12
|
+
@rowid = rowid
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.string_to_binary(value)
|
16
|
+
value
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.binary_to_string(value)
|
20
|
+
if value.respond_to?(:encoding) && value.encoding != Encoding::ASCII_8BIT
|
21
|
+
value = value.force_encoding(Encoding::ASCII_8BIT)
|
22
|
+
end
|
23
|
+
value
|
24
|
+
end
|
25
|
+
|
26
|
+
# @override {ActiveRecord::ConnectionAdapters::JdbcColumn#default_value}
|
27
|
+
def default_value(value)
|
28
|
+
# JDBC returns column default strings with actual single quotes :
|
29
|
+
return $1 if value =~ /^'(.*)'$/
|
30
|
+
|
31
|
+
value
|
32
|
+
end
|
33
|
+
|
34
|
+
def auto_increment?
|
35
|
+
@auto_increment
|
36
|
+
end
|
37
|
+
|
38
|
+
def auto_incremented_by_db?
|
39
|
+
auto_increment? || rowid
|
40
|
+
end
|
41
|
+
|
42
|
+
def init_with(coder)
|
43
|
+
@auto_increment = coder["auto_increment"]
|
44
|
+
super
|
45
|
+
end
|
46
|
+
|
47
|
+
def encode_with(coder)
|
48
|
+
coder["auto_increment"] = @auto_increment
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
def ==(other)
|
53
|
+
other.is_a?(Column) &&
|
54
|
+
super &&
|
55
|
+
auto_increment? == other.auto_increment?
|
56
|
+
end
|
57
|
+
alias :eql? :==
|
58
|
+
|
59
|
+
def hash
|
60
|
+
Column.hash ^
|
61
|
+
super.hash ^
|
62
|
+
auto_increment?.hash ^
|
63
|
+
rowid.hash
|
64
|
+
end
|
65
|
+
|
66
|
+
# @override {ActiveRecord::ConnectionAdapters::Column#type_cast}
|
67
|
+
def type_cast(value)
|
68
|
+
return nil if value.nil?
|
69
|
+
case type
|
70
|
+
when :string then value
|
71
|
+
when :primary_key
|
72
|
+
value.respond_to?(:to_i) ? value.to_i : ( value ? 1 : 0 )
|
73
|
+
when :float then value.to_f
|
74
|
+
when :decimal then self.class.value_to_decimal(value)
|
75
|
+
when :boolean then self.class.value_to_boolean(value)
|
76
|
+
else super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# @override {ActiveRecord::ConnectionAdapters::Column#extract_limit}
|
83
|
+
def extract_limit(sql_type)
|
84
|
+
return nil if sql_type =~ /^(real)\(\d+/i
|
85
|
+
super
|
86
|
+
end
|
87
|
+
|
88
|
+
def extract_precision(sql_type)
|
89
|
+
case sql_type
|
90
|
+
when /^(real)\((\d+)(,\d+)?\)/i then $2.to_i
|
91
|
+
else super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def extract_scale(sql_type)
|
96
|
+
case sql_type
|
97
|
+
when /^(real)\((\d+)\)/i then 0
|
98
|
+
when /^(real)\((\d+)(,(\d+))\)/i then $4.to_i
|
99
|
+
else super
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|