/* * CreatePortal * Returns a new portal given a name. * * allowDup: if true, automatically drop any pre-existing portal of the * same name (if false, an error is raised). * * dupSilent: if true, don't even emit a WARNING. */ Portal CreatePortal(const char *name, bool allowDup, bool dupSilent) { Portal portal; AssertArg(PointerIsValid(name)); portal = GetPortalByName(name); if (PortalIsValid(portal)) { if (!allowDup) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_CURSOR), errmsg("cursor \"%s\" already exists", name))); if (!dupSilent) ereport(WARNING, (errcode(ERRCODE_DUPLICATE_CURSOR), errmsg("closing existing cursor \"%s\"", name))); PortalDrop(portal, false); } /* make new portal structure */ portal = (Portal) MemoryContextAllocZero(PortalMemory, sizeof *portal); /* initialize portal heap context; typically it won't store much */ portal->heap = AllocSetContextCreate(PortalMemory, "PortalHeapMemory", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE); /* create a resource owner for the portal */ portal->resowner = ResourceOwnerCreate(CurTransactionResourceOwner, "Portal"); /* initialize portal fields that don't start off zero */ portal->cleanup = PortalCleanup; portal->createSubid = GetCurrentSubTransactionId(); portal->strategy = PORTAL_MULTI_QUERY; portal->cursorOptions = CURSOR_OPT_NO_SCROLL; portal->atStart = true; portal->atEnd = true; /* disallow fetches until query is set */ /* put portal in table (sets portal->name) */ PortalHashTableInsert(portal, name); return portal; }
static Jsonb * jnumber_op(PGFunction f, Jsonb *l, Jsonb *r) { FunctionCallInfoData fcinfo; JsonbValue *jv; Datum n; AssertArg(r != NULL); if (!((l == NULL || JB_ROOT_IS_SCALAR(l)) && JB_ROOT_IS_SCALAR(r))) ereport_op(f, l, r); InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL); if (l != NULL) { jv = getIthJsonbValueFromContainer(&l->root, 0); if (jv->type != jbvNumeric) ereport_op(f, l, r); fcinfo.arg[fcinfo.nargs] = NumericGetDatum(jv->val.numeric); fcinfo.argnull[fcinfo.nargs] = false; fcinfo.nargs++; } jv = getIthJsonbValueFromContainer(&r->root, 0); if (jv->type != jbvNumeric) ereport_op(f, l, r); fcinfo.arg[fcinfo.nargs] = NumericGetDatum(jv->val.numeric); fcinfo.argnull[fcinfo.nargs] = false; fcinfo.nargs++; n = (*f) (&fcinfo); if (fcinfo.isnull) elog(ERROR, "function %p returned NULL", (void *) f); if (f == numeric_power || f == numeric_div) { int s; s = DatumGetInt32(DirectFunctionCall1(numeric_scale, fcinfo.arg[0])) + DatumGetInt32(DirectFunctionCall1(numeric_scale, fcinfo.arg[1])); if (s == 0) n = DirectFunctionCall2(numeric_trunc, n, 0); } return numeric_to_jnumber(DatumGetNumeric(n)); }
/* * MemoryContextResetOnly * Release all space allocated within a context. * Nothing is done to the context's descendant contexts. */ void MemoryContextResetOnly(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); /* Nothing to do if no pallocs since startup or last reset */ if (!context->isReset) { MemoryContextCallResetCallbacks(context); (*context->methods->reset) (context); context->isReset = true; VALGRIND_DESTROY_MEMPOOL(context); VALGRIND_CREATE_MEMPOOL(context, 0, false); } }
/* * MemoryContextReset * Release all space allocated within a context and its descendants, * but don't delete the contexts themselves. * * The type-specific reset routine handles the context itself, but we * have to do the recursion for the children. */ void MemoryContextReset(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); /* save a function call in common case where there are no children */ if (context->firstchild != NULL) MemoryContextResetChildren(context); /* Nothing to do if no pallocs since startup or last reset */ if (!context->isReset) { (*context->methods->reset) (context); context->isReset = true; } }
/* * MemoryContextSetPeakSpace * Resets the peak space statistic to the space currently occupied or * the specified value, whichever is greater. Returns the former peak * space value. * * Can be used to observe local maximum usage over an interval and then to * restore the overall maximum. */ Size MemoryContextSetPeakSpace(MemoryContext context, Size nbytes) { Size held; Size oldpeak; AssertArg(MemoryContextIsValid(context)); Assert(context->allBytesAlloc >= context->allBytesFreed); Assert(context->allBytesAlloc - context->allBytesFreed < SIZE_MAX); oldpeak = context->maxBytesHeld; held = (Size)(context->allBytesAlloc - context->allBytesFreed); context->maxBytesHeld = Max(held, nbytes); return oldpeak; } /* MemoryContextSetPeakSpace */
/* * PortalDrop * Destroy the portal. * * isError: if true, we are destroying portals at the end of a failed * transaction. (This causes PortalCleanup to skip unneeded steps.) */ void PortalDrop(Portal portal, bool isError) { AssertArg(PortalIsValid(portal)); /* Not sure if this case can validly happen or not... */ if (portal->portalActive) elog(ERROR, "cannot drop active portal"); /* * Remove portal from hash table. Because we do this first, we will * not come back to try to remove the portal again if there's any * error in the subsequent steps. Better to leak a little memory than * to get into an infinite error-recovery loop. */ PortalHashTableDelete(portal); /* let portalcmds.c clean up the state it knows about */ if (PointerIsValid(portal->cleanup)) (*portal->cleanup) (portal, isError); /* * Delete tuplestore if present. We should do this even under error * conditions; since the tuplestore would have been using cross- * transaction storage, its temp files need to be explicitly deleted. */ if (portal->holdStore) { MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(portal->holdContext); tuplestore_end(portal->holdStore); MemoryContextSwitchTo(oldcontext); portal->holdStore = NULL; } /* delete tuplestore storage, if any */ if (portal->holdContext) MemoryContextDelete(portal->holdContext); /* release subsidiary storage */ MemoryContextDelete(PortalGetHeapMemory(portal)); /* release portal struct (it's in PortalMemory) */ pfree(portal); }
/* * getOidListDiff * Helper for updateAclDependencies. * * Takes two Oid arrays and returns elements from the first not found in the * second. We assume both arrays are sorted and de-duped, and that the * second array does not contain any values not found in the first. * * NOTE: Both input arrays are pfreed. */ static int getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff) { Oid *result; int i, j, k = 0; AssertArg(nlist1 >= nlist2 && nlist2 >= 0); result = palloc(sizeof(Oid) * (nlist1 - nlist2)); *diff = result; for (i = 0, j = 0; i < nlist1 && j < nlist2;) { if (list1[i] == list2[j]) { i++; j++; } else if (list1[i] < list2[j]) { result[k++] = list1[i]; i++; } else { /* can't happen */ elog(WARNING, "invalid element %u in shorter list", list2[j]); j++; } } for (; i < nlist1; i++) result[k++] = list1[i]; /* We should have copied the exact number of elements */ AssertState(k == (nlist1 - nlist2)); if (list1) pfree(list1); if (list2) pfree(list2); return k; }
/* * MemoryContextAlloc * Allocate space within the specified context. * * This could be turned into a macro, but we'd have to import * nodes/memnodes.h into postgres.h which seems a bad idea. */ void * MemoryContextAlloc(MemoryContext context, Size size) { void *ret; AssertArg(MemoryContextIsValid(context)); AssertNotInCriticalSection(context); if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); context->isReset = false; ret = (*context->methods->alloc) (context, size); VALGRIND_MEMPOOL_ALLOC(context, ret, size); return ret; }
/* * MemoryContextAllocHuge * Allocate (possibly-expansive) space within the specified context. * * See considerations in comment at MaxAllocHugeSize. */ void * MemoryContextAllocHuge(MemoryContext context, Size size) { void *ret; AssertArg(MemoryContextIsValid(context)); if (!AllocHugeSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %lu", (unsigned long) size); context->isReset = false; ret = (*context->methods->alloc) (context, size); VALGRIND_MEMPOOL_ALLOC(context, ret, size); return ret; }
/* * MemoryContextDelete * Delete a context and its descendants, and release all space * allocated therein. * * The type-specific delete routine removes all subsidiary storage * for the context, but we have to delete the context node itself, * as well as recurse to get the children. We must also delink the * node from its parent, if it has one. */ void MemoryContextDeleteImpl(MemoryContext context, const char* sfile, const char *func, int sline) { AssertArg(MemoryContextIsValid(context)); /* We had better not be deleting TopMemoryContext ... */ Assert(context != TopMemoryContext); /* And not CurrentMemoryContext, either */ Assert(context != CurrentMemoryContext); #ifdef CDB_PALLOC_CALLER_ID context->callerFile = sfile; context->callerLine = sline; #endif MemoryContextDeleteChildren(context); /* * We delink the context from its parent before deleting it, so that if * there's an error we won't have deleted/busted contexts still attached * to the context tree. Better a leak than a crash. */ if (context->parent) { MemoryContext parent = context->parent; if (context == parent->firstchild) parent->firstchild = context->nextchild; else { MemoryContext child; for (child = parent->firstchild; child; child = child->nextchild) { if (context == child->nextchild) { child->nextchild = context->nextchild; break; } } } } (*context->methods.delete_context)(context); pfree(context); }
/* * repalloc * Adjust the size of a previously allocated chunk. */ void * repalloc(void *pointer, Size size) { MemoryContext context; void *ret; if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); /* * Try to detect bogus pointers handed to us, poorly though we can. * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an * allocated chunk. */ Assert(pointer != NULL); Assert(pointer == (void *) MAXALIGN(pointer)); /* * OK, it's probably safe to look at the chunk header. */ context = ((StandardChunkHeader *) ((char *) pointer - STANDARDCHUNKHEADERSIZE))->context; AssertArg(MemoryContextIsValid(context)); AssertNotInCriticalSection(context); /* isReset must be false already */ Assert(!context->isReset); ret = (*context->methods->realloc) (context, pointer, size); if (ret == NULL) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu.", size))); } VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size); return ret; }
void * palloc(Size size) { /* duplicates MemoryContextAlloc to avoid increased overhead */ void *ret; AssertArg(MemoryContextIsValid(CurrentMemoryContext)); if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %lu", (unsigned long) size); CurrentMemoryContext->isReset = false; ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size); VALGRIND_MEMPOOL_ALLOC(CurrentMemoryContext, ret, size); return ret; }
static void iterateMemoryContext(MemoryContextIteratorState *state) { MemoryContext context = state->context; AssertArg(MemoryContextIsValid(context)); if (context->firstchild) { /* perfor first-depth search */ state->context = context->firstchild; state->level++; } else if (context->nextchild) { /* goto next child if current context doesn't have a child */ state->context = context->nextchild; } else if (context->parent) { /* * walk up on tree to first parent which has a next child, * that parent context was already visited */ while(context) { context = context->parent; state->level--; if (context == NULL) { /* we visited the whole context's tree */ state->context = NULL; break; } else if (context->nextchild) { state->context = context->nextchild; break; } } } }
/* * MemoryContextAllocZeroAligned * MemoryContextAllocZero where length is suitable for MemSetLoop * * This might seem overly specialized, but it's not because newNode() * is so often called with compile-time-constant sizes. */ void * MemoryContextAllocZeroAligned(MemoryContext context, Size size) { void *ret; AssertArg(MemoryContextIsValid(context)); if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %lu", (unsigned long) size); context->isReset = false; ret = (*context->methods->alloc) (context, size); MemSetLoop(ret, 0, size); return ret; }
static int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version) { int ssl_options = 0; AssertArg(version != 0); #ifdef TLS1_1_VERSION if (version < TLS1_1_VERSION) ssl_options |= SSL_OP_NO_TLSv1_1; #endif #ifdef TLS1_2_VERSION if (version < TLS1_2_VERSION) ssl_options |= SSL_OP_NO_TLSv1_2; #endif SSL_CTX_set_options(ctx, ssl_options); return 1; /* success */ }
/* * MemoryContextName * Format the name of the memory context into the caller's buffer. * * Returns ptr to the name string within the supplied buffer. (The string * is built at the tail of the buffer from right to left.) */ char * MemoryContextName(MemoryContext context, MemoryContext relativeTo, char *buf, int bufsize) { MemoryContext ctx; char *cbp = buf + bufsize - 1; AssertArg(MemoryContextIsValid(context)); if (bufsize <= 0) return buf; for (ctx = context; ctx && ctx != relativeTo; ctx = ctx->parent) { const char *name = ctx->name ? ctx->name : ""; int len = strlen(name); if (cbp - buf < len + 1) { len = Min(3, cbp - buf); cbp -= len; memcpy(cbp, "...", len); break; } if (ctx != context) *--cbp = '/'; cbp -= len; memcpy(cbp, name, len); } if (buf < cbp) { if (!ctx) *--cbp = '/'; else if (ctx == context) *--cbp = '.'; } buf[bufsize-1] = '\0'; return cbp; } /* MemoryContextName */
/* * CreateTupleDesc * This function allocates a new TupleDesc pointing to a given * Form_pg_attribute array. * * Note: if the TupleDesc is ever freed, the Form_pg_attribute array * will not be freed thereby. * * Tuple type ID information is initially set for an anonymous record type; * caller can overwrite this if needed. */ TupleDesc CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs) { TupleDesc desc; /* * sanity checks */ AssertArg(natts >= 0); desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); desc->attrs = attrs; desc->natts = natts; desc->constr = NULL; desc->tdtypeid = RECORDOID; desc->tdtypmod = -1; desc->tdhasoid = hasoid; desc->tdrefcount = -1; /* assume not reference-counted */ return desc; }
void * palloc0(Size size) { /* duplicates MemoryContextAllocZero to avoid increased overhead */ void *ret; AssertArg(MemoryContextIsValid(CurrentMemoryContext)); AssertNotInCriticalSection(CurrentMemoryContext); if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); CurrentMemoryContext->isReset = false; ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size); VALGRIND_MEMPOOL_ALLOC(CurrentMemoryContext, ret, size); MemSetAligned(ret, 0, size); return ret; }
/* * MemoryContextDelete * Delete a context and its descendants, and release all space * allocated therein. * * The type-specific delete routine removes all subsidiary storage * for the context, but we have to delete the context node itself, * as well as recurse to get the children. We must also delink the * node from its parent, if it has one. */ void MemoryContextDelete(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); /* We had better not be deleting TopMemoryContext ... */ Assert(context != TopMemoryContext); /* And not CurrentMemoryContext, either */ Assert(context != CurrentMemoryContext); MemoryContextDeleteChildren(context); /* * We delink the context from its parent before deleting it, so that if * there's an error we won't have deleted/busted contexts still attached * to the context tree. Better a leak than a crash. */ MemoryContextSetParent(context, NULL); (*context->methods->delete_context) (context); pfree(context); }
/* * PortalDefineQuery * A simple subroutine to establish a portal's query. * * Notes: commandTag shall be NULL if and only if the original query string * (before rewriting) was an empty string. Also, the passed commandTag must * be a pointer to a constant string, since it is not copied. The caller is * responsible for ensuring that the passed sourceText (if any), parse and * plan trees have adequate lifetime. Also, queryContext must accurately * describe the location of the parse and plan trees. */ void PortalDefineQuery(Portal portal, const char *sourceText, const char *commandTag, List *parseTrees, List *planTrees, MemoryContext queryContext) { AssertArg(PortalIsValid(portal)); AssertState(portal->queryContext == NULL); /* else defined already */ Assert(list_length(parseTrees) == list_length(planTrees)); Assert(commandTag != NULL || parseTrees == NIL); portal->sourceText = sourceText; portal->commandTag = commandTag; portal->parseTrees = parseTrees; portal->planTrees = planTrees; portal->queryContext = queryContext; }
/* * MemoryContextNoteFree * Update lifetime cumulative statistics upon free to host memory manager. * * Called by the context-type-specific memory manager upon relinquishing a * block of size 'nbytes' back to its lower-level source (e.g. free()). */ void MemoryContextNoteFree(MemoryContext context, Size nbytes) { Size held; AssertArg(MemoryContextIsValid(context)); while (context) { Assert(context->allBytesAlloc >= context->allBytesFreed + nbytes); Assert(context->allBytesFreed + nbytes >= context->allBytesFreed); context->allBytesFreed += nbytes; held = (Size)(context->allBytesAlloc - context->allBytesFreed); if (context->localMinHeld > held) context->localMinHeld = held; context = context->parent; } } /* MemoryContextNoteFree */
/* * pfree * Release an allocated chunk. */ void MemoryContextFreeImpl(void *pointer, const char *sfile, const char *sfunc, int sline) { /* * Try to detect bogus pointers handed to us, poorly though we can. * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an * allocated chunk. */ Assert(pointer != NULL); Assert(pointer == (void *) MAXALIGN(pointer)); /* * OK, it's probably safe to look at the chunk header. */ StandardChunkHeader* header = (StandardChunkHeader *) ((char *) pointer - STANDARDCHUNKHEADERSIZE); AssertArg(MemoryContextIsValid(header->sharedHeader->context)); #ifdef PGTRACE_ENABLED PG_TRACE5(memctxt__free, 0, 0, #ifdef MEMORY_CONTEXT_CHECKING header->requested_size, header->size, #else 0, header->size, #endif (long) header->sharedHeader->context->name); #endif #ifdef CDB_PALLOC_CALLER_ID header->sharedHeader->context->callerFile = sfile; header->sharedHeader->context->callerLine = sline; #endif if (header->sharedHeader->context->methods.free_p) (*header->sharedHeader->context->methods.free_p) (header->sharedHeader->context, pointer); else Assert(header); /* this assert never fails. Just here so we can set breakpoint in debugger. */ }
/* ---------------- * heap_addheader * * This routine forms a HeapTuple by copying the given structure (tuple * data) and adding a generic header. Note that the tuple data is * presumed to contain no null fields and no varlena fields. * * This routine is really only useful for certain system tables that are * known to be fixed-width and null-free. Currently it is only used for * pg_attribute tuples. * ---------------- */ HeapTuple heap_addheader(int natts, /* max domain index */ bool withoid, /* reserve space for oid */ Size structlen, /* its length */ void *structure) /* pointer to the struct */ { HeapTuple tuple; HeapTupleHeader td; Size len; int hoff; AssertArg(natts > 0); /* header needs no null bitmap */ hoff = offsetof(HeapTupleHeaderData, t_bits); if (withoid) hoff += sizeof(Oid); hoff = MAXALIGN(hoff); len = hoff + structlen; tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len); tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE); tuple->t_len = len; ItemPointerSetInvalid(&(tuple->t_self)); /* we don't bother to fill the Datum fields */ HeapTupleHeaderSetNatts(td, natts); td->t_hoff = hoff; if (withoid) /* else leave infomask = 0 */ td->t_infomask = HEAP_HASOID; memcpy((char *) td + hoff, structure, structlen); return tuple; }
/* * GetMemoryChunkContext * Given a currently-allocated chunk, determine the context * it belongs to. */ MemoryContext GetMemoryChunkContext(void *pointer) { StandardChunkHeader *header; /* * Try to detect bogus pointers handed to us, poorly though we can. * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an * allocated chunk. */ Assert(pointer != NULL); Assert(pointer == (void *) MAXALIGN(pointer)); /* * OK, it's probably safe to look at the chunk header. */ header = (StandardChunkHeader *) ((char *) pointer - STANDARDCHUNKHEADERSIZE); AssertArg(MemoryContextIsValid(header->context)); return header->context; }
/* * MemoryContextNoteAlloc * Update lifetime cumulative statistics upon allocation from host mem mgr. * * Called by the context-type-specific memory manager upon successfully * obtaining a block of size 'nbytes' from its lower-level source (e.g. malloc). */ void MemoryContextNoteAlloc(MemoryContext context, Size nbytes) { Size held; AssertArg(MemoryContextIsValid(context)); for (;;) { Assert(context->allBytesAlloc >= context->allBytesFreed); Assert(context->allBytesAlloc - context->allBytesFreed < SIZE_MAX - nbytes); context->allBytesAlloc += nbytes; held = (Size)(context->allBytesAlloc - context->allBytesFreed); if (context->maxBytesHeld < held) context->maxBytesHeld = held; if (!context->parent) break; context = context->parent; } } /* MemoryContextNoteAlloc */
/* * pfree * Release an allocated chunk. */ void pfree(void *pointer) { MemoryContext context; /* * Try to detect bogus pointers handed to us, poorly though we can. * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an * allocated chunk. */ Assert(pointer != NULL); Assert(pointer == (void *) MAXALIGN(pointer)); /* * OK, it's probably safe to look at the chunk header. */ context = ((StandardChunkHeader *) ((char *) pointer - STANDARDCHUNKHEADERSIZE))->context; AssertArg(MemoryContextIsValid(context)); (*context->methods->free_p) (context, pointer); VALGRIND_MEMPOOL_FREE(context, pointer); }
void * palloc_extended(Size size, int flags) { /* duplicates MemoryContextAllocExtended to avoid increased overhead */ void *ret; AssertArg(MemoryContextIsValid(CurrentMemoryContext)); AssertNotInCriticalSection(CurrentMemoryContext); if (((flags & MCXT_ALLOC_HUGE) != 0 && !AllocHugeSizeIsValid(size)) || ((flags & MCXT_ALLOC_HUGE) == 0 && !AllocSizeIsValid(size))) elog(ERROR, "invalid memory alloc request size %zu", size); CurrentMemoryContext->isReset = false; ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size); if (ret == NULL) { if ((flags & MCXT_ALLOC_NO_OOM) == 0) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu.", size))); } return NULL; } VALGRIND_MEMPOOL_ALLOC(CurrentMemoryContext, ret, size); if ((flags & MCXT_ALLOC_ZERO) != 0) MemSetAligned(ret, 0, size); return ret; }
/* * CollationCreate * * Add a new tuple to pg_collation. * * if_not_exists: if true, don't fail on duplicate name, just print a notice * and return InvalidOid. * quiet: if true, don't fail on duplicate name, just silently return * InvalidOid (overrides if_not_exists). */ Oid CollationCreate(const char *collname, Oid collnamespace, Oid collowner, char collprovider, int32 collencoding, const char *collcollate, const char *collctype, const char *collversion, bool if_not_exists, bool quiet) { Relation rel; TupleDesc tupDesc; HeapTuple tup; Datum values[Natts_pg_collation]; bool nulls[Natts_pg_collation]; NameData name_name, name_collate, name_ctype; Oid oid; ObjectAddress myself, referenced; AssertArg(collname); AssertArg(collnamespace); AssertArg(collowner); AssertArg(collcollate); AssertArg(collctype); /* * Make sure there is no existing collation of same name & encoding. * * This would be caught by the unique index anyway; we're just giving a * friendlier error message. The unique index provides a backstop against * race conditions. */ if (SearchSysCacheExists3(COLLNAMEENCNSP, PointerGetDatum(collname), Int32GetDatum(collencoding), ObjectIdGetDatum(collnamespace))) { if (quiet) return InvalidOid; else if (if_not_exists) { ereport(NOTICE, (errcode(ERRCODE_DUPLICATE_OBJECT), collencoding == -1 ? errmsg("collation \"%s\" already exists, skipping", collname) : errmsg("collation \"%s\" for encoding \"%s\" already exists, skipping", collname, pg_encoding_to_char(collencoding)))); return InvalidOid; } else ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), collencoding == -1 ? errmsg("collation \"%s\" already exists", collname) : errmsg("collation \"%s\" for encoding \"%s\" already exists", collname, pg_encoding_to_char(collencoding)))); } /* open pg_collation; see below about the lock level */ rel = heap_open(CollationRelationId, ShareRowExclusiveLock); /* * Also forbid a specific-encoding collation shadowing an any-encoding * collation, or an any-encoding collation being shadowed (see * get_collation_name()). This test is not backed up by the unique index, * so we take a ShareRowExclusiveLock earlier, to protect against * concurrent changes fooling this check. */ if ((collencoding == -1 && SearchSysCacheExists3(COLLNAMEENCNSP, PointerGetDatum(collname), Int32GetDatum(GetDatabaseEncoding()), ObjectIdGetDatum(collnamespace))) || (collencoding != -1 && SearchSysCacheExists3(COLLNAMEENCNSP, PointerGetDatum(collname), Int32GetDatum(-1), ObjectIdGetDatum(collnamespace)))) { if (quiet) { heap_close(rel, NoLock); return InvalidOid; } else if (if_not_exists) { heap_close(rel, NoLock); ereport(NOTICE, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("collation \"%s\" already exists, skipping", collname))); return InvalidOid; } else ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("collation \"%s\" already exists", collname))); } tupDesc = RelationGetDescr(rel); /* form a tuple */ memset(nulls, 0, sizeof(nulls)); namestrcpy(&name_name, collname); values[Anum_pg_collation_collname - 1] = NameGetDatum(&name_name); values[Anum_pg_collation_collnamespace - 1] = ObjectIdGetDatum(collnamespace); values[Anum_pg_collation_collowner - 1] = ObjectIdGetDatum(collowner); values[Anum_pg_collation_collprovider - 1] = CharGetDatum(collprovider); values[Anum_pg_collation_collencoding - 1] = Int32GetDatum(collencoding); namestrcpy(&name_collate, collcollate); values[Anum_pg_collation_collcollate - 1] = NameGetDatum(&name_collate); namestrcpy(&name_ctype, collctype); values[Anum_pg_collation_collctype - 1] = NameGetDatum(&name_ctype); if (collversion) values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(collversion); else nulls[Anum_pg_collation_collversion - 1] = true; tup = heap_form_tuple(tupDesc, values, nulls); /* insert a new tuple */ oid = CatalogTupleInsert(rel, tup); Assert(OidIsValid(oid)); /* set up dependencies for the new collation */ myself.classId = CollationRelationId; myself.objectId = oid; myself.objectSubId = 0; /* create dependency on namespace */ referenced.classId = NamespaceRelationId; referenced.objectId = collnamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* create dependency on owner */ recordDependencyOnOwner(CollationRelationId, HeapTupleGetOid(tup), collowner); /* dependency on extension */ recordDependencyOnCurrentExtension(&myself, false); /* Post creation hook for new collation */ InvokeObjectPostCreateHook(CollationRelationId, oid, 0); heap_freetuple(tup); heap_close(rel, NoLock); return oid; }
/* * Deserialize a HeapTuple's data from a byte-array. * * This code is based on the binary input handling functions in copy.c. */ HeapTuple DeserializeTuple(SerTupInfo * pSerInfo, StringInfo serialTup) { MemoryContext oldCtxt; TupleDesc tupdesc; HeapTuple htup; int natts; SerAttrInfo *attrInfo; uint32 attr_size; int i; StringInfoData attr_data; bool fHandled; AssertArg(pSerInfo != NULL); AssertArg(serialTup != NULL); tupdesc = pSerInfo->tupdesc; natts = tupdesc->natts; /* * Flip to our tuple-serialization memory-context, to speed up memory * reclamation operations. */ AssertState(s_tupSerMemCtxt != NULL); oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt); /* Receive nulls character-array. */ pq_copymsgbytes(serialTup, pSerInfo->nulls, natts); skipPadding(serialTup); /* Deserialize the non-NULL attributes of this tuple */ initStringInfo(&attr_data); for (i = 0; i < natts; ++i) { attrInfo = pSerInfo->myinfo + i; if (pSerInfo->nulls[i]) /* NULL field. */ { pSerInfo->values[i] = (Datum) 0; continue; } /* * Assume that the data's output will be handled by the special IO * code, and if not then we can handle it the slow way. */ fHandled = true; switch (attrInfo->atttypid) { case INT4OID: pSerInfo->values[i] = Int32GetDatum(stringInfoGetInt32(serialTup)); break; case CHAROID: pSerInfo->values[i] = CharGetDatum(pq_getmsgbyte(serialTup)); skipPadding(serialTup); break; case BPCHAROID: case VARCHAROID: case INT2VECTOROID: /* postgres serialization logic broken, use our own */ case OIDVECTOROID: /* postgres serialization logic broken, use our own */ case ANYARRAYOID: { text *pText; int textSize; textSize = stringInfoGetInt32(serialTup); #ifdef TUPSER_SCRATCH_SPACE if (textSize + VARHDRSZ <= attrInfo->varlen_scratch_size) pText = (text *) attrInfo->pv_varlen_scratch; else pText = (text *) palloc(textSize + VARHDRSZ); #else pText = (text *) palloc(textSize + VARHDRSZ); #endif SET_VARSIZE(pText, textSize + VARHDRSZ); pq_copymsgbytes(serialTup, VARDATA(pText), textSize); skipPadding(serialTup); pSerInfo->values[i] = PointerGetDatum(pText); break; } case DATEOID: { /* * TODO: I would LIKE to do something more efficient, but * DateADT is not strictly limited to 4 bytes by its * definition. */ DateADT date; pq_copymsgbytes(serialTup, (char *) &date, sizeof(DateADT)); skipPadding(serialTup); pSerInfo->values[i] = DateADTGetDatum(date); break; } case NUMERICOID: { /* * Treat the numeric as a varlena variable, and just push * the whole shebang to the output-buffer. We don't care * about the guts of the numeric. */ Numeric num; int numSize; numSize = stringInfoGetInt32(serialTup); #ifdef TUPSER_SCRATCH_SPACE if (numSize + VARHDRSZ <= attrInfo->varlen_scratch_size) num = (Numeric) attrInfo->pv_varlen_scratch; else num = (Numeric) palloc(numSize + VARHDRSZ); #else num = (Numeric) palloc(numSize + VARHDRSZ); #endif SET_VARSIZE(num, numSize + VARHDRSZ); pq_copymsgbytes(serialTup, VARDATA(num), numSize); skipPadding(serialTup); pSerInfo->values[i] = NumericGetDatum(num); break; } case ACLITEMOID: { int aclSize, k, cnt; char *inputstring, *starsfree; aclSize = stringInfoGetInt32(serialTup); inputstring = (char*) palloc(aclSize + 1); starsfree = (char*) palloc(aclSize + 1); cnt = 0; pq_copymsgbytes(serialTup, inputstring, aclSize); skipPadding(serialTup); inputstring[aclSize] = '\0'; for(k=0; k<aclSize; k++) { if( inputstring[k] != '*') { starsfree[cnt] = inputstring[k]; cnt++; } } starsfree[cnt] = '\0'; pSerInfo->values[i] = DirectFunctionCall1(aclitemin, CStringGetDatum(starsfree)); pfree(inputstring); break; } case 210: { int strsize; char *smgrstr; strsize = stringInfoGetInt32(serialTup); smgrstr = (char*) palloc(strsize + 1); pq_copymsgbytes(serialTup, smgrstr, strsize); skipPadding(serialTup); smgrstr[strsize] = '\0'; pSerInfo->values[i] = DirectFunctionCall1(smgrin, CStringGetDatum(smgrstr)); break; } default: fHandled = false; } if (fHandled) continue; attr_size = stringInfoGetInt32(serialTup); /* reset attr_data to empty, and load raw data into it */ attr_data.len = 0; attr_data.data[0] = '\0'; attr_data.cursor = 0; appendBinaryStringInfo(&attr_data, pq_getmsgbytes(serialTup, attr_size), attr_size); skipPadding(serialTup); /* Call the attribute type's binary input converter. */ if (attrInfo->recv_finfo.fn_nargs == 1) pSerInfo->values[i] = FunctionCall1(&attrInfo->recv_finfo, PointerGetDatum(&attr_data)); else if (attrInfo->recv_finfo.fn_nargs == 2) pSerInfo->values[i] = FunctionCall2(&attrInfo->recv_finfo, PointerGetDatum(&attr_data), ObjectIdGetDatum(attrInfo->recv_typio_param)); else if (attrInfo->recv_finfo.fn_nargs == 3) pSerInfo->values[i] = FunctionCall3(&attrInfo->recv_finfo, PointerGetDatum(&attr_data), ObjectIdGetDatum(attrInfo->recv_typio_param), Int32GetDatum(tupdesc->attrs[i]->atttypmod) ); else { ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("Conversion function takes %d args",attrInfo->recv_finfo.fn_nargs))); } /* Trouble if it didn't eat the whole buffer */ if (attr_data.cursor != attr_data.len) { ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("incorrect binary data format"))); } } /* * Construct the tuple from the Datums and nulls values. NOTE: Switch * out of our temporary context before we form the tuple! */ MemoryContextSwitchTo(oldCtxt); htup = heap_form_tuple(tupdesc, pSerInfo->values, pSerInfo->nulls); MemoryContextReset(s_tupSerMemCtxt); /* All done. Return the result. */ return htup; }
/* * Serialize a tuple directly into a buffer. * * We're called with at least enough space for a tuple-chunk-header. */ int SerializeTupleDirect(HeapTuple tuple, SerTupInfo * pSerInfo, struct directTransportBuffer *b) { int natts; int dataSize = TUPLE_CHUNK_HEADER_SIZE; TupleDesc tupdesc; AssertArg(tuple != NULL); AssertArg(pSerInfo != NULL); AssertArg(b != NULL); tupdesc = pSerInfo->tupdesc; natts = tupdesc->natts; do { if (natts == 0) { /* TC_EMTPY is just one chunk */ SetChunkType(b->pri, TC_EMPTY); SetChunkDataSize(b->pri, 0); break; } /* easy case */ if (is_heaptuple_memtuple(tuple)) { int tupleSize; int paddedSize; tupleSize = memtuple_get_size((MemTuple)tuple, NULL); paddedSize = TYPEALIGN(TUPLE_CHUNK_ALIGN, tupleSize); if (paddedSize + TUPLE_CHUNK_HEADER_SIZE > b->prilen) return 0; /* will fit. */ memcpy(b->pri + TUPLE_CHUNK_HEADER_SIZE, tuple, tupleSize); memset(b->pri + TUPLE_CHUNK_HEADER_SIZE + tupleSize, 0, paddedSize - tupleSize); dataSize += paddedSize; SetChunkType(b->pri, TC_WHOLE); SetChunkDataSize(b->pri, dataSize - TUPLE_CHUNK_HEADER_SIZE); break; } else { TupSerHeader tsh; unsigned int datalen; unsigned int nullslen; HeapTupleHeader t_data = tuple->t_data; unsigned char *pos; datalen = tuple->t_len - t_data->t_hoff; if (HeapTupleHasNulls(tuple)) nullslen = BITMAPLEN(HeapTupleHeaderGetNatts(t_data)); else nullslen = 0; tsh.tuplen = sizeof(TupSerHeader) + TYPEALIGN(TUPLE_CHUNK_ALIGN, nullslen) + TYPEALIGN(TUPLE_CHUNK_ALIGN, datalen); tsh.natts = HeapTupleHeaderGetNatts(t_data); tsh.infomask = t_data->t_infomask; if (dataSize + tsh.tuplen > b->prilen || (tsh.infomask & HEAP_HASEXTERNAL) != 0) return 0; pos = b->pri + TUPLE_CHUNK_HEADER_SIZE; memcpy(pos, (char *)&tsh, sizeof(TupSerHeader)); pos += sizeof(TupSerHeader); if (nullslen) { memcpy(pos, (char *)t_data->t_bits, nullslen); pos += nullslen; memset(pos, 0, TYPEALIGN(TUPLE_CHUNK_ALIGN, nullslen) - nullslen); pos += TYPEALIGN(TUPLE_CHUNK_ALIGN, nullslen) - nullslen; } memcpy(pos, (char *)t_data + t_data->t_hoff, datalen); pos += datalen; memset(pos, 0, TYPEALIGN(TUPLE_CHUNK_ALIGN, datalen) - datalen); pos += TYPEALIGN(TUPLE_CHUNK_ALIGN, datalen) - datalen; dataSize += tsh.tuplen; SetChunkType(b->pri, TC_WHOLE); SetChunkDataSize(b->pri, dataSize - TUPLE_CHUNK_HEADER_SIZE); break; } /* tuple that we can't handle here (big ?) -- do the older "out-of-line" serialization */ return 0; } while (0); return dataSize; }