Received: from localhost (unknown [200.46.204.183]) by postgresql.org (Postfix) with ESMTP id B8BAF650179 for ; Wed, 6 Aug 2008 19:31:36 -0300 (ADT) Received: from postgresql.org ([200.46.204.86]) by localhost (mx1.hub.org [200.46.204.183]) (amavisd-maia, port 10024) with ESMTP id 23782-07 for ; Wed, 6 Aug 2008 19:31:28 -0300 (ADT) X-Greylist: from auto-whitelisted by SQLgrey-1.7.6 Received: from outmail137182.authsmtp.com (outmail137182.authsmtp.com [62.13.137.182]) by postgresql.org (Postfix) with ESMTP id 82429650174 for ; Wed, 6 Aug 2008 19:31:31 -0300 (ADT) Received: from mail-c187.authsmtp.com (mail-c187.authsmtp.com [62.13.128.33]) by punt5.authsmtp.com (8.14.2/8.14.2/Kp) with ESMTP id m76MVQmj071979; Wed, 6 Aug 2008 23:31:26 +0100 (BST) Received: from [192.168.0.3] (85-211-19-153.dyn.gotadsl.co.uk [85.211.19.153]) (authenticated bits=0) by mail.authsmtp.com (8.14.2/8.14.2/Kp) with ESMTP id m76MVG1Y042867; Wed, 6 Aug 2008 23:31:16 +0100 (BST) Subject: Re: [HACKERS] get_relation_stats_hook() From: Simon Riggs To: Tom Lane Cc: Alvaro Herrera , List pgsql-patches In-Reply-To: <1218037047.4549.291.camel@ebony.2ndQuadrant> References: <1214490037.3845.136.camel@ebony.site> <16415.1214493494@sss.pgh.pa.us> <1214496170.3845.196.camel@ebony.site> <17759.1214499056@sss.pgh.pa.us> <1214858056.3845.514.camel@ebony.site> <12400.1215716369@sss.pgh.pa.us> <1218037047.4549.291.camel@ebony.2ndQuadrant> Content-Type: multipart/mixed; boundary="=-l3jYGjJErWezVgNa2Arg" Date: Wed, 06 Aug 2008 23:38:23 +0100 Message-Id: <1218062303.4549.302.camel@ebony.2ndQuadrant> Mime-Version: 1.0 X-Mailer: Evolution 2.12.0 X-Server-Quench: 62689559-6407-11dd-8155-001185d377ca X-AuthRoute: OCdxZQATClZOTQEd DAteCiNZVAwpPBRK HVkIKg5MJUcNSQVJ NksadRtFaANbbExW HGQLW1xEUl57WmJ/ agwfZQBDYEtPQQRj TklMQU1QEQdtHhxP WxkYL38FdmxGfHx4 Y0EsX3lTWUQ0c0R7 SkdREGQbY2Ngb30e URQMagtVdQZXfh9E a1h6AHAKZjZWKBg1 TUcABxkNFhVyExEd cSEkBnE/e30+VjEx WycYEDEmAUwCQRIo MxU6MWkcG0EScgRa X-Authentic-SMTP: 61633235383639.squirrel.dmpriest.net.uk:1186/Kp X-Report-SPAM: If SPAM / abuse - report it at: http://www.authsmtp.com/abuse X-Virus-Status: No virus detected - but ensure you scan with your own anti-virus system! X-Virus-Scanned: Maia Mailguard 1.0.1 X-Spam-Status: No, hits=0 tagged_above=0 required=5 tests=none X-Spam-Level: X-Archive-Number: 200808/13 X-Sequence-Number: 6989 --=-l3jYGjJErWezVgNa2Arg Content-Type: text/plain Content-Transfer-Encoding: 7bit On Wed, 2008-08-06 at 16:37 +0100, Simon Riggs wrote: > I'll submit the fully working plugin once we've stabilised the API. It's > designed as a contrib module, so it can go in pgfoundry or contrib. OK, here's fully working plugin, plus API patch. I expect to open a pgfoundry project for the plugin, but will wait for the main patch review. I've tried the APIs three different ways and this seems cleanest and most widely applicable approach. It's possible to add calls in more places, but I haven't done this for reasons discussed previously. -- Simon Riggs www.2ndQuadrant.com PostgreSQL Training, Services and Support --=-l3jYGjJErWezVgNa2Arg Content-Disposition: attachment; filename=stat_hooks.v4.patch Content-Type: text/x-patch; name=stat_hooks.v4.patch; charset=utf-8 Content-Transfer-Encoding: 7bit Index: src/backend/utils/adt/selfuncs.c =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/backend/utils/adt/selfuncs.c,v retrieving revision 1.250 diff -c -r1.250 selfuncs.c *** src/backend/utils/adt/selfuncs.c 7 Jul 2008 20:24:55 -0000 1.250 --- src/backend/utils/adt/selfuncs.c 6 Aug 2008 22:24:08 -0000 *************** *** 103,108 **** --- 103,111 ---- #include "utils/selfuncs.h" #include "utils/syscache.h" + /* Hook for plugins to get control when we ask for stats */ + get_relation_stats_hook_type get_relation_stats_hook = NULL; + release_relation_stats_hook_type release_relation_stats_hook = NULL; static double var_eq_const(VariableStatData *vardata, Oid operator, Datum constval, bool constisnull, *************** *** 3769,3775 **** } else if (rte->rtekind == RTE_RELATION) { ! vardata->statsTuple = SearchSysCache(STATRELATT, ObjectIdGetDatum(rte->relid), Int16GetDatum(var->varattno), 0, 0); --- 3772,3786 ---- } else if (rte->rtekind == RTE_RELATION) { ! if (get_relation_stats_hook) ! vardata->statsTuple = (*get_relation_stats_hook) ! (rte->relid, ! var->varattno); ! ! if (vardata->statsTuple) ! vardata->free_using_plugin = true; ! else ! vardata->statsTuple = SearchSysCache(STATRELATT, ObjectIdGetDatum(rte->relid), Int16GetDatum(var->varattno), 0, 0); *************** *** 3889,3898 **** index->indpred == NIL) vardata->isunique = true; /* Has it got stats? */ ! vardata->statsTuple = SearchSysCache(STATRELATT, ObjectIdGetDatum(index->indexoid), ! Int16GetDatum(pos + 1), ! 0, 0); if (vardata->statsTuple) break; } --- 3900,3918 ---- index->indpred == NIL) vardata->isunique = true; /* Has it got stats? */ ! if (get_relation_stats_hook) ! { ! vardata->statsTuple = (*get_relation_stats_hook) ! (index->indexoid, ! pos + 1); ! vardata->free_using_plugin = true; ! } ! ! if (!vardata->statsTuple) ! vardata->statsTuple = SearchSysCache(STATRELATT, ObjectIdGetDatum(index->indexoid), ! Int16GetDatum(pos + 1), ! 0, 0); if (vardata->statsTuple) break; } *************** *** 5323,5329 **** double *indexCorrelation = (double *) PG_GETARG_POINTER(7); Oid relid; AttrNumber colnum; ! HeapTuple tuple; double numIndexTuples; List *indexBoundQuals; int indexcol; --- 5343,5349 ---- double *indexCorrelation = (double *) PG_GETARG_POINTER(7); Oid relid; AttrNumber colnum; ! HeapTuple tuple = NULL; double numIndexTuples; List *indexBoundQuals; int indexcol; *************** *** 5332,5337 **** --- 5352,5358 ---- bool found_null_op; double num_sa_scans; ListCell *l; + bool free_using_plugin = false; /* * For a btree scan, only leading '=' quals plus inequality quals for the *************** *** 5527,5536 **** colnum = 1; } ! tuple = SearchSysCache(STATRELATT, ! ObjectIdGetDatum(relid), ! Int16GetDatum(colnum), ! 0, 0); if (HeapTupleIsValid(tuple)) { --- 5548,5563 ---- colnum = 1; } ! if (get_relation_stats_hook) ! tuple = (*get_relation_stats_hook) (relid, colnum); ! ! if (tuple) ! free_using_plugin = true; ! else ! tuple = SearchSysCache(STATRELATT, ! ObjectIdGetDatum(relid), ! Int16GetDatum(colnum), ! 0, 0); if (HeapTupleIsValid(tuple)) { *************** *** 5571,5577 **** free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers); } ! ReleaseSysCache(tuple); } PG_RETURN_VOID(); --- 5598,5605 ---- free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers); } ! ! ReleaseStatsTuple(tuple, free_using_plugin); } PG_RETURN_VOID(); Index: src/backend/utils/cache/lsyscache.c =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/backend/utils/cache/lsyscache.c,v retrieving revision 1.157 diff -c -r1.157 lsyscache.c *** src/backend/utils/cache/lsyscache.c 13 Apr 2008 20:51:21 -0000 1.157 --- src/backend/utils/cache/lsyscache.c 6 Aug 2008 17:45:35 -0000 *************** *** 27,32 **** --- 27,33 ---- #include "catalog/pg_proc.h" #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" + #include "optimizer/plancat.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "utils/array.h" *************** *** 35,40 **** --- 36,43 ---- #include "utils/lsyscache.h" #include "utils/syscache.h" + /* Hook for plugins to get control in get_attavgwidth() */ + get_attavgwidth_hook_type get_attavgwidth_hook = NULL; /* ---------- AMOP CACHES ---------- */ *************** *** 2451,2466 **** * * Given the table and attribute number of a column, get the average * width of entries in the column. Return zero if no data available. */ int32 get_attavgwidth(Oid relid, AttrNumber attnum) { HeapTuple tp; tp = SearchSysCache(STATRELATT, ! ObjectIdGetDatum(relid), ! Int16GetDatum(attnum), ! 0, 0); if (HeapTupleIsValid(tp)) { int32 stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth; --- 2454,2483 ---- * * Given the table and attribute number of a column, get the average * width of entries in the column. Return zero if no data available. + * + * Calling a hook at this point looks somewhat strange, but is required + * because the optimizer handles inheritance relations by calling for + * the avg width later in the planner than get_relation_info_hook(). + * So the APIs and call points of hooks must match the optimizer. */ int32 get_attavgwidth(Oid relid, AttrNumber attnum) { HeapTuple tp; + int32 stawidth; + + if (get_attavgwidth_hook) + { + stawidth = (*get_attavgwidth_hook) (relid, attnum); + if (stawidth > 0) + return stawidth; + } tp = SearchSysCache(STATRELATT, ! ObjectIdGetDatum(relid), ! Int16GetDatum(attnum), ! 0, 0); ! if (HeapTupleIsValid(tp)) { int32 stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth; Index: src/include/optimizer/plancat.h =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/include/optimizer/plancat.h,v retrieving revision 1.50 diff -c -r1.50 plancat.h *** src/include/optimizer/plancat.h 19 Jun 2008 00:46:06 -0000 1.50 --- src/include/optimizer/plancat.h 6 Aug 2008 21:51:09 -0000 *************** *** 14,19 **** --- 14,20 ---- #ifndef PLANCAT_H #define PLANCAT_H + #include "access/htup.h" #include "nodes/relation.h" #include "utils/relcache.h" *************** *** 24,29 **** --- 25,41 ---- RelOptInfo *rel); extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook; + /* Hooks for plugins to get control in lsyscache.c and selfuncs.c */ + typedef HeapTuple (*get_relation_stats_hook_type) (Oid relid, AttrNumber attnum); + extern PGDLLIMPORT get_relation_stats_hook_type get_relation_stats_hook; + + typedef void (*release_relation_stats_hook_type) (HeapTuple statstup); + extern PGDLLIMPORT release_relation_stats_hook_type release_relation_stats_hook; + + typedef int32 (*get_attavgwidth_hook_type) (Oid relid, AttrNumber attnum); + extern PGDLLIMPORT get_attavgwidth_hook_type get_attavgwidth_hook; + + extern void get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, RelOptInfo *rel); Index: src/include/utils/selfuncs.h =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/include/utils/selfuncs.h,v retrieving revision 1.44 diff -c -r1.44 selfuncs.h *** src/include/utils/selfuncs.h 9 Mar 2008 00:32:09 -0000 1.44 --- src/include/utils/selfuncs.h 6 Aug 2008 21:53:53 -0000 *************** *** 74,85 **** Oid atttype; /* type to pass to get_attstatsslot */ int32 atttypmod; /* typmod to pass to get_attstatsslot */ bool isunique; /* true if matched to a unique index */ } VariableStatData; #define ReleaseVariableStats(vardata) \ do { \ if (HeapTupleIsValid((vardata).statsTuple)) \ ! ReleaseSysCache((vardata).statsTuple); \ } while(0) --- 74,112 ---- Oid atttype; /* type to pass to get_attstatsslot */ int32 atttypmod; /* typmod to pass to get_attstatsslot */ bool isunique; /* true if matched to a unique index */ + bool free_using_plugin; } VariableStatData; + #define ReleaseStatsTuple(tuple, free_using_plugin) \ + do { \ + if (HeapTupleIsValid(tuple)) \ + { \ + if (free_using_plugin) \ + { \ + if (release_relation_stats_hook) \ + (* release_relation_stats_hook)(tuple); \ + else \ + elog(ERROR, "unable to release variable stats correctly"); \ + } \ + else \ + ReleaseSysCache(tuple); \ + } \ + } while(0) + #define ReleaseVariableStats(vardata) \ do { \ if (HeapTupleIsValid((vardata).statsTuple)) \ ! { \ ! if ((vardata).free_using_plugin) \ ! { \ ! if (release_relation_stats_hook) \ ! (* release_relation_stats_hook)((vardata).statsTuple); \ ! else \ ! elog(ERROR, "unable to release variable stats correctly"); \ ! } \ ! else \ ! ReleaseSysCache((vardata).statsTuple); \ ! } \ } while(0) --=-l3jYGjJErWezVgNa2Arg Content-Disposition: attachment; filename=TOM.v2.tar Content-Type: application/x-tar; name=TOM.v2.tar Content-Transfer-Encoding: base64 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 CkbBKBgFo2AUjIJRMAqGOgAAhdX6BQB4AAA= --=-l3jYGjJErWezVgNa2Arg--