未验证 提交 78a81880 编写于 作者: E Eugene Kenny 提交者: GitHub

Merge pull request #40192 from agrobbin/postgresql-not-valid-check-constraint

Add support for `NOT VALID` check constraints in PostgreSQL
* Add support for check constraints that are `NOT VALID` via `validate: false` (PostgreSQL-only).
*Alex Robbin*
* Ensure the default configuration is considered primary or first for an environment
If a multiple database application provides a configuration named primary, that will be treated as default. In applications that do not have a primary entry, the default database configuration will be the first configuration for an environment.
......
......@@ -132,6 +132,11 @@ def name
options[:name]
end
def validate?
options.fetch(:validate, true)
end
alias validated? validate?
def export_name_on_schema_dump?
!ActiveRecord::SchemaDumper.chk_ignore_pattern.match?(name) if name
end
......
......@@ -1142,6 +1142,11 @@ def check_constraints(table_name)
#
# ALTER TABLE "products" ADD CONSTRAINT price_check CHECK (price > 0)
#
# The +options+ hash can include the following keys:
# [<tt>:name</tt>]
# The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
# [<tt>:validate</tt>]
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
def add_check_constraint(table_name, expression, **options)
return unless supports_check_constraints?
......
......@@ -13,6 +13,10 @@ def visit_AddForeignKey(o)
super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? }
end
def visit_CheckConstraintDefinition(o)
super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? }
end
def visit_ValidateConstraint(name)
"VALIDATE CONSTRAINT #{quote_column_name(name)}"
end
......
......@@ -523,7 +523,7 @@ def check_constraints(table_name) # :nodoc:
scope = quoted_scope(table_name)
check_info = exec_query(<<-SQL, "SCHEMA")
SELECT conname, pg_get_constraintdef(c.oid) AS constraintdef
SELECT conname, pg_get_constraintdef(c.oid) AS constraintdef, c.convalidated AS valid
FROM pg_constraint c
JOIN pg_class t ON c.conrelid = t.oid
WHERE c.contype = 'c'
......@@ -532,7 +532,8 @@ def check_constraints(table_name) # :nodoc:
check_info.map do |row|
options = {
name: row["conname"]
name: row["conname"],
validate: row["valid"]
}
expression = row["constraintdef"][/CHECK \({2}(.+)\){2}/, 1]
......@@ -632,6 +633,19 @@ def validate_foreign_key(from_table, to_table = nil, **options)
validate_constraint from_table, fk_name_to_validate
end
# Validates the given check constraint.
#
# validate_check_constraint :products, name: "price_check"
#
# The +options+ hash accepts the same keys as add_check_constraint[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
def validate_check_constraint(table_name, **options)
return unless supports_validate_constraints?
chk_name_to_validate = check_constraint_for!(table_name, **options).name
validate_constraint table_name, chk_name_to_validate
end
private
def schema_creation
PostgreSQL::SchemaCreation.new(self)
......
......@@ -64,6 +64,43 @@ def test_added_check_constraint_ensures_valid_values
end
end
if ActiveRecord::Base.connection.supports_validate_constraints?
def test_not_valid_check_constraint
Trade.create(quantity: -1)
@connection.add_check_constraint :trades, "quantity > 0", name: "quantity_check", validate: false
assert_raises(ActiveRecord::StatementInvalid) do
Trade.create(quantity: -1)
end
end
def test_validate_check_constraint_by_name
@connection.add_check_constraint :trades, "quantity > 0", name: "quantity_check", validate: false
assert_not_predicate @connection.check_constraints("trades").first, :validated?
@connection.validate_check_constraint :trades, name: "quantity_check"
assert_predicate @connection.check_constraints("trades").first, :validated?
end
def test_validate_non_existing_check_constraint_raises
assert_raises ArgumentError do
@connection.validate_check_constraint :trades, name: "quantity_check"
end
end
else
# Check constraint should still be created, but should not be invalid
def test_add_invalid_check_constraint
@connection.add_check_constraint :trades, "quantity > 0", name: "quantity_check", validate: false
check_constraints = @connection.check_constraints("trades")
assert_equal 1, check_constraints.size
cc = check_constraints.first
assert_predicate cc, :validated?
end
end
def test_remove_check_constraint
@connection.add_check_constraint :trades, "price > 0", name: "price_check"
@connection.add_check_constraint :trades, "quantity > 0", name: "quantity_check"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册