1

Temat: Partycje na regułach i zwracana wartość po insert

Więc jest sobie tabela nadrzędna z pozycjami dokumentów np:
CREATE TABLE magazyn_inout_pos_parent
(
  doc_id character varying(25) NOT NULL, -- Referencja do dokumentu
  doc_pos integer NOT NULL,
  quantity numeric(12,2) NOT NULL,
  product_id integer NOT NULL,
  unit_price numeric(12,2) NOT NULL, -- cena za 1 unit
  mc_id character varying(12) NOT NULL, -- referencja do otwartego miesiąca
  CONSTRAINT magazyn_inout_pos_parent_pkey PRIMARY KEY (doc_id, doc_pos)
)
i tabela z miesiącami  :
CREATE TABLE miesiace_mag
(
  id character varying(12) NOT NULL,
  miesiac integer,
  rok integer,
  mag_id integer NOT NULL, -- FK do magazynu
  data date NOT NULL DEFAULT now(), -- Data która przez trigger zostanie podzielona na miesiąc i rok
  CONSTRAINT "PK_miesiace" PRIMARY KEY (id),
  CONSTRAINT "FK_miesiace_mag_id" FOREIGN KEY (mag_id)
      REFERENCES magazyny (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
Na której wyzwalany jest trigger tworzący partycje
CREATE TRIGGER utworz_partycje
  AFTER INSERT OR UPDATE OR DELETE
  ON miesiace_mag
  FOR EACH ROW
  EXECUTE PROCEDURE magmc_modyfikator();
Procedura magmc w zależności czy insert, update czy delete coś tam robi ale w każdym razie podczas insert wywołuje :

CREATE OR REPLACE FUNCTION create_mag_pos(mcid character varying, magnr integer)
  RETURNS boolean AS
$BODY$DECLARE
    tname text:='magazyny.magazyn_inout_' || magnr;
    suffix text:='_doc';
    pname text:='magazyny.magazyn_inout_pos_' || mcid;
BEGIN
    EXECUTE 'CREATE TABLE ' || pname || ' (CHECK (mc_id=''' || mcid || ''')) INHERITS (magazyn_inout_pos_parent);';
    EXECUTE 'CREATE RULE magazyn_inout_pos_' || mcid || '_insert_rule AS ON INSERT to magazyn_inout_pos_parent WHERE (mc_id=''' || mcid || ''' ) DO INSTEAD INSERT INTO ' || pname || ' VALUES (NEW.*);';

    EXECUTE 'ALTER TABLE '||pname||' ADD CONSTRAINT "PK_'||pname||'" PRIMARY KEY ("doc_id", "doc_pos");';
    EXECUTE 'ALTER TABLE '|| pname ||' ADD CONSTRAINT "FK_'||pname||'_docid" FOREIGN KEY (doc_id) REFERENCES '||tname||' (id) ON UPDATE NO ACTION ON DELETE NO ACTION;';
    EXECUTE 'ALTER TABLE '|| pname ||' ADD CONSTRAINT "FK_'||pname||'_mcid" FOREIGN KEY (mc_id) REFERENCES miesiace_mag (id) ON UPDATE NO ACTION ON DELETE NO ACTION;';
    EXECUTE 'ALTER TABLE '|| pname ||' ADD CONSTRAINT "FK_'||pname||'_prodid" FOREIGN KEY (product_id) REFERENCES products (id) ON UPDATE NO ACTION ON DELETE NO ACTION;';

    RETURN TRUE;
END;

W tym wszystkim chodzi o to, żeby partycje i reguły były tworzone automatycznie podczas modyfikacji tabel i to działa. Problem pojawia się natomiast podczas wpisywania danych do tabeli kiedy potrzebuję zwrócić klucz łączony (doc_id, doc_pos);
Niestety INSERTY tworzone w regułach z clauzurą WHERE pozbawione są możliwości zwracania wartości w posgresql-u.
Klucz ten jest też tworzony wg. dość skomplikowanych funkcji i raczej nie interesuje mnie jego zamiana na sekwencje a następnie wybieranie tej wartości funkcjami curentval itp.
Czy istnieje jakiś elegancki sposób tworzenia partycji i pracy na zwracanych po insert wartościach.
Dajcie znać proszę jeśli macie na to jakiś sprytny sposób.

Ostatnio edytowany przez risc (2013-07-03 16:56:00)

2

Odp: Partycje na regułach i zwracana wartość po insert

Ostatnio edytowany przez c_michal (2013-07-03 15:31:58)

3

Odp: Partycje na regułach i zwracana wartość po insert

Ach miał bym to zrobione pare dni temu bo to co piszesz ma ręce i nogi, niestety : BŁĄD:  listy RETURNING nie są obsługiwane w regułach warunkowych Stan SQL: 0A000

4

Odp: Partycje na regułach i zwracana wartość po insert

którą masz wersje postgresa, i załącz skrypt, który Ci nie działa zarówno definicje jak i wywołanie

5

Odp: Partycje na regułach i zwracana wartość po insert

Wersja 9.2.4 w tej chwili na innym kompie 9.1 EnterpriseDB, też nie działa, z tego co wiem 8 też nie będzie działać.
Całe definicje załączone w 1 poście z tym że jeśli miało by to działać w ten sposób to linijka tworząca regułę w funkcji create_mag_pos powinna wyglądać tak :
EXECUTE 'CREATE RULE magazyn_inout_pos_' || mcid || '_insert_rule AS ON INSERT to magazyn_inout_pos_parent WHERE (mc_id=''' || mcid || ''' ) DO INSTEAD INSERT INTO ' || pname || ' VALUES (NEW.*) RETURNING *;';

6

Odp: Partycje na regułach i zwracana wartość po insert

7

Odp: Partycje na regułach i zwracana wartość po insert

Niestety ten przykład nie jest bezpieczny (thread safe). Nie dostane z niego wartości np. kluczy zaraz po insercie a zanim wykonam select wiele może się zmienić. Postanowiłem go trochę zmodyfikować.
1. W regule zamiast instead insert dałem instead select insertfunc ze zwracaną wartością:
CREATE RULE "_INSERT_1" AS ON INSERT to tab WHERE (new.partycja=1) DO INSTEAD (SELECT insert_into_p('tab_i1', NEW.*) );
CREATE RULE "_INSERT_2" AS ON INSERT to tab WHERE (new.partycja=2) DO INSTEAD (SELECT insert_into_p('tab_i2', NEW.*) );
2. Funkcja insert wygląda następująco:
CREATE OR REPLACE FUNCTION insert_into_p(table_name regclass, dane tab)
  RETURNS RECORD AS
$BODY$
DECLARE
result tab;
BEGIN
    EXECUTE 'INSERT INTO '||table_name||' VALUES '|| dane ||' RETURNING *;' INTO result;
    return result;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

Użycie:
insert into tab (partycja,nazwa) values (2,'tab2 rek 1');
Prawie działa - wpisuje dane tam gdzie trzeba niestety zawsze zwraca pusty rekord zamiast ten który zawierał wpisywane dane. W zasadzie zawsze zwraca puste wartości, również gdy zmodyfikuje funkcję tak by zwracała np tylko 1 pole jako ::text.
Dajcie znać jeśli macie jeszcze jakieś pomysły.

Ostatnio edytowany przez risc (2013-07-04 20:11:06)

8

Odp: Partycje na regułach i zwracana wartość po insert

9

Odp: Partycje na regułach i zwracana wartość po insert

10

Odp: Partycje na regułach i zwracana wartość po insert

Jedyne co mi przychodzi do głowy to zrezygnowanie w twojej funkcji z EXECUTE a wykorzystanie INSERT np

if table_name = 'tab_i1' then
  insert into tab_i1 values (dane.*) returning * into result;
elsif table_name = 'tab_i2' then
  insert into tab_i2 values (dane.*) returning * into result;
end if;

niestety to nadal dość kiepskie rozwiązanie

PS (nie mam teraz dostępu do serwera więc sprawdź sobie dokładnie składnie

11

Odp: Partycje na regułach i zwracana wartość po insert