@@ -90,20 +90,40 @@ def dealloc(stmt)
90
90
end
91
91
end
92
92
93
- def initialize ( config )
94
- @memory_database = config [ :database ] == ":memory:"
93
+ def initialize ( ...)
95
94
super
96
- configure_connection
97
- end
98
95
99
- def self . database_exists? ( config )
100
- config = config . symbolize_keys
101
- if config [ :database ] == ":memory:"
102
- true
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
103
else
104
- database_file = defined? ( Rails . root ) ? File . expand_path ( config [ :database ] , Rails . root ) : config [ :database ]
105
- File . exist? ( database_file )
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
106
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
+ def self . database_exists? ( config )
126
+ @config [ :database ] == ":memory:" || File . exist? ( @config [ :database ] . to_s )
107
127
end
108
128
109
129
def supports_ddl_transactions?
@@ -178,19 +198,15 @@ def return_value_after_insert?(column) # :nodoc:
178
198
column . auto_populated?
179
199
end
180
200
181
- def reconnect
182
- if active?
183
- @raw_connection . rollback rescue nil
184
- else
185
- connect
186
- end
187
- end
201
+ # MISSING: alias :reset! :reconnect!
188
202
189
203
# Disconnects from the database if already connected. Otherwise, this
190
204
# method does nothing.
191
205
def disconnect!
192
206
super
193
- @raw_connection . close rescue nil
207
+
208
+ @raw_connection &.close rescue nil
209
+ @raw_connection = nil
194
210
end
195
211
196
212
def supports_index_sort_order?
@@ -230,16 +246,7 @@ def disable_referential_integrity # :nodoc:
230
246
end
231
247
end
232
248
233
- def all_foreign_keys_valid? # :nodoc:
234
- # Rails 7
235
- check_all_foreign_keys_valid!
236
- true
237
- rescue ActiveRecord ::StatementInvalid
238
- false
239
- end
240
-
241
249
def check_all_foreign_keys_valid! # :nodoc:
242
- # Rails 7.1
243
250
sql = "PRAGMA foreign_key_check"
244
251
result = execute ( sql )
245
252
@@ -269,7 +276,8 @@ def remove_index(table_name, column_name = nil, **options) # :nodoc:
269
276
#
270
277
# Example:
271
278
# rename_table('octopuses', 'octopi')
272
- def rename_table ( table_name , new_name )
279
+ def rename_table ( table_name , new_name , **options )
280
+ validate_table_length! ( new_name ) unless options [ :_uses_legacy_table_name ]
273
281
schema_cache . clear_data_source_cache! ( table_name . to_s )
274
282
schema_cache . clear_data_source_cache! ( new_name . to_s )
275
283
internal_exec_query "ALTER TABLE #{ quote_table_name ( table_name ) } RENAME TO #{ quote_table_name ( new_name ) } "
@@ -312,6 +320,8 @@ def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
312
320
end
313
321
314
322
def change_column_null ( table_name , column_name , null , default = nil ) #:nodoc:
323
+ validate_change_column_null_argument! ( null )
324
+
315
325
unless null || default . nil?
316
326
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" )
317
327
end
@@ -322,10 +332,7 @@ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
322
332
323
333
def change_column ( table_name , column_name , type , **options ) #:nodoc:
324
334
alter_table ( table_name ) do |definition |
325
- definition [ column_name ] . instance_eval do
326
- self . type = aliased_types ( type . to_s , type )
327
- self . options . merge! ( options )
328
- end
335
+ definition . change_column ( column_name , type , **options )
329
336
end
330
337
end
331
338
@@ -413,6 +420,7 @@ def check_version
413
420
end
414
421
end
415
422
423
+ # DIFFERENCE: here to
416
424
def new_column_from_field ( table_name , field , definitions )
417
425
default = field [ "dflt_value" ]
418
426
@@ -475,7 +483,7 @@ def extract_default_function(default_value, default)
475
483
end
476
484
477
485
def has_default_function? ( default_value , default )
478
- !default_value && %r{\w +\( .*\) |CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP} . match? ( default )
486
+ !default_value && %r{\w +\( .*\) |CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP| \| \| } . match? ( default )
479
487
end
480
488
481
489
# See: https://www.sqlite.org/lang_altertable.html
@@ -688,18 +696,57 @@ def build_statement_pool
688
696
StatementPool . new ( self . class . type_cast_config_to_integer ( @config [ :statement_limit ] ) )
689
697
end
690
698
699
+ # DIFFERENCE: we delve into jdbc shared code and this does self.class.new_client.
691
700
def connect
692
701
@raw_connection = jdbc_connection_class ( @config [ :adapter_spec ] ) . new ( @config , self )
693
702
@raw_connection . configure_connection
694
703
end
695
704
705
+ def reconnect
706
+ if active?
707
+ @raw_connection . rollback rescue nil
708
+ else
709
+ connect
710
+ end
711
+ end
712
+
696
713
def configure_connection
697
- # FIXME: missing from adapter
698
- # @connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
714
+ if @config [ :timeout ] && @config [ :retries ]
715
+ raise ArgumentError , "Cannot specify both timeout and retries arguments"
716
+ elsif @config [ :timeout ]
717
+ # FIXME:
718
+ # @raw_connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout]))
719
+ elsif @config [ :retries ]
720
+ retries = self . class . type_cast_config_to_integer ( @config [ :retries ] )
721
+ raw_connection . busy_handler do |count |
722
+ count <= retries
723
+ end
724
+ end
699
725
700
- execute ( "PRAGMA foreign_keys = ON" , "SCHEMA" )
726
+ # Enforce foreign key constraints
727
+ # https://www.sqlite.org/pragma.html#pragma_foreign_keys
728
+ # https://www.sqlite.org/foreignkeys.html
729
+ raw_execute ( "PRAGMA foreign_keys = ON" , "SCHEMA" )
730
+ unless @memory_database
731
+ # Journal mode WAL allows for greater concurrency (many readers + one writer)
732
+ # https://www.sqlite.org/pragma.html#pragma_journal_mode
733
+ raw_execute ( "PRAGMA journal_mode = WAL" , "SCHEMA" )
734
+ # Set more relaxed level of database durability
735
+ # 2 = "FULL" (sync on every write), 1 = "NORMAL" (sync every 1000 written pages) and 0 = "NONE"
736
+ # https://www.sqlite.org/pragma.html#pragma_synchronous
737
+ raw_execute ( "PRAGMA synchronous = NORMAL" , "SCHEMA" )
738
+ # Set the global memory map so all processes can share some data
739
+ # https://www.sqlite.org/pragma.html#pragma_mmap_size
740
+ # https://www.sqlite.org/mmap.html
741
+ raw_execute ( "PRAGMA mmap_size = #{ 128 . megabytes } " , "SCHEMA" )
742
+ end
743
+ # Impose a limit on the WAL file to prevent unlimited growth
744
+ # https://www.sqlite.org/pragma.html#pragma_journal_size_limit
745
+ raw_execute ( "PRAGMA journal_size_limit = #{ 64 . megabytes } " , "SCHEMA" )
746
+ # Set the local connection cache to 2000 pages
747
+ # https://www.sqlite.org/pragma.html#pragma_cache_size
748
+ raw_execute ( "PRAGMA cache_size = 2000" , "SCHEMA" )
701
749
end
702
-
703
750
end
704
751
# DIFFERENCE: A registration here is moved down to concrete class so we are not registering part of an adapter.
705
752
end
@@ -720,6 +767,18 @@ class SQLite3Adapter < AbstractAdapter
720
767
include ArJdbc ::Abstract ::StatementCache
721
768
include ArJdbc ::Abstract ::TransactionSupport
722
769
770
+ ##
771
+ # :singleton-method:
772
+ # Configure the SQLite3Adapter to be used in a strict strings mode.
773
+ # This will disable double-quoted string literals, because otherwise typos can silently go unnoticed.
774
+ # For example, it is possible to create an index for a non existing column.
775
+ # If you wish to enable this mode you can add the following line to your application.rb file:
776
+ #
777
+ # config.active_record.sqlite3_adapter_strict_strings_by_default = true
778
+ class_attribute :strict_strings_by_default , default : false
779
+
780
+
781
+
723
782
def self . represent_boolean_as_integer = ( value ) # :nodoc:
724
783
if value == false
725
784
raise "`.represent_boolean_as_integer=` is now always true, so make sure your application can work with it and remove this settings."
0 commit comments