/* * RelationInitLockInfo * Initializes the lock information in a relation descriptor. * * relcache.c must call this during creation of any reldesc. */ void RelationInitLockInfo(Relation relation) { Assert(RelationIsValid(relation)); Assert(OidIsValid(RelationGetRelid(relation))); relation->rd_lockInfo.lockRelId.relId = RelationGetRelid(relation); if (relation->rd_rel->relisshared) relation->rd_lockInfo.lockRelId.dbId = InvalidOid; else relation->rd_lockInfo.lockRelId.dbId = MyDatabaseId; }
/* * hashbuild() -- build a new hash index. * * We use a global variable to record the fact that we're creating * a new index. This is used to avoid high-concurrency locking, * since the index won't be visible until this transaction commits * and since building is guaranteed to be single-threaded. */ Datum hashbuild(PG_FUNCTION_ARGS) { Relation heap = (Relation) PG_GETARG_POINTER(0); Relation index = (Relation) PG_GETARG_POINTER(1); IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2); double reltuples; HashBuildState buildstate; /* * We expect to be called exactly once for any index relation. If * that's not the case, big trouble's what we have. */ if (RelationGetNumberOfBlocks(index) != 0) elog(ERROR, "index \"%s\" already contains data", RelationGetRelationName(index)); /* initialize the hash index metadata page */ _hash_metapinit(index); /* build the index */ buildstate.indtuples = 0; /* do the heap scan */ reltuples = IndexBuildHeapScan(heap, index, indexInfo, hashbuildCallback, (void *) &buildstate); /* * Since we just counted the tuples in the heap, we update its stats * in pg_class to guarantee that the planner takes advantage of the * index we just created. But, only update statistics during normal * index definitions, not for indices on system catalogs created * during bootstrap processing. We must close the relations before * updating statistics to guarantee that the relcache entries are * flushed when we increment the command counter in UpdateStats(). But * we do not release any locks on the relations; those will be held * until end of transaction. */ if (IsNormalProcessingMode()) { Oid hrelid = RelationGetRelid(heap); Oid irelid = RelationGetRelid(index); heap_close(heap, NoLock); index_close(index); UpdateStats(hrelid, reltuples); UpdateStats(irelid, buildstate.indtuples); } PG_RETURN_VOID(); }
/* * IsErrorTable * * Returns true if relid is used as an error table, which has dependent object * that is an external table. Though it's not great we didn't have a clear * definition of Error Table, it satisfies the current requirements. */ bool IsErrorTable(Relation rel) { cqContext *pcqCtx, *pcqCtxExt, ctxExt; HeapTuple tup; Relation extrel; bool result = false; /* fast path to quick check */ if (!RelationIsHeap(rel)) return false; /* * Otherwise, go deeper and play with pg_depend... */ pcqCtx = caql_beginscan(NULL, cql("SELECT * FROM pg_depend " " WHERE refclassid = :1 " " AND refobjid = :2 " " AND refobjsubid = :3 ", ObjectIdGetDatum(RelationRelationId), ObjectIdGetDatum(RelationGetRelid(rel)), Int32GetDatum(0))); extrel = relation_open(ExtTableRelationId, AccessShareLock); pcqCtxExt = caql_addrel(cqclr(&ctxExt), extrel); while (HeapTupleIsValid(tup = caql_getnext(pcqCtx))) { Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(tup); Oid fmterrtbl; fmterrtbl = caql_getoid(pcqCtxExt, cql("SELECT fmterrtbl FROM pg_exttable " " WHERE reloid = :1", ObjectIdGetDatum(dep->objid))); if (RelationGetRelid(rel) == fmterrtbl) { result = true; break; } } relation_close(extrel, AccessShareLock); caql_endscan(pcqCtx); return result; }
/* ---------------------------------------------------------------- * UpdateRelationRelation * ---------------------------------------------------------------- */ static void UpdateRelationRelation(Relation indexRelation) { Relation pg_class; HeapTuple tuple; pg_class = heap_openr(RelationRelationName, RowExclusiveLock); /* XXX Natts_pg_class_fixed is a hack - see pg_class.h */ tuple = heap_addheader(Natts_pg_class_fixed, true, CLASS_TUPLE_SIZE, (void *) indexRelation->rd_rel); /* * the new tuple must have the oid already chosen for the index. sure * would be embarrassing to do this sort of thing in polite company. */ HeapTupleSetOid(tuple, RelationGetRelid(indexRelation)); simple_heap_insert(pg_class, tuple); /* update the system catalog indexes */ CatalogUpdateIndexes(pg_class, tuple); heap_freetuple(tuple); heap_close(pg_class, RowExclusiveLock); }
/* * Executor state preparation for evaluation of constraint expressions, * indexes and triggers. * * This is based on similar code in copy.c */ static EState * create_estate_for_relation(LogicalRepRelMapEntry *rel) { EState *estate; ResultRelInfo *resultRelInfo; RangeTblEntry *rte; estate = CreateExecutorState(); rte = makeNode(RangeTblEntry); rte->rtekind = RTE_RELATION; rte->relid = RelationGetRelid(rel->localrel); rte->relkind = rel->localrel->rd_rel->relkind; estate->es_range_table = list_make1(rte); resultRelInfo = makeNode(ResultRelInfo); InitResultRelInfo(resultRelInfo, rel->localrel, 1, NULL, 0); estate->es_result_relations = resultRelInfo; estate->es_num_result_relations = 1; estate->es_result_relation_info = resultRelInfo; estate->es_output_cid = GetCurrentCommandId(true); /* Triggers might need a slot */ if (resultRelInfo->ri_TrigDesc) estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL); /* Prepare to catch AFTER triggers. */ AfterTriggerBeginQuery(); return estate; }
/* ---------------------------------------------------------------- * ForeignNext * * This is a workhorse for ExecForeignScan * ---------------------------------------------------------------- */ static TupleTableSlot * ForeignNext(ForeignScanState *node) { TupleTableSlot *slot; ForeignScan *plan = (ForeignScan *) node->ss.ps.plan; ExprContext *econtext = node->ss.ps.ps_ExprContext; MemoryContext oldcontext; /* Call the Iterate function in short-lived context */ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); slot = node->fdwroutine->IterateForeignScan(node); MemoryContextSwitchTo(oldcontext); /* * If any system columns are requested, we have to force the tuple into * physical-tuple form to avoid "cannot extract system attribute from * virtual tuple" errors later. We also insert a valid value for * tableoid, which is the only actually-useful system column. */ if (plan->fsSystemCol && !TupIsNull(slot)) { HeapTuple tup = ExecMaterializeSlot(slot); tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation); } return slot; }
/* * Write relation description to the output stream. */ static void pglogical_write_rel(StringInfo out, PGLogicalOutputData *data, Relation rel) { const char *nspname; uint8 nspnamelen; const char *relname; uint8 relnamelen; Oid relid; if (MtmIsFilteredTxn) { return; } relid = RelationGetRelid(rel); pq_sendbyte(out, 'R'); /* sending RELATION */ pq_sendint(out, relid, sizeof relid); /* use Oid as relation identifier */ nspname = get_namespace_name(rel->rd_rel->relnamespace); if (nspname == NULL) elog(ERROR, "cache lookup failed for namespace %u", rel->rd_rel->relnamespace); nspnamelen = strlen(nspname) + 1; relname = NameStr(rel->rd_rel->relname); relnamelen = strlen(relname) + 1; pq_sendbyte(out, nspnamelen); /* schema name length */ pq_sendbytes(out, nspname, nspnamelen); pq_sendbyte(out, relnamelen); /* table name length */ pq_sendbytes(out, relname, relnamelen); }
/* * Find the ObjectAddress for an attribute. */ static ObjectAddress get_object_address_attribute(ObjectType objtype, List *objname, Relation *relp, LOCKMODE lockmode) { ObjectAddress address; List *relname; Oid reloid; Relation relation; const char *attname; /* Extract relation name and open relation. */ attname = strVal(lfirst(list_tail(objname))); relname = list_truncate(list_copy(objname), list_length(objname) - 1); relation = relation_openrv(makeRangeVarFromNameList(relname), lockmode); reloid = RelationGetRelid(relation); /* Look up attribute and construct return value. */ address.classId = RelationRelationId; address.objectId = reloid; address.objectSubId = get_attnum(reloid, attname); if (address.objectSubId == InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", attname, RelationGetRelationName(relation)))); *relp = relation; return address; }
/* * This routine is in charge of "vacuuming" a BRIN index: we just summarize * ranges that are currently unsummarized. */ Datum brinvacuumcleanup(PG_FUNCTION_ARGS) { IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0); IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1); Relation heapRel; /* No-op in ANALYZE ONLY mode */ if (info->analyze_only) PG_RETURN_POINTER(stats); if (!stats) stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult)); stats->num_pages = RelationGetNumberOfBlocks(info->index); /* rest of stats is initialized by zeroing */ heapRel = heap_open(IndexGetRelation(RelationGetRelid(info->index), false), AccessShareLock); brinsummarize(info->index, heapRel, &stats->num_index_tuples, &stats->num_index_tuples); heap_close(heapRel, AccessShareLock); PG_RETURN_POINTER(stats); }
/** * Initialize and get ready for inserting values into the parquet table. If the * parquet insert descriptor is not created, NULL is returned. * * @rel the relation to insert * @segno the segment number */ ParquetInsertDesc parquet_insert_init(Relation rel, ResultRelSegFileInfo *segfileinfo) { ParquetInsertDesc parquetInsertDesc = NULL; AppendOnlyEntry *aoentry = NULL; MemoryContext oldMemoryContext = NULL; StringInfoData titleBuf; int relNameLen = 0; /* * Get the pg_appendonly information for this table */ aoentry = GetAppendOnlyEntry(RelationGetRelid(rel), SnapshotNow); /* The parquet entry must exist and is at right version. The version number is hard coded when the entry is created. */ Assert(aoentry != NULL); Assert(aoentry->majorversion == 1 && aoentry->minorversion == 0); parquetInsertDesc = (ParquetInsertDesc) palloc0(sizeof(ParquetInsertDescData)); parquetInsertDesc->memoryContext = CurrentMemoryContext; oldMemoryContext = MemoryContextSwitchTo(parquetInsertDesc->memoryContext); parquetInsertDesc->parquet_rel = rel; relNameLen = strlen(rel->rd_rel->relname.data); parquetInsertDesc->relname = (char*)palloc0(relNameLen + 1); memcpy(parquetInsertDesc->relname, rel->rd_rel->relname.data, relNameLen); parquetInsertDesc->parquetMetaDataSnapshot = SnapshotNow; parquetInsertDesc->parquet_file = -1; parquetInsertDesc->parquetFilePathNameMaxLen = AOSegmentFilePathNameLen(rel) + 1; parquetInsertDesc->parquetFilePathName = (char*) palloc0(parquetInsertDesc->parquetFilePathNameMaxLen); parquetInsertDesc->parquetFilePathName[0] = '\0'; parquetInsertDesc->footerProtocol = NULL; Assert(segfileinfo->segno >= 0); parquetInsertDesc->cur_segno = segfileinfo->segno; parquetInsertDesc->aoEntry = aoentry; parquetInsertDesc->insertCount = 0; initStringInfo(&titleBuf); appendStringInfo(&titleBuf, "Write of Parquet relation '%s'", RelationGetRelationName(parquetInsertDesc->parquet_rel)); parquetInsertDesc->title = titleBuf.data; parquetInsertDesc->mirroredOpen = (MirroredAppendOnlyOpen *) palloc0(sizeof(MirroredAppendOnlyOpen)); parquetInsertDesc->mirroredOpen->isActive = FALSE; parquetInsertDesc->mirroredOpen->segmentFileNum = 0; parquetInsertDesc->mirroredOpen->primaryFile = -1; parquetInsertDesc->previous_rowgroupcnt = 0; /* open our current relation file segment for write */ SetCurrentFileSegForWrite(parquetInsertDesc, segfileinfo); /* Allocation is done. Go back to caller memory-context. */ MemoryContextSwitchTo(oldMemoryContext); return parquetInsertDesc; }
/* * Given a WITH(...) clause and no other column encoding directives -- such as * in the case of CREATE TABLE WITH () AS SELECT -- fill in the column encoding * catalog entries for that relation. */ void AddDefaultRelationAttributeOptions(Relation rel, List *options) { Datum opts; AttrNumber attno; List *ce; /* only supported on AOCO at this stage */ if (true) return; ce = form_default_storage_directive(options); if (!ce) ce = default_column_encoding_clause(); ce = transformStorageEncodingClause(ce); opts = transformRelOptions(PointerGetDatum(NULL), ce, true, false); for (attno = 1; attno <= RelationGetNumberOfAttributes(rel); attno++) add_attribute_encoding_entry(RelationGetRelid(rel), attno, opts); CommandCounterIncrement(); }
/* * OpenErrorTable * * Open the error table for this operation, and perform all necessary checks. */ void OpenErrorTable(CdbSreh *cdbsreh, RangeVar *errortable) { AclResult aclresult; AclMode required_access = ACL_INSERT; Oid relOid; /* Open and lock the error relation, using the appropriate lock type. */ cdbsreh->errtbl = heap_openrv(errortable, RowExclusiveLock); relOid = RelationGetRelid(cdbsreh->errtbl); /* Check relation permissions. */ aclresult = pg_class_aclcheck(relOid, GetUserId(), required_access); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(cdbsreh->errtbl)); /* check read-only transaction */ if (XactReadOnly && !isTempNamespace(RelationGetNamespace(cdbsreh->errtbl))) ereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), errmsg("transaction is read-only"))); /* make sure this is a regular relation */ if (cdbsreh->errtbl->rd_rel->relkind != RELKIND_RELATION) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" exists in the database but is a non table relation", RelationGetRelationName(cdbsreh->errtbl)))); }
/* * AlterConversionOwner_internal * * Internal routine for changing the owner. rel must be pg_conversion, already * open and suitably locked; it will not be closed. */ static void AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) { Form_pg_conversion convForm; HeapTuple tup; Assert(RelationGetRelid(rel) == ConversionRelationId); tup = SearchSysCacheCopy(CONOID, ObjectIdGetDatum(conversionOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for conversion %u", conversionOid); convForm = (Form_pg_conversion) GETSTRUCT(tup); /* * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ if (convForm->conowner != newOwnerId) { AclResult aclresult; /* Superusers can always do it */ if (!superuser()) { /* Otherwise, must be owner of the existing object */ if (!pg_conversion_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameStr(convForm->conname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); /* New owner must have CREATE privilege on namespace */ aclresult = pg_namespace_aclcheck(convForm->connamespace, newOwnerId, ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(convForm->connamespace)); } /* * Modify the owner --- okay to scribble on tup because it's a copy */ convForm->conowner = newOwnerId; simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); /* Update owner dependency reference */ changeDependencyOnOwner(ConversionRelationId, conversionOid, newOwnerId); } heap_freetuple(tup); }
/* * Returns true if the relation has no tuples. Prepare phase of * compaction invokes this function on each QE. * * Examples of empty tables: * 1. parent of a partitioned table * 2. table that is created but no tuples have been inserted yet * 3. table from which all existing tuples are deleted and the table * is vacuumed. This is a special case in which pg_aoseg_<oid> has * non-zero number of rows but tupcount value is zero for all rows. */ bool AppendOnlyCompaction_IsRelationEmpty(Relation aorel) { AppendOnlyEntry *aoEntry; Relation pg_aoseg_rel; TupleDesc pg_aoseg_dsc; HeapTuple tuple; HeapScanDesc aoscan; int Anum_tupcount; bool empty = true; Assert(RelationIsAoRows(aorel) || RelationIsAoCols(aorel)); aoEntry = GetAppendOnlyEntry(RelationGetRelid(aorel), SnapshotNow); pg_aoseg_rel = heap_open(aoEntry->segrelid, AccessShareLock); pg_aoseg_dsc = RelationGetDescr(pg_aoseg_rel); aoscan = heap_beginscan(pg_aoseg_rel, SnapshotNow, 0, NULL); Anum_tupcount = RelationIsAoRows(aorel)? Anum_pg_aoseg_tupcount: Anum_pg_aocs_tupcount; while ((tuple = heap_getnext(aoscan, ForwardScanDirection)) != NULL && empty) { if (0 < fastgetattr(tuple, Anum_tupcount, pg_aoseg_dsc, NULL)) empty = false; } heap_endscan(aoscan); heap_close(pg_aoseg_rel, AccessShareLock); return empty; }
/* * DynamicScan_ObtainRelations * Returns old and new relations for a new part oid */ static void DynamicScan_ObtainRelations(ScanState *scanState, Oid newOid, Relation *outOldRelation, Relation *outNewRelation) { Relation oldRelation = NULL; Relation newRelation = NULL; Oid oldOid = InvalidOid; if (NULL != scanState->ss_currentRelation) { oldOid = RelationGetRelid(scanState->ss_currentRelation); oldRelation = scanState->ss_currentRelation; } else { /* Must be the initial state */ Assert(SCAN_INIT == scanState->scan_state); oldOid = DynamicScan_GetOriginalRelationOid(scanState); Assert(OidIsValid(oldOid)); oldRelation = OpenScanRelationByOid(oldOid); } if (oldOid == newOid) { newRelation = oldRelation; } else { newRelation = OpenScanRelationByOid(newOid); } Assert(NULL != oldRelation); Assert(NULL != newRelation); *outOldRelation = oldRelation; *outNewRelation = newRelation; }
/* ---------------- * BuildIndexInfo * Construct an IndexInfo record for an open index * * IndexInfo stores the information about the index that's needed by * FormIndexDatum, which is used for both index_build() and later insertion * of individual index tuples. Normally we build an IndexInfo for an index * just once per command, and then use it for (potentially) many tuples. * ---------------- */ IndexInfo * BuildIndexInfo(Relation index) { IndexInfo *ii = makeNode(IndexInfo); Form_pg_index indexStruct = index->rd_index; int i; int numKeys; /* check the number of keys, and copy attr numbers into the IndexInfo */ numKeys = indexStruct->indnatts; if (numKeys < 1 || numKeys > INDEX_MAX_KEYS) elog(ERROR, "invalid indnatts %d for index %u", numKeys, RelationGetRelid(index)); ii->ii_NumIndexAttrs = numKeys; for (i = 0; i < numKeys; i++) ii->ii_KeyAttrNumbers[i] = indexStruct->indkey[i]; /* fetch any expressions needed for expressional indexes */ ii->ii_Expressions = RelationGetIndexExpressions(index); ii->ii_ExpressionsState = NIL; /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); ii->ii_PredicateState = NIL; /* other info */ ii->ii_Unique = indexStruct->indisunique; return ii; }
/* * Write UPDATE to the output stream. */ void logicalrep_write_update(StringInfo out, Relation rel, HeapTuple oldtuple, HeapTuple newtuple) { pq_sendbyte(out, 'U'); /* action UPDATE */ Assert(rel->rd_rel->relreplident == REPLICA_IDENTITY_DEFAULT || rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL || rel->rd_rel->relreplident == REPLICA_IDENTITY_INDEX); /* use Oid as relation identifier */ pq_sendint(out, RelationGetRelid(rel), 4); if (oldtuple != NULL) { if (rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL) pq_sendbyte(out, 'O'); /* old tuple follows */ else pq_sendbyte(out, 'K'); /* old key follows */ logicalrep_write_tuple(out, rel, oldtuple); } pq_sendbyte(out, 'N'); /* new tuple follows */ logicalrep_write_tuple(out, rel, newtuple); }
/* * Check iff the write target is ok */ void VerifyTarget(Relation rel, int64 max_dup_errors) { AclMode required_access; AclMode aclresult; if (rel->rd_rel->relkind != RELKIND_RELATION) { const char *type; switch (rel->rd_rel->relkind) { case RELKIND_VIEW: type = "view"; break; case RELKIND_SEQUENCE: type = "sequence"; break; default: type = "non-table relation"; break; } ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot load to %s \"%s\"", type, RelationGetRelationName(rel)))); } required_access = ACL_INSERT | (max_dup_errors != 0 ? ACL_DELETE : ACL_NO_RIGHTS); aclresult = pg_class_aclmask( RelationGetRelid(rel), GetUserId(), required_access, ACLMASK_ALL); if (required_access != aclresult) aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, RelationGetRelationName(rel)); }
TableScanDesc table_beginscan_parallel(Relation relation, ParallelTableScanDesc parallel_scan) { Snapshot snapshot; Assert(RelationGetRelid(relation) == parallel_scan->phs_relid); if (!parallel_scan->phs_snapshot_any) { /* Snapshot was serialized -- restore it */ snapshot = RestoreSnapshot((char *) parallel_scan + parallel_scan->phs_snapshot_off); RegisterSnapshot(snapshot); } else { /* SnapshotAny passed by caller (not serialized) */ snapshot = SnapshotAny; } return relation->rd_tableam->scan_begin(relation, snapshot, 0, NULL, parallel_scan, true, true, true, false, false, !parallel_scan->phs_snapshot_any); }
/* Updates the given frame with information about a table row that was deleted. * This is used only during stream replication. */ int update_frame_with_delete(avro_value_t *frame_val, schema_cache_t cache, Relation rel, HeapTuple oldtuple) { int err = 0; schema_cache_entry *entry; bytea *key_bin = NULL, *old_bin = NULL; int changed = schema_cache_lookup(cache, rel, &entry); if (changed < 0) { return EINVAL; } else if (changed) { check(err, update_frame_with_table_schema(frame_val, entry)); } if (oldtuple) { check(err, extract_tuple_key(entry, rel, RelationGetDescr(rel), oldtuple, &key_bin)); check(err, avro_value_reset(&entry->row_value)); check(err, tuple_to_avro_row(&entry->row_value, RelationGetDescr(rel), oldtuple)); check(err, try_writing(&old_bin, &write_avro_binary, &entry->row_value)); } check(err, update_frame_with_delete_raw(frame_val, RelationGetRelid(rel), key_bin, old_bin)); if (key_bin) pfree(key_bin); if (old_bin) pfree(old_bin); return err; }
/* * This will use heap scan. */ void test__caql_switch2(void **state) { const char *query = "INSERT into pg_proc"; struct caql_hash_cookie *hash_cookie; cqContext context = {0}, *pCtx; Datum keys[] = {}; cq_list *pcql = CaQL(query, 0, keys); RelationData dummyrel; dummyrel.rd_id = ProcedureRelationId; hash_cookie = cq_lookup(query, strlen(query), pcql); /* setup heap_open */ expect__heap_open(ProcedureRelationId, true, RowExclusiveLock, true, &dummyrel); pCtx = caql_switch(hash_cookie, &context, pcql); assert_true(pCtx != NULL); assert_true(pCtx->cq_sysScan == NULL); assert_int_equal(RelationGetRelid(pCtx->cq_heap_rel), ProcedureRelationId); }
/* * GetFdwRoutineForRelation - look up the handler of the foreign-data wrapper * for the given foreign table, and retrieve its FdwRoutine struct. * * This function is preferred over GetFdwRoutineByRelId because it caches * the data in the relcache entry, saving a number of catalog lookups. * * If makecopy is true then the returned data is freshly palloc'd in the * caller's memory context. Otherwise, it's a pointer to the relcache data, * which will be lost in any relcache reset --- so don't rely on it long. */ FdwRoutine * GetFdwRoutineForRelation(Relation relation, bool makecopy) { FdwRoutine *fdwroutine; FdwRoutine *cfdwroutine; if (relation->rd_fdwroutine == NULL) { /* Get the info by consulting the catalogs and the FDW code */ fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(relation)); /* Save the data for later reuse in CacheMemoryContext */ cfdwroutine = (FdwRoutine *) MemoryContextAlloc(CacheMemoryContext, sizeof(FdwRoutine)); memcpy(cfdwroutine, fdwroutine, sizeof(FdwRoutine)); relation->rd_fdwroutine = cfdwroutine; /* Give back the locally palloc'd copy regardless of makecopy */ return fdwroutine; } /* We have valid cached data --- does the caller want a copy? */ if (makecopy) { fdwroutine = (FdwRoutine *) palloc(sizeof(FdwRoutine)); memcpy(fdwroutine, relation->rd_fdwroutine, sizeof(FdwRoutine)); return fdwroutine; } /* Only a short-lived reference is needed, so just hand back cached copy */ return relation->rd_fdwroutine; }
/* * This routine is in charge of "vacuuming" a BRIN index: we just summarize * ranges that are currently unsummarized. */ IndexBulkDeleteResult * brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats) { Relation heapRel; /* No-op in ANALYZE ONLY mode */ if (info->analyze_only) return stats; if (!stats) stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult)); stats->num_pages = RelationGetNumberOfBlocks(info->index); /* rest of stats is initialized by zeroing */ heapRel = heap_open(IndexGetRelation(RelationGetRelid(info->index), false), AccessShareLock); brin_vacuum_scan(info->index, info->strategy); brinsummarize(info->index, heapRel, &stats->num_index_tuples, &stats->num_index_tuples); heap_close(heapRel, AccessShareLock); return stats; }
static void gp_statistics_estimate_reltuples_relpages_ao_rows(Relation rel, float4 *reltuples, float4 *relpages) { FileSegTotals *fstotal; AppendOnlyEntry *aoEntry; AppendOnlyVisimap visimap; int64 hidden_tupcount = 0; /** * Ensure that the right kind of relation with the right type of storage is passed to us. */ Assert(rel->rd_rel->relkind == RELKIND_RELATION); Assert(RelationIsAoRows(rel)); fstotal = GetSegFilesTotals(rel, SnapshotNow); Assert(fstotal); /** * The planner doesn't understand AO's blocks, so need this method to try to fudge up a number for * the planner. */ *relpages = RelationGuessNumberOfBlocks((double)fstotal->totalbytes); aoEntry = GetAppendOnlyEntry(RelationGetRelid(rel), SnapshotNow); AppendOnlyVisimap_Init(&visimap, aoEntry->visimaprelid, aoEntry->visimapidxid, AccessShareLock, SnapshotNow); hidden_tupcount = AppendOnlyVisimap_GetRelationHiddenTupleCount(&visimap); AppendOnlyVisimap_Finish(&visimap, AccessShareLock); /** * The number of tuples in AO table is known accurately. Therefore, we just utilize this value. */ *reltuples = (double)(fstotal->totaltuples - hidden_tupcount); pfree(fstotal); pfree(aoEntry); return; }
/* * CStoreTable checks if the given table name belongs to a foreign columnar store * table. If it does, the function returns true. Otherwise, it returns false. */ static bool CStoreTable(RangeVar *rangeVar) { bool cstoreTable = false; Relation relation = heap_openrv(rangeVar, AccessShareLock); Oid relationId = RelationGetRelid(relation); char relationKind = get_rel_relkind(relationId); if (relationKind == RELKIND_FOREIGN_TABLE) { ForeignTable *foreignTable = GetForeignTable(relationId); ForeignServer *server = GetForeignServer(foreignTable->serverid); ForeignDataWrapper *foreignDataWrapper = GetForeignDataWrapper(server->fdwid); char *foreignWrapperName = foreignDataWrapper->fdwname; if (strncmp(foreignWrapperName, CSTORE_FDW_NAME, NAMEDATALEN) == 0) { cstoreTable = true; } } heap_close(relation, AccessShareLock); return cstoreTable; }
/* * Setup a ScanKey for a search in the relation 'rel' for a tuple 'key' that * is setup to match 'rel' (*NOT* idxrel!). * * Returns whether any column contains NULLs. * * This is not generic routine, it expects the idxrel to be replication * identity of a rel and meet all limitations associated with that. */ static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel, TupleTableSlot *searchslot) { int attoff; bool isnull; Datum indclassDatum; oidvector *opclass; int2vector *indkey = &idxrel->rd_index->indkey; bool hasnulls = false; Assert(RelationGetReplicaIndex(rel) == RelationGetRelid(idxrel)); indclassDatum = SysCacheGetAttr(INDEXRELID, idxrel->rd_indextuple, Anum_pg_index_indclass, &isnull); Assert(!isnull); opclass = (oidvector *) DatumGetPointer(indclassDatum); /* Build scankey for every attribute in the index. */ for (attoff = 0; attoff < IndexRelationGetNumberOfKeyAttributes(idxrel); attoff++) { Oid operator; Oid opfamily; RegProcedure regop; int pkattno = attoff + 1; int mainattno = indkey->values[attoff]; Oid optype = get_opclass_input_type(opclass->values[attoff]); /* * Load the operator info. We need this to get the equality operator * function for the scan key. */ opfamily = get_opclass_family(opclass->values[attoff]); operator = get_opfamily_member(opfamily, optype, optype, BTEqualStrategyNumber); if (!OidIsValid(operator)) elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", BTEqualStrategyNumber, optype, optype, opfamily); regop = get_opcode(operator); /* Initialize the scankey. */ ScanKeyInit(&skey[attoff], pkattno, BTEqualStrategyNumber, regop, searchslot->tts_values[mainattno - 1]); /* Check for null value. */ if (searchslot->tts_isnull[mainattno - 1]) { hasnulls = true; skey[attoff].sk_flags |= SK_ISNULL; } } return hasnulls; }
Datum currtid_byrelname(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); ItemPointer tid = PG_GETARG_ITEMPOINTER(1); ItemPointer result; RangeVar *relrv; Relation rel; AclResult aclresult; Snapshot snapshot; relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = heap_openrv(relrv, AccessShareLock); aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); if (rel->rd_rel->relkind == RELKIND_VIEW || rel->rd_rel->relkind == RELKIND_CONTVIEW) return currtid_for_view(rel, tid); result = (ItemPointer) palloc(sizeof(ItemPointerData)); ItemPointerCopy(tid, result); snapshot = RegisterSnapshot(GetLatestSnapshot()); heap_get_latest_tid(rel, snapshot, result); UnregisterSnapshot(snapshot); heap_close(rel, AccessShareLock); PG_RETURN_ITEMPOINTER(result); }
Datum currtid_byreloid(PG_FUNCTION_ARGS) { Oid reloid = PG_GETARG_OID(0); ItemPointer tid = PG_GETARG_ITEMPOINTER(1); ItemPointer result; Relation rel; AclResult aclresult; result = (ItemPointer) palloc(sizeof(ItemPointerData)); if (!reloid) { *result = Current_last_tid; PG_RETURN_ITEMPOINTER(result); } rel = heap_open(reloid, AccessShareLock); aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); if (rel->rd_rel->relkind == RELKIND_VIEW) return currtid_for_view(rel, tid); ItemPointerCopy(tid, result); heap_get_latest_tid(rel, SnapshotNow, result); heap_close(rel, AccessShareLock); PG_RETURN_ITEMPOINTER(result); }
/* * hidden_beginscan * * Allocates the hidden scan descriptor. */ HiddenScanDesc hidden_beginscan(Relation heapRelation, int nkeys, ScanKey key) { HiddenScanDesc hscan = NULL; switch (RelationGetRelid(heapRelation)) { case ProcedureRelationId: { int i; hscan = palloc(sizeof(HiddenScanDescData)); hscan->hdn_rel = heapRelation; hscan->hdn_nkeys = nkeys; hscan->hdn_key = palloc(sizeof(ScanKeyData) * nkeys); /* need a copy if it's indexscan as it scribles the key */ for (i = 0; i < nkeys; i++) memcpy(&hscan->hdn_key[i], &key[i], sizeof(ScanKeyData)); hscan->hdn_tuples = GetHiddenPgProcTuples(heapRelation, &hscan->hdn_len); hscan->hdn_idx = 0; } break; default: break; } return hscan; }
/* * Work horse underneath DefineRelation(). * * Simply adds user specified ENCODING () clause information to * pg_attribute_encoding. Should be absolutely valid at this point. */ void AddRelationAttributeEncodings(Relation rel, List *attr_encodings) { Oid relid = RelationGetRelid(rel); ListCell *lc; foreach(lc, attr_encodings) { Datum attoptions; ColumnReferenceStorageDirective *c = lfirst(lc); List *encoding; AttrNumber attnum; Insist(IsA(c, ColumnReferenceStorageDirective)); attnum = get_attnum(relid, c->column); if (attnum == InvalidAttrNumber) elog(ERROR, "column \"%s\" does not exist", c->column); if (attnum < 0) elog(ERROR, "column \"%s\" is a system column", c->column); encoding = c->encoding; if (!encoding) continue; attoptions = transformRelOptions(PointerGetDatum(NULL), encoding, true, false); add_attribute_encoding_entry(relid, attnum, attoptions); }