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

1,258 views

Published on

Лекция №3 курса Hacking PostgreSQL о различных возможностях contrib-модулей.

Published in: Data & Analytics
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,258
On SlideShare
0
From Embeds
0
Number of Embeds
437
Actions
Shares
0
Downloads
54
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

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

  1. 1. www.postgrespro.ru РАСШИРЕНИЯ ДЛЯ POSTGRESQL Hacking PostgreSQL 10.03.2016
  2. 2. 2 Содержание 1. Расширение (contrib) 2. Новый тип данных 3. SPI (Server Programming Interface) 4. Особенности функций на Си 5. SRF (Set-Returning Function)
  3. 3. 3 Расширение (contrib) ● Инфраструктура сборки PGXS ● PGXN (PostgreSQL Extension Network) ● PgFoundry ● Управляется каталогом pg_extension SELECT * FROM pg_extension; -[ RECORD 1 ]--+-------- extname | plpgsql extowner | 10 extnamespace | 11 extrelocatable | f extversion | 1.0 extconfig | extcondition |
  4. 4. 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
  5. 5. 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 |
  6. 6. 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
  7. 7. 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
  8. 8. 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
  9. 9. 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
  10. 10. 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
  11. 11. 11 Содержание 1. Расширение (contrib) 2. Новый тип данных 3. SPI (Server Programming Interface) 4. Особенности функций на Си 5. SRF (Set-Returning Function)
  12. 12. 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 );
  13. 13. 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
  14. 14. 14 contrib/hstore Реализация key/value типа данных. Предшественник json. Документация модуля hstore Презентация про hstore.
  15. 15. 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 | hstore typnamespace | 2200 typowner | 16384 typlen | -1 typbyval | f typinput | hstore_in typoutput | hstore_out typreceive | hstore_recv typsend | hstore_send typstorage | x (extended) .......
  16. 16. 16 hstore_in CREATE FUNCTION hstore_in(cstring) RETURNS hstore AS '$libdir/hstore', 'hstore_in' LANGUAGE C STRICT IMMUTABLE; pg_proc ----------------+--------------- proname | hstore_in pronamespace | 2200 proowner | 16384 prolang | 13 procost | 1 prorows | 0 provariadic | 0 prorettype | 16385 (hstore) proargtypes | 2275 (cstring) prosrc | hstore_in probin | $libdir/hstore .......
  17. 17. 17 Новый оператор CREATE OPERATOR -> ( LEFTARG = hstore, RIGHTARG = text, PROCEDURE = fetchval ); pg_operator -------------+------------ oprname | -> oprnamespace | 2200 oprowner | 16384 oprkind | b oprleft | 16385 (hstore) Oprright | 25 (text) oprresult | 25 oprcom | 0 oprnegate | 0 oprcode | fetchval
  18. 18. 18 Функция для оператора CREATE FUNCTION fetchval(hstore,text) RETURNS text AS '$libdir/hstore', 'hstore_fetchval' LANGUAGE C STRICT IMMUTABLE; pg_proc ----------------+---------------- proname | fetchval pronamespace | 2200 proowner | 16384 prolang | 13 procost | 1 prosrc | hstore_fetchval probin | $libdir/hstore proconfig | proacl | .......
  19. 19. 19 Индексная поддержка CREATE OPERATOR CLASS gist_hstore_ops DEFAULT FOR TYPE hstore USING gist AS 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;
  20. 20. 20 Ещё примеры ● math3d ● IMCS (In-Memory Columnar Store)
  21. 21. 21 Содержание 1. Расширение (contrib) 2. Новый тип данных 3. SPI (Server Programming Interface) 4. Особенности функций на Си 5. SRF (Set-Returning Function)
  22. 22. 22 SPI. Функции SPI - Server Programming Interface ● Процедурные языки (PL/pgSQL, PL/python, PL/perl, PL/R ...) ● API для расширений (pg_paxos api) ● Триггеры (contrib/spi)
  23. 23. 23 SPI. Функции ● SPI_connect / SPI_finish ● SPI_push / SPI_pop ● SPI_execute / SPI_exec ● SPI_prepare ● SPI_execute_plan ● SPI_keepplan / SPI_saveplan
  24. 24. 24 SPI. Функции (2) ● SPI_gettype / SPI_gettypeid ● SPI_getrelname / SPI_getnspnam ● SPI_copytuple ● SPI_returntuple ● SPI_modifytuple
  25. 25. 25 SPI. Функции (3) ● SPI_palloc ● SPI_repalloc ● SPI_pfree
  26. 26. 26 SPI. Timetravel timetravel ('date_on', 'date_off' [,'insert_user', 'update_user', 'delete_user' ] ) set_timetravel(relname, on) get_timetravel(relename)
  27. 27. 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);
  28. 28. 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
  29. 29. 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);
  30. 30. 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
  31. 31. 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
  32. 32. 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
  33. 33. 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 = 3 AND 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
  34. 34. 34 Ещё примеры SPI ● pg_pathman ● pg_paxos
  35. 35. 35 Недостатки SPI ● Низкая скорость ● Доступ только к HeapTuples
  36. 36. 36 Содержание 1. Расширение (contrib) 2. Новый тип данных 3. SPI (Server Programming Interface) 4. Особенности функций на Си 5. SRF (Set-Returning Function)
  37. 37. 37 Datum ● Datum – абстрактный тип данных в PostgreSQL ● Значения, которые помещаются в Datum, могут быть переданы по значению, остальные передаются по указателю. ● Аргументы функции и возвращаемые значения передаются как Datum. ● src/include/postgres.h ● src/include/utils/datum.h
  38. 38. 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);
  39. 39. 39 Function manager ● src/backend/utils/fmgr/README ● src/include/fmgr.h ● src/include/funcapi.h
  40. 40. 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 */
  41. 41. 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()
  42. 42. 42 Пример /* функция на Си */ PG_FUNCTION_INFO_V1(add_one) Datum add_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;
  43. 43. 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)
  44. 44. 44 Ещё примеры SRF ● src/include/access/tupdesc.h ● src/backend/access/common/tupdesc.c
  45. 45. 45 Содержание 1. Расширение (contrib) 2. Новый тип данных 3. SPI (Server Programming Interface) 4. Особенности функций на Си 5. SRF (Set-Returning Function)
  46. 46. 46 SRF ● 35.9.9. Returning Sets ● src/include/funcapi.h
  47. 47. 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)
  48. 48. 48 SRF (1) Datum my_Set_Returning_Function(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; Datum result; MemoryContext oldcontext; <user defined declarations>
  49. 49. 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); }
  50. 50. 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); }
  51. 51. 51 contrib/pg_buffercache pg_buffercache – информация о shared buffers в реальном времени
  52. 52. 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);
  53. 53. 53 pg_buffercache_pages.c PG_FUNCTION_INFO_V1(pg_buffercache_pages); Datum pg_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 */
  54. 54. 54 Ещё примеры SRF ● generate_series ● contrib/pageinspect
  55. 55. 55 Домашнее задание ● Написать на hacking@postgrespro.ru, какими расширениями вы часто пользуетесь и каких вам очень не хватает. ● Написать, используя SPI, расширение, которое на любое изменение строки добавляет в зарезервированные столбцы текущее время и имя пользователя. ● Ревью amcheck (B-Tree integrity checking tool). ● Написать расширение bufferinspect по аналогии с pageinspect для страниц в разделяемой памяти. Можно добавить дополнительные функции в pg_buffercache. ● Расширение dirtyread, которое позволяет прочитать (насколько возможно) битый файл из другой базы.
  56. 56. 56 Продолжение следует... В следующий раз: – Обзор source tree – Путь разных запросов от получения текста до выдачи результата
  57. 57. www.postgrespro.ru СПАСИБО ЗА ВНИМАНИЕ! ВОПРОСЫ? Hacking PostgreSQL 10.03.2016 hacking@postgrespro.ru

×