| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /*------------------------------------------------------------------------- |
| * |
| * cdbinmemheapam.c |
| * goh in-memory heap table access method |
| * |
| *------------------------------------------------------------------------- |
| */ |
| |
| #include "postgres.h" |
| |
| #include "access/genam.h" |
| #include "access/valid.h" |
| #include "catalog/catalog.h" |
| #include "catalog/gp_policy.h" |
| #include "catalog/pg_exttable.h" |
| #include "catalog/pg_namespace.h" |
| #include "catalog/pg_type.h" |
| #include "cdb/cdbinmemheapam.h" |
| #include "cdb/cdbvars.h" |
| #include "nodes/memnodes.h" |
| #include "nodes/parsenodes.h" |
| #include "storage/lock.h" |
| #include "utils/fmgroids.h" |
| #include "utils/hsearch.h" |
| #include "utils/inval.h" |
| |
| HTAB* OidInMemMappings[INMEM_MAPPINGS_SIZE] = { NULL }; |
| static MemoryContext InMemMappingCxt[INMEM_MAPPINGS_SIZE] = { NULL }; |
| static const char* InMemMappingNames[INMEM_MAPPINGS_SIZE] = { "OidInMemHeapMapping", "OidInMemOnlyMapping"}; |
| |
| static int |
| InMemHeap_Find(InMemHeapRelation relation, ItemPointer otid); |
| |
| static bool |
| InMemHeap_GetNextIndex(InMemHeapScanDesc scan, ScanDirection direction); |
| |
| static void |
| CheckInMemConstraintsPgNamespace(InMemHeapRelation relation, HeapTuple newTuple); |
| |
| static void |
| CheckInMemConstraintsPgClass(InMemHeapRelation relation, HeapTuple newTuple); |
| |
| static void |
| CheckInMemConstraintsPgType(InMemHeapRelation relation, HeapTuple newTuple); |
| |
| static void |
| CheckInMemConstraintsPgAttribute(InMemHeapRelation relation, HeapTuple newTuple); |
| |
| static void |
| CheckInMemConstraintsPgExttable(InMemHeapRelation relation, HeapTuple newTuple); |
| |
| static void |
| CheckInMemConstraintsGpDistributionPolicy(InMemHeapRelation relation, HeapTuple newTuple); |
| |
| typedef void (*CheckConstraintsFn) (InMemHeapRelation relation, HeapTuple newTuple); |
| |
| /* |
| * init relid to in-memory table mapping |
| */ |
| void |
| InitOidInMemHeapMapping(long initSize, MemoryContext memcxt, InMemMappingType mappingType) |
| { |
| HASHCTL info; |
| |
| Assert(mappingType < INMEM_MAPPINGS_SIZE); |
| Assert(NULL == OidInMemMappings[mappingType]); |
| |
| info.hcxt = memcxt; |
| info.hash = oid_hash; |
| info.keysize = sizeof(Oid); |
| info.entrysize = sizeof(struct OidInMemHeapMappingEntry); |
| |
| OidInMemMappings[mappingType] = hash_create(InMemMappingNames[mappingType], initSize, &info, |
| HASH_CONTEXT | HASH_FUNCTION | HASH_ELEM); |
| |
| Assert(NULL != OidInMemMappings[mappingType]); |
| |
| InMemMappingCxt[mappingType] = memcxt; |
| } |
| |
| /* |
| * cleanup relid to in-memory table mapping |
| */ |
| void |
| CleanupOidInMemHeapMapping(InMemMappingType mappingType) |
| { |
| Assert(mappingType < INMEM_MAPPINGS_SIZE); |
| |
| if (NULL == OidInMemMappings[mappingType]) |
| { |
| return; |
| } |
| |
| hash_destroy(OidInMemMappings[mappingType]); |
| OidInMemMappings[mappingType] = NULL; |
| |
| InMemMappingCxt[mappingType] = NULL; |
| } |
| |
| /* |
| * get a in-memory table by relid, |
| */ |
| InMemHeapRelation |
| OidGetInMemHeapRelation(Oid relid, InMemMappingType mappingType) |
| { |
| bool found = FALSE; |
| struct OidInMemHeapMappingEntry *retval; |
| |
| Assert(mappingType < INMEM_MAPPINGS_SIZE); |
| |
| if (NULL != OidInMemMappings[mappingType]) |
| { |
| retval = hash_search(OidInMemMappings[mappingType], &relid, HASH_FIND, &found); |
| if (NULL != retval) |
| { |
| return retval->rel; |
| } |
| } |
| |
| return NULL ; |
| } |
| |
| struct MemHeapHashIndexEntry { |
| Oid key; |
| List *values; |
| }; |
| |
| typedef struct MemHeapHashIndexEntry MemHeapHashIndexEntry; |
| |
| /* |
| * create a in-memory heap table with Oid. |
| * the in-memory table and all its tuples are in memcxt memory context. |
| * at first, initSize tuples space will be alloced in the tuple, |
| * and will re-alloc at runtime if inserting more tuples. |
| */ |
| InMemHeapRelation |
| InMemHeap_Create(Oid relid, Relation rel, bool ownrel, |
| int32 initSize, LOCKMODE lock, const char *relname, bool createIndex, int keyAttrno, |
| InMemMappingType mappingType) |
| { |
| bool found = FALSE; |
| struct OidInMemHeapMappingEntry *entry; |
| InMemHeapRelation memheap = NULL; |
| MemoryContext oldcxt; |
| |
| Assert(mappingType < INMEM_MAPPINGS_SIZE); |
| Assert(NULL != OidInMemMappings[mappingType]); |
| |
| hash_search(OidInMemMappings[mappingType], &relid, HASH_FIND, &found); |
| |
| if (found) |
| { |
| ereport(ERROR, |
| (errcode(ERRCODE_INTERNAL_ERROR), |
| errmsg("in-memory table with Oid = %d already exist.", relid))); |
| } |
| |
| Assert(MemoryContextIsValid(InMemMappingCxt[mappingType])); |
| |
| oldcxt = CurrentMemoryContext; |
| CurrentMemoryContext = InMemMappingCxt[mappingType]; |
| |
| memheap = palloc(sizeof(InMemHeapRelationData)); |
| |
| memheap->memcxt = InMemMappingCxt[mappingType]; |
| memheap->relid = relid; |
| memheap->tupsize = 0; |
| memheap->tupmaxsize = initSize; |
| memheap->tuples = NULL; |
| memheap->rellock = lock; |
| memheap->ownrel = ownrel; |
| memheap->rel = rel; |
| memheap->hashIndex = NULL; |
| memheap->keyAttrno = 0; |
| StrNCpy(memheap->relname, relname, NAMEDATALEN); |
| |
| if (createIndex) |
| { |
| HASHCTL info; |
| memheap->keyAttrno = keyAttrno; |
| |
| /* Set key and entry sizes. */ |
| MemSet(&info, 0, sizeof(info)); |
| info.keysize = sizeof(Oid); |
| info.entrysize = sizeof(MemHeapHashIndexEntry); |
| info.hash = oid_hash; |
| info.hcxt = memheap->memcxt; |
| |
| memheap->hashIndex = hash_create("InMemHeap hash index", |
| 10, &info, |
| HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); |
| } |
| |
| initSize = initSize > 1 ? initSize : 1; |
| memheap->tuples = palloc(sizeof(InMemHeapTupleData) * initSize); |
| |
| entry = hash_search(OidInMemMappings[mappingType], &relid, HASH_ENTER, &found); |
| |
| entry->relid = relid; |
| entry->rel = memheap; |
| |
| CurrentMemoryContext = oldcxt; |
| |
| return memheap; |
| } |
| |
| /* |
| * drop a in-memory heap table. |
| */ |
| void |
| InMemHeap_Drop(Oid relid, InMemMappingType mappingType) |
| { |
| bool found = FALSE; |
| struct OidInMemHeapMappingEntry *entry = NULL; |
| |
| Assert(mappingType < INMEM_MAPPINGS_SIZE); |
| Assert(NULL != OidInMemMappings[mappingType]); |
| |
| entry = hash_search(OidInMemMappings[mappingType], &relid, HASH_FIND, &found); |
| |
| if (NULL == entry) |
| { |
| return; |
| } |
| |
| Assert(NULL != entry->rel); |
| |
| if (entry->rel->hashIndex) |
| { |
| hash_destroy(entry->rel->hashIndex); |
| entry->rel->hashIndex = NULL; |
| } |
| |
| if (entry->rel->tuples) |
| { |
| int i; |
| HeapTuple tup; |
| for (i = 0; i < entry->rel->tupsize; ++i) |
| { |
| tup = entry->rel->tuples[i].tuple; |
| if (tup) |
| { |
| pfree(tup); |
| } |
| } |
| pfree(entry->rel->tuples); |
| } |
| |
| if (entry->rel->ownrel && entry->rel->rel) |
| { |
| heap_close(entry->rel->rel, entry->rel->rellock); |
| } |
| |
| pfree(entry->rel); |
| |
| hash_search(OidInMemMappings[mappingType], &relid, HASH_REMOVE, &found); |
| } |
| |
| /* |
| * drop all in-memory tables of given mapping |
| */ |
| void |
| InMemHeap_DropAll(InMemMappingType mappingType) |
| { |
| HASH_SEQ_STATUS scan; |
| struct OidInMemHeapMappingEntry *entry; |
| Assert(mappingType < INMEM_MAPPINGS_SIZE); |
| |
| if (NULL == OidInMemMappings[mappingType]) |
| { |
| return; |
| } |
| |
| elog(DEBUG1, "Dropping in memory mapping %s", InMemMappingNames[mappingType]); |
| |
| hash_seq_init(&scan, OidInMemMappings[mappingType]); |
| |
| while (!!(entry = (struct OidInMemHeapMappingEntry *) hash_seq_search(&scan))) |
| { |
| InMemHeap_Drop(entry->relid, mappingType); |
| } |
| } |
| |
| /* |
| * begin a in-memory heap table scan. |
| */ |
| InMemHeapScanDesc |
| InMemHeap_BeginScan(InMemHeapRelation memheap, int nkeys, |
| ScanKey key, AttrNumber *orig_attnos, bool inmemonly) |
| { |
| InMemHeapScanDesc scan = palloc0(sizeof (InMemHeapScanDescData)); |
| Assert(NULL != scan); |
| |
| /* |
| * HAWQ-1525 |
| * |
| * The rel in InMemHeapRelation is a pointer, which is the address of heap relation |
| * in relcache. When the heap relation in relcache is clear for some reason, |
| * the value of rel in InMemHeapRelation is wrong. So we should reopen this relation |
| * to make sure it's correct. |
| */ |
| memheap->rel = RelationIdGetRelation(memheap->relid); |
| |
| scan->rs_rd = memheap; |
| scan->rs_nkeys = nkeys; |
| scan->rs_index = -1; |
| |
| if (nkeys > 0) |
| { |
| scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys); |
| } |
| else |
| { |
| scan->rs_key = NULL; |
| } |
| |
| if (key != NULL) |
| { |
| memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData)); |
| if (NULL != orig_attnos) |
| { |
| /* restore original key attribute numbers as the they are invalid in the passed array of keys */ |
| /* note: the scankey struct contains the attnos of the keys in the index scan, and here we need to |
| * refer to the original ones from the heap relation |
| */ |
| int i = 0; |
| for (i = 0; i < nkeys; i++) |
| { |
| scan->rs_key[i].sk_attno = orig_attnos[i]; |
| } |
| } |
| /* |
| * test if we can use hash index |
| */ |
| if (memheap->hashIndex) |
| { |
| int i; |
| for (i = 0 ; i < nkeys; ++i) |
| { |
| if(scan->rs_key[i].sk_attno == memheap->keyAttrno |
| && scan->rs_key[i].sk_strategy == BTEqualStrategyNumber) |
| { |
| /* |
| * we have a hash index on this attribute |
| */ |
| scan->hashIndexOk = TRUE; |
| scan->hashKeyIndexInScanKey = i; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (!inmemonly && (NULL != scan->rs_rd->rel)) |
| { |
| /* |
| * GPSQL-483, GPSQL-486 |
| * |
| * When a QE exists on the master, we still want to |
| * leverage metadata that was extracted for query execution via metadata dispatch. |
| * (Otherwise, we'd have to reintroduce snapshot propagation for some sort of |
| * bastardized DTM that exists to coordinate the dispatcher with a master QE.) |
| * In leveraging dispatched metadata on a master QE, we also need to ensure that |
| * we can't read duplicate metadata from the heap itself. To accomplish this, |
| * we constrain the fallback heap scan to only metadata which could not have |
| * been dispatched, namely the builtin catalog data. Thus, we add |
| * OID < FirstNormalOid to the scan key. |
| */ |
| |
| int heap_nkeys = nkeys + 1; |
| ScanKey heap_key = (ScanKey) palloc0(sizeof(ScanKeyData) * heap_nkeys); |
| /* Copy the given input keys */ |
| if (NULL != key) |
| { |
| memcpy(heap_key, scan->rs_key, nkeys * sizeof(ScanKeyData)); |
| } |
| |
| ScanKeyInit(&heap_key[heap_nkeys -1], |
| ObjectIdAttributeNumber, |
| BTLessStrategyNumber, F_OIDLT, |
| ObjectIdGetDatum(FirstNormalObjectId)); |
| |
| scan->hscan = heap_beginscan(scan->rs_rd->rel, SnapshotNow, |
| heap_nkeys, heap_key); |
| |
| if (NULL != heap_key) |
| { |
| pfree(heap_key); |
| } |
| } |
| return scan; |
| } |
| |
| /* |
| * end a in-memory heap table scan. |
| */ |
| void |
| InMemHeap_EndScan(InMemHeapScanDesc scan) |
| { |
| Assert(NULL != scan); |
| |
| RelationClose(scan->rs_rd->rel); |
| |
| if (NULL != scan->rs_key) |
| { |
| pfree(scan->rs_key); |
| } |
| |
| if (NULL != scan->hscan) |
| { |
| heap_endscan(scan->hscan); |
| } |
| |
| if (NIL != scan->indexReverseList) |
| { |
| list_free(scan->indexReverseList); |
| } |
| |
| pfree(scan); |
| } |
| |
| /* |
| * Increment scan->rs_index based on scan direction. |
| * Returns false when scan reaches its end. |
| */ |
| static bool |
| InMemHeap_GetNextIndex(InMemHeapScanDesc scan, ScanDirection direction) |
| { |
| if (BackwardScanDirection == direction) |
| { |
| if (-1 == scan->rs_index) /* scan beginning */ |
| { |
| scan->rs_index = scan->rs_rd->tupsize; |
| } |
| scan->rs_index--; |
| return (scan->rs_index > -1); |
| } |
| else |
| { |
| scan->rs_index++; |
| return (scan->rs_index < scan->rs_rd->tupsize); |
| } |
| } |
| |
| /* |
| * get next tuple in in-memory heap table. |
| */ |
| HeapTuple |
| InMemHeap_GetNext(InMemHeapScanDesc scan, ScanDirection direction) |
| { |
| bool valid = true; |
| |
| InMemHeapTuple pmemtup = NULL; |
| |
| Assert(NULL != scan); |
| |
| if (scan->hashIndexOk) |
| { |
| if (FALSE == scan->indexScanInitialized) |
| { |
| Oid key; |
| bool found; |
| |
| key = DatumGetObjectId(scan->rs_key[scan->hashKeyIndexInScanKey].sk_argument); |
| |
| MemHeapHashIndexEntry *entry; |
| entry = (MemHeapHashIndexEntry *) hash_search(scan->rs_rd->hashIndex, &key, |
| HASH_FIND, &found); |
| |
| if (found) |
| { |
| if (BackwardScanDirection == direction) |
| { |
| /* if direction is backward, reverse list */ |
| scan->indexReverseList = list_reverse_ints(entry->values); |
| entry->values = scan->indexReverseList; |
| } |
| scan->indexNext = list_head(entry->values); |
| } |
| else |
| scan->indexNext = NULL; |
| |
| scan->indexScanInitialized = TRUE; |
| scan->indexScanKey = key; |
| } |
| |
| for (; scan->indexNext != NULL; |
| scan->indexNext = lnext(scan->indexNext)) |
| { |
| int32 index = lfirst_int(scan->indexNext); |
| |
| elog(DEBUG1, "read index %d key %d for relation %s", index, scan->indexScanKey, scan->rs_rd->relname); |
| |
| pmemtup = &scan->rs_rd->tuples[index]; |
| HeapKeyTest(pmemtup->tuple, RelationGetDescr(scan->rs_rd->rel), |
| scan->rs_nkeys, scan->rs_key, &valid); |
| |
| if (!valid) |
| { |
| continue; |
| } |
| |
| scan->rs_ctup = pmemtup->tuple; |
| scan->indexNext = lnext(scan->indexNext); |
| return scan->rs_ctup; |
| } |
| } |
| else |
| { |
| /* for backward scan, change direction of iterator */ |
| while (InMemHeap_GetNextIndex(scan, direction)) |
| { |
| pmemtup = &scan->rs_rd->tuples[scan->rs_index]; |
| |
| Assert(NULL != pmemtup->tuple); |
| |
| if (scan->rs_key != NULL) |
| { |
| Assert(NULL != scan->rs_rd->rel); |
| HeapKeyTest(pmemtup->tuple, RelationGetDescr(scan->rs_rd->rel), |
| scan->rs_nkeys, scan->rs_key, &valid); |
| } |
| |
| if (!valid) |
| { |
| continue; |
| } |
| |
| scan->rs_ctup = pmemtup->tuple; |
| return scan->rs_ctup; |
| } |
| } |
| |
| /* |
| * read from local read only heap table. |
| */ |
| if (NULL != scan->hscan) |
| { |
| return heap_getnext(scan->hscan, direction); |
| } |
| |
| return NULL ; |
| } |
| |
| /* |
| * find a tuple which is only in memory, do not find it in heap table. |
| */ |
| static int |
| InMemHeap_Find(InMemHeapRelation relation, ItemPointer otid) |
| { |
| int rc = 0; |
| Assert(NULL != relation); |
| for (rc = 0; rc < relation->tupsize; ++rc) |
| { |
| if (!memcmp(&relation->tuples[rc].tuple->t_self, otid, |
| sizeof(ItemPointerData))) |
| { |
| break; |
| } |
| } |
| return rc; |
| } |
| |
| /* |
| * insert a tuple into in-memory heap table. |
| */ |
| void |
| InMemHeap_Insert(InMemHeapRelation relation, HeapTuple tup, int contentid) |
| { |
| InMemHeapTuple inmemtup; |
| |
| MemoryContext oldmem = CurrentMemoryContext; |
| Assert(NULL != relation && NULL != tup); |
| Assert(GP_ROLE_EXECUTE == Gp_role || -1 == contentid); |
| Assert(NULL != relation && NULL != tup); |
| |
| CurrentMemoryContext = relation->memcxt; |
| |
| if (relation->tupsize >= relation->tupmaxsize) |
| { |
| Assert(NULL != relation->tuples); |
| relation->tuples = repalloc(relation->tuples, |
| sizeof(InMemHeapTupleData) * relation->tupmaxsize * 2); |
| relation->tupmaxsize *= 2; |
| } |
| |
| inmemtup = &relation->tuples[relation->tupsize]; |
| |
| inmemtup->contentid = contentid; |
| inmemtup->flags = INMEM_HEAP_TUPLE_DISPATCHED; |
| inmemtup->tuple = heaptuple_copy_to(tup, NULL, NULL); |
| Assert(inmemtup->tuple != NULL); |
| |
| if (relation->hashIndex) |
| { |
| Oid key; |
| bool isNull, found; |
| |
| key = DatumGetObjectId( |
| heap_getattr(tup, relation->keyAttrno, |
| RelationGetDescr(relation->rel), &isNull)); |
| |
| Insist(!isNull && "index key cannot be null"); |
| |
| MemHeapHashIndexEntry *entry; |
| entry = (MemHeapHashIndexEntry *) hash_search(relation->hashIndex, &key, |
| HASH_ENTER, &found); |
| |
| if (!found) |
| { |
| entry->key = key; |
| entry->values = NIL; |
| } |
| |
| entry->values = lappend_int(entry->values, relation->tupsize); |
| |
| elog(DEBUG1, "add index %d key %d relation %s", relation->tupsize, key, relation->relname); |
| } |
| |
| ++relation->tupsize; |
| |
| CurrentMemoryContext = oldmem; |
| } |
| |
| /* |
| * update a tuple in in-memory heap table. |
| * |
| * if the target tuple already in the memory, |
| * update it in-place with flag INMEM_HEAP_TUPLE_UPDATED. |
| * else report an error. |
| * |
| * update should not change the otid of the old tuple, |
| * since updated tuple should write back to the master and update there. |
| */ |
| void |
| InMemHeap_Update(InMemHeapRelation relation, ItemPointer otid, |
| HeapTuple tup) |
| { |
| int pos; |
| HeapTuple target; |
| MemoryContext oldmem = CurrentMemoryContext; |
| |
| Assert(ItemPointerIsValid(otid)); |
| |
| pos = InMemHeap_Find(relation, otid); |
| |
| CurrentMemoryContext = relation->memcxt; |
| |
| /* |
| * not found, report error |
| */ |
| if (pos >= relation->tupsize) |
| { |
| ereport(ERROR, |
| (errcode(ERRCODE_INTERNAL_ERROR), |
| errmsg("update a tuple which does not exist," |
| " relname = %s, relid = %u", relation->rel->rd_rel->relname.data, |
| relation->relid))); |
| } |
| |
| Insist(relation->hashIndex == NULL && "cannot handle index in in-memory heap when update"); |
| |
| /* |
| * already in table |
| */ |
| Assert(relation->tuples[pos].flags == INMEM_HEAP_TUPLE_DISPATCHED |
| || relation->tuples[pos].flags == INMEM_HEAP_TUPLE_UPDATED); |
| relation->tuples[pos].flags = INMEM_HEAP_TUPLE_UPDATED; |
| |
| target = heaptuple_copy_to(tup, NULL, NULL ); |
| |
| /* |
| * do not modify original tuple header |
| */ |
| ItemPointerCopy(&target->t_self, &relation->tuples[pos].tuple->t_self); |
| |
| Assert(ItemPointerEquals(&target->t_self, otid)); |
| |
| memcpy(target->t_data, relation->tuples[pos].tuple->t_data, |
| sizeof(HeapTupleHeaderData)); |
| |
| CurrentMemoryContext = oldmem; |
| |
| pfree(relation->tuples[pos].tuple); |
| relation->tuples[pos].tuple = target; |
| } |
| |
| /* |
| * InMemHeap_CheckConstraints |
| * Check uniqueness constraints for in-memory tuples upon insert |
| */ |
| void |
| InMemHeap_CheckConstraints(InMemHeapRelation relation, HeapTuple newTuple) |
| { |
| Assert(NULL != relation); |
| Assert(NULL != newTuple); |
| |
| Oid relid = relation->relid; |
| |
| CheckConstraintsFn fn = NULL; |
| |
| switch (relid) |
| { |
| case NamespaceRelationId: |
| fn = CheckInMemConstraintsPgNamespace; |
| break; |
| case RelationRelationId: |
| fn = CheckInMemConstraintsPgClass; |
| break; |
| case TypeRelationId: |
| fn = CheckInMemConstraintsPgType; |
| break; |
| case AttributeRelationId: |
| fn = CheckInMemConstraintsPgAttribute; |
| break; |
| case ExtTableRelationId: |
| fn = CheckInMemConstraintsPgExttable; |
| break; |
| case GpPolicyRelationId: |
| fn = CheckInMemConstraintsGpDistributionPolicy; |
| break; |
| default: |
| /* no constraint checking for relations other than: |
| * pg_namespace, pg_class, pg_type, pg_attribute, |
| * pg_exttable, gp_distribution_policy */ |
| return; |
| } |
| |
| fn(relation, newTuple); |
| } |
| |
| /* |
| * CheckInMemConstraintsPgNamespace |
| * Check uniqueness constraints for pg_namespace in-memory tuples upon insert |
| */ |
| static void |
| CheckInMemConstraintsPgNamespace(InMemHeapRelation relation, HeapTuple newTuple) |
| { |
| Assert(NULL != newTuple); |
| Assert(NULL != relation); |
| Assert(NULL != relation->rel); |
| |
| TupleDesc tupleDesc = relation->rel->rd_att; |
| Oid nspdboidNew = DatumGetObjectId(tuple_getattr(newTuple, tupleDesc, Anum_pg_namespace_nspdboid)); |
| char *nspnameNew = DatumGetCString(tuple_getattr(newTuple, tupleDesc, Anum_pg_namespace_nspname)); |
| |
| for (int i = 0; i < relation->tupsize; i++) |
| { |
| HeapTuple tuple = relation->tuples[i].tuple; |
| Assert(NULL != tuple); |
| |
| insist_log(HeapTupleGetOid(tuple) != HeapTupleGetOid(newTuple), |
| "in-memory tuple with Oid = %d already exists in pg_namespace.", HeapTupleGetOid(tuple)); |
| |
| Oid nspdboid = DatumGetObjectId(tuple_getattr(tuple, tupleDesc, Anum_pg_namespace_nspdboid)); |
| char *nspname = DatumGetCString(tuple_getattr(tuple, tupleDesc, Anum_pg_namespace_nspname)); |
| size_t nspnameLen = strlen(nspname); |
| |
| insist_log(nspdboid != nspdboidNew || |
| nspnameLen != strlen(nspnameNew) || |
| 0 != strncmp(nspname, nspnameNew, nspnameLen), |
| "in-memory tuple with nspname = %s and nspdboid = %d already exists in pg_namespace.", nspname, nspdboid); |
| } |
| } |
| |
| /* |
| * CheckInMemConstraintsPgClass |
| * Check uniqueness constraints for pg_class in-memory tuples upon insert |
| */ |
| static void |
| CheckInMemConstraintsPgClass(InMemHeapRelation relation, HeapTuple newTuple) |
| { |
| Assert(NULL != newTuple); |
| Assert(NULL != relation); |
| Assert(NULL != relation->rel); |
| |
| TupleDesc tupleDesc = relation->rel->rd_att; |
| Oid relnamespaceNew = DatumGetObjectId(tuple_getattr(newTuple, tupleDesc, Anum_pg_class_relnamespace)); |
| char *relnameNew = DatumGetCString(tuple_getattr(newTuple, tupleDesc, Anum_pg_class_relname)); |
| |
| for (int i = 0; i < relation->tupsize; i++) |
| { |
| HeapTuple tuple = relation->tuples[i].tuple; |
| Assert(NULL != tuple); |
| |
| insist_log(HeapTupleGetOid(tuple) != HeapTupleGetOid(newTuple), |
| "in-memory tuple with Oid = %d already exists in pg_class.", HeapTupleGetOid(tuple)); |
| |
| Oid relnamespace = DatumGetObjectId(tuple_getattr(tuple, tupleDesc, Anum_pg_class_relnamespace)); |
| char *relname = DatumGetCString(tuple_getattr(tuple, tupleDesc, Anum_pg_class_relname)); |
| size_t relnameLen = strlen(relname); |
| |
| insist_log(relnamespace != relnamespaceNew || |
| relnameLen != strlen(relnameNew) || |
| 0 != strncmp(relname, relnameNew, relnameLen), |
| "in-memory tuple with relname = %s and relnamespace = %d already exists in pg_class.", relname, relnamespace); |
| } |
| } |
| |
| /* |
| * CheckInMemConstraintsPgType |
| * Check uniqueness constraints for pg_type in-memory tuples upon insert |
| */ |
| static void |
| CheckInMemConstraintsPgType(InMemHeapRelation relation, HeapTuple newTuple) |
| { |
| Assert(NULL != newTuple); |
| Assert(NULL != relation); |
| Assert(NULL != relation->rel); |
| |
| TupleDesc tupleDesc = relation->rel->rd_att; |
| Oid relnamespaceNew = DatumGetObjectId(tuple_getattr(newTuple, tupleDesc, Anum_pg_type_typnamespace)); |
| char *typnameNew = DatumGetCString(tuple_getattr(newTuple, tupleDesc, Anum_pg_type_typname)); |
| |
| for (int i = 0; i < relation->tupsize; i++) |
| { |
| HeapTuple tuple = relation->tuples[i].tuple; |
| Assert(NULL != tuple); |
| |
| insist_log(HeapTupleGetOid(tuple) != HeapTupleGetOid(newTuple), |
| "in-memory tuple with Oid = %d already exists in pg_type.", HeapTupleGetOid(tuple)); |
| |
| Oid relnamespace = DatumGetObjectId(tuple_getattr(tuple, tupleDesc, Anum_pg_type_typnamespace)); |
| char *typname = DatumGetCString(tuple_getattr(tuple, tupleDesc, Anum_pg_type_typname)); |
| size_t typnameLen = strlen(typname); |
| |
| insist_log(relnamespace != relnamespaceNew || |
| typnameLen != strlen(typnameNew) || |
| 0 != strncmp(typname, typnameNew, typnameLen), |
| "in-memory tuple with typname = %s and typnamespace = %d already exists in pg_type.", typname, relnamespace); |
| } |
| } |
| |
| /* |
| * CheckInMemConstraintsPgAttribute |
| * Check uniqueness constraints for pg_attribute in-memory tuples upon insert |
| */ |
| static void |
| CheckInMemConstraintsPgAttribute(InMemHeapRelation relation, HeapTuple newTuple) |
| { |
| Assert(NULL != newTuple); |
| Assert(NULL != relation); |
| Assert(NULL != relation->rel); |
| |
| TupleDesc tupleDesc = relation->rel->rd_att; |
| Oid attrelidNew = DatumGetObjectId(tuple_getattr(newTuple, tupleDesc, Anum_pg_attribute_attrelid)); |
| char *attnameNew = DatumGetCString(tuple_getattr(newTuple, tupleDesc, Anum_pg_attribute_attname)); |
| AttrNumber attnoNew = DatumGetInt16((tuple_getattr(newTuple, tupleDesc, Anum_pg_attribute_attnum))); |
| |
| for (int i = 0; i < relation->tupsize; i++) |
| { |
| HeapTuple tuple = relation->tuples[i].tuple; |
| Assert(NULL != tuple); |
| |
| Oid attrelid = DatumGetObjectId(tuple_getattr(tuple, tupleDesc, Anum_pg_attribute_attrelid)); |
| char *attname = DatumGetCString(tuple_getattr(tuple, tupleDesc, Anum_pg_attribute_attname)); |
| AttrNumber attno = DatumGetInt16((tuple_getattr(tuple, tupleDesc, Anum_pg_attribute_attnum))); |
| size_t attnameLen = strlen(attname); |
| |
| if (attrelid != attrelidNew) |
| { |
| /* attributes belong to different relations */ |
| continue; |
| } |
| |
| insist_log(attno != attnoNew, |
| "in-memory tuple with attrelid = %d and attno = %d already exists in pg_attribute.", attrelid, attno); |
| |
| insist_log((attnameLen != strlen(attnameNew)) || |
| (0 != strncmp(attname, attnameNew, attnameLen)), |
| "in-memory tuple with attrelid = %d and attname = %s already exists in pg_attribute.", attrelid, attname); |
| } |
| } |
| |
| /* |
| * CheckInMemConstraintsGpDistributionPolicy |
| * Check uniqueness constraints for gp_distribution_policy in-memory tuples upon insert |
| */ |
| static void |
| CheckInMemConstraintsGpDistributionPolicy(InMemHeapRelation relation, HeapTuple newTuple) |
| { |
| Assert(NULL != newTuple); |
| Assert(NULL != relation); |
| Assert(NULL != relation->rel); |
| |
| TupleDesc tupleDesc = relation->rel->rd_att; |
| Oid reloidNew = DatumGetObjectId(tuple_getattr(newTuple, tupleDesc, Anum_gp_policy_localoid)); |
| |
| for (int i = 0; i < relation->tupsize; i++) |
| { |
| HeapTuple tuple = relation->tuples[i].tuple; |
| Assert(NULL != tuple); |
| |
| Oid reloid = DatumGetObjectId(tuple_getattr(tuple, tupleDesc, Anum_gp_policy_localoid)); |
| |
| insist_log(reloidNew != reloid, |
| "in-memory tuple with localoid = %d already exists in gp_distribution_policy.", reloid); |
| } |
| } |
| |
| /* |
| * CheckInMemConstraintsPgExttable |
| * Check uniqueness constraints for pg_exttable in-memory tuples upon insert |
| */ |
| static void |
| CheckInMemConstraintsPgExttable(InMemHeapRelation relation, HeapTuple newTuple) |
| { |
| Assert(NULL != newTuple); |
| Assert(NULL != relation); |
| Assert(NULL != relation->rel); |
| |
| TupleDesc tupleDesc = relation->rel->rd_att; |
| Oid reloidNew = DatumGetObjectId(tuple_getattr(newTuple, tupleDesc, Anum_pg_exttable_reloid)); |
| |
| for (int i = 0; i < relation->tupsize; i++) |
| { |
| HeapTuple tuple = relation->tuples[i].tuple; |
| Assert(NULL != tuple); |
| |
| Oid reloid = DatumGetObjectId(tuple_getattr(tuple, tupleDesc, Anum_pg_exttable_reloid)); |
| |
| insist_log(reloidNew != reloid, |
| "in-memory tuple with reloid = %d already exists in pg_exttable.", reloid); |
| } |
| } |
| |
| /* ---------------- |
| * tuple_getattr |
| * |
| * Extracts an attribute from a HeapTuple given its attnum and |
| * returns it as a Datum. |
| * |
| * <tuple> is the pointer to the heap tuple. <attnum> is the attribute |
| * number of the column (field) caller wants. <tupleDesc> is a |
| * pointer to the structure describing the row and all its fields. |
| * |
| * ---------------- |
| */ |
| Datum |
| tuple_getattr(HeapTuple tuple, TupleDesc tupleDesc, int attnum) |
| { |
| Assert(NULL != tupleDesc); |
| Assert(NULL != tuple); |
| bool isnull; |
| Datum attr = heap_getattr(tuple, attnum, tupleDesc, &isnull); |
| insist_log(!isnull, "attribute cannot be null"); |
| return attr; |
| } |