`ActiveRecord::ConnectionAdapters::SQLite3Adapter#initialize` does not correctly create missing parent directories
ActiveRecord::ConnectionAdapters::SQLite3Adapter#initialize will check if a sqlite3 database file path exists, and if not it will attempt to create the directory using Dir.mkdir. However, Dir.mkdir cannot create all parent directories like FileUtils.mkdir_p. Thus an No such file or directory @ dir_s_mkdir - does/not/exist/yet (Errno::ENOENT) will be raised if one of the parent-parent directories of the sqlite3 database file do not exist yet.
Steps to reproduce
-
ActiveRecord::Base.establish_connect({adapter: 'sqlite3', database: 'does/not/exist/yet/database.sqlite3'}) -
migrations = ActiveRecord::MigrationContext.new(['path/to/db/migrate'])
Your reproduction script goes here
require 'active_record'
ActiveRecord::Base.establish_connect({adapter: 'sqlite3', database: 'does/not/exist/yet/database.sqlite3'})
migrations = ActiveRecord::MigrationContext.new(['path/to/db/migrate'])
Expected behavior
Use FileUtils.mkdir_p to create all parent directories for the missing sqlite3 database file.
Actual behavior
/home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/sqlite3_adapter.rb:119:in `rescue in initialize': Database not found (ActiveRecord::NoDatabaseError)
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/sqlite3_adapter.rb:115:in `initialize'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/sqlite3_adapter.rb:24:in `new'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/sqlite3_adapter.rb:24:in `sqlite3_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:676:in `public_send'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:676:in `new_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:723:in `checkout_new_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:702:in `try_to_checkout_new_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:654:in `acquire_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:353:in `checkout'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:182:in `connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_handler.rb:246:in `retrieve_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_handling.rb:287:in `retrieve_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_handling.rb:254:in `connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/tasks/database_tasks.rb:510:in `migration_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/migration.rb:1367:in `connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/migration.rb:1229:in `initialize'
... 5 levels...
/home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/sqlite3_adapter.rb:116:in `mkdir': No such file or directory @ dir_s_mkdir - does/not/exist/yet (Errno::ENOENT)
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/sqlite3_adapter.rb:116:in `initialize'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/sqlite3_adapter.rb:24:in `new'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/sqlite3_adapter.rb:24:in `sqlite3_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:676:in `public_send'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:676:in `new_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:723:in `checkout_new_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:702:in `try_to_checkout_new_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:654:in `acquire_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:353:in `checkout'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:182:in `connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_adapters/abstract/connection_handler.rb:246:in `retrieve_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_handling.rb:287:in `retrieve_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/connection_handling.rb:254:in `connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/tasks/database_tasks.rb:510:in `migration_connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/migration.rb:1367:in `connection'
from /home/postmodern/.gem/ruby/gems/activerecord-7.1.3.2/lib/active_record/migration.rb:1229:in `initialize'
... 5 levels...
System configuration
Rails version: 7.1.3.2
Ruby version: ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]
@postmodern Please check this one. Seems like the actual cause of the issue. https://github.com/rails/rails/issues/50391
@maniSHarma7575 as indicated in my issue, I'm not using rails db:drop. I am directly calling ActiveRecord::Base.establish_connection and directly invoking the migrations using ActiveRecord::MigrationContext.new(['path/to/db/migrate']). I discovered this limitation when trying to run my own database tool that uses ActiveRecord to create a database in the ~/.local/share/ronin-db/ directory, but when I ran it in a completely new user environment that did not have a ~/.local directory, ActiveRecord failed.
@maniSHarma7575 as indicated in my issue, I'm not using
rails db:drop. I am directly callingActiveRecord::Base.establish_connectionand directly invoking the migrations usingActiveRecord::MigrationContext.new(['path/to/db/migrate']). I discovered this limitation when trying to run my own database tool that uses ActiveRecord to create a database in the~/.local/share/ronin-db/directory, but when I ran it in a completely new user environment that did not have a~/.localdirectory, ActiveRecord failed.
@postmodern I think it's intentional that it should raise an error when parent directory does not exists. Rails have a test case for that in source code as well.
https://github.com/rails/rails/blob/82054e8eb76a72ded0abee83019e60c19f552a8b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb#L26-L32
@eileencodes Should we fixed this? For Sqlite3 Rails should allow to create the database even if the database path with parent directories doesn't exists?
@maniSHarma7575 why? It does not have to raise an error, it can simply create the parent directories using FileUtils.mkdir_p. I don't see any benefit for having such a limitation. This only prevents ActiveRecord from creating databases in nested directories that do not exist yet.
@maniSHarma7575 why? It does not have to raise an error, it can simply create the parent directories using
FileUtils.mkdir_p. I don't see any benefit for having such a limitation. This only preventsActiveRecordfrom creating databases in nested directories that do not exist yet.
@postmodern I also agree with you and have created a pull request for it. However, I'm unsure about the opinion of the Rails core team. Let's wait for their input and see what their take on it is.