0

我需要为 Rails 应用程序迁移 PG 数据库 - 我正在将表的一列的类型从布尔值更新为 int。这是代码:

class ChangeAdminToInt < ActiveRecord::Migration
  def up
    execute '
      ALTER TABLE "users"
      ALTER COLUMN "admin"
      TYPE int USING ("admin"::int)
      SET DEFAULT "1"
      '
  end
end

当我运行时,bundle exec rake db:migrate我收到错误:

PG::Error: ERROR:  syntax error at or near "SET"
LINE 5:  SET DEFAULT "1"

但是,当我将代码切换到:

class ChangeAdminToInt < ActiveRecord::Migration
  def up
    execute '
      ALTER TABLE users 
      ALTER COLUMN admin 
      TYPE int USING (admin::int);
      SET DEFAULT 1
      '
  end
end

我得到错误:

PG::Error: ERROR:  syntax error at or near "TYPE"
LINE 5:  TYPE int USING ("admin"::int)

因为问题不在于函数,而在于第 5 行,所以我认为数据库不喜欢第 5 行。PG 仅解析我的部分执行命令的引用长度是否有限制?我尝试了许多类型的语法,单引号/双引号,%q/%Q 块,甚至将整个引号放在一行中,但语法错误仍然指向 TYPE 和 SET DEFAULT 中的较早者。当然问题不在于 ALTER 这里发生了什么?

任何帮助表示赞赏。如果我错过了一些完全明显的东西,请告诉我。顺便说一句,一些 Gemfile:

source 'https://rubygems.org'
ruby '1.9.3'

gem 'rails', '~> 4.0.1.rc3'
gem 'bootstrap-sass', '2.3.2.0'
gem 'bcrypt-ruby', '~> 3.1.1'
gem 'faker', '1.1.2'
gem 'will_paginate', '3.0.4'
gem 'bootstrap-will_paginate', '0.0.9'
gem 'safe_attributes'

group :development, :test do
  gem 'pg', '0.15.1'
  gem 'sqlite3'
  gem 'rspec-rails', '2.13.1'
  gem 'guard-rspec', '2.5.0'
  gem 'spork-rails', github: 'sporkrb/spork-rails'
  gem 'guard-spork', '1.5.0'
  gem 'childprocess', '0.3.6'
end

以及用于开发环境的 database.yml:

development:
  adapter: postgresql
  encoding: utf8
  database: project_development
  pool: 5
  username: postgres
4

2 回答 2

1

我想到了!

这句话来自 PostgreSQL 网站 http://www.postgresql.org/docs/8.1/static/sql-altertable.html

由于这种灵活性,USING 表达式不适用于列的默认值(如果有);结果可能不是默认值所需的常量表达式。这意味着当没有从旧类型到新类型的隐式或赋值强制转换时,即使提供了 USING 子句,ALTER TYPE 也可能无法转换默认值。在这种情况下,使用 DROP DEFAULT 删除默认值,执行 ALTER TYPE,然后使用 SET DEFAULT 添加合适的新默认值。类似的考虑适用于涉及列的索引和约束。

如果我第一次尝试更改类型,它不喜欢旧的默认值。如果我首先设置一个新的默认值,它不知道如何输入转换。这篇文章说我具体操作的正确方法是先去掉默认,改变类型再设置新的默认

这是对我有用的代码。

class ChangeAdminToInt < ActiveRecord::Migration
  def up
    execute %q(
      ALTER TABLE users 
      ALTER COLUMN admin DROP DEFAULT,
      ALTER COLUMN admin TYPE int USING ("admin"::int),
      ALTER COLUMN admin SET DEFAULT 1;
      )
  end
end

就这样!

于 2013-10-25T17:37:35.743 回答
0

我认为您的 USING 子句不正确。这是来自 postgre 文档(http://www.postgresql.org/docs/8.0/static/sql-altertable.html):

可选的 USING 子句指定如何从旧的列中计算新的值;如果省略,则默认转换与从旧数据类型到新数据类型的赋值转换相同。如果没有从旧类型到新类型的隐式或赋值强制转换,则必须提供 USING 子句。

所以 USING 用于告诉 postgres 如何从旧值类型计算新值类型。我真的不明白USING ("admin"::int)你的查询是什么意思。尝试省略 USING 子句或尝试以下操作:

USING CASE WHEN "admin" = TRUE THEN 1 ELSE 0 END
于 2013-10-25T08:47:35.993 回答