1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Add support for UNLOGGED Postgresql tables

This commit adds support for the
`ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables`
setting, which turns `CREATE TABLE` SQL statements into
`CREATE UNLOGGED TABLE` statements.

This can improve PostgreSQL performance but at the
cost of data durability, and thus it is highly recommended
that you *DO NOT* enable this in a production environment.
This commit is contained in:
Jacob Evelyn 2018-10-15 15:00:07 -04:00
parent 82f2e9741f
commit bfc4d8be0a
6 changed files with 118 additions and 1 deletions

View file

@ -39,7 +39,7 @@ module ActiveRecord
end
def visit_TableDefinition(o)
create_sql = +"CREATE#{' TEMPORARY' if o.temporary} TABLE "
create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
create_sql << "IF NOT EXISTS " if o.if_not_exists
create_sql << "#{quote_table_name(o.name)} "
@ -121,6 +121,11 @@ module ActiveRecord
sql
end
# Returns any SQL string to go between CREATE and TABLE. May be nil.
def table_modifier_in_create(o)
" TEMPORARY" if o.temporary
end
def foreign_key_in_create(from_table, to_table, options)
options = foreign_key_options(from_table, to_table, options)
accept ForeignKeyDefinition.new(from_table, to_table, options)

View file

@ -23,6 +23,17 @@ module ActiveRecord
end
super
end
# Returns any SQL string to go between CREATE and TABLE. May be nil.
def table_modifier_in_create(o)
# A table cannot be both TEMPORARY and UNLOGGED, since all TEMPORARY
# tables are already UNLOGGED.
if o.temporary
" TEMPORARY"
elsif o.unlogged
" UNLOGGED"
end
end
end
end
end

View file

@ -175,6 +175,13 @@ module ActiveRecord
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
include ColumnMethods
attr_reader :unlogged
def initialize(*)
super
@unlogged = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables
end
private
def integer_like_primary_key_type(type, options)
if type == :bigint || options[:limit] == 8

View file

@ -85,6 +85,18 @@ module ActiveRecord
class PostgreSQLAdapter < AbstractAdapter
ADAPTER_NAME = "PostgreSQL"
##
# :singleton-method:
# PostgreSQL allows the creation of "unlogged" tables, which do not record
# data in the PostgreSQL Write-Ahead Log. This can make the tables faster,
# bug significantly increases the risk of data loss if the database
# crashes. As a result, this should not be used in production
# environments. If you would like all created tables to be unlogged you
# can add the following line to your test.rb file:
#
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
class_attribute :create_unlogged_tables, default: false
NATIVE_DATABASE_TYPES = {
primary_key: "bigserial primary key",
string: { name: "character varying" },

View file

@ -0,0 +1,74 @@
# frozen_string_literal: true
require "cases/helper"
require "support/schema_dumping_helper"
class UnloggedTablesTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
TABLE_NAME = "things"
LOGGED_FIELD = "relpersistence"
LOGGED_QUERY = "SELECT #{LOGGED_FIELD} FROM pg_class WHERE relname = '#{TABLE_NAME}'"
LOGGED = "p"
UNLOGGED = "u"
TEMPORARY = "t"
class Thing < ActiveRecord::Base
self.table_name = TABLE_NAME
end
def setup
@connection = ActiveRecord::Base.connection
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = false
end
teardown do
@connection.drop_table TABLE_NAME, if_exists: true
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = false
end
def test_logged_by_default
@connection.create_table(TABLE_NAME) do |t|
end
assert_equal @connection.execute(LOGGED_QUERY).first[LOGGED_FIELD], LOGGED
end
def test_unlogged_in_test_environment_when_unlogged_setting_enabled
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
@connection.create_table(TABLE_NAME) do |t|
end
assert_equal @connection.execute(LOGGED_QUERY).first[LOGGED_FIELD], UNLOGGED
end
def test_not_included_in_schema_dump
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
@connection.create_table(TABLE_NAME) do |t|
end
assert_no_match(/unlogged/i, dump_table_schema(TABLE_NAME))
end
def test_not_changed_in_change_table
@connection.create_table(TABLE_NAME) do |t|
end
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
@connection.change_table(TABLE_NAME) do |t|
t.column :name, :string
end
assert_equal @connection.execute(LOGGED_QUERY).first[LOGGED_FIELD], LOGGED
end
def test_gracefully_handles_temporary_tables
@connection.create_table(TABLE_NAME, temporary: true) do |t|
end
# Temporary tables are already unlogged, though this query results in a
# different result ("t" vs. "u"). This test is really just checking that we
# didn't try to run `CREATE TEMPORARY UNLOGGED TABLE`, which would result in
# a PostgreSQL error.
assert_equal @connection.execute(LOGGED_QUERY).first[LOGGED_FIELD], TEMPORARY
end
end

View file

@ -379,6 +379,14 @@ The MySQL adapter adds one additional configuration option:
* `ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns as booleans. Defaults to `true`.
The PostgreSQL adapter adds one additional configuration option:
* `ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables`
controls whether database tables created should be "unlogged," which can speed
up performance but adds a risk of data loss if the database crashes. It is
highly recommended that you do not enable this in a production environment.
Defaults to `false` in all environments.
The SQLite3Adapter adapter adds one additional configuration option:
* `ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer`