From 8d9e025e7fcc68737d5a0e99b457417ed595af46 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 23 Mar 2003 05:14:37 +0000 Subject: [PATCH] Instead of storing pg_statistic stavalues entries as text strings, store them as arrays of the internal datatype. This requires treating the stavalues columns as 'anyarray' rather than 'text[]', which is not 100% kosher but seems to work fine for the purposes we need for pg_statistic. Perhaps in the future 'anyarray' will be allowed more generally. --- doc/src/sgml/catalogs.sgml | 12 +++--- src/backend/catalog/heap.c | 10 +++-- src/backend/commands/analyze.c | 36 ++++-------------- src/backend/utils/adt/selfuncs.c | 7 +--- src/backend/utils/cache/lsyscache.c | 58 +++++++++++++---------------- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_statistic.h | 22 +++++++---- src/test/regress/expected/rules.out | 6 +-- 8 files changed, 67 insertions(+), 88 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 4b50e8f442..ddac7b3842 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,6 +1,6 @@ @@ -3050,12 +3050,14 @@ stavaluesN - text[] + anyarray Column data values of the appropriate kind for the Nth - slot, or NULL if the slot kind does not store any data values. - For data-type independence, all column data values are converted - to external textual form and stored as TEXT datums. + slot, or NULL if the slot kind does not store any data + values. + Each array's element values are actually of the specific column's + datatype, so there is no way to define these columns' type more + specifically than anyarray. diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 105026a50e..802f5932f7 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.240 2003/03/20 03:34:55 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.241 2003/03/23 05:14:36 tgl Exp $ * * * INTERFACE ROUTINES @@ -421,8 +421,12 @@ CheckAttributeType(const char *attname, Oid atttypid) "\n\tProceeding with relation creation anyway", attname); else if (att_typtype == 'p') - elog(ERROR, "Attribute \"%s\" has pseudo-type %s", - attname, format_type_be(atttypid)); + { + /* Special hack for pg_statistic: allow ANYARRAY during initdb */ + if (atttypid != ANYARRAYOID || IsUnderPostmaster) + elog(ERROR, "Attribute \"%s\" has pseudo-type %s", + attname, format_type_be(atttypid)); + } else if (att_typtype == 'c') { Oid typrelid = get_typ_typrelid(atttypid); diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 2af7a3cd7c..09862f6d84 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.51 2002/11/29 21:39:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.52 2003/03/23 05:14:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1694,7 +1694,6 @@ update_attstats(Oid relid, int natts, VacAttrStats **vacattrstats) for (attno = 0; attno < natts; attno++) { VacAttrStats *stats = vacattrstats[attno]; - FmgrInfo out_function; HeapTuple stup, oldtup; int i, @@ -1708,8 +1707,6 @@ update_attstats(Oid relid, int natts, VacAttrStats **vacattrstats) if (!stats->stats_valid) continue; - fmgr_info(stats->attrtype->typoutput, &out_function); - /* * Construct a new pg_statistic tuple */ @@ -1758,33 +1755,16 @@ update_attstats(Oid relid, int natts, VacAttrStats **vacattrstats) } for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { - int ntxt = stats->numvalues[k]; - - if (ntxt > 0) + if (stats->numvalues[k] > 0) { - Datum *txtdatums = (Datum *) palloc(ntxt * sizeof(Datum)); ArrayType *arry; - for (n = 0; n < ntxt; n++) - { - /* - * Convert data values to a text string to be inserted - * into the text array. - */ - Datum stringdatum; - - stringdatum = - FunctionCall3(&out_function, - stats->stavalues[k][n], - ObjectIdGetDatum(stats->attrtype->typelem), - Int32GetDatum(stats->attr->atttypmod)); - txtdatums[n] = DirectFunctionCall1(textin, stringdatum); - pfree(DatumGetPointer(stringdatum)); - } - /* XXX knows more than it should about type text: */ - arry = construct_array(txtdatums, ntxt, - TEXTOID, - -1, false, 'i'); + arry = construct_array(stats->stavalues[k], + stats->numvalues[k], + stats->attr->atttypid, + stats->attrtype->typlen, + stats->attrtype->typbyval, + stats->attrtype->typalign); values[i++] = PointerGetDatum(arry); /* stavaluesN */ } else diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 208b7eb290..2a5ceb767f 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.133 2003/03/23 01:49:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.134 2003/03/23 05:14:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1826,16 +1826,11 @@ mergejoinscansel(Query *root, Node *clause, /* * Now skip any binary-compatible relabeling; there can only be one level * since constant-expression folder eliminates adjacent RelabelTypes. - * - * XXX can't enable this quite yet, it exposes regproc uncertainty problems - * in regression tests. FIXME soon. */ -#if 0 if (IsA(left, RelabelType)) left = (Var *) ((RelabelType *) left)->arg; if (IsA(right, RelabelType)) right = (Var *) ((RelabelType *) right)->arg; -#endif /* Can't do anything if inputs are not Vars */ if (!IsA(left, Var) || diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index abc00a9204..277793905e 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.90 2003/02/03 21:15:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.91 2003/03/23 05:14:36 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -28,6 +28,7 @@ #include "nodes/makefuncs.h" #include "utils/array.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -1387,8 +1388,7 @@ get_attstatsslot(HeapTuple statstuple, ArrayType *statarray; int narrayelem; HeapTuple typeTuple; - FmgrInfo inputproc; - Oid typelem; + Form_pg_type typeForm; for (i = 0; i < STATISTIC_NUM_SLOTS; i++) { @@ -1408,48 +1408,40 @@ get_attstatsslot(HeapTuple statstuple, elog(ERROR, "get_attstatsslot: stavalues is null"); statarray = DatumGetArrayTypeP(val); - /* - * Do initial examination of the array. This produces a list of - * text Datums --- ie, pointers into the text array value. - */ - deconstruct_array(statarray, - TEXTOID, -1, false, 'i', - values, nvalues); - narrayelem = *nvalues; - - /* - * We now need to replace each text Datum by its internal - * equivalent. - * - * Get the type input proc and typelem for the column datatype. - */ + /* Need to get info about the array element type */ typeTuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(atttype), 0, 0, 0); if (!HeapTupleIsValid(typeTuple)) elog(ERROR, "get_attstatsslot: Cache lookup failed for type %u", atttype); - fmgr_info(((Form_pg_type) GETSTRUCT(typeTuple))->typinput, &inputproc); - typelem = ((Form_pg_type) GETSTRUCT(typeTuple))->typelem; - ReleaseSysCache(typeTuple); + typeForm = (Form_pg_type) GETSTRUCT(typeTuple); + + /* Deconstruct array into Datum elements */ + deconstruct_array(statarray, + atttype, + typeForm->typlen, + typeForm->typbyval, + typeForm->typalign, + values, nvalues); /* - * Do the conversions. The palloc'd array of Datums is reused in - * place. + * If the element type is pass-by-reference, we now have a bunch + * of Datums that are pointers into the syscache value. Copy them + * to avoid problems if syscache decides to drop the entry. */ - for (j = 0; j < narrayelem; j++) + if (!typeForm->typbyval) { - char *strval; - - strval = DatumGetCString(DirectFunctionCall1(textout, - (*values)[j])); - (*values)[j] = FunctionCall3(&inputproc, - CStringGetDatum(strval), - ObjectIdGetDatum(typelem), - Int32GetDatum(atttypmod)); - pfree(strval); + for (j = 0; j < *nvalues; j++) + { + (*values)[j] = datumCopy((*values)[j], + typeForm->typbyval, + typeForm->typlen); + } } + ReleaseSysCache(typeTuple); + /* * Free statarray if it's a detoasted copy. */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 1bbd5f6636..a551518106 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.182 2003/03/21 01:58:04 tgl Exp $ + * $Id: catversion.h,v 1.183 2003/03/23 05:14:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200303201 +#define CATALOG_VERSION_NO 200303221 #endif diff --git a/src/include/catalog/pg_statistic.h b/src/include/catalog/pg_statistic.h index 8c8806a159..c72c68265d 100644 --- a/src/include/catalog/pg_statistic.h +++ b/src/include/catalog/pg_statistic.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_statistic.h,v 1.19 2003/03/10 22:28:21 tgl Exp $ + * $Id: pg_statistic.h,v 1.20 2003/03/23 05:14:37 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -26,6 +26,12 @@ * ---------------- */ +/* + * Keep C compiler happy with anyarray, below. This will need to go elsewhere + * if we ever use anyarray for more than pg_statistic. + */ +typedef struct varlena anyarray; + /* ---------------- * pg_statistic definition. cpp turns this into * typedef struct FormData_pg_statistic @@ -109,14 +115,14 @@ CATALOG(pg_statistic) BKI_WITHOUT_OIDS float4 stanumbers4[1]; /* - * Values in these text arrays are external representations of values - * of the column's data type. To re-create the actual Datum, do - * datatypein(textout(arrayelement)). + * Values in these arrays are values of the column's data type. We + * presently have to cheat quite a bit to allow polymorphic arrays + * of this kind, but perhaps someday it'll be a less bogus facility. */ - text stavalues1[1]; - text stavalues2[1]; - text stavalues3[1]; - text stavalues4[1]; + anyarray stavalues1; + anyarray stavalues2; + anyarray stavalues3; + anyarray stavalues4; } FormData_pg_statistic; #define STATISTIC_NUM_SLOTS 4 diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index c0127828f2..f173b6cf54 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1267,8 +1267,8 @@ drop table cchild; -- Check that ruleutils are working -- SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname; - viewname | definition ---------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + viewname | definition +--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, pg_get_indexdef(i.oid) AS indexdef FROM (((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char")); pg_locks | SELECT l.relation, l."database", l."transaction", l.pid, l."mode", l.granted FROM pg_lock_status() l(relation oid, "database" oid, "transaction" xid, pid integer, "mode" text, granted boolean); @@ -1291,7 +1291,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem pg_statio_user_indexes | SELECT pg_statio_all_indexes.relid, pg_statio_all_indexes.indexrelid, pg_statio_all_indexes.schemaname, pg_statio_all_indexes.relname, pg_statio_all_indexes.indexrelname, pg_statio_all_indexes.idx_blks_read, pg_statio_all_indexes.idx_blks_hit FROM pg_statio_all_indexes WHERE ((pg_statio_all_indexes.schemaname <> 'pg_catalog'::name) AND (pg_statio_all_indexes.schemaname <> 'pg_toast'::name)); pg_statio_user_sequences | SELECT pg_statio_all_sequences.relid, pg_statio_all_sequences.schemaname, pg_statio_all_sequences.relname, pg_statio_all_sequences.blks_read, pg_statio_all_sequences.blks_hit FROM pg_statio_all_sequences WHERE ((pg_statio_all_sequences.schemaname <> 'pg_catalog'::name) AND (pg_statio_all_sequences.schemaname <> 'pg_toast'::name)); pg_statio_user_tables | SELECT pg_statio_all_tables.relid, pg_statio_all_tables.schemaname, pg_statio_all_tables.relname, pg_statio_all_tables.heap_blks_read, pg_statio_all_tables.heap_blks_hit, pg_statio_all_tables.idx_blks_read, pg_statio_all_tables.idx_blks_hit, pg_statio_all_tables.toast_blks_read, pg_statio_all_tables.toast_blks_hit, pg_statio_all_tables.tidx_blks_read, pg_statio_all_tables.tidx_blks_hit FROM pg_statio_all_tables WHERE ((pg_statio_all_tables.schemaname <> 'pg_catalog'::name) AND (pg_statio_all_tables.schemaname <> 'pg_toast'::name)); - pg_stats | SELECT nspname AS schemaname, relname AS tablename, attname, stanullfrac AS null_frac, stawidth AS avg_width, stadistinct AS n_distinct, CASE WHEN (1 = stakind1) THEN stavalues1 WHEN (1 = stakind2) THEN stavalues2 WHEN (1 = stakind3) THEN stavalues3 WHEN (1 = stakind4) THEN stavalues4 ELSE NULL::text[] END AS most_common_vals, CASE WHEN (1 = stakind1) THEN stanumbers1 WHEN (1 = stakind2) THEN stanumbers2 WHEN (1 = stakind3) THEN stanumbers3 WHEN (1 = stakind4) THEN stanumbers4 ELSE NULL::real[] END AS most_common_freqs, CASE WHEN (2 = stakind1) THEN stavalues1 WHEN (2 = stakind2) THEN stavalues2 WHEN (2 = stakind3) THEN stavalues3 WHEN (2 = stakind4) THEN stavalues4 ELSE NULL::text[] END AS histogram_bounds, CASE WHEN (3 = stakind1) THEN stanumbers1[1] WHEN (3 = stakind2) THEN stanumbers2[1] WHEN (3 = stakind3) THEN stanumbers3[1] WHEN (3 = stakind4) THEN stanumbers4[1] ELSE NULL::real END AS correlation FROM (((pg_statistic s JOIN pg_class c ON ((c.oid = s.starelid))) JOIN pg_attribute a ON (((c.oid = a.attrelid) AND (a.attnum = s.staattnum)))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE has_table_privilege(c.oid, 'select'::text); + pg_stats | SELECT nspname AS schemaname, relname AS tablename, attname, stanullfrac AS null_frac, stawidth AS avg_width, stadistinct AS n_distinct, CASE WHEN (1 = stakind1) THEN stavalues1 WHEN (1 = stakind2) THEN stavalues2 WHEN (1 = stakind3) THEN stavalues3 WHEN (1 = stakind4) THEN stavalues4 ELSE NULL::anyarray END AS most_common_vals, CASE WHEN (1 = stakind1) THEN stanumbers1 WHEN (1 = stakind2) THEN stanumbers2 WHEN (1 = stakind3) THEN stanumbers3 WHEN (1 = stakind4) THEN stanumbers4 ELSE NULL::real[] END AS most_common_freqs, CASE WHEN (2 = stakind1) THEN stavalues1 WHEN (2 = stakind2) THEN stavalues2 WHEN (2 = stakind3) THEN stavalues3 WHEN (2 = stakind4) THEN stavalues4 ELSE NULL::anyarray END AS histogram_bounds, CASE WHEN (3 = stakind1) THEN stanumbers1[1] WHEN (3 = stakind2) THEN stanumbers2[1] WHEN (3 = stakind3) THEN stanumbers3[1] WHEN (3 = stakind4) THEN stanumbers4[1] ELSE NULL::real END AS correlation FROM (((pg_statistic s JOIN pg_class c ON ((c.oid = s.starelid))) JOIN pg_attribute a ON (((c.oid = a.attrelid) AND (a.attnum = s.staattnum)))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE has_table_privilege(c.oid, 'select'::text); pg_tables | SELECT n.nspname AS schemaname, c.relname AS tablename, pg_get_userbyid(c.relowner) AS tableowner, c.relhasindex AS hasindexes, c.relhasrules AS hasrules, (c.reltriggers > 0) AS hastriggers FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((c.relkind = 'r'::"char") OR (c.relkind = 's'::"char")); pg_user | SELECT pg_shadow.usename, pg_shadow.usesysid, pg_shadow.usecreatedb, pg_shadow.usesuper, pg_shadow.usecatupd, '********'::text AS passwd, pg_shadow.valuntil, pg_shadow.useconfig FROM pg_shadow; pg_views | SELECT n.nspname AS schemaname, c.relname AS viewname, pg_get_userbyid(c.relowner) AS viewowner, pg_get_viewdef(c.oid) AS definition FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char");