/* * Decode an UPDATE entry */ static void decoder_raw_update(StringInfo s, Relation relation, HeapTuple oldtuple, HeapTuple newtuple) { TupleDesc tupdesc = RelationGetDescr(relation); int natt; bool first_column = true; /* If there are no new values, simply leave as there is nothing to do */ if (newtuple == NULL) return; appendStringInfo(s, "UPDATE "); print_relname(s, relation); /* Build the SET clause with the new values */ appendStringInfo(s, " SET "); for (natt = 0; natt < tupdesc->natts; natt++) { Form_pg_attribute attr; Datum origval; bool isnull; attr = tupdesc->attrs[natt]; /* Skip dropped columns and system columns */ if (attr->attisdropped || attr->attnum < 0) continue; /* Skip comma for first colums */ if (!first_column) { appendStringInfoString(s, ", "); } else first_column = false; /* Print attribute name */ appendStringInfo(s, "%s = ", quote_identifier(NameStr(attr->attname))); /* Get Datum from tuple */ origval = heap_getattr(newtuple, natt + 1, tupdesc, &isnull); /* Get output function */ print_value(s, origval, attr->atttypid, isnull); } /* Print WHERE clause */ print_where_clause(s, relation, oldtuple, newtuple); appendStringInfoString(s, ";"); }
static SV * plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc) { HV *hv; int i; hv = newHV(); for (i = 0; i < tupdesc->natts; i++) { Datum attr; bool isnull; char *attname; char *outputstr; Oid typoutput; bool typisvarlena; int namelen; SV *sv; if (tupdesc->attrs[i]->attisdropped) continue; attname = NameStr(tupdesc->attrs[i]->attname); namelen = strlen(attname); attr = heap_getattr(tuple, i + 1, tupdesc, &isnull); if (isnull) { /* Store (attname => undef) and move on. */ hv_store(hv, attname, namelen, newSV(0), 0); continue; } /* XXX should have a way to cache these lookups */ getTypeOutputInfo(tupdesc->attrs[i]->atttypid, &typoutput, &typisvarlena); outputstr = DatumGetCString(OidFunctionCall1(typoutput, attr)); sv = newSVpv(outputstr, 0); #if PERL_BCDVERSION >= 0x5006000L if (GetDatabaseEncoding() == PG_UTF8) SvUTF8_on(sv); #endif hv_store(hv, attname, namelen, sv, 0); pfree(outputstr); } return newRV_noinc((SV *) hv); }
/* * GetComment -- get the comment for an object, or null if not found. */ char * GetComment(Oid oid, Oid classoid, int32 subid) { Relation description; ScanKeyData skey[3]; SysScanDesc sd; TupleDesc tupdesc; HeapTuple tuple; char *comment; /* Use the index to search for a matching old tuple */ ScanKeyInit(&skey[0], Anum_pg_description_objoid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(oid)); ScanKeyInit(&skey[1], Anum_pg_description_classoid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(classoid)); ScanKeyInit(&skey[2], Anum_pg_description_objsubid, BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(subid)); description = heap_open(DescriptionRelationId, AccessShareLock); tupdesc = RelationGetDescr(description); sd = systable_beginscan(description, DescriptionObjIndexId, true, SnapshotNow, 3, skey); comment = NULL; while ((tuple = systable_getnext(sd)) != NULL) { Datum value; bool isnull; /* Found the tuple, get description field */ value = heap_getattr(tuple, Anum_pg_description_description, tupdesc, &isnull); if (!isnull) comment = TextDatumGetCString(value); break; /* Assume there can be only one match */ } systable_endscan(sd); /* Done */ heap_close(description, AccessShareLock); return comment; }
/* * Transform a tuple into a Python dict object. */ static PyObject * PLyDict_FromTuple(PLyDatumToOb *arg, HeapTuple tuple, TupleDesc desc) { PyObject *volatile dict; /* Simple sanity check that desc matches */ Assert(desc->natts == arg->u.tuple.natts); dict = PyDict_New(); if (dict == NULL) return NULL; PG_TRY(); { int i; for (i = 0; i < arg->u.tuple.natts; i++) { PLyDatumToOb *att = &arg->u.tuple.atts[i]; Form_pg_attribute attr = TupleDescAttr(desc, i); char *key; Datum vattr; bool is_null; PyObject *value; if (attr->attisdropped) continue; key = NameStr(attr->attname); vattr = heap_getattr(tuple, (i + 1), desc, &is_null); if (is_null) PyDict_SetItemString(dict, key, Py_None); else { value = att->func(att, vattr); PyDict_SetItemString(dict, key, value); Py_DECREF(value); } } } PG_CATCH(); { Py_DECREF(dict); PG_RE_THROW(); } PG_END_TRY(); return dict; }
Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull) { SPI_result = 0; if (fnumber > tupdesc->natts || fnumber == 0 || fnumber <= FirstLowInvalidHeapAttributeNumber) { SPI_result = SPI_ERROR_NOATTRIBUTE; *isnull = true; return (Datum) NULL; } return heap_getattr(tuple, fnumber, tupdesc, isnull); }
/* * Get datum representations of the attoptions field in pg_attribute_encoding * for the given relation. */ Datum * get_rel_attoptions(Oid relid, AttrNumber max_attno) { Form_pg_attribute attform; HeapTuple tuple; cqContext cqc; cqContext *pcqCtx; Datum *dats; Relation pgae = heap_open(AttributeEncodingRelationId, AccessShareLock); /* used for attbyval and len below */ attform = pgae->rd_att->attrs[Anum_pg_attribute_encoding_attoptions - 1]; dats = palloc0(max_attno * sizeof(Datum)); pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), pgae), cql("SELECT * FROM pg_attribute_encoding " " WHERE attrelid = :1 ", ObjectIdGetDatum(relid))); while (HeapTupleIsValid(tuple = caql_getnext(pcqCtx))) { Form_pg_attribute_encoding a = (Form_pg_attribute_encoding)GETSTRUCT(tuple); int16 attnum = a->attnum; Datum attoptions; bool isnull; Insist(attnum > 0 && attnum <= max_attno); attoptions = heap_getattr(tuple, Anum_pg_attribute_encoding_attoptions, RelationGetDescr(pgae), &isnull); Insist(!isnull); dats[attnum - 1] = datumCopy(attoptions, attform->attbyval, attform->attlen); } caql_endscan(pcqCtx); heap_close(pgae, AccessShareLock); return dats; }
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) { char *result; Datum origval, val; bool isnull; Oid typoid, foutoid; bool typisvarlena; SPI_result = 0; if (fnumber > tupdesc->natts || fnumber == 0 || fnumber <= FirstLowInvalidHeapAttributeNumber) { SPI_result = SPI_ERROR_NOATTRIBUTE; return NULL; } origval = heap_getattr(tuple, fnumber, tupdesc, &isnull); if (isnull) return NULL; if (fnumber > 0) typoid = tupdesc->attrs[fnumber - 1]->atttypid; else typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; getTypeOutputInfo(typoid, &foutoid, &typisvarlena); /* * If we have a toasted datum, forcibly detoast it here to avoid memory * leakage inside the type's output routine. */ if (typisvarlena) val = PointerGetDatum(PG_DETOAST_DATUM(origval)); else val = origval; result = OidOutputFunctionCall(foutoid, val); /* Clean up detoasted copy, if any */ if (val != origval) pfree(DatumGetPointer(val)); return result; }
/* --------------------- * lookupProcCallback() - Find a specified callback for a specified function * * Parameters: * profnoid - oid of the function that has a callback * promethod - which callback to find * --------------------- */ Oid lookupProcCallback(Oid profnoid, char promethod) { Relation rel; ScanKeyData skey[2]; SysScanDesc scan; HeapTuple tup; Oid result; Insist(OidIsValid(profnoid)); /* open pg_proc_callback */ rel = heap_open(ProcCallbackRelationId, AccessShareLock); /* Lookup (profnoid, promethod) from index */ /* (profnoid, promethod) is guaranteed unique by the index */ ScanKeyInit(&skey[0], Anum_pg_proc_callback_profnoid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(profnoid)); ScanKeyInit(&skey[1], Anum_pg_proc_callback_promethod, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(promethod)); scan = systable_beginscan(rel, ProcCallbackProfnoidPromethodIndexId, true, SnapshotNow, 2, skey); tup = systable_getnext(scan); if (HeapTupleIsValid(tup)) { Datum d; bool isnull; d = heap_getattr(tup, Anum_pg_proc_callback_procallback, RelationGetDescr(rel), &isnull); Assert(!isnull); result = DatumGetObjectId(d); } else result = InvalidOid; systable_endscan(scan); heap_close(rel, AccessShareLock); return result; }
/* * Scan pg_db_role_setting looking for applicable settings, and load them on * the current process. * * relsetting is pg_db_role_setting, already opened and locked. * * Note: we only consider setting for the exact databaseid/roleid combination. * This probably needs to be called more than once, with InvalidOid passed as * databaseid/roleid. */ void ApplySetting(Snapshot snapshot, Oid databaseid, Oid roleid, Relation relsetting, GucSource source) { SysScanDesc scan; ScanKeyData keys[2]; HeapTuple tup; ScanKeyInit(&keys[0], Anum_pg_db_role_setting_setdatabase, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(databaseid)); ScanKeyInit(&keys[1], Anum_pg_db_role_setting_setrole, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(roleid)); scan = systable_beginscan(relsetting, DbRoleSettingDatidRolidIndexId, true, snapshot, 2, keys); while (HeapTupleIsValid(tup = systable_getnext(scan))) { bool isnull; Datum datum; datum = heap_getattr(tup, Anum_pg_db_role_setting_setconfig, RelationGetDescr(relsetting), &isnull); if (!isnull) { ArrayType *a = DatumGetArrayTypeP(datum); /* * We process all the options at SUSET level. We assume that the * right to insert an option into pg_db_role_setting was checked * when it was inserted. */ ProcessGUCArray(a, PGC_SUSET, source, GUC_ACTION_SET); } } systable_endscan(scan); }
/* * SysCacheGetAttr * * Given a tuple previously fetched by SearchSysCache(), * extract a specific attribute. * * This is equivalent to using heap_getattr() on a tuple fetched * from a non-cached relation. Usually, this is only used for attributes * that could be NULL or variable length; the fixed-size attributes in * a system table are accessed just by mapping the tuple onto the C struct * declarations from include/catalog/. * * As with heap_getattr(), if the attribute is of a pass-by-reference type * then a pointer into the tuple data area is returned --- the caller must * not modify or pfree the datum! */ Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull) { /* * We just need to get the TupleDesc out of the cache entry, and then * we can apply heap_getattr(). We expect that the cache control data * is currently valid --- if the caller recently fetched the tuple, * then it should be. */ if (cacheId < 0 || cacheId >= SysCacheSize) elog(ERROR, "invalid cache id: %d", cacheId); if (!PointerIsValid(SysCache[cacheId]) || !PointerIsValid(SysCache[cacheId]->cc_tupdesc)) elog(ERROR, "missing cache data for cache id %d", cacheId); return heap_getattr(tup, attributeNumber, SysCache[cacheId]->cc_tupdesc, isNull); }
/* * GetSysCacheOid * * A convenience routine that does SearchSysCache and returns the OID in the * oidcol column of the found tuple, or InvalidOid if no tuple could be found. * No lock is retained on the syscache entry. */ Oid GetSysCacheOid(int cacheId, AttrNumber oidcol, Datum key1, Datum key2, Datum key3, Datum key4) { HeapTuple tuple; bool isNull; Oid result; tuple = SearchSysCache(cacheId, key1, key2, key3, key4); if (!HeapTupleIsValid(tuple)) return InvalidOid; result = heap_getattr(tuple, oidcol, SysCache[cacheId]->cc_tupdesc, &isNull); Assert(!isNull); /* columns used as oids should never be NULL */ ReleaseSysCache(tuple); return result; }
uint64 lookup_pkey(Oid heapRelOid, char *pkeyFieldname, ItemPointer ctid) { Relation heapRel; HeapTupleData tuple; Buffer buffer; Datum pkey; bool isnull; heapRel = RelationIdGetRelation(heapRelOid); tuple.t_self = *ctid; if (!heap_fetch(heapRel, SnapshotAny, &tuple, &buffer, false, NULL)) elog(ERROR, "Unable to fetch heap page from index"); pkey = heap_getattr(&tuple, get_attnum(heapRelOid, pkeyFieldname), RelationGetDescr(heapRel), &isnull); if (isnull) elog(ERROR, "detected NULL key value. Cannot update row"); ReleaseBuffer(buffer); RelationClose(heapRel); return DatumGetInt64(pkey); }
static void luaP_pushtuple_cmn (lua_State *L, HeapTuple tuple, int readonly, RTupDesc* rtupdesc) { luaP_Tuple *t; TupleDesc tupleDesc; int i, n; BEGINLUA; tupleDesc = rtupdesc->tupdesc; n = tupleDesc->natts; t = lua_newuserdata(L, sizeof(luaP_Tuple) + n * (sizeof(Datum) + sizeof(bool))); t->value = (Datum *) (t + 1); t->null = (bool *) (t->value + n); t->rtupdesc = rtupdesc_ref(rtupdesc); for (i = 0; i < n; i++) { bool isnull; t->value[i] = heap_getattr(tuple, tupleDesc->attrs[i]->attnum, tupleDesc, &isnull); t->null[i] = isnull; } if (readonly) { t->changed = -1; } else { t->changed = 0; } t->tupdesc = 0; t->relid = 0; t->tuple = tuple; luaP_getfield(L, PLLUA_TUPLEMT); lua_setmetatable(L, -2); ENDLUAV(1); }
void composite_to_bson(mongo::BSONObjBuilder& builder, Datum composite) { PGBSON_LOG << "BEGIN composite_to_bson" << PGBSON_ENDL; HeapTupleHeader td; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tmptup; td = DatumGetHeapTupleHeader(composite); /* Extract rowtype info and find a tupdesc */ tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; HeapTupleData* tuple = &tmptup; for (int i = 0; i < tupdesc->natts; i++) { bool isnull; if (tupdesc->attrs[i]->attisdropped) continue; const char* field_name = NameStr(tupdesc->attrs[i]->attname); Datum val = heap_getattr(tuple, i + 1, tupdesc, &isnull); datum_to_bson(field_name, builder, val, isnull, tupdesc->attrs[i]->atttypid); } ReleaseTupleDesc(tupdesc); PGBSON_LOG << "END composite_to_bson" << PGBSON_ENDL; }
/* * SysCacheGetAttr * * Given a tuple previously fetched by SearchSysCache(), * extract a specific attribute. * * This is equivalent to using heap_getattr() on a tuple fetched * from a non-cached relation. Usually, this is only used for attributes * that could be NULL or variable length; the fixed-size attributes in * a system table are accessed just by mapping the tuple onto the C struct * declarations from include/catalog/. * * As with heap_getattr(), if the attribute is of a pass-by-reference type * then a pointer into the tuple data area is returned --- the caller must * not modify or pfree the datum! * * Note: it is legal to use SysCacheGetAttr() with a cacheId referencing * a different cache for the same catalog the tuple was fetched from. */ Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull) { /* * We just need to get the TupleDesc out of the cache entry, and then we * can apply heap_getattr(). Normally the cache control data is already * valid (because the caller recently fetched the tuple via this same * cache), but there are cases where we have to initialize the cache here. */ if (cacheId < 0 || cacheId >= SysCacheSize || !PointerIsValid(SysCache[cacheId])) elog(ERROR, "invalid cache id: %d", cacheId); if (!PointerIsValid(SysCache[cacheId]->cc_tupdesc)) { InitCatCachePhase2(SysCache[cacheId], false); Assert(PointerIsValid(SysCache[cacheId]->cc_tupdesc)); } return heap_getattr(tup, attributeNumber, SysCache[cacheId]->cc_tupdesc, isNull); }
/* * Look to see if we have template information for the given language name. */ static PLTemplate * find_language_template(const char *languageName) { PLTemplate *result; Relation rel; SysScanDesc scan; ScanKeyData key; HeapTuple tup; rel = heap_open(PLTemplateRelationId, AccessShareLock); ScanKeyInit(&key, Anum_pg_pltemplate_tmplname, BTEqualStrategyNumber, F_NAMEEQ, NameGetDatum(languageName)); scan = systable_beginscan(rel, PLTemplateNameIndexId, true, SnapshotNow, 1, &key); tup = systable_getnext(scan); if (HeapTupleIsValid(tup)) { Form_pg_pltemplate tmpl = (Form_pg_pltemplate) GETSTRUCT(tup); Datum datum; bool isnull; result = (PLTemplate *) palloc0(sizeof(PLTemplate)); result->tmpltrusted = tmpl->tmpltrusted; result->tmpldbacreate = tmpl->tmpldbacreate; /* Remaining fields are variable-width so we need heap_getattr */ datum = heap_getattr(tup, Anum_pg_pltemplate_tmplhandler, RelationGetDescr(rel), &isnull); if (!isnull) result->tmplhandler = TextDatumGetCString(datum); datum = heap_getattr(tup, Anum_pg_pltemplate_tmplinline, RelationGetDescr(rel), &isnull); if (!isnull) result->tmplinline = TextDatumGetCString(datum); datum = heap_getattr(tup, Anum_pg_pltemplate_tmplvalidator, RelationGetDescr(rel), &isnull); if (!isnull) result->tmplvalidator = TextDatumGetCString(datum); datum = heap_getattr(tup, Anum_pg_pltemplate_tmpllibrary, RelationGetDescr(rel), &isnull); if (!isnull) result->tmpllibrary = TextDatumGetCString(datum); /* Ignore template if handler or library info is missing */ if (!result->tmplhandler || !result->tmpllibrary) result = NULL; } else result = NULL; systable_endscan(scan); heap_close(rel, AccessShareLock); return result; }
/* * ndistinct_for_combination * Estimates number of distinct values in a combination of columns. * * This uses the same ndistinct estimator as compute_scalar_stats() in * ANALYZE, i.e., * n*d / (n - f1 + f1*n/N) * * except that instead of values in a single column we are dealing with * combination of multiple columns. */ static double ndistinct_for_combination(double totalrows, int numrows, HeapTuple *rows, VacAttrStats **stats, int k, int *combination) { int i, j; int f1, cnt, d; bool *isnull; Datum *values; SortItem *items; MultiSortSupport mss; mss = multi_sort_init(k); /* * In order to determine the number of distinct elements, create separate * values[]/isnull[] arrays with all the data we have, then sort them * using the specified column combination as dimensions. We could try to * sort in place, but it'd probably be more complex and bug-prone. */ items = (SortItem *) palloc(numrows * sizeof(SortItem)); values = (Datum *) palloc0(sizeof(Datum) * numrows * k); isnull = (bool *) palloc0(sizeof(bool) * numrows * k); for (i = 0; i < numrows; i++) { items[i].values = &values[i * k]; items[i].isnull = &isnull[i * k]; } /* * For each dimension, set up sort-support and fill in the values from the * sample data. */ for (i = 0; i < k; i++) { VacAttrStats *colstat = stats[combination[i]]; TypeCacheEntry *type; type = lookup_type_cache(colstat->attrtypid, TYPECACHE_LT_OPR); if (type->lt_opr == InvalidOid) /* shouldn't happen */ elog(ERROR, "cache lookup failed for ordering operator for type %u", colstat->attrtypid); /* prepare the sort function for this dimension */ multi_sort_add_dimension(mss, i, type->lt_opr); /* accumulate all the data for this dimension into the arrays */ for (j = 0; j < numrows; j++) { items[j].values[i] = heap_getattr(rows[j], colstat->attr->attnum, colstat->tupDesc, &items[j].isnull[i]); } } /* We can sort the array now ... */ qsort_arg((void *) items, numrows, sizeof(SortItem), multi_sort_compare, mss); /* ... and count the number of distinct combinations */ f1 = 0; cnt = 1; d = 1; for (i = 1; i < numrows; i++) { if (multi_sort_compare(&items[i], &items[i - 1], mss) != 0) { if (cnt == 1) f1 += 1; d++; cnt = 0; } cnt += 1; } if (cnt == 1) f1 += 1; return estimate_ndistinct(totalrows, numrows, d, f1); }
/* ---------- * toast_insert_or_update - * * Delete no-longer-used toast-entries and create new ones to * make the new tuple fit on INSERT or UPDATE * ---------- */ static void toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup) { TupleDesc tupleDesc; Form_pg_attribute *att; int numAttrs; int i; bool old_isnull; bool new_isnull; bool need_change = false; bool need_free = false; bool need_delold = false; bool has_nulls = false; Size maxDataLen; char toast_action[MaxHeapAttributeNumber]; char toast_nulls[MaxHeapAttributeNumber]; Datum toast_values[MaxHeapAttributeNumber]; int32 toast_sizes[MaxHeapAttributeNumber]; bool toast_free[MaxHeapAttributeNumber]; bool toast_delold[MaxHeapAttributeNumber]; /* * Get the tuple descriptor, the number of and attribute descriptors * and the location of the tuple values. */ tupleDesc = rel->rd_att; numAttrs = tupleDesc->natts; att = tupleDesc->attrs; /* ---------- * Then collect information about the values given * * NOTE: toast_action[i] can have these values: * ' ' default handling * 'p' already processed --- don't touch it * 'x' incompressible, but OK to move off * ---------- */ memset(toast_action, ' ', numAttrs * sizeof(char)); memset(toast_nulls, ' ', numAttrs * sizeof(char)); memset(toast_free, 0, numAttrs * sizeof(bool)); memset(toast_delold, 0, numAttrs * sizeof(bool)); for (i = 0; i < numAttrs; i++) { varattrib *old_value; varattrib *new_value; if (oldtup != NULL) { /* * For UPDATE get the old and new values of this attribute */ old_value = (varattrib *) DatumGetPointer( heap_getattr(oldtup, i + 1, tupleDesc, &old_isnull)); toast_values[i] = heap_getattr(newtup, i + 1, tupleDesc, &new_isnull); new_value = (varattrib *) DatumGetPointer(toast_values[i]); /* * If the old value is an external stored one, check if it has * changed so we have to delete it later. */ if (!old_isnull && att[i]->attlen == -1 && VARATT_IS_EXTERNAL(old_value)) { if (new_isnull || !VARATT_IS_EXTERNAL(new_value) || old_value->va_content.va_external.va_valueid != new_value->va_content.va_external.va_valueid || old_value->va_content.va_external.va_toastrelid != new_value->va_content.va_external.va_toastrelid) { /* * The old external store value isn't needed any more * after the update */ toast_delold[i] = true; need_delold = true; } else { /* * This attribute isn't changed by this update so we * reuse the original reference to the old value in * the new tuple. */ toast_action[i] = 'p'; toast_sizes[i] = VARATT_SIZE(toast_values[i]); continue; } } } else { /* * For INSERT simply get the new value */ toast_values[i] = heap_getattr(newtup, i + 1, tupleDesc, &new_isnull); } /* * Handle NULL attributes */ if (new_isnull) { toast_action[i] = 'p'; toast_nulls[i] = 'n'; has_nulls = true; continue; } /* * Now look at varsize attributes */ if (att[i]->attlen == -1) { /* * If the table's attribute says PLAIN always, force it so. */ if (att[i]->attstorage == 'p') toast_action[i] = 'p'; /* * We took care of UPDATE above, so any external value we find * still in the tuple must be someone else's we cannot reuse. * Expand it to plain (and, probably, toast it again below). */ if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i]))) { toast_values[i] = PointerGetDatum(heap_tuple_untoast_attr( (varattrib *) DatumGetPointer(toast_values[i]))); toast_free[i] = true; need_change = true; need_free = true; } /* * Remember the size of this attribute */ toast_sizes[i] = VARATT_SIZE(DatumGetPointer(toast_values[i])); } else { /* * Not a variable size attribute, plain storage always */ toast_action[i] = 'p'; toast_sizes[i] = att[i]->attlen; } } /* ---------- * Compress and/or save external until data fits into target length * * 1: Inline compress attributes with attstorage 'x' * 2: Store attributes with attstorage 'x' or 'e' external * 3: Inline compress attributes with attstorage 'm' * 4: Store attributes with attstorage 'm' external * ---------- */ maxDataLen = offsetof(HeapTupleHeaderData, t_bits); if (has_nulls) maxDataLen += BITMAPLEN(numAttrs); maxDataLen = TOAST_TUPLE_TARGET - MAXALIGN(maxDataLen); /* * Look for attributes with attstorage 'x' to compress */ while (MAXALIGN(ComputeDataSize(tupleDesc, toast_values, toast_nulls)) > maxDataLen) { int biggest_attno = -1; int32 biggest_size = MAXALIGN(sizeof(varattrib)); Datum old_value; Datum new_value; /* * Search for the biggest yet uncompressed internal attribute */ for (i = 0; i < numAttrs; i++) { if (toast_action[i] != ' ') continue; if (VARATT_IS_EXTENDED(toast_values[i])) continue; if (att[i]->attstorage != 'x') continue; if (toast_sizes[i] > biggest_size) { biggest_attno = i; biggest_size = toast_sizes[i]; } } if (biggest_attno < 0) break; /* * Attempt to compress it inline */ i = biggest_attno; old_value = toast_values[i]; new_value = toast_compress_datum(old_value); if (DatumGetPointer(new_value) != NULL) { /* successful compression */ if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_values[i] = new_value; toast_free[i] = true; toast_sizes[i] = VARATT_SIZE(toast_values[i]); need_change = true; need_free = true; } else { /* * incompressible data, ignore on subsequent compression * passes */ toast_action[i] = 'x'; } } /* * Second we look for attributes of attstorage 'x' or 'e' that are * still inline. */ while (MAXALIGN(ComputeDataSize(tupleDesc, toast_values, toast_nulls)) > maxDataLen && rel->rd_rel->reltoastrelid != InvalidOid) { int biggest_attno = -1; int32 biggest_size = MAXALIGN(sizeof(varattrib)); Datum old_value; /*------ * Search for the biggest yet inlined attribute with * attstorage equals 'x' or 'e' *------ */ for (i = 0; i < numAttrs; i++) { if (toast_action[i] == 'p') continue; if (VARATT_IS_EXTERNAL(toast_values[i])) continue; if (att[i]->attstorage != 'x' && att[i]->attstorage != 'e') continue; if (toast_sizes[i] > biggest_size) { biggest_attno = i; biggest_size = toast_sizes[i]; } } if (biggest_attno < 0) break; /* * Store this external */ i = biggest_attno; old_value = toast_values[i]; toast_action[i] = 'p'; toast_values[i] = toast_save_datum(rel, toast_values[i]); if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_free[i] = true; toast_sizes[i] = VARATT_SIZE(toast_values[i]); need_change = true; need_free = true; } /* * Round 3 - this time we take attributes with storage 'm' into * compression */ while (MAXALIGN(ComputeDataSize(tupleDesc, toast_values, toast_nulls)) > maxDataLen) { int biggest_attno = -1; int32 biggest_size = MAXALIGN(sizeof(varattrib)); Datum old_value; Datum new_value; /* * Search for the biggest yet uncompressed internal attribute */ for (i = 0; i < numAttrs; i++) { if (toast_action[i] != ' ') continue; if (VARATT_IS_EXTENDED(toast_values[i])) continue; if (att[i]->attstorage != 'm') continue; if (toast_sizes[i] > biggest_size) { biggest_attno = i; biggest_size = toast_sizes[i]; } } if (biggest_attno < 0) break; /* * Attempt to compress it inline */ i = biggest_attno; old_value = toast_values[i]; new_value = toast_compress_datum(old_value); if (DatumGetPointer(new_value) != NULL) { /* successful compression */ if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_values[i] = new_value; toast_free[i] = true; toast_sizes[i] = VARATT_SIZE(toast_values[i]); need_change = true; need_free = true; } else { /* * incompressible data, ignore on subsequent compression * passes */ toast_action[i] = 'x'; } } /* * Finally we store attributes of type 'm' external */ while (MAXALIGN(ComputeDataSize(tupleDesc, toast_values, toast_nulls)) > maxDataLen && rel->rd_rel->reltoastrelid != InvalidOid) { int biggest_attno = -1; int32 biggest_size = MAXALIGN(sizeof(varattrib)); Datum old_value; /*-------- * Search for the biggest yet inlined attribute with * attstorage = 'm' *-------- */ for (i = 0; i < numAttrs; i++) { if (toast_action[i] == 'p') continue; if (VARATT_IS_EXTERNAL(toast_values[i])) continue; if (att[i]->attstorage != 'm') continue; if (toast_sizes[i] > biggest_size) { biggest_attno = i; biggest_size = toast_sizes[i]; } } if (biggest_attno < 0) break; /* * Store this external */ i = biggest_attno; old_value = toast_values[i]; toast_action[i] = 'p'; toast_values[i] = toast_save_datum(rel, toast_values[i]); if (toast_free[i]) pfree(DatumGetPointer(old_value)); toast_free[i] = true; toast_sizes[i] = VARATT_SIZE(toast_values[i]); need_change = true; need_free = true; } /* * In the case we toasted any values, we need to build a new heap * tuple with the changed values. */ if (need_change) { HeapTupleHeader olddata = newtup->t_data; char *new_data; int32 new_len; /* * Calculate the new size of the tuple. Header size should not * change, but data size might. */ new_len = offsetof(HeapTupleHeaderData, t_bits); if (has_nulls) new_len += BITMAPLEN(numAttrs); if (olddata->t_infomask & HEAP_HASOID) new_len += sizeof(Oid); new_len = MAXALIGN(new_len); Assert(new_len == olddata->t_hoff); new_len += ComputeDataSize(tupleDesc, toast_values, toast_nulls); /* * Allocate new tuple in same context as old one. */ new_data = (char *) MemoryContextAlloc(newtup->t_datamcxt, new_len); newtup->t_data = (HeapTupleHeader) new_data; newtup->t_len = new_len; /* * Put the tuple header and the changed values into place */ memcpy(new_data, olddata, olddata->t_hoff); DataFill((char *) new_data + olddata->t_hoff, tupleDesc, toast_values, toast_nulls, &(newtup->t_data->t_infomask), has_nulls ? newtup->t_data->t_bits : NULL); /* * In the case we modified a previously modified tuple again, free * the memory from the previous run */ if ((char *) olddata != ((char *) newtup + HEAPTUPLESIZE)) pfree(olddata); } /* * Free allocated temp values */ if (need_free) for (i = 0; i < numAttrs; i++) if (toast_free[i]) pfree(DatumGetPointer(toast_values[i])); /* * Delete external values from the old tuple */ if (need_delold) for (i = 0; i < numAttrs; i++) if (toast_delold[i]) toast_delete_datum(rel, heap_getattr(oldtup, i + 1, tupleDesc, &old_isnull)); }
/* ---------- * toast_fetch_datum_slice - * * Reconstruct a segment of a varattrib from the chunks saved * in the toast relation * ---------- */ static varattrib * toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length) { Relation toastrel; Relation toastidx; ScanKeyData toastkey[3]; int nscankeys; IndexScanDesc toastscan; HeapTuple ttup; TupleDesc toasttupDesc; varattrib *result; int32 attrsize; int32 residx; int32 nextidx; int numchunks; int startchunk; int endchunk; int32 startoffset; int32 endoffset; int totalchunks; Pointer chunk; bool isnull; int32 chunksize; int32 chcpystrt; int32 chcpyend; attrsize = attr->va_content.va_external.va_extsize; totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1; if (sliceoffset >= attrsize) { sliceoffset = 0; length = 0; } if (((sliceoffset + length) > attrsize) || length < 0) length = attrsize - sliceoffset; result = (varattrib *) palloc(length + VARHDRSZ); VARATT_SIZEP(result) = length + VARHDRSZ; if (VARATT_IS_COMPRESSED(attr)) VARATT_SIZEP(result) |= VARATT_FLAG_COMPRESSED; if (length == 0) return (result); /* Can save a lot of work at this point! */ startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE; endchunk = (sliceoffset + length - 1) / TOAST_MAX_CHUNK_SIZE; numchunks = (endchunk - startchunk) + 1; startoffset = sliceoffset % TOAST_MAX_CHUNK_SIZE; endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE; /* * Open the toast relation and it's index */ toastrel = heap_open(attr->va_content.va_external.va_toastrelid, AccessShareLock); toasttupDesc = toastrel->rd_att; toastidx = index_open(toastrel->rd_rel->reltoastidxid); /* * Setup a scan key to fetch from the index. This is either two keys * or three depending on the number of chunks. */ ScanKeyEntryInitialize(&toastkey[0], (bits16) 0, (AttrNumber) 1, (RegProcedure) F_OIDEQ, ObjectIdGetDatum(attr->va_content.va_external.va_valueid)); /* * Now dependent on number of chunks: */ if (numchunks == 1) { ScanKeyEntryInitialize(&toastkey[1], (bits16) 0, (AttrNumber) 2, (RegProcedure) F_INT4EQ, Int32GetDatum(startchunk)); nscankeys = 2; } else { ScanKeyEntryInitialize(&toastkey[1], (bits16) 0, (AttrNumber) 2, (RegProcedure) F_INT4GE, Int32GetDatum(startchunk)); ScanKeyEntryInitialize(&toastkey[2], (bits16) 0, (AttrNumber) 2, (RegProcedure) F_INT4LE, Int32GetDatum(endchunk)); nscankeys = 3; } /* * Read the chunks by index * * The index is on (valueid, chunkidx) so they will come in order */ nextidx = startchunk; toastscan = index_beginscan(toastrel, toastidx, SnapshotToast, nscankeys, toastkey); while ((ttup = index_getnext(toastscan, ForwardScanDirection)) != NULL) { /* * Have a chunk, extract the sequence number and the data */ residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull)); Assert(!isnull); chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull)); Assert(!isnull); chunksize = VARATT_SIZE(chunk) - VARHDRSZ; /* * Some checks on the data we've found */ if ((residx != nextidx) || (residx > endchunk) || (residx < startchunk)) elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u", residx, nextidx, attr->va_content.va_external.va_valueid); if (residx < totalchunks - 1) { if (chunksize != TOAST_MAX_CHUNK_SIZE) elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u", chunksize, residx, attr->va_content.va_external.va_valueid); } else { if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != attrsize) elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u", chunksize, residx, attr->va_content.va_external.va_valueid); } /* * Copy the data into proper place in our result */ chcpystrt = 0; chcpyend = chunksize - 1; if (residx == startchunk) chcpystrt = startoffset; if (residx == endchunk) chcpyend = endoffset; memcpy(((char *) VARATT_DATA(result)) + (residx * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt, VARATT_DATA(chunk) + chcpystrt, (chcpyend - chcpystrt) + 1); nextidx++; } /* * Final checks that we successfully fetched the datum */ if (nextidx != (endchunk + 1)) elog(ERROR, "missing chunk number %d for toast value %u", nextidx, attr->va_content.va_external.va_valueid); /* * End scan and close relations */ index_endscan(toastscan); index_close(toastidx); heap_close(toastrel, AccessShareLock); return result; }
/* ---------- * toast_fetch_datum - * * Reconstruct an in memory varattrib from the chunks saved * in the toast relation * ---------- */ static varattrib * toast_fetch_datum(varattrib *attr) { Relation toastrel; Relation toastidx; ScanKeyData toastkey; IndexScanDesc toastscan; HeapTuple ttup; TupleDesc toasttupDesc; varattrib *result; int32 ressize; int32 residx, nextidx; int32 numchunks; Pointer chunk; bool isnull; int32 chunksize; ressize = attr->va_content.va_external.va_extsize; numchunks = ((ressize - 1) / TOAST_MAX_CHUNK_SIZE) + 1; result = (varattrib *) palloc(ressize + VARHDRSZ); VARATT_SIZEP(result) = ressize + VARHDRSZ; if (VARATT_IS_COMPRESSED(attr)) VARATT_SIZEP(result) |= VARATT_FLAG_COMPRESSED; /* * Open the toast relation and its index */ toastrel = heap_open(attr->va_content.va_external.va_toastrelid, AccessShareLock); toasttupDesc = toastrel->rd_att; toastidx = index_open(toastrel->rd_rel->reltoastidxid); /* * Setup a scan key to fetch from the index by va_valueid */ ScanKeyEntryInitialize(&toastkey, (bits16) 0, (AttrNumber) 1, (RegProcedure) F_OIDEQ, ObjectIdGetDatum(attr->va_content.va_external.va_valueid)); /* * Read the chunks by index * * Note that because the index is actually on (valueid, chunkidx) we will * see the chunks in chunkidx order, even though we didn't explicitly * ask for it. */ nextidx = 0; toastscan = index_beginscan(toastrel, toastidx, SnapshotToast, 1, &toastkey); while ((ttup = index_getnext(toastscan, ForwardScanDirection)) != NULL) { /* * Have a chunk, extract the sequence number and the data */ residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull)); Assert(!isnull); chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull)); Assert(!isnull); chunksize = VARATT_SIZE(chunk) - VARHDRSZ; /* * Some checks on the data we've found */ if (residx != nextidx) elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u", residx, nextidx, attr->va_content.va_external.va_valueid); if (residx < numchunks - 1) { if (chunksize != TOAST_MAX_CHUNK_SIZE) elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u", chunksize, residx, attr->va_content.va_external.va_valueid); } else if (residx < numchunks) { if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != ressize) elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u", chunksize, residx, attr->va_content.va_external.va_valueid); } else elog(ERROR, "unexpected chunk number %d for toast value %u", residx, attr->va_content.va_external.va_valueid); /* * Copy the data into proper place in our result */ memcpy(((char *) VARATT_DATA(result)) + residx * TOAST_MAX_CHUNK_SIZE, VARATT_DATA(chunk), chunksize); nextidx++; } /* * Final checks that we successfully fetched the datum */ if (nextidx != numchunks) elog(ERROR, "missing chunk number %d for toast value %u", nextidx, attr->va_content.va_external.va_valueid); /* * End scan and close relations */ index_endscan(toastscan); index_close(toastidx); heap_close(toastrel, AccessShareLock); return result; }
static void getAddressesForDBid(CdbComponentDatabaseInfo *c, int elevel) { Relation gp_db_interface_rel; Relation gp_interface_rel; HeapTuple tuple; ScanKeyData key; SysScanDesc dbscan, ifacescan; int j, i=0; struct priority_iface *ifaces=NULL; int iface_count, iface_max=0; Datum attr; bool isNull; int dbid; int iface_id; int priority; char *name; Assert(c != NULL); gp_db_interface_rel = heap_open(GpDbInterfacesRelationId, AccessShareLock); /* CaQL UNDONE: no test coverage */ ScanKeyInit(&key, Anum_gp_db_interfaces_dbid, BTEqualStrategyNumber, F_INT2EQ, ObjectIdGetDatum(0)); dbscan = systable_beginscan(gp_db_interface_rel, GpDbInterfacesDbidIndexId, true, SnapshotNow, 1, &key); while (HeapTupleIsValid(tuple = systable_getnext(dbscan))) { i++; if (i > iface_max) { /* allocate 8-more slots */ if (ifaces == NULL) ifaces = palloc((iface_max + 8) * sizeof(struct priority_iface)); else ifaces = repalloc(ifaces, (iface_max + 8) * sizeof(struct priority_iface)); memset(ifaces + iface_max, 0, 8 * sizeof(struct priority_iface)); iface_max += 8; } /* dbid is for sanity-check on scan condition only */ attr = heap_getattr(tuple, Anum_gp_db_interfaces_dbid, gp_db_interface_rel->rd_att, &isNull); Assert(!isNull); dbid = DatumGetInt16(attr); //Assert(dbid == c->dbid); attr = heap_getattr(tuple, Anum_gp_db_interfaces_interfaceid, gp_db_interface_rel->rd_att, &isNull); Assert(!isNull); iface_id = DatumGetInt16(attr); attr = heap_getattr(tuple, Anum_gp_db_interfaces_priority, gp_db_interface_rel->rd_att, &isNull); Assert(!isNull); priority = DatumGetInt16(attr); ifaces[i-1].priority = priority; ifaces[i-1].interface_id = iface_id; } iface_count = i; /* Finish up scan and close appendonly catalog. */ systable_endscan(dbscan); heap_close(gp_db_interface_rel, AccessShareLock); /* we now have the unsorted list, or an empty list. */ do { /* fallback to using hostname if our list is empty */ if (iface_count == 0) break; qsort(ifaces, iface_count, sizeof(struct priority_iface), iface_priority_compare); /* we now have interfaces, sorted by priority. */ gp_interface_rel = heap_open(GpInterfacesRelationId, AccessShareLock); j=0; for (i=0; i < iface_count; i++) { int status=0; /* CaQL UNDONE: no test coverage */ /* Start a new scan. */ ScanKeyInit(&key, Anum_gp_interfaces_interfaceid, BTEqualStrategyNumber, F_INT2EQ, ObjectIdGetDatum(ifaces[i].interface_id)); ifacescan = systable_beginscan(gp_interface_rel, GpInterfacesInterfaceidIndexId, true, SnapshotNow, 1, &key); tuple = systable_getnext(ifacescan); Assert(HeapTupleIsValid(tuple)); /* iface_id is for sanity-check on scan condition only */ attr = heap_getattr(tuple, Anum_gp_interfaces_interfaceid, gp_interface_rel->rd_att, &isNull); Assert(!isNull); iface_id = DatumGetInt16(attr); Assert(iface_id == ifaces[i].interface_id); attr = heap_getattr(tuple, Anum_gp_interfaces_status, gp_interface_rel->rd_att, &isNull); Assert(!isNull); status = DatumGetInt16(attr); /* if the status is "alive" use the interface. */ if (status == 1) { attr = heap_getattr(tuple, Anum_gp_interfaces_address, gp_interface_rel->rd_att, &isNull); Assert(!isNull); name = getDnsCachedAddress(DatumGetCString(attr), c->port, elevel); if (name) c->hostaddrs[j++] = pstrdup(name); } systable_endscan(ifacescan); } heap_close(gp_interface_rel, AccessShareLock); /* fallback to using hostname if our list is empty */ if (j == 0) break; /* successfully retrieved at least one entry. */ return; } while (0); /* fallback to using hostname */ memset(c->hostaddrs, 0, COMPONENT_DBS_MAX_ADDRS * sizeof(char *)); /* * add an entry, using the first the "address" and then the * "hostname" as fallback. */ name = getDnsCachedAddress(c->address, c->port, elevel); if (name) { c->hostaddrs[0] = pstrdup(name); return; } /* now the hostname. */ name = getDnsCachedAddress(c->hostname, c->port, elevel); if (name) { c->hostaddrs[0] = pstrdup(name); } else { c->hostaddrs[0] = NULL; } return; }
/* * get dbinfo by role * There should be only one master in gp_segment_configuration table, one standby at most. */ CdbComponentDatabaseInfo * role_get_dbinfo(char role) { HeapTuple tuple; Relation rel; cqContext cqc; bool bOnly; CdbComponentDatabaseInfo *i = NULL; Assert(role == SEGMENT_ROLE_PRIMARY || role == SEGMENT_ROLE_MASTER_CONFIG || role == SEGMENT_ROLE_STANDBY_CONFIG); /* * Can only run on a master node, this restriction is due to the reliance * on the gp_segment_configuration table. This may be able to be relaxed * by switching to a different method of checking. */ if (!AmActiveMaster() && !AmStandbyMaster()) elog(ERROR, "role_get_dbinfo() executed on execution segment"); rel = heap_open(GpSegmentConfigRelationId, AccessShareLock); tuple = caql_getfirst_only( caql_addrel(cqclr(&cqc), rel), &bOnly, cql("SELECT * FROM gp_segment_configuration " " WHERE role = :1 ", CharGetDatum(role))); if (HeapTupleIsValid(tuple)) { Datum attr; bool isNull; i = palloc(sizeof(CdbComponentDatabaseInfo)); /* * role */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_role, RelationGetDescr(rel), &isNull); Assert(!isNull); i->role = DatumGetChar(attr); /* * status */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_status, RelationGetDescr(rel), &isNull); Assert(!isNull); i->status = DatumGetChar(attr); /* * hostname */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_hostname, RelationGetDescr(rel), &isNull); Assert(!isNull); i->hostname = TextDatumGetCString(attr); /* * address */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_address, RelationGetDescr(rel), &isNull); Assert(!isNull); i->address = TextDatumGetCString(attr); /* * port */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_port, RelationGetDescr(rel), &isNull); Assert(!isNull); i->port = DatumGetInt32(attr); } else { elog(ERROR, "could not find configuration entry for role %c", role); } heap_close(rel, NoLock); return i; }
/* * Determine whether a relation can be proven functionally dependent on * a set of grouping columns. If so, return TRUE and add the pg_constraint * OIDs of the constraints needed for the proof to the *constraintDeps list. * * grouping_columns is a list of grouping expressions, in which columns of * the rel of interest are Vars with the indicated varno/varlevelsup. * * Currently we only check to see if the rel has a primary key that is a * subset of the grouping_columns. We could also use plain unique constraints * if all their columns are known not null, but there's a problem: we need * to be able to represent the not-null-ness as part of the constraints added * to *constraintDeps. FIXME whenever not-null constraints get represented * in pg_constraint. */ bool check_functional_grouping(Oid relid, Index varno, Index varlevelsup, List *grouping_columns, List **constraintDeps) { bool result = false; Relation pg_constraint; HeapTuple tuple; SysScanDesc scan; ScanKeyData skey[1]; /* Scan pg_constraint for constraints of the target rel */ pg_constraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true, NULL, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(scan))) { Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); Datum adatum; bool isNull; ArrayType *arr; int16 *attnums; int numkeys; int i; bool found_col; /* Only PK constraints are of interest for now, see comment above */ if (con->contype != CONSTRAINT_PRIMARY) continue; /* Constraint must be non-deferrable */ if (con->condeferrable) continue; /* Extract the conkey array, ie, attnums of PK's columns */ adatum = heap_getattr(tuple, Anum_pg_constraint_conkey, RelationGetDescr(pg_constraint), &isNull); if (isNull) elog(ERROR, "null conkey for constraint %u", HeapTupleGetOid(tuple)); arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ numkeys = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || numkeys < 0 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != INT2OID) elog(ERROR, "conkey is not a 1-D smallint array"); attnums = (int16 *) ARR_DATA_PTR(arr); found_col = false; for (i = 0; i < numkeys; i++) { AttrNumber attnum = attnums[i]; ListCell *gl; found_col = false; foreach(gl, grouping_columns) { Var *gvar = (Var *) lfirst(gl); if (IsA(gvar, Var) && gvar->varno == varno && gvar->varlevelsup == varlevelsup && gvar->varattno == attnum) { found_col = true; break; } } if (!found_col) break; } if (found_col) { /* The PK is a subset of grouping_columns, so we win */ *constraintDeps = lappend_oid(*constraintDeps, HeapTupleGetOid(tuple)); result = true; break; } }
/* * getCdbComponentDatabases * * * Storage for the SegmentInstances block and all subsidiary * structures are allocated from the caller's context. */ CdbComponentDatabases * getCdbComponentInfo(bool DNSLookupAsError) { #if 0 CdbComponentDatabaseInfo *pOld = NULL; CdbComponentDatabases *component_databases = NULL; Relation gp_seg_config_rel; HeapTuple gp_seg_config_tuple = NULL; HeapScanDesc gp_seg_config_scan; /* * Initial size for info arrays. */ int segment_array_size = 500; int entry_array_size = 4; /* we currently support a max of 2 */ /* * isNull and attr are used when getting the data for a specific column from a HeapTuple */ bool isNull; Datum attr; /* * Local variables for fields from the rows of the tables that we are reading. */ char role; char status = 0; int i; /* * Allocate component_databases return structure and * component_databases->segment_db_info array with an initial size * of 128, and component_databases->entry_db_info with an initial * size of 4. If necessary during row fetching, we grow these by * doubling each time we run out. */ component_databases = palloc0(sizeof(CdbComponentDatabases)); component_databases->segment_db_info = (CdbComponentDatabaseInfo *) palloc0(sizeof(CdbComponentDatabaseInfo) * segment_array_size); component_databases->entry_db_info = (CdbComponentDatabaseInfo *) palloc0(sizeof(CdbComponentDatabaseInfo) * entry_array_size); gp_seg_config_rel = heap_open(GpSegmentConfigRelationId, AccessShareLock); gp_seg_config_scan = heap_beginscan(gp_seg_config_rel, SnapshotNow, 0, NULL); while (HeapTupleIsValid(gp_seg_config_tuple = heap_getnext(gp_seg_config_scan, ForwardScanDirection))) { /* * Grab the fields that we need from gp_configuration. We do * this first, because until we read them, we don't know * whether this is an entry database row or a segment database * row. */ CdbComponentDatabaseInfo *pRow; /* * dbid */ //attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_dbid, RelationGetDescr(gp_seg_config_rel), &isNull); //Assert(!isNull); //dbid = DatumGetInt16(attr); /* * content */ //attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_content, RelationGetDescr(gp_seg_config_rel), &isNull); //Assert(!isNull); //content = DatumGetInt16(attr); /* * role */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_role, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); role = DatumGetChar(attr); /* * preferred-role */ //attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_preferred_role, RelationGetDescr(gp_seg_config_rel), &isNull); //Assert(!isNull); //preferred_role = DatumGetChar(attr); /* * mode */ //attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_mode, RelationGetDescr(gp_seg_config_rel), &isNull); //Assert(!isNull); //mode = DatumGetChar(attr); /* * status */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_status, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); status = DatumGetChar(attr); /* * Determine which array to place this rows data in: entry or * segment, based on the content field. */ if (role == SEGMENT_ROLE_PRIMARY) { /* if we have a dbid bigger than our array we'll have to grow the array. (MPP-2104) */ if (dbid >= segment_array_size || component_databases->total_segment_dbs >= segment_array_size) { /* * Expand CdbComponentDatabaseInfo array if we've used up currently allocated space */ segment_array_size = Max((segment_array_size * 2), dbid * 2); pOld = component_databases->segment_db_info; component_databases->segment_db_info = (CdbComponentDatabaseInfo *) repalloc(pOld, sizeof(CdbComponentDatabaseInfo) * segment_array_size); } pRow = &component_databases->segment_db_info[component_databases->total_segment_dbs]; component_databases->total_segment_dbs++; } else { if (component_databases->total_entry_dbs >= entry_array_size) { /* * Expand CdbComponentDatabaseInfo array if we've used up currently allocated space */ entry_array_size *= 2; pOld = component_databases->entry_db_info; component_databases->entry_db_info = (CdbComponentDatabaseInfo *) repalloc(pOld, sizeof(CdbComponentDatabaseInfo) * entry_array_size); } pRow = &component_databases->entry_db_info[component_databases->total_entry_dbs]; component_databases->total_entry_dbs++; } pRow->role = role; pRow->status = status; /* * hostname */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_hostname, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); pRow->hostname = TextDatumGetCString(attr); /* * address */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_address, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); pRow->address = TextDatumGetCString(attr); /* * port */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_port, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); pRow->port = DatumGetInt32(attr); getAddressesForDBid(pRow, DNSLookupAsError ? ERROR : LOG); pRow->hostip = pRow->hostaddrs[0]; } /* * We're done with the catalog entries, cleanup them up, closing * all the relations we opened. */ heap_endscan(gp_seg_config_scan); heap_close(gp_seg_config_rel, AccessShareLock); /* * Validate that there exists at least one entry and one segment * database in the configuration */ if (component_databases->total_segment_dbs == 0) { ereport(ERROR, (errcode(ERRCODE_CARDINALITY_VIOLATION), errmsg("Greenplum Database number of segment databases cannot be 0"))); } if (component_databases->total_entry_dbs == 0) { ereport(ERROR, (errcode(ERRCODE_CARDINALITY_VIOLATION), errmsg("Greenplum Database number of entry databases cannot be 0"))); } /* * Now sort the data by segindex, isprimary desc */ qsort(component_databases->segment_db_info, component_databases->total_segment_dbs, sizeof(CdbComponentDatabaseInfo), CdbComponentDatabaseInfoCompare); qsort(component_databases->entry_db_info, component_databases->total_entry_dbs, sizeof(CdbComponentDatabaseInfo), CdbComponentDatabaseInfoCompare); /* * Now count the number of distinct segindexes. * Since it's sorted, this is easy. */ for (i = 0; i < component_databases->total_segment_dbs; i++) { if (i == 0 || (component_databases->segment_db_info[i].segindex != component_databases->segment_db_info[i - 1].segindex)) { component_databases->total_segments++; } } return component_databases; #endif return NULL; }
CdbComponentDatabaseInfo * dbid_get_dbinfo(int16 dbid) { HeapTuple tuple; Relation rel; cqContext cqc; bool bOnly; CdbComponentDatabaseInfo *i = NULL; /* * Can only run on a master node, this restriction is due to the reliance * on the gp_segment_configuration table. This may be able to be relaxed * by switching to a different method of checking. */ if (GpIdentity.segindex != MASTER_CONTENT_ID) elog(ERROR, "dbid_get_dbinfo() executed on execution segment"); rel = heap_open(GpSegmentConfigRelationId, AccessShareLock); tuple = caql_getfirst_only( caql_addrel(cqclr(&cqc), rel), &bOnly, cql("SELECT * FROM gp_segment_configuration " " WHERE dbid = :1 ", Int16GetDatum(dbid))); if (HeapTupleIsValid(tuple)) { Datum attr; bool isNull; i = palloc(sizeof(CdbComponentDatabaseInfo)); /* * dbid */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_dbid, RelationGetDescr(rel), &isNull); Assert(!isNull); i->dbid = DatumGetInt16(attr); /* * content */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_content, RelationGetDescr(rel), &isNull); Assert(!isNull); i->segindex = DatumGetInt16(attr); /* * role */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_role, RelationGetDescr(rel), &isNull); Assert(!isNull); i->role = DatumGetChar(attr); /* * preferred-role */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_preferred_role, RelationGetDescr(rel), &isNull); Assert(!isNull); i->preferred_role = DatumGetChar(attr); /* * mode */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_mode, RelationGetDescr(rel), &isNull); Assert(!isNull); i->mode = DatumGetChar(attr); /* * status */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_status, RelationGetDescr(rel), &isNull); Assert(!isNull); i->status = DatumGetChar(attr); /* * hostname */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_hostname, RelationGetDescr(rel), &isNull); Assert(!isNull); i->hostname = TextDatumGetCString(attr); /* * address */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_address, RelationGetDescr(rel), &isNull); Assert(!isNull); i->address = TextDatumGetCString(attr); /* * port */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_port, RelationGetDescr(rel), &isNull); Assert(!isNull); i->port = DatumGetInt32(attr); /* * Filerep_port */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_replication_port, RelationGetDescr(rel), &isNull); if (!isNull) i->filerep_port = DatumGetInt32(attr); else i->filerep_port = -1; Assert(bOnly); /* should be only 1 */ } else { elog(ERROR, "could not find configuration entry for dbid %i", dbid); } heap_close(rel, NoLock); return i; }
/* * Turn a composite / record into JSON. */ static void composite_to_json(Datum composite, StringInfo result, bool use_line_feeds) { HeapTupleHeader td; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tmptup, *tuple; int i; bool needsep = false; char *sep; sep = use_line_feeds ? ",\n " : ","; td = DatumGetHeapTupleHeader(composite); /* Extract rowtype info and find a tupdesc */ tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; tuple = &tmptup; appendStringInfoChar(result,'{'); for (i = 0; i < tupdesc->natts; i++) { Datum val, origval; bool isnull; char *attname; TYPCATEGORY tcategory; Oid typoutput; bool typisvarlena; if (tupdesc->attrs[i]->attisdropped) continue; if (needsep) appendStringInfoString(result,sep); needsep = true; attname = NameStr(tupdesc->attrs[i]->attname); escape_json(result,attname); appendStringInfoChar(result,':'); origval = heap_getattr(tuple, i + 1, tupdesc, &isnull); if (tupdesc->attrs[i]->atttypid == RECORDARRAYOID) tcategory = TYPCATEGORY_ARRAY; else if (tupdesc->attrs[i]->atttypid == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (tupdesc->attrs[i]->atttypid == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(tupdesc->attrs[i]->atttypid); getTypeOutputInfo(tupdesc->attrs[i]->atttypid, &typoutput, &typisvarlena); /* * If we have a toasted datum, forcibly detoast it here to avoid memory * leakage inside the type's output routine. */ if (typisvarlena && ! isnull) val = PointerGetDatum(PG_DETOAST_DATUM(origval)); else val = origval; datum_to_json(val, result, tcategory, typoutput); /* Clean up detoasted copy, if any */ if (val != origval) pfree(DatumGetPointer(val)); } appendStringInfoChar(result,'}'); ReleaseTupleDesc(tupdesc); }
/* * Post vacuum, iterate over all entries in index, check if the h_tid * of each entry exists and is not dead. For specific system tables, * also ensure that the key in index entry matches the corresponding * attribute in the heap tuple. */ void _bt_validate_vacuum(Relation irel, Relation hrel, TransactionId oldest_xmin) { MIRROREDLOCK_BUFMGR_DECLARE; BlockNumber blkno; BlockNumber num_pages; Buffer ibuf = InvalidBuffer; Buffer hbuf = InvalidBuffer; Page ipage; BTPageOpaque opaque; IndexTuple itup; HeapTupleData htup; OffsetNumber maxoff, minoff, offnum; Oid ioid, hoid; bool isnull; blkno = BTREE_METAPAGE + 1; num_pages = RelationGetNumberOfBlocks(irel); elog(LOG, "btvalidatevacuum: index %s, heap %s", RelationGetRelationName(irel), RelationGetRelationName(hrel)); MIRROREDLOCK_BUFMGR_LOCK; for (; blkno < num_pages; blkno++) { ibuf = ReadBuffer(irel, blkno); ipage = BufferGetPage(ibuf); opaque = (BTPageOpaque) PageGetSpecialPointer(ipage); if (!PageIsNew(ipage)) _bt_checkpage(irel, ibuf); if (P_ISLEAF(opaque)) { minoff = P_FIRSTDATAKEY(opaque); maxoff = PageGetMaxOffsetNumber(ipage); for (offnum = minoff; offnum <= maxoff; offnum = OffsetNumberNext(offnum)) { itup = (IndexTuple) PageGetItem(ipage, PageGetItemId(ipage, offnum)); ItemPointerCopy(&itup->t_tid, &htup.t_self); /* * TODO: construct a tid bitmap based on index tids * and fetch heap tids in order afterwards. That will * also allow validating if a heap tid appears twice * in a unique index. */ if (!heap_release_fetch(hrel, SnapshotAny, &htup, &hbuf, true, NULL)) { elog(ERROR, "btvalidatevacuum: tid (%d,%d) from index %s " "not found in heap %s", ItemPointerGetBlockNumber(&itup->t_tid), ItemPointerGetOffsetNumber(&itup->t_tid), RelationGetRelationName(irel), RelationGetRelationName(hrel)); } switch (HeapTupleSatisfiesVacuum(hrel, htup.t_data, oldest_xmin, hbuf)) { case HEAPTUPLE_RECENTLY_DEAD: case HEAPTUPLE_LIVE: case HEAPTUPLE_INSERT_IN_PROGRESS: case HEAPTUPLE_DELETE_IN_PROGRESS: /* these tuples are considered alive by vacuum */ break; case HEAPTUPLE_DEAD: elog(ERROR, "btvalidatevacuum: vacuum did not remove " "dead tuple (%d,%d) from heap %s and index %s", ItemPointerGetBlockNumber(&itup->t_tid), ItemPointerGetOffsetNumber(&itup->t_tid), RelationGetRelationName(hrel), RelationGetRelationName(irel)); break; default: elog(ERROR, "btvalidatevacuum: invalid visibility"); break; } switch(RelationGetRelid(irel)) { case DatabaseOidIndexId: case TypeOidIndexId: case ClassOidIndexId: case ConstraintOidIndexId: hoid = HeapTupleGetOid(&htup); ioid = index_getattr(itup, 1, RelationGetDescr(irel), &isnull); if (hoid != ioid) { elog(ERROR, "btvalidatevacuum: index oid(%d) != heap oid(%d)" " tuple (%d,%d) index %s", ioid, hoid, ItemPointerGetBlockNumber(&itup->t_tid), ItemPointerGetOffsetNumber(&itup->t_tid), RelationGetRelationName(irel)); } break; case GpRelationNodeOidIndexId: hoid = heap_getattr(&htup, 1, RelationGetDescr(hrel), &isnull); ioid = index_getattr(itup, 1, RelationGetDescr(irel), &isnull); if (hoid != ioid) { elog(ERROR, "btvalidatevacuum: index oid(%d) != heap oid(%d)" " tuple (%d,%d) index %s", ioid, hoid, ItemPointerGetBlockNumber(&itup->t_tid), ItemPointerGetOffsetNumber(&itup->t_tid), RelationGetRelationName(irel)); } int4 hsegno = heap_getattr(&htup, 2, RelationGetDescr(hrel), &isnull); int4 isegno = index_getattr(itup, 2, RelationGetDescr(irel), &isnull); if (isegno != hsegno) { elog(ERROR, "btvalidatevacuum: index segno(%d) != heap segno(%d)" " tuple (%d,%d) index %s", isegno, hsegno, ItemPointerGetBlockNumber(&itup->t_tid), ItemPointerGetOffsetNumber(&itup->t_tid), RelationGetRelationName(irel)); } break; default: break; } if (RelationGetNamespace(irel) == PG_AOSEGMENT_NAMESPACE) { int4 isegno = index_getattr(itup, 1, RelationGetDescr(irel), &isnull); int4 hsegno = heap_getattr(&htup, 1, RelationGetDescr(hrel), &isnull); if (isegno != hsegno) { elog(ERROR, "btvalidatevacuum: index segno(%d) != heap segno(%d)" " tuple (%d,%d) index %s", isegno, hsegno, ItemPointerGetBlockNumber(&itup->t_tid), ItemPointerGetOffsetNumber(&itup->t_tid), RelationGetRelationName(irel)); } } } } if (BufferIsValid(ibuf)) ReleaseBuffer(ibuf); } if (BufferIsValid(hbuf)) ReleaseBuffer(hbuf); MIRROREDLOCK_BUFMGR_UNLOCK; }
/* * getCdbComponentDatabases * * * Storage for the SegmentInstances block and all subsidiary * strucures are allocated from the caller's context. */ CdbComponentDatabases * getCdbComponentInfo(bool DNSLookupAsError) { CdbComponentDatabaseInfo *pOld = NULL; CdbComponentDatabases *component_databases = NULL; Relation gp_seg_config_rel; HeapTuple gp_seg_config_tuple = NULL; HeapScanDesc gp_seg_config_scan; /* * Initial size for info arrays. */ int segment_array_size = 500; int entry_array_size = 4; /* we currently support a max of 2 */ /* * isNull and attr are used when getting the data for a specific column from a HeapTuple */ bool isNull; Datum attr; /* * Local variables for fields from the rows of the tables that we are reading. */ int dbid; int content; char role; char preferred_role; char mode = 0; char status = 0; int i; int x = 0; /* * Allocate component_databases return structure and * component_databases->segment_db_info array with an initial size * of 128, and component_databases->entry_db_info with an initial * size of 4. If necessary during row fetching, we grow these by * doubling each time we run out. */ component_databases = palloc0(sizeof(CdbComponentDatabases)); component_databases->segment_db_info = (CdbComponentDatabaseInfo *) palloc0(sizeof(CdbComponentDatabaseInfo) * segment_array_size); component_databases->entry_db_info = (CdbComponentDatabaseInfo *) palloc0(sizeof(CdbComponentDatabaseInfo) * entry_array_size); gp_seg_config_rel = heap_open(GpSegmentConfigRelationId, AccessShareLock); gp_seg_config_scan = heap_beginscan(gp_seg_config_rel, SnapshotNow, 0, NULL); while (HeapTupleIsValid(gp_seg_config_tuple = heap_getnext(gp_seg_config_scan, ForwardScanDirection))) { /* * Grab the fields that we need from gp_configuration. We do * this first, because until we read them, we don't know * whether this is an entry database row or a segment database * row. */ CdbComponentDatabaseInfo *pRow; /* * dbid */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_dbid, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); dbid = DatumGetInt16(attr); /* * content */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_content, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); content = DatumGetInt16(attr); /* * role */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_role, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); role = DatumGetChar(attr); /* * preferred-role */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_preferred_role, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); preferred_role = DatumGetChar(attr); /* * mode */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_mode, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); mode = DatumGetChar(attr); /* * status */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_status, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); status = DatumGetChar(attr); /* * Determine which array to place this rows data in: entry or * segment, based on the content field. */ if (content >= 0) { /* if we have a dbid bigger than our array we'll have to grow the array. (MPP-2104) */ if (dbid >= segment_array_size || component_databases->total_segment_dbs >= segment_array_size) { /* * Expand CdbComponentDatabaseInfo array if we've used up currently allocated space */ segment_array_size = Max((segment_array_size * 2), dbid * 2); pOld = component_databases->segment_db_info; component_databases->segment_db_info = (CdbComponentDatabaseInfo *) repalloc(pOld, sizeof(CdbComponentDatabaseInfo) * segment_array_size); } pRow = &component_databases->segment_db_info[component_databases->total_segment_dbs]; component_databases->total_segment_dbs++; } else { if (component_databases->total_entry_dbs >= entry_array_size) { /* * Expand CdbComponentDatabaseInfo array if we've used up currently allocated space */ entry_array_size *= 2; pOld = component_databases->entry_db_info; component_databases->entry_db_info = (CdbComponentDatabaseInfo *) repalloc(pOld, sizeof(CdbComponentDatabaseInfo) * entry_array_size); } pRow = &component_databases->entry_db_info[component_databases->total_entry_dbs]; component_databases->total_entry_dbs++; } pRow->dbid = dbid; pRow->segindex = content; pRow->role = role; pRow->preferred_role = preferred_role; pRow->mode = mode; pRow->status = status; /* * hostname */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_hostname, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); pRow->hostname = TextDatumGetCString(attr); /* * address */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_address, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); pRow->address = TextDatumGetCString(attr); /* * port */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_port, RelationGetDescr(gp_seg_config_rel), &isNull); Assert(!isNull); pRow->port = DatumGetInt32(attr); /* * Filerep_port */ attr = heap_getattr(gp_seg_config_tuple, Anum_gp_segment_configuration_replication_port, RelationGetDescr(gp_seg_config_rel), &isNull); if (!isNull) pRow->filerep_port = DatumGetInt32(attr); else pRow->filerep_port = -1; getAddressesForDBid(pRow, DNSLookupAsError ? ERROR : LOG); pRow->hostip = pRow->hostaddrs[0]; } /* * We're done with the catalog entries, cleanup them up, closing * all the relations we opened. */ heap_endscan(gp_seg_config_scan); heap_close(gp_seg_config_rel, AccessShareLock); /* * Validate that there exists at least one entry and one segment * database in the configuration */ if (component_databases->total_segment_dbs == 0) { ereport(ERROR, (errcode(ERRCODE_CARDINALITY_VIOLATION), errmsg("Greenplum Database number of segment databases cannot be 0"))); } if (component_databases->total_entry_dbs == 0) { ereport(ERROR, (errcode(ERRCODE_CARDINALITY_VIOLATION), errmsg("Greenplum Database number of entry databases cannot be 0"))); } /* * Now sort the data by segindex, isprimary desc */ qsort(component_databases->segment_db_info, component_databases->total_segment_dbs, sizeof(CdbComponentDatabaseInfo), CdbComponentDatabaseInfoCompare); qsort(component_databases->entry_db_info, component_databases->total_entry_dbs, sizeof(CdbComponentDatabaseInfo), CdbComponentDatabaseInfoCompare); /* * Now count the number of distinct segindexes. * Since it's sorted, this is easy. */ for (i = 0; i < component_databases->total_segment_dbs; i++) { if (i == 0 || (component_databases->segment_db_info[i].segindex != component_databases->segment_db_info[i - 1].segindex)) { component_databases->total_segments++; } } /* * Validate that gp_numsegments == segment_databases->total_segment_dbs */ if (getgpsegmentCount() != component_databases->total_segments) { ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("Greenplum Database number of segments inconsistency: count is %d from pg_catalog.%s table, but %d from getCdbComponentDatabases()", getgpsegmentCount(), GpIdRelationName, component_databases->total_segments))); } /* * Now validate that our identity is present in the entry databases */ for (i = 0; i < component_databases->total_entry_dbs; i++) { CdbComponentDatabaseInfo *pInfo = &component_databases->entry_db_info[i]; if (pInfo->dbid == GpIdentity.dbid && pInfo->segindex == Gp_segment) { break; } } if (i == component_databases->total_entry_dbs) { ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("Cannot locate entry database represented by this db in gp_segment_configuration: dbid %d content %d", GpIdentity.dbid, Gp_segment))); } /* * Now validate that the segindexes for the segment databases are * between 0 and (GpIdentity.numsegments - 1) inclusive, and that we * hit them all. Since it's sorted, this is relatively easy. */ x = 0; for (i = 0; i < getgpsegmentCount(); i++) { int this_segindex = -1; while (x < component_databases->total_segment_dbs) { this_segindex = component_databases->segment_db_info[x].segindex; if (this_segindex < i) x++; else if (this_segindex == i) break; else if (this_segindex > i) { ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("Content values not valid in %s table. They must be in the range 0 to %d inclusive", GpSegmentConfigRelationName, getgpsegmentCount() - 1))); } } if (this_segindex != i) { ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("Content values not valid in %s table. They must be in the range 0 to %d inclusive", GpSegmentConfigRelationName, getgpsegmentCount() - 1))); } } return component_databases; }
Datum gp_inject_fault(PG_FUNCTION_ARGS) { char *faultName = TextDatumGetCString(PG_GETARG_DATUM(0)); char *type = TextDatumGetCString(PG_GETARG_DATUM(1)); char *ddlStatement = TextDatumGetCString(PG_GETARG_DATUM(2)); char *databaseName = TextDatumGetCString(PG_GETARG_DATUM(3)); char *tableName = TextDatumGetCString(PG_GETARG_DATUM(4)); int numOccurrences = PG_GETARG_INT32(5); int sleepTimeSeconds = PG_GETARG_INT32(6); int dbid = PG_GETARG_INT32(7); StringInfo faultmsg = makeStringInfo(); /* Fast path if injecting fault in our postmaster. */ if (GpIdentity.dbid == dbid) { appendStringInfo(faultmsg, "%s\n%s\n%s\n%s\n%s\n%d\n%d\n", faultName, type, ddlStatement, databaseName, tableName, numOccurrences, sleepTimeSeconds); int offset = 0; char *response = processTransitionRequest_faultInject( faultmsg->data, &offset, faultmsg->len); if (!response) elog(ERROR, "failed to inject fault locally (dbid %d)", dbid); if (strncmp(response, "Success:", strlen("Success:")) != 0) elog(ERROR, "%s", response); elog(NOTICE, "%s", response); PG_RETURN_DATUM(true); } /* Obtain host and port of the requested dbid */ HeapTuple tuple; Relation rel = heap_open(GpSegmentConfigRelationId, AccessShareLock); ScanKeyData scankey; SysScanDesc sscan; ScanKeyInit(&scankey, Anum_gp_segment_configuration_dbid, BTEqualStrategyNumber, F_INT2EQ, Int16GetDatum((int16) dbid)); sscan = systable_beginscan(rel, GpSegmentConfigDbidIndexId, true, GetTransactionSnapshot(), 1, &scankey); tuple = systable_getnext(sscan); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cannot find dbid %d", dbid); bool isnull; Datum datum = heap_getattr(tuple, Anum_gp_segment_configuration_hostname, RelationGetDescr(rel), &isnull); char *hostname; if (!isnull) hostname = DatumGetCString(DirectFunctionCall1(textout, datum)); else elog(ERROR, "hostname is null for dbid %d", dbid); int port = DatumGetInt32(heap_getattr(tuple, Anum_gp_segment_configuration_port, RelationGetDescr(rel), &isnull)); systable_endscan(sscan); heap_close(rel, NoLock); struct addrinfo *addrList = NULL; struct addrinfo hint; int ret; /* Initialize hint structure */ MemSet(&hint, 0, sizeof(hint)); hint.ai_socktype = SOCK_STREAM; hint.ai_family = AF_UNSPEC; char portStr[100]; if (snprintf(portStr, sizeof(portStr), "%d", port) >= sizeof(portStr)) elog(ERROR, "port number too long for dbid %d", dbid); /* Use pg_getaddrinfo_all() to resolve the address */ ret = pg_getaddrinfo_all(hostname, portStr, &hint, &addrList); if (ret || !addrList) { if (addrList) pg_freeaddrinfo_all(hint.ai_family, addrList); elog(ERROR, "could not translate host name \"%s\" to address: %s\n", hostname, gai_strerror(ret)); } PrimaryMirrorTransitionClientInfo client; client.receivedDataCallbackFn = transitionReceivedDataFn; client.errorLogFn = transitionErrorLogFn; client.checkForNeedToExitFn = checkForNeedToExitFn; transitionMsgErrors = makeStringInfo(); appendStringInfo(faultmsg, "%s\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n", "faultInject", faultName, type, ddlStatement, databaseName, tableName, numOccurrences, sleepTimeSeconds); if (sendTransitionMessage(&client, addrList, faultmsg->data, faultmsg->len, 1 /* retries */, 60 /* timeout */) != TRANS_ERRCODE_SUCCESS) { pg_freeaddrinfo_all(hint.ai_family, addrList); ereport(ERROR, (errmsg("failed to inject %s fault in dbid %d", faultName, dbid), errdetail("%s", transitionMsgErrors->data))); } pg_freeaddrinfo_all(hint.ai_family, addrList); PG_RETURN_DATUM(BoolGetDatum(true)); }
/* * get dbinfo by registration_order */ CdbComponentDatabaseInfo * registration_order_get_dbinfo(int32 order) { HeapTuple tuple; Relation rel; bool bOnly; CdbComponentDatabaseInfo *i = NULL; cqContext cqc; if (!AmActiveMaster() && !AmStandbyMaster()) elog(ERROR, "registration_order_get_dbinfo() executed on execution segment"); rel = heap_open(GpSegmentConfigRelationId, AccessShareLock); tuple = caql_getfirst_only( caql_addrel(cqclr(&cqc), rel), &bOnly, cql("SELECT * FROM gp_segment_configuration " " WHERE registration_order = :1 ", Int32GetDatum(order))); if (HeapTupleIsValid(tuple)) { Datum attr; bool isNull; i = palloc(sizeof(CdbComponentDatabaseInfo)); /* * role */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_role, RelationGetDescr(rel), &isNull); Assert(!isNull); i->role = DatumGetChar(attr); /* * status */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_status, RelationGetDescr(rel), &isNull); Assert(!isNull); i->status = DatumGetChar(attr); /* * hostname */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_hostname, RelationGetDescr(rel), &isNull); Assert(!isNull); i->hostname = TextDatumGetCString(attr); /* * address */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_address, RelationGetDescr(rel), &isNull); Assert(!isNull); i->address = TextDatumGetCString(attr); /* * port */ attr = heap_getattr(tuple, Anum_gp_segment_configuration_port, RelationGetDescr(rel), &isNull); Assert(!isNull); i->port = DatumGetInt32(attr); } else { heap_close(rel, NoLock); elog(ERROR, "could not find configuration entry for registration_order %c", order); } heap_close(rel, NoLock); return i; }