static int btnamefastcmp(Datum x, Datum y, SortSupport ssup) { Name a = DatumGetName(x); Name b = DatumGetName(y); return strncmp(NameStr(*a), NameStr(*b), NAMEDATALEN); }
/* * Check whether the function is IMMUTABLE. */ bool is_immutable_func(Oid funcid) { HeapTuple tp; bool isnull; Datum datum; tp = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for function %u", funcid); #ifdef DEBUG_FDW /* print function name and its immutability */ { char *proname; datum = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_proname, &isnull); proname = pstrdup(DatumGetName(datum)->data); elog(DEBUG1, "func %s(%u) is%s immutable", proname, funcid, (DatumGetChar(datum) == PROVOLATILE_IMMUTABLE) ? "" : " not"); pfree(proname); } #endif datum = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_provolatile, &isnull); ReleaseSysCache(tp); return (DatumGetChar(datum) == PROVOLATILE_IMMUTABLE); }
static int InitFsysInterface(FsysName name, FsysInterface fsys) { int retval = 0; Relation rel; TupleDesc dsc; HeapScanDesc scandesc; HeapTuple tuple; ScanKeyData entry[1]; Datum funcDatum; Datum libFileDatum; char *libFile; char *funcName; bool isNull; /* * Search pg_filesystem. We use a heapscan here even though there is an * index on oid, on the theory that pg_filesystem will usually have just a * few entries and so an indexed lookup is a waste of effort. */ rel = heap_open(FileSystemRelationId, AccessShareLock); dsc = RelationGetDescr(rel); ScanKeyInit(&entry[0], Anum_pg_filesystem_fsysname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(name)); scandesc = heap_beginscan(rel, SnapshotNow, 1, entry); tuple = heap_getnext(scandesc, ForwardScanDirection); /* We assume that there can be at most one matching tuple */ if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("filesystem \"%s\" does not exist", name))); /* get libfile */ libFileDatum = heap_getattr(tuple, Anum_pg_filesystem_fsyslibfile, dsc, &isNull); if(isNull) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("filesystem \"%s\" has no libfile specified", name))); } libFile = TextDatumGetCString(libFileDatum); /* Init all funcs used by filesystem */ for(int i = 0; i < FSYS_FUNC_TOTALNUM; i++) { FmgrInfo *finfo = &(fsys->fsysFuncs[i]); void *libraryhandle; funcDatum = heap_getattr(tuple, fsys_func_type_to_attnum(i), dsc, &isNull); if(isNull) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("filesystem \"%s\" has no %s function defined", name, fsys_func_type_to_name(i)))); } funcName = NameStr(*(DatumGetName(funcDatum))); finfo->fn_addr = load_external_function(libFile, funcName, true, &libraryhandle); finfo->fn_oid = (Oid) 1; finfo->fn_nargs = 0; finfo->fn_strict = 0; finfo->fn_strict = 0; finfo->fn_retset = 0; finfo->fn_stats = 1; finfo->fn_extra = NULL; finfo->fn_mcxt = CurrentMemoryContext; finfo->fn_expr = NULL; } heap_endscan(scandesc); heap_close(rel, AccessShareLock); return retval; }
/* * Add an attribute to the hash calculation. * **IMPORTANT: any new hard coded support for a data type in here * must be added to isGreenplumDbHashable() below! * * Note that the caller should provide the base type if the datum is * of a domain type. It is quite expensive to call get_typtype() and * getBaseType() here since this function gets called a lot for the * same set of Datums. * * @param hashFn called to update the hash value. * @param clientData passed to hashFn. */ void hashDatum(Datum datum, Oid type, datumHashFunction hashFn, void *clientData) { void *buf = NULL; /* pointer to the data */ size_t len = 0; /* length for the data buffer */ int64 intbuf; /* an 8 byte buffer for all integer sizes */ float4 buf_f4; float8 buf_f8; Timestamp tsbuf; /* timestamp data dype is either a double or * int8 (determined in compile time) */ TimestampTz tstzbuf; DateADT datebuf; TimeADT timebuf; TimeTzADT *timetzptr; Interval *intervalptr; AbsoluteTime abstime_buf; RelativeTime reltime_buf; TimeInterval tinterval; AbsoluteTime tinterval_len; Numeric num; bool bool_buf; char char_buf; Name namebuf; ArrayType *arrbuf; inet *inetptr; /* inet/cidr */ unsigned char inet_hkey[sizeof(inet_struct)]; macaddr *macptr; /* MAC address */ VarBit *vbitptr; int2vector *i2vec_buf; oidvector *oidvec_buf; Cash cash_buf; AclItem *aclitem_ptr; uint32 aclitem_buf; /* * special case buffers */ uint32 nanbuf; uint32 invalidbuf; void *tofree = NULL; /* * Select the hash to be performed according to the field type we are adding to the * hash. */ switch (type) { /* * ======= NUMERIC TYPES ======== */ case INT2OID: /* -32 thousand to 32 thousand, 2-byte storage */ intbuf = (int64) DatumGetInt16(datum); /* cast to 8 byte before * hashing */ buf = &intbuf; len = sizeof(intbuf); break; case INT4OID: /* -2 billion to 2 billion integer, 4-byte * storage */ intbuf = (int64) DatumGetInt32(datum); /* cast to 8 byte before * hashing */ buf = &intbuf; len = sizeof(intbuf); break; case INT8OID: /* ~18 digit integer, 8-byte storage */ intbuf = DatumGetInt64(datum); /* cast to 8 byte before * hashing */ buf = &intbuf; len = sizeof(intbuf); break; case FLOAT4OID: /* single-precision floating point number, * 4-byte storage */ buf_f4 = DatumGetFloat4(datum); /* * On IEEE-float machines, minus zero and zero have different bit * patterns but should compare as equal. We must ensure that they * have the same hash value, which is most easily done this way: */ if (buf_f4 == (float4) 0) buf_f4 = 0.0; buf = &buf_f4; len = sizeof(buf_f4); break; case FLOAT8OID: /* double-precision floating point number, * 8-byte storage */ buf_f8 = DatumGetFloat8(datum); /* * On IEEE-float machines, minus zero and zero have different bit * patterns but should compare as equal. We must ensure that they * have the same hash value, which is most easily done this way: */ if (buf_f8 == (float8) 0) buf_f8 = 0.0; buf = &buf_f8; len = sizeof(buf_f8); break; case NUMERICOID: num = DatumGetNumeric(datum); if (NUMERIC_IS_NAN(num)) { nanbuf = NAN_VAL; buf = &nanbuf; len = sizeof(nanbuf); } else /* not a nan */ { buf = num->n_data; len = (VARSIZE(num) - NUMERIC_HDRSZ); } /* * If we did a pg_detoast_datum, we need to remember to pfree, * or we will leak memory. Because of the 1-byte varlena header stuff. */ if (num != DatumGetPointer(datum)) tofree = num; break; /* * ====== CHARACTER TYPES ======= */ case CHAROID: /* char(1), single character */ char_buf = DatumGetChar(datum); buf = &char_buf; len = 1; break; case BPCHAROID: /* char(n), blank-padded string, fixed storage */ case TEXTOID: /* text */ case VARCHAROID: /* varchar */ case BYTEAOID: /* bytea */ { int tmplen; varattrib_untoast_ptr_len(datum, (char **) &buf, &tmplen, &tofree); /* adjust length to not include trailing blanks */ if (type != BYTEAOID && tmplen > 1) tmplen = ignoreblanks((char *) buf, tmplen); len = tmplen; break; } case NAMEOID: namebuf = DatumGetName(datum); len = NAMEDATALEN; buf = NameStr(*namebuf); /* adjust length to not include trailing blanks */ if (len > 1) len = ignoreblanks((char *) buf, len); break; /* * ====== OBJECT IDENTIFIER TYPES ====== */ case OIDOID: /* object identifier(oid), maximum 4 billion */ case REGPROCOID: /* function name */ case REGPROCEDUREOID: /* function name with argument types */ case REGOPEROID: /* operator name */ case REGOPERATOROID: /* operator with argument types */ case REGCLASSOID: /* relation name */ case REGTYPEOID: /* data type name */ intbuf = (int64) DatumGetUInt32(datum); /* cast to 8 byte before hashing */ buf = &intbuf; len = sizeof(intbuf); break; case TIDOID: /* tuple id (6 bytes) */ buf = DatumGetPointer(datum); len = SizeOfIptrData; break; /* * ====== DATE/TIME TYPES ====== */ case TIMESTAMPOID: /* date and time */ tsbuf = DatumGetTimestamp(datum); buf = &tsbuf; len = sizeof(tsbuf); break; case TIMESTAMPTZOID: /* date and time with time zone */ tstzbuf = DatumGetTimestampTz(datum); buf = &tstzbuf; len = sizeof(tstzbuf); break; case DATEOID: /* ANSI SQL date */ datebuf = DatumGetDateADT(datum); buf = &datebuf; len = sizeof(datebuf); break; case TIMEOID: /* hh:mm:ss, ANSI SQL time */ timebuf = DatumGetTimeADT(datum); buf = &timebuf; len = sizeof(timebuf); break; case TIMETZOID: /* time with time zone */ /* * will not compare to TIMEOID on equal values. * Postgres never attempts to compare the two as well. */ timetzptr = DatumGetTimeTzADTP(datum); buf = (unsigned char *) timetzptr; /* * Specify hash length as sizeof(double) + sizeof(int4), not as * sizeof(TimeTzADT), so that any garbage pad bytes in the structure * won't be included in the hash! */ len = sizeof(timetzptr->time) + sizeof(timetzptr->zone); break; case INTERVALOID: /* @ <number> <units>, time interval */ intervalptr = DatumGetIntervalP(datum); buf = (unsigned char *) intervalptr; /* * Specify hash length as sizeof(double) + sizeof(int4), not as * sizeof(Interval), so that any garbage pad bytes in the structure * won't be included in the hash! */ len = sizeof(intervalptr->time) + sizeof(intervalptr->month); break; case ABSTIMEOID: abstime_buf = DatumGetAbsoluteTime(datum); if (abstime_buf == INVALID_ABSTIME) { /* hash to a constant value */ invalidbuf = INVALID_VAL; len = sizeof(invalidbuf); buf = &invalidbuf; } else { len = sizeof(abstime_buf); buf = &abstime_buf; } break; case RELTIMEOID: reltime_buf = DatumGetRelativeTime(datum); if (reltime_buf == INVALID_RELTIME) { /* hash to a constant value */ invalidbuf = INVALID_VAL; len = sizeof(invalidbuf); buf = &invalidbuf; } else { len = sizeof(reltime_buf); buf = &reltime_buf; } break; case TINTERVALOID: tinterval = DatumGetTimeInterval(datum); /* * check if a valid interval. the '0' status code * stands for T_INTERVAL_INVAL which is defined in * nabstime.c. We use the actual value instead * of defining it again here. */ if(tinterval->status == 0 || tinterval->data[0] == INVALID_ABSTIME || tinterval->data[1] == INVALID_ABSTIME) { /* hash to a constant value */ invalidbuf = INVALID_VAL; len = sizeof(invalidbuf); buf = &invalidbuf; } else { /* normalize on length of the time interval */ tinterval_len = tinterval->data[1] - tinterval->data[0]; len = sizeof(tinterval_len); buf = &tinterval_len; } break; /* * ======= NETWORK TYPES ======== */ case INETOID: case CIDROID: inetptr = DatumGetInetP(datum); len = inet_getkey(inetptr, inet_hkey, sizeof(inet_hkey)); /* fill-in inet_key & get len */ buf = inet_hkey; break; case MACADDROID: macptr = DatumGetMacaddrP(datum); len = sizeof(macaddr); buf = (unsigned char *) macptr; break; /* * ======== BIT STRINGS ======== */ case BITOID: case VARBITOID: /* * Note that these are essentially strings. * we don't need to worry about '10' and '010' * to compare, b/c they will not, by design. * (see SQL standard, and varbit.c) */ vbitptr = DatumGetVarBitP(datum); len = VARBITBYTES(vbitptr); buf = (char *) VARBITS(vbitptr); break; /* * ======= other types ======= */ case BOOLOID: /* boolean, 'true'/'false' */ bool_buf = DatumGetBool(datum); buf = &bool_buf; len = sizeof(bool_buf); break; /* * We prepare the hash key for aclitems just like postgresql does. * (see code and comment in acl.c: hash_aclitem() ). */ case ACLITEMOID: aclitem_ptr = DatumGetAclItemP(datum); aclitem_buf = (uint32) (aclitem_ptr->ai_privs + aclitem_ptr->ai_grantee + aclitem_ptr->ai_grantor); buf = &aclitem_buf; len = sizeof(aclitem_buf); break; /* * ANYARRAY is a pseudo-type. We use it to include * any of the array types (OIDs 1007-1033 in pg_type.h). * caller needs to be sure the type is ANYARRAYOID * before calling cdbhash on an array (INSERT and COPY do so). */ case ANYARRAYOID: arrbuf = DatumGetArrayTypeP(datum); len = VARSIZE(arrbuf) - VARHDRSZ; buf = VARDATA(arrbuf); break; case INT2VECTOROID: i2vec_buf = (int2vector *) DatumGetPointer(datum); len = i2vec_buf->dim1 * sizeof(int2); buf = (void *)i2vec_buf->values; break; case OIDVECTOROID: oidvec_buf = (oidvector *) DatumGetPointer(datum); len = oidvec_buf->dim1 * sizeof(Oid); buf = oidvec_buf->values; break; case CASHOID: /* cash is stored in int32 internally */ cash_buf = (* (Cash *)DatumGetPointer(datum)); len = sizeof(Cash); buf = &cash_buf; break; default: ereport(ERROR, (errcode(ERRCODE_CDB_FEATURE_NOT_YET), errmsg("Type %u is not hashable.", type))); } /* switch(type) */ /* do the hash using the selected algorithm */ hashFn(clientData, buf, len); if(tofree) pfree(tofree); }
/* * Fetch the subscription from the syscache. */ Subscription * GetSubscription(Oid subid, bool missing_ok) { HeapTuple tup; Subscription *sub; Form_pg_subscription subform; Datum datum; bool isnull; tup = SearchSysCache1(SUBSCRIPTIONOID, ObjectIdGetDatum(subid)); if (!HeapTupleIsValid(tup)) { if (missing_ok) return NULL; elog(ERROR, "cache lookup failed for subscription %u", subid); } subform = (Form_pg_subscription) GETSTRUCT(tup); sub = (Subscription *) palloc(sizeof(Subscription)); sub->oid = subid; sub->dbid = subform->subdbid; sub->name = pstrdup(NameStr(subform->subname)); sub->owner = subform->subowner; sub->enabled = subform->subenabled; /* Get conninfo */ datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subconninfo, &isnull); Assert(!isnull); sub->conninfo = TextDatumGetCString(datum); /* Get slotname */ datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subslotname, &isnull); if (!isnull) sub->slotname = pstrdup(NameStr(*DatumGetName(datum))); else sub->slotname = NULL; /* Get synccommit */ datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subsynccommit, &isnull); Assert(!isnull); sub->synccommit = TextDatumGetCString(datum); /* Get publications */ datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subpublications, &isnull); Assert(!isnull); sub->publications = textarray_to_stringlist(DatumGetArrayTypeP(datum)); ReleaseSysCache(tup); return sub; }
/* * dbms_stats_import * Import exported statistics from stdin or a file. * * Order of arguments: * 1) schema name * 2) relation oid * 3) attribute name * 4) absolute path of source file, or 'stdin' (case insensitive) */ Datum dbms_stats_import(PG_FUNCTION_ARGS) { char *nspname; char *relname; char *attname; char *filename; /* filename, or NULL for STDIN */ int ret; int i; uint32 r_num; HeapTuple *r_tups; TupleDesc r_tupdesc; SPIPlanPtr r_upd_plan = NULL; SPIPlanPtr r_ins_plan = NULL; SPIPlanPtr c_sel_plan = NULL; SPIPlanPtr c_del_plan = NULL; SPIPlanPtr c_ins_plan = NULL; /* get validated arguments */ get_args(fcinfo, &nspname, &relname, &attname, &filename); /* for debug use */ elog(DEBUG3, "%s() f=%s n=%s r=%s a=%s", __FUNCTION__, filename ? filename : "(null)", nspname ? nspname : "(null)", relname ? relname : "(null)", attname ? attname : "(null)"); /* connect to SPI */ ret = SPI_connect(); if (ret != SPI_OK_CONNECT) elog(ERROR, "pg_dbms_stats: SPI_connect => %d", ret); /* lock dummy statistics tables. */ spi_exec_utility("LOCK dbms_stats.relation_stats_locked" " IN SHARE UPDATE EXCLUSIVE MODE"); spi_exec_utility("LOCK dbms_stats.column_stats_locked" " IN SHARE UPDATE EXCLUSIVE MODE"); /* * Create a temp table to save the statistics to import. * This table should fit with the content of export files. */ spi_exec_utility("CREATE TEMP TABLE dbms_stats_work_stats (" "nspname name NOT NULL," "relname name NOT NULL," "relpages int4 NOT NULL," "reltuples float4 NOT NULL," #if PG_VERSION_NUM >= 90200 "relallvisible int4 NOT NULL," #endif "curpages int4 NOT NULL," "last_analyze timestamp with time zone," "last_autoanalyze timestamp with time zone," "attname name," "nspname_of_typename name," "typname name," "atttypmod int4," "stainherit bool," "stanullfrac float4," "stawidth int4," "stadistinct float4," "stakind1 int2," "stakind2 int2," "stakind3 int2," "stakind4 int2," #if PG_VERSION_NUM >= 90200 "stakind5 int2," #endif "staop1 oid," "staop2 oid," "staop3 oid," "staop4 oid," #if PG_VERSION_NUM >= 90200 "staop5 oid," #endif "stanumbers1 float4[]," "stanumbers2 float4[]," "stanumbers3 float4[]," "stanumbers4 float4[]," #if PG_VERSION_NUM >= 90200 "stanumbers5 float4[]," #endif "stavalues1 dbms_stats.anyarray," "stavalues2 dbms_stats.anyarray," "stavalues3 dbms_stats.anyarray," "stavalues4 dbms_stats.anyarray" #if PG_VERSION_NUM >= 90200 ",stavalues5 dbms_stats.anyarray" #endif ")"); /* load the statistics from export file to the temp table */ import_stats_from_file(filename, nspname, relname, attname); /* Determine the Oid of local table from the tablename and schemaname. */ ret = SPI_execute("SELECT DISTINCT w.nspname, w.relname, c.oid, " "w.relpages, w.reltuples, " "w.curpages, w.last_analyze, w.last_autoanalyze " #if PG_VERSION_NUM >= 90200 ",w.relallvisible " #endif "FROM pg_catalog.pg_class c " "JOIN pg_catalog.pg_namespace n " "ON (c.relnamespace = n.oid) " "RIGHT JOIN dbms_stats_work_stats w " "ON (w.relname = c.relname AND w.nspname = n.nspname) " "ORDER BY 1, 2", false, 0); if (ret != SPI_OK_SELECT) elog(ERROR, "pg_dbms_stats: SPI_execute => %d", ret); /* * If there is no record in the staging table after loading source and * deleting unnecessary records, we treat it as an error. */ if (SPI_processed == 0) elog(ERROR, "no per-table statistic data to be imported"); /* */ r_num = SPI_processed; r_tups = SPI_tuptable->vals; r_tupdesc = SPI_tuptable->tupdesc; for (i = 0; i < r_num; i++) { bool isnull; Datum w_nspname; Datum w_relname; Datum w_relid; Datum values[9]; char nulls[9] = {'t', 't', 't', 't', 't', 't', 't', 't', 't'}; Oid r_types[9] = {NAMEOID, NAMEOID, INT4OID, FLOAT4OID, INT4OID, TIMESTAMPTZOID, TIMESTAMPTZOID, OIDOID, INT4OID}; Oid c_types[5] = {OIDOID, INT2OID, NAMEOID, NAMEOID, NAMEOID}; uint32 c_num; TupleDesc c_tupdesc; HeapTuple *c_tups; int j; values[0] = w_nspname = SPI_getbinval(r_tups[i], r_tupdesc, 1, &isnull); values[1] = w_relname = SPI_getbinval(r_tups[i], r_tupdesc, 2, &isnull); values[7] = w_relid = SPI_getbinval(r_tups[i], r_tupdesc, 3, &isnull); if (isnull) { elog(WARNING, "relation \"%s.%s\" does not exist", DatumGetName(w_nspname)->data, DatumGetName(w_relname)->data); continue; } values[2] = SPI_getbinval(r_tups[i], r_tupdesc, 4, &isnull); values[3] = SPI_getbinval(r_tups[i], r_tupdesc, 5, &isnull); values[4] = SPI_getbinval(r_tups[i], r_tupdesc, 6, &isnull); values[5] = SPI_getbinval(r_tups[i], r_tupdesc, 7, &isnull); nulls[5] = isnull ? 'n' : 't'; values[6] = SPI_getbinval(r_tups[i], r_tupdesc, 8, &isnull); nulls[6] = isnull ? 'n' : 't'; values[8] = SPI_getbinval(r_tups[i], r_tupdesc, 9, &isnull); /* * First we try UPDATE with the oid. When no record matched, try * INSERT. We can't use DELETE-then-INSERT method because we have FK * on relation_stats_locked so DELETE would delete child records in * column_stats_locked undesirably. */ spi_exec_query("UPDATE dbms_stats.relation_stats_locked SET " "relname = quote_ident($1) || '.' || quote_ident($2), " "relpages = $3, reltuples = $4, " #if PG_VERSION_NUM >= 90200 "relallvisible = $9, " #endif "curpages = $5, last_analyze = $6, last_autoanalyze = $7 " "WHERE relid = $8", RELATION_PARAM_NUM, r_types, &r_upd_plan, values, nulls, SPI_OK_UPDATE); if (SPI_processed == 0) { spi_exec_query("INSERT INTO dbms_stats.relation_stats_locked " "(relname, relpages, reltuples, curpages, " "last_analyze, last_autoanalyze, relid" #if PG_VERSION_NUM >= 90200 ", relallvisible" #endif ") VALUES (quote_ident($1) || '.' || quote_ident($2), " "$3, $4, $5, $6, $7, $8" #if PG_VERSION_NUM >= 90200 ", $9" #endif ")", RELATION_PARAM_NUM, r_types, &r_ins_plan, values, nulls, SPI_OK_INSERT); /* If we failed to insert, we can't proceed. */ if (SPI_processed != 1) elog(ERROR, "failed to insert import data"); } elog(DEBUG2, "\"%s.%s\" relation statistic import", DatumGetName(w_nspname)->data, DatumGetName(w_relname)->data); /* * Determine the attnum of the attribute with given name, and load * statistics from temp table into dbms.column_stats_locked. */ spi_exec_query("SELECT w.stainherit, w.attname, a.attnum, " "w.nspname_of_typename, tn.nspname, " "w.typname, t.typname, w.atttypmod, a.atttypmod " "FROM pg_catalog.pg_class c " "JOIN pg_catalog.pg_namespace cn " "ON (cn.oid = c.relnamespace) " "JOIN pg_catalog.pg_attribute a " "ON (a.attrelid = c.oid) " "JOIN pg_catalog.pg_type t " "ON (t.oid = a.atttypid) " "JOIN pg_catalog.pg_namespace tn " "ON (tn.oid = t.typnamespace) " "RIGHT JOIN dbms_stats_work_stats w " "ON (w.nspname = cn.nspname AND w.relname = c.relname " "AND (w.attname = a.attname OR w.attname = '')) " "WHERE w.nspname = $1 AND w.relname = $2 " "AND a.attnum > 0" "ORDER BY 1, 3, 2", 2, r_types, &c_sel_plan, values, NULL, SPI_OK_SELECT); /* This query ought to return at least one record. */ if (SPI_processed == 0) elog(ERROR, "no per-column statistic data to be imported"); values[0] = w_relid; values[2] = w_nspname; values[3] = w_relname; c_num = SPI_processed; c_tups = SPI_tuptable->vals; c_tupdesc = SPI_tuptable->tupdesc; for (j = 0; j < c_num; j++) { char *w_typnamespace; char *a_typnamespace; char *w_typname; char *a_typname; int w_typmod; int a_typmod; /* * If we have only per-relation statistics in source, all of * column_stats_effective for per-column statistics are NULL. */ (void) SPI_getbinval(c_tups[j], c_tupdesc, 1, &isnull); if (isnull) continue; /* * If there is no column with given name, we skip the rest of * import process. */ values[4] = SPI_getbinval(c_tups[j], c_tupdesc, 2, &isnull); values[1] = SPI_getbinval(c_tups[j], c_tupdesc, 3, &isnull); if (isnull) { elog(WARNING, "column \"%s\" of \"%s.%s\" does not exist", DatumGetName(values[4])->data, DatumGetName(w_nspname)->data, DatumGetName(w_relname)->data); continue; } /* * If the destination column has different data type from source * column, we stop importing to avoid corrupted statistics. */ w_typnamespace = DatumGetName(SPI_getbinval(c_tups[j], c_tupdesc, 4, &isnull))->data; a_typnamespace = DatumGetName(SPI_getbinval(c_tups[j], c_tupdesc, 5, &isnull))->data; w_typname = DatumGetName(SPI_getbinval(c_tups[j], c_tupdesc, 6, &isnull))->data; a_typname = DatumGetName(SPI_getbinval(c_tups[j], c_tupdesc, 7, &isnull))->data; if (strcmp(w_typnamespace, a_typnamespace) != 0 || strcmp(w_typname, a_typname) != 0) { ereport(WARNING, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" is of type \"%s.%s\"" " but import data is of type \"%s.%s\"", DatumGetName(values[4])->data, a_typnamespace, a_typname, w_typnamespace, w_typname))); continue; } /* * If the atttypmod of the destination column is different from the * one of source, column, we stop importing to avoid corrupted * statistics. */ w_typmod = DatumGetInt32(SPI_getbinval(c_tups[j], c_tupdesc, 8, &isnull)); a_typmod = DatumGetInt32(SPI_getbinval(c_tups[j], c_tupdesc, 9, &isnull)); if (w_typmod != a_typmod) { ereport(WARNING, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" is of atttypmod %d" " but import data is of atttypmod %d", DatumGetName(values[4])->data, a_typmod, a_typmod))); continue; } /* * First delete old dummy statistics, and import new one. We use * DELETE-then-INSERT method here to simplify codes. */ spi_exec_query("DELETE FROM dbms_stats.column_stats_locked " "WHERE starelid = $1 AND staattnum = $2", 2, c_types, &c_del_plan, values, NULL, SPI_OK_DELETE); spi_exec_query("INSERT INTO dbms_stats.column_stats_locked " "SELECT $1, $2, " "stainherit, stanullfrac, stawidth, stadistinct, " "stakind1, stakind2, stakind3, stakind4, " #if PG_VERSION_NUM >= 90200 "stakind5, " #endif "staop1, staop2, staop3, staop4, " #if PG_VERSION_NUM >= 90200 "staop5, " #endif "stanumbers1, stanumbers2, stanumbers3, stanumbers4, " #if PG_VERSION_NUM >= 90200 "stanumbers5, " #endif "stavalues1, stavalues2, stavalues3, stavalues4 " #if PG_VERSION_NUM >= 90200 ", stavalues5 " #endif "FROM dbms_stats_work_stats " "WHERE nspname = $3 AND relname = $4 " "AND attname = $5 " "ORDER BY 3", 5, c_types, &c_ins_plan, values, NULL, SPI_OK_INSERT); elog(DEBUG2, "\"%s.%s.%s\" column statistic import", DatumGetName(w_nspname)->data, DatumGetName(w_relname)->data, DatumGetName(values[4])->data); } if (c_num == 0) elog(DEBUG2, "\"%s.%s\" column statistic no data", DatumGetName(w_nspname)->data, DatumGetName(w_relname)->data); } /* release the cached plan */ SPI_freeplan(r_upd_plan); SPI_freeplan(r_ins_plan); SPI_freeplan(c_sel_plan); SPI_freeplan(c_del_plan); SPI_freeplan(c_ins_plan); /* delete the temp table */ spi_exec_utility("DROP TABLE dbms_stats_work_stats"); /* disconnect SPI */ ret = SPI_finish(); if (ret != SPI_OK_FINISH) elog(ERROR, "pg_dbms_stats: SPI_finish => %d", ret); /* * Recover the protocol state because it has been invalidated by our * COPY-from-stdin. */ if (filename == NULL) pq_puttextmessage('C', "dbms_stats_import"); PG_RETURN_VOID(); }
Datum _Slony_I_logTrigger(PG_FUNCTION_ARGS) { TransactionId newXid = GetTopTransactionId(); Slony_I_ClusterStatus *cs; TriggerData *tg; Datum argv[4]; text *cmdtype = NULL; int rc; Name cluster_name; int32 tab_id; char *attkind; int attkind_idx; int cmddata_need; /* * Don't do any logging if the current session role isn't Origin. */ if (SessionReplicationRole != SESSION_REPLICATION_ROLE_ORIGIN) return PointerGetDatum(NULL); /* * Get the trigger call context */ if (!CALLED_AS_TRIGGER(fcinfo)) elog(ERROR, "Slony-I: logTrigger() not called as trigger"); tg = (TriggerData *) (fcinfo->context); /* * Check all logTrigger() calling conventions */ if (!TRIGGER_FIRED_AFTER(tg->tg_event)) elog(ERROR, "Slony-I: logTrigger() must be fired AFTER"); if (!TRIGGER_FIRED_FOR_ROW(tg->tg_event)) elog(ERROR, "Slony-I: logTrigger() must be fired FOR EACH ROW"); if (tg->tg_trigger->tgnargs != 3) elog(ERROR, "Slony-I: logTrigger() must be defined with 3 args"); /* * Connect to the SPI manager */ if ((rc = SPI_connect()) < 0) elog(ERROR, "Slony-I: SPI_connect() failed in createEvent()"); /* * Get all the trigger arguments */ cluster_name = DatumGetName(DirectFunctionCall1(namein, CStringGetDatum(tg->tg_trigger->tgargs[0]))); tab_id = strtol(tg->tg_trigger->tgargs[1], NULL, 10); attkind = tg->tg_trigger->tgargs[2]; /* * Get or create the cluster status information and make sure it has the * SPI plans that we need here. */ cs = getClusterStatus(cluster_name, PLAN_INSERT_LOG); /* * Do the following only once per transaction. */ if (!TransactionIdEquals(cs->currentXid, newXid)) { int32 log_status; bool isnull; /* * Determine the currently active log table */ if (SPI_execp(cs->plan_get_logstatus, NULL, NULL, 0) < 0) elog(ERROR, "Slony-I: cannot determine log status"); if (SPI_processed != 1) elog(ERROR, "Slony-I: cannot determine log status"); log_status = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); SPI_freetuptable(SPI_tuptable); switch (log_status) { case 0: case 2: cs->plan_active_log = cs->plan_insert_log_1; break; case 1: case 3: cs->plan_active_log = cs->plan_insert_log_2; break; default: elog(ERROR, "Slony-I: illegal log status %d", log_status); break; } cs->currentXid = newXid; } /* * Determine cmdtype and cmddata depending on the command type */ if (TRIGGER_FIRED_BY_INSERT(tg->tg_event)) { HeapTuple new_row = tg->tg_trigtuple; TupleDesc tupdesc = tg->tg_relation->rd_att; char *col_ident; char *col_value; int len_ident; int len_value; int i; int need_comma = false; char *OldDateStyle; char *cp = VARDATA(cs->cmddata_buf); /* * INSERT * * cmdtype = 'I' cmddata = ("col" [, ...]) values ('value' [, ...]) */ cmdtype = cs->cmdtype_I; /* * Specify all the columns */ *cp++ = '('; for (i = 0; i < tg->tg_relation->rd_att->natts; i++) { /* * Skip dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; col_ident = (char *) slon_quote_identifier(SPI_fname(tupdesc, i + 1)); cmddata_need = (cp - (char *) (cs->cmddata_buf)) + 16 + (len_ident = strlen(col_ident)); if (cs->cmddata_size < cmddata_need) { int have = (cp - (char *) (cs->cmddata_buf)); while (cs->cmddata_size < cmddata_need) cs->cmddata_size *= 2; cs->cmddata_buf = realloc(cs->cmddata_buf, cs->cmddata_size); cp = (char *) (cs->cmddata_buf) + have; } if (need_comma) *cp++ = ','; else need_comma = true; memcpy(cp, col_ident, len_ident); cp += len_ident; } /* * Append the string ") values (" */ *cp++ = ')'; *cp++ = ' '; *cp++ = 'v'; *cp++ = 'a'; *cp++ = 'l'; *cp++ = 'u'; *cp++ = 'e'; *cp++ = 's'; *cp++ = ' '; *cp++ = '('; /* * Append the values */ need_comma = false; OldDateStyle = GetConfigOptionByName("DateStyle", NULL); if (!strstr(OldDateStyle, "ISO")) set_config_option("DateStyle", "ISO", PGC_USERSET, PGC_S_SESSION, true, true); for (i = 0; i < tg->tg_relation->rd_att->natts; i++) { /* * Skip dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; if ((col_value = SPI_getvalue(new_row, tupdesc, i + 1)) == NULL) { col_value = "NULL"; } else { col_value = slon_quote_literal(col_value); } cmddata_need = (cp - (char *) (cs->cmddata_buf)) + 16 + (len_value = strlen(col_value)); if (cs->cmddata_size < cmddata_need) { int have = (cp - (char *) (cs->cmddata_buf)); while (cs->cmddata_size < cmddata_need) cs->cmddata_size *= 2; cs->cmddata_buf = realloc(cs->cmddata_buf, cs->cmddata_size); cp = (char *) (cs->cmddata_buf) + have; } if (need_comma) *cp++ = ','; else need_comma = true; memcpy(cp, col_value, len_value); cp += len_value; } if (!strstr(OldDateStyle, "ISO")) set_config_option("DateStyle", OldDateStyle, PGC_USERSET, PGC_S_SESSION, true, true); /* * Terminate and done */ *cp++ = ')'; *cp = '\0'; SET_VARSIZE(cs->cmddata_buf, VARHDRSZ + (cp - VARDATA(cs->cmddata_buf))); } else if (TRIGGER_FIRED_BY_UPDATE(tg->tg_event)) { HeapTuple old_row = tg->tg_trigtuple; HeapTuple new_row = tg->tg_newtuple; TupleDesc tupdesc = tg->tg_relation->rd_att; Datum old_value; Datum new_value; bool old_isnull; bool new_isnull; char *col_ident; char *col_value; int len_ident; int len_value; int i; int need_comma = false; int need_and = false; char *OldDateStyle; char *cp = VARDATA(cs->cmddata_buf); /* * UPDATE * * cmdtype = 'U' cmddata = "col_ident"='value' [, ...] where * "pk_ident" = 'value' [ and ...] */ cmdtype = cs->cmdtype_U; for (i = 0; i < tg->tg_relation->rd_att->natts; i++) { /* * Ignore dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; old_value = SPI_getbinval(old_row, tupdesc, i + 1, &old_isnull); new_value = SPI_getbinval(new_row, tupdesc, i + 1, &new_isnull); /* * If old and new value are NULL, the column is unchanged */ if (old_isnull && new_isnull) continue; /* * If both are NOT NULL, we need to compare the values and skip * setting the column if equal */ if (!old_isnull && !new_isnull) { Oid opr_oid; FmgrInfo *opr_finfo_p; /* * Lookup the equal operators function call info using the * typecache if available */ #ifdef HAVE_TYPCACHE TypeCacheEntry *type_cache; type_cache = lookup_type_cache( SPI_gettypeid(tupdesc, i + 1), TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO); opr_oid = type_cache->eq_opr; if (opr_oid == ARRAY_EQ_OP) opr_oid = InvalidOid; else opr_finfo_p = &(type_cache->eq_opr_finfo); #else FmgrInfo opr_finfo; opr_oid = compatible_oper_funcid(makeList1(makeString("=")), SPI_gettypeid(tupdesc, i + 1), SPI_gettypeid(tupdesc, i + 1), true); if (OidIsValid(opr_oid)) { fmgr_info(opr_oid, &opr_finfo); opr_finfo_p = &opr_finfo; } #endif /* * If we have an equal operator, use that to do binary * comparision. Else get the string representation of both * attributes and do string comparision. */ if (OidIsValid(opr_oid)) { if (DatumGetBool(FunctionCall2(opr_finfo_p, old_value, new_value))) continue; } else { char *old_strval = SPI_getvalue(old_row, tupdesc, i + 1); char *new_strval = SPI_getvalue(new_row, tupdesc, i + 1); if (strcmp(old_strval, new_strval) == 0) continue; } } if (need_comma) *cp++ = ','; else need_comma = true; col_ident = (char *) slon_quote_identifier(SPI_fname(tupdesc, i + 1)); if (new_isnull) col_value = "NULL"; else { OldDateStyle = GetConfigOptionByName("DateStyle", NULL); if (!strstr(OldDateStyle, "ISO")) set_config_option("DateStyle", "ISO", PGC_USERSET, PGC_S_SESSION, true, true); col_value = slon_quote_literal(SPI_getvalue(new_row, tupdesc, i + 1)); if (!strstr(OldDateStyle, "ISO")) set_config_option("DateStyle", OldDateStyle, PGC_USERSET, PGC_S_SESSION, true, true); } cmddata_need = (cp - (char *) (cs->cmddata_buf)) + 16 + (len_ident = strlen(col_ident)) + (len_value = strlen(col_value)); if (cs->cmddata_size < cmddata_need) { int have = (cp - (char *) (cs->cmddata_buf)); while (cs->cmddata_size < cmddata_need) cs->cmddata_size *= 2; cs->cmddata_buf = realloc(cs->cmddata_buf, cs->cmddata_size); cp = (char *) (cs->cmddata_buf) + have; } memcpy(cp, col_ident, len_ident); cp += len_ident; *cp++ = '='; memcpy(cp, col_value, len_value); cp += len_value; } /* * It can happen that the only UPDATE an application does is to set a * column to the same value again. In that case, we'd end up here with * no columns in the SET clause yet. We add the first key column here * with it's old value to simulate the same for the replication * engine. */ if (!need_comma) { for (i = 0, attkind_idx = -1; i < tg->tg_relation->rd_att->natts; i++) { if (tupdesc->attrs[i]->attisdropped) continue; attkind_idx++; if (!attkind[attkind_idx]) elog(ERROR, "Slony-I: no key columns found in logTrigger() attkind parameter"); if (attkind[attkind_idx] == 'k') break; } col_ident = (char *) slon_quote_identifier(SPI_fname(tupdesc, i + 1)); col_value = slon_quote_literal(SPI_getvalue(old_row, tupdesc, i + 1)); cmddata_need = (cp - (char *) (cs->cmddata_buf)) + 16 + (len_ident = strlen(col_ident)) + (len_value = strlen(col_value)); if (cs->cmddata_size < cmddata_need) { int have = (cp - (char *) (cs->cmddata_buf)); while (cs->cmddata_size < cmddata_need) cs->cmddata_size *= 2; cs->cmddata_buf = realloc(cs->cmddata_buf, cs->cmddata_size); cp = (char *) (cs->cmddata_buf) + have; } memcpy(cp, col_ident, len_ident); cp += len_ident; *cp++ = '='; memcpy(cp, col_value, len_value); cp += len_value; } *cp++ = ' '; *cp++ = 'w'; *cp++ = 'h'; *cp++ = 'e'; *cp++ = 'r'; *cp++ = 'e'; *cp++ = ' '; for (i = 0, attkind_idx = -1; i < tg->tg_relation->rd_att->natts; i++) { /* * Ignore dropped columns */ if (tupdesc->attrs[i]->attisdropped) continue; attkind_idx++; if (!attkind[attkind_idx]) break; if (attkind[attkind_idx] != 'k') continue; col_ident = (char *) slon_quote_identifier(SPI_fname(tupdesc, i + 1)); col_value = slon_quote_literal(SPI_getvalue(old_row, tupdesc, i + 1)); if (col_value == NULL) elog(ERROR, "Slony-I: old key column %s.%s IS NULL on UPDATE", NameStr(tg->tg_relation->rd_rel->relname), col_ident); cmddata_need = (cp - (char *) (cs->cmddata_buf)) + 16 + (len_ident = strlen(col_ident)) + (len_value = strlen(col_value)); if (cs->cmddata_size < cmddata_need) { int have = (cp - (char *) (cs->cmddata_buf)); while (cs->cmddata_size < cmddata_need) cs->cmddata_size *= 2; cs->cmddata_buf = realloc(cs->cmddata_buf, cs->cmddata_size); cp = (char *) (cs->cmddata_buf) + have; } if (need_and) { *cp++ = ' '; *cp++ = 'a'; *cp++ = 'n'; *cp++ = 'd'; *cp++ = ' '; } else need_and = true; memcpy(cp, col_ident, len_ident); cp += len_ident; *cp++ = '='; memcpy(cp, col_value, len_value); cp += len_value; } *cp = '\0'; SET_VARSIZE(cs->cmddata_buf, VARHDRSZ + (cp - VARDATA(cs->cmddata_buf))); } else if (TRIGGER_FIRED_BY_DELETE(tg->tg_event)) { HeapTuple old_row = tg->tg_trigtuple; TupleDesc tupdesc = tg->tg_relation->rd_att; char *col_ident; char *col_value; int len_ident; int len_value; int i; int need_and = false; char *cp = VARDATA(cs->cmddata_buf); /* * DELETE * * cmdtype = 'D' cmddata = "pk_ident"='value' [and ...] */ cmdtype = cs->cmdtype_D; for (i = 0, attkind_idx = -1; i < tg->tg_relation->rd_att->natts; i++) { if (tupdesc->attrs[i]->attisdropped) continue; attkind_idx++; if (!attkind[attkind_idx]) break; if (attkind[attkind_idx] != 'k') continue; col_ident = (char *) slon_quote_identifier(SPI_fname(tupdesc, i + 1)); col_value = slon_quote_literal(SPI_getvalue(old_row, tupdesc, i + 1)); if (col_value == NULL) elog(ERROR, "Slony-I: old key column %s.%s IS NULL on DELETE", NameStr(tg->tg_relation->rd_rel->relname), col_ident); cmddata_need = (cp - (char *) (cs->cmddata_buf)) + 16 + (len_ident = strlen(col_ident)) + (len_value = strlen(col_value)); if (cs->cmddata_size < cmddata_need) { int have = (cp - (char *) (cs->cmddata_buf)); while (cs->cmddata_size < cmddata_need) cs->cmddata_size *= 2; cs->cmddata_buf = realloc(cs->cmddata_buf, cs->cmddata_size); cp = (char *) (cs->cmddata_buf) + have; } if (need_and) { *cp++ = ' '; *cp++ = 'a'; *cp++ = 'n'; *cp++ = 'd'; *cp++ = ' '; } else need_and = true; memcpy(cp, col_ident, len_ident); cp += len_ident; *cp++ = '='; memcpy(cp, col_value, len_value); cp += len_value; } *cp = '\0'; SET_VARSIZE(cs->cmddata_buf, VARHDRSZ + (cp - VARDATA(cs->cmddata_buf))); } else elog(ERROR, "Slony-I: logTrigger() fired for unhandled event"); /* * Construct the parameter array and insert the log row. */ argv[0] = Int32GetDatum(tab_id); argv[1] = PointerGetDatum(cmdtype); argv[2] = PointerGetDatum(cs->cmddata_buf); SPI_execp(cs->plan_active_log, argv, NULL, 0); SPI_finish(); return PointerGetDatum(NULL); }