4

PostgreSQL 8.4データベースバックエンドを使用して(現在)Rails2.3.xアプリケーションを開発しています。Railsアプリケーションには、データ型SERIALの2つの列があり、NOTNULLとして設定されているデータベーステーブルに対応するモデルがあります。これらの列の1つを、RailsとPostgreSQL制約の両方の主キーとして設定しています。

テーブル定義:

CREATE TABLE problem_table
(
  col1 serial NOT NULL,
  col2 serial NOT NULL,
  other_col1 character varying,
  other_col2 character varying,
  ...,
  CONSTRAINT problem_table_pkey PRIMARY KEY (col1)
);

モデルクラスの定義:

class ModelClass1 < ActiveRecord::Base
  self.table_name = 'problem_table'
  self.primary_key = 'col1'
end

私の問題は、非主キーのSERIALNOTNULL列に関するものです。Rails ActiveRecord :: Base#createを実行しようとすると、Railsは主キーSERIAL NOT NULL列の値を正しく設定しませんが、他の列の列値をNULLに設定します。これにより、PostgreSQLは次のように文句を言います。 NOTNULL列がNULLに設定されています。

Railsに指示すること:

ModelClass1.create(
  other_col1: 'normal'
  other_col2: 'data',
  ...
);

RailsがPostgreSQLに伝える内容:

INSERT INTO problem_table (
  col2, 
  other_col1, 
  other_col2,
  ...
) VALUES (
  NULL,
  'normal',
  'data',
  ...
);

私の質問は、Railsがこの列のNULLの受け渡しを停止し、何も渡さないようにして、DEFAULT nextval(my_seq)に引き継がせるにはどうすればよいですか?または、それが不可能な場合、渡されたときにこのNULL値を無視するようにPostgreSQLに指示したり、これを「DEFAULTとして設定」と同じであると認識するようにするにはどうすればよいですか?

Rails 2.3.x ActiveRecordの内部にモンキーパッチを適用しようとしましたが、そうすると、Rails3への移行に関しては困惑するでしょう。

INSERTの前にPL/pgSQLトリガーを使用して問題を修正することを検討しましたが、PL / pgSQLを使用するPostgreSQLにNEW.col2値を「定義解除」するか、NEW.col2:=DEFAULTと言う方法がわかりません。 (これは機能しません)。

回答および/または提案をいただければ幸いです。

4

3 に答える 3

4

nextval()これを行う簡単な方法は、おそらく独自のシーケンスを定義し、ActiveRecord コールバック内で Postgres 独自のものを使用することです。nextval()1 つのアトミック操作で、シーケンスを 1 ステップ進めることと、次の値を返すことの両方を処理します。

移行では:

def self.up
    execute "CREATE SEQUENCE myseq"
end

def self.down
    execute "DROP SEQUENCE myseq"
end    

そしてモデルでは:

before_save :set_column_from_sequence, :on => :create

def set_column_from_sequence
    self.mycolumn = self.class.connection.select_value("SELECT nextval('myseq')")
end
于 2010-11-24T03:02:16.637 に答える
0

うまくいくものを見つけました。インクリメントに異なるシーケンスを使用するかなりの数のテーブルで、この2番目のSERIAL列が必要になる可能性があります。以下は、一意のシーケンスによってインクリメントされるcol1を含む任意の数のテーブルに対して1つのトリガー関数のみが必要なソリューションです。

CREATE OR REPLACE FUNCTION fn_set_col1_as_nextval_sequence_if_null()
  RETURNS trigger AS
$BODY$
  BEGIN
    IF NEW.col1 IS NULL THEN
      SELECT nextval(TG_ARGV[0]) INTO NEW.col1;
    END IF;
    RETURN NEW;
  END;
$BODY$
  LANGUAGE 'plpgsql';

CREATE TRIGGER trg_set_col1_as_nextval_sequence_on_problem_table_create
BEFORE INSERT
ON problem_table
FOR EACH ROW
EXECUTE PROCEDURE fn_set_col1_as_nextval_sequence_if_null('problem_table_col1_seq');

可能であれば、2.3.xと3.0のRailsの動作を修正する方法を知りたいのですが、それまではうまくいくでしょう。

于 2010-10-13T15:55:58.917 に答える
0

正確なPL/pgSQL構文についてはわかりません(私のPostgreSQLのインストールは家にあるので、いじることはできません)が、Oracle PL/SQLでは次のようにします

CREATE OR REPLACE TRIGGER MYSCHEMA.PROBLEM_TABLE_BI
  BEFORE INSERT ON MYSCHEMA.PROBLEM_TABLE
  REFERENCING NEW AS NEW
  FOR EACH ROW
BEGIN
  IF :NEW.COL2 IS NULL THEN
    :NEW.COL2 := MY_SEQ.NEXT_VAL;
  END IF;
END PROBLEM_TABLE_BI;

PL/pgSQL も似ているはずです。

お役に立てれば。

于 2010-10-13T11:49:36.080 に答える