Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

1,382 views

Published on

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

Published in: Data & Analytics
  • Be the first to comment

Расширения для 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

×