确定函数中实际查询的模式名称

时间:2012-05-16 11:43:10

标签: postgresql

我想用它来构造unique_key。但我不想明确提供架构名称。有什么方法可以搞清楚吗?

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar)
  RETURNS BOOLEAN AS
$$
BEGIN
   RETURN NOT EXISTS (
      SELECT header_id, value
      FROM myschema.mytable
      WHERE value LIKE _value AND header_id != _header_id
      LIMIT 1);
END;
$$ LANGUAGE plpgsql;

ALTER TABLE mytable
  ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value))

如果我这样做,它必须有效:

set search_path = schema1;

CREATE FUNCTION AS ABOVE

set search_path = schema2;

INSERT INTO schema1.mytable(id, header_id, value)
VALUES (1,1,'a');  -- Should be ok

INSERT INTO schema1.mytable(id, header_id, value)
VALUES (1,2,'a');  -- Should violate error

考虑具有相同表结构的多个模式。

Edt:2012.05.16-1533 以此表为例: Header_table:id serial Snapshot_table:id serial,header_id int(FK到头表),值varchar

id               id | header_id | value
 1                1 |         1 | a
 2                2 |         1 | a
 3                3 |         2 | b
                  4 |         3 | b

等。使用header_id 3进行记录会违反unique_key

这是指:Unique constraint on one column with excluding row with same values in other

Edt:20120516-2356 添加一些测试脚本(对不起,我不知道如何加入文件)

drop schema if exists s1 cascade;
drop schema if exists s2 cascade;
drop schema if exists tp cascade; 

create schema s1;
create schema s2;
create schema tp; -- Temporary schema to hold insert function

-- SCHEMA 1 --

set search_path = s1;

-- * * * header table * * * 

CREATE TABLE th1 (
    id SERIAL NOT NULL,
    constraint th1_pk_id PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);

-- * * * snapshot table * * *

CREATE TABLE ts1 (
    id SERIAL NOT NULL,
    header_id INTEGER NOT NULL,
    version INTEGER NOT NULL,
    value VARCHAR NOT NULL,
    CONSTRAINT ts1_pk_id PRIMARY KEY (id),
    CONSTRAINT ts1_header_id_fk FOREIGN KEY (header_id) REFERENCES th1(id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
)
WITH (
  OIDS=FALSE
);

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar)
  RETURNS BOOLEAN AS
$$
DECLARE
   b boolean;
BEGIN
   RAISE INFO ' ---- CHECK UNIQUE FUNCTION ----';
   RAISE INFO 'Search_path: %', current_schema();
   RAISE INFO 'Function caled schema: %', 's1';
   SELECT NOT EXISTS (
      SELECT 1
      FROM ts1
      WHERE value LIKE _value AND header_id != _header_id
      LIMIT 1) INTO b;
   RAISE INFO 'Result: %', b;
   RETURN b;
END;
$$ LANGUAGE plpgsql;

ALTER TABLE ts1
  ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value));

CREATE OR REPLACE FUNCTION logSchema() RETURNS TRIGGER AS $$
BEGIN
    RAISE INFO 'Call on schema: %', 's1';
    RETURN new;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER logSchemaTrigger
BEFORE INSERT OR UPDATE ON ts1
FOR EACH ROW EXECUTE PROCEDURE logSchema();


-- SCHEMA2 --

set search_path = s2;

-- * * * header table * * * 

CREATE TABLE th1 (
    id SERIAL NOT NULL,
    constraint th1_pk_id PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);

-- * * * snapshot table * * *

CREATE TABLE ts1 (
    id SERIAL NOT NULL,
    header_id INTEGER NOT NULL,
    version INTEGER NOT NULL,
    value VARCHAR NOT NULL,
    CONSTRAINT ts1_pk_id PRIMARY KEY (id),
    CONSTRAINT ts1_header_id_fk FOREIGN KEY (header_id) REFERENCES th1(id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
)
WITH (
  OIDS=FALSE
);

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar)
  RETURNS BOOLEAN AS
$$
DECLARE
   b boolean;
BEGIN
   RAISE INFO ' ---- CHECK UNIQUE FUNCTION ----';
   RAISE INFO 'Search_path: %', current_schema();
   RAISE INFO 'Function caled schema: %', 's2';
   SELECT NOT EXISTS (
      SELECT 1
      FROM ts1
      WHERE value LIKE _value AND header_id != _header_id
      LIMIT 1) INTO b;
   RAISE INFO 'Result: %', b;
   RETURN b;
END;
$$ LANGUAGE plpgsql;

ALTER TABLE ts1
  ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value));

CREATE OR REPLACE FUNCTION logSchema() RETURNS TRIGGER AS $$
BEGIN
    RAISE INFO 'Actual schema: %', 's2';
    RETURN new;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER logSchemaTrigger
BEFORE INSERT OR UPDATE ON ts1
FOR EACH ROW EXECUTE PROCEDURE logSchema();

-- INSERT PREPARE DATA --

-- insert function to handle error
CREATE OR REPLACE FUNCTION tp.insert_data_test(_schemaname name, _header_id integer, _version integer, _value varchar)
  RETURNS VOID AS
$$
DECLARE
   schemaname name;
   b boolean;
