Расширения для PostgreSQL

57
www.postgrespro.ru РАСШИРЕНИЯ ДЛЯ POSTGRESQL Hacking PostgreSQL 10.03.2016

Transcript of Расширения для PostgreSQL

Page 1: Расширения для PostgreSQL

www.postgrespro.ru

РАСШИРЕНИЯ ДЛЯ POSTGRESQL

Hacking PostgreSQL

10.03.2016

Page 2: Расширения для PostgreSQL

2

Содержание

1. Расширение (contrib)

2. Новый тип данных

3. SPI (Server Programming Interface)

4. Особенности функций на Си

5. SRF (Set-Returning Function)

Page 3: Расширения для PostgreSQL

3

Расширение (contrib)

● Инфраструктура сборки PGXS● PGXN (PostgreSQL Extension Network)● PgFoundry● Управляется каталогом pg_extension

SELECT * FROM pg_extension;-[ RECORD 1 ]--+--------extname | plpgsqlextowner | 10extnamespace | 11extrelocatable | fextversion | 1.0extconfig | extcondition |

Page 4: Расширения для PostgreSQL

4

Управление расширением

CREATE EXTENSION [ IF NOT EXISTS ] extension_name

[ WITH ] [ SCHEMA schema_name ]

[ VERSION version ]

[ FROM old_version ]

DROP EXTENSION [ IF EXISTS ] extension_name [, ...] [ CASCADE | RESTRICT ]

ALTER EXTENSION extension_name UPDATE [ TO new_version ]

ALTER EXTENSION extension_name SET SCHEMA new_schema

ALTER EXTENSION extension_name ADD member_object

ALTER EXTENSION extension_name DROP member_object

Page 5: Расширения для PostgreSQL

5

Объекты в расширении

AGGREGATE agg_name (agg_type [, ...] ) |

CAST (source_type AS target_type) |

COLLATION object_name |

CONVERSION object_name |

DOMAIN object_name |

FOREIGN DATA WRAPPER object_name |

FOREIGN TABLE object_name |

FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) |

OPERATOR operator_name (left_type, right_type) |

OPERATOR CLASS object_name USING index_method |

OPERATOR FAMILY object_name USING index_method |

Page 6: Расширения для PostgreSQL

6

Объекты в расширении

[ PROCEDURAL ] LANGUAGE object_name |

SCHEMA object_name |

SEQUENCE object_name |

SERVER object_name |

TABLE object_name |

TEXT SEARCH CONFIGURATION object_name |

TEXT SEARCH DICTIONARY object_name |

TEXT SEARCH PARSER object_name |

TEXT SEARCH TEMPLATE object_name |

TYPE object_name |

VIEW object_name

Page 7: Расширения для PostgreSQL

7

Из чего состоит расширение?

pg_example.control – управляющий файл

pg_example.c – исходный текст на C

Makefile – файл для сборки с помощью GNU make

pg_example--1.0.sql – SQL-скрипт, создающий объекты БД

sql/pg_example.sql – регрессионные тесты

data/example.data – данные для регрессионных тестов

expected/pg_example.out – ожидаемые результаты

README.txt

Page 8: Расширения для PostgreSQL

8

pg_example.control

# pg_example extention

comment = 'my first extension'

default_version = '1.0'

module_pathname = '$libdir/pg_example'

relocatable = true

directory #Каталог, содержащий файл(ы) SQL скриптов

encoding #Кодировка набора символов, используемая файлами скриптов

requires #Расширения, от которых зависит данное расширение

superuser #Нужны ли права суперпользователя для установки/обновления расширения

schema #Схема, в которую должно быть загружено расширение. Только для не relocatable

Page 9: Расширения для PostgreSQL

9

Makefile

# pg_example/Makefile

# Название собираемого .so файла

MODULE_big = pg_example

# Список .o файлов, из которых собирается .so

OBJS = pg_example.o

# Название расширения

EXTENSION = pg_example

DATA = pg_example—1.0.sql

# Список тестов

REGRESS = pg_example

Page 10: Расширения для PostgreSQL

10

Makefile (2)

ifdef USE_PGXS

# Если USE_PGXS установлено, то нужные для сборки файлы# PostgreSQL находятся с помощью утилиты pg_config

PG_CONFIG = pg_config

PGXS := $( shell $( PG_CONFIG ) --pgxs )

include $(PGXS)

else

# Если нет, то считается что расширение помещено в папку# contrib исходников PostgreSQL

subdir = contrib/pg_example

top_builddir = ../..

include $(top_builddir)/src/Makefile.global

include $(top_srcdir)/contrib/contrib-global.mk

endif

Page 11: Расширения для PostgreSQL

11

Содержание

1. Расширение (contrib)

2. Новый тип данных

3. SPI (Server Programming Interface)

4. Особенности функций на Си

5. SRF (Set-Returning Function)

Page 12: Расширения для PostgreSQL

12

pair

Простейшая реализация типа данных пара key/value

/* pg_example--1.0.sql */

CREATE TYPE pair AS ( k text, v text );

CREATE OR REPLACE FUNCTION pair(anyelement, anyelement)

RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';

CREATE OPERATOR ~> (

LEFTARG = anyelement,

RIGHTARG = anyelement,

PROCEDURE = pair

);

Page 13: Расширения для PostgreSQL

13

pair

CREATE EXTENSION pg_example;

CREATE TABLE keyvalue(kv pair);

INSERT INTO keyvalue VALUES ('key'::text~>'value'::text), ('foo'::text~>'bar'::text);

SELECT * FROM keyvalue;

SELECT (kv).k, (kv).v FROM keyvalue ORDER BY (kv).k;

kv ------------- (key,value) (foo,bar)

k | v -----+------- foo | bar key | value

Page 14: Расширения для PostgreSQL

14

contrib/hstore

Реализация key/value типа данных.

Предшественник json.

Документация модуля hstore

Презентация про hstore.

Page 15: Расширения для PostgreSQL

15

CREATE TYPE

CREATE TYPE hstore ( INTERNALLENGTH = -1, INPUT = hstore_in, OUTPUT = hstore_out, RECEIVE = hstore_recv, SEND = hstore_send, STORAGE = extended);

pg_type---------------+------------typname | hstoretypnamespace | 2200typowner | 16384typlen | -1typbyval | ftypinput | hstore_intypoutput | hstore_outtypreceive | hstore_recvtypsend | hstore_sendtypstorage | x (extended).......

Page 16: Расширения для PostgreSQL

16

hstore_in

CREATE FUNCTION hstore_in(cstring)RETURNS hstoreAS '$libdir/hstore','hstore_in'LANGUAGE C STRICTIMMUTABLE;

pg_proc----------------+---------------proname | hstore_inpronamespace | 2200proowner | 16384prolang | 13procost | 1prorows | 0provariadic | 0prorettype | 16385 (hstore)proargtypes | 2275 (cstring)prosrc | hstore_inprobin | $libdir/hstore.......

Page 17: Расширения для PostgreSQL

17

Новый оператор

CREATE OPERATOR -> ( LEFTARG = hstore, RIGHTARG = text, PROCEDURE = fetchval);

pg_operator-------------+------------oprname | ->oprnamespace | 2200oprowner | 16384oprkind | boprleft | 16385 (hstore)Oprright | 25 (text)oprresult | 25oprcom | 0oprnegate | 0oprcode | fetchval

Page 18: Расширения для PostgreSQL

18

Функция для оператора

CREATE FUNCTION fetchval(hstore,text)RETURNS textAS '$libdir/hstore','hstore_fetchval'LANGUAGE C STRICT IMMUTABLE;

pg_proc----------------+----------------proname | fetchvalpronamespace | 2200proowner | 16384prolang | 13procost | 1prosrc | hstore_fetchvalprobin | $libdir/hstoreproconfig | proacl | .......

Page 19: Расширения для PostgreSQL

19

Индексная поддержка

CREATE OPERATOR CLASS gist_hstore_opsDEFAULT FOR TYPE hstore USING gistAS

OPERATOR 7 @> ,OPERATOR 9 ?(hstore,text) ,OPERATOR 10 ?|(hstore,text[]) ,OPERATOR 11 ?&(hstore,text[]) ,OPERATOR 13 @ ,FUNCTION 1 ghstore_consistent (internal, hstore,

smallint, oid, internal),FUNCTION 2 ghstore_union (internal, internal),FUNCTION 3 ghstore_compress (internal),FUNCTION 4 ghstore_decompress (internal),FUNCTION 5 ghstore_penalty (internal, internal, internal),FUNCTION 6 ghstore_picksplit (internal, internal),FUNCTION 7 ghstore_same (ghstore, ghstore, internal),STORAGE ghstore;

Page 20: Расширения для PostgreSQL

20

Ещё примеры

● math3d● IMCS (In-Memory Columnar Store)

Page 21: Расширения для PostgreSQL

21

Содержание

1. Расширение (contrib)

2. Новый тип данных

3. SPI (Server Programming Interface)

4. Особенности функций на Си

5. SRF (Set-Returning Function)

Page 22: Расширения для PostgreSQL

22

SPI. Функции

SPI - Server Programming Interface● Процедурные языки

(PL/pgSQL, PL/python, PL/perl, PL/R ...)● API для расширений (pg_paxos api)● Триггеры (contrib/spi)

Page 23: Расширения для PostgreSQL

23

SPI. Функции

● SPI_connect / SPI_finish● SPI_push / SPI_pop● SPI_execute / SPI_exec● SPI_prepare● SPI_execute_plan● SPI_keepplan / SPI_saveplan

Page 24: Расширения для PostgreSQL

24

SPI. Функции (2)

● SPI_gettype / SPI_gettypeid● SPI_getrelname / SPI_getnspnam● SPI_copytuple● SPI_returntuple● SPI_modifytuple

Page 25: Расширения для PostgreSQL

25

SPI. Функции (3)

● SPI_palloc● SPI_repalloc● SPI_pfree

Page 26: Расширения для PostgreSQL

26

SPI. Timetravel

timetravel ('date_on', 'date_off' [,'insert_user', 'update_user',

'delete_user' ] )

set_timetravel(relname, on)

get_timetravel(relename)

Page 27: Расширения для PostgreSQL

27

SPI. Timetravel

CREATE EXTENSION timetravel;

CREATE TABLE tttest(

price_id int4, price_val int4,

price_on abstime, price_off abstime);

CREATE UNIQUE INDEX tttest_idx ON tttest (price_id,price_off);

CREATE TRIGGER timetravel

BEFORE INSERT OR DELETE OR UPDATE ON tttest

FOR EACH ROW EXECUTE PROCEDURE timetravel (price_on, price_off);

Page 28: Расширения для PostgreSQL

28

SPI. Timetravel. INSERT

INSERT INTO tttest VALUES (1, 1, NULL, NULL);

INSERT INTO tttest(price_id, price_val)VALUES (2, 2);

INSERT INTO tttest(price_id, price_val, price_off) VALUES (3, 3, 'infinity');

price_id | price_val | price_on | price_off ---------+-----------+------------------------+----------- 1 | 1 | 2016-03-07 17:59:46+03 | infinity 2 | 2 | 2016-03-07 18:00:09+03 | infinity 3 | 3 | 2016-03-07 18:00:32+03 | infinity

Page 29: Расширения для PostgreSQL

29

SPI. Timetravel. INSERT

if (TRIGGER_FIRED_BY_INSERT(trigdata→tg_event)) isinsert = true;

