Hello
I enhanced DO statement syntax to allowing a parameters. Syntax is
relative simple:
do ([varname] vartype := value, ...) $$ ... $$
It allows to pass a content of psql variables to inline code block to
allows more easy scripting
\set schema 'public'
do(text := :'schema') $$
declare r record;
begin
for r in
select * from information_schema.tables where table_schema = $1
loop
raise notice '>>> table %', r.table_name;
end loop;
end $$;
NOTICE: >>> table t
NOTICE: >>> table t1
DO
ToDo:
* doesn't allows SubLinks :(
pa...@postgres:5432=# do(text := (SELECT :'schema')) $$ declare r
record; begin for r in select * from information_schema.tables where
table_schema = $1 loop raise notice '>>> table %', r.table_name; end
loop; end $$;
ERROR: XX000: unrecognized node type: 315
LOCATION: ExecInitExpr, execQual.c:4868
ideas, notes, comments??
Regards
Pavel Stehule
*** ./src/backend/commands/functioncmds.c.orig 2010-02-26 03:00:39.000000000 +0100
--- ./src/backend/commands/functioncmds.c 2010-07-04 07:50:16.175265641 +0200
***************
*** 47,53 ****
--- 47,55 ----
#include "catalog/pg_type_fn.h"
#include "commands/defrem.h"
#include "commands/proclang.h"
+ #include "executor/executor.h"
#include "miscadmin.h"
+ #include "optimizer/planmain.h"
#include "optimizer/var.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
***************
*** 55,60 ****
--- 57,63 ----
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+ #include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
***************
*** 1928,1934 ****
* Execute inline procedural-language code
*/
void
! ExecuteDoStmt(DoStmt *stmt)
{
InlineCodeBlock *codeblock = makeNode(InlineCodeBlock);
ListCell *arg;
--- 1931,1937 ----
* Execute inline procedural-language code
*/
void
! ExecuteDoStmt(DoStmt *stmt, const char *queryString)
{
InlineCodeBlock *codeblock = makeNode(InlineCodeBlock);
ListCell *arg;
***************
*** 1939,1944 ****
--- 1942,1948 ----
Oid laninline;
HeapTuple languageTuple;
Form_pg_language languageStruct;
+ ParseState *pstate;
/* Process options we got from gram.y */
foreach(arg, stmt->args)
***************
*** 1973,1978 ****
--- 1977,2088 ----
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("no inline code specified")));
+ /* Transform parameters - when are used */
+ if (stmt->params != NIL)
+ {
+ ListCell *param;
+ EState *estate;
+ int nparams;
+ int i = 0;
+
+ nparams = list_length(stmt->params);
+
+ codeblock->nparams = nparams;
+ codeblock->dvalues = (Datum *) palloc(nparams * sizeof(Datum));
+ codeblock->nulls = (bool *) palloc(nparams * sizeof(bool));
+ codeblock->names = (char **) palloc(nparams * sizeof(char *));
+ codeblock->typoids = (Oid *) palloc(nparams * sizeof(Oid));
+
+ /* prepare pstate for parse analysis of param exprs */
+ pstate = make_parsestate(NULL);
+ pstate->p_sourcetext = queryString;
+
+ foreach(param, stmt->params)
+ {
+ FunctionParameter *p = (FunctionParameter *) lfirst(param);
+ Oid toid;
+ Type typtup;
+ TypeName *t = p->argType;
+ MemoryContext oldcontext;
+ ExprState *exprstate;
+ Node *expr;
+ Datum const_val;
+ bool const_is_null;
+ int16 resultTypLen;
+ bool resultTypByVal;
+
+ codeblock->names[i] = p->name;
+
+ if (t->setof)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("inline procedural code cannot accept set arguments")));
+
+ typtup = LookupTypeName(NULL, t, NULL);
+ if (typtup)
+ {
+ if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
+ ereport(NOTICE,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("argument type %s is only a shell",
+ TypeNameToString(t))));
+
+ toid = typeTypeId(typtup);
+ ReleaseSysCache(typtup);
+ codeblock->typoids[i] = toid;
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type %s does not exist",
+ TypeNameToString(t))));
+ toid = InvalidOid; /* keep compiler quiet */
+ }
+
+ estate = CreateExecutorState();
+
+ expr = transformExpr(pstate, p->defexpr);
+ expr = coerce_to_specific_type(pstate, expr,
+ toid, "DEFAULT");
+
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ fix_opfuncids(expr);
+
+ /* Prepare expr for execution */
+ exprstate = ExecPrepareExpr((Expr *) expr, estate);
+
+ /* And evaluaye it */
+ const_val = ExecEvalExprSwitchContext(exprstate,
+ GetPerTupleExprContext(estate),
+ &const_is_null, NULL);
+ /* Get info needed about result datatype */
+ get_typlenbyval(toid, &resultTypLen, &resultTypByVal);
+
+ /* Get back outer memory context */
+ MemoryContextSwitchTo(oldcontext);
+
+ if (!const_is_null)
+ const_val = datumCopy(const_val, resultTypByVal, resultTypLen);
+
+ codeblock->nulls[i] = const_is_null;
+ codeblock->dvalues[i] = const_val;
+
+ FreeExecutorState(estate);
+ i += 1;
+ }
+ }
+ else
+ {
+ codeblock->nparams = 0;
+ codeblock->dvalues = NULL;
+ codeblock->nulls = NULL;
+ codeblock->names = NULL;
+ codeblock->typoids = NULL;
+ }
+
+
/* if LANGUAGE option wasn't specified, use the default */
if (language_item)
language = strVal(language_item->arg);
*** ./src/backend/nodes/copyfuncs.c.orig 2010-02-26 03:00:43.000000000 +0100
--- ./src/backend/nodes/copyfuncs.c 2010-06-27 13:45:22.340830586 +0200
***************
*** 2680,2685 ****
--- 2680,2686 ----
DoStmt *newnode = makeNode(DoStmt);
COPY_NODE_FIELD(args);
+ COPY_NODE_FIELD(params);
return newnode;
}
*** ./src/backend/nodes/equalfuncs.c.orig 2010-02-26 03:00:43.000000000 +0100
--- ./src/backend/nodes/equalfuncs.c 2010-06-27 13:46:01.856829383 +0200
***************
*** 1242,1247 ****
--- 1242,1248 ----
_equalDoStmt(DoStmt *a, DoStmt *b)
{
COMPARE_NODE_FIELD(args);
+ COMPARE_NODE_FIELD(params);
return true;
}
*** ./src/backend/parser/gram.y.orig 2010-06-13 19:43:12.000000000 +0200
--- ./src/backend/parser/gram.y 2010-07-04 07:55:13.124266470 +0200
***************
*** 437,442 ****
--- 437,444 ----
opt_frame_clause frame_extent frame_bound
%type <str> opt_existing_window_name
+ %type <node> do_arg
+ %type <list> do_args
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
***************
*** 5445,5454 ****
*
*****************************************************************************/
! DoStmt: DO dostmt_opt_list
{
DoStmt *n = makeNode(DoStmt);
n->args = $2;
$$ = (Node *)n;
}
;
--- 5447,5465 ----
*
*****************************************************************************/
! DoStmt:
! DO '(' do_args ')' dostmt_opt_list
! {
! DoStmt *n = makeNode(DoStmt);
! n->args = $5;
! n->params = $3;
! $$ = (Node *)n;
! }
! | DO dostmt_opt_list
{
DoStmt *n = makeNode(DoStmt);
n->args = $2;
+ n->params = NIL;
$$ = (Node *)n;
}
;
***************
*** 5469,5474 ****
--- 5480,5519 ----
}
;
+ do_args:
+ do_arg
+ {
+ $$ = list_make1($1);
+ }
+ | do_args ',' do_arg
+ {
+ $$ = lappend($1, $3);
+ }
+ ;
+
+ do_arg:
+ func_type COLON_EQUALS a_expr
+ {
+ FunctionParameter *n = makeNode(FunctionParameter);
+ n->name = NULL;
+ n->argType = $1;
+ n->mode = FUNC_PARAM_IN;
+ n->defexpr = $3;
+ $$ = (Node *) n;
+ }
+ | param_name func_type COLON_EQUALS a_expr
+ {
+ FunctionParameter *n = makeNode(FunctionParameter);
+ n->name = $1;
+ n->argType = $2;
+ n->mode = FUNC_PARAM_IN;
+ n->defexpr = $4;
+ $$ = (Node *) n;
+ }
+ ;
+
+
+
/*****************************************************************************
*
* CREATE CAST / DROP CAST
*** ./src/backend/tcop/utility.c.orig 2010-02-26 03:01:04.000000000 +0100
--- ./src/backend/tcop/utility.c 2010-06-27 16:20:12.075291309 +0200
***************
*** 930,936 ****
break;
case T_DoStmt:
! ExecuteDoStmt((DoStmt *) parsetree);
break;
case T_CreatedbStmt:
--- 930,936 ----
break;
case T_DoStmt:
! ExecuteDoStmt((DoStmt *) parsetree, queryString);
break;
case T_CreatedbStmt:
*** ./src/include/commands/defrem.h.orig 2010-02-26 03:01:24.000000000 +0100
--- ./src/include/commands/defrem.h 2010-06-27 16:21:19.119051968 +0200
***************
*** 66,72 ****
extern void DropCastById(Oid castOid);
extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
const char *newschema);
! extern void ExecuteDoStmt(DoStmt *stmt);
/* commands/operatorcmds.c */
extern void DefineOperator(List *names, List *parameters);
--- 66,72 ----
extern void DropCastById(Oid castOid);
extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
const char *newschema);
! extern void ExecuteDoStmt(DoStmt *stmt, const char *queryString);
/* commands/operatorcmds.c */
extern void DefineOperator(List *names, List *parameters);
*** ./src/include/nodes/parsenodes.h.orig 2010-02-26 03:01:25.000000000 +0100
--- ./src/include/nodes/parsenodes.h 2010-07-03 20:56:28.828265897 +0200
***************
*** 1991,1996 ****
--- 1991,1997 ----
{
NodeTag type;
List *args; /* List of DefElem nodes */
+ List *params; /* List of Function parameters */
} DoStmt;
typedef struct InlineCodeBlock
***************
*** 1999,2004 ****
--- 2000,2010 ----
char *source_text; /* source text of anonymous code block */
Oid langOid; /* OID of selected language */
bool langIsTrusted; /* trusted property of the language */
+ int nparams;
+ Datum *dvalues; /* Values of parameters if they are */
+ bool *nulls; /* nulls of parameters if they are */
+ char **names; /* used names for parameters if they are */
+ Oid *typoids; /* array of parameter types if they are */
} InlineCodeBlock;
/* ----------------------
*** ./src/pl/plpgsql/src/pl_comp.c.orig 2010-02-26 03:01:34.000000000 +0100
--- ./src/pl/plpgsql/src/pl_comp.c 2010-07-04 07:58:19.441266296 +0200
***************
*** 727,733 ****
* ----------
*/
PLpgSQL_function *
! plpgsql_compile_inline(char *proc_source)
{
char *func_name = "inline_code_block";
PLpgSQL_function *function;
--- 727,733 ----
* ----------
*/
PLpgSQL_function *
! plpgsql_compile_inline(InlineCodeBlock *codeblock)
{
char *func_name = "inline_code_block";
PLpgSQL_function *function;
***************
*** 737,742 ****
--- 737,744 ----
int parse_rc;
MemoryContext func_cxt;
int i;
+ char *proc_source = codeblock->source_text;
+ int *in_arg_varnos = NULL;
/*
* Setup the scanner input and error info. We assume that this function
***************
*** 812,818 ****
plpgsql_build_datatype(BOOLOID, -1),
true);
function->found_varno = var->dno;
!
/*
* Now parse the function's text
*/
--- 814,868 ----
plpgsql_build_datatype(BOOLOID, -1),
true);
function->found_varno = var->dno;
!
! /*
! * Complete the function's info
! */
! function->fn_nargs = codeblock->nparams;
! in_arg_varnos = (int *) palloc(codeblock->nparams * sizeof(int));
!
! /*
! * Create variables for inline outer parameters
! */
! for(i = 0; i < codeblock->nparams; i++)
! {
! char buf[32];
! PLpgSQL_type *argtype;
! PLpgSQL_variable *argvariable;
! int argitemtype;
!
! /* Create $n name for variable */
! snprintf(buf, sizeof(buf), "$%d", i + 1);
!
! /* Create datatype info */
! argtype = plpgsql_build_datatype(codeblock->typoids[i], -1);
!
! /* Disallow pseudotype argument */
! if (argtype->ttype != PLPGSQL_TTYPE_SCALAR &&
! argtype->ttype != PLPGSQL_TTYPE_ROW)
! ereport(ERROR,
! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("PL/pgSQL inline code cannot accept type %s",
! format_type_be(codeblock->typoids[i]))));
!
! /* Build variable and add to datum list */
! argvariable = plpgsql_build_variable(buf, 0,
! argtype, false);
! if (argvariable->dtype == PLPGSQL_DTYPE_ROW)
! argitemtype = PLPGSQL_NSTYPE_VAR;
! else
! argitemtype = PLPGSQL_NSTYPE_ROW;
!
! in_arg_varnos[i] = argvariable->dno;
!
! /* Add to namespace */
! plpgsql_ns_additem(argitemtype, argvariable->dno, buf);
!
! if (codeblock->names[i] != NULL)
! plpgsql_ns_additem(argitemtype, argvariable->dno,
! codeblock->names[i]);
! }
!
/*
* Now parse the function's text
*/
***************
*** 830,839 ****
if (function->fn_rettype == VOIDOID)
add_dummy_return(function);
! /*
! * Complete the function's info
! */
! function->fn_nargs = 0;
function->ndatums = plpgsql_nDatums;
function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
for (i = 0; i < plpgsql_nDatums; i++)
--- 880,888 ----
if (function->fn_rettype == VOIDOID)
add_dummy_return(function);
! for (i = 0; i < function->fn_nargs; i++)
! function->fn_argvarnos[i] = in_arg_varnos[i];
!
function->ndatums = plpgsql_nDatums;
function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
for (i = 0; i < plpgsql_nDatums; i++)
***************
*** 849,854 ****
--- 898,904 ----
MemoryContextSwitchTo(compile_tmp_cxt);
compile_tmp_cxt = NULL;
+
return function;
}
*** ./src/pl/plpgsql/src/pl_handler.c.orig 2010-02-26 03:01:35.000000000 +0100
--- ./src/pl/plpgsql/src/pl_handler.c 2010-07-04 07:27:03.842263509 +0200
***************
*** 160,165 ****
--- 160,166 ----
FmgrInfo flinfo;
Datum retval;
int rc;
+ int i;
Assert(IsA(codeblock, InlineCodeBlock));
***************
*** 170,183 ****
elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
/* Compile the anonymous code block */
! func = plpgsql_compile_inline(codeblock->source_text);
/*
* Set up a fake fcinfo with just enough info to satisfy
* plpgsql_exec_function(). In particular note that this sets things up
* with no arguments passed.
*/
! MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
MemSet(&flinfo, 0, sizeof(flinfo));
fake_fcinfo.flinfo = &flinfo;
flinfo.fn_oid = InvalidOid;
--- 171,191 ----
elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
/* Compile the anonymous code block */
! func = plpgsql_compile_inline(codeblock);
/*
* Set up a fake fcinfo with just enough info to satisfy
* plpgsql_exec_function(). In particular note that this sets things up
* with no arguments passed.
*/
! InitFunctionCallInfoData(fake_fcinfo, &flinfo, codeblock->nparams, NULL, NULL);
!
! for (i = 0; i < codeblock->nparams; i++)
! {
! fake_fcinfo.arg[i] = codeblock->dvalues[i];
! fake_fcinfo.argnull[i] = codeblock->nulls[i];
! }
!
MemSet(&flinfo, 0, sizeof(flinfo));
fake_fcinfo.flinfo = &flinfo;
flinfo.fn_oid = InvalidOid;
*** ./src/pl/plpgsql/src/plpgsql.h.orig 2010-02-26 03:01:35.000000000 +0100
--- ./src/pl/plpgsql/src/plpgsql.h 2010-07-03 20:49:40.992266524 +0200
***************
*** 832,838 ****
*/
extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
bool forValidator);
! extern PLpgSQL_function *plpgsql_compile_inline(char *proc_source);
extern void plpgsql_parser_setup(struct ParseState *pstate,
PLpgSQL_expr *expr);
extern bool plpgsql_parse_word(char *word1, const char *yytxt,
--- 832,838 ----
*/
extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
bool forValidator);
! extern PLpgSQL_function *plpgsql_compile_inline(InlineCodeBlock *codeblock);
extern void plpgsql_parser_setup(struct ParseState *pstate,
PLpgSQL_expr *expr);
extern bool plpgsql_parse_word(char *word1, const char *yytxt,
*** ./src/test/regress/expected/plpgsql.out.orig 2010-06-25 18:40:13.000000000 +0200
--- ./src/test/regress/expected/plpgsql.out 2010-07-04 08:21:55.000000000 +0200
***************
*** 4121,4123 ****
--- 4121,4141 ----
(1 row)
drop function unreserved_test();
+ -- Inline code parametrization
+ do (int := 11, int := 22) $$
+ begin
+ raise notice '%', $1 + $2;
+ end $$;
+ NOTICE: 33
+ do (a int := 11, b int := 22) $$
+ begin
+ raise notice '%', a + b;
+ end $$;
+ NOTICE: 33
+ \set myvar1 'Hello'
+ \set myvar2 'World'
+ do (msg1 text := :'myvar1', msg2 text := :'myvar2') $$
+ begin
+ raise notice '% %', msg1, msg2;
+ end $$;
+ NOTICE: Hello World
*** ./src/test/regress/sql/plpgsql.sql.orig 2010-06-25 18:40:13.000000000 +0200
--- ./src/test/regress/sql/plpgsql.sql 2010-07-04 08:20:42.244266106 +0200
***************
*** 3268,3270 ****
--- 3268,3289 ----
select unreserved_test();
drop function unreserved_test();
+
+ -- Inline code parametrization
+ do (int := 11, int := 22) $$
+ begin
+ raise notice '%', $1 + $2;
+ end $$;
+
+ do (a int := 11, b int := 22) $$
+ begin
+ raise notice '%', a + b;
+ end $$;
+
+ \set myvar1 'Hello'
+ \set myvar2 'World'
+ do (msg1 text := :'myvar1', msg2 text := :'myvar2') $$
+ begin
+ raise notice '% %', msg1, msg2;
+ end $$;
+
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers