StringInfo rest_call_with_lock(char *method, char *url, char *params, StringInfo postData, int64 mutex, bool shared, bool allowCancel) { CURL *curl; struct curl_slist *headers = NULL; char *errorbuff; StringInfo response = makeStringInfo(); CURLcode ret; int64 response_code; errorbuff = (char *) palloc0(CURL_ERROR_SIZE); curl = curl_easy_init(); if (curl) { headers = curl_slist_append(headers, "Transfer-Encoding:"); headers = curl_slist_append(headers, "Expect:"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 0L); /* allow connections to be reused */ if (allowCancel) { curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); /* we want progress ... */ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, curl_progress_func); /* to go here so we can detect a ^C within postgres */ } curl_easy_setopt(curl, CURLOPT_USERAGENT, "zombodb for PostgreSQL"); curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 0); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_func); curl_easy_setopt(curl, CURLOPT_FAILONERROR, 0); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuff); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60 * 60L); /* timeout of 60 minutes */ curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method); curl_easy_setopt(curl, CURLOPT_WRITEDATA, response); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, postData ? postData->len : 0); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData ? postData->data : NULL); curl_easy_setopt(curl, CURLOPT_POST, (strcmp(method, "POST") == 0) || (strcmp(method, "GET") != 0 && postData && postData->data) ? 1 : 0); } else { elog(IsTransactionState() ? ERROR : WARNING, "Unable to initialize libcurl"); } // if (mutex != 0) // { // if (shared) DirectFunctionCall1(pg_advisory_lock_shared_int8, Int64GetDatum(mutex)); // else DirectFunctionCall1(pg_advisory_lock_int8, Int64GetDatum(mutex)); // } ret = curl_easy_perform(curl); // if (mutex != 0) // { // if (shared) DirectFunctionCall1(pg_advisory_unlock_shared_int8, Int64GetDatum(mutex)); // else DirectFunctionCall1(pg_advisory_unlock_int8, Int64GetDatum(mutex)); // } if (allowCancel && IsTransactionState() && InterruptPending) { /* we might have detected one in the progress function, so check for sure */ CHECK_FOR_INTERRUPTS(); } if (ret != 0) { /* curl messed up */ elog(IsTransactionState() ? ERROR : WARNING, "libcurl error-code: %s(%d); message: %s; req=-X%s %s ", curl_easy_strerror(ret), ret, errorbuff, method, url); } curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); if (response_code < 200 || (response_code >=300 && response_code != 404)) { text *errorText = DatumGetTextP(DirectFunctionCall2(json_object_field_text, CStringGetTextDatum(response->data), CStringGetTextDatum("error"))); elog(IsTransactionState() ? ERROR : WARNING, "rc=%ld; %s", response_code, errorText != NULL ? TextDatumGetCString(errorText) : response->data); } if (headers) curl_slist_free_all(headers); curl_easy_cleanup(curl); pfree(errorbuff); return response; }
/* * len < 0 means "length is not specified". */ static text * ora_substr(Datum str, int start, int len) { if (start == 0) start = 1; /* 0 is interpreted as 1 */ else if (start < 0) { text *t; int32 n; t = DatumGetTextPP(str); n = pg_mbstrlen_with_len(VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t)); start = n + start + 1; if (start <= 0) return cstring_to_text(""); str = PointerGetDatum(t); /* save detoasted text */ } if (len < 0) return DatumGetTextP(DirectFunctionCall2(text_substr_no_len, str, Int32GetDatum(start))); else return DatumGetTextP(DirectFunctionCall3(text_substr, str, Int32GetDatum(start), Int32GetDatum(len))); }
Datum stem_token_arr(PG_FUNCTION_ARGS) { if (PG_ARGISNULL(0)) { PG_RETURN_NULL(); } /* Prepare elements to receive input text[] */ ArrayType *arr = PG_GETARG_ARRAYTYPE_P(0); Datum *dtum; bool *nulls; int ndim; /* Deconstruct input text[] */ deconstruct_array(arr, TEXTOID, -1, false, 'i', &dtum, &nulls, &ndim); /* Prepare stemmer */ struct sb_stemmer *stemmer = sb_stemmer_new( "english" /* language */, NULL /* language encoding NULL for UTF-8 */); Assert(stemmer); /* Call stemming code */ text **result = (text **) palloc(ndim * sizeof(text * )); for(int i=0; i< ndim; i++) { text *token = dtum[i] == 0 ? NULL : DatumGetTextP(dtum[i]); char *empty; if(token == NULL) { empty = (char *)palloc(sizeof(char)); empty[0] = '\0'; } result[i] = (token == NULL ? cstring_to_text(empty) : cstring_to_text(stem_token_text(stemmer, token))); } ArrayType *res = construct_array((Datum*)result, ndim, TEXTOID, -1, false, 'i'); sb_stemmer_delete(stemmer); PG_RETURN_ARRAYTYPE_P(res); }
Datum gbt_text_consistent(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); void *query = (void *) DatumGetTextP(PG_GETARG_DATUM(1)); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); /* Oid subtype = PG_GETARG_OID(3); */ bool *recheck = (bool *) PG_GETARG_POINTER(4); bool retval; GBT_VARKEY *key = (GBT_VARKEY *) DatumGetPointer(entry->key); GBT_VARKEY_R r = gbt_var_key_readable(key); /* All cases served by this function are exact */ *recheck = false; if (tinfo.eml == 0) { tinfo.eml = pg_database_encoding_max_length(); } retval = gbt_var_consistent(&r, query, &strategy, GIST_LEAF(entry), &tinfo); PG_RETURN_BOOL(retval); }
Datum metaphone(PG_FUNCTION_ARGS) { int reqlen; char *str_i; size_t str_i_len; char *metaph; text *result_text; int retval; str_i = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(0)))); str_i_len = strlen(str_i); /* return an empty string if we receive one */ if (!(str_i_len > 0)) PG_RETURN_TEXT_P(GET_TEXT("")); if (str_i_len > MAX_METAPHONE_STRLEN) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("argument exceeds the maximum length of %d bytes", MAX_METAPHONE_STRLEN))); if (!(str_i_len > 0)) ereport(ERROR, (errcode(ERRCODE_ZERO_LENGTH_CHARACTER_STRING), errmsg("argument is empty string"))); reqlen = PG_GETARG_INT32(1); if (reqlen > MAX_METAPHONE_STRLEN) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("output exceeds the maximum length of %d bytes", MAX_METAPHONE_STRLEN))); if (!(reqlen > 0)) ereport(ERROR, (errcode(ERRCODE_ZERO_LENGTH_CHARACTER_STRING), errmsg("output cannot be empty string"))); retval = _metaphone(str_i, reqlen, &metaph); if (retval == META_SUCCESS) { result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(metaph))); PG_RETURN_TEXT_P(result_text); } else { /* internal error */ elog(ERROR, "metaphone: failure"); /* * Keep the compiler quiet */ PG_RETURN_NULL(); } }
Datum sha_to_text_fn(PG_FUNCTION_ARGS) { Sha *value = PG_GETARG_SHA(0); char *cstring; text *textval; cstring = hexarr_to_cstring(value->bytes, SHA_LENGTH); textval = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstring))); PG_RETURN_TEXT_P(textval); }
/* * Transform a relation options list (list of DefElem) into the text array * format that is kept in pg_class.reloptions. * * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing * reloptions value (possibly NULL), and we replace or remove entries * as needed. * * If ignoreOids is true, then we should ignore any occurrence of "oids" * in the list (it will be or has been handled by interpretOidsOption()). * * Note that this is not responsible for determining whether the options * are valid. * * Both oldOptions and the result are text arrays (or NULL for "default"), * but we declare them as Datums to avoid including array.h in reloptions.h. */ Datum transformRelOptions(Datum oldOptions, List *defList, bool ignoreOids, bool isReset) { Datum result; ArrayBuildState *astate; ListCell *cell; /* no change if empty list */ if (defList == NIL) return oldOptions; /* We build new array using accumArrayResult */ astate = NULL; /* Copy any oldOptions that aren't to be replaced */ if (DatumGetPointer(oldOptions) != 0) { ArrayType *array = DatumGetArrayTypeP(oldOptions); Datum *oldoptions; int noldoptions; int i; Assert(ARR_ELEMTYPE(array) == TEXTOID); deconstruct_array(array, TEXTOID, -1, false, 'i', &oldoptions, NULL, &noldoptions); for (i = 0; i < noldoptions; i++) { text *oldoption = DatumGetTextP(oldoptions[i]); char *text_str = VARDATA(oldoption); int text_len = VARSIZE(oldoption) - VARHDRSZ; /* Search for a match in defList */ foreach(cell, defList) { DefElem *def = lfirst(cell); int kw_len = strlen(def->defname); if (text_len > kw_len && text_str[kw_len] == '=' && pg_strncasecmp(text_str, def->defname, kw_len) == 0) break; } if (!cell) { /* No match, so keep old option */ astate = accumArrayResult(astate, oldoptions[i], false, TEXTOID, CurrentMemoryContext); } }
Datum nameicnlike(PG_FUNCTION_ARGS) { Name str = PG_GETARG_NAME(0); text *pat = PG_GETARG_TEXT_PP(1); bool result; text *strtext; strtext = DatumGetTextP(DirectFunctionCall1(name_text, NameGetDatum(str))); result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) != LIKE_TRUE); PG_RETURN_BOOL(result); }
static inline int Generic_Text_IC_like(text *str, text *pat) { char *s, *p; int slen, plen; /* * For efficiency reasons, in the single byte case we don't call lower() * on the pattern and text, but instead call to_lower on each character. * In the multi-byte case we don't have much choice :-( */ if (pg_database_encoding_max_length() > 1) { /* lower's result is never packed, so OK to use old macros here */ pat = DatumGetTextP(DirectFunctionCall1(lower, PointerGetDatum(pat))); p = VARDATA(pat); plen = (VARSIZE(pat) - VARHDRSZ); str = DatumGetTextP(DirectFunctionCall1(lower, PointerGetDatum(str))); s = VARDATA(str); slen = (VARSIZE(str) - VARHDRSZ); if (GetDatabaseEncoding() == PG_UTF8) return UTF8_MatchText(s, slen, p, plen); else return MB_MatchText(s, slen, p, plen); } else { p = VARDATA_ANY(pat); plen = VARSIZE_ANY_EXHDR(pat); s = VARDATA_ANY(str); slen = VARSIZE_ANY_EXHDR(str); return SB_IMatchText(s, slen, p, plen); } }
Datum orafce_to_char_timestamp(PG_FUNCTION_ARGS) { Timestamp date_txt = PG_GETARG_TIMESTAMP(0); text *result = NULL; if(nls_date_format && strlen(nls_date_format)) { /* it will return the DATE in nls_date_format*/ result = DatumGetTextP(DirectFunctionCall2(timestamp_to_char, CStringGetDatum(date_txt), CStringGetDatum(cstring_to_text(nls_date_format)))); } PG_RETURN_TEXT_P(result); }
Datum gbt_text_consistent(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); GBT_VARKEY *key = (GBT_VARKEY *) DatumGetPointer(entry->key); void *query = (void *) DatumGetTextP(PG_GETARG_DATUM(1)); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); bool retval = FALSE; GBT_VARKEY_R r = gbt_var_key_readable(key); if (tinfo.eml == 0) { tinfo.eml = pg_database_encoding_max_length(); } retval = gbt_var_consistent(&r, query, &strategy, GIST_LEAF(entry), &tinfo); PG_RETURN_BOOL(retval); }
Datum gtrgm_compress(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); GISTENTRY *retval = entry; if (entry->leafkey) { /* trgm */ TRGM *res; text *val = DatumGetTextP(entry->key); res = generate_trgm(VARDATA(val), VARSIZE(val) - VARHDRSZ); retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(res), entry->rel, entry->page, entry->offset, FALSE); } else if (ISSIGNKEY(DatumGetPointer(entry->key)) && !ISALLTRUE(DatumGetPointer(entry->key))) { int32 i, len; TRGM *res; BITVECP sign = GETSIGN(DatumGetPointer(entry->key)); LOOPBYTE { if ((sign[i] & 0xff) != 0xff) PG_RETURN_POINTER(retval); } len = CALCGTSIZE(SIGNKEY | ALLISTRUE, 0); res = (TRGM *) palloc(len); SET_VARSIZE(res, len); res->flag = SIGNKEY | ALLISTRUE; retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(res), entry->rel, entry->page, entry->offset, FALSE); }
/* * plr_quote_literal() - quote literal strings that are to * be used in SPI_exec query strings */ SEXP plr_quote_literal(SEXP rval) { const char *value; text *value_text; text *result_text; SEXP result; /* extract the C string */ PROTECT(rval = AS_CHARACTER(rval)); value = CHAR(STRING_ELT(rval, 0)); /* convert using the pgsql quote_literal function */ value_text = PG_STR_GET_TEXT(value); result_text = DatumGetTextP(DirectFunctionCall1(quote_literal, PointerGetDatum(value_text))); /* copy result back into an R object */ PROTECT(result = NEW_CHARACTER(1)); SET_STRING_ELT(result, 0, COPY_TO_USER_STRING(PG_TEXT_GET_STR(result_text))); UNPROTECT(2); return result; }
Datum dbms_alert_defered_signal(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; TupleDesc tupdesc; HeapTuple rettuple; char *relname; text *name; text *message; int event_col; int message_col; Datum datum; bool isnull; int cycle = 0; float8 endtime; float8 timeout = 2; if (!CALLED_AS_TRIGGER(fcinfo)) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("not called by trigger manager"))); if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("not called on valid event"))); if (SPI_connect() < 0) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("SPI_connect failed"))); if (strcmp((relname = SPI_getrelname(trigdata->tg_relation)), "ora_alerts") != 0) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("not called with valid relation"))); rettuple = trigdata->tg_trigtuple; tupdesc = trigdata->tg_relation->rd_att; if (SPI_ERROR_NOATTRIBUTE == (event_col = SPI_fnumber(tupdesc, "event"))) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("attribute event not found"))); if (SPI_ERROR_NOATTRIBUTE == (message_col = SPI_fnumber(tupdesc, "message"))) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("attribute message not found"))); datum = SPI_getbinval(rettuple, tupdesc, event_col, &isnull); if (isnull) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("event name is NULL"), errdetail("Eventname may not be NULL."))); name = DatumGetTextP(datum); datum = SPI_getbinval(rettuple, tupdesc, message_col, &isnull); if (isnull) message = NULL; else message = DatumGetTextP(datum); WATCH_PRE(timeout, endtime, cycle); if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES, MAX_EVENTS, MAX_LOCKS, false)) { ItemPointer tid; Oid argtypes[1] = {TIDOID}; char nulls[1] = {' '}; Datum values[1]; void *plan; create_message(name, message); LWLockRelease(shmem_lock); tid = &rettuple->t_data->t_ctid; if (!(plan = SPI_prepare("DELETE FROM ora_alerts WHERE ctid = $1", 1, argtypes))) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("SPI_prepare failed"))); values[0] = ItemPointerGetDatum(tid); if (SPI_OK_DELETE != SPI_execute_plan(plan, values, nulls, false, 1)) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("can't execute sql"))); SPI_finish(); return PointerGetDatum(rettuple); } WATCH_POST(timeout, endtime, cycle); LOCK_ERROR(); PG_RETURN_NULL(); }
Datum xmlelement(PG_FUNCTION_ARGS) { Datum nameText; ArrayType *attrs = NULL; char *elName; unsigned int nameLen, resSizeMax; unsigned int childSize = 0; char *c, *result, *resData, *resCursor, *nameDst; XMLCompNodeHdr element; XMLNodeOffset *rootOffPtr; bool nameFirstChar = true; char **attrNames = NULL; char **attrValues = NULL; char *attrValFlags = NULL; XMLNodeHdr *attrNodes = NULL; XMLNodeHdr child = NULL; char **newNds = NULL; char *newNd = NULL; unsigned int attrCount = 0; unsigned int attrsSizeTotal = 0; unsigned short childCount = 0; if (PG_ARGISNULL(0)) { elog(ERROR, "invalid element name"); } nameText = PG_GETARG_DATUM(0); elName = TextDatumGetCString(nameText); nameLen = strlen(elName); if (nameLen == 0) { elog(ERROR, "invalid element name"); } if (!PG_ARGISNULL(1)) { int *dims; Oid elType, arrType; int16 arrLen, elLen; bool elByVal, elIsNull; char elAlign; unsigned int i; attrs = PG_GETARG_ARRAYTYPE_P(1); if (ARR_NDIM(attrs) != 2) { elog(ERROR, "attributes must be passed in 2 dimensional array"); } dims = ARR_DIMS(attrs); if (dims[1] != 2) { elog(ERROR, "the second dimension of attribute array must be 2"); } attrCount = dims[0]; Assert(attrCount > 0); elType = attrs->elemtype; arrType = get_array_type(elType); arrLen = get_typlen(arrType); Assert(arrType != InvalidOid); get_typlenbyvalalign(elType, &elLen, &elByVal, &elAlign); attrNames = (char **) palloc(attrCount * sizeof(char *)); attrValues = (char **) palloc(attrCount * sizeof(char *)); attrValFlags = (bool *) palloc(attrCount * sizeof(char)); for (i = 1; i <= attrCount; i++) { int subscrName[] = {i, 1}; int subscrValue[] = {i, 2}; Datum elDatum; char *nameStr, *valueStr; bool valueHasRefs = false; elDatum = array_ref(attrs, 2, subscrName, arrLen, elLen, elByVal, elAlign, &elIsNull); if (elIsNull) { elog(ERROR, "attribute name must not be null"); } nameStr = text_to_cstring(DatumGetTextP(elDatum)); if (strlen(nameStr) == 0) { elog(ERROR, "attribute name must be a string of non-zero length"); } else { /* Check validity of characters. */ char *c = nameStr; int cWidth = pg_utf_mblen((unsigned char *) c); if (!XNODE_VALID_NAME_START(c)) { elog(ERROR, "attribute name starts with invalid character"); } do { c += cWidth; cWidth = pg_utf_mblen((unsigned char *) c); } while (XNODE_VALID_NAME_CHAR(c)); if (*c != '\0') { elog(ERROR, "invalid character in attribute name"); } } /* Check uniqueness of the attribute name. */ if (i > 1) { unsigned short j; for (j = 0; j < (i - 1); j++) { if (strcmp(nameStr, attrNames[j]) == 0) { elog(ERROR, "attribute name '%s' is not unique", nameStr); } } } elDatum = array_ref(attrs, 2, subscrValue, arrLen, elLen, elByVal, elAlign, &elIsNull); if (elIsNull) { elog(ERROR, "attribute value must not be null"); } valueStr = text_to_cstring(DatumGetTextP(elDatum)); attrValFlags[i - 1] = 0; if (strlen(valueStr) > 0) { XMLNodeParserStateData state; char *valueStrOrig = valueStr; /* Parse the value and check validity. */ initXMLParserState(&state, valueStr, true); valueStr = readXMLAttValue(&state, true, &valueHasRefs); /* * If the value contains quotation mark, then apostrophe is * the delimiter. */ if (strchr(valueStr, XNODE_CHAR_QUOTMARK) != NULL) { attrValFlags[i - 1] |= XNODE_ATTR_APOSTROPHE; } finalizeXMLParserState(&state); pfree(valueStrOrig); } attrNames[i - 1] = nameStr; attrValues[i - 1] = valueStr; if (valueHasRefs) { attrValFlags[i - 1] |= XNODE_ATTR_CONTAINS_REF; } attrsSizeTotal += sizeof(XMLNodeHdrData) + strlen(nameStr) + strlen(valueStr) + 2; } } if (!PG_ARGISNULL(2)) { Datum childNodeDatum = PG_GETARG_DATUM(2); xmlnode childRaw = (xmlnode) PG_DETOAST_DATUM(childNodeDatum); child = XNODE_ROOT(childRaw); if (child->kind == XMLNODE_DOC_FRAGMENT) { childSize = getXMLNodeSize(child, true) - getXMLNodeSize(child, false); } else { childSize = getXMLNodeSize(child, true); } } /* Make sure the element name is valid. */ c = elName; while (*c != '\0') { if ((nameFirstChar && !XNODE_VALID_NAME_START(c)) || (!nameFirstChar && !XNODE_VALID_NAME_CHAR(c))) { elog(ERROR, "unrecognized character '%c' in element name", *c); } if (nameFirstChar) { nameFirstChar = false; } c += pg_utf_mblen((unsigned char *) c); }; if (child != NULL) { if (child->kind == XMLNODE_DOC_FRAGMENT) { childCount = ((XMLCompNodeHdr) child)->children; } else { childCount = 1; } } /* * It's hard to determine the byte width of references until the copying * has finished. Therefore we assume the worst case: 4 bytes per * reference. */ resSizeMax = VARHDRSZ + attrsSizeTotal + childSize + (attrCount + childCount) * 4 + sizeof(XMLCompNodeHdrData) + nameLen + 1 + sizeof(XMLNodeOffset); result = (char *) palloc(resSizeMax); resCursor = resData = VARDATA(result); if (attrCount > 0) { /* Copy attributes. */ unsigned short i; Assert(attrNames != NULL && attrValues != NULL && attrValFlags != NULL); attrNodes = (XMLNodeHdr *) palloc(attrCount * sizeof(XMLNodeHdr)); for (i = 0; i < attrCount; i++) { XMLNodeHdr attrNode = (XMLNodeHdr) resCursor; char *name = attrNames[i]; unsigned int nameLen = strlen(name); char *value = attrValues[i]; unsigned int valueLen = strlen(value); attrNodes[i] = attrNode; attrNode->kind = XMLNODE_ATTRIBUTE; attrNode->flags = attrValFlags[i]; if (xmlAttrValueIsNumber(value)) { attrNode->flags |= XNODE_ATTR_NUMBER; } resCursor = XNODE_CONTENT(attrNode); memcpy(resCursor, name, nameLen); resCursor += nameLen; *(resCursor++) = '\0'; pfree(name); memcpy(resCursor, value, valueLen); resCursor += valueLen; *(resCursor++) = '\0'; pfree(value); } pfree(attrNames); pfree(attrValues); pfree(attrValFlags); } if (child != NULL) { XMLNodeKind k = child->kind; /* * Check if the node to be inserted is of a valid kind. If the node is * document fragment, its assumed that invalid node kinds are never * added. Otherwise we'd have to check the node fragment (recursively) * not only here. */ if (k != XMLNODE_DOC_FRAGMENT) { if (k == XMLNODE_DOC || k == XMLNODE_DTD || k == XMLNODE_ATTRIBUTE) { elog(ERROR, "the nested node must not be %s", getXMLNodeKindStr(k)); } } copyXMLNodeOrDocFragment(child, childSize, &resCursor, &newNd, &newNds); } element = (XMLCompNodeHdr) resCursor; element->common.kind = XMLNODE_ELEMENT; element->common.flags = (child == NULL) ? XNODE_EMPTY : 0; element->children = attrCount + childCount; if (childCount > 0 || attrCount > 0) { XMLNodeOffset childOff, childOffMax; char bwidth; char *refPtr; /* Save relative offset(s) of the child node(s). */ if (attrCount > 0) { childOffMax = (char *) element - resData; } else if (childCount > 0) { if (child->kind == XMLNODE_DOC_FRAGMENT) { Assert(newNds != NULL); childOffMax = (char *) element - newNds[0]; } else { childOffMax = (char *) element - newNd; } } else { childOffMax = 0; } bwidth = getXMLNodeOffsetByteWidth(childOffMax); XNODE_SET_REF_BWIDTH(element, bwidth); refPtr = XNODE_FIRST_REF(element); if (attrCount > 0) { unsigned short i; /* The attribute references first... */ for (i = 0; i < attrCount; i++) { XMLNodeHdr node = attrNodes[i]; childOff = (char *) element - (char *) node; writeXMLNodeOffset(childOff, &refPtr, bwidth, true); } pfree(attrNodes); } if (childCount > 0) { /* ...followed by those of the other children. */ if (child->kind == XMLNODE_DOC_FRAGMENT) { unsigned short i; for (i = 0; i < childCount; i++) { childOff = (char *) element - newNds[i]; writeXMLNodeOffset(childOff, &refPtr, bwidth, true); } pfree(newNds); } else { childOff = (char *) element - newNd; writeXMLNodeOffset(childOff, &refPtr, bwidth, true); } } } /* And finally set the element name. */ nameDst = XNODE_ELEMENT_NAME(element); memcpy(nameDst, elName, nameLen); nameDst[nameLen] = '\0'; resCursor = nameDst + strlen(elName) + 1; SET_VARSIZE(result, (char *) resCursor - result + sizeof(XMLNodeOffset)); rootOffPtr = XNODE_ROOT_OFFSET_PTR(result); *rootOffPtr = (char *) element - resData; PG_RETURN_POINTER(result); }
Datum spg_text_leaf_consistent(PG_FUNCTION_ARGS) { spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0); spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1); StrategyNumber strategy = in->strategy; text *query = DatumGetTextPP(in->query); int level = in->level; text *leafValue, *reconstrValue = NULL; char *fullValue; int fullLen; int queryLen; int r; bool res; /* all tests are exact */ out->recheck = false; leafValue = DatumGetTextPP(in->leafDatum); if (DatumGetPointer(in->reconstructedValue)) reconstrValue = DatumGetTextP(in->reconstructedValue); Assert(level == 0 ? reconstrValue == NULL : VARSIZE_ANY_EXHDR(reconstrValue) == level); fullLen = level + VARSIZE_ANY_EXHDR(leafValue); queryLen = VARSIZE_ANY_EXHDR(query); /* * For an equality check, we needn't reconstruct fullValue if not same * length; it can't match */ if (strategy == BTEqualStrategyNumber && queryLen != fullLen) PG_RETURN_BOOL(false); /* Else, reconstruct the full string represented by this leaf tuple */ if (VARSIZE_ANY_EXHDR(leafValue) == 0 && level > 0) { fullValue = VARDATA(reconstrValue); out->leafValue = PointerGetDatum(reconstrValue); } else { text *fullText = palloc(VARHDRSZ + fullLen); SET_VARSIZE(fullText, VARHDRSZ + fullLen); fullValue = VARDATA(fullText); if (level) memcpy(fullValue, VARDATA(reconstrValue), level); if (VARSIZE_ANY_EXHDR(leafValue) > 0) memcpy(fullValue + level, VARDATA_ANY(leafValue), VARSIZE_ANY_EXHDR(leafValue)); out->leafValue = PointerGetDatum(fullText); } /* Run the appropriate type of comparison */ if (strategy > 10) { /* Collation-aware comparison */ strategy -= 10; /* If asserts are enabled, verify encoding of reconstructed string */ Assert(pg_verifymbstr(fullValue, fullLen, false)); r = varstr_cmp(fullValue, Min(queryLen, fullLen), VARDATA_ANY(query), Min(queryLen, fullLen), PG_GET_COLLATION()); } else { /* Non-collation-aware comparison */ r = memcmp(fullValue, VARDATA_ANY(query), Min(queryLen, fullLen)); } if (r == 0) { if (queryLen > fullLen) r = -1; else if (queryLen < fullLen) r = 1; } switch (strategy) { case BTLessStrategyNumber: res = (r < 0); break; case BTLessEqualStrategyNumber: res = (r <= 0); break; case BTEqualStrategyNumber: res = (r == 0); break; case BTGreaterEqualStrategyNumber: res = (r >= 0); break; case BTGreaterStrategyNumber: res = (r > 0); break; default: elog(ERROR, "unrecognized strategy number: %d", in->strategy); res = false; break; } PG_RETURN_BOOL(res); }
/* MPP-6923: */ static List * AlterResqueueCapabilityEntry( List *stmtOptIdList, Oid queueid, ListCell *initcell, bool bCreate) { ListCell *lc; List *elems = NIL; List *dropelems = NIL; List *dupcheck = NIL; HeapTuple tuple; cqContext *pcqCtx; cqContext cqc; Relation rel = NULL; bool bWithout = false; TupleDesc tupdesc = NULL; #ifdef USE_ASSERT_CHECKING { DefElem *defel = (DefElem *) lfirst(initcell); Assert(0 == strcmp(defel->defname, "withliststart")); } #endif initcell = lnext(initcell); /* walk the original list and build a list of valid entries */ for_each_cell(lc, initcell) { DefElem *defel = (DefElem *) lfirst(lc); Oid resTypeOid = InvalidOid; int resTypeInt = 0; List *pentry = NIL; Value *pKeyVal = NULL; Value *pStrVal = NULL; if (!bWithout && (strcmp(defel->defname, "withoutliststart") == 0)) { bWithout = true; rel = heap_open(ResourceTypeRelationId, RowExclusiveLock); tupdesc = RelationGetDescr(rel); goto L_loop_cont; } /* ignore the basic threshold entries -- should already be processed */ if (strcmp(defel->defname, "active_statements") == 0) goto L_loop_cont; if (strcmp(defel->defname, "max_cost") == 0) goto L_loop_cont; if (strcmp(defel->defname, "cost_overcommit") == 0) goto L_loop_cont; if (strcmp(defel->defname, "min_cost") == 0) goto L_loop_cont; if (!GetResourceTypeByName(defel->defname, &resTypeInt, &resTypeOid)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("option \"%s\" is not a valid resource type", defel->defname))); pKeyVal = makeString(defel->defname); /* WITHOUT clause value determined in pg_resourcetype */ if (!bWithout) pStrVal = makeString(defGetString(defel)); else { pStrVal = NULL; /* if NULL, delete entry from * pg_resqueuecapability */ pcqCtx = caql_beginscan( caql_addrel(cqclr(&cqc), rel), cql("SELECT * FROM pg_resourcetype" " WHERE restypid = :1 FOR UPDATE", Int16GetDatum(resTypeInt))); while (HeapTupleIsValid(tuple = caql_getnext(pcqCtx))) { text *shutoff_text = NULL; char *shutoff_str = NULL; Datum shutoff_datum; bool isnull = false; Form_pg_resourcetype rtyp = (Form_pg_resourcetype)GETSTRUCT(tuple); if (!rtyp->reshasdisable) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("option \"%s\" cannot be disabled", defel->defname))); } /* required type must have a default value if it can * be disabled */ if (!rtyp->reshasdefault) { if (!rtyp->resrequired) /* optional resource without a default is * turned off by removing entry from * pg_resqueuecapability */ break; else { /* XXX XXX */ Assert(0); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("required option \"%s\" cannot be disabled", defel->defname))); } } /* get the shutoff string */ shutoff_datum = heap_getattr(tuple, Anum_pg_resourcetype_resdisabledsetting, tupdesc, &isnull); Assert(!isnull); shutoff_text = DatumGetTextP(shutoff_datum); shutoff_str = DatumGetCString( DirectFunctionCall1( textout, PointerGetDatum(shutoff_text))); pStrVal = makeString(shutoff_str); break; } /* end while heaptuple is valid */ caql_endscan(pcqCtx); } /* check for duplicate key specifications */ if (list_member(dupcheck, pKeyVal)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant option for \"%s\"", defel->defname))); dupcheck = lappend(dupcheck, pKeyVal); pentry = list_make2( makeInteger(resTypeInt), pStrVal); /* list of lists - (resource type, resource setting) */ if (bWithout) { /* if the "without" entry has an "off" value, then treat * it as a regular "with" item and update it in * pg_resqueuecapability, else remove its entry */ if (!pStrVal) dropelems = lappend(dropelems, pentry); else elems = lappend(elems, pentry); } else elems = lappend(elems, pentry); L_loop_cont: resTypeInt = 0; /* make compiler happy */ }
void datum_to_bson(const char* field_name, mongo::BSONObjBuilder& builder, Datum val, bool is_null, Oid typid) { PGBSON_LOG << "BEGIN datum_to_bson, field_name=" << field_name << ", typeid=" << typid << PGBSON_ENDL; if (field_name == NULL) { field_name = ""; } if (is_null) { builder.appendNull(field_name); } else { switch(typid) { case BOOLOID: builder.append(field_name, DatumGetBool(val)); break; case CHAROID: { char c = DatumGetChar(val); builder.append(field_name, &c, 1); break; } case INT8OID: builder.append(field_name, (long long)DatumGetInt64(val)); break; case INT2OID: builder.append(field_name, DatumGetInt16(val)); break; case INT4OID: builder.append(field_name, DatumGetInt32(val)); break; case TEXTOID: case JSONOID: case XMLOID: { text* t = DatumGetTextP(val); builder.append(field_name, VARDATA(t), VARSIZE(t)-VARHDRSZ+1); break; } case FLOAT4OID: builder.append(field_name, DatumGetFloat4(val)); break; case FLOAT8OID: builder.append(field_name, DatumGetFloat8(val)); break; case RECORDOID: { mongo::BSONObjBuilder sub(builder.subobjStart(field_name)); composite_to_bson(sub, val); sub.done(); break; } case TIMESTAMPOID: { Timestamp ts = DatumGetTimestamp(val); #ifdef HAVE_INT64_TIMESTAMP mongo::Date_t date(ts); #else mongo::Date_t date(ts * 1000); #endif builder.append(field_name, date); break; } default: { PGBSON_LOG << "datum_to_bson - unknown type, using text output." << PGBSON_ENDL; PGBSON_LOG << "datum_to_bson - type=" << get_typename(typid) << PGBSON_ENDL; if (get_typename(typid) == "bson") { bytea* data = DatumGetBson(val); mongo::BSONObj obj(VARDATA_ANY(data)); builder.append(field_name, obj); } else { // use text output for the type bool typisvarlena = false; Oid typoutput; getTypeOutputInfo(typid, &typoutput, &typisvarlena); PGBSON_LOG << "datum_to_bson - typisvarlena=" << std::boolalpha << typisvarlena << PGBSON_ENDL; Datum out_val = val; /* * If we have a toasted datum, forcibly detoast it here to avoid * memory leakage inside the type's output routine. */ if (typisvarlena) { out_val = PointerGetDatum(PG_DETOAST_DATUM(val)); PGBSON_LOG << "datum_to_bson - var len valuie detoasted" << PGBSON_ENDL; } char* outstr = OidOutputFunctionCall(typoutput, out_val); builder.append(field_name, outstr); /* Clean up detoasted copy, if any */ if (val != out_val) pfree(DatumGetPointer(out_val)); } } } // switch } // if not null PGBSON_LOG << "END datum_to_bson, field_name=" << field_name << PGBSON_ENDL; }
Datum WTBtree_consistent(PG_FUNCTION_ARGS) { printf("------------------consistent\n"); GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); void *query = (void *) DatumGetTextP(PG_GETARG_DATUM(1)); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); bool result; /* Oid subtype = PG_GETARG_OID(3); */ /* GBT_VARKEY *orge = (GBT_VARKEY *) DatumGetPointer(entry->key); GBT_VARKEY_R ok; ok = gbt_var_key_readable(orge); printf("ok.lower : %s\n", ok.lower); printf("VARDATA(ok.lower) : %s\n", VARDATA(ok.lower)); printf("VARDATA(ok.upper) : %s\n", VARDATA(ok.upper)); printf("VARDATA_4B(ok.lower) : %s\n", VARDATA_4B(ok.lower)); printf("VARDATA_4B(ok.upper) : %s\n", VARDATA_4B(ok.upper)); */ printf("strategy : %d\n", strategy); char* temp; temp = (char*) palloc(12); memcpy(temp, "wx4fccknzng3", 12); GeoCoord gCoord = geohash_decode(temp); printf("gCoord.north : %lf\n", gCoord.north); printf("gCoord.east : %lf\n", gCoord.east); printf("gCoord.south : %lf\n", gCoord.south); printf("gCoord.west : %lf\n", gCoord.west); BOX2DF* tbox; tbox = (BOX2DF*) palloc(sizeof(BOX2DF)); tbox->xmin = (float) gCoord.west; tbox->ymin = (float) gCoord.south; tbox->xmax = (float) gCoord.east; tbox->ymax = (float) gCoord.north; printf("temp : %s\n", temp); BOX2DF query_gbox_index; gserialized_datum_get_box2df_p(query, &query_gbox_index); if (GIST_LEAF(entry)) { result = gserialized_gist_consistent_leaf_2d(tbox, &query_gbox_index, strategy); } else { result = gserialized_gist_consistent_internal_2d(tbox, &query_gbox_index, strategy); } /* BOX2DF query_gbox_index; gserialized_datum_get_box2df_p(query, &query_gbox_index); if (GIST_LEAF(entry)) { result = gserialized_gist_consistent_leaf_2d( (BOX2DF*)DatumGetPointer(entry->key), &query_gbox_index, strategy); } else { result = gserialized_gist_consistent_internal_2d( (BOX2DF*)DatumGetPointer(entry->key), &query_gbox_index, strategy); } */ /* int size = 12; char *minPnt, *maxPnt, *cvtGeoHash; minPnt = (char*) palloc(size); maxPnt = (char*) palloc(size); cvtGeoHash = (char*) palloc(size); memcpy(minPnt, geohash_encode((double) query_gbox_index.ymin, (double) query_gbox_index.xmin, size), size); memcpy(maxPnt, geohash_encode((double) query_gbox_index.ymax, (double) query_gbox_index.xmax, size), size); cvtGeoHash = convert_GeoHash_from_box2d(minPnt, maxPnt, size); printf("-----------geohash_encode : %s\n", cvtGeoHash); */ /* PostgreSQL 8.4 and later require the RECHECK flag to be set here, rather than being supplied as part of the operator class definition */ /* bool *recheck = (bool *) PG_GETARG_POINTER(4); LEAF_KEY key; memcpy(key, DatumGetPointer(entry->key), KEY_SIZE); INTERNAL_KEY *ikey = leafKey_to_internaKey(key); *recheck = false; */ PG_RETURN_BOOL(result); }
/* * Turn a scalar Datum into JSON, appending the string to "result". * * Hand off a non-scalar datum to composite_to_json or array_to_json_internal * as appropriate. */ static void datum_to_json(Datum val, bool is_null, StringInfo result, TYPCATEGORY tcategory, Oid typoutputfunc) { char *outputstr; text *jsontext; if (is_null) { appendStringInfoString(result, "null"); return; } switch (tcategory) { case TYPCATEGORY_ARRAY: array_to_json_internal(val, result, false); break; case TYPCATEGORY_COMPOSITE: composite_to_json(val, result, false); break; case TYPCATEGORY_BOOLEAN: if (DatumGetBool(val)) appendStringInfoString(result, "true"); else appendStringInfoString(result, "false"); break; case TYPCATEGORY_NUMERIC: outputstr = OidOutputFunctionCall(typoutputfunc, val); /* * Don't call escape_json here if it's a valid JSON number. * Numeric output should usually be a valid JSON number and JSON * numbers shouldn't be quoted. Quote cases like "Nan" and * "Infinity", however. */ if (strpbrk(outputstr, NON_NUMERIC_LETTER) == NULL) appendStringInfoString(result, outputstr); else escape_json(result, outputstr); pfree(outputstr); break; case TYPCATEGORY_JSON: /* JSON will already be escaped */ outputstr = OidOutputFunctionCall(typoutputfunc, val); appendStringInfoString(result, outputstr); pfree(outputstr); break; case TYPCATEGORY_JSON_CAST: jsontext = DatumGetTextP(OidFunctionCall1(typoutputfunc, val)); outputstr = text_to_cstring(jsontext); appendStringInfoString(result, outputstr); pfree(outputstr); pfree(jsontext); break; default: outputstr = OidOutputFunctionCall(typoutputfunc, val); escape_json(result, outputstr); pfree(outputstr); break; } }
Datum autoinc(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; Trigger *trigger; /* to get trigger name */ int nargs; /* # of arguments */ int *chattrs; /* attnums of attributes to change */ int chnattrs = 0; /* # of above */ Datum *newvals; /* vals of above */ char **args; /* arguments */ char *relname; /* triggered relation name */ Relation rel; /* triggered relation */ HeapTuple rettuple = NULL; TupleDesc tupdesc; /* tuple description */ bool isnull; int i; if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */ elog(ERROR, "not fired by trigger manager"); if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) /* internal error */ elog(ERROR, "cannot process STATEMENT events"); if (TRIGGER_FIRED_AFTER(trigdata->tg_event)) /* internal error */ elog(ERROR, "must be fired before event"); if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) rettuple = trigdata->tg_trigtuple; else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) rettuple = trigdata->tg_newtuple; else /* internal error */ elog(ERROR, "cannot process DELETE events"); rel = trigdata->tg_relation; relname = SPI_getrelname(rel); trigger = trigdata->tg_trigger; nargs = trigger->tgnargs; if (nargs <= 0 || nargs % 2 != 0) /* internal error */ elog(ERROR, "autoinc (%s): even number gt 0 of arguments was expected", relname); args = trigger->tgargs; tupdesc = rel->rd_att; chattrs = (int *) palloc(nargs / 2 * sizeof(int)); newvals = (Datum *) palloc(nargs / 2 * sizeof(Datum)); for (i = 0; i < nargs;) { int attnum = SPI_fnumber(tupdesc, args[i]); int32 val; Datum seqname; if (attnum < 0) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("\"%s\" has no attribute \"%s\"", relname, args[i]))); if (SPI_gettypeid(tupdesc, attnum) != INT4OID) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("attribute \"%s\" of \"%s\" must be type INT4", args[i], relname))); val = DatumGetInt32(SPI_getbinval(rettuple, tupdesc, attnum, &isnull)); if (!isnull && val != 0) { i += 2; continue; } i++; chattrs[chnattrs] = attnum; seqname = CStringGetTextDatum(args[i]); newvals[chnattrs] = DirectFunctionCall1(nextval, seqname); /* nextval now returns int64; coerce down to int32 */ newvals[chnattrs] = Int32GetDatum((int32) DatumGetInt64(newvals[chnattrs])); if (DatumGetInt32(newvals[chnattrs]) == 0) { newvals[chnattrs] = DirectFunctionCall1(nextval, seqname); newvals[chnattrs] = Int32GetDatum((int32) DatumGetInt64(newvals[chnattrs])); } pfree(DatumGetTextP(seqname)); chnattrs++; i++; } if (chnattrs > 0) { rettuple = SPI_modifytuple(rel, rettuple, chnattrs, chattrs, newvals, NULL); if (rettuple == NULL) /* internal error */ elog(ERROR, "autoinc (%s): %d returned by SPI_modifytuple", relname, SPI_result); } pfree(relname); pfree(chattrs); pfree(newvals); return PointerGetDatum(rettuple); }
static void *uri_char(HeapTupleHeader ud, bool hdr, bool term) { TupleDesc td; HeapTupleData tuple; Datum d[URI_LEN]; bool n[URI_LEN]; text *scheme = NULL, *host = NULL, *path = NULL; int16 port; char portbuf[8]; unsigned schemelen = 0, hostlen = 0, portlen = 0, pathlen = 0; unsigned len; void *out; char *p; td = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(ud), HeapTupleHeaderGetTypMod(ud)); tuple.t_len = HeapTupleHeaderGetDatumLength(ud); ItemPointerSetInvalid(&(tuple.t_self)); tuple.t_tableOid = InvalidOid; tuple.t_data = ud; heap_deform_tuple(&tuple, td, d, n); ReleaseTupleDesc(td); if (!n[URI_SCHEME]) { scheme = DatumGetTextP(d[URI_SCHEME]); schemelen = VARSIZE_ANY_EXHDR(scheme); } if (!n[URI_HOST]) { host = DatumGetTextP(d[URI_HOST]); hostlen = VARSIZE_ANY_EXHDR(host); } if (!n[URI_PORT]) { port = DatumGetInt16(d[URI_PORT]); portlen = snprintf(portbuf, sizeof(portbuf)-1, ":%hu", port); } if (!n[URI_PATH]) { path = DatumGetTextP(d[URI_PATH]); pathlen = VARSIZE_ANY_EXHDR(path); } len = (hdr ? VARHDRSZ : 0) + schemelen + (scheme ? 3 : 0) + hostlen + portlen + pathlen + term; out = palloc(len); if (hdr) SET_VARSIZE(out, len); p = hdr ? VARDATA(out) : out; if (scheme) { memcpy(p, VARDATA(scheme), schemelen); p += schemelen; *p++ = ':'; *p++ = '/'; *p++ = '/'; } if (host) { domainname_flip(p, VARDATA(host), hostlen); p += hostlen; } memcpy(p, portbuf, portlen); p += portlen; if (path) { memcpy(p, VARDATA(path), pathlen); p += pathlen; } if (term) *p = '\0'; return out; }
/* * pgpool_switch_log is the same as pg_switch_xlog except that * it wait till archiving is completed. * We call xlog functions with the oid to avoid a compile error * at old PostgreSQL. */ Datum pgpool_switch_xlog(PG_FUNCTION_ARGS) { char *archive_dir; char *filename; char path[MAXPGPATH]; struct stat fst; Datum location; text *filename_t; text *result; Oid switch_xlog_oid; Oid xlogfile_name_oid; #if !defined(PG_VERSION_NUM) || (PG_VERSION_NUM < 90400) char *pg_xlogfile_name_arg_type = "text"; #else /* * The argument data type of PG's pg_xlogfile_name() function has been * changed from text to pg_lsn since PostgreSQL 9.4 */ char *pg_xlogfile_name_arg_type = "pg_lsn"; #endif archive_dir = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(0)))); if (stat(archive_dir, &fst) < 0) #ifdef ERRCODE_INSUFFICIENT_PRIVILEGE ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", archive_dir))); #else elog(ERROR, "could not stat file \"%s\"", archive_dir); #endif switch_xlog_oid = get_function_oid("pg_switch_xlog", NULL, "pg_catalog"); xlogfile_name_oid = get_function_oid("pg_xlogfile_name", pg_xlogfile_name_arg_type, "pg_catalog"); if (!switch_xlog_oid || !xlogfile_name_oid) { /* probably PostgreSQL is 10 or greater */ switch_xlog_oid = get_function_oid("pg_switch_wal", NULL, "pg_catalog"); xlogfile_name_oid = get_function_oid("pg_walfile_name", pg_xlogfile_name_arg_type, "pg_catalog"); if (!switch_xlog_oid || !xlogfile_name_oid) elog(ERROR, "cannot find xlog functions"); } location = OidFunctionCall1(switch_xlog_oid, PointerGetDatum(NULL)); filename_t = DatumGetTextP(OidFunctionCall1(xlogfile_name_oid, location)); filename = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(filename_t))); snprintf(path, MAXPGPATH, "%s/%s", archive_dir, filename); elog(LOG, "pgpool_switch_xlog: waiting for \"%s\"", path); while (stat(path, &fst) != 0 || fst.st_size == 0 || fst.st_size % (1024 * 1024) != 0) { CHECK_FOR_INTERRUPTS(); sleep(1); } result = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(path))); PG_RETURN_TEXT_P(result); }
Datum spg_text_leaf_consistent(PG_FUNCTION_ARGS) { spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0); spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1); int level = in->level; text *leafValue, *reconstrValue = NULL; char *fullValue; int fullLen; bool res; int j; /* all tests are exact */ out->recheck = false; leafValue = DatumGetTextPP(in->leafDatum); if (DatumGetPointer(in->reconstructedValue)) reconstrValue = DatumGetTextP(in->reconstructedValue); Assert(level == 0 ? reconstrValue == NULL : VARSIZE_ANY_EXHDR(reconstrValue) == level); /* Reconstruct the full string represented by this leaf tuple */ fullLen = level + VARSIZE_ANY_EXHDR(leafValue); if (VARSIZE_ANY_EXHDR(leafValue) == 0 && level > 0) { fullValue = VARDATA(reconstrValue); out->leafValue = PointerGetDatum(reconstrValue); } else { text *fullText = palloc(VARHDRSZ + fullLen); SET_VARSIZE(fullText, VARHDRSZ + fullLen); fullValue = VARDATA(fullText); if (level) memcpy(fullValue, VARDATA(reconstrValue), level); if (VARSIZE_ANY_EXHDR(leafValue) > 0) memcpy(fullValue + level, VARDATA_ANY(leafValue), VARSIZE_ANY_EXHDR(leafValue)); out->leafValue = PointerGetDatum(fullText); } /* Perform the required comparison(s) */ res = true; for (j = 0; j < in->nkeys; j++) { StrategyNumber strategy = in->scankeys[j].sk_strategy; text *query = DatumGetTextPP(in->scankeys[j].sk_argument); int queryLen = VARSIZE_ANY_EXHDR(query); int r; if (strategy > 10) { /* Collation-aware comparison */ strategy -= 10; /* If asserts enabled, verify encoding of reconstructed string */ Assert(pg_verifymbstr(fullValue, fullLen, false)); r = varstr_cmp(fullValue, Min(queryLen, fullLen), VARDATA_ANY(query), Min(queryLen, fullLen), PG_GET_COLLATION()); } else { /* Non-collation-aware comparison */ r = memcmp(fullValue, VARDATA_ANY(query), Min(queryLen, fullLen)); } if (r == 0) { if (queryLen > fullLen) r = -1; else if (queryLen < fullLen) r = 1; } switch (strategy) { case BTLessStrategyNumber: res = (r < 0); break; case BTLessEqualStrategyNumber: res = (r <= 0); break; case BTEqualStrategyNumber: res = (r == 0); break; case BTGreaterEqualStrategyNumber: res = (r >= 0); break; case BTGreaterStrategyNumber: res = (r > 0); break; default: elog(ERROR, "unrecognized strategy number: %d", in->scankeys[j].sk_strategy); res = false; break; } if (!res) break; /* no need to consider remaining conditions */ } PG_RETURN_BOOL(res); }
Datum formatter_export(PG_FUNCTION_ARGS) { HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0); TupleDesc tupdesc; HeapTupleData tuple; int ncolumns = 0; format_t *myData; char *data; int datlen; int i; /* Must be called via the external table format manager */ if (!CALLED_AS_FORMATTER(fcinfo)) elog(ERROR, "formatter_export: not called by format manager"); tupdesc = FORMATTER_GET_TUPDESC(fcinfo); /* Get our internal description of the formatter */ ncolumns = tupdesc->natts; myData = (format_t *) FORMATTER_GET_USER_CTX(fcinfo); if (myData == NULL) { myData = palloc(sizeof(format_t)); myData->ncols = ncolumns; myData->values = palloc(sizeof(Datum) * ncolumns); myData->nulls = palloc(sizeof(bool) * ncolumns); /* Determine required buffer size */ myData->buflen = 0; for (i = 0; i < ncolumns; i++) { Oid type = tupdesc->attrs[i]->atttypid; int32 typmod = tupdesc->attrs[i]->atttypmod; /* Don't know how to format dropped columns, error for now */ if (tupdesc->attrs[i]->attisdropped) elog(ERROR, "formatter_export: dropped columns"); switch (type) { case FLOAT8OID: { myData->buflen += sizeof(double); break; } case VARCHAROID: case BPCHAROID: case TEXTOID: { myData->buflen += (typmod > 0) ? typmod : MAX_FORMAT_STRING; break; } default: { elog(ERROR, "formatter_export error: unsupported data type"); break; } } } myData->buflen = Max(128, myData->buflen); /* allocate at least 128 bytes */ myData->buffer = palloc(myData->buflen + VARHDRSZ); FORMATTER_SET_USER_CTX(fcinfo, myData); } if (myData->ncols != ncolumns) elog(ERROR, "formatter_export: unexpected change of output record type"); /* break the input tuple into fields */ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ItemPointerSetInvalid(&(tuple.t_self)); tuple.t_data = rec; heap_deform_tuple(&tuple, tupdesc, myData->values, myData->nulls); datlen = 0; data = VARDATA(myData->buffer); /* ======================================================================= * MAIN FORMATTING CODE * * Currently this code assumes: * - Homogoneos hardware => No need to convert data to network byte order * - Support for TEXT/VARCHAR/BPCHAR/FLOAT8 only * - Length Prefixed strings * - No end of record tags, checksums, or optimizations for alignment. * - NULL values are cast to some sensible default value (NaN, "") * * ======================================================================= */ for (i = 0; i < ncolumns; i++) { Oid type = tupdesc->attrs[i]->atttypid; int typmod = tupdesc->attrs[i]->atttypmod; Datum val = myData->values[i]; bool nul = myData->nulls[i]; switch (type) { case FLOAT8OID: { float8 value; if (datlen + sizeof(value) >= myData->buflen) elog(ERROR, "formatter_export: buffer too small"); if (nul) value = NULL_FLOAT8_VALUE; else value = DatumGetFloat8(val); memcpy(&data[datlen], &value, sizeof(value)); datlen += sizeof(value); break; } case TEXTOID: case VARCHAROID: case BPCHAROID: { text *str; int32 len; if (nul) { str = NULL; len = 0; } else { str = DatumGetTextP(val); len = VARSIZE(str) - VARHDRSZ; if (typmod < 0) len = Min(len, MAX_FORMAT_STRING); } if (datlen + sizeof(len) + len >= myData->buflen) elog(ERROR, "formatter_export: buffer too small"); memcpy(&data[datlen], &len, sizeof(len)); datlen += sizeof(len); if (len > 0) { memcpy(&data[datlen], VARDATA(str), len); datlen += len; } break; } default: elog(ERROR, "formatter_export: unsupported datatype"); break; } } /* ======================================================================= */ SET_VARSIZE(myData->buffer, datlen + VARHDRSZ); PG_RETURN_BYTEA_P(myData->buffer); }
/* * Transform a relation options list (list of DefElem) into the text array * format that is kept in pg_class.reloptions, including only those options * that are in the passed namescpace___. The output values do not include the * namescpace___. * * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing * reloptions value (possibly NULL), and we replace or remove entries * as needed. * * If ignoreOids is true, then we should ignore any occurrence of "oids" * in the list (it will be or has been handled by interpretOidsOption()). * * Note that this is not responsible for determining whether the options * are valid, but it does check that namespaces for all the options given are * listed in validnsps. The NULL namescpace___ is always valid and need not be * explicitly listed. Passing a NULL pointer means that only the NULL * namescpace___ is valid. * * Both oldOptions and the result are text arrays (or NULL for "default"), * but we declare them as Datums to avoid including array.h in reloptions.h. */ Datum transformRelOptions(Datum oldOptions, List *defList, char *namspace, char *validnsps[], bool ignoreOids, bool isReset) { Datum result; ArrayBuildState *astate; ListCell *cell; /* no change if empty list */ if (defList == NIL) return oldOptions; /* We build new___ array using accumArrayResult */ astate = NULL; /* Copy any oldOptions that aren't to be replaced */ if (PointerIsValid(DatumGetPointer(oldOptions))) { ArrayType *array = DatumGetArrayTypeP(oldOptions); Datum *oldoptions; int noldoptions; int i; deconstruct_array(array, TEXTOID, -1, false, 'i', &oldoptions, NULL, &noldoptions); for (i = 0; i < noldoptions; i++) { text *oldoption = DatumGetTextP(oldoptions[i]); char *text_str = VARDATA(oldoption); int text_len = VARSIZE(oldoption) - VARHDRSZ; /* Search for a match in defList */ foreach(cell, defList) { DefElem *def = (DefElem *) lfirst(cell); int kw_len; /* ignore if not in the same namescpace___ */ if (namspace == NULL) { if (def->defnamespace != NULL) continue; } else if (def->defnamespace == NULL) continue; else if (pg_strcasecmp(def->defnamespace, namspace) != 0) continue; kw_len = strlen(def->defname); if (text_len > kw_len && text_str[kw_len] == '=' && pg_strncasecmp(text_str, def->defname, kw_len) == 0) break; } if (!cell) { /* No match, so keep old option */ astate = accumArrayResult(astate, oldoptions[i], false, TEXTOID, CurrentMemoryContext); } } }
/* * Convert a HeapTuple into a byte-sequence, and store it directly * into a chunklist for transmission. * * This code is based on the printtup_internal_20() function in printtup.c. */ void SerializeTupleIntoChunks(HeapTuple tuple, SerTupInfo * pSerInfo, TupleChunkList tcList) { TupleChunkListItem tcItem = NULL; MemoryContext oldCtxt; TupleDesc tupdesc; int i, natts; bool fHandled; AssertArg(tcList != NULL); AssertArg(tuple != NULL); AssertArg(pSerInfo != NULL); tupdesc = pSerInfo->tupdesc; natts = tupdesc->natts; /* get ready to go */ tcList->p_first = NULL; tcList->p_last = NULL; tcList->num_chunks = 0; tcList->serialized_data_length = 0; tcList->max_chunk_length = Gp_max_tuple_chunk_size; if (natts == 0) { tcItem = getChunkFromCache(&pSerInfo->chunkCache); if (tcItem == NULL) { ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Could not allocate space for first chunk item in new chunk list."))); } /* TC_EMTPY is just one chunk */ SetChunkType(tcItem->chunk_data, TC_EMPTY); tcItem->chunk_length = TUPLE_CHUNK_HEADER_SIZE; appendChunkToTCList(tcList, tcItem); return; } tcItem = getChunkFromCache(&pSerInfo->chunkCache); if (tcItem == NULL) { ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Could not allocate space for first chunk item in new chunk list."))); } /* assume that we'll take a single chunk */ SetChunkType(tcItem->chunk_data, TC_WHOLE); tcItem->chunk_length = TUPLE_CHUNK_HEADER_SIZE; appendChunkToTCList(tcList, tcItem); AssertState(s_tupSerMemCtxt != NULL); if (is_heaptuple_memtuple(tuple)) { addByteStringToChunkList(tcList, (char *)tuple, memtuple_get_size((MemTuple)tuple, NULL), &pSerInfo->chunkCache); addPadding(tcList, &pSerInfo->chunkCache, memtuple_get_size((MemTuple)tuple, NULL)); } else { TupSerHeader tsh; unsigned int datalen; unsigned int nullslen; HeapTupleHeader t_data = tuple->t_data; 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) + datalen; tsh.natts = HeapTupleHeaderGetNatts(t_data); tsh.infomask = t_data->t_infomask; addByteStringToChunkList(tcList, (char *)&tsh, sizeof(TupSerHeader), &pSerInfo->chunkCache); /* If we don't have any attributes which have been toasted, we * can be very very simple: just send the raw data. */ if ((tsh.infomask & HEAP_HASEXTERNAL) == 0) { if (nullslen) { addByteStringToChunkList(tcList, (char *)t_data->t_bits, nullslen, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,nullslen); } addByteStringToChunkList(tcList, (char *)t_data + t_data->t_hoff, datalen, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,datalen); } else { /* We have to be more careful when we have tuples that * have been toasted. Ideally we'd like to send the * untoasted attributes in as "raw" a format as possible * but that makes rebuilding the tuple harder . */ oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt); /* deconstruct the tuple (faster than a heap_getattr loop) */ heap_deform_tuple(tuple, tupdesc, pSerInfo->values, pSerInfo->nulls); MemoryContextSwitchTo(oldCtxt); /* Send the nulls character-array. */ addByteStringToChunkList(tcList, pSerInfo->nulls, natts, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,natts); /* * send the attributes of this tuple: NOTE anything which allocates * temporary space (e.g. could result in a PG_DETOAST_DATUM) should be * executed with the memory context set to s_tupSerMemCtxt */ for (i = 0; i < natts; ++i) { SerAttrInfo *attrInfo = pSerInfo->myinfo + i; Datum origattr = pSerInfo->values[i], attr; bytea *outputbytes=0; /* skip null attributes (already taken care of above) */ if (pSerInfo->nulls[i]) continue; /* * If we have a toasted datum, forcibly detoast it here to avoid * memory leakage: we want to force the detoast allocation(s) to * happen in our reset-able serialization context. */ if (attrInfo->typisvarlena) { oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt); /* we want to detoast but leave compressed, if * possible, but we have to handle varlena * attributes (and others ?) differently than we * currently do (first step is to use * heap_tuple_fetch_attr() instead of * PG_DETOAST_DATUM()). */ attr = PointerGetDatum(PG_DETOAST_DATUM(origattr)); MemoryContextSwitchTo(oldCtxt); } else attr = origattr; /* * 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: addInt32ToChunkList(tcList, DatumGetInt32(attr), &pSerInfo->chunkCache); break; case CHAROID: addCharToChunkList(tcList, DatumGetChar(attr), &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,1); 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 = DatumGetTextP(attr); int32 textSize = VARSIZE(pText) - VARHDRSZ; addInt32ToChunkList(tcList, textSize, &pSerInfo->chunkCache); addByteStringToChunkList(tcList, (char *) VARDATA(pText), textSize, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,textSize); break; } case DATEOID: { DateADT date = DatumGetDateADT(attr); addByteStringToChunkList(tcList, (char *) &date, sizeof(DateADT), &pSerInfo->chunkCache); 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 = DatumGetNumeric(attr); int32 numSize = VARSIZE(num) - VARHDRSZ; addInt32ToChunkList(tcList, numSize, &pSerInfo->chunkCache); addByteStringToChunkList(tcList, (char *) VARDATA(num), numSize, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,numSize); break; } case ACLITEMOID: { AclItem *aip = DatumGetAclItemP(attr); char *outputstring; int32 aclSize ; outputstring = DatumGetCString(DirectFunctionCall1(aclitemout, PointerGetDatum(aip))); aclSize = strlen(outputstring); addInt32ToChunkList(tcList, aclSize, &pSerInfo->chunkCache); addByteStringToChunkList(tcList, outputstring,aclSize, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,aclSize); break; } case 210: /* storage manager */ { char *smgrstr; int32 strsize; smgrstr = DatumGetCString(DirectFunctionCall1(smgrout, 0)); strsize = strlen(smgrstr); addInt32ToChunkList(tcList, strsize, &pSerInfo->chunkCache); addByteStringToChunkList(tcList, smgrstr, strsize, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,strsize); break; } default: fHandled = false; } if (fHandled) continue; /* * the FunctionCall2 call into the send function may result in some * allocations which we'd like to have contained by our reset-able * context */ oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt); /* Call the attribute type's binary input converter. */ if (attrInfo->send_finfo.fn_nargs == 1) outputbytes = DatumGetByteaP(FunctionCall1(&attrInfo->send_finfo, attr)); else if (attrInfo->send_finfo.fn_nargs == 2) outputbytes = DatumGetByteaP(FunctionCall2(&attrInfo->send_finfo, attr, ObjectIdGetDatum(attrInfo->send_typio_param))); else if (attrInfo->send_finfo.fn_nargs == 3) outputbytes = DatumGetByteaP(FunctionCall3(&attrInfo->send_finfo, attr, ObjectIdGetDatum(attrInfo->send_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))); } MemoryContextSwitchTo(oldCtxt); /* We assume the result will not have been toasted */ addInt32ToChunkList(tcList, VARSIZE(outputbytes) - VARHDRSZ, &pSerInfo->chunkCache); addByteStringToChunkList(tcList, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ, &pSerInfo->chunkCache); addPadding(tcList,&pSerInfo->chunkCache,VARSIZE(outputbytes) - VARHDRSZ); /* * this was allocated in our reset-able context, but we *are* done * with it; and for tuples with several large columns it'd be nice to * free the memory back to the context */ pfree(outputbytes); } MemoryContextReset(s_tupSerMemCtxt); } } /* * if we have more than 1 chunk we have to set the chunk types on our * first chunk and last chunk */ if (tcList->num_chunks > 1) { TupleChunkListItem first, last; first = tcList->p_first; last = tcList->p_last; Assert(first != NULL); Assert(first != last); Assert(last != NULL); SetChunkType(first->chunk_data, TC_PARTIAL_START); SetChunkType(last->chunk_data, TC_PARTIAL_END); /* * any intervening chunks are already set to TC_PARTIAL_MID when * allocated */ } return; }
/* * variant_get_variant_name: Return the name of a named variant */ char * variant_get_variant_name(int typmod, Oid org_typid, bool ignore_storage) { Datum values[2]; MemoryContext cctx = CurrentMemoryContext; bool do_pop = _SPI_conn(); bool isnull; Oid types[2] = {INT4OID, REGTYPEOID}; char *cmd; int nargs; int ret; Datum result; char *out; values[0] = Int32GetDatum(typmod); /* * There's a race condition here; someone could be attempting to remove an * allowed type from this registered variant or even remove it entirely. We * could avoid that by taking a share/keyshare lock here and taking the * appropriate blocking lock when modifying the registration record. Doing * that would probably be quite bad though; not only are type IO and typmod * IO routines assumed to be non-volatile, taking such a lock would end up * generating a lot of lock updates to the registration rows. * * Since the whole purpose of registration is to handle the issue of someone * attempting to drop a type that has made it into a variant in a table * column, which we can't completely handle anyway, I don't think it's worth * it to lock the rows. */ if(ignore_storage) { cmd = "SELECT variant_name, variant_enabled, storage_allowed FROM variant._registered WHERE variant_typmod = $1"; nargs = 1; } else { cmd = "SELECT variant_name, variant_enabled, storage_allowed, allowed_types @> array[ $2 ] FROM variant._registered WHERE variant_typmod = $1"; nargs = 2; values[1] = ObjectIdGetDatum(org_typid); } /* command, nargs, Oid *argument_types, *values, *nulls, read_only, count */ if( (ret = SPI_execute_with_args( cmd, nargs, types, values, " ", true, 0 )) != SPI_OK_SELECT ) elog( ERROR, "SPI_execute_with_args(%s) returned %s", cmd, SPI_result_code_string(ret)); Assert( SPI_tuptable ); if ( SPI_processed > 1 ) ereport(ERROR, ( errmsg( "Got %u records for variant typmod %i", SPI_processed, typmod ), errhint( "This means _variant._registered is corrupted" ) ) ); if ( SPI_processed < 1 ) elog( ERROR, "invalid typmod %i", typmod ); /* Note 0 vs 1 based numbering */ Assert(SPI_tuptable->tupdesc->attrs[0]->atttypid == VARCHAROID); Assert(SPI_tuptable->tupdesc->attrs[1]->atttypid == BOOLOID); Assert(SPI_tuptable->tupdesc->attrs[2]->atttypid == BOOLOID); result = heap_getattr( SPI_tuptable->vals[0], 1, SPI_tuptable->tupdesc, &isnull ); if( isnull ) ereport( ERROR, ( errmsg( "Found NULL variant_name for typmod %i", typmod ), errhint( "This should never happen; is _variant._registered corrupted?" ) ) ); MemoryContextSwitchTo(cctx); out = text_to_cstring(DatumGetTextP(result)); result = heap_getattr( SPI_tuptable->vals[0], 2, SPI_tuptable->tupdesc, &isnull ); if( !DatumGetBool(result) ) ereport( ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg( "variant.variant(%s) is disabled", out ) ) ); /* * If storage is allowed, then throw an error if we don't know what our * original type is, or if that type is not listed as allowed. */ if(!ignore_storage) { result = heap_getattr( SPI_tuptable->vals[0], 3, SPI_tuptable->tupdesc, &isnull ); if( DatumGetBool(result) ) { if( org_typid == InvalidOid) ereport( ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg( "Unable to determine original type" ) ) ); result = heap_getattr( SPI_tuptable->vals[0], 4, SPI_tuptable->tupdesc, &isnull ); if( !DatumGetBool(result) ) ereport( ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg( "type %s is not allowed in variant.variant(%s)", format_type_be(org_typid), out ), errhint( "you can permanently allow a type to be used by calling variant.allow_type()" ) ) ); } } _SPI_disc(do_pop); /* pfree's all SPI stuff */ return out; }
static Datum tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column) { TriggerData *trigdata; Trigger *trigger; Relation rel; HeapTuple rettuple = NULL; int tsvector_attr_num, i; ParsedText prs; Datum datum; bool isnull; text *txt; Oid cfgId; /* Check call context */ if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */ elog(ERROR, "tsvector_update_trigger: not fired by trigger manager"); trigdata = (TriggerData *) fcinfo->context; if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) elog(ERROR, "tsvector_update_trigger: must be fired for row"); if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event)) elog(ERROR, "tsvector_update_trigger: must be fired BEFORE event"); if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) rettuple = trigdata->tg_trigtuple; else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) rettuple = trigdata->tg_newtuple; else elog(ERROR, "tsvector_update_trigger: must be fired for INSERT or UPDATE"); trigger = trigdata->tg_trigger; rel = trigdata->tg_relation; if (trigger->tgnargs < 3) elog(ERROR, "tsvector_update_trigger: arguments must be tsvector_field, ts_config, text_field1, ...)"); /* Find the target tsvector column */ tsvector_attr_num = SPI_fnumber(rel->rd_att, trigger->tgargs[0]); if (tsvector_attr_num == SPI_ERROR_NOATTRIBUTE) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("tsvector column \"%s\" does not exist", trigger->tgargs[0]))); if (!IsBinaryCoercible(SPI_gettypeid(rel->rd_att, tsvector_attr_num), TSVECTOROID)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" is not of tsvector type", trigger->tgargs[0]))); /* Find the configuration to use */ if (config_column) { int config_attr_num; config_attr_num = SPI_fnumber(rel->rd_att, trigger->tgargs[1]); if (config_attr_num == SPI_ERROR_NOATTRIBUTE) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("configuration column \"%s\" does not exist", trigger->tgargs[1]))); if (!IsBinaryCoercible(SPI_gettypeid(rel->rd_att, config_attr_num), REGCONFIGOID)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" is not of regconfig type", trigger->tgargs[1]))); datum = SPI_getbinval(rettuple, rel->rd_att, config_attr_num, &isnull); if (isnull) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("configuration column \"%s\" must not be null", trigger->tgargs[1]))); cfgId = DatumGetObjectId(datum); } else { List *names; names = stringToQualifiedNameList(trigger->tgargs[1]); /* require a schema so that results are not search path dependent */ if (list_length(names) < 2) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("text search configuration name \"%s\" must be schema-qualified", trigger->tgargs[1]))); cfgId = get_ts_config_oid(names, false); } /* initialize parse state */ prs.lenwords = 32; prs.curwords = 0; prs.pos = 0; prs.words = (ParsedWord *) palloc(sizeof(ParsedWord) * prs.lenwords); /* find all words in indexable column(s) */ for (i = 2; i < trigger->tgnargs; i++) { int numattr; numattr = SPI_fnumber(rel->rd_att, trigger->tgargs[i]); if (numattr == SPI_ERROR_NOATTRIBUTE) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" does not exist", trigger->tgargs[i]))); if (!IsBinaryCoercible(SPI_gettypeid(rel->rd_att, numattr), TEXTOID)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" is not of a character type", trigger->tgargs[i]))); datum = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull); if (isnull) continue; txt = DatumGetTextP(datum); parsetext(cfgId, &prs, VARDATA(txt), VARSIZE(txt) - VARHDRSZ); if (txt != (text *) DatumGetPointer(datum)) pfree(txt); } /* make tsvector value */ if (prs.curwords) { datum = PointerGetDatum(make_tsvector(&prs)); rettuple = SPI_modifytuple(rel, rettuple, 1, &tsvector_attr_num, &datum, NULL); pfree(DatumGetPointer(datum)); } else { TSVector out = palloc(CALCDATASIZE(0, 0)); SET_VARSIZE(out, CALCDATASIZE(0, 0)); out->size = 0; datum = PointerGetDatum(out); rettuple = SPI_modifytuple(rel, rettuple, 1, &tsvector_attr_num, &datum, NULL); pfree(prs.words); } if (rettuple == NULL) /* internal error */ elog(ERROR, "tsvector_update_trigger: %d returned by SPI_modifytuple", SPI_result); return PointerGetDatum(rettuple); }
/* * Turn a Datum into jsonb, adding it to the result JsonbInState. * * tcategory and outfuncoid are from a previous call to json_categorize_type, * except that if is_null is true then they can be invalid. * * If key_scalar is true, the value is stored as a key, so insist * it's of an acceptable type, and force it to be a jbvString. */ static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, JsonbTypeCategory tcategory, Oid outfuncoid, bool key_scalar) { char *outputstr; bool numeric_error; JsonbValue jb; bool scalar_jsonb = false; check_stack_depth(); /* Convert val to a JsonbValue in jb (in most cases) */ if (is_null) { Assert(!key_scalar); jb.type = jbvNull; } else if (key_scalar && (tcategory == JSONBTYPE_ARRAY || tcategory == JSONBTYPE_COMPOSITE || tcategory == JSONBTYPE_JSON || tcategory == JSONBTYPE_JSONB || tcategory == JSONBTYPE_JSONCAST)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("key value must be scalar, not array, composite, or json"))); } else { if (tcategory == JSONBTYPE_JSONCAST) val = OidFunctionCall1(outfuncoid, val); switch (tcategory) { case JSONBTYPE_ARRAY: array_to_jsonb_internal(val, result); break; case JSONBTYPE_COMPOSITE: composite_to_jsonb(val, result); break; case JSONBTYPE_BOOL: if (key_scalar) { outputstr = DatumGetBool(val) ? "true" : "false"; jb.type = jbvString; jb.val.string.len = strlen(outputstr); jb.val.string.val = outputstr; } else { jb.type = jbvBool; jb.val.boolean = DatumGetBool(val); } break; case JSONBTYPE_NUMERIC: outputstr = OidOutputFunctionCall(outfuncoid, val); if (key_scalar) { /* always quote keys */ jb.type = jbvString; jb.val.string.len = strlen(outputstr); jb.val.string.val = outputstr; } else { /* * Make it numeric if it's a valid JSON number, otherwise * a string. Invalid numeric output will always have an * 'N' or 'n' in it (I think). */ numeric_error = (strchr(outputstr, 'N') != NULL || strchr(outputstr, 'n') != NULL); if (!numeric_error) { jb.type = jbvNumeric; jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1)); pfree(outputstr); } else { jb.type = jbvString; jb.val.string.len = strlen(outputstr); jb.val.string.val = outputstr; } } break; case JSONBTYPE_DATE: { DateADT date; struct pg_tm tm; char buf[MAXDATELEN + 1]; date = DatumGetDateADT(val); /* Same as date_out(), but forcing DateStyle */ if (DATE_NOT_FINITE(date)) EncodeSpecialDate(date, buf); else { j2date(date + POSTGRES_EPOCH_JDATE, &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday)); EncodeDateOnly(&tm, USE_XSD_DATES, buf); } jb.type = jbvString; jb.val.string.len = strlen(buf); jb.val.string.val = pstrdup(buf); } break; case JSONBTYPE_TIMESTAMP: { Timestamp timestamp; struct pg_tm tm; fsec_t fsec; char buf[MAXDATELEN + 1]; timestamp = DatumGetTimestamp(val); /* Same as timestamp_out(), but forcing DateStyle */ if (TIMESTAMP_NOT_FINITE(timestamp)) EncodeSpecialTimestamp(timestamp, buf); else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0) EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf); else ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); jb.type = jbvString; jb.val.string.len = strlen(buf); jb.val.string.val = pstrdup(buf); } break; case JSONBTYPE_TIMESTAMPTZ: { TimestampTz timestamp; struct pg_tm tm; int tz; fsec_t fsec; const char *tzn = NULL; char buf[MAXDATELEN + 1]; timestamp = DatumGetTimestampTz(val); /* Same as timestamptz_out(), but forcing DateStyle */ if (TIMESTAMP_NOT_FINITE(timestamp)) EncodeSpecialTimestamp(timestamp, buf); else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0) EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf); else ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); jb.type = jbvString; jb.val.string.len = strlen(buf); jb.val.string.val = pstrdup(buf); } break; case JSONBTYPE_JSONCAST: case JSONBTYPE_JSON: { /* parse the json right into the existing result object */ JsonLexContext *lex; JsonSemAction sem; text *json = DatumGetTextP(val); lex = makeJsonLexContext(json, true); memset(&sem, 0, sizeof(sem)); sem.semstate = (void *) result; sem.object_start = jsonb_in_object_start; sem.array_start = jsonb_in_array_start; sem.object_end = jsonb_in_object_end; sem.array_end = jsonb_in_array_end; sem.scalar = jsonb_in_scalar; sem.object_field_start = jsonb_in_object_field_start; pg_parse_json(lex, &sem); } break; case JSONBTYPE_JSONB: { Jsonb *jsonb = DatumGetJsonb(val); JsonbIterator *it; it = JsonbIteratorInit(&jsonb->root); if (JB_ROOT_IS_SCALAR(jsonb)) { (void) JsonbIteratorNext(&it, &jb, true); Assert(jb.type == jbvArray); (void) JsonbIteratorNext(&it, &jb, true); scalar_jsonb = true; } else { JsonbIteratorToken type; while ((type = JsonbIteratorNext(&it, &jb, false)) != WJB_DONE) { if (type == WJB_END_ARRAY || type == WJB_END_OBJECT || type == WJB_BEGIN_ARRAY || type == WJB_BEGIN_OBJECT) result->res = pushJsonbValue(&result->parseState, type, NULL); else result->res = pushJsonbValue(&result->parseState, type, &jb); } } } break; default: outputstr = OidOutputFunctionCall(outfuncoid, val); jb.type = jbvString; jb.val.string.len = checkStringLen(strlen(outputstr)); jb.val.string.val = outputstr; break; } } /* Now insert jb into result, unless we did it recursively */ if (!is_null && !scalar_jsonb && tcategory >= JSONBTYPE_JSON && tcategory <= JSONBTYPE_JSONCAST) { /* work has been done recursively */ return; } else if (result->parseState == NULL) { /* single root scalar */ JsonbValue va; va.type = jbvArray; va.val.array.rawScalar = true; va.val.array.nElems = 1; result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, &va); result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb); result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL); } else { JsonbValue *o = &result->parseState->contVal; switch (o->type) { case jbvArray: result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb); break; case jbvObject: result->res = pushJsonbValue(&result->parseState, key_scalar ? WJB_KEY : WJB_VALUE, &jb); break; default: elog(ERROR, "unexpected parent of nested structure"); } } }