Created
March 25, 2011 18:39
-
-
Save matthewd/887353 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c | |
new file mode 100644 | |
index ce3b77b..be71fbb | |
*** a/src/backend/executor/functions.c | |
--- b/src/backend/executor/functions.c | |
*************** typedef SQLFunctionCache *SQLFunctionCac | |
*** 116,122 **** | |
--- 116,124 ---- | |
*/ | |
typedef struct SQLFunctionParseInfo | |
{ | |
+ char *name; /* function's name */ | |
Oid *argtypes; /* resolved types of input arguments */ | |
+ char **argnames; /* names of input arguments */ | |
int nargs; /* number of input arguments */ | |
Oid collation; /* function's input collation, if known */ | |
} SQLFunctionParseInfo; | |
*************** typedef struct SQLFunctionParseInfo | |
*** 124,129 **** | |
--- 126,133 ---- | |
/* non-export function prototypes */ | |
static Node *sql_fn_param_ref(ParseState *pstate, ParamRef *pref); | |
+ static Node *sql_fn_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var); | |
+ static Node *sql_fn_param_ref_num(ParseState *pstate, SQLFunctionParseInfoPtr pinfo, int paramno, int location); | |
static List *init_execution_state(List *queryTree_list, | |
SQLFunctionCachePtr fcache, | |
bool lazyEvalOK); | |
*************** prepare_sql_fn_parse_info(HeapTuple proc | |
*** 163,168 **** | |
--- 167,173 ---- | |
int nargs; | |
pinfo = (SQLFunctionParseInfoPtr) palloc0(sizeof(SQLFunctionParseInfo)); | |
+ pinfo->name = NameStr(procedureStruct->proname); | |
/* Save the function's input collation */ | |
pinfo->collation = inputCollation; | |
*************** prepare_sql_fn_parse_info(HeapTuple proc | |
*** 201,206 **** | |
--- 206,241 ---- | |
pinfo->argtypes = argOidVect; | |
} | |
+ if (nargs > 0) | |
+ { | |
+ Datum proargnames; | |
+ Datum proargmodes; | |
+ int argnum; | |
+ int n_arg_names; | |
+ bool isNull; | |
+ | |
+ proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, procedureTuple, | |
+ Anum_pg_proc_proargnames, | |
+ &isNull); | |
+ if (isNull) | |
+ proargmodes = PointerGetDatum(NULL); /* just to be sure */ | |
+ | |
+ proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, procedureTuple, | |
+ Anum_pg_proc_proargmodes, | |
+ &isNull); | |
+ if (isNull) | |
+ proargmodes = PointerGetDatum(NULL); /* just to be sure */ | |
+ | |
+ n_arg_names = get_func_input_arg_names(proargnames, proargmodes, &pinfo->argnames); | |
+ | |
+ if (n_arg_names < nargs) | |
+ pinfo->argnames = NULL; | |
+ } | |
+ else | |
+ { | |
+ pinfo->argnames = NULL; | |
+ } | |
+ | |
return pinfo; | |
} | |
*************** prepare_sql_fn_parse_info(HeapTuple proc | |
*** 210,223 **** | |
void | |
sql_fn_parser_setup(struct ParseState *pstate, SQLFunctionParseInfoPtr pinfo) | |
{ | |
- /* Later we might use these hooks to support parameter names */ | |
pstate->p_pre_columnref_hook = NULL; | |
! pstate->p_post_columnref_hook = NULL; | |
pstate->p_paramref_hook = sql_fn_param_ref; | |
/* no need to use p_coerce_param_hook */ | |
pstate->p_ref_hook_state = (void *) pinfo; | |
} | |
/* | |
* sql_fn_param_ref parser callback for ParamRefs ($n symbols) | |
*/ | |
--- 245,354 ---- | |
void | |
sql_fn_parser_setup(struct ParseState *pstate, SQLFunctionParseInfoPtr pinfo) | |
{ | |
pstate->p_pre_columnref_hook = NULL; | |
! pstate->p_post_columnref_hook = sql_fn_post_column_ref; | |
pstate->p_paramref_hook = sql_fn_param_ref; | |
/* no need to use p_coerce_param_hook */ | |
pstate->p_ref_hook_state = (void *) pinfo; | |
} | |
+ static Node * | |
+ sql_fn_resolve_name(ParseState *pstate, SQLFunctionParseInfoPtr pinfo, const char *paramname, int location) | |
+ { | |
+ int i; | |
+ for (i = 0; i < pinfo->nargs; i++) | |
+ if (pinfo->argnames[i] && strcmp(pinfo->argnames[i], paramname) == 0) | |
+ return sql_fn_param_ref_num(pstate, pinfo, i + 1, location); | |
+ | |
+ return NULL; | |
+ } | |
+ | |
+ /* | |
+ * sql_fn_post_column_ref parser callback for ColumnRefs | |
+ */ | |
+ static Node * | |
+ sql_fn_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var) | |
+ { | |
+ SQLFunctionParseInfoPtr pinfo = (SQLFunctionParseInfoPtr) pstate->p_ref_hook_state; | |
+ int names; | |
+ Node *field1; | |
+ Node *subfield = NULL; | |
+ const char *pname; | |
+ Node *param; | |
+ | |
+ if (var != NULL) | |
+ return NULL; /* there's a table column, prefer that */ | |
+ | |
+ /* | |
+ * The allowed syntaxes are: | |
+ * | |
+ * A A = parameter name | |
+ * A.B A = (record-typed) parameter name, B = field reference, | |
+ * OR A = function name, B = parameter name | |
+ * A.B.C A = function name, B = (record-typed) parameter name, | |
+ * C = field reference | |
+ */ | |
+ names = list_length(cref->fields); | |
+ | |
+ if (names > 3) | |
+ return NULL; | |
+ | |
+ field1 = (Node *) linitial(cref->fields); | |
+ if (names > 1) | |
+ subfield = (Node *) lsecond(cref->fields); | |
+ Assert(IsA(field1, String)); | |
+ pname = strVal(field1); | |
+ | |
+ if (names == 3) | |
+ { | |
+ /* | |
+ * Function-qualified reference: if the first name doesn't match | |
+ * the function, we can fail immediately. Otherwise, discard the | |
+ * first name, and continue. | |
+ */ | |
+ if (strcmp(pname, pinfo->name) != 0) | |
+ return NULL; | |
+ | |
+ Assert(IsA(subfield, String)); | |
+ pname = strVal(subfield); | |
+ param = sql_fn_resolve_name(pstate, pinfo, pname, cref->location); | |
+ subfield = (Node *) lthird(cref->fields); | |
+ } | |
+ else | |
+ { | |
+ param = sql_fn_resolve_name(pstate, pinfo, pname, cref->location); | |
+ | |
+ if (!param && names == 2 && strcmp(pname, pinfo->name) == 0) | |
+ { | |
+ /* | |
+ * We have a two-part name, the first part matches the name | |
+ * of our containing function, and did not match a | |
+ * parameter; discard the first name, and try again. | |
+ */ | |
+ Assert(IsA(subfield, String)); | |
+ pname = strVal(subfield); | |
+ param = sql_fn_resolve_name(pstate, pinfo, pname, cref->location); | |
+ subfield = NULL; | |
+ } | |
+ } | |
+ | |
+ if (!param) | |
+ return NULL; | |
+ | |
+ if (subfield) | |
+ { | |
+ Assert(IsA(subfield, String)); | |
+ | |
+ param = ParseFuncOrColumn(pstate, | |
+ list_make1(subfield), | |
+ list_make1(param), | |
+ NIL, false, false, false, | |
+ NULL, true, cref->location); | |
+ } | |
+ | |
+ return param; | |
+ } | |
+ | |
/* | |
* sql_fn_param_ref parser callback for ParamRefs ($n symbols) | |
*/ | |
*************** sql_fn_param_ref(ParseState *pstate, Par | |
*** 226,231 **** | |
--- 357,372 ---- | |
{ | |
SQLFunctionParseInfoPtr pinfo = (SQLFunctionParseInfoPtr) pstate->p_ref_hook_state; | |
int paramno = pref->number; | |
+ | |
+ return sql_fn_param_ref_num(pstate, pinfo, paramno, pref->location); | |
+ } | |
+ | |
+ /* | |
+ * sql_fn_param_ref_num construct a Param node for the given paramno | |
+ */ | |
+ static Node * | |
+ sql_fn_param_ref_num(ParseState *pstate, SQLFunctionParseInfoPtr pinfo, int paramno, int location) | |
+ { | |
Param *param; | |
/* Check parameter number is valid */ | |
*************** sql_fn_param_ref(ParseState *pstate, Par | |
*** 238,244 **** | |
param->paramtype = pinfo->argtypes[paramno - 1]; | |
param->paramtypmod = -1; | |
param->paramcollid = get_typcollation(param->paramtype); | |
! param->location = pref->location; | |
/* | |
* If we have a function input collation, allow it to override the | |
--- 379,385 ---- | |
param->paramtype = pinfo->argtypes[paramno - 1]; | |
param->paramtypmod = -1; | |
param->paramcollid = get_typcollation(param->paramtype); | |
! param->location = location; | |
/* | |
* If we have a function input collation, allow it to override the | |
diff --git a/src/test/regress/input/create_function_2.source b/src/test/regress/input/create_function_2.source | |
new file mode 100644 | |
index 6aed5f0..3207870 | |
*** a/src/test/regress/input/create_function_2.source | |
--- b/src/test/regress/input/create_function_2.source | |
*************** CREATE FUNCTION hobby_construct(text, te | |
*** 13,18 **** | |
--- 13,24 ---- | |
LANGUAGE SQL; | |
+ CREATE FUNCTION hobby_construct_named(name text, hobby text) | |
+ RETURNS hobbies_r | |
+ AS 'select name, hobby' | |
+ LANGUAGE SQL; | |
+ | |
+ | |
CREATE FUNCTION hobbies_by_name(hobbies_r.name%TYPE) | |
RETURNS hobbies_r.person%TYPE | |
AS 'select person from hobbies_r where name = $1' | |
*************** CREATE FUNCTION equipment(hobbies_r) | |
*** 25,30 **** | |
--- 31,42 ---- | |
LANGUAGE SQL; | |
+ CREATE FUNCTION equipment_named(hobby hobbies_r) | |
+ RETURNS setof equipment_r | |
+ AS 'select * from equipment_r where equipment_r.hobby = equipment_named.hobby.name' | |
+ LANGUAGE SQL; | |
+ | |
+ | |
CREATE FUNCTION user_relns() | |
RETURNS setof name | |
AS 'select relname | |
diff --git a/src/test/regress/input/misc.source b/src/test/regress/input/misc.source | |
new file mode 100644 | |
index 0930a6a..958db67 | |
*** a/src/test/regress/input/misc.source | |
--- b/src/test/regress/input/misc.source | |
*************** SELECT user_relns() AS user_relns | |
*** 217,222 **** | |
--- 217,226 ---- | |
SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))); | |
+ SELECT name(equipment(hobby_construct_named(text 'skywalking', text 'mer'))); | |
+ | |
+ SELECT name(equipment_named(hobby_construct_named(text 'skywalking', text 'mer'))); | |
+ | |
SELECT hobbies_by_name('basketball'); | |
SELECT name, overpaid(emp.*) FROM emp; | |
diff --git a/src/test/regress/output/create_function_2.source b/src/test/regress/output/create_function_2.source | |
new file mode 100644 | |
index 94ab7eb..e7ef281 | |
*** a/src/test/regress/output/create_function_2.source | |
--- b/src/test/regress/output/create_function_2.source | |
*************** CREATE FUNCTION hobby_construct(text, te | |
*** 9,14 **** | |
--- 9,18 ---- | |
RETURNS hobbies_r | |
AS 'select $1 as name, $2 as hobby' | |
LANGUAGE SQL; | |
+ CREATE FUNCTION hobby_construct_named(name text, hobby text) | |
+ RETURNS hobbies_r | |
+ AS 'select name, hobby' | |
+ LANGUAGE SQL; | |
CREATE FUNCTION hobbies_by_name(hobbies_r.name%TYPE) | |
RETURNS hobbies_r.person%TYPE | |
AS 'select person from hobbies_r where name = $1' | |
*************** CREATE FUNCTION equipment(hobbies_r) | |
*** 19,24 **** | |
--- 23,32 ---- | |
RETURNS setof equipment_r | |
AS 'select * from equipment_r where hobby = $1.name' | |
LANGUAGE SQL; | |
+ CREATE FUNCTION equipment_named(hobby hobbies_r) | |
+ RETURNS setof equipment_r | |
+ AS 'select * from equipment_r where equipment_r.hobby = equipment_named.hobby.name' | |
+ LANGUAGE SQL; | |
CREATE FUNCTION user_relns() | |
RETURNS setof name | |
AS 'select relname | |
diff --git a/src/test/regress/output/misc.source b/src/test/regress/output/misc.source | |
new file mode 100644 | |
index c225d0f..e1312e6 | |
*** a/src/test/regress/output/misc.source | |
--- b/src/test/regress/output/misc.source | |
*************** SELECT name(equipment(hobby_construct(te | |
*** 677,682 **** | |
--- 677,694 ---- | |
guts | |
(1 row) | |
+ SELECT name(equipment(hobby_construct_named(text 'skywalking', text 'mer'))); | |
+ name | |
+ ------ | |
+ guts | |
+ (1 row) | |
+ | |
+ SELECT name(equipment_named(hobby_construct_named(text 'skywalking', text 'mer'))); | |
+ name | |
+ ------ | |
+ guts | |
+ (1 row) | |
+ | |
SELECT hobbies_by_name('basketball'); | |
hobbies_by_name | |
----------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment