@@ -90,38 +90,6 @@ def dealloc(stmt)
90
90
end
91
91
end
92
92
93
- def initialize ( ...)
94
- super
95
-
96
- @memory_database = false
97
- case @config [ :database ] . to_s
98
- when ""
99
- raise ArgumentError , "No database file specified. Missing argument: database"
100
- when ":memory:"
101
- @memory_database = true
102
- when /\A file:/
103
- else
104
- # Otherwise we have a path relative to Rails.root
105
- @config [ :database ] = File . expand_path ( @config [ :database ] , Rails . root ) if defined? ( Rails . root )
106
- dirname = File . dirname ( @config [ :database ] )
107
- unless File . directory? ( dirname )
108
- begin
109
- Dir . mkdir ( dirname )
110
- rescue Errno ::ENOENT => error
111
- if error . message . include? ( "No such file or directory" )
112
- raise ActiveRecord ::NoDatabaseError . new ( connection_pool : @pool )
113
- else
114
- raise
115
- end
116
- end
117
- end
118
- end
119
-
120
- @config [ :strict ] = ConnectionAdapters ::SQLite3Adapter . strict_strings_by_default unless @config . key? ( :strict )
121
- @connection_parameters = @config . merge ( database : @config [ :database ] . to_s , results_as_hash : true )
122
- @use_insert_returning = @config . key? ( :insert_returning ) ? self . class . type_cast_config_to_boolean ( @config [ :insert_returning ] ) : true
123
- end
124
-
125
93
def self . database_exists? ( config )
126
94
@config [ :database ] == ":memory:" || File . exist? ( @config [ :database ] . to_s )
127
95
end
@@ -321,7 +289,7 @@ def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
321
289
322
290
def change_column_null ( table_name , column_name , null , default = nil ) #:nodoc:
323
291
validate_change_column_null_argument! ( null )
324
-
292
+
325
293
unless null || default . nil?
326
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" )
327
295
end
@@ -635,6 +603,11 @@ def translate_exception(exception, message:, sql:, binds:)
635
603
elsif exception . message . match? ( /called on a closed database/i )
636
604
# DIFFERENCE: FQN
637
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 )
638
611
else
639
612
super
640
613
end
@@ -710,6 +683,44 @@ def reconnect
710
683
end
711
684
end
712
685
686
+ def configure_connection
687
+ if @config [ :timeout ] && @config [ :retries ]
688
+ raise ArgumentError , "Cannot specify both timeout and retries arguments"
689
+ elsif @config [ :timeout ]
690
+ # FIXME: missing from adapter
691
+ # @raw_connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout]))
692
+ elsif @config [ :retries ]
693
+ retries = self . class . type_cast_config_to_integer ( @config [ :retries ] )
694
+ raw_connection . busy_handler do |count |
695
+ count <= retries
696
+ end
697
+ end
698
+
699
+ # Enforce foreign key constraints
700
+ # https://www.sqlite.org/pragma.html#pragma_foreign_keys
701
+ # https://www.sqlite.org/foreignkeys.html
702
+ raw_execute ( "PRAGMA foreign_keys = ON" , "SCHEMA" )
703
+ unless @memory_database
704
+ # Journal mode WAL allows for greater concurrency (many readers + one writer)
705
+ # https://www.sqlite.org/pragma.html#pragma_journal_mode
706
+ raw_execute ( "PRAGMA journal_mode = WAL" , "SCHEMA" )
707
+ # Set more relaxed level of database durability
708
+ # 2 = "FULL" (sync on every write), 1 = "NORMAL" (sync every 1000 written pages) and 0 = "NONE"
709
+ # https://www.sqlite.org/pragma.html#pragma_synchronous
710
+ raw_execute ( "PRAGMA synchronous = NORMAL" , "SCHEMA" )
711
+ # Set the global memory map so all processes can share some data
712
+ # https://www.sqlite.org/pragma.html#pragma_mmap_size
713
+ # https://www.sqlite.org/mmap.html
714
+ raw_execute ( "PRAGMA mmap_size = #{ 128 . megabytes } " , "SCHEMA" )
715
+ end
716
+ # Impose a limit on the WAL file to prevent unlimited growth
717
+ # https://www.sqlite.org/pragma.html#pragma_journal_size_limit
718
+ raw_execute ( "PRAGMA journal_size_limit = #{ 64 . megabytes } " , "SCHEMA" )
719
+ # Set the local connection cache to 2000 pages
720
+ # https://www.sqlite.org/pragma.html#pragma_cache_size
721
+ raw_execute ( "PRAGMA cache_size = 2000" , "SCHEMA" )
722
+ end
723
+
713
724
def configure_connection
714
725
if @config [ :timeout ] && @config [ :retries ]
715
726
raise ArgumentError , "Cannot specify both timeout and retries arguments"
@@ -775,9 +786,7 @@ class SQLite3Adapter < AbstractAdapter
775
786
# If you wish to enable this mode you can add the following line to your application.rb file:
776
787
#
777
788
# config.active_record.sqlite3_adapter_strict_strings_by_default = true
778
- class_attribute :strict_strings_by_default , default : false
779
-
780
-
789
+ class_attribute :strict_strings_by_default , default : false # Does not actually do anything right now
781
790
782
791
def self . represent_boolean_as_integer = ( value ) # :nodoc:
783
792
if value == false
0 commit comments