Помогите разобраться с правилами на изменение VIEW

Доброго времени суток. Задавал эти вопросы в forum.postgresqlrussia.org, но внятного ответа так и не дождался.
Надеюсь, что здесь есть кто-нибудь, кто сталкивался с подобного рода ситуациями.
Проблема в следующем. Есть 4 таблицы:

CREATE TABLE "employees" (
  "emp_uid" SERIAL,
  "emp_fam" TEXT DEFAULT 'н е  и з в е с т н о'::text NOT NULL,
  "emp_name" TEXT DEFAULT 'н е  и з в е с т н о'::text NOT NULL,
  "emp_otch" TEXT DEFAULT 'н е  и з в е с т н о'::text NOT NULL,
  "emp_was_born" DATE,
  "emp_email" TEXT,
  "emp_icq_num" VARCHAR(9),
  "emp_sex" CHAR(1),
  CONSTRAINT "employees_pkey" PRIMARY KEY("emp_uid")
) WITH OIDS;
 
CREATE TABLE "organization" (
  "organization_uid" SERIAL,
  "organization_name" TEXT NOT NULL,
  "organization_www_site" TEXT,
  "organization_email" TEXT,
  "organization_phone" VARCHAR(7),
  "organization_fax" VARCHAR(7),
  CONSTRAINT "organization_pkey" PRIMARY KEY("organization_uid")
) WITH OIDS;
 
CREATE TABLE "employees_post" (
  "post_uid" SERIAL,
  "post_name" TEXT NOT NULL,
  "post_short_name" TEXT,
  CONSTRAINT "employees_post_pkey" PRIMARY KEY("post_uid")
) WITH OIDS;
 
CREATE TABLE "organization_employees" (
  "organization_uid" INTEGER NOT NULL,
  "emp_uid" INTEGER NOT NULL,
  "org_boss" BOOLEAN DEFAULT false NOT NULL,
  "post_uid" INTEGER,
  CONSTRAINT "organization_employees_pkey" PRIMARY KEY("organization_uid", "emp_uid"),
  CONSTRAINT "organization_employees_post_fk" FOREIGN KEY ("post_uid")
    REFERENCES "employees_post"("post_uid")
    ON DELETE CASCADE
    ON UPDATE CASCADE
    NOT DEFERRABLE,
  CONSTRAINT "organization_employees_to_employees_fk" FOREIGN KEY ("emp_uid")
    REFERENCES "employees"("emp_uid")
    ON DELETE CASCADE
    ON UPDATE CASCADE
    NOT DEFERRABLE,
  CONSTRAINT "organization_employees_to_organization_fk" FOREIGN KEY ("organization_uid")
    REFERENCES "organization"("organization_uid")
    ON DELETE CASCADE
    ON UPDATE CASCADE
    NOT DEFERRABLE
) WITH OIDS;

Создаю представление:

CREATE VIEW "view1" (
    emp_uid,
    emp_fam,
    emp_name,
    emp_otch,
    emp_was_born,
    emp_email,
    emp_icq_num,
    emp_sex,
    organization_uid,
    organization_name,
    org_boss,
    post_uid,
    post_name)
AS
SELECT employees.emp_uid, employees.emp_fam, employees.emp_name,
    employees.emp_otch, employees.emp_was_born, employees.emp_email,
    employees.emp_icq_num, employees.emp_sex,
    organization_employees.organization_uid, organization.organization_name,
    organization_employees.org_boss, organization_employees.post_uid,
    employees_post.post_name
FROM employees
LEFT JOIN organization_employees ON employees.emp_uid = organization_employees.emp_uid
LEFT JOIN organization ON organization_employees.organization_uid = organization.organization_uid
LEFT JOIN employees_post ON organization_employees.post_uid = employees_post.post_uid;

Теперь создаю правило на UPDATE:

CREATE RULE "view1_update_rl" AS ON UPDATE TO "view1"
DO INSTEAD (
UPDATE employees SET
(emp_fam,
emp_name,
emp_otch,
emp_was_born,
emp_email,
emp_icq_num,
emp_sex)=
(NEW.emp_fam,
NEW.emp_name,
NEW.emp_otch,
NEW.emp_was_born,
NEW.emp_email,
NEW.emp_icq_num,
NEW.emp_sex)
WHERE emp_uid=NEW.emp_uid
RETURNING employees.*;
UPDATE organization_employees SET
(org_boss,
post_uid)=
(NEW.org_boss,
NEW.post_uid)
WHERE organization_uid=NEW.organization_uid AND emp_uid=NEW.emp_uid
RETURNING organization_employees.*,
(SELECT organization_name FROM organization
WHERE organization.organization_uid=organization_employees.organization_uid),
(SELECT post_name FROM employees_post
WHERE employees_post.post_uid = organization_employees.post_uid);
);

