Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

DML — Data Manipulation Language

Goal

Справочник поддерживаемых DML-операций и поведение mutation policy.

INSERT

INSERT INTO t (id, v) VALUES (1, 100);
INSERT INTO t (id, v) VALUES (2, 200), (3, 300);

Для секционированных таблиц INSERT в родительскую таблицу автоматически маршрутизирует строку в подходящую партицию. Если подходящая партиция (и DEFAULT) отсутствует — 23514 check_violation.

INSERT … SELECT

RM-0.6.3.7 (RFC-2026-497, S8): источник INSERT — произвольный SELECT-запрос. Колонки результата сопоставляются с целевыми по позиции (или по явному списку колонок); SERIAL/IDENTITY/DEFAULT резолвятся для каждой строки независимо.

INSERT INTO archive (id, v) SELECT id, v FROM t WHERE created_at < '2025-01-01';
INSERT INTO log (note) SELECT 'rebuilt:' || name FROM rebuilt_items;

INSERT … ON CONFLICT (UPSERT)

RM-0.6.3.7 (RFC-2026-497, S9): поддерживается одно-колоночный конфликтный target (или PK по умолчанию). EXCLUDED.<col> ссылается на предложенную строку.

-- DO NOTHING (раннее поведение, теперь принимает явный target):
INSERT INTO t (id, v) VALUES (1, 100) ON CONFLICT (id) DO NOTHING;

-- DO UPDATE SET ... [WHERE ...]
INSERT INTO counters (key, n)
VALUES ('hits', 1)
ON CONFLICT (key) DO UPDATE SET n = counters.n + EXCLUDED.n;

-- WHERE-фильтр над существующей строкой (PG-семантика: конфликт + WHERE-false = silent no-op):
INSERT INTO inventory (sku, qty)
VALUES ('A', 5)
ON CONFLICT (sku) DO UPDATE SET qty = EXCLUDED.qty
WHERE inventory.qty < EXCLUDED.qty;

Не поддерживается:

  • мультиколоночный target (ON CONFLICT (a, b)) → 0A000;
  • ON CONFLICT ON CONSTRAINT <name>0A000.

UPDATE

UPDATE t SET v = 999 WHERE id = 1;

Начиная с RM-0.6.5.5, UPDATE SET поддерживает функциональные выражения и приведение типов:

  • UPDATE t SET write_date = NOW() WHERE id = 1;
  • UPDATE t SET ts = '2026-05-07 14:30:00'::timestamp WHERE id = 1;
  • UPDATE t SET day = date_trunc('day', NOW()) WHERE id = 1;

Поддерживаются NOW(), CURRENT_TIMESTAMP, CURRENT_DATE, date_trunc() и явные CAST (синтаксис ::).

DELETE

DELETE FROM t WHERE id = 2;

RETURNING

RM-0.6.3.7 (RFC-2026-497, S10): RETURNING поддержан для multi-row INSERT, UPDATE, DELETE. В проекции допустимы *, явный список колонок и выражения. Для INSERT ... ON CONFLICT DO UPDATE возвращается пост-апдейт строка (как в PostgreSQL).

INSERT INTO t (v) VALUES (100), (200), (300) RETURNING id, v;
UPDATE t SET v = v + 1 WHERE v > 0 RETURNING id, v AS new_v;
DELETE FROM t WHERE v IS NULL RETURNING id;

Sequence functions

RM-0.6.3.7 (RFC-2026-497): nextval / currval / setval для объектов SEQUENCE (см. ddl.md — раздел «CREATE / ALTER / DROP SEQUENCE»). Не транзакционные: gap-on-rollback корректное поведение.

SELECT nextval('s1');                  -- 1, 2, 3, ...; overflow без CYCLE → 2200H
SELECT currval('s1');                  -- последнее значение, выданное в ЭТОЙ сессии
SELECT setval('s1', 100);              -- last_value=100, is_called=true; следующий nextval = 101
SELECT setval('s1', 50, false);        -- следующий nextval = 50

currvalsession-bound: возвращает значение последнего nextval (или setval(_, _, true)) в той же pgwire-сессии. Если в текущей сессии nextval ещё не вызывался — 55000 object_not_in_prerequisite_state, независимо от того, что nextval был вызван в других сессиях.

SERIAL / IDENTITY

SERIAL / BIGSERIAL и GENERATED [ALWAYS|BY DEFAULT] AS IDENTITY автоматически создают owned-последовательность <table>_<col>_seq, которая удаляется вместе с таблицей.

CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT);
INSERT INTO users (name) VALUES ('alice'), ('bob') RETURNING id;
-- id заполняется через nextval('users_id_seq')

CREATE TABLE orders (
  id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  amount NUMERIC
);
INSERT INTO orders (id, amount) VALUES (42, 100);
-- → 428C9 generated_always: id колонка GENERATED ALWAYS

CREATE TABLE invoices (
  id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  total NUMERIC
);
INSERT INTO invoices (id, total) VALUES (DEFAULT, 250) RETURNING id;
INSERT INTO invoices (id, total) VALUES (1000, 250);     -- explicit OK

SELECT

SELECT id, v FROM t WHERE v > 50 ORDER BY id;

Подробно о запросах (CTE, JOIN, GROUP BY и т.д.) — см. queries.md.

TRUNCATE

TRUNCATE TABLE t;

TRUNCATE — DDL-сброс таблицы. Для append-only таблиц это единственный способ удалить данные.

Mutation policy enforcement

Mutation policy контролирует допустимые DML-операции на таблице.

append_only

ОперацияРезультат
INSERTРазрешён
UPDATEОтклонён — 42809 wrong_object_type
DELETEОтклонён — 42809 wrong_object_type
TRUNCATEРазрешён (DDL reset)

no_delete

ОперацияРезультат
INSERTРазрешён
UPDATE (non-PK, non-FK columns)Разрешён
UPDATE PK column (id)Отклонён — 42809 wrong_object_type
UPDATE FK child columnОтклонён — 42809 wrong_object_type
DELETEОтклонён — 42809 wrong_object_type
TRUNCATEОтклонён — 42809 wrong_object_type

unrestricted

Все DML-операции разрешены (поведение по умолчанию).

Autocommit vs explicit transactions

По умолчанию каждый DML-запрос выполняется в autocommit-режиме (неявная транзакция, фиксируется сразу).

Для явных транзакций:

BEGIN;
INSERT INTO t (id, v) VALUES (10, 1000);
UPDATE t SET v = 2000 WHERE id = 10;
COMMIT;

ROLLBACK откатывает все изменения текущей транзакции.

Expected SQLSTATE

СитуацияSQLSTATE
UPDATE/DELETE on append-only table42809
DELETE/TRUNCATE under no_delete42809
PK/FK UPDATE under no_delete42809
Insert into partitioned table, no matching partition23514
nextval overflow (без CYCLE)2200H
currval до первого nextval в этой сессии55000
setval value вне [MINVALUE..MAXVALUE]22023
Несуществующая sequence (nextval/currval/setval/DROP SEQUENCE без IF EXISTS)42P01
INSERT в GENERATED ALWAYS AS IDENTITY колонку с явным non-NULL428C9
INSERT ... ON CONFLICT (a, b) (multi-column target)0A000
INSERT ... ON CONFLICT ON CONSTRAINT <name>0A000