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

Commit820c030

Browse files
committed
Support domains over composite types in PL/Tcl.
Since PL/Tcl does little with SQL types internally, this is just amatter of making it work with composite-domain function argumentsand results.In passing, make it allow RECORD-type arguments --- that's a trivialchange that nobody had bothered with up to now.Discussion:https://postgr.es/m/4206.1499798337@sss.pgh.pa.us
1 parent37a795a commit820c030

File tree

3 files changed

+193
-26
lines changed

3 files changed

+193
-26
lines changed

‎src/pl/tcl/expected/pltcl_queries.out

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,46 @@ select tcl_composite_arg_ref2(row('tkey', 42, 'ref2'));
327327
ref2
328328
(1 row)
329329

330+
-- More tests for composite argument/result types
331+
create domain d_dta1 as T_dta1 check ((value).ref1 > 0);
332+
create function tcl_record_arg(record, fldname text) returns int as '
333+
return $1($2)
334+
' language pltcl;
335+
select tcl_record_arg(row('tkey', 42, 'ref2')::T_dta1, 'ref1');
336+
tcl_record_arg
337+
----------------
338+
42
339+
(1 row)
340+
341+
select tcl_record_arg(row('tkey', 42, 'ref2')::d_dta1, 'ref1');
342+
tcl_record_arg
343+
----------------
344+
42
345+
(1 row)
346+
347+
select tcl_record_arg(row(2,4), 'f2');
348+
tcl_record_arg
349+
----------------
350+
4
351+
(1 row)
352+
353+
create function tcl_cdomain_arg(d_dta1) returns int as '
354+
return $1(ref1)
355+
' language pltcl;
356+
select tcl_cdomain_arg(row('tkey', 42, 'ref2'));
357+
tcl_cdomain_arg
358+
-----------------
359+
42
360+
(1 row)
361+
362+
select tcl_cdomain_arg(row('tkey', 42, 'ref2')::T_dta1);
363+
tcl_cdomain_arg
364+
-----------------
365+
42
366+
(1 row)
367+
368+
select tcl_cdomain_arg(row('tkey', -1, 'ref2')); -- fail
369+
ERROR: value for domain d_dta1 violates check constraint "d_dta1_check"
330370
-- Test argisnull primitive
331371
select tcl_argisnull('foo');
332372
tcl_argisnull
@@ -438,6 +478,60 @@ return_next [list a 1 b 2 cow 3]
438478
$$ language pltcl;
439479
select bad_field_srf();
440480
ERROR: column name/value list contains nonexistent column name "cow"
481+
-- test composite and domain-over-composite results
482+
create function tcl_composite_result(int) returns T_dta1 as $$
483+
return [list tkey tkey1 ref1 $1 ref2 ref22]
484+
$$ language pltcl;
485+
select tcl_composite_result(1001);
486+
tcl_composite_result
487+
--------------------------------------------
488+
("tkey1 ",1001,"ref22 ")
489+
(1 row)
490+
491+
select * from tcl_composite_result(1002);
492+
tkey | ref1 | ref2
493+
------------+------+----------------------
494+
tkey1 | 1002 | ref22
495+
(1 row)
496+
497+
create function tcl_dcomposite_result(int) returns d_dta1 as $$
498+
return [list tkey tkey2 ref1 $1 ref2 ref42]
499+
$$ language pltcl;
500+
select tcl_dcomposite_result(1001);
501+
tcl_dcomposite_result
502+
--------------------------------------------
503+
("tkey2 ",1001,"ref42 ")
504+
(1 row)
505+
506+
select * from tcl_dcomposite_result(1002);
507+
tkey | ref1 | ref2
508+
------------+------+----------------------
509+
tkey2 | 1002 | ref42
510+
(1 row)
511+
512+
select * from tcl_dcomposite_result(-1); -- fail
513+
ERROR: value for domain d_dta1 violates check constraint "d_dta1_check"
514+
create function tcl_record_result(int) returns record as $$
515+
return [list q1 sometext q2 $1 q3 moretext]
516+
$$ language pltcl;
517+
select tcl_record_result(42); -- fail
518+
ERROR: function returning record called in context that cannot accept type record
519+
select * from tcl_record_result(42); -- fail
520+
ERROR: a column definition list is required for functions returning "record" at character 15
521+
select * from tcl_record_result(42) as (q1 text, q2 int, q3 text);
522+
q1 | q2 | q3
523+
----------+----+----------
524+
sometext | 42 | moretext
525+
(1 row)
526+
527+
select * from tcl_record_result(42) as (q1 text, q2 int, q3 text, q4 int);
528+
q1 | q2 | q3 | q4
529+
----------+----+----------+----
530+
sometext | 42 | moretext |
531+
(1 row)
532+
533+
select * from tcl_record_result(42) as (q1 text, q2 int, q4 int); -- fail
534+
ERROR: column name/value list contains nonexistent column name "q3"
441535
-- test quote
442536
select tcl_eval('quote foo bar');
443537
ERROR: wrong # args: should be "quote string"

‎src/pl/tcl/pltcl.c

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,13 @@ typedef struct pltcl_proc_desc
143143
boolfn_readonly;/* is function readonly? */
144144
boollanpltrusted;/* is it pltcl (vs. pltclu)? */
145145
pltcl_interp_desc*interp_desc;/* interpreter to use */
146+
Oidresult_typid;/* OID of fn's result type */
146147
FmgrInforesult_in_func;/* input function for fn's result type */
147148
Oidresult_typioparam;/* param to pass to same */
148149
boolfn_retisset;/* true if function returns a set */
149150
boolfn_retistuple;/* true if function returns composite */
151+
boolfn_retisdomain;/* true if function returns domain */
152+
void*domain_info;/* opaque cache for domain checks */
150153
intnargs;/* number of arguments */
151154
/* these arrays have nargs entries: */
152155
FmgrInfo*arg_out_func;/* output fns for arg types */
@@ -988,11 +991,26 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
988991
* result type is a named composite type, so it's not exactly trivial.
989992
* Maybe worth improving someday.
990993
*/
991-
if (get_call_result_type(fcinfo,NULL,&td)!=TYPEFUNC_COMPOSITE)
992-
ereport(ERROR,
993-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
994-
errmsg("function returning record called in context "
995-
"that cannot accept type record")));
994+
switch (get_call_result_type(fcinfo,NULL,&td))
995+
{
996+
caseTYPEFUNC_COMPOSITE:
997+
/* success */
998+
break;
999+
caseTYPEFUNC_COMPOSITE_DOMAIN:
1000+
Assert(prodesc->fn_retisdomain);
1001+
break;
1002+
caseTYPEFUNC_RECORD:
1003+
/* failed to determine actual type of RECORD */
1004+
ereport(ERROR,
1005+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1006+
errmsg("function returning record called in context "
1007+
"that cannot accept type record")));
1008+
break;
1009+
default:
1010+
/* result type isn't composite? */
1011+
elog(ERROR,"return type must be a row type");
1012+
break;
1013+
}
9961014

9971015
Assert(!call_state->ret_tupdesc);
9981016
Assert(!call_state->attinmeta);
@@ -1490,40 +1508,41 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
14901508
************************************************************/
14911509
if (!is_trigger&& !is_event_trigger)
14921510
{
1493-
typeTup=
1494-
SearchSysCache1(TYPEOID,
1495-
ObjectIdGetDatum(procStruct->prorettype));
1511+
Oidrettype=procStruct->prorettype;
1512+
1513+
typeTup=SearchSysCache1(TYPEOID,ObjectIdGetDatum(rettype));
14961514
if (!HeapTupleIsValid(typeTup))
1497-
elog(ERROR,"cache lookup failed for type %u",
1498-
procStruct->prorettype);
1515+
elog(ERROR,"cache lookup failed for type %u",rettype);
14991516
typeStruct= (Form_pg_type)GETSTRUCT(typeTup);
15001517

15011518
/* Disallow pseudotype result, except VOID and RECORD */
15021519
if (typeStruct->typtype==TYPTYPE_PSEUDO)
15031520
{
1504-
if (procStruct->prorettype==VOIDOID||
1505-
procStruct->prorettype==RECORDOID)
1521+
if (rettype==VOIDOID||
1522+
rettype==RECORDOID)
15061523
/* okay */ ;
1507-
elseif (procStruct->prorettype==TRIGGEROID||
1508-
procStruct->prorettype==EVTTRIGGEROID)
1524+
elseif (rettype==TRIGGEROID||
1525+
rettype==EVTTRIGGEROID)
15091526
ereport(ERROR,
15101527
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15111528
errmsg("trigger functions can only be called as triggers")));
15121529
else
15131530
ereport(ERROR,
15141531
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15151532
errmsg("PL/Tcl functions cannot return type %s",
1516-
format_type_be(procStruct->prorettype))));
1533+
format_type_be(rettype))));
15171534
}
15181535

1536+
prodesc->result_typid=rettype;
15191537
fmgr_info_cxt(typeStruct->typinput,
15201538
&(prodesc->result_in_func),
15211539
proc_cxt);
15221540
prodesc->result_typioparam=getTypeIOParam(typeTup);
15231541

15241542
prodesc->fn_retisset=procStruct->proretset;
1525-
prodesc->fn_retistuple= (procStruct->prorettype==RECORDOID||
1526-
typeStruct->typtype==TYPTYPE_COMPOSITE);
1543+
prodesc->fn_retistuple=type_is_rowtype(rettype);
1544+
prodesc->fn_retisdomain= (typeStruct->typtype==TYPTYPE_DOMAIN);
1545+
prodesc->domain_info=NULL;
15271546

15281547
ReleaseSysCache(typeTup);
15291548
}
@@ -1537,21 +1556,22 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
15371556
proc_internal_args[0]='\0';
15381557
for (i=0;i<prodesc->nargs;i++)
15391558
{
1540-
typeTup=SearchSysCache1(TYPEOID,
1541-
ObjectIdGetDatum(procStruct->proargtypes.values[i]));
1559+
Oidargtype=procStruct->proargtypes.values[i];
1560+
1561+
typeTup=SearchSysCache1(TYPEOID,ObjectIdGetDatum(argtype));
15421562
if (!HeapTupleIsValid(typeTup))
1543-
elog(ERROR,"cache lookup failed for type %u",
1544-
procStruct->proargtypes.values[i]);
1563+
elog(ERROR,"cache lookup failed for type %u",argtype);
15451564
typeStruct= (Form_pg_type)GETSTRUCT(typeTup);
15461565

1547-
/* Disallow pseudotype argument */
1548-
if (typeStruct->typtype==TYPTYPE_PSEUDO)
1566+
/* Disallow pseudotype argument, except RECORD */
1567+
if (typeStruct->typtype==TYPTYPE_PSEUDO&&
1568+
argtype!=RECORDOID)
15491569
ereport(ERROR,
15501570
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15511571
errmsg("PL/Tcl functions cannot accept type %s",
1552-
format_type_be(procStruct->proargtypes.values[i]))));
1572+
format_type_be(argtype))));
15531573