При компиляции выдаёт: ERROR: RETURNING list has too few entries

правило на INSERT:

CREATE RULE "view1_insert_rl" AS ON INSERT TO "view1"
DO INSTEAD (
INSERT INTO employees (emp_uid, emp_fam, emp_name,
    emp_otch, emp_was_born, emp_email,
    emp_icq_num, emp_sex)
VALUES (NEW.emp_uid, NEW.emp_fam, NEW.emp_name,
    NEW.emp_otch, NEW.emp_was_born, NEW.emp_email,
    NEW.emp_icq_num, NEW.emp_sex) RETURNING employees.*;
INSERT INTO organization_employees (organization_uid, emp_uid,
    org_boss, post_uid) VALUES (NEW.organization_uid, NEW.emp_uid,
    NEW.org_boss, NEW.post_uid) RETURNING organization_employees.*,
    (SELECT organization.organization_name FROM organization
    WHERE organization.organization_uid = organization_employees.organization_uid),
    (SELECT employees_post.post_name FROM employees_post
    WHERE employees_post.post_uid = organization_employees.post_uid);
);

Выдаёт: ERROR: RETURNING list has too few entries

Вопрос!!! Что не так? Почему мало вхождений? А главный вопрос - что такое "RETURNING list"?
И где его можно посмотреть или как вычислить. В мануале по этому поводу я ничего не нашёл.

А если правило создаю так:

CREATE RULE "view1_update_rl" AS ON UPDATE TO "view1"
DO INSTEAD (
UPDATE employees SET
(emp_fam,
emp_name,
emp_otc,
emp_was_born,
emp_email,
emp_icq_num,
emp_sex)=
(NEW.emp_fam,
NEW.emp_name,
NEW.emp_otch,
NEW.emp_was_born,
NEW.emp_email,
NEW.emp_icq_num,
NEW.emp_sex)
WHERE emp_uid=NEW.emp_uid
RETURNING employees.emp_fam,
employees.emp_name,
employees.emp_otch,
employees.emp_was_born,
employees.emp_email,
employees.emp_icq_num,
employees.emp_sex;
UPDATE organization_employees SET
(org_boss,
post_uid)=
(NEW.org_boss,
NEW.post_uid)
WHERE organization_uid=NEW.organization_uid AND emp_uid=NEW.emp_uid
RETURNING organization_employees.org_boss,
organization_employees.post_uid,organization_employees.organization_uid,organization_employees.emp_uid,
(SELECT organization_name FROM organization
WHERE organization.organization_uid=organization_employees.organization_uid),
(SELECT post_name FROM employees_post
WHERE employees_post.post_uid = organization_employees.post_uid);
);

то выдаёт: ERROR: RETURNING list's entry 1 has different type from column "emp_uid"

объясните мне пожалуйста, что я делаю не так. А если всё не правильно, то рад буду выслушать предложения по альтернативной реализации задуманного. Надеюсь, что смысл понятен без лишних объяснений.

Сразу оговорюсь, что смысл конструкций WHERE organization_uid=NEW.organization_uid AND emp_uid=NEW.emp_uid вместо WHERE organization_uid=OLD.organization_uid AND emp_uid=OLD.emp_uid в том чтобы "безусловно" выполнить UPDATE на VIEW1, указав столбцы и значения.

По ходу дела возник ещё один вопрос: почему, если вставка или обновление осуществляется через представление, то дефолтные значения полей не подставляются? Вместо этого пытается вставить NULL и на полях с NOT NULL CONSTRAINT, естественно, выдаёт ошибку!

Вот пример (для вышеуказанных таблиц):

CREATE VIEW "public"."employees_view" (
    emp_uid,
    emp_fam,
    emp_name,
    emp_otch,
    emp_was_born,
    emp_email,
    emp_icq_num,
    emp_sex)
AS
SELECT employees.emp_uid, employees.emp_fam, employees.emp_name,
    employees.emp_otch, employees.emp_was_born, employees.emp_email,
    employees.emp_icq_num, employees.emp_sex
