Re: delayed planning of unnamed statements - Mailing list pgsql-patches
From | Bruce Momjian |
---|---|
Subject | Re: delayed planning of unnamed statements |
Date | |
Msg-id | 200406110116.i5B1GjZ22179@candle.pha.pa.us Whole thread Raw |
In response to | delayed planning of unnamed statements (Oliver Jowett <oliver@opencloud.com>) |
List | pgsql-patches |
Applied by Tom. Thanks. --------------------------------------------------------------------------- Oliver Jowett wrote: > Per discussion on -hackers > (http://archives.postgresql.org/pgsql-hackers/2004-05/msg00990.php), > here is a first cut at delaying query planning for unnamed statements > until we have concrete parameters (i.e. at Bind). > > This passes 'make check' (not particularly informative since that > doesn't exercise the extended query protocol path). It also passes the > JDBC driver's regression tests when using a modified driver that uses > the extended query protocol and parameterized queries. > > Also attached is a python script that talks the V3 protocol directly and > does some simple tests of various bind/rebind cases with named and > unnamed statements (beware, that code is very ad-hoc!). > > With these changes the JDBC driver gets performance similar to the > unparameterized case when using the unnamed statement: > > > FE=> Parse(stmt=null,query="SELECT count(*) FROM test_big WHERE value = 2",oids={}) > > FE=> Bind(stmt=null,portal=null) > > FE=> Describe(portal=null) > > FE=> Execute(portal=null,limit=0) > > FE=> Sync > > <=BE ParseComplete [null] > > <=BE BindComplete [null] > > <=BE RowDescription(1) > > <=BE DataRow > > <=BE CommandStatus(SELECT) > > <=BE ReadyForQuery(I) > > <<<<< END > > Elapsed: 54ms > > Result: 1 > > > FE=> Parse(stmt=S_1,query="SELECT count(*) FROM test_big WHERE value = $1",oids={23}) > > FE=> Bind(stmt=S_1,portal=null,$1=<2>) > > FE=> Describe(portal=null) > > FE=> Execute(portal=null,limit=0) > > FE=> Sync > > <=BE ParseComplete [S_1] > > <=BE BindComplete [null] > > <=BE RowDescription(1) > > <=BE DataRow > > <=BE CommandStatus(SELECT) > > <=BE ReadyForQuery(I) > > <<<<< END > > Elapsed: 1484ms > > Result: 1 > > > FE=> Parse(stmt=null,query="SELECT count(*) FROM test_big WHERE value = $1",oids={23}) > > FE=> Bind(stmt=null,portal=null,$1=<2>) > > FE=> Describe(portal=null) > > FE=> Execute(portal=null,limit=0) > > FE=> Sync > > <=BE ParseComplete [null] > > <=BE BindComplete [null] > > <=BE RowDescription(1) > > <=BE DataRow > > <=BE CommandStatus(SELECT) > > <=BE ReadyForQuery(I) > > <<<<< END > > Elapsed: 65ms > > Result: 1 > > -O > Index: doc/src/sgml/protocol.sgml > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/protocol.sgml,v > retrieving revision 1.51 > diff -u -c -r1.51 protocol.sgml > *** doc/src/sgml/protocol.sgml 21 Mar 2004 22:29:10 -0000 1.51 > --- doc/src/sgml/protocol.sgml 24 May 2004 14:42:10 -0000 > *************** > *** 663,668 **** > --- 663,688 ---- > </para> > > <para> > + Query planning of named prepared-statement objects occurs when the Parse > + message is received. If a query will be repeatedly executed with > + different parameters, it may be beneficial to send a single Parse message > + containing a parameterized query, followed by multiple Bind > + and Execute messages. This will avoid replanning the query on each > + execution. > + </para> > + > + <note> > + <para> > + Query plans generated from a parameterized query may be less > + efficient than query plans generated from an equivalent query with actual > + parameter values substituted. The query planner cannot make decisions > + based on actual parameter values (for example, index selectivity) when > + planning a parameterized query assigned to a named prepared-statement > + object. > + </para> > + </note> > + > + <para> > Once a prepared statement exists, it can be readied for execution using a > Bind message. The Bind message gives the name of the source prepared > statement (empty string denotes the unnamed prepared statement), the name > *************** > *** 674,679 **** > --- 694,720 ---- > by the query; the format can be specified overall, or per-column. > The response is either BindComplete or ErrorResponse. > </para> > + > + <para> > + Query planning of the unnamed prepared-statement object occurs when the > + first Bind message after a Parse message is received. The planner will > + consider the actual values of any parameters provided in the Bind message > + when planning the query. A Parse followed by Bind of the unnamed > + prepared-statement object will produce the same query plan as for the > + equivalent unparameterized query. > + </para> > + > + <note> > + <para> > + When a second or subsequent Bind referencing the unnamed prepared- > + statement object is received without an intervening Parse, the query is > + not replanned. The parameter values used in the first Bind message may > + produce a query plan that is only efficient for a subset of possible > + parameter values. To force replanning of the query on each execution, send > + a Parse message to replace the unnamed prepared-statement object before > + each Bind. > + </para> > + </note> > > <note> > <para> > Index: src/backend/commands/explain.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/explain.c,v > retrieving revision 1.120 > diff -u -c -r1.120 explain.c > *** src/backend/commands/explain.c 1 Apr 2004 21:28:44 -0000 1.120 > --- src/backend/commands/explain.c 24 May 2004 14:42:10 -0000 > *************** > *** 176,182 **** > } > > /* plan the query */ > ! plan = planner(query, isCursor, cursorOptions); > > /* Create a QueryDesc requesting no output */ > queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL, > --- 176,182 ---- > } > > /* plan the query */ > ! plan = planner(query, isCursor, cursorOptions, NULL); > > /* Create a QueryDesc requesting no output */ > queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL, > Index: src/backend/commands/portalcmds.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/portalcmds.c,v > retrieving revision 1.26 > diff -u -c -r1.26 portalcmds.c > *** src/backend/commands/portalcmds.c 21 Mar 2004 22:29:10 -0000 1.26 > --- src/backend/commands/portalcmds.c 24 May 2004 14:42:10 -0000 > *************** > *** 84,90 **** > errmsg("DECLARE CURSOR ... FOR UPDATE is not supported"), > errdetail("Cursors must be READ ONLY."))); > > ! plan = planner(query, true, stmt->options); > > /* > * Create a portal and copy the query and plan into its memory > --- 84,90 ---- > errmsg("DECLARE CURSOR ... FOR UPDATE is not supported"), > errdetail("Cursors must be READ ONLY."))); > > ! plan = planner(query, true, stmt->options, NULL); > > /* > * Create a portal and copy the query and plan into its memory > Index: src/backend/commands/prepare.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/prepare.c,v > retrieving revision 1.26 > diff -u -c -r1.26 prepare.c > *** src/backend/commands/prepare.c 22 Apr 2004 02:58:20 -0000 1.26 > --- src/backend/commands/prepare.c 24 May 2004 14:42:10 -0000 > *************** > *** 91,97 **** > query_list = QueryRewrite(stmt->query); > > /* Generate plans for queries. Snapshot is already set. */ > ! plan_list = pg_plan_queries(query_list, false); > > /* Save the results. */ > StorePreparedStatement(stmt->name, > --- 91,97 ---- > query_list = QueryRewrite(stmt->query); > > /* Generate plans for queries. Snapshot is already set. */ > ! plan_list = pg_plan_queries(query_list, false, NULL); > > /* Save the results. */ > StorePreparedStatement(stmt->name, > Index: src/backend/executor/functions.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/executor/functions.c,v > retrieving revision 1.80 > diff -u -c -r1.80 functions.c > *** src/backend/executor/functions.c 2 Apr 2004 23:14:08 -0000 1.80 > --- src/backend/executor/functions.c 24 May 2004 14:42:10 -0000 > *************** > *** 100,106 **** > Plan *planTree; > execution_state *newes; > > ! planTree = pg_plan_query(queryTree); > > newes = (execution_state *) palloc(sizeof(execution_state)); > if (preves) > --- 100,106 ---- > Plan *planTree; > execution_state *newes; > > ! planTree = pg_plan_query(queryTree, NULL); > > newes = (execution_state *) palloc(sizeof(execution_state)); > if (preves) > Index: src/backend/executor/spi.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/executor/spi.c,v > retrieving revision 1.113 > diff -u -c -r1.113 spi.c > *** src/backend/executor/spi.c 1 Apr 2004 21:28:44 -0000 1.113 > --- src/backend/executor/spi.c 24 May 2004 14:42:10 -0000 > *************** > *** 1130,1136 **** > QueryDesc *qdesc; > DestReceiver *dest; > > ! planTree = pg_plan_query(queryTree); > plan_list = lappend(plan_list, planTree); > > dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL); > --- 1130,1136 ---- > QueryDesc *qdesc; > DestReceiver *dest; > > ! planTree = pg_plan_query(queryTree, NULL); > plan_list = lappend(plan_list, planTree); > > dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL); > Index: src/backend/optimizer/path/clausesel.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/path/clausesel.c,v > retrieving revision 1.65 > diff -u -c -r1.65 clausesel.c > *** src/backend/optimizer/path/clausesel.c 10 May 2004 22:44:45 -0000 1.65 > --- src/backend/optimizer/path/clausesel.c 24 May 2004 14:42:11 -0000 > *************** > *** 489,496 **** > } > else if (IsA(clause, Param)) > { > ! /* XXX any way to do better? */ > ! s1 = 1.0; > } > else if (IsA(clause, Const)) > { > --- 489,504 ---- > } > else if (IsA(clause, Param)) > { > ! /* Try to collapse to Const. */ > ! Node *collapsed_clause = collapse_parameters_to_const(clause); > ! if (IsA(collapsed_clause, Const)) { > ! /* bool constant is pretty easy... */ > ! s1 = ((bool) ((Const *) collapsed_clause)->constvalue) ? 1.0 : 0.0; > ! } else { > ! /* Can't collapse to Const. */ > ! /* XXX any way to do better? */ > ! s1 = 1.0; > ! } > } > else if (IsA(clause, Const)) > { > Index: src/backend/optimizer/path/indxpath.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/path/indxpath.c,v > retrieving revision 1.158 > diff -u -c -r1.158 indxpath.c > *** src/backend/optimizer/path/indxpath.c 27 Mar 2004 00:24:28 -0000 1.158 > --- src/backend/optimizer/path/indxpath.c 24 May 2004 14:42:11 -0000 > *************** > *** 1068,1073 **** > --- 1068,1076 ---- > rightop = get_rightop(predicate); > if (rightop == NULL) > return false; /* not a binary opclause */ > + > + rightop = collapse_parameters_to_const(rightop); > + leftop = collapse_parameters_to_const(leftop); > if (IsA(rightop, Const)) > { > pred_var = leftop; > *************** > *** 1091,1096 **** > --- 1094,1102 ---- > rightop = get_rightop((Expr *) clause); > if (rightop == NULL) > return false; /* not a binary opclause */ > + > + rightop = collapse_parameters_to_const(rightop); > + leftop = collapse_parameters_to_const(leftop); > if (IsA(rightop, Const)) > { > clause_var = leftop; > *************** > *** 1873,1878 **** > --- 1879,1885 ---- > expr_op = ((OpExpr *) clause)->opno; > > /* again, required for all current special ops: */ > + rightop = collapse_parameters_to_const(rightop); > if (!IsA(rightop, Const) || > ((Const *) rightop)->constisnull) > return false; > *************** > *** 2056,2062 **** > Node *leftop = get_leftop(clause); > Node *rightop = get_rightop(clause); > Oid expr_op = ((OpExpr *) clause)->opno; > ! Const *patt = (Const *) rightop; > Const *prefix = NULL; > Const *rest = NULL; > Pattern_Prefix_Status pstatus; > --- 2063,2069 ---- > Node *leftop = get_leftop(clause); > Node *rightop = get_rightop(clause); > Oid expr_op = ((OpExpr *) clause)->opno; > ! Const *patt = (Const *) collapse_parameters_to_const(rightop); > Const *prefix = NULL; > Const *rest = NULL; > Pattern_Prefix_Status pstatus; > Index: src/backend/optimizer/plan/createplan.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/plan/createplan.c,v > retrieving revision 1.169 > diff -u -c -r1.169 createplan.c > *** src/backend/optimizer/plan/createplan.c 25 Apr 2004 18:23:56 -0000 1.169 > --- src/backend/optimizer/plan/createplan.c 24 May 2004 14:42:11 -0000 > *************** > *** 2330,2335 **** > --- 2330,2337 ---- > * level, but if we are building a subquery then it's important to > * report correct info to the outer planner. > */ > + if (limitOffset) > + limitOffset = collapse_parameters_to_const(limitOffset); > if (limitOffset && IsA(limitOffset, Const)) > { > Const *limito = (Const *) limitOffset; > *************** > *** 2348,2353 **** > --- 2350,2357 ---- > plan->plan_rows = 1; > } > } > + if (limitCount) > + limitCount = collapse_parameters_to_const(limitCount); > if (limitCount && IsA(limitCount, Const)) > { > Const *limitc = (Const *) limitCount; > Index: src/backend/optimizer/plan/planner.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/plan/planner.c,v > retrieving revision 1.169 > diff -u -c -r1.169 planner.c > *** src/backend/optimizer/plan/planner.c 11 May 2004 02:21:37 -0000 1.169 > --- src/backend/optimizer/plan/planner.c 24 May 2004 14:42:11 -0000 > *************** > *** 71,82 **** > * > *****************************************************************************/ > Plan * > ! planner(Query *parse, bool isCursor, int cursorOptions) > { > double tuple_fraction; > Plan *result_plan; > Index save_PlannerQueryLevel; > List *save_PlannerParamList; > > /* > * The planner can be called recursively (an example is when > --- 71,83 ---- > * > *****************************************************************************/ > Plan * > ! planner(Query *parse, bool isCursor, int cursorOptions, ParamListInfo boundParams) > { > double tuple_fraction; > Plan *result_plan; > Index save_PlannerQueryLevel; > List *save_PlannerParamList; > + ParamListInfo save_PlannerBoundParamList; > > /* > * The planner can be called recursively (an example is when > *************** > *** 93,102 **** > --- 94,105 ---- > */ > save_PlannerQueryLevel = PlannerQueryLevel; > save_PlannerParamList = PlannerParamList; > + save_PlannerBoundParamList = PlannerBoundParamList; > > /* Initialize state for handling outer-level references and params */ > PlannerQueryLevel = 0; /* will be 1 in top-level subquery_planner */ > PlannerParamList = NIL; > + PlannerBoundParamList = boundParams; > > /* Determine what fraction of the plan is likely to be scanned */ > if (isCursor) > *************** > *** 139,144 **** > --- 142,148 ---- > /* restore state for outer planner, if any */ > PlannerQueryLevel = save_PlannerQueryLevel; > PlannerParamList = save_PlannerParamList; > + PlannerBoundParamList = save_PlannerBoundParamList; > > return result_plan; > } > *************** > *** 401,407 **** > /* > * Simplify constant expressions. > */ > ! expr = eval_const_expressions(expr); > > /* Expand SubLinks to SubPlans */ > if (parse->hasSubLinks) > --- 405,411 ---- > /* > * Simplify constant expressions. > */ > ! expr = eval_const_expressions(expr, NULL); > > /* Expand SubLinks to SubPlans */ > if (parse->hasSubLinks) > *************** > *** 762,770 **** > */ > double limit_fraction = 0.0; > > ! if (IsA(parse->limitCount, Const)) > { > ! Const *limitc = (Const *) parse->limitCount; > int32 count = DatumGetInt32(limitc->constvalue); > > /* > --- 766,775 ---- > */ > double limit_fraction = 0.0; > > ! Node *limitCount = collapse_parameters_to_const(parse->limitCount); > ! if (IsA(limitCount, Const)) > { > ! Const *limitc = (Const *) limitCount; > int32 count = DatumGetInt32(limitc->constvalue); > > /* > *************** > *** 778,788 **** > /* We must also consider the OFFSET, if present */ > if (parse->limitOffset != NULL) > { > ! if (IsA(parse->limitOffset, Const)) > { > int32 offset; > > ! limitc = (Const *) parse->limitOffset; > offset = DatumGetInt32(limitc->constvalue); > if (!limitc->constisnull && offset > 0) > limit_fraction += (double) offset; > --- 783,794 ---- > /* We must also consider the OFFSET, if present */ > if (parse->limitOffset != NULL) > { > ! Node *limitOffset = collapse_parameters_to_const(parse->limitCount); > ! if (IsA(limitOffset, Const)) > { > int32 offset; > > ! limitc = (Const *) limitOffset; > offset = DatumGetInt32(limitc->constvalue); > if (!limitc->constisnull && offset > 0) > limit_fraction += (double) offset; > Index: src/backend/optimizer/prep/prepunion.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/prep/prepunion.c,v > retrieving revision 1.110 > diff -u -c -r1.110 prepunion.c > *** src/backend/optimizer/prep/prepunion.c 11 May 2004 22:43:55 -0000 1.110 > --- src/backend/optimizer/prep/prepunion.c 24 May 2004 14:42:11 -0000 > *************** > *** 430,435 **** > --- 430,436 ---- > TargetEntry *inputtle = (TargetEntry *) lfirst(input_tlist); > TargetEntry *reftle = (TargetEntry *) lfirst(refnames_tlist); > int32 colTypmod; > + Node *collapsed_expr; > > Assert(inputtle->resdom->resno == resno); > Assert(reftle->resdom->resno == resno); > *************** > *** 449,456 **** > * subquery-scan plans; we don't want phony constants appearing in > * the output tlists of upper-level nodes! > */ > ! if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const)) > ! expr = (Node *) inputtle->expr; > else > expr = (Node *) makeVar(0, > inputtle->resdom->resno, > --- 450,459 ---- > * subquery-scan plans; we don't want phony constants appearing in > * the output tlists of upper-level nodes! > */ > ! if (hack_constants && inputtle->expr && > ! NULL != (collapsed_expr = collapse_parameters_to_const((Node *) inputtle->expr)) && > ! IsA(collapsed_expr, Const)) > ! expr = collapsed_expr; > else > expr = (Node *) makeVar(0, > inputtle->resdom->resno, > Index: src/backend/optimizer/util/clauses.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/util/clauses.c,v > retrieving revision 1.170 > diff -u -c -r1.170 clauses.c > *** src/backend/optimizer/util/clauses.c 10 May 2004 22:44:45 -0000 1.170 > --- src/backend/optimizer/util/clauses.c 24 May 2004 14:42:12 -0000 > *************** > *** 28,37 **** > --- 28,39 ---- > #include "optimizer/clauses.h" > #include "optimizer/cost.h" > #include "optimizer/planmain.h" > + #include "optimizer/planner.h" > #include "optimizer/var.h" > #include "parser/analyze.h" > #include "parser/parse_clause.h" > #include "parser/parse_expr.h" > + #include "parser/parse_type.h" > #include "tcop/tcopprot.h" > #include "utils/acl.h" > #include "utils/builtins.h" > *************** > *** 41,48 **** > --- 43,61 ---- > #include "utils/syscache.h" > > > + ParamListInfo PlannerBoundParamList = NULL; /* to keep track of currently-bound parameter values */ > + > + > + typedef struct > + { > + List *active_fns; > + ParamListInfo params; > + int nparams; > + } eval_const_expressions_context; > + > typedef struct > { > + > int nargs; > List *args; > int *usecounts; > *************** > *** 57,73 **** > static bool contain_volatile_functions_walker(Node *node, void *context); > static bool contain_nonstrict_functions_walker(Node *node, void *context); > static bool set_coercionform_dontcare_walker(Node *node, void *context); > ! static Node *eval_const_expressions_mutator(Node *node, List *active_fns); > static List *simplify_or_arguments(List *args, > bool *haveNull, bool *forceTrue); > static List *simplify_and_arguments(List *args, > bool *haveNull, bool *forceFalse); > static Expr *simplify_function(Oid funcid, Oid result_type, List *args, > ! bool allow_inline, List *active_fns); > static Expr *evaluate_function(Oid funcid, Oid result_type, List *args, > HeapTuple func_tuple); > static Expr *inline_function(Oid funcid, Oid result_type, List *args, > ! HeapTuple func_tuple, List *active_fns); > static Node *substitute_actual_parameters(Node *expr, int nargs, List *args, > int *usecounts); > static Node *substitute_actual_parameters_mutator(Node *node, > --- 70,86 ---- > static bool contain_volatile_functions_walker(Node *node, void *context); > static bool contain_nonstrict_functions_walker(Node *node, void *context); > static bool set_coercionform_dontcare_walker(Node *node, void *context); > ! static Node *eval_const_expressions_mutator(Node *node, eval_const_expressions_context *context); > static List *simplify_or_arguments(List *args, > bool *haveNull, bool *forceTrue); > static List *simplify_and_arguments(List *args, > bool *haveNull, bool *forceFalse); > static Expr *simplify_function(Oid funcid, Oid result_type, List *args, > ! bool allow_inline, eval_const_expressions_context *active_fns); > static Expr *evaluate_function(Oid funcid, Oid result_type, List *args, > HeapTuple func_tuple); > static Expr *inline_function(Oid funcid, Oid result_type, List *args, > ! HeapTuple func_tuple, eval_const_expressions_context *active_fns); > static Node *substitute_actual_parameters(Node *expr, int nargs, List *args, > int *usecounts); > static Node *substitute_actual_parameters_mutator(Node *node, > *************** > *** 1062,1078 **** > *-------------------- > */ > Node * > ! eval_const_expressions(Node *node) > { > /* > ! * The context for the mutator is a list of SQL functions being > ! * recursively simplified, so we start with an empty list. > */ > ! return eval_const_expressions_mutator(node, NIL); > } > > static Node * > ! eval_const_expressions_mutator(Node *node, List *active_fns) > { > if (node == NULL) > return NULL; > --- 1075,1128 ---- > *-------------------- > */ > Node * > ! eval_const_expressions(Node *node, ParamListInfo params) > { > /* > ! * The context for the mutator is the parameter list plus > ! * a list of SQL functions being recursively simplified. > ! * The function list is initially empty. > */ > ! int i; > ! eval_const_expressions_context context; > ! context.active_fns = NIL; > ! context.params = params; > ! > ! if (params != NULL) { > ! /* Count and check parameters. */ > ! for (i = 0; params[i].kind != PARAM_INVALID; ++i) { > ! if (params[i].id != (i + 1)) > ! elog(ERROR, "param id mismatch: expected %d but was %d", i+1, params[i].id); > ! } > ! > ! context.nparams = i; > ! } else { > ! context.nparams = 0; > ! } > ! > ! return eval_const_expressions_mutator(node, &context); > ! } > ! > ! /* > ! * Helper for selectivity functions: apply a Param to Const replacement, > ! * using parameters from PlannerBoundParamList, and return the new tree. Might > ! * not copy the tree if it's obvious no change will happen. > ! */ > ! Node * > ! collapse_parameters_to_const(Node *node) > ! { > ! /* Already a leaf? */ > ! if (IsA(node, Const) || IsA(node, Var)) > ! return node; > ! > ! /* No parameters? */ > ! if (PlannerBoundParamList == NULL || PlannerBoundParamList[0].kind == PARAM_INVALID) > ! return node; > ! > ! return eval_const_expressions(node, PlannerBoundParamList); > } > > static Node * > ! eval_const_expressions_mutator(Node *node, eval_const_expressions_context *context) > { > if (node == NULL) > return NULL; > *************** > *** 1090,1103 **** > */ > args = (List *) expression_tree_mutator((Node *) expr->args, > eval_const_expressions_mutator, > ! (void *) active_fns); > > /* > * Code for op/func reduction is pretty bulky, so split it out as > * a separate function. > */ > simple = simplify_function(expr->funcid, expr->funcresulttype, args, > ! true, active_fns); > if (simple) /* successfully simplified it */ > return (Node *) simple; > > --- 1140,1153 ---- > */ > args = (List *) expression_tree_mutator((Node *) expr->args, > eval_const_expressions_mutator, > ! (void *) context); > > /* > * Code for op/func reduction is pretty bulky, so split it out as > * a separate function. > */ > simple = simplify_function(expr->funcid, expr->funcresulttype, args, > ! true, context); > if (simple) /* successfully simplified it */ > return (Node *) simple; > > *************** > *** 1128,1134 **** > */ > args = (List *) expression_tree_mutator((Node *) expr->args, > eval_const_expressions_mutator, > ! (void *) active_fns); > > /* > * Need to get OID of underlying function. Okay to scribble on > --- 1178,1184 ---- > */ > args = (List *) expression_tree_mutator((Node *) expr->args, > eval_const_expressions_mutator, > ! (void *) context); > > /* > * Need to get OID of underlying function. Okay to scribble on > *************** > *** 1141,1147 **** > * a separate function. > */ > simple = simplify_function(expr->opfuncid, expr->opresulttype, args, > ! true, active_fns); > if (simple) /* successfully simplified it */ > return (Node *) simple; > > --- 1191,1197 ---- > * a separate function. > */ > simple = simplify_function(expr->opfuncid, expr->opresulttype, args, > ! true, context); > if (simple) /* successfully simplified it */ > return (Node *) simple; > > *************** > *** 1176,1182 **** > */ > args = (List *) expression_tree_mutator((Node *) expr->args, > eval_const_expressions_mutator, > ! (void *) active_fns); > > /* > * We must do our own check for NULLs because DistinctExpr has > --- 1226,1232 ---- > */ > args = (List *) expression_tree_mutator((Node *) expr->args, > eval_const_expressions_mutator, > ! (void *) context); > > /* > * We must do our own check for NULLs because DistinctExpr has > *************** > *** 1220,1226 **** > * as a separate function. > */ > simple = simplify_function(expr->opfuncid, expr->opresulttype, > ! args, false, active_fns); > if (simple) /* successfully simplified it */ > { > /* > --- 1270,1276 ---- > * as a separate function. > */ > simple = simplify_function(expr->opfuncid, expr->opresulttype, > ! args, false, context); > if (simple) /* successfully simplified it */ > { > /* > *************** > *** 1261,1267 **** > */ > args = (List *) expression_tree_mutator((Node *) expr->args, > eval_const_expressions_mutator, > ! (void *) active_fns); > > switch (expr->boolop) > { > --- 1311,1317 ---- > */ > args = (List *) expression_tree_mutator((Node *) expr->args, > eval_const_expressions_mutator, > ! (void *) context); > > switch (expr->boolop) > { > *************** > *** 1354,1360 **** > Node *arg; > > arg = eval_const_expressions_mutator((Node *) relabel->arg, > ! active_fns); > > /* > * If we find stacked RelabelTypes (eg, from foo :: int :: oid) we > --- 1404,1410 ---- > Node *arg; > > arg = eval_const_expressions_mutator((Node *) relabel->arg, > ! context); > > /* > * If we find stacked RelabelTypes (eg, from foo :: int :: oid) we > *************** > *** 1418,1424 **** > > /* Simplify the test expression, if any */ > newarg = eval_const_expressions_mutator((Node *) caseexpr->arg, > ! active_fns); > > /* Simplify the WHEN clauses */ > FastListInit(&newargs); > --- 1468,1474 ---- > > /* Simplify the test expression, if any */ > newarg = eval_const_expressions_mutator((Node *) caseexpr->arg, > ! context); > > /* Simplify the WHEN clauses */ > FastListInit(&newargs); > *************** > *** 1428,1434 **** > CaseWhen *casewhen = (CaseWhen *) > expression_tree_mutator((Node *) lfirst(arg), > eval_const_expressions_mutator, > ! (void *) active_fns); > > Assert(IsA(casewhen, CaseWhen)); > if (casewhen->expr == NULL || > --- 1478,1484 ---- > CaseWhen *casewhen = (CaseWhen *) > expression_tree_mutator((Node *) lfirst(arg), > eval_const_expressions_mutator, > ! (void *) context); > > Assert(IsA(casewhen, CaseWhen)); > if (casewhen->expr == NULL || > *************** > *** 1458,1464 **** > > /* Simplify the default result */ > defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult, > ! active_fns); > > /* > * If no non-FALSE alternatives, CASE reduces to the default > --- 1508,1514 ---- > > /* Simplify the default result */ > defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult, > ! context); > > /* > * If no non-FALSE alternatives, CASE reduces to the default > *************** > *** 1488,1494 **** > Node *e; > > e = eval_const_expressions_mutator((Node *) lfirst(element), > ! active_fns); > if (!IsA(e, Const)) > all_const = false; > FastAppend(&newelems, e); > --- 1538,1544 ---- > Node *e; > > e = eval_const_expressions_mutator((Node *) lfirst(element), > ! context); > if (!IsA(e, Const)) > all_const = false; > FastAppend(&newelems, e); > *************** > *** 1519,1525 **** > Node *e; > > e = eval_const_expressions_mutator((Node *) lfirst(arg), > ! active_fns); > > /* > * We can remove null constants from the list. For a non-null > --- 1569,1575 ---- > Node *e; > > e = eval_const_expressions_mutator((Node *) lfirst(arg), > ! context); > > /* > * We can remove null constants from the list. For a non-null > *************** > *** 1555,1561 **** > Node *arg; > > arg = eval_const_expressions_mutator((Node *) fselect->arg, > ! active_fns); > if (arg && IsA(arg, Var) && > ((Var *) arg)->varattno == InvalidAttrNumber) > { > --- 1605,1611 ---- > Node *arg; > > arg = eval_const_expressions_mutator((Node *) fselect->arg, > ! context); > if (arg && IsA(arg, Var) && > ((Var *) arg)->varattno == InvalidAttrNumber) > { > *************** > *** 1580,1585 **** > --- 1630,1665 ---- > newfselect->resulttypmod = fselect->resulttypmod; > return (Node *) newfselect; > } > + if (IsA(node, Param)) > + { > + /* > + * Attempt to substitute concrete parameter values in, if we have them. > + */ > + > + Param *param = (Param *) node; > + > + if (param->paramkind == PARAM_NUM && context->params != NULL) { > + /* > + * Return a Constant in place of this Param. > + */ > + > + Type type; > + Const *replacement; > + > + if (param->paramid <= 0 || param->paramid > context->nparams) > + elog(ERROR, "invalid paramid: %d", param->paramid); > + > + type = typeidType(param->paramtype); > + replacement = makeConst(param->paramtype, > + typeLen(type), > + context->params[param->paramid - 1].value, > + context->params[param->paramid - 1].isnull, > + typeByVal(type)); > + > + ReleaseSysCache(type); > + return (Node *) replacement; > + } > + } > > /* > * For any node type not handled above, we recurse using > *************** > *** 1589,1595 **** > * simplify constant expressions in its subscripts. > */ > return expression_tree_mutator(node, eval_const_expressions_mutator, > ! (void *) active_fns); > } > > /* > --- 1669,1675 ---- > * simplify constant expressions in its subscripts. > */ > return expression_tree_mutator(node, eval_const_expressions_mutator, > ! (void *) context); > } > > /* > *************** > *** 1727,1733 **** > */ > static Expr * > simplify_function(Oid funcid, Oid result_type, List *args, > ! bool allow_inline, List *active_fns) > { > HeapTuple func_tuple; > Expr *newexpr; > --- 1807,1813 ---- > */ > static Expr * > simplify_function(Oid funcid, Oid result_type, List *args, > ! bool allow_inline, eval_const_expressions_context *context) > { > HeapTuple func_tuple; > Expr *newexpr; > *************** > *** 1750,1756 **** > > if (!newexpr && allow_inline) > newexpr = inline_function(funcid, result_type, args, > ! func_tuple, active_fns); > > ReleaseSysCache(func_tuple); > > --- 1830,1836 ---- > > if (!newexpr && allow_inline) > newexpr = inline_function(funcid, result_type, args, > ! func_tuple, context); > > ReleaseSysCache(func_tuple); > > *************** > *** 1854,1860 **** > */ > static Expr * > inline_function(Oid funcid, Oid result_type, List *args, > ! HeapTuple func_tuple, List *active_fns) > { > Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); > bool polymorphic = false; > --- 1934,1940 ---- > */ > static Expr * > inline_function(Oid funcid, Oid result_type, List *args, > ! HeapTuple func_tuple, eval_const_expressions_context *context) > { > Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); > bool polymorphic = false; > *************** > *** 1884,1890 **** > return NULL; > > /* Check for recursive function, and give up trying to expand if so */ > ! if (oidMember(funcid, active_fns)) > return NULL; > > /* Check permission to call function (fail later, if not) */ > --- 1964,1970 ---- > return NULL; > > /* Check for recursive function, and give up trying to expand if so */ > ! if (oidMember(funcid, context->active_fns)) > return NULL; > > /* Check permission to call function (fail later, if not) */ > *************** > *** 2077,2084 **** > * Recursively try to simplify the modified expression. Here we must > * add the current function to the context list of active functions. > */ > ! newexpr = eval_const_expressions_mutator(newexpr, > ! lconso(funcid, active_fns)); > > error_context_stack = sqlerrcontext.previous; > > --- 2157,2164 ---- > * Recursively try to simplify the modified expression. Here we must > * add the current function to the context list of active functions. > */ > ! context->active_fns = lconso(funcid, context->active_fns); > ! newexpr = eval_const_expressions_mutator(newexpr, context); > > error_context_stack = sqlerrcontext.previous; > > Index: src/backend/tcop/postgres.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/tcop/postgres.c,v > retrieving revision 1.414 > diff -u -c -r1.414 postgres.c > *** src/backend/tcop/postgres.c 23 May 2004 03:50:45 -0000 1.414 > --- src/backend/tcop/postgres.c 24 May 2004 14:42:12 -0000 > *************** > *** 631,637 **** > > /* Generate a plan for a single already-rewritten query. */ > Plan * > ! pg_plan_query(Query *querytree) > { > Plan *plan; > > --- 631,637 ---- > > /* Generate a plan for a single already-rewritten query. */ > Plan * > ! pg_plan_query(Query *querytree, ParamListInfo params) > { > Plan *plan; > > *************** > *** 643,649 **** > ResetUsage(); > > /* call the optimizer */ > ! plan = planner(querytree, false, 0); > > if (log_planner_stats) > ShowUsage("PLANNER STATISTICS"); > --- 643,649 ---- > ResetUsage(); > > /* call the optimizer */ > ! plan = planner(querytree, false, 0, params); > > if (log_planner_stats) > ShowUsage("PLANNER STATISTICS"); > *************** > *** 688,694 **** > * statements in the rewriter's output.) > */ > List * > ! pg_plan_queries(List *querytrees, bool needSnapshot) > { > List *plan_list = NIL; > List *query_list; > --- 688,694 ---- > * statements in the rewriter's output.) > */ > List * > ! pg_plan_queries(List *querytrees, bool needSnapshot, ParamListInfo params) > { > List *plan_list = NIL; > List *query_list; > *************** > *** 710,716 **** > SetQuerySnapshot(); > needSnapshot = false; > } > ! plan = pg_plan_query(query); > } > > plan_list = lappend(plan_list, plan); > --- 710,716 ---- > SetQuerySnapshot(); > needSnapshot = false; > } > ! plan = pg_plan_query(query, params); > } > > plan_list = lappend(plan_list, plan); > *************** > *** 868,874 **** > > querytree_list = pg_analyze_and_rewrite(parsetree, NULL, 0); > > ! plantree_list = pg_plan_queries(querytree_list, true); > > /* If we got a cancel signal in analysis or planning, quit */ > CHECK_FOR_INTERRUPTS(); > --- 868,874 ---- > > querytree_list = pg_analyze_and_rewrite(parsetree, NULL, 0); > > ! plantree_list = pg_plan_queries(querytree_list, true, NULL); > > /* If we got a cancel signal in analysis or planning, quit */ > CHECK_FOR_INTERRUPTS(); > *************** > *** 1206,1212 **** > > querytree_list = pg_rewrite_queries(querytree_list); > > ! plantree_list = pg_plan_queries(querytree_list, true); > } > else > { > --- 1206,1217 ---- > > querytree_list = pg_rewrite_queries(querytree_list); > > ! /* If this is the unnamed statement and we have parameters, defer query planning until Bind. */ > ! if (!is_named && numParams > 0) { > ! plantree_list = NIL; > ! } else { > ! plantree_list = pg_plan_queries(querytree_list, true, NULL); > ! } > } > else > { > *************** > *** 1357,1368 **** > else > portal = CreatePortal(portal_name, false, false); > > ! PortalDefineQuery(portal, > ! pstmt->query_string, > ! pstmt->commandTag, > ! pstmt->query_list, > ! pstmt->plan_list, > ! pstmt->context); > > /* > * Fetch parameters, if any, and store in the portal's memory context. > --- 1362,1368 ---- > else > portal = CreatePortal(portal_name, false, false); > > ! /* Defer portal query definition until we're sure planning is done. */ > > /* > * Fetch parameters, if any, and store in the portal's memory context. > *************** > *** 1517,1524 **** > pq_getmsgend(input_message); > > /* > ! * Start portal execution. > */ > PortalStart(portal, params); > > /* > --- 1517,1542 ---- > pq_getmsgend(input_message); > > /* > ! * If this is the unnamed statement, we may not have planned the > ! * query yet. In that case, do the planning (in the query's > ! * memory context) now that we have concrete parameter values. > ! */ > ! if (pstmt->plan_list == NIL && pstmt->query_list != NIL) { > ! MemoryContext oldContext = MemoryContextSwitchTo(pstmt->context); > ! pstmt->plan_list = pg_plan_queries(pstmt->query_list, true, params); > ! MemoryContextSwitchTo(oldContext); > ! } > ! > ! /* > ! * Define portal and start execution. > */ > + PortalDefineQuery(portal, > + pstmt->query_string, > + pstmt->commandTag, > + pstmt->query_list, > + pstmt->plan_list, > + pstmt->context); > + > PortalStart(portal, params); > > /* > Index: src/backend/utils/adt/selfuncs.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/selfuncs.c,v > retrieving revision 1.158 > diff -u -c -r1.158 selfuncs.c > *** src/backend/utils/adt/selfuncs.c 27 Feb 2004 21:44:34 -0000 1.158 > --- src/backend/utils/adt/selfuncs.c 24 May 2004 14:42:12 -0000 > *************** > *** 242,247 **** > --- 242,249 ---- > &vardata, &other, &varonleft)) > PG_RETURN_FLOAT8(DEFAULT_EQ_SEL); > > + other = collapse_parameters_to_const(other); > + > /* > * If the something is a NULL constant, assume operator is strict and > * return zero, ie, operator will never return TRUE. > *************** > *** 711,716 **** > --- 713,720 ---- > &vardata, &other, &varonleft)) > PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); > > + other = collapse_parameters_to_const(other); > + > /* > * Can't do anything useful if the something is not a constant, > * either. > *************** > *** 787,792 **** > --- 791,798 ---- > &vardata, &other, &varonleft)) > PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); > > + other = collapse_parameters_to_const(other); > + > /* > * Can't do anything useful if the something is not a constant, > * either. > *************** > *** 870,876 **** > if (!get_restriction_variable(root, args, varRelid, > &vardata, &other, &varonleft)) > return DEFAULT_MATCH_SEL; > ! if (!varonleft || !IsA(other, Const)) > { > ReleaseVariableStats(vardata); > return DEFAULT_MATCH_SEL; > --- 876,891 ---- > if (!get_restriction_variable(root, args, varRelid, > &vardata, &other, &varonleft)) > return DEFAULT_MATCH_SEL; > ! > ! if (!varonleft) > ! { > ! ReleaseVariableStats(vardata); > ! return DEFAULT_MATCH_SEL; > ! } > ! > ! other = collapse_parameters_to_const(other); > ! > ! if (!IsA(other, Const)) > { > ReleaseVariableStats(vardata); > return DEFAULT_MATCH_SEL; > Index: src/backend/utils/cache/relcache.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/cache/relcache.c,v > retrieving revision 1.202 > diff -u -c -r1.202 relcache.c > *** src/backend/utils/cache/relcache.c 8 May 2004 19:09:25 -0000 1.202 > --- src/backend/utils/cache/relcache.c 24 May 2004 14:42:13 -0000 > *************** > *** 2680,2686 **** > */ > result = (List *) flatten_andors((Node *) result); > > ! result = (List *) eval_const_expressions((Node *) result); > > /* > * Also mark any coercion format fields as "don't care", so that the > --- 2680,2686 ---- > */ > result = (List *) flatten_andors((Node *) result); > > ! result = (List *) eval_const_expressions((Node *) result, NULL); > > /* > * Also mark any coercion format fields as "don't care", so that the > *************** > *** 2754,2760 **** > */ > result = (List *) canonicalize_qual((Expr *) result); > > ! result = (List *) eval_const_expressions((Node *) result); > > /* > * Also mark any coercion format fields as "don't care", so that the > --- 2754,2760 ---- > */ > result = (List *) canonicalize_qual((Expr *) result); > > ! result = (List *) eval_const_expressions((Node *) result, NULL); > > /* > * Also mark any coercion format fields as "don't care", so that the > Index: src/include/optimizer/clauses.h > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/include/optimizer/clauses.h,v > retrieving revision 1.73 > diff -u -c -r1.73 clauses.h > *** src/include/optimizer/clauses.h 14 Mar 2004 23:41:27 -0000 1.73 > --- src/include/optimizer/clauses.h 24 May 2004 14:42:13 -0000 > *************** > *** 15,22 **** > #define CLAUSES_H > > #include "nodes/relation.h" > > ! > > #define is_opclause(clause) ((clause) != NULL && IsA(clause, OpExpr)) > #define is_funcclause(clause) ((clause) != NULL && IsA(clause, FuncExpr)) > --- 15,23 ---- > #define CLAUSES_H > > #include "nodes/relation.h" > + #include "nodes/params.h" > > ! extern ParamListInfo PlannerBoundParamList; /* to keep track of externally-specified parameter values */ > > #define is_opclause(clause) ((clause) != NULL && IsA(clause, OpExpr)) > #define is_funcclause(clause) ((clause) != NULL && IsA(clause, FuncExpr)) > *************** > *** 65,71 **** > > extern void set_coercionform_dontcare(Node *node); > > ! extern Node *eval_const_expressions(Node *node); > > extern bool expression_tree_walker(Node *node, bool (*walker) (), > void *context); > --- 66,74 ---- > > extern void set_coercionform_dontcare(Node *node); > > ! extern Node *eval_const_expressions(Node *node, ParamListInfo params); > ! > ! extern Node *collapse_parameters_to_const(Node *node); > > extern bool expression_tree_walker(Node *node, bool (*walker) (), > void *context); > Index: src/include/optimizer/planner.h > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/include/optimizer/planner.h,v > retrieving revision 1.28 > diff -u -c -r1.28 planner.h > *** src/include/optimizer/planner.h 29 Nov 2003 22:41:07 -0000 1.28 > --- src/include/optimizer/planner.h 24 May 2004 14:42:13 -0000 > *************** > *** 16,24 **** > > #include "nodes/parsenodes.h" > #include "nodes/plannodes.h" > > ! > ! extern Plan *planner(Query *parse, bool isCursor, int cursorOptions); > extern Plan *subquery_planner(Query *parse, double tuple_fraction); > > #endif /* PLANNER_H */ > --- 16,24 ---- > > #include "nodes/parsenodes.h" > #include "nodes/plannodes.h" > + #include "nodes/params.h" > > ! extern Plan *planner(Query *parse, bool isCursor, int cursorOptions, ParamListInfo extParams); > extern Plan *subquery_planner(Query *parse, double tuple_fraction); > > #endif /* PLANNER_H */ > Index: src/include/tcop/tcopprot.h > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/include/tcop/tcopprot.h,v > retrieving revision 1.65 > diff -u -c -r1.65 tcopprot.h > *** src/include/tcop/tcopprot.h 11 Apr 2004 00:54:45 -0000 1.65 > --- src/include/tcop/tcopprot.h 24 May 2004 14:42:13 -0000 > *************** > *** 24,29 **** > --- 24,30 ---- > #include "executor/execdesc.h" > #include "tcop/dest.h" > #include "utils/guc.h" > + #include "nodes/params.h" > > > extern DLLIMPORT sigjmp_buf Warn_restart; > *************** > *** 57,64 **** > extern List *pg_analyze_and_rewrite(Node *parsetree, > Oid *paramTypes, int numParams); > extern List *pg_rewrite_queries(List *querytree_list); > ! extern Plan *pg_plan_query(Query *querytree); > ! extern List *pg_plan_queries(List *querytrees, bool needSnapshot); > > extern bool assign_max_stack_depth(int newval, bool doit, GucSource source); > > --- 58,65 ---- > extern List *pg_analyze_and_rewrite(Node *parsetree, > Oid *paramTypes, int numParams); > extern List *pg_rewrite_queries(List *querytree_list); > ! extern Plan *pg_plan_query(Query *querytree, ParamListInfo params); > ! extern List *pg_plan_queries(List *querytrees, bool needSnapshot, ParamListInfo params); > > extern bool assign_max_stack_depth(int newval, bool doit, GucSource source); > [ text/x-python is unsupported, treating like TEXT/PLAIN ] > #!/usr/bin/env python > > import sys, socket, struct > > # V3 primitives > > def send_startup(conn, db, user): > paramValues = 'user\0%s\0database\0%s\0\0' % (user, db) > msgLen = 8 + len(paramValues) > msg = struct.pack('>II', msgLen, 196608) + paramValues > conn.send(msg) > > def send(conn, type, data): > msg = type + struct.pack('>i', len(data)+4) + data > conn.send(msg) > > def send_parse(conn, statement, query, oids): > sys.stdout.write('<= Parse(%s,%s,%s)\n' % (repr(statement), repr(query), repr(oids))) > msg = statement + '\0' > msg += query + '\0' > msg += struct.pack('>H', len(oids)) > for oid in oids: > msg += struct.pack('>I', oid) > send(conn, 'P', msg) > > def send_bind(conn, portal, statement, params): > sys.stdout.write('<= Bind(%s,%s,%s)\n' % (repr(portal), repr(statement), repr(params))) > msg = portal + '\0' > msg += statement + '\0' > msg += struct.pack('>H', 0) # param format count > msg += struct.pack('>H', len(params)) # param count > for param in params: > if param is None: > msg += struct.pack('>i', -1) # NULL > else: > msg += struct.pack('>i', len(param) + 1) # param data length > msg += param + '\0' > msg += struct.pack('>H', 0) # result format count > send(conn, 'B', msg) > > def send_execute(conn, portal, count): > sys.stdout.write('<= Execute(%s,%d)\n' % (repr(portal), count)) > msg = portal + '\0' > msg += struct.pack('>i', 0) # max rows > send(conn, 'E', msg) > > def send_sync(conn): > sys.stdout.write('<= Sync\n') > send(conn, 'S', '') > > # > # Connection setup. > # > > def connect(host, port, db, user): > conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) > conn.connect( (host, port) ) > send_startup(conn, db, user) > > type = conn.recv(1) > if type != 'R': raise RuntimeError('bad startup type: ' + type) > > length, = struct.unpack('>i', conn.recv(4)) > data = conn.recv(length-4) > res, = struct.unpack('>i', data[:4]) > > if res != 0: raise RuntimeError("auth was needed but we don't do it") > > process_results(conn) > return conn > > # > # Normal connection processing > # > > def make_log(type): > return lambda conn, data: sys.stdout.write(' => ' + type + '\n') > > def make_server_response(type): > def server_response(conn, data, type=type): > sys.stdout.write(' => ' + type + '\n') > i = 0 > while i != -1 and data[i] != '\0': > type = data[i] > end = data.find('\0', i+1) > if end == -1: > value = data[i+1:] > i = -1 > else: > value = data[i+1:end] > i = end + 1 > > sys.stdout.write(' => ' + type + ': ' + value + '\n') > > return server_response > > def command_complete(conn, data): > sys.stdout.write(' => CommandComplete: ' + data + '\n') > > def data_row(conn, data): > sys.stdout.write(' => DataRow ') > count, = struct.unpack('>H', data[:2]) > o = 2 > for i in xrange(count): > length, = struct.unpack('>i', data[o:o+4]) > o += 4 > if length == -1: sys.stdout.write('NULL,') > else: > sys.stdout.write(data[o:o+length] + ',') > o += length > sys.stdout.write('\n') > > handlers = { > 'K': make_log('BackendKeyData'), > '2': make_log('BindComplete'), > '3': make_log('CloseComplete'), > 'C': command_complete, > 'D': data_row, > 'I': make_log('EmptyQuery'), > 'E': make_server_response('ErrorResponse'), > 'N': make_server_response('NoticeResponse'), > 'S': make_log('ParameterStatus'), > '1': make_log('ParseComplete'), > 'Z': make_log('ReadyForQuery') > } > > def process_results(conn): > seen_sync = False > type = None > while type != 'Z': > type = conn.recv(1) > if not type: > raise RuntimeError('EOF seen') > > length, = struct.unpack('>i', conn.recv(4)) > data = conn.recv(length-4) > > if not handlers.has_key(type): > raise RuntimeError('Unhandled message type ' + type) > > handlers[type](conn,data) > > statement_number = 1 > def combos(conn, query, oids, params, params_2): > global statement_number > > send_parse(conn, '', query, oids) > send_parse(conn, '', query, oids) > send_bind(conn, '', '', params) > send_execute(conn, '', 0) > send_sync(conn) > process_results(conn) > > send_bind(conn, '', '', params_2) > send_execute(conn, '', 0) > send_sync(conn) > process_results(conn) > > stmt = 's_%d' % statement_number > statement_number += 1 > send_parse(conn, stmt, query, oids) > send_bind(conn, '', stmt, params) > send_execute(conn, '', 0) > send_sync(conn) > process_results(conn) > > send_bind(conn, '', stmt, params_2) > send_execute(conn, '', 0) > send_sync(conn) > process_results(conn) > > def tests(conn): > # Empty query > combos(conn=conn, query='', oids=(), params=(), params_2=()) > > # Simple SELECT > combos(conn=conn, query='SELECT 1', oids=(), params=(), params_2=()) > > # Simple parameterized SELECT > combos(conn=conn, query='SELECT $1', oids=(23,), > params=('42',), params_2=('24',)) > > # Parameterized SELECT that calls a function that can be preevaluated > combos(conn=conn, query='SELECT abs($1)', oids=(23,), > params=('42',), params_2=('-24',)) > > # Parameterized SELECT that calls a function that can't be preevaluated > combos(conn=conn, query='SELECT abs($1 + random())', oids=(23,), > params=('42',), params_2=('-24',)) > > if __name__ == '__main__': > host, port, db, user = sys.argv[1:] > conn = connect(host, int(port), db, user) > tests(conn) > send(conn, 'X', '') > conn.close() > > > ---------------------------(end of broadcast)--------------------------- > TIP 5: Have you checked our extensive FAQ? > > http://www.postgresql.org/docs/faqs/FAQ.html -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
pgsql-patches by date: