Продолжаю читать книгу «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