FROM employees;
 
CREATE RULE "employees_view_ins" AS ON INSERT TO "public"."employees_view"
DO INSTEAD (
INSERT INTO employees (emp_fam, emp_name, emp_otch, emp_was_born, emp_email, emp_icq_num, emp_sex)
  VALUES (new.emp_fam, new.emp_name, new.emp_otch, new.emp_was_born, new.emp_email, new.emp_icq_num, new.emp_sex)
  RETURNING employees.emp_uid, employees.emp_fam, employees.emp_name, employees.emp_otch, employees.emp_was_born, employees.emp_email, employees.emp_icq_num, employees.emp_sex;
);

Теперь:

INSERT INTO public.employees (emp_fam,emp_name,emp_otch)
VALUES ('Иванов',DEFAULT,DEFAULT),
(DEFAULT,DEFAULT,DEFAULT)
RETURNING *;

emp_uid | emp_fam | emp_name | emp_otch | emp_was_born | emp_email | emp_icq_num | emp_sex
5 | Иванов | н е и з в е с т н о | н е и з в е с т н о | null | null | null | null
6 | н е и з в е с т н о | н е и з в е с т н о | н е и з в е с т н о | null | null | null | null

Пробуем тоже самое на представлении:

INSERT INTO public.employees_view (emp_fam,emp_name,emp_otch)
VALUES ('Иванов',DEFAULT,DEFAULT),
(DEFAULT,DEFAULT,DEFAULT)
RETURNING *;

ERROR: null value in column "emp_name" violates not-null constraint

Объясните мне пожалуйста, почему так? Что происходит в системе правила. Что-то я вообще не догоняю как с ней работать. А ведь, судя по мануалу, это очень полезная и мощная вещь. Хотелось бы овладеть ею в совершенстве. Может это всё недоработки или ошибки разработчиков.
Использую версию 8.3.3 под windows. Буду очень признателен за доходчивые объяснения.

Опции просмотра комментариев

Выберите предпочитаемый вами способ показа комментариев и нажмите "Сохранить настройки" для активации изменений.

с первым вашим

с первым вашим вопросом все элементарно:
ERROR: RETURNING list has too few entries - список в предложении RETURNING имеет слишком мало полей.
Посмотрите сами - ваше представление имеет 13 полей, а вы возвращаете 6 - из второго update.
ошибку RETURNING list's entry 1 has different type from column "emp_uid" вы, наверное, уже поняли сами.

По поводу дефолта: на сколько я понимаю документацию, дефолтные значения подставляются только в случае отсутствия поля в инструкции на вставку. У вас фактическая вставка происходит в правиле и там присутствуют все поля, поэтому подставляется NULL. Здесь можно выкрутиться триггером.

msr, спасибо за ответ. Разобрался.

Подведу итог на счет правил на INSERT, UPDATE и DELETE, поскольку в мануале этот вопрос почти не освещён, а кому-то это может пригодиться.
Итак, для определения правила на изменение представления (VIEW), объединяющего несколько таблиц, секция RETURNING определяется в завершающей команде (действии) правила и должна содержать ВСЕ поля определённые для этого представления и ИМЕННО В ТОМ ПОРЯДКЕ, в котором они ПЕРЕЧИСЛЕНЫ ДЛЯ ПРЕДСТАВЛЕНИЯ.
Таким образом для примеров, приведенных выше это будет:

CREATE OR REPLACE VIEW "public"."view1" (
    emp_uid,
    emp_fam,
    emp_name,
    emp_otch,
    emp_was_born,
    emp_email,
    emp_icq_num,
    emp_sex,
    organization_uid,
    organization_name,
    org_boss,
    post_uid,
    post_name)
AS
SELECT employees.emp_uid, employees.emp_fam, employees.emp_name,
    employees.emp_otch, employees.emp_was_born, employees.emp_email,
    employees.emp_icq_num, employees.emp_sex,
    organization_employees.organization_uid, organization.organization_name,
    organization_employees.org_boss, organization_employees.post_uid,
    employees_post.post_name
FROM employees 
LEFT JOIN organization_employees ON employees.emp_uid = organization_employees.emp_uid 
LEFT JOIN organization ON organization_employees.organization_uid = organization.organization_uid 
LEFT JOIN employees_post ON organization_employees.post_uid = employees_post.post_uid;
 
CREATE RULE "view1_insert_rl" AS ON INSERT TO "public"."view1" 
DO INSTEAD (
INSERT INTO employees (emp_fam, emp_name, emp_otch, emp_was_born, emp_email, emp_icq_num, emp_sex) 
 VALUES (new.emp_fam, new.emp_name, new.emp_otch, new.emp_was_born, new.emp_email, new.emp_icq_num, new.emp_sex);
 INSERT INTO organization_employees (org_boss, post_uid, organization_uid, emp_uid) 
 VALUES (new.org_boss, new.post_uid, new.organization_uid, new.emp_uid)
  RETURNING 
  (SELECT employees.emp_uid FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
  (SELECT employees.emp_fam FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
  (SELECT employees.emp_name FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
  (SELECT employees.emp_otch FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
  (SELECT employees.emp_was_born FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
  (SELECT employees.emp_email FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
  (SELECT employees.emp_icq_num FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
  (SELECT employees.emp_sex FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
  organization_employees.organization_uid, 
  (SELECT organization.organization_name FROM organization WHERE organization.organization_uid = organization_employees.organization_uid), 
  organization_employees.org_boss, 
  organization_employees.post_uid, 
  (SELECT employees_post.post_name FROM employees_post WHERE employees_post.post_uid = organization_employees.post_uid);
);
 
CREATE RULE "view1_update_rl" AS ON UPDATE TO "public"."view1" 
DO INSTEAD (
 UPDATE employees SET 
emp_fam = new.emp_fam, 
emp_name = new.emp_name, 
emp_otch = new.emp_otch, 
emp_was_born = new.emp_was_born, 
emp_email = new.emp_email, 
emp_icq_num = new.emp_icq_num, 
emp_sex = new.emp_sex
 WHERE employees.emp_uid = new.emp_uid;
 UPDATE organization_employees SET 
org_boss = new.org_boss, 
post_uid = new.post_uid
WHERE organization_employees.organization_uid = new.organization_uid AND organization_employees.emp_uid = new.emp_uid
RETURNING 
(SELECT employees.emp_uid FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
(SELECT employees.emp_fam FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
(SELECT employees.emp_name FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
(SELECT employees.emp_otch FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
(SELECT employees.emp_was_born FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
(SELECT employees.emp_email FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
(SELECT employees.emp_icq_num FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), 
(SELECT employees.emp_sex FROM employees WHERE employees.emp_uid = organization_employees.emp_uid), organization_employees.organization_uid, 
(SELECT organization.organization_name FROM organization WHERE organization.organization_uid = organization_employees.organization_uid), 
organization_employees.org_boss, 
organization_employees.post_uid, 
(SELECT employees_post.post_name FROM employees_post WHERE employees_post.post_uid = organization_employees.post_uid);
);

Именно в таком порядке всё работает нормально.

А вот по поводу ДЕФОЛТНЫХ значений позволю себе усомниться в предположениях msr'а. В мануале сказано:
5.2. Default Values
A column can be assigned a default value. When a new row is created and no values are specified for some of the columns, those columns will be filled with their respective default values. A data manipulation command can also request explicitly that a column be set to its default value, without having to know what that value is.

Т.е. можно явно задать значение DEFAULT в команде и для указанной колонки оно должно быть подставлено в соответствии со значением определённым для поля по умолчанию. А у меня именно такой случай. Я указываю для вставки или обновления значения DEFAULT, но после обработки в правилах представления, они почему-то становятся NULL.
Буду рад услышать ваши мысли по этому поводу. Или ткните носом в мануал :-) в то место где про это сказано.
Заранее благодарен.

Проблема с DEFAULT для VIEW решена.

Помог gold с форума forum.postgresqlrussia.org. Вот как:

Чтобы сделать дефаулт значение для вьюшки:
после создание вьюшки воспользуемся командой
ALTER TABLE name_view ALTER COLUMN name_col SET DEFAULT ...

Этот способ успешно работает, я сам проверил. Всем спасибо, тема закрыта.

Это я не

Это я не знал.
Самое странное, это значение можно увидеть только в pgAdmin в свойствах конкретного поля представления, другие программы ни под каким видом не отображают это значение.

Опции просмотра комментариев

Выберите предпочитаемый вами способ показа комментариев и нажмите "Сохранить настройки" для активации изменений.

Back to top

(С) Виктор Вислобоков, 2008-2023