www.postgrespro.ru
РАСШИРЕНИЯ ДЛЯ POSTGRESQL
Hacking PostgreSQL
10.03.2016
2
Содержание
1. Расширение (contrib)
2. Новый тип данных
3. SPI (Server Programming Interface)
4. Особенности функций на Си
5. SRF (Set-Returning Function)
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
Управление расширением
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
Объекты в расширении
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
Объекты в расширении
[ 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
Из чего состоит расширение?
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
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
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
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
Содержание
1. Расширение (contrib)
2. Новый тип данных
3. SPI (Server Programming Interface)
4. Особенности функций на Си
5. SRF (Set-Returning Function)
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
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
contrib/hstore
Реализация key/value типа данных.
Предшественник json.
Документация модуля hstore
Презентация про hstore.
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
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
Новый оператор
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
Функция для оператора
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
Индексная поддержка
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
Ещё примеры
●
math3d
●
IMCS (In-Memory Columnar Store)
21
Содержание
1. Расширение (contrib)
2. Новый тип данных
3. SPI (Server Programming Interface)
4. Особенности функций на Си
5. SRF (Set-Returning Function)
22
SPI. Функции
SPI - Server Programming Interface
●
Процедурные языки
(PL/pgSQL, PL/python, PL/perl, PL/R ...)
●
API для расширений (pg_paxos api)
●
Триггеры (contrib/spi)
23
SPI. Функции
●
SPI_connect / SPI_finish
●
SPI_push / SPI_pop
●
SPI_execute / SPI_exec
●
SPI_prepare
●
SPI_execute_plan
●
SPI_keepplan / SPI_saveplan
24
SPI. Функции (2)
●
SPI_gettype / SPI_gettypeid
●
SPI_getrelname / SPI_getnspnam
●
SPI_copytuple
●
SPI_returntuple
●
SPI_modifytuple
25
SPI. Функции (3)
●
SPI_palloc
●
SPI_repalloc
●
SPI_pfree
26
SPI. Timetravel
timetravel ('date_on', 'date_off'
[,'insert_user',
'update_user',
'delete_user' ] )
set_timetravel(relname, on)
get_timetravel(relename)
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
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
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
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
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
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
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
Ещё примеры SPI
●
pg_pathman
●
pg_paxos
35
Недостатки SPI
●
Низкая скорость
●
Доступ только к HeapTuples
36
Содержание
1. Расширение (contrib)
2. Новый тип данных
3. SPI (Server Programming Interface)
4. Особенности функций на Си
5. SRF (Set-Returning Function)
37
Datum
●
Datum – абстрактный тип данных в PostgreSQL
●
Значения, которые помещаются в Datum, могут
быть переданы по значению, остальные
передаются по указателю.
●
Аргументы функции и возвращаемые значения
передаются как Datum.
●
src/include/postgres.h
●
src/include/utils/datum.h
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
Function manager
●
src/backend/utils/fmgr/README
●
src/include/fmgr.h
●
src/include/funcapi.h
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
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
Пример
/* функция на Си */
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
Работа с 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
Ещё примеры SRF
●
src/include/access/tupdesc.h
●
src/backend/access/common/tupdesc.c
45
Содержание
1. Расширение (contrib)
2. Новый тип данных
3. SPI (Server Programming Interface)
4. Особенности функций на Си
5. SRF (Set-Returning Function)
46
SRF
●
35.9.9. Returning Sets
●
src/include/funcapi.h
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
SRF (1)
Datum
my_Set_Returning_Function(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
Datum result;
MemoryContext oldcontext;
<user defined declarations>
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
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
contrib/pg_buffercache
pg_buffercache – информация о shared
buffers в реальном времени
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
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
Ещё примеры SRF
●
generate_series
●
contrib/pageinspect
55
Домашнее задание
●
Написать на hacking@postgrespro.ru, какими
расширениями вы часто пользуетесь и каких вам очень
не хватает.
●
Написать, используя SPI, расширение, которое на любое
изменение строки добавляет в зарезервированные
столбцы текущее время и имя пользователя.
●
Ревью amcheck (B-Tree integrity checking tool).
●
Написать расширение bufferinspect по аналогии с
pageinspect для страниц в разделяемой памяти. Можно
добавить дополнительные функции в pg_buffercache.
●
Расширение dirtyread, которое позволяет прочитать
(насколько возможно) битый файл из другой базы.
56
Продолжение следует...
В следующий раз:
– Обзор source tree
– Путь разных запросов от получения
текста до выдачи результата
www.postgrespro.ru
СПАСИБО ЗА ВНИМАНИЕ!
ВОПРОСЫ?
Hacking PostgreSQL
10.03.2016
hacking@postgrespro.ru

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

  • 1.
  • 2.
    2 Содержание 1. Расширение (contrib) 2.Новый тип данных 3. SPI (Server Programming Interface) 4. Особенности функций на Си 5. SRF (Set-Returning Function)
  • 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 Управление расширением 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 Объекты в расширении AGGREGATEagg_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 Объекты в расширении [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 Из чего состоитрасширение? 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 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 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 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 Содержание 1. Расширение (contrib) 2.Новый тип данных 3. SPI (Server Programming Interface) 4. Особенности функций на Си 5. SRF (Set-Returning Function)
  • 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 pair CREATE EXTENSION pg_example; CREATETABLE 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 contrib/hstore Реализация key/value типаданных. Предшественник json. Документация модуля hstore Презентация про hstore.
  • 15.
    15 CREATE TYPE CREATE TYPEhstore ( 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 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 Новый оператор 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 Функция для оператора CREATEFUNCTION 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 Индексная поддержка CREATE OPERATORCLASS 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.
  • 21.
    21 Содержание 1. Расширение (contrib) 2.Новый тип данных 3. SPI (Server Programming Interface) 4. Особенности функций на Си 5. SRF (Set-Returning Function)
  • 22.
    22 SPI. Функции SPI -Server Programming Interface ● Процедурные языки (PL/pgSQL, PL/python, PL/perl, PL/R ...) ● API для расширений (pg_paxos api) ● Триггеры (contrib/spi)
  • 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 SPI. Функции (2) ● SPI_gettype/ SPI_gettypeid ● SPI_getrelname / SPI_getnspnam ● SPI_copytuple ● SPI_returntuple ● SPI_modifytuple
  • 25.
  • 26.
    26 SPI. Timetravel timetravel ('date_on','date_off' [,'insert_user', 'update_user', 'delete_user' ] ) set_timetravel(relname, on) get_timetravel(relename)
  • 27.
    27 SPI. Timetravel CREATE EXTENSIONtimetravel; 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 SPI. Timetravel. INSERT INSERTINTO 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 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 SPI. Timetravel. DELETE DELETEFROM 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 SPI. Timetravel. UPDATE UPDATEtttest 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 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 SPI. Timetravel. CHANGEDATE 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.
  • 35.
  • 36.
    36 Содержание 1. Расширение (contrib) 2.Новый тип данных 3. SPI (Server Programming Interface) 4. Особенности функций на Си 5. SRF (Set-Returning Function)
  • 37.
    37 Datum ● Datum – абстрактныйтип данных в PostgreSQL ● Значения, которые помещаются в Datum, могут быть переданы по значению, остальные передаются по указателю. ● Аргументы функции и возвращаемые значения передаются как Datum. ● src/include/postgres.h ● src/include/utils/datum.h
  • 38.
    38 Datum (2) typedef uintptr_tDatum; #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.
  • 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 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 Пример /* функция наСи */ 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 Работа с 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.
  • 45.
    45 Содержание 1. Расширение (contrib) 2.Новый тип данных 3. SPI (Server Programming Interface) 4. Особенности функций на Си 5. SRF (Set-Returning Function)
  • 46.
  • 47.
    47 SRF (Set ReturningFunctions) #Определить, что функция вызвана первый раз SRF_IS_FIRSTCALL() #Инициализировать FuncCallContext при первом вызове SRF_FIRSTCALL_INIT() #Очистить контекст при каждом последующем вызове SRF_PERCALL_SETUP() #Вернуть очередное значение функции SRF_RETURN_NEXT(funcctx, result) #Завершить выполнение SRF, сбросить контекст SRF_RETURN_DONE(funcctx)
  • 48.
  • 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 SRF (3) ... <user definedcode> 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 contrib/pg_buffercache pg_buffercache – информацияо shared buffers в реальном времени
  • 52.
    52 pg_buffercache--1.1.sql CREATE FUNCTION pg_buffercache_pages() RETURNSSETOF 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 pg_buffercache_pages.c PG_FUNCTION_INFO_V1(pg_buffercache_pages); Datum pg_buffercache_pages(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; Datum result; MemoryContextoldcontext; BufferCachePagesContext *fctx; /*User function context.*/ TupleDesc tupledesc; TupleDesc expected_tupledesc; HeapTuple tuple; /* Продолжение по ссылке pg_buffercache_pages */
  • 54.
  • 55.
    55 Домашнее задание ● Написать наhacking@postgrespro.ru, какими расширениями вы часто пользуетесь и каких вам очень не хватает. ● Написать, используя SPI, расширение, которое на любое изменение строки добавляет в зарезервированные столбцы текущее время и имя пользователя. ● Ревью amcheck (B-Tree integrity checking tool). ● Написать расширение bufferinspect по аналогии с pageinspect для страниц в разделяемой памяти. Можно добавить дополнительные функции в pg_buffercache. ● Расширение dirtyread, которое позволяет прочитать (насколько возможно) битый файл из другой базы.
  • 56.
    56 Продолжение следует... В следующийраз: – Обзор source tree – Путь разных запросов от получения текста до выдачи результата
  • 57.