BEGIN
   RAISE INFO '==== NEW INSERT COMMAND ====';
   SELECT _schemaname into schemaname;
   IF schemaname IS NULL THEN
       SELECT current_schema() INTO schemaname;
   end if;

   RAISE INFO 'Search_path: %', schemaname;
   RAISE INFO 'Inserting data: %, %, %', _header_id, _version, _value;

   BEGIN
      EXECUTE 'SELECT NOT EXISTS ( SELECT 1 FROM ' ||(select case when schemaname is null then 'null' else schemaname end)|| '.th1 WHERE id = ' || quote_literal(_header_id) || ' LIMIT 1)' INTO b;
      IF (SELECT b) THEN      
          EXECUTE 'INSERT INTO ' || (select case when schemaname is null then 'null' else schemaname end) || '.th1 VALUES(' || quote_literal(_header_id) ||')';
      END IF;

      EXECUTE 'INSERT INTO ' || (select case when schemaname is null then 'null' else schemaname end) || '.ts1(header_id, version, value) VALUES( ' || quote_literal(_header_id) || ',' || quote_literal(_version) ||','|| quote_literal(_value) ||')';
   EXCEPTION WHEN others THEN
      RAISE INFO 'Insert error: % %', SQLERRM, SQLSTATE;
      RETURN;
   END;

   RAISE INFO 'Insert succesfull';
END;
$$ LANGUAGE plpgsql;

-- Wiht no current schema --
set search_path = default;

SELECT tp.insert_data_test('s1',1,0,'a');
SELECT tp.insert_data_test('s2',1,0,'b');

/* IF you do this above with before set search_path its go fine, but if you do insert from newone query thats drop:
ERROR:  relation "ts1" does not exist
LINE 3:       FROM ts1
                   ^
QUERY:  SELECT NOT EXISTS (
      SELECT 1
      FROM ts1
      WHERE value LIKE _value AND header_id != _header_id
      LIMIT 1)
CONTEXT:  PL/pgSQL function "is_value_free" line 3 at RETURN

********** Chyba **********

ERROR: relation "ts1" does not exist
Stav SQL: 42P01
Kontext:PL/pgSQL function "is_value_free" line 3 at RETURN
*/

-- Insert data with corect schema --
set search_path = s1;
SELECT tp.insert_data_test(null,1,1,'a2');

set search_path = s2;
SELECT tp.insert_data_test(null,1,1,'b2');

-- Insert data with incorect schema --
set search_path = s2;
SELECT tp.insert_data_test('s1',1,2,'a3');

set search_path = s1;
SELECT tp.insert_data_test('s2',1,2,'b3');

-- TEST DATA (Should for all violate unique_key)--
-- Wiht no current schema --
set search_path = default;

SELECT tp.insert_data_test('s1',2,0,'a');
SELECT tp.insert_data_test('s2',2,0,'b');

-- Wiht corect current schema --
set search_path = s1;
SELECT tp.insert_data_test(null,3,0,'a');

set search_path = s2;
SELECT tp.insert_data_test(null,3,0,'b');

-- Wiht incorect current schema --
set search_path = s2;
SELECT tp.insert_data_test('s1',4,0,'a');

set search_path = s1;
SELECT tp.insert_data_test('s2',4,0,'b');

drop schema if exists s1 cascade;
drop schema if exists s2 cascade;
drop schema if exists tp cascade; 
美国东部时间:2012.05.28:1937 托盘并在数据库中找到它(我之前没有看到)

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar)
   RETURNS BOOLEAN AS
   $BODY$
   BEGIN
      RETURN SELECT  NOT EXISTS 
            (
            SELECT  NULL
            FROM    mytable
            WHERE   value LIKE $2
                    AND header_id != $1
            );
   END;
   $BODY$
   LANGUAGE plpgsql VOLATILE
   COST 100;

   ALTER FUNCTION schema1.is_value_free(integer, character varying) SET search_path=schema1, public;

但是我需要在创建数据库函数时使用相同的代码并使用postgre函数从数据库中获取定义(如备份...)

1 个答案:

答案 0 :(得分:4)

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar)
  RETURNS BOOLEAN AS
$$
        SELECT  NOT EXISTS 
                (
                SELECT  NULL
                FROM    mytable
                WHERE   value LIKE $2
                        AND header_id != $1
                );
$$ LANGUAGE sql
SET search_path FROM CURRENT;

不需要EXISTS半联接LIMIT 1SELECT项无关紧要。

请注意,您的唯一功能不可交换,因此表格内容将取决于记录的插入顺序。

<强>更新

DROP SCHEMA IF EXISTS s1 CASCADE;
DROP SCHEMA IF EXISTS s2 CASCADE;
CREATE SCHEMA s1;
CREATE SCHEMA s2;

SET search_path = s1;

DROP TABLE IF EXISTS mytable;

CREATE TABLE mytable (id bigserial primary key, header_id integer not null, value text);

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar)
  RETURNS BOOLEAN AS
$$
        SELECT  NOT EXISTS 
                (
                SELECT  NULL
                FROM    mytable
                WHERE   value LIKE $2
                        AND header_id != $1
                );
$$ LANGUAGE sql
-- Below is the most important clause
SET search_path FROM CURRENT;

ALTER TABLE mytable
  ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value));

SET search_path = s2;

DELETE
FROM    s1.mytable;

INSERT INTO s1.mytable(id, header_id, value)
VALUES (1,1,'a');  -- Should be ok

INSERT INTO s1.mytable(id, header_id, value)
VALUES (2,2,'a');  -- Should violate error