hacking postgresql. Обзор исходного кода
TRANSCRIPT
www.postgrespro.ru
ОБЗОР ИСХОДНОГО КОДА
Hacking PostgreSQL
17.03.2016
2
Содержание
1. Обзор source-tree1. Утилиты и скрипты
2. Backend flowchart
2. Пример
3
postgres/
contrib – модули расширенийdoc – документация (SGML)src – ядро PostgreSQL...src/backend – сервер PostgreSQL ("Back-End")src/bin – psql, pg_dump, initdb, и т.д. ("Front-End")src/common – код, общий для front & backsrc/include – .h файлыsrc/interfaces – libpq, ecpgsrc/pl – процедурные языки (plpgsql, plperl, plpython, tcl)src/port – разные функции для портируемостиsrc/tools – инструменты для разработчиков (pgindent, и т.д.)
4
src/binСерверные утилиты
initdb – инициализация нового кластера баз данных PostgreSQLpg_ctl – инициализация, запуск, останов и управление серверомpg_controldata – вывод свойств, установленных командой initdbpg_upgrade – обновление мажорной версии PostrgeSQLpg_archivecleanup – очистка архивных WAL файловpg_resetxlog – очистка журнала упреждающей записи и другой управляющей информацию кластера PostgreSQLpg_xlogdump – вывод WAL файлов в удобном для чтения форматеpg_test_timing – измерение timing оверхедаpg_rewind – синхронизация директорий PGDATA. Типичный сценарий – восстановить упавший мастер как реплику нового мастера.pg_test_fsync – определяет самый быстрый метод wal_sync_method для конкретной системы
5
src/binКлиентские утилиты
psql – интерактивный терминалpgeventpg_dump – выгрузить базу данных PostgreSQL в формате скрипта в файл или архивpgbench – выполнение бенчмарковpg_basebackup – создать резервную копию кластера PostgreSQLpg_config – информация по конфигурационным параметрам развёрнутого кластера
BINDIR = /home/anastasia/projects/postgresql_bin/binCONFIGURE = 'enabledebug' 'CFLAGS=O0' 'enabledepend' 'enablecassert' 'prefix=/home/anastasia/projects/postgresql_bin/'
6
src/bin/scriptsКлиентские скрипты
clusterdb -- кластеризовать базу данныхcreatedb -- создать базу данных createlang -- установить процедурный язык createuser -- создать новую учётную запись dropdb -- удалить базу данных droplang -- удалить процедурный язык dropuser -- удалить учётную запись пользователя
pg_isready -- проверить соединение с сервером reindexdb -- переиндексировать базу данных vacuumdb -- выполнить очистку и анализ базы данных
7
src/backend
8
src/backend
access - Методы доступа для разных структур данных (heap, btree indexes, gist, gin, brin…).bootstrap - режим первоначальной загрузкиcatalog - определения таблиц системного каталогасommands - DDL и служебные команды (ALTER, CREATE TABLE, VACUUM, ...)executor - выполнение запросовforeign - Foreign Data Wrappers, user mappings, etclib - разные функции “общего назначения”libpq - клиент-серверный протоколmain - основной процесс, передающий управление нужной подсистемеnodes - обобщенная структура “Node” и функции из сравнения, копирования и т.д.optimizer - оптимизатор запросов, реализует систему оценок стоимости и генерирует планparser - лексер и парсер, разбор текста запросовport - специфические функции для портированияpostmaster - основной процесс PostgreSQLregex - Henry Spencer's regex library, also used by TCL, maintained more-or-less by PG nowreplication – поддержка репликации, пересылка WAL-логов, чтение и т.д.rewrite - перезапись запросов с использованием правил (RULE)snowball - Snowball stemming, used with full-text searchstorage - уровень хранилища, ввод/вывод, поддержка LO.tcop - "Traffic Cop"- получение запросов и их обработкаtsearch - полнотекстовый поискutils - различные компоненты сервера, (кэш, управление памятью и т.д.)
9
10
Инициализация
bootstrapСоздание исходных баз через initdb. mainВыполняет первоначальный запуск некоторых подсистем (например error management и memory management), устанавливает информацию о локали. Проверяет различные флаги и передает управление процессу postmaster или postgres. postmasterКонтролирует запуск и остановку сервера postgres. Создает разделяемую память, а затем в цикле ждет запросов на подключение. Сразу после получения такого запроса порождает backend процесс postgres и передает управление ему.libpqBackend операции библиотеки libpq для взаимодействия с процессом клиента.
11
initdb
src/bin/initdb/initdb.c – создание инстанса PostgreSQL.
Создание таблиц template0, template1 и postgres.
template0 – не изменяется после initdb
template1 – может содержать локальные данные
12
bootstrap
BKI – Backend Interface.
src/backend/bootstrap/bootstrap.c – функции для работы в режиме инициализации кластера
pg_class, pg_attributes, pg_proc, pg_type – основные таблицы каталога.
src/include/catalog/genbki.pl – скрипт на Perl, который генерирует файл postgres.bki (и несколько других) из заголовочных файлов особого формата src/include/catalog/pg_*.
src/backend/catalog/postgres.bki – создание таблиц каталога, индексов и TOAST таблиц.
13
src/backend/postmaster
autovacuum.c bgworker.cbgwriter.c checkpointer.cfork_process.cpgarch.cpgstat.cpostmaster.cstartup.c – выполняет запуск сервера и восстановление (recovery). syslogger.cwalwriter.c
14
Глобальные переменные
src/backend/utils/init/globals.c – объявления глобальных переменных
src/backend/utils/init/postinit.c – функции для инициализации
15
Клиент-серверный протокол
src/backend/libpq
● TCP/IP протокол● Документация● Версия 2.0 начиная с 6.4, в 1999● Версия 3.0 начиная с 7.4, в 2003● Новые возможности 3.0:
– расширенный протокол запросов– улучшения COPY
16
Формат сообщений
17
Формат передаваемых данных
src/backend/libpq/pqformat.c
● Бинарный или текстовый форматы● Для разных типов данных разный формат.● Не документирован (кроме кода).● int8send, int8recv● json_send, jsonb_send
18
Простой запрос
19
Асинхронные операции
NoticeResponce – уведомления от сервераParameterStatus – уведомления об изменении параметровNotificationResponse – сообщения, переданные через NOTIFY, если клиент выполняет команду LISTEN
20
Отмена запроса
21
Прерывания
src/include/miscadmin.h
#define CHECK_FOR_INTERRUPTS() \do { \
if (InterruptPending) \ProcessInterrupts(); \
} while(0)
22
Подробнее
● Презентация "Postgres on the wire"● Документация "Клиент-серверный протокол"
23
src/backend/parser
24
src/backend/parser/README
parser.c things start herescan.l break query into tokensscansup.c handle escapes in input stringskwlookup.c turn keywords into specific tokenskeywords.c table of standard keywords (passed to kwlookup.c)gram.y parse the tokens and produce a "raw" parse treeanalyze.c top level of parse analysis for optimizable queriesparse_agg.c handle aggregates, like SUM(col1), AVG(col2), ...parse_clause.c handle clauses like WHERE, ORDER BY, GROUP BY, ...parse_coerce.c handle coercing expressions to different data typesparse_collate.c assign collation information in completed expressionsparse_cte.c handle Common Table Expressions (WITH clauses)parse_expr.c handle expressions like col, col + 3, x = 3 or x = 4parse_func.c handle functions, table.column and column identifiersparse_node.c create nodes for various structuresparse_oper.c handle operators in expressionsparse_param.c handle Params (for the cases used in the core backend)parse_relation.c support routines for tables and column handlingparse_target.c handle the result list of the queryparse_type.c support routines for data type handlingparse_utilcmd.c parse analysis for utility commands (done at execution time)
25
src/backend/parser/scan.l
digit [0-9]integer {digit}+decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*))
{integer} {SET_YYLLOC();return process_integer_literal(yytext, yylval);
}{decimal} {
SET_YYLLOC();yylval->str = pstrdup(yytext);return FCONST;
}
26
src/backend/parser/gram.y
CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'OptInherit OptWith OnCommitOption OptTableSpace
{CreateStmt *n = makeNode(CreateStmt);$4->relpersistence = $2;n->relation = $4;n->tableElts = $6;n->inhRelations = $8;n->ofTypename = NULL;n->constraints = NIL;n->options = $9;n->oncommit = $10;n->tablespacename = $11;n->if_not_exists = false;$$ = (Node *)n;
}
27
DDL vs DML
28
DML
29
src/backend/nodes
30
Пример. CREATE TABLE
CREATE TABLE tbl (a int, b int, PRIMARY KEY (a) INCLUDING (b)
);
31
gram.y
CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'OptInherit OptWith OnCommitOption OptTableSpace
{CreateStmt *n = makeNode(CreateStmt);$4->relpersistence = $2;n->relation = $4;n->tableElts = $6;n->inhRelations = $8;n->ofTypename = NULL;n->constraints = NIL;n->options = $9;n->oncommit = $10;n->tablespacename = $11;n->if_not_exists = false;$$ = (Node *)n;
}
32
gram.y
| PRIMARY KEY '(' columnList ')' opt_c_including opt_definitionOptConsTableSpace ConstraintAttributeSpec
{Constraint *n = makeNode(Constraint);n->contype = CONSTR_PRIMARY;n->location = @1;n->keys = $4;n->including = $6;n->options = $7;n->indexname = NULL;n->indexspace = $8;processCASbits($9, @9, "PRIMARY KEY", &n->deferrable, &n->initdeferred, NULL, NULL, yyscanner);$$ = (Node *)n;
}
opt_c_including: INCLUDING optcincluding { $$ = $2; } | /* EMPTY */ { $$ = NIL; }
;optcincluding : '(' columnList ')' { $$ = $2; }
;
33
src/include/nodes/parsenodes.h
typedef struct Query{NodeTag type;CmdType commandType; /* select|insert|update|delete|utility */QuerySource querySource; /* where did I come from? */uint32 queryId; /* query identifier (can be set by plugins) */bool canSetTag; /* do I set the command result tag? */Node *utilityStmt; /* non-null if this is DECLARE CURSOR or a * non-optimizable statement */int resultRelation; /* rtable index of target relation for * INSERT/UPDATE/DELETE; 0 for SELECT */bool hasAggs; /* has aggregates in tlist or havingQual */bool hasWindowFuncs; /* has window functions in tlist */bool hasSubLinks; /* has subquery SubLink */bool hasDistinctOn; /* distinctClause is from DISTINCT ON */bool hasRecursive; /* WITH RECURSIVE was specified */bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */bool hasRowSecurity; /* row security applied? */
34
Query
List *cteList; /* WITH list (of CommonTableExpr's) */List *rtable; /* list of range table entries */FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */List *targetList; /* target list (of TargetEntry) */OnConflictExpr *onConflict; /* ON CONFLICT DO [NOTHING | UPDATE] */List *returningList; /* return-values list (of TargetEntry) */List *groupClause; /* a list of SortGroupClause's */List *groupingSets; /* a list of GroupingSet's if present */Node *havingQual; /* qualifications applied to groups */List *windowClause; /* a list of WindowClause's */List *distinctClause; /* a list of SortGroupClause's */List *sortClause; /* a list of SortGroupClause's */Node *limitOffset; /* # of result tuples to skip (int8 expr) */Node *limitCount; /* # of result tuples to return (int8 expr) */List *rowMarks; /* a list of RowMarkClause's */Node *setOperations; /* set-operation tree if this is top level of * a UNION/INTERSECT/EXCEPT query */List *constraintDeps; /* a list of pg_constraint OIDs that the query * depends on to be semantically valid */
...} Query;
35
CreateStmt
typedef struct CreateStmt{NodeTag type;RangeVar *relation; /* relation to create */List *tableElts; /* column definitions (list of ColumnDef) */List *inhRelations; /* relations to inherit from (list of * inhRelation) */TypeName *ofTypename; /* OF typename */List *constraints; /* constraints (list of Constraint nodes) */List *options; /* options from WITH clause */OnCommitAction oncommit; /* what do we do at COMMIT? */char *tablespacename; /* table space to use, or NULL */bool if_not_exists; /* just do nothing if it already exists? */} CreateStmt;
36
Constraint
typedef struct Constraint{
NodeTag type;ConstrType contype; /* see above */
/* Fields used for most/all constraint types: */char *conname; /* Constraint name, or NULL if unnamed */bool deferrable; /* DEFERRABLE? */bool initdeferred; /* INITIALLY DEFERRED? */int location; /* token location, or -1 if unknown */
/* Fields used for unique constraints (UNIQUE and PRIMARY KEY): */List *keys; /* String nodes naming referenced key column(s) */List *including; /* String nodes naming referenced nonkey
* column(s) */...
}
37
tcop/utility.c
ProcessUtilitySlow {switch (nodeTag(parsetree)) {case T_CreateStmt:
/* Run parse analysis ... */stmts = transformCreateStmt((CreateStmt *) parsetree,
queryString);foreach(l, stmts) {
if (IsA(stmt, CreateStmt)) { /* Create the table itself */
address = DefineRelation((CreateStmt *) stmt,RELKIND_RELATION, InvalidOid, NULL);
}else {/* * Recurse for anything else. */ProcessUtility(...)
}}
38
parse_utilcmd.c
transformCreateStmt {
transformTableConstraint {switch (constraint→contype){
case CONSTR_PRIMARY:cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);Break;
}}
transformIndexConstraints {foreach(lc, cxt->ixconstraints){
Constraint *constraint = (Constraint *) lfirst(lc);index = transformIndexConstraint(constraint, cxt);indexlist = lappend(indexlist, index);
}}
}
39
parse_utilcmd.c
transformIndexConstraint {foreach(lc, constraint->including){
.../* add it to the index definition*/iparam = makeNode(IndexElem);iparam->name = pstrdup(key);iparam->expr = NULL;iparam->indexcolname = NULL;iparam->collation = NIL;iparam->opclass = NIL;index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
}}
40
indexcmds.c
ObjectAddressDefineIndex(Oid relationId, IndexStmt *stmt, …) {
indexRelationId = index_create(rel, indexRelationName,indexRelationId, stmt→oldNode,indexInfo, indexColNames,accessMethodId, tablespaceId,collationObjectId, classObjectId,coloptions, reloptions, stmt->primary,stmt->isconstraint,stmt->deferrable, stmt->initdeferred,allowSystemTableMods,skip_build || stmt->concurrent,stmt->concurrent, !check_rights,stmt→if_not_exists);
}
41
Продолжение следует...
В следующий раз:– Физическое представление данных– Методы доступа– Внутренняя структура tuple