【问题标题】:Activerecord-import & serial column in PostgreSQLPostgreSQL 中的 Activerecord-import 和串行列
【发布时间】:2012-04-01 23:54:45
【问题描述】:

我正在将 Rails 2.3.4 项目升级到 Rails 3.1.1。旧版本使用 ar-extensions 来处理数据导入。我拔出 ar-extensions 并用 activerecord-import 替换它,据我了解,它具有完全相同的接口。

我的代码调用如下所示

Student.import(columns, values)

两个 args 都是保存正确数据的有效数组,但我得到一个大错误!

错误堆栈如下所示:

NoMethodError (You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.split):
  activerecord (3.1.1) lib/active_record/connection_adapters/postgresql_adapter.rb:828:in 'default_sequence_name'
  activerecord (3.1.1) lib/active_record/base.rb:647:in `reset_sequence_name'
  activerecord (3.1.1) lib/active_record/base.rb:643:in `sequence_name'
  activerecord-import (0.2.9) lib/activerecord-import/import.rb:203:in `import'

查看代码,好像 Activerecord-import 调用了 activerecord,它依次查找 Postgres 序列的名称和下一个值。

所以 activerecord-import 寻找 sequence_name lib/activerecord-import/import.rb:203

# Force the primary key col into the insert if it's not
# on the list and we are using a sequence and stuff a nil
# value for it into each row so the sequencer will fire later
-> if !column_names.include?(primary_key) && sequence_name && connection.prefetch_primary_key?
   column_names << primary_key
   array_of_attributes.each { |a| a << nil }
end

它调用活动记录... lib/active_record/base.rb:647:in `reset_sequence_name'

# Lazy-set the sequence name to the connection's default. This method
# is only ever called once since set_sequence_name overrides it.
def sequence_name #:nodoc:
->  reset_sequence_name
end

def reset_sequence_name #:nodoc:
  -> default = connection.default_sequence_name(table_name, primary_key)
  set_sequence_name(default)
  default
end

serial_sequence 返回 nil 并且 default_sequence_name 尝试拆分时代码错误。

lib/active_record/connection_adapters/postgresql_adapter.rb

# Returns the sequence name for a table's primary key or some other specified key.
def default_sequence_name(table_name, pk = nil) #:nodoc:
  -> serial_sequence(table_name, pk || 'id').split('.').last
rescue ActiveRecord::StatementInvalid
  "#{table_name}_#{pk || 'id'}_seq"
end

def serial_sequence(table, column)
  result = exec_query(<<-eosql, 'SCHEMA', [[nil, table], [nil, column]])
    SELECT pg_get_serial_sequence($1, $2)
  eosql
  result.rows.first.first
end

当我直接对数据库执行 pg_get_serial_sequence() 时,我没有返回任何值:

SELECT pg_get_serial_sequence('student', 'id')

但是我可以看到在数据库中有一个叫做student_id_seq的序列

我正在使用以下版本的 Ruby、rails PG 等。

  • Rails 3.1.1
  • Ruby 1.9.2
  • Activerecord 导入 0.2.9
  • 第 0.12.2 页
  • psql(9.0.5,服务器 9.1.3)

我已将数据库从 MySQL 迁移到 PostgreSQL,我认为这与问题没有任何关系,但我认为我最好添加它以确保完整性。

我不知道为什么这不起作用!

【问题讨论】:

    标签: postgresql activerecord ruby-on-rails-3.1 auto-increment


    【解决方案1】:

    您的描述摘要:

    • student 存在。
    • id 列存在。
    • 序列student_id_seq存在。
    • pg_get_serial_sequence('student', 'id') 仍然返回 NULL

    两种可能的解释:

    1) 序列未链接到列。

    列默认值以及列和序列之间的联系是独立的特征。仅存在拟合序列并不意味着它会按照您的假设进行。但是,如果您将列创建为serial,您将获得整个包。阅读the details in the manual

    要解决此问题(如果您确定应该是这样),您可以将序列标记为“由”student.id 拥有:

    ALTER SEQUENCE student_id_seq OWNED BY student.id;
    

    同时检查列默认值是否按预期设置:

    SELECT column_name, column_default
    FROM   information_schema.columns
    WHERE  table_name = 'student'
    -- AND    schema = 'your_schema' -- if needed
    

    如果没有,修复:

    ALTER TABLE student ALTER COLUMN id SET DEFAULT nextval('student.id')
    

    2) 主机地址/端口/数据库/模式/表名大小写的混合

    它一直在发生。确保检查您的应用程序连接到的同一数据库,使用相同的用户或至少相同的search_path。确保对象位于您期望它们的架构中,并且没有,例如,另一个架构中的另一个 student 表混淆了。

    【讨论】:

    • 序列未附加到列!感谢您的帮助。
    • 这在stackoverflow.com/questions/10546850/… 中也有介绍,我在那里放了一些关于为什么它在 Rails 3.1 中出现问题的 cmets。简短的故事是,在 Rails 3.1 之前,当不知道列到序列的映射时,ActiveRecord 将回退到默认字符串 tablename_columnname_seq,但现在没有回退,因此您必须显式设置所有权Postgres(如上述解决方案所述)。
    猜你喜欢
    • 1970-01-01
    • 2017-02-13
    • 1970-01-01
    • 2019-05-18
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 2020-01-11
    相关资源
    最近更新 更多