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
Объекты в расширении
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
Объекты в расширении
[ 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
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;
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
37. 37
Datum
●
Datum – абстрактный тип данных в PostgreSQL
●
Значения, которые помещаются в Datum, могут
быть переданы по значению, остальные
передаются по указателю.
●
Аргументы функции и возвращаемые значения
передаются как Datum.
●
src/include/postgres.h
●
src/include/utils/datum.h
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);
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)
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)
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 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);
}
55. 55
Домашнее задание
●
Написать на hacking@postgrespro.ru, какими
расширениями вы часто пользуетесь и каких вам очень
не хватает.
●
Написать, используя SPI, расширение, которое на любое
изменение строки добавляет в зарезервированные
столбцы текущее время и имя пользователя.
●
Ревью amcheck (B-Tree integrity checking tool).
●
Написать расширение bufferinspect по аналогии с
pageinspect для страниц в разделяемой памяти. Можно
добавить дополнительные функции в pg_buffercache.
●
Расширение dirtyread, которое позволяет прочитать
(насколько возможно) битый файл из другой базы.