if (isinsert) {oldtimeon = SPI_getbinval(trigtuple,

tupdesc, attnum[a_time_on], &isnull);

if (isnull)

/* Установить в a_time_on * значение GetCurrentAbsoluteTime */

rettuple = SPI_modifytuple(rel, trigtuple, chnattrs, chattrs, newvals, newnulls);

return PointerGetDatum(rettuple);

Page 30: Расширения для PostgreSQL

30

SPI. Timetravel. DELETE

DELETE FROM tttest WHERE price_id = 1;

price_id | price_val | price_on | price_off ----------+-----------+------------------------+------------------------ 2 | 2 | 2016-03-07 18:00:09+03 | infinity 3 | 3 | 2016-03-07 18:00:32+03 | infinity 1 | 1 | 2016-03-07 17:59:46+03 | 2016-03-07 18:04:16+03

Page 31: Расширения для PostgreSQL

31

SPI. Timetravel. UPDATE

UPDATE tttest SET price_val = 30 WHERE price_id = 3;

price_id | price_val | price_on | price_off ----------+-----------+------------------------+------------------------ 2 | 2 | 2016-03-07 18:00:09+03 | infinity 1 | 1 | 2016-03-07 17:59:46+03 | 2016-03-07 18:04:16+03 3 | 3 | 2016-03-07 18:00:32+03 | 2016-03-07 18:06:19+03 3 | 30 | 2016-03-07 18:06:19+03 | infinity

Page 32: Расширения для PostgreSQL

32

SPI. Timetravel. SELECT

SELECT * FROM tttest WHERE price_off = 'infinity';

price_id | price_val | price_on | price_off ----------+-----------+------------------------+------------------------ 2 | 2 | 2016-03-07 18:00:09+03 | infinity 3 | 30 | 2016-03-07 18:06:19+03 | infinity

Page 33: Расширения для PostgreSQL

33

SPI. Timetravel. CHANGE DATE

SELECT set_timetravel('tttest', 0);

UPDATE tttest SET price_on = '01-Jan-1990 00:00:01'

WHERE price_id = 3 AND price_off <> 'infinity';

SELECT set_timetrave l('tttest', 1);

SELECT * FROM tttest WHERE price_id = 3AND price_on <= '10-Jan-1990' AND price_off > '10-Jan-1990';

price_id | price_val | price_on | price_off ----------+-----------+------------------------+------------------------ 3 | 3 | 1990-01-01 00:00:01+03 | 2016-03-07 18:06:19+03

Page 35: Расширения для PostgreSQL

35

Недостатки SPI

● Низкая скорость● Доступ только к HeapTuples

Page 36: Расширения для PostgreSQL

36

Содержание

1. Расширение (contrib)

2. Новый тип данных

3. SPI (Server Programming Interface)

4. Особенности функций на Си

5. SRF (Set-Returning Function)

Page 37: Расширения для PostgreSQL

37

Datum

● Datum – абстрактный тип данных в PostgreSQL

● Значения, которые помещаются в Datum, могут быть переданы по значению, остальные передаются по указателю.

● Аргументы функции и возвращаемые значения передаются как Datum.

● src/include/postgres.h

● src/include/utils/datum.h

Page 38: Расширения для PostgreSQL

38

Datum (2)

typedef uintptr_t Datum;

#define DatumGetPointer(X) ((Pointer) (X))

#define PointerGetDatum(X) ((Datum) (X))

#define DatumGetInt32(X) ((int32) GET_4_BYTES(X))

#define Int32GetDatum(X) ((Datum) SET_4_BYTES(X))

Size datumGetSize(Datum value, bool typByVal, int typLen);

Datum datumCopy(Datum value, bool typByVal, int typLen);

bool datumIsEqual(Datum value1, Datum value2,bool typByVal, int typLen);

Page 39: Расширения для PostgreSQL

39

Function manager

● src/backend/utils/fmgr/README● src/include/fmgr.h● src/include/funcapi.h

Page 40: Расширения для PostgreSQL

40

PG_MODULE_MAGIC

#include "fmgr.h"

#ifdef PG_MODULE_MAGIC

PG_MODULE_MAGIC;

#endif

void _PG_init(void)

{

/* Define custom GUC variables */

/* Install hooks */

}

/* Source code */

Page 41: Расширения для PostgreSQL

41

Calling convention 1

/* Standard parameter list for fmgr-compatible functions */

#define PG_FUNCTION_ARGS FunctionCallInfo fcinfo

/* Макросы для доступа к параметрам */

#define PG_GETARG_DATUM(n) (fcinfo->arg[n])

#define PG_GETARG_INT32(n) DatumGetInt32(PG_GETARG_DATUM(n))

#define PG_NARGS() (fcinfo->nargs)

#define PG_ARGISNULL(n) (fcinfo->argnull[n])

/* Макросы для возврата параметров */

#define PG_RETURN_DATUM(x) return (x)

#define PG_RETURN_VOID() return (Datum) 0

#define PG_RETURN_NULL()

Page 42: Расширения для PostgreSQL

42

Пример

/* функция на Си */PG_FUNCTION_INFO_V1(add_one)Datumadd_one(PG_FUNCTION_ARGS){ int32 arg = PG_GETARG_INT32(0); PG_RETURN_INT32(arg + 1);}

/* SQL объявление функции */CREATE FUNCTION add_one(integer) RETURNS integer AS 'DIRECTORY/funcs', 'add_one' LANGUAGE C STRICT;

Page 43: Расширения для PostgreSQL

43

Работа с Tuple

#include "funcapi.h"

/* Узнать возвращаемый тип данных, * и, если он составной, получить TupleDesc */TypeFuncClass get_call_result_type(

FunctionCallInfo fcinfo,

Oid *resultTypeId,

TupleDesc *resultTupleDesc)

TupleDesc BlessTupleDesc(TupleDesc tupdesc)

HeapTuple heap_form_tuple(TupleDesc tupdesc,Datum *values, bool *isnull)

HeapTupleGetDatum(HeapTuple tuple)

Page 44: Расширения для PostgreSQL

44

Ещё примеры SRF

● src/include/access/tupdesc.h● src/backend/access/common/tupdesc.c

Page 45: Расширения для PostgreSQL

45

Содержание

1. Расширение (contrib)

2. Новый тип данных

3. SPI (Server Programming Interface)

4. Особенности функций на Си

5. SRF (Set-Returning Function)

Page 47: Расширения для PostgreSQL

47

SRF (Set Returning Functions)

#Определить, что функция вызвана первый разSRF_IS_FIRSTCALL()

#Инициализировать FuncCallContext при первом вызовеSRF_FIRSTCALL_INIT()

#Очистить контекст при каждом последующем вызовеSRF_PERCALL_SETUP()

#Вернуть очередное значение функцииSRF_RETURN_NEXT(funcctx, result)

#Завершить выполнение SRF, сбросить контекстSRF_RETURN_DONE(funcctx)

Page 48: Расширения для PostgreSQL

48

SRF (1)

Datummy_Set_Returning_Function(PG_FUNCTION_ARGS){

FuncCallContext *funcctx;Datum result;MemoryContext oldcontext;<user defined declarations>

Page 49: Расширения для PostgreSQL

49

SRF (2)

...

if (SRF_IS_FIRSTCALL()){ funcctx = SRF_FIRSTCALL_INIT(); /* switch context when allocating stuff * to be used in later calls */ oldcontext = MemoryContextSwitchTo( funcctx->multi_call_memory_ctx); <user defined code> <if returning composite> <build TupleDesc, and perhaps AttInMetaData> <endif returning composite> <user defined code> /* return to original context * when allocating transient memory */ MemoryContextSwitchTo(oldcontext);}

Page 50: Расширения для PostgreSQL

50

SRF (3)

...<user defined code>funcctx = SRF_PERCALL_SETUP();<user defined code>

if (funcctx->call_cntr < funcctx->max_calls){ <user defined code> <obtain result Datum> SRF_RETURN_NEXT(funcctx, result);}else SRF_RETURN_DONE(funcctx);

}

Page 51: Расширения для PostgreSQL

51

contrib/pg_buffercache

pg_buffercache – информация о shared buffers в реальном времени

Page 52: Расширения для PostgreSQL

52

pg_buffercache--1.1.sql

CREATE FUNCTION pg_buffercache_pages()

RETURNS SETOF RECORD

AS 'MODULE_PATHNAME', 'pg_buffercache_pages'

LANGUAGE C;

CREATE VIEW pg_buffercache AS

SELECT P.* FROM pg_buffercache_pages() AS P

(bufferid integer, relfilenode oid,

reltablespace oid, reldatabase oid,

relforknumber int2, relblocknumber int8,

isdirty bool, usagecount int2,

pinning_backends int4);

Page 53: Расширения для PostgreSQL

53

pg_buffercache_pages.c

PG_FUNCTION_INFO_V1(pg_buffercache_pages);

Datumpg_buffercache_pages(PG_FUNCTION_ARGS)

{

FuncCallContext *funcctx;

Datum result;

MemoryContext oldcontext;

BufferCachePagesContext *fctx; /*User function context.*/

TupleDesc tupledesc;

TupleDesc expected_tupledesc;

HeapTuple tuple;

/* Продолжение по ссылке pg_buffercache_pages */

Page 54: Расширения для PostgreSQL

54

Ещё примеры SRF

● generate_series● contrib/pageinspect

Page 55: Расширения для PostgreSQL

55

Домашнее задание

● Написать на [email protected], какими расширениями вы часто пользуетесь и каких вам очень не хватает.

● Написать, используя SPI, расширение, которое на любое изменение строки добавляет в зарезервированные столбцы текущее время и имя пользователя.

● Ревью amcheck (B-Tree integrity checking tool).● Написать расширение bufferinspect по аналогии с

pageinspect для страниц в разделяемой памяти. Можно добавить дополнительные функции в pg_buffercache.

● Расширение dirtyread, которое позволяет прочитать (насколько возможно) битый файл из другой базы.

Page 56: Расширения для PostgreSQL

56

Продолжение следует...

В следующий раз:

– Обзор source tree– Путь разных запросов от получения

текста до выдачи результата

Page 57: Расширения для PostgreSQL

www.postgrespro.ru

СПАСИБО ЗА ВНИМАНИЕ! ВОПРОСЫ?

Hacking PostgreSQL

10.03.2016

[email protected]