Продолжаю читать книгу «PostgreSQL Изнутри».
Начинаю Часть I «Изоляция и многоверсионность».
Глава 2 «Изоляция».
На этот раз вообще всего-лишь один небольшой раздел:
- 2.1. Согласованность
В этом разделе описаны два важных понятия:
- Согласованность (consistency)
- Ограничения целостности (integrity constraints)
- Согласованность строже чем целостность
- Приложение может нарушить согласованность, не нарушая целостности, у СУБД нет способа узнать об этом.
Но это не значит что можно забить на целостность. Тут я сразу отправлю в пару источников. Например, можно почитать SQL стандарт. В стандарте 2023 года есть раздел 4.25 Integrity constraints. Там много и подробно и на английском языке.
Но можно сделать проще — открыть книгу Евгений Павловича Моргунова PostgreSQL. Основы языка SQL, и там — раздел 5.1. Значения по умолчанию и ограничения целостности. Там понятно много расписано. Я ниже только основную информацию оттуда покажу, но со своими примерами:
- Значения по умолчанию DEFAULT
- Ограничение CHECK
- Ограничение NOT NULL
- Ограничение уникальности UNIQUE
- Первичный ключ PRIMARY KEY
- Внешний ключ FOREIGN KEY
Почти все эти ограничения можно описать двумя способами — либо на уровне столбца, либо на уровне таблицы. Еще можно указывать названия ограничений, можно не указывать.
На уровне столбцов:
CREATE TABLE for_constraints (id integer CONSTRAINT pk_for_constraints PRIMARY KEY, mark numeric (1) CONSTRAINT def_fc DEFAULT 5, CONSTRAINT check_mark CHECK (mark >= 3 AND mark <= 5), book_num numeric (5) CONSTRAINT unique_book_num UNIQUE NOT NULL );
На уровне таблицы:
CREATE TABLE for_constraints2 (id integer, mark numeric(1) DEFAULT 5, book_num numeric (5) NOT NULL, CONSTRAINT pk_for_constraints2 PRIMARY KEY (id), CONSTRAINT check_mark2 CHECK (mark >= 3 AND mark <= 5), CONSTRAINT unique_book_num2 UNIQUE (book_num) );
Внешний ключ можно описать вот так:
CREATE TABLE for_fk (id_fk integer REFERENCES for_constraints (id));
Либо так:
CREATE TABLE for_fk2 (id_fk integer, CONSTRAINT fk_for_constraints2 FOREIGN KEY (id_fk) REFERENCES for_constraints2 );
-- Сразу подготовка - посмотрим ограничения и индексы с помощью подготовленного оператора, понадобится чуть позже
PREPARE constrs (text) as
SELECT pgc.conname AS constraint_name,
ccu.column_name,
CASE WHEN contype = 'c' THEN 'check'
WHEN contype = 'f' THEN 'foreign key'
WHEN contype = 'p' THEN 'primary key'
WHEN contype = 'u' THEN 'unique'
WHEN contype = 't' THEN 'trigger'
WHEN contype = 'x' THEN 'exclusion'
END constraint,
pg_get_constraintdef(pgc.oid)
FROM pg_constraint pgc
JOIN pg_namespace nsp ON nsp.oid = pgc.connamespace
JOIN pg_class cls ON pgc.conrelid = cls.oid
left JOIN information_schema.constraint_column_usage ccu
ON pgc.conname = ccu.constraint_name
AND nsp.nspname = ccu.constraint_schema
WHERE table_name = $1
union all
SELECT 'null_not_null' constraint_name, attname column_name,
CASE WHEN attnotnull = 't' THEN 'not null' ELSE 'null' END constraint,
CASE WHEN attnotnull = 't' THEN 'NOT NULL' ELSE 'NULL' END pg_get_constraintdef
FROM pg_attribute
WHERE attrelid = $1::regclass
AND attname NOT IN ('tableoid', 'cmax', 'cmin', 'xmax','xmin', 'ctid');
-- Ограничения на уровне столбцов
CREATE TABLE for_constraints (id integer CONSTRAINT pk_for_constraints PRIMARY KEY,
mark numeric (1) CONSTRAINT def_fc DEFAULT 5,
CONSTRAINT check_mark CHECK (mark >= 3 AND mark <= 5),
book_num numeric (5) CONSTRAINT unique_book_num UNIQUE NOT NULL
);
-- PRIMARY KEY
INSERT INTO for_constraints VALUES (1,5,1);
INSERT INTO for_constraints VALUES (1,5,2);
-- DEFAULT
SELECT * FROM for_constraints;
-- CHECK
INSERT INTO for_constraints VALUES (2, 4, 2);
INSERT INTO for_constraints VALUES (3, 8, 3);
-- UNIQUE
INSERT INTO for_constraints VALUES (4, 4, 2);
-- FOREIGN KEY
CREATE TABLE for_fk (id_fk integer REFERENCES for_constraints (id));
INSERT INTO for_fk VALUES (1);
INSERT INTO for_fk VALUES (11);
-- Посмотрим ограничения и индексы
EXECUTE constrs ('for_constraints');
-- Другой вариант - ограничения на уровне таблицы
CREATE TABLE for_constraints2 (id integer,
mark numeric(1) DEFAULT 5,
book_num numeric (5) NOT NULL,
CONSTRAINT pk_for_constraints2 PRIMARY KEY (id),
CONSTRAINT check_mark2 CHECK (mark >= 3 AND mark <= 5),
CONSTRAINT unique_book_num2 UNIQUE (book_num)
);
-- PRIMARY KEY
INSERT INTO for_constraints2 VALUES (1,5,1);
INSERT INTO for_constraints2 VALUES (1,5,2);
-- DEFAULT
INSERT INTO for_constraints2 (id, book_num) VALUES (1,1);
SELECT * FROM for_constraints2;
-- CHECK
INSERT INTO for_constraints2 VALUES (2, 4, 2);
INSERT INTO for_constraints2 VALUES (3, 8, 3);
-- UNIQUE
INSERT INTO for_constraints2 VALUES (4, 4, 2);
-- FOREIGN KEY
CREATE TABLE for_fk2 (id_fk integer,
CONSTRAINT fk_for_constraints2 FOREIGN KEY (id_fk) REFERENCES for_constraints2
);
INSERT INTO for_fk2 VALUES (1);
INSERT INTO for_fk2 VALUES (11);
-- Посмотрим ограничения и индексы
EXECUTE constrs ('for_constraints2');
-- Очистка лишнего
DROP TABLE for_constraints CASCADE;
DROP TABLE for_constraints2 CASCADE;
DROP TABLE for_fk CASCADE;
DROP TABLE for_fk2 CASCADE;
DEALLOCATE constrs;
Ну и немного исследования исходного кода:
- htup_details.h, и там HeapTupleFields
- heapam_visibility.c — состояние строк и их видимость, в т.ч. простановка хинтбитов
- procarray.c — и там, в т.ч. GetSnapshotData
И про транзакции. Вспоминая студенческие времена — тогда я долго не мог понять что такое транзакция, даже читая учебник. Но ведь вроде всё понятно и логично написано. Так что просто продублирую книгу:
Таким образом, транзакцией называется множество операций, которые переводят базу данных из одного корректного состояния в другое корректное состояние (согласованность) при условии, что транзакция выполнена полностью (атомарность) и без помех со стороны других транзакций (изоляция). Это определение объединяет требования, стоящие за первыми тремя буквами акронима ACID: Atomicity, Consistency, Isolation.
Для начала можно запомнить что транзакция либо выполняется полностью, либо не выполняется ни один из операторов, входящих в неё.
Транзакция открывается словом BEGIN, заверить её нужно COMMIT, ROLLBACK либо END. Но помните, что в программах, с помощью которых вы работаете с PostgreSQL может быть активировать режим автокоммита.
BEGIN; CREATE TABLE t_tmp (id integer); SELECT * FROM pg_tables where tablename = 't_tmp'; ROLLBACK; SELECT * FROM pg_tables where tablename = 't_tmp';
Про разные виды транзакций далее будет рассказано подробнее.
Leave a Reply