当使用ActiveRecord将字符串存储到SqlServer nvchar列中,并且字符串的长度大于列的最大长度时,字符串将被静默截断为列的大小。 如何发出警告或错误而不是默默地截断值?
数据库模型:
class Email < ActiveRecord::Base
self.table_name = "Email"
end
以静默方式截断的插入内容:
Email.create!(Address: "X" * 76)
email.reload
p email.Address.size # => 75
insert语句的日志:
D, [2017-06-30T16:04:35.320283 #9061] DEBUG -- : dest SQL (0.5ms) EXEC sp_executesql N'INSERT INTO [Email] ([Address]) OUTPUT INSERTED.[Id] VALUES (@0)', N'@0 nvarchar(75)', @0 = N'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' [["Address", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"]]
CREATE TABLE [dbo].[Email](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Address] [nvarchar](75) NOT NULL,
CONSTRAINT [PK_dbo.Email] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
答案 0 :(得分:3)
为了使SqlServer响应文本溢出并出现错误,需要做两件事:
ANSI_WARNINGS设置的效果之一是在字符列溢出时中止INSERT或UPDATE。有一个设置可以控制这种效果:
如果对字符,Unicode或二进制列尝试INSERT或UPDATE,其中新值的长度超过列的最大大小。如果SET ANSI_WARNINGS为ON,则按照ISO标准的规定取消INSERT或UPDATE。
此设置可能已经开启,但您可以使用此猴子补丁强制启用它:
class ActiveRecord::ConnectionAdapters::SQLServerAdapter
orig_configure_connection = instance_method(:configure_connection)
define_method(:configure_connection) do
orig_configure_connection.bind(self).call
@connection.execute('SET ANSI_WARNINGS ON').do
end
end
您还必须阻止适配器截断此列。这个猴子补丁会这样做:
class ActiveRecord::ConnectionAdapters::SQLServerColumn
orig_sql_type_for_statement = instance_method(:sql_type_for_statement)
define_method(:sql_type_for_statement) do
if sql_type =~ /\A(nvarchar|nchar|varchar|char|text|ntext|image|varbinary|xml)\(\d+\)\Z/
"#{$1}(max)"
else
orig_sql_type_for_statement.bind(self).call
end
end
end
有了这些猴子补丁,溢出了一个字符列:
email = Dest::Email.create!(Address: "X" * 76)
会导致异常:
ActiveRecord::StatementInvalid:
TinyTds::Error: String or binary data would be truncated.: EXEC sp_executesql N'INSERT INTO [Email] ([Address]) OUTPUT INSERTED.[Id] VALUES (@0)', N'@0 nvarchar(max)', @0 = N'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
答案 1 :(得分:1)
您可以使用ActiveRecord验证,如下所示:
class Email < ActiveRecord::Base
self.table_name = "Email"
validates :Address, length: { maximum: 75 }
end
在这种情况下,Email.create!(Address: "X" * 76)
会引发异常。
您可以通过执行以下操作来访问错误:
email = Email.create(Address: "X" * 76)
email.valid? # => false
email.errors[:Address] # => ["is too long (maximum is 75 characters)"]
您有更多可用信息here。