1554-
if (typeStruct->typtype==TYPTYPE_COMPOSITE)
1574+
if (type_is_rowtype(argtype))
15551575
{
15561576
prodesc->arg_is_rowtype[i]= true;
15571577
snprintf(buf,sizeof(buf),"__PLTcl_Tup_%d",i+1);
@@ -3075,6 +3095,7 @@ static HeapTuple
30753095
pltcl_build_tuple_result(Tcl_Interp*interp,Tcl_Obj**kvObjv,intkvObjc,
30763096
pltcl_call_state*call_state)
30773097
{
3098+
HeapTupletuple;
30783099
TupleDesctupdesc;
30793100
AttInMetadata*attinmeta;
30803101
char**values;
@@ -3133,7 +3154,16 @@ pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc,
31333154
values[attn-1]=utf_u2e(Tcl_GetString(kvObjv[i+1]));
31343155
}
31353156

3136-
returnBuildTupleFromCStrings(attinmeta,values);
3157+
tuple=BuildTupleFromCStrings(attinmeta,values);
3158+
3159+
/* if result type is domain-over-composite, check domain constraints */
3160+
if (call_state->prodesc->fn_retisdomain)
3161+
domain_check(HeapTupleGetDatum(tuple), false,
3162+
call_state->prodesc->result_typid,
3163+
&call_state->prodesc->domain_info,
3164+
call_state->prodesc->fn_cxt);
3165+
3166+
returntuple;
31373167
}
31383168

31393169
/**********************************************************************

‎src/pl/tcl/sql/pltcl_queries.sql

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,26 @@ truncate trigger_test;
8989
select tcl_composite_arg_ref1(row('tkey',42,'ref2'));
9090
select tcl_composite_arg_ref2(row('tkey',42,'ref2'));
9191

92+
-- More tests for composite argument/result types
93+
94+
createdomaind_dta1as T_dta1check ((value).ref1>0);
95+
96+
createfunctiontcl_record_arg(record, fldnametext) returnsintas'
97+
return $1($2)
98+
' language pltcl;
99+
100+
select tcl_record_arg(row('tkey',42,'ref2')::T_dta1,'ref1');
101+
select tcl_record_arg(row('tkey',42,'ref2')::d_dta1,'ref1');
102+
select tcl_record_arg(row(2,4),'f2');
103+
104+
createfunctiontcl_cdomain_arg(d_dta1) returnsintas'
105+
return $1(ref1)
106+
' language pltcl;
107+
108+
select tcl_cdomain_arg(row('tkey',42,'ref2'));
109+
select tcl_cdomain_arg(row('tkey',42,'ref2')::T_dta1);
110+
select tcl_cdomain_arg(row('tkey',-1,'ref2'));-- fail
111+
92112
-- Test argisnull primitive
93113
select tcl_argisnull('foo');
94114
select tcl_argisnull('');
@@ -136,6 +156,29 @@ return_next [list a 1 b 2 cow 3]
136156
$$ language pltcl;
137157
select bad_field_srf();
138158

159+
-- test composite and domain-over-composite results
160+
createfunctiontcl_composite_result(int) returns T_dta1as $$
161+
return [list tkey tkey1 ref1 $1 ref2 ref22]
162+
$$ language pltcl;
163+
select tcl_composite_result(1001);
164+
select*from tcl_composite_result(1002);
165+
166+
createfunctiontcl_dcomposite_result(int) returns d_dta1as $$
167+
return [list tkey tkey2 ref1 $1 ref2 ref42]
168+
$$ language pltcl;
169+
select tcl_dcomposite_result(1001);
170+
select*from tcl_dcomposite_result(1002);
171+
select*from tcl_dcomposite_result(-1);-- fail
172+
173+
createfunctiontcl_record_result(int) returns recordas $$
174+
return [list q1 sometext q2 $1 q3 moretext]
175+
$$ language pltcl;
176+
select tcl_record_result(42);-- fail
177+
select*from tcl_record_result(42);-- fail
178+
select*from tcl_record_result(42)as (q1text, q2int, q3text);
179+
select*from tcl_record_result(42)as (q1text, q2int, q3text, q4int);
180+
select*from tcl_record_result(42)as (q1text, q2int, q4int);-- fail
181+
139182
-- test quote
140183
select tcl_eval('quote foo bar');
141184
select tcl_eval('quote [format %c 39]');

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp