This blog is part of our Rails 7 series.
Rails 7 has introduced a new setting called enumerate_columns_in_select_statements for enumerating columns in activerecord select query statements by which we can avoid common ActiveRecord::PreparedStatementCacheExpired errors.
Rails uses prepared statements for database query efficiency. When prepared statements are being used, the repeated queries will be cached based on the prepared statement query plan at Postgres database level. This cached value will become invalid when the returned results are changed.
Whenever we make any schema changes to database tables while application is running, the cached select statements with wildcard column definition will raise PreparedStatementCacheExpired error since the query output has modified.
Before
1=> User.limit(10) 2=> SELECT * FROM users LIMIT 10
If we use the select query with *, then any change in the database schema for the particular table (eg: users) will invalidate the prepared statement cache and result in the PreparedStatementCacheExpired error. The solution here is to mention the columns explicitly in the select statement as shown below:
1=> SELECT "first_name,last_name,email ..." FROM users LIMIT 10
Rails 7 onwards
Rails 7 adds a new setting, by which we can ensure all select statements are generated by enumerating the columns explicitly. Hence any modifications to the database schema won't result in PreparedStatementCacheExpired, instead the prepared statements will be changed and the respective query will be cached freshly by the Postgres database.
We can either configure the setting for all models or at specific model level.
1# config/application.rb 2module MyApp 3 class Application < Rails::Application 4 config.active_record.enumerate_columns_in_select_statements = true 5 end 6end 7 8# User model specific 9class User < ApplicationRecord 10 self.enumerate_columns_in_select_statements = true 11end
When the setting value is set to true the select statement will always contains columns explicitly.
1=> User.limit(10) 2=> SELECT "first_name,last_name,email ..." FROM users LIMIT 10
Check out this pull request for more details.