activerecord-jdbc-alt-adapter 71.0.0-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/activerecord-jdbc-adapter.gemspec +1 -1
- data/activerecord-jdbc-alt-adapter.gemspec +1 -1
- data/lib/arel/visitors/sqlserver.rb +13 -9
- data/lib/arjdbc/abstract/core.rb +10 -1
- data/lib/arjdbc/abstract/database_statements.rb +1 -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 +18 -15
- data/lib/arjdbc/mssql/adapter_hash_config.rb +53 -0
- data/lib/arjdbc/mssql/column.rb +16 -7
- data/lib/arjdbc/mssql/database_statements.rb +7 -2
- data/lib/arjdbc/mssql/quoting.rb +56 -36
- data/lib/arjdbc/mssql/schema_creation.rb +16 -0
- data/lib/arjdbc/mssql/schema_statements.rb +44 -19
- data/lib/arjdbc/mssql.rb +1 -1
- data/lib/arjdbc/mysql/adapter.rb +8 -11
- data/lib/arjdbc/mysql/adapter_hash_config.rb +159 -0
- data/lib/arjdbc/mysql.rb +1 -1
- data/lib/arjdbc/postgresql/adapter.rb +17 -11
- data/lib/arjdbc/postgresql/adapter_hash_config.rb +98 -0
- data/lib/arjdbc/postgresql/base/array_encoder.rb +3 -1
- data/lib/arjdbc/postgresql/oid_types.rb +2 -2
- data/lib/arjdbc/postgresql.rb +1 -1
- data/lib/arjdbc/sqlite3/adapter.rb +193 -90
- data/lib/arjdbc/sqlite3/adapter_hash_config.rb +91 -0
- data/lib/arjdbc/sqlite3/column.rb +17 -3
- data/lib/arjdbc/sqlite3/pragmas.rb +105 -0
- data/lib/arjdbc/sqlite3.rb +1 -1
- data/lib/arjdbc/version.rb +1 -1
- data/lib/arjdbc.rb +16 -1
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +5 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +12 -2
- metadata +12 -8
- data/lib/arjdbc/jdbc/base_ext.rb +0 -17
@@ -17,6 +17,8 @@ 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
19
|
require "arjdbc/sqlite3/column"
|
20
|
+
require "arjdbc/sqlite3/adapter_hash_config"
|
21
|
+
require "arjdbc/sqlite3/pragmas"
|
20
22
|
|
21
23
|
require "arjdbc/abstract/relation_query_attribute_monkey_patch"
|
22
24
|
|
@@ -58,18 +60,12 @@ module ArJdbc
|
|
58
60
|
# DIFFERENCE: Some common constant names to reduce differences in rest of this module from AR5 version
|
59
61
|
ConnectionAdapters = ::ActiveRecord::ConnectionAdapters
|
60
62
|
IndexDefinition = ::ActiveRecord::ConnectionAdapters::IndexDefinition
|
63
|
+
ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
|
61
64
|
Quoting = ::ActiveRecord::ConnectionAdapters::SQLite3::Quoting
|
62
65
|
RecordNotUnique = ::ActiveRecord::RecordNotUnique
|
63
66
|
SchemaCreation = ConnectionAdapters::SQLite3::SchemaCreation
|
64
67
|
SQLite3Adapter = ConnectionAdapters::AbstractAdapter
|
65
68
|
|
66
|
-
ADAPTER_NAME = 'SQLite'
|
67
|
-
|
68
|
-
# DIFFERENCE: FQN
|
69
|
-
include ::ActiveRecord::ConnectionAdapters::SQLite3::Quoting
|
70
|
-
include ::ActiveRecord::ConnectionAdapters::SQLite3::SchemaStatements
|
71
|
-
include ::ActiveRecord::ConnectionAdapters::SQLite3::DatabaseStatements
|
72
|
-
|
73
69
|
NATIVE_DATABASE_TYPES = {
|
74
70
|
primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
|
75
71
|
string: { name: "varchar" },
|
@@ -84,7 +80,16 @@ module ArJdbc
|
|
84
80
|
boolean: { name: "boolean" },
|
85
81
|
json: { name: "json" },
|
86
82
|
}
|
87
|
-
|
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
|
+
|
88
93
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
89
94
|
private
|
90
95
|
def dealloc(stmt)
|
@@ -160,8 +165,23 @@ module ArJdbc
|
|
160
165
|
!@memory_database
|
161
166
|
end
|
162
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
|
+
|
163
176
|
def active?
|
164
|
-
|
177
|
+
if connected?
|
178
|
+
@lock.synchronize do
|
179
|
+
if @raw_connection&.active?
|
180
|
+
verified!
|
181
|
+
true
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end || false
|
165
185
|
end
|
166
186
|
|
167
187
|
def return_value_after_insert?(column) # :nodoc:
|
@@ -173,10 +193,11 @@ module ArJdbc
|
|
173
193
|
# Disconnects from the database if already connected. Otherwise, this
|
174
194
|
# method does nothing.
|
175
195
|
def disconnect!
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
196
|
+
@lock.synchronize do
|
197
|
+
super
|
198
|
+
@raw_connection&.close rescue nil
|
199
|
+
@raw_connection = nil
|
200
|
+
end
|
180
201
|
end
|
181
202
|
|
182
203
|
def supports_index_sort_order?
|
@@ -241,7 +262,6 @@ module ArJdbc
|
|
241
262
|
internal_exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
242
263
|
end
|
243
264
|
|
244
|
-
|
245
265
|
# Renames a table.
|
246
266
|
#
|
247
267
|
# Example:
|
@@ -330,15 +350,31 @@ module ArJdbc
|
|
330
350
|
end
|
331
351
|
alias :add_belongs_to :add_reference
|
332
352
|
|
353
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("([^"]+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
354
|
+
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
333
355
|
def foreign_keys(table_name)
|
334
356
|
# SQLite returns 1 row for each column of composite foreign keys.
|
335
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
|
+
|
336
371
|
grouped_fk = fk_info.group_by { |row| row["id"] }.values.each { |group| group.sort_by! { |row| row["seq"] } }
|
337
372
|
grouped_fk.map do |group|
|
338
373
|
row = group.first
|
339
374
|
options = {
|
340
375
|
on_delete: extract_foreign_key_action(row["on_delete"]),
|
341
|
-
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"]]]
|
342
378
|
}
|
343
379
|
|
344
380
|
if group.one?
|
@@ -348,8 +384,7 @@ module ArJdbc
|
|
348
384
|
options[:column] = group.map { |row| row["from"] }
|
349
385
|
options[:primary_key] = group.map { |row| row["to"] }
|
350
386
|
end
|
351
|
-
|
352
|
-
::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(table_name, row["table"], options)
|
387
|
+
ForeignKeyDefinition.new(table_name, row["table"], options)
|
353
388
|
end
|
354
389
|
end
|
355
390
|
|
@@ -396,7 +431,14 @@ module ArJdbc
|
|
396
431
|
|
397
432
|
type_metadata = fetch_type_metadata(field["type"])
|
398
433
|
default_value = extract_value_from_default(default)
|
399
|
-
|
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
|
+
|
400
442
|
rowid = is_column_the_rowid?(field, definitions)
|
401
443
|
|
402
444
|
ActiveRecord::ConnectionAdapters::SQLite3Column.new(
|
@@ -407,7 +449,8 @@ module ArJdbc
|
|
407
449
|
default_function,
|
408
450
|
collation: field["collation"],
|
409
451
|
auto_increment: field["auto_increment"],
|
410
|
-
rowid: rowid
|
452
|
+
rowid: rowid,
|
453
|
+
generated_type: generated_type
|
411
454
|
)
|
412
455
|
end
|
413
456
|
|
@@ -419,7 +462,12 @@ module ArJdbc
|
|
419
462
|
end
|
420
463
|
|
421
464
|
def table_structure(table_name)
|
422
|
-
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
|
+
|
423
471
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
424
472
|
table_structure_with_collation(table_name, structure)
|
425
473
|
end
|
@@ -459,8 +507,9 @@ module ArJdbc
|
|
459
507
|
# See: https://www.sqlite.org/lang_altertable.html
|
460
508
|
# SQLite has an additional restriction on the ALTER TABLE statement
|
461
509
|
def invalid_alter_table_type?(type, options)
|
462
|
-
type
|
463
|
-
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])
|
464
513
|
end
|
465
514
|
|
466
515
|
def alter_table(
|
@@ -516,12 +565,6 @@ module ArJdbc
|
|
516
565
|
options[:rename][column.name.to_sym] ||
|
517
566
|
column.name) : column.name
|
518
567
|
|
519
|
-
if column.has_default?
|
520
|
-
type = lookup_cast_type_from_column(column)
|
521
|
-
default = type.deserialize(column.default)
|
522
|
-
default = -> { column.default_function } if default.nil?
|
523
|
-
end
|
524
|
-
|
525
568
|
column_options = {
|
526
569
|
limit: column.limit,
|
527
570
|
precision: column.precision,
|
@@ -531,19 +574,31 @@ module ArJdbc
|
|
531
574
|
primary_key: column_name == from_primary_key
|
532
575
|
}
|
533
576
|
|
534
|
-
|
535
|
-
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
|
536
589
|
end
|
537
590
|
|
538
|
-
column_type = column.bigint? ? :bigint : column.type
|
591
|
+
column_type = column.virtual? ? :virtual : (column.bigint? ? :bigint : column.type)
|
539
592
|
@definition.column(column_name, column_type, **column_options)
|
540
593
|
end
|
541
594
|
|
542
595
|
yield @definition if block_given?
|
543
596
|
end
|
544
597
|
copy_table_indexes(from, to, options[:rename] || {})
|
598
|
+
|
599
|
+
columns_to_copy = @definition.columns.reject { |col| col.options.key?(:as) }.map(&:name)
|
545
600
|
copy_table_contents(from, to,
|
546
|
-
|
601
|
+
columns_to_copy,
|
547
602
|
options[:rename] || {})
|
548
603
|
end
|
549
604
|
|
@@ -617,32 +672,22 @@ module ArJdbc
|
|
617
672
|
|
618
673
|
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
|
619
674
|
PRIMARY_KEY_AUTOINCREMENT_REGEX = /.*\"(\w+)\".+PRIMARY KEY AUTOINCREMENT/i
|
675
|
+
GENERATED_ALWAYS_AS_REGEX = /.*"(\w+)".+GENERATED ALWAYS AS \((.+)\) (?:STORED|VIRTUAL)/i
|
620
676
|
|
621
677
|
def table_structure_with_collation(table_name, basic_structure)
|
622
678
|
collation_hash = {}
|
623
679
|
auto_increments = {}
|
624
|
-
|
625
|
-
SELECT sql FROM
|
626
|
-
(SELECT * FROM sqlite_master UNION ALL
|
627
|
-
SELECT * FROM sqlite_temp_master)
|
628
|
-
WHERE type = 'table' AND name = #{quote(table_name)}
|
629
|
-
SQL
|
680
|
+
generated_columns = {}
|
630
681
|
|
631
|
-
|
632
|
-
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
633
|
-
# "password_digest" varchar COLLATE "NOCASE");
|
634
|
-
result = query_value(sql, "SCHEMA")
|
682
|
+
column_strings = table_structure_sql(table_name, basic_structure.map { |column| column["name"] })
|
635
683
|
|
636
|
-
if
|
637
|
-
|
638
|
-
# columns separated with comma(,).
|
639
|
-
columns_string = result.split("(", 2).last
|
640
|
-
|
641
|
-
columns_string.split(",").each do |column_string|
|
684
|
+
if column_strings.any?
|
685
|
+
column_strings.each do |column_string|
|
642
686
|
# This regex will match the column name and collation type and will save
|
643
687
|
# the value in $1 and $2 respectively.
|
644
688
|
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
645
689
|
auto_increments[$1] = true if PRIMARY_KEY_AUTOINCREMENT_REGEX =~ column_string
|
690
|
+
generated_columns[$1] = $2 if GENERATED_ALWAYS_AS_REGEX =~ column_string
|
646
691
|
end
|
647
692
|
|
648
693
|
basic_structure.map do |column|
|
@@ -656,6 +701,10 @@ module ArJdbc
|
|
656
701
|
column["auto_increment"] = true
|
657
702
|
end
|
658
703
|
|
704
|
+
if generated_columns.has_key?(column_name)
|
705
|
+
column["dflt_value"] = generated_columns[column_name]
|
706
|
+
end
|
707
|
+
|
659
708
|
column
|
660
709
|
end
|
661
710
|
else
|
@@ -663,6 +712,50 @@ module ArJdbc
|
|
663
712
|
end
|
664
713
|
end
|
665
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
|
+
|
666
759
|
def arel_visitor
|
667
760
|
Arel::Visitors::SQLite.new(self)
|
668
761
|
end
|
@@ -692,29 +785,17 @@ module ArJdbc
|
|
692
785
|
end
|
693
786
|
end
|
694
787
|
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
raw_execute("PRAGMA synchronous = NORMAL", "SCHEMA")
|
707
|
-
# Set the global memory map so all processes can share some data
|
708
|
-
# https://www.sqlite.org/pragma.html#pragma_mmap_size
|
709
|
-
# https://www.sqlite.org/mmap.html
|
710
|
-
raw_execute("PRAGMA mmap_size = #{128.megabytes}", "SCHEMA")
|
711
|
-
end
|
712
|
-
# Impose a limit on the WAL file to prevent unlimited growth
|
713
|
-
# https://www.sqlite.org/pragma.html#pragma_journal_size_limit
|
714
|
-
raw_execute("PRAGMA journal_size_limit = #{64.megabytes}", "SCHEMA")
|
715
|
-
# Set the local connection cache to 2000 pages
|
716
|
-
# https://www.sqlite.org/pragma.html#pragma_cache_size
|
717
|
-
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
|
718
799
|
end
|
719
800
|
end
|
720
801
|
# DIFFERENCE: A registration here is moved down to concrete class so we are not registering part of an adapter.
|
@@ -729,13 +810,44 @@ module ActiveRecord::ConnectionAdapters
|
|
729
810
|
# ActiveRecord::ConnectionAdapters::SQLite3Adapter. Once we can do that we can remove the
|
730
811
|
# module SQLite3 above and remove a majority of this file.
|
731
812
|
class SQLite3Adapter < AbstractAdapter
|
813
|
+
ADAPTER_NAME = "SQLite"
|
814
|
+
|
815
|
+
class << self
|
816
|
+
def new_client(conn_params, adapter_instance)
|
817
|
+
jdbc_connection_class.new(conn_params, adapter_instance)
|
818
|
+
end
|
819
|
+
|
820
|
+
def dbconsole(config, options = {})
|
821
|
+
args = []
|
822
|
+
|
823
|
+
args << "-#{options[:mode]}" if options[:mode]
|
824
|
+
args << "-header" if options[:header]
|
825
|
+
args << File.expand_path(config.database, const_defined?(:Rails) && Rails.respond_to?(:root) ? Rails.root : nil)
|
826
|
+
|
827
|
+
find_cmd_and_exec("sqlite3", *args)
|
828
|
+
end
|
829
|
+
|
830
|
+
def jdbc_connection_class
|
831
|
+
::ActiveRecord::ConnectionAdapters::SQLite3JdbcConnection
|
832
|
+
end
|
833
|
+
end
|
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
|
+
|
732
841
|
include ArJdbc::Abstract::Core
|
733
842
|
include ArJdbc::SQLite3
|
843
|
+
include ArJdbc::SQLite3Config
|
844
|
+
|
734
845
|
include ArJdbc::Abstract::ConnectionManagement
|
735
846
|
include ArJdbc::Abstract::DatabaseStatements
|
736
847
|
include ArJdbc::Abstract::StatementCache
|
737
848
|
include ArJdbc::Abstract::TransactionSupport
|
738
849
|
|
850
|
+
|
739
851
|
##
|
740
852
|
# :singleton-method:
|
741
853
|
# Configure the SQLite3Adapter to be used in a strict strings mode.
|
@@ -749,7 +861,16 @@ module ActiveRecord::ConnectionAdapters
|
|
749
861
|
def initialize(...)
|
750
862
|
super
|
751
863
|
|
752
|
-
|
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
|
+
|
872
|
+
# assign arjdbc extra connection params
|
873
|
+
conn_params = build_connection_config(@config.compact)
|
753
874
|
|
754
875
|
# NOTE: strict strings is not supported by the jdbc driver yet,
|
755
876
|
# hope it will supported soon, I open a issue in their repository.
|
@@ -820,24 +941,6 @@ module ActiveRecord::ConnectionAdapters
|
|
820
941
|
::ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
|
821
942
|
|
822
943
|
class << self
|
823
|
-
def jdbc_connection_class
|
824
|
-
::ActiveRecord::ConnectionAdapters::SQLite3JdbcConnection
|
825
|
-
end
|
826
|
-
|
827
|
-
def new_client(conn_params, adapter_instance)
|
828
|
-
jdbc_connection_class.new(conn_params, adapter_instance)
|
829
|
-
end
|
830
|
-
|
831
|
-
def dbconsole(config, options = {})
|
832
|
-
args = []
|
833
|
-
|
834
|
-
args << "-#{options[:mode]}" if options[:mode]
|
835
|
-
args << "-header" if options[:header]
|
836
|
-
args << File.expand_path(config.database, const_defined?(:Rails) && Rails.respond_to?(:root) ? Rails.root : nil)
|
837
|
-
|
838
|
-
find_cmd_and_exec("sqlite3", *args)
|
839
|
-
end
|
840
|
-
|
841
944
|
private
|
842
945
|
def initialize_type_map(m)
|
843
946
|
super
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ArJdbc
|
4
|
+
module SQLite3Config
|
5
|
+
def build_connection_config(config)
|
6
|
+
config = config.deep_dup
|
7
|
+
|
8
|
+
load_jdbc_driver
|
9
|
+
|
10
|
+
config[:driver] ||= "org.sqlite.JDBC"
|
11
|
+
|
12
|
+
parse_sqlite3_config!(config)
|
13
|
+
|
14
|
+
database = config[:database]
|
15
|
+
|
16
|
+
# NOTE: "jdbc:sqlite::memory:" syntax is supported
|
17
|
+
config[:url] ||= "jdbc:sqlite:#{database == ':memory:' ? '' : database}"
|
18
|
+
config[:connection_alive_sql] ||= "SELECT 1"
|
19
|
+
|
20
|
+
config[:properties] = build_properties(config)
|
21
|
+
|
22
|
+
config
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def load_jdbc_driver
|
28
|
+
require "jdbc/sqlite3"
|
29
|
+
|
30
|
+
::Jdbc::SQLite3.load_driver(:require) if defined?(::Jdbc::SQLite3.load_driver)
|
31
|
+
rescue LoadError
|
32
|
+
# assuming driver.jar is on the class-path
|
33
|
+
end
|
34
|
+
|
35
|
+
def build_properties(config)
|
36
|
+
properties = config[:properties] || {}
|
37
|
+
|
38
|
+
if config[:readonly]
|
39
|
+
# See
|
40
|
+
# * http://sqlite.org/c3ref/open.html
|
41
|
+
# * http://sqlite.org/c3ref/c_open_autoproxy.html
|
42
|
+
# => 0x01 = readonly, 0x40 = uri (default in JDBC)
|
43
|
+
properties[:open_mode] =
|
44
|
+
::SQLite3::Constants::Open::READONLY | ::SQLite3::Constants::Open::URI
|
45
|
+
end
|
46
|
+
|
47
|
+
if config[:flags]
|
48
|
+
properties[:open_mode] ||= 0
|
49
|
+
properties[:open_mode] |= config[:flags]
|
50
|
+
|
51
|
+
# JDBC driver has an extra flag for it
|
52
|
+
if config[:flags] & ::SQLite3::Constants::Open::SHAREDCACHE != 0
|
53
|
+
properties[:shared_cache] = true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
timeout = config[:timeout]
|
58
|
+
if timeout && timeout.to_s !~ /\A\d+\Z/
|
59
|
+
raise ActiveRecord::StatementInvalid.new(
|
60
|
+
"TypeError: Timeout must be nil or a number (got: #{timeout}).",
|
61
|
+
connection_pool: ActiveRecord::ConnectionAdapters::NullPool.new
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
properties["busy_timeout"] ||= timeout unless timeout.nil?
|
66
|
+
|
67
|
+
properties
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_sqlite3_config!(config)
|
71
|
+
database = (config[:database] ||= config[:dbfile])
|
72
|
+
|
73
|
+
if database != ":memory:"
|
74
|
+
# make sure to have an absolute path. Ruby and Java don't agree
|
75
|
+
# on working directory
|
76
|
+
base_dir = defined?(Rails.root) ? Rails.root : nil
|
77
|
+
config[:database] = File.expand_path(database, base_dir)
|
78
|
+
dirname = File.dirname(config[:database])
|
79
|
+
Dir.mkdir(dirname) unless File.directory?(dirname)
|
80
|
+
end
|
81
|
+
rescue Errno::ENOENT => e
|
82
|
+
if e.message.include?("No such file or directory")
|
83
|
+
raise ActiveRecord::NoDatabaseError.new(
|
84
|
+
connection_pool: ActiveRecord::ConnectionAdapters::NullPool.new
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
raise
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -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
|