Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitea61e76

Browse files
committed
Add separate documentation with custom subscription examples
1 parent1bfa84a commitea61e76

File tree

5 files changed

+390
-2
lines changed

5 files changed

+390
-2
lines changed

‎doc/src/sgml/filelist.sgml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
<!ENTITY xplang SYSTEM "xplang.sgml">
7171
<!ENTITY xoper SYSTEM "xoper.sgml">
7272
<!ENTITY xtypes SYSTEM "xtypes.sgml">
73+
<!ENTITY xsubscription SYSTEM "xsubscription.sgml">
7374
<!ENTITY plperl SYSTEM "plperl.sgml">
7475
<!ENTITY plpython SYSTEM "plpython.sgml">
7576
<!ENTITY plsql SYSTEM "plpgsql.sgml">

‎doc/src/sgml/xsubscription.sgml‎

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<!-- doc/src/sgml/xsubscription.sgml -->
2+
3+
<sect1 id="xsubscription">
4+
<title>User-defined subscription procedure</title>
5+
6+
<indexterm zone="xsubscription">
7+
<primary>custom subscription</primary>
8+
</indexterm>
9+
When you define a new base type, you can also specify a custom procedure
10+
to handle subscription expressions. It should contains logic for verification
11+
and for extraction or update your data. For instance:
12+
13+
<programlisting><![CDATA[
14+
typedef struct Custom
15+
{
16+
intfirst;
17+
intsecond;
18+
}Custom;
19+
20+
Datum
21+
custom_subscription_evaluate(PG_FUNCTION_ARGS)
22+
{
23+
SubscriptionExecData*sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1);
24+
Custom*result = (Custom *) sbsdata->containerSource;
25+
26+
// Some extraction or update logic based on sbsdata
27+
}
28+
29+
Datum
30+
custom_subscription_prepare(PG_FUNCTION_ARGS)
31+
{
32+
SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0);
33+
34+
// Some verifications or type coersion
35+
36+
PG_RETURN_POINTER(sbsref);
37+
}
38+
39+
PG_FUNCTION_INFO_V1(custom_subscription);
40+
41+
Datum
42+
custom_subscription(PG_FUNCTION_ARGS)
43+
{
44+
intop_type = PG_GETARG_INT32(0);
45+
FunctionCallInfoDatatarget_fcinfo = get_slice_arguments(fcinfo, 1,
46+
fcinfo->nargs);
47+
48+
if (op_type & SBS_VALIDATION)
49+
return custom_subscription_prepare(&target_fcinfo);
50+
51+
if (op_type & SBS_EXEC)
52+
return custom_subscription_evaluate(&target_fcinfo);
53+
54+
elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
55+
}]]>
56+
</programlisting>
57+
58+
Then you can define a subscription procedure and a custom data type:
59+
60+
<programlisting>
61+
CREATE FUNCTION custom_subscription(internal)
62+
RETURNS internal
63+
AS '<replaceable>filename</replaceable>'
64+
LANGUAGE C IMMUTABLE STRICT;
65+
66+
CREATE TYPE custom (
67+
internallength = 4,
68+
input = custom_in,
69+
output = custom_out,
70+
subscription = custom_subscription
71+
);
72+
</programlisting>
73+
74+
and use it as usual:
75+
76+
<programlisting>
77+
CREATE TABLE test_subscription (
78+
datacustom,
79+
);
80+
81+
INSERT INTO test_subscription VALUES ('(1, 2)');
82+
83+
SELECT data[0] from test_subscription;
84+
85+
UPDATE test_subscription SET data[1] = 3;
86+
</programlisting>
87+
88+
</para>
89+
90+
<para>
91+
The examples of custom subscription implementation can be found in
92+
<filename>subscription.sql</filename> and <filename>subscription.c</filename>
93+
in the <filename>src/tutorial</> directory of the source distribution.
94+
See the <filename>README</> file in that directory for instructions
95+
about running the examples.
96+
</para>
97+
98+
</sect1>

‎src/tutorial/Makefile‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
#
1414
#-------------------------------------------------------------------------
1515

16-
MODULES = complex funcs
17-
DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql
16+
MODULES = complex funcs subscription
17+
DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql subscription.sql
1818

1919
ifdefNO_PGXS
2020
subdir = src/tutorial

‎src/tutorial/subscription.c‎

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
* src/tutorial/subscription.c
3+
*
4+
******************************************************************************
5+
This file contains routines that can be bound to a Postgres backend and
6+
called by the backend in the process of processing queries. The calling
7+
format for these routines is dictated by Postgres architecture.
8+
******************************************************************************/
9+
10+
#include"postgres.h"
11+
12+
#include"catalog/pg_type.h"
13+
#include"executor/executor.h"
14+
#include"nodes/execnodes.h"
15+
#include"nodes/nodeFuncs.h"
16+
#include"parser/parse_coerce.h"
17+
#include"parser/parse_node.h"
18+
#include"utils/array.h"
19+
#include"fmgr.h"
20+
#include"funcapi.h"
21+
22+
PG_MODULE_MAGIC;
23+
24+
typedefstructCustom
25+
{
26+
intfirst;
27+
intsecond;
28+
}Custom;
29+
30+
31+
/*****************************************************************************
32+
* Input/Output functions
33+
*****************************************************************************/
34+
35+
PG_FUNCTION_INFO_V1(custom_in);
36+
37+
Datum
38+
custom_in(PG_FUNCTION_ARGS)
39+
{
40+
char*str=PG_GETARG_CSTRING(0);
41+
intfirstValue,
42+
secondValue;
43+
Custom*result;
44+
45+
if (sscanf(str," ( %d , %d )",&firstValue,&secondValue)!=2)
46+
ereport(ERROR,
47+
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
48+
errmsg("invalid input syntax for complex: \"%s\"",
49+
str)));
50+
51+
52+
result= (Custom*)palloc(sizeof(Custom));
53+
result->first=firstValue;
54+
result->second=secondValue;
55+
PG_RETURN_POINTER(result);
56+
}
57+
58+
PG_FUNCTION_INFO_V1(custom_out);
59+
60+
Datum
61+
custom_out(PG_FUNCTION_ARGS)
62+
{
63+
Custom*custom= (Custom*)PG_GETARG_POINTER(0);
64+
char*result;
65+
66+
result=psprintf("(%d, %d)",custom->first,custom->second);
67+
PG_RETURN_CSTRING(result);
68+
}
69+
70+
/*****************************************************************************
71+
* Custom subscription logic functions
72+
*****************************************************************************/
73+
74+
Datum
75+
custom_subscription_evaluate(PG_FUNCTION_ARGS)
76+
{
77+
SubscriptionRefExprState*sbstate= (SubscriptionRefExprState*)PG_GETARG_POINTER(0);
78+
SubscriptionExecData*sbsdata= (SubscriptionExecData*)PG_GETARG_POINTER(1);
79+
SubscriptionRef*custom_ref= (SubscriptionRef*)sbstate->xprstate.expr;
80+
Custom*result= (Custom*)sbsdata->containerSource;
81+
bool*is_null=sbsdata->isNull;
82+
boolis_assignment= (custom_ref->refassgnexpr!=NULL);
83+
84+
intindex;
85+
86+
if (sbsdata->indexprNumber!=1)
87+
ereport(ERROR, (errmsg("custom does not support nested subscription")));
88+
89+
index=DatumGetInt32(sbsdata->upper[0]);
90+
91+
if (is_assignment)
92+
{
93+
ExprContext*econtext=sbsdata->xprcontext;
94+
DatumsourceData;
95+
Datumsave_datum;
96+
boolsave_isNull;
97+
booleisnull;
98+
99+
/*
100+
* We might have a nested-assignment situation, in which the
101+
* refassgnexpr is itself a FieldStore or SubscriptionRef that needs to
102+
* obtain and modify the previous value of the array element or slice
103+
* being replaced. If so, we have to extract that value from the
104+
* array and pass it down via the econtext's caseValue. It's safe to
105+
* reuse the CASE mechanism because there cannot be a CASE between
106+
* here and where the value would be needed, and an array assignment
107+
* can't be within a CASE either. (So saving and restoring the
108+
* caseValue is just paranoia, but let's do it anyway.)
109+
*
110+
* Since fetching the old element might be a nontrivial expense, do it
111+
* only if the argument appears to actually need it.
112+
*/
113+
save_datum=econtext->caseValue_datum;
114+
save_isNull=econtext->caseValue_isNull;
115+
116+
/*
117+
* Evaluate the value to be assigned into the container.
118+
*/
119+
sourceData=ExecEvalExpr(sbstate->refassgnexpr,
120+
econtext,
121+
&eisnull,
122+
NULL);
123+
124+
econtext->caseValue_datum=save_datum;
125+
econtext->caseValue_isNull=save_isNull;
126+
127+
/*
128+
* For an assignment to a fixed-length array type, both the original
129+
* array and the value to be assigned into it must be non-NULL, else
130+
* we punt and return the original array.
131+
*/
132+
if (sbstate->refattrlength>0)/* fixed-length container? */
133+
if (eisnull||*is_null)
134+
returnsbsdata->containerSource;
135+
136+
/*
137+
* For assignment to varlena container, we handle a NULL original array
138+
* by substituting an empty (zero-dimensional) array; insertion of the
139+
* new element will result in a singleton array value. It does not
140+
* matter whether the new element is NULL.
141+
*/
142+
if (*is_null)
143+
{
144+
sbsdata->containerSource=
145+
PointerGetDatum(construct_empty_array(custom_ref->refelemtype));
146+
*is_null= false;
147+
}
148+
149+
if (index==1)
150+
result->first=DatumGetInt32(sourceData);
151+
else
152+
result->second=DatumGetInt32(sourceData);
153+
154+
PG_RETURN_POINTER(result);
155+
}
156+
else
157+
{
158+
if (index==1)
159+
PG_RETURN_INT32(result->first);
160+
else
161+
PG_RETURN_INT32(result->second);
162+
}
163+
}
164+
165+
Datum
166+
custom_subscription_prepare(PG_FUNCTION_ARGS)
167+
{
168+
SubscriptionRef*sbsref= (SubscriptionRef*)PG_GETARG_POINTER(0);
169+
ParseState*pstate= (ParseState*)PG_GETARG_POINTER(1);
170+
List*upperIndexpr=NIL;
171+
ListCell*l;
172+
173+
if (sbsref->reflowerindexpr!=NIL)
174+
ereport(ERROR,
175+
(errcode(ERRCODE_DATATYPE_MISMATCH),
176+
errmsg("custom subscript does not support slices"),
177+
parser_errposition(pstate,exprLocation(
178+
((Node*)lfirst(sbsref->reflowerindexpr->head))))));
179+
180+
foreach(l,sbsref->refupperindexpr)
181+
{
182+
Node*subexpr= (Node*)lfirst(l);
183+
184+
Assert(subexpr!=NULL);
185+
186+
if (subexpr==NULL)
187+
ereport(ERROR,
188+
(errcode(ERRCODE_DATATYPE_MISMATCH),
189+
errmsg("custom subscript does not support slices"),
190+
parser_errposition(pstate,exprLocation(
191+
((Node*)lfirst(sbsref->refupperindexpr->head))))));
192+
193+
subexpr=coerce_to_target_type(pstate,
194+
subexpr,exprType(subexpr),
195+
INT4OID,-1,
196+
COERCION_ASSIGNMENT,
197+
COERCE_IMPLICIT_CAST,
198+
-1);
199+
if (subexpr==NULL)
200+
ereport(ERROR,
201+
(errcode(ERRCODE_DATATYPE_MISMATCH),
202+
errmsg("custom subscript must have int type"),
203+
parser_errposition(pstate,exprLocation(subexpr))));
204+
205+
upperIndexpr=lappend(upperIndexpr,subexpr);
206+
}
207+
208+
sbsref->refupperindexpr=upperIndexpr;
209+
sbsref->refelemtype=INT4OID;
210+
211+
PG_RETURN_POINTER(sbsref);
212+
}
213+
214+
PG_FUNCTION_INFO_V1(custom_subscription);
215+
216+
Datum
217+
custom_subscription(PG_FUNCTION_ARGS)
218+
{
219+
intop_type=PG_GETARG_INT32(0);
220+
FunctionCallInfoDatatarget_fcinfo=get_slice_arguments(fcinfo,1,
221+
fcinfo->nargs);
222+
223+
if (op_type&SBS_VALIDATION)
224+
returncustom_subscription_prepare(&target_fcinfo);
225+
226+
if (op_type&SBS_EXEC)
227+
returncustom_subscription_evaluate(&target_fcinfo);
228+
229+
elog(ERROR,"incorrect op_type for subscription function: %d",op_type);
230+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp