/* * Compute the difference in bytes between two WAL locations. */ Datum pg_xlog_location_diff(PG_FUNCTION_ARGS) { text *location1 = PG_GETARG_TEXT_P(0); text *location2 = PG_GETARG_TEXT_P(1); char *str1, *str2; XLogRecPtr loc1, loc2; Numeric result; /* * Read and parse input */ str1 = text_to_cstring(location1); str2 = text_to_cstring(location2); validate_xlog_location(str1); validate_xlog_location(str2); if (sscanf(str1, "%X/%X", &loc1.xlogid, &loc1.xrecoff) != 2) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not parse transaction log location \"%s\"", str1))); if (sscanf(str2, "%X/%X", &loc2.xlogid, &loc2.xrecoff) != 2) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not parse transaction log location \"%s\"", str2))); /* * Sanity check */ if (loc1.xrecoff > XLogFileSize) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("xrecoff \"%X\" is out of valid range, 0..%X", loc1.xrecoff, XLogFileSize))); if (loc2.xrecoff > XLogFileSize) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("xrecoff \"%X\" is out of valid range, 0..%X", loc2.xrecoff, XLogFileSize))); /* * result = XLogFileSize * (xlogid1 - xlogid2) + xrecoff1 - xrecoff2 */ result = DatumGetNumeric(DirectFunctionCall2(numeric_sub, DirectFunctionCall1(int8_numeric, Int64GetDatum((int64) loc1.xlogid)), DirectFunctionCall1(int8_numeric, Int64GetDatum((int64) loc2.xlogid)))); result = DatumGetNumeric(DirectFunctionCall2(numeric_mul, DirectFunctionCall1(int8_numeric, Int64GetDatum((int64) XLogFileSize)), NumericGetDatum(result))); result = DatumGetNumeric(DirectFunctionCall2(numeric_add, NumericGetDatum(result), DirectFunctionCall1(int8_numeric, Int64GetDatum((int64) loc1.xrecoff)))); result = DatumGetNumeric(DirectFunctionCall2(numeric_sub, NumericGetDatum(result), DirectFunctionCall1(int8_numeric, Int64GetDatum((int64) loc2.xrecoff)))); PG_RETURN_NUMERIC(result); }
/* cash_numeric() * Convert cash to numeric. */ Datum cash_numeric(PG_FUNCTION_ARGS) { Cash money = PG_GETARG_CASH(0); Numeric result; int fpoint; int64 scale; int i; Datum amount; Datum numeric_scale; Datum quotient; struct lconv *lconvert = PGLC_localeconv(); /* see comments about frac_digits in cash_in() */ fpoint = lconvert->frac_digits; if (fpoint < 0 || fpoint > 10) fpoint = 2; /* compute required scale factor */ scale = 1; for (i = 0; i < fpoint; i++) scale *= 10; /* form the result as money / scale */ amount = DirectFunctionCall1(int8_numeric, Int64GetDatum(money)); numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale)); quotient = DirectFunctionCall2(numeric_div, amount, numeric_scale); /* forcibly round to exactly the intended number of digits */ result = DatumGetNumeric(DirectFunctionCall2(numeric_round, quotient, Int32GetDatum(fpoint))); PG_RETURN_NUMERIC(result); }
JsonbValue* make_jsonb_numeric_value(double value) { JsonbValue *jvalue = (JsonbValue*)palloc(sizeof(JsonbValue)); memset(jvalue, 0, sizeof(JsonbValue)); Datum d_num_value = Float8GetDatum(value); Numeric num_value = DatumGetNumeric(DirectFunctionCall1(float8_numeric, d_num_value)); jvalue->type = jbvNumeric; jvalue->val.numeric = num_value; return jvalue; }
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)); }
Datum gbt_numeric_consistent(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); void *query = (void *) DatumGetNumeric(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; retval = gbt_var_consistent(&r, query, &strategy, GIST_LEAF(entry), &tinfo); PG_RETURN_BOOL(retval); }
/* * PLyNumber_ToJsonbValue(PyObject *obj) * * Transform python number to JsonbValue. */ static JsonbValue * PLyNumber_ToJsonbValue(PyObject *obj, JsonbValue *jbvNum) { Numeric num; char *str = PLyObject_AsString(obj); PG_TRY(); { Datum numd; numd = DirectFunctionCall3(numeric_in, CStringGetDatum(str), ObjectIdGetDatum(InvalidOid), Int32GetDatum(-1)); num = DatumGetNumeric(numd); } PG_CATCH(); { ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), (errmsg("could not convert value \"%s\" to jsonb", str)))); } PG_END_TRY(); pfree(str); /* * jsonb doesn't allow NaN (per JSON specification), so we have to prevent * it here explicitly. (Infinity is also not allowed in jsonb, but * numeric_in above already catches that.) */ if (numeric_is_nan(num)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errmsg("cannot convert NaN to jsonb")))); jbvNum->type = jbvNumeric; jbvNum->val.numeric = num; return jbvNum; }
Datum pgstrom_numeric_avg_final(PG_FUNCTION_ARGS) { numeric_agg_state *state; Datum vN; Datum result; state = PG_ARGISNULL(0) ? NULL : (numeric_agg_state *)PG_GETARG_POINTER(0); /* If there were no non-null inputs, return NULL */ if (state == NULL || state->N == 0) PG_RETURN_NULL(); /* If any NaN value is accumlated, return NaN */ if (numeric_is_nan(DatumGetNumeric(state->sumX))) PG_RETURN_NUMERIC(state->sumX); vN = DirectFunctionCall1(int8_numeric, Int64GetDatum(state->N)); result = DirectFunctionCall2(numeric_div, state->sumX, vN); PG_RETURN_NUMERIC(result); }
Datum orafce_to_number(PG_FUNCTION_ARGS) { text *arg0 = PG_GETARG_TEXT_PP(0); char *buf; struct lconv *lconv = PGLC_localeconv(); Numeric res; char *p; buf = text_to_cstring(arg0); for (p = buf; *p; p++) if (*p == lconv->decimal_point[0] && lconv->decimal_point[0]) *p = '.'; else if (*p == lconv->thousands_sep[0] && lconv->thousands_sep[0]) *p = ','; res = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(buf), 0, -1)); PG_RETURN_NUMERIC(res); }
Datum hstore_to_jsonb_loose(PG_FUNCTION_ARGS) { HStore *in = PG_GETARG_HS(0); int i; int count = HS_COUNT(in); char *base = STRPTR(in); HEntry *entries = ARRPTR(in); JsonbParseState *state = NULL; JsonbValue *res; StringInfoData tmp; bool is_number; initStringInfo(&tmp); res = pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL); for (i = 0; i < count; i++) { JsonbValue key, val; key.estSize = sizeof(JEntry); key.type = jbvString; key.val.string.len = HS_KEYLEN(entries, i); key.val.string.val = pnstrdup(HS_KEY(entries, base, i), key.val.string.len); key.estSize += key.val.string.len; res = pushJsonbValue(&state, WJB_KEY, &key); val.estSize = sizeof(JEntry); if (HS_VALISNULL(entries, i)) { val.type = jbvNull; } /* guess that values of 't' or 'f' are booleans */ else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 't') { val.type = jbvBool; val.val.boolean = true; } else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 'f') { val.type = jbvBool; val.val.boolean = false; } else { is_number = false; resetStringInfo(&tmp); appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); /* * don't treat something with a leading zero followed by another * digit as numeric - could be a zip code or similar */ if (tmp.len > 0 && !(tmp.data[0] == '0' && isdigit((unsigned char) tmp.data[1])) && strspn(tmp.data, "+-0123456789Ee.") == tmp.len) { /* * might be a number. See if we can input it as a numeric * value. Ignore any actual parsed value. */ char *endptr = "junk"; long lval; lval = strtol(tmp.data, &endptr, 10); (void) lval; if (*endptr == '\0') { /* * strol man page says this means the whole string is * valid */ is_number = true; } else { /* not an int - try a double */ double dval; dval = strtod(tmp.data, &endptr); (void) dval; if (*endptr == '\0') is_number = true; } } if (is_number) { val.type = jbvNumeric; val.val.numeric = DatumGetNumeric( DirectFunctionCall3(numeric_in, CStringGetDatum(tmp.data), 0, -1)); val.estSize += VARSIZE_ANY(val.val.numeric) +sizeof(JEntry); } else { val.estSize = sizeof(JEntry); val.type = jbvString; val.val.string.len = HS_VALLEN(entries, i); val.val.string.val = pnstrdup(HS_VAL(entries, base, i), val.val.string.len); val.estSize += val.val.string.len; } } res = pushJsonbValue(&state, WJB_VALUE, &val); } res = pushJsonbValue(&state, WJB_END_OBJECT, NULL); PG_RETURN_POINTER(JsonbValueToJsonb(res)); }
static Numeric pgstrom_numeric_stddev_internal(numeric_agg_state *state, bool variance, bool sample) { Datum vZero; Datum vN; Datum vN2; Datum vSumX; Datum vSumX2; Datum result; if (state == NULL) return NULL; /* NaN checks */ if (numeric_is_nan(DatumGetNumeric(state->sumX))) return DatumGetNumeric(state->sumX); if (numeric_is_nan(DatumGetNumeric(state->sumX2))) return DatumGetNumeric(state->sumX2); /* * Sample stddev and variance are undefined when N <= 1; population stddev * is undefined when N == 0. Return NULL in either case. */ if (sample ? state->N <= 1 : state->N <= 0) return NULL; /* const_zero = (Numeric)0 */ vZero = DirectFunctionCall3(numeric_in, CStringGetDatum("0"), ObjectIdGetDatum(0), Int32GetDatum(-1)); /* vN = (Numeric)N */ vN = DirectFunctionCall1(int8_numeric, Int64GetDatum(state->N)); /* vsumX = sumX * sumX */ vSumX = DirectFunctionCall2(numeric_mul, state->sumX, state->sumX); /* vsumX2 = N * sumX2 */ vSumX2 = DirectFunctionCall2(numeric_mul, state->sumX2, vN); /* N * sumX2 - sumX * sumX */ vSumX2 = DirectFunctionCall2(numeric_sub, vSumX2, vSumX); /* Watch out for roundoff error producing a negative numerator */ if (DirectFunctionCall2(numeric_cmp, vSumX2, vZero) <= 0) return DatumGetNumeric(vZero); if (!sample) vN2 = DirectFunctionCall2(numeric_mul, vN, vN); /* N * N */ else { Datum vOne; Datum vNminus; vOne = DirectFunctionCall3(numeric_in, CStringGetDatum("1"), ObjectIdGetDatum(0), Int32GetDatum(-1)); vNminus = DirectFunctionCall2(numeric_sub, vN, vOne); vN2 = DirectFunctionCall2(numeric_mul, vN, vNminus); /* N * (N - 1) */ } /* variance */ result = DirectFunctionCall2(numeric_div, vSumX2, vN2); /* stddev? */ if (!variance) result = DirectFunctionCall1(numeric_sqrt, result); return DatumGetNumeric(result); }
/* * 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; }
/* * For jsonb we always want the de-escaped value - that's what's in token */ static void jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype) { JsonbInState *_state = (JsonbInState *) pstate; JsonbValue v; v.estSize = sizeof(JEntry); switch (tokentype) { case JSON_TOKEN_STRING: Assert(token != NULL); v.type = jbvString; v.val.string.len = checkStringLen(strlen(token)); v.val.string.val = pnstrdup(token, v.val.string.len); v.estSize += v.val.string.len; break; case JSON_TOKEN_NUMBER: /* * No need to check size of numeric values, because maximum * numeric size is well below the JsonbValue restriction */ Assert(token != NULL); v.type = jbvNumeric; v.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1)); v.estSize += VARSIZE_ANY(v.val.numeric) +sizeof(JEntry) /* alignment */ ; break; case JSON_TOKEN_TRUE: v.type = jbvBool; v.val.boolean = true; break; case JSON_TOKEN_FALSE: v.type = jbvBool; v.val.boolean = false; break; case JSON_TOKEN_NULL: v.type = jbvNull; break; default: /* should not be possible */ elog(ERROR, "invalid json token type"); break; } if (_state->parseState == NULL) { /* single scalar */ JsonbValue va; va.type = jbvArray; va.val.array.rawScalar = true; va.val.array.nElems = 1; _state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_ARRAY, &va); _state->res = pushJsonbValue(&_state->parseState, WJB_ELEM, &v); _state->res = pushJsonbValue(&_state->parseState, WJB_END_ARRAY, NULL); } else { JsonbValue *o = &_state->parseState->contVal; switch (o->type) { case jbvArray: _state->res = pushJsonbValue(&_state->parseState, WJB_ELEM, &v); break; case jbvObject: _state->res = pushJsonbValue(&_state->parseState, WJB_VALUE, &v); break; default: elog(ERROR, "unexpected parent of nested structure"); } } }
static bool executeExpr(JsQueryItem *jsq, int32 op, JsonbValue *jb, JsQueryItem *jsqLeftArg) { bool res = false; /* * read arg type */ Assert(jsqGetNext(jsq, NULL) == false); Assert(jsq->type == jqiAny || jsq->type == jqiString || jsq->type == jqiNumeric || jsq->type == jqiNull || jsq->type == jqiBool || jsq->type == jqiArray); if (jsqLeftArg && jsqLeftArg->type == jqiLength) { if (JsonbType(jb) == jbvArray || JsonbType(jb) == jbvObject) { int32 length; JsonbIterator *it; JsonbValue v; int r; it = JsonbIteratorInit(jb->val.binary.data); r = JsonbIteratorNext(&it, &v, true); Assert(r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT); length = (r == WJB_BEGIN_ARRAY) ? v.val.array.nElems : v.val.object.nPairs; v.type = jbvNumeric; v.val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(length))); switch(op) { case jqiEqual: case jqiLess: case jqiGreater: case jqiLessOrEqual: case jqiGreaterOrEqual: res = makeCompare(jsq, op, &v); break; case jqiIn: res = checkScalarIn(jsq, &v); break; case jqiOverlap: case jqiContains: case jqiContained: break; default: elog(ERROR, "Unknown operation"); } } } else { switch(op) { case jqiEqual: if (JsonbType(jb) == jbvArray && jsq->type == jqiArray) res = checkArrayEquality(jsq, jb); else res = checkScalarEquality(jsq, jb); break; case jqiIn: res = checkScalarIn(jsq, jb); break; case jqiOverlap: case jqiContains: case jqiContained: res = executeArrayOp(jsq, op, jb); break; case jqiLess: case jqiGreater: case jqiLessOrEqual: case jqiGreaterOrEqual: res = makeCompare(jsq, op, jb); break; default: elog(ERROR, "Unknown operation"); } } return res; }
Datum gbt_numeric_penalty(PG_FUNCTION_ARGS) { GISTENTRY *o = (GISTENTRY *) PG_GETARG_POINTER(0); GISTENTRY *n = (GISTENTRY *) PG_GETARG_POINTER(1); float *result = (float *) PG_GETARG_POINTER(2); Numeric us, os, ds; GBT_VARKEY *org = (GBT_VARKEY *) DatumGetPointer(o->key); GBT_VARKEY *newe = (GBT_VARKEY *) DatumGetPointer(n->key); Datum uni; GBT_VARKEY_R rk, ok, uk; rk = gbt_var_key_readable(org); uni = PointerGetDatum(gbt_var_key_copy(&rk, TRUE)); gbt_var_bin_union(&uni, newe, &tinfo); ok = gbt_var_key_readable(org); uk = gbt_var_key_readable((GBT_VARKEY *) DatumGetPointer(uni)); us = DatumGetNumeric(DirectFunctionCall2( numeric_sub, PointerGetDatum(uk.upper), PointerGetDatum(uk.lower) )); os = DatumGetNumeric(DirectFunctionCall2( numeric_sub, PointerGetDatum(ok.upper), PointerGetDatum(ok.lower) )); ds = DatumGetNumeric(DirectFunctionCall2( numeric_sub, NumericGetDatum(us), NumericGetDatum(os) )); if (numeric_is_nan(us)) { if (numeric_is_nan(os)) *result = 0.0; else *result = 1.0; } else { Numeric nul = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(0))); *result = 0.0; if (DirectFunctionCall2(numeric_gt, NumericGetDatum(ds), NumericGetDatum(nul))) { *result += FLT_MIN; os = DatumGetNumeric(DirectFunctionCall2( numeric_div, NumericGetDatum(ds), NumericGetDatum(us) )); *result += (float4) DatumGetFloat8(DirectFunctionCall1(numeric_float8_no_overflow, NumericGetDatum(os))); } } if (*result > 0) *result *= (FLT_MAX / (((GISTENTRY *) PG_GETARG_POINTER(0))->rel->rd_att->natts + 1)); PG_RETURN_POINTER(result); }
Datum jsonb_add(PG_FUNCTION_ARGS) { Jsonb *l = PG_GETARG_JSONB(0); Jsonb *r = PG_GETARG_JSONB(1); JsonbValue *ljv; JsonbValue *rjv; JsonbValue jv; Size len; char *buf; Datum n; char *nstr; if (!(JB_ROOT_IS_SCALAR(l) && JB_ROOT_IS_SCALAR(r))) { Datum j; if ((JB_ROOT_IS_SCALAR(l) && JB_ROOT_IS_OBJECT(r)) || (JB_ROOT_IS_OBJECT(l) && JB_ROOT_IS_SCALAR(r)) || (JB_ROOT_IS_OBJECT(l) && JB_ROOT_IS_OBJECT(r))) ereport_op_str("+", l, r); j = DirectFunctionCall2(jsonb_concat, JsonbGetDatum(l), JsonbGetDatum(r)); PG_RETURN_DATUM(j); } ljv = getIthJsonbValueFromContainer(&l->root, 0); rjv = getIthJsonbValueFromContainer(&r->root, 0); if (ljv->type == jbvString && rjv->type == jbvString) { len = ljv->val.string.len + rjv->val.string.len; buf = palloc(len + 1); strncpy(buf, ljv->val.string.val, ljv->val.string.len); strncpy(buf + ljv->val.string.len, rjv->val.string.val, rjv->val.string.len); buf[len] = '\0'; jv.type = jbvString; jv.val.string.len = len; jv.val.string.val = buf; PG_RETURN_JSONB(JsonbValueToJsonb(&jv)); } else if (ljv->type == jbvString && rjv->type == jbvNumeric) { n = DirectFunctionCall1(numeric_out, NumericGetDatum(rjv->val.numeric)); nstr = DatumGetCString(n); len = ljv->val.string.len + strlen(nstr); buf = palloc(len + 1); strncpy(buf, ljv->val.string.val, ljv->val.string.len); strcpy(buf + ljv->val.string.len, nstr); jv.type = jbvString; jv.val.string.len = len; jv.val.string.val = buf; PG_RETURN_JSONB(JsonbValueToJsonb(&jv)); } else if (ljv->type == jbvNumeric && rjv->type == jbvString) { Size nlen; n = DirectFunctionCall1(numeric_out, NumericGetDatum(ljv->val.numeric)); nstr = DatumGetCString(n); nlen = strlen(nstr); len = nlen + rjv->val.string.len; buf = palloc(len + 1); strcpy(buf, nstr); strncpy(buf + nlen, rjv->val.string.val, rjv->val.string.len); buf[len] = '\0'; jv.type = jbvString; jv.val.string.len = len; jv.val.string.val = buf; PG_RETURN_JSONB(JsonbValueToJsonb(&jv)); } else if (ljv->type == jbvNumeric && rjv->type == jbvNumeric) { n = DirectFunctionCall2(numeric_add, NumericGetDatum(ljv->val.numeric), NumericGetDatum(rjv->val.numeric)); PG_RETURN_JSONB(numeric_to_jnumber(DatumGetNumeric(n))); } else { ereport_op_str("+", l, r); } PG_RETURN_NULL(); }
/* * Add an attribute to the hash calculation. * **IMPORTANT: any new hard coded support for a data type in here * must be added to isGreenplumDbHashable() below! * * Note that the caller should provide the base type if the datum is * of a domain type. It is quite expensive to call get_typtype() and * getBaseType() here since this function gets called a lot for the * same set of Datums. * * @param hashFn called to update the hash value. * @param clientData passed to hashFn. */ void hashDatum(Datum datum, Oid type, datumHashFunction hashFn, void *clientData) { void *buf = NULL; /* pointer to the data */ size_t len = 0; /* length for the data buffer */ int64 intbuf; /* an 8 byte buffer for all integer sizes */ float4 buf_f4; float8 buf_f8; Timestamp tsbuf; /* timestamp data dype is either a double or * int8 (determined in compile time) */ TimestampTz tstzbuf; DateADT datebuf; TimeADT timebuf; TimeTzADT *timetzptr; Interval *intervalptr; AbsoluteTime abstime_buf; RelativeTime reltime_buf; TimeInterval tinterval; AbsoluteTime tinterval_len; Numeric num; bool bool_buf; char char_buf; Name namebuf; ArrayType *arrbuf; inet *inetptr; /* inet/cidr */ unsigned char inet_hkey[sizeof(inet_struct)]; macaddr *macptr; /* MAC address */ VarBit *vbitptr; int2vector *i2vec_buf; oidvector *oidvec_buf; Cash cash_buf; AclItem *aclitem_ptr; uint32 aclitem_buf; /* * special case buffers */ uint32 nanbuf; uint32 invalidbuf; void *tofree = NULL; /* * Select the hash to be performed according to the field type we are adding to the * hash. */ switch (type) { /* * ======= NUMERIC TYPES ======== */ case INT2OID: /* -32 thousand to 32 thousand, 2-byte storage */ intbuf = (int64) DatumGetInt16(datum); /* cast to 8 byte before * hashing */ buf = &intbuf; len = sizeof(intbuf); break; case INT4OID: /* -2 billion to 2 billion integer, 4-byte * storage */ intbuf = (int64) DatumGetInt32(datum); /* cast to 8 byte before * hashing */ buf = &intbuf; len = sizeof(intbuf); break; case INT8OID: /* ~18 digit integer, 8-byte storage */ intbuf = DatumGetInt64(datum); /* cast to 8 byte before * hashing */ buf = &intbuf; len = sizeof(intbuf); break; case FLOAT4OID: /* single-precision floating point number, * 4-byte storage */ buf_f4 = DatumGetFloat4(datum); /* * On IEEE-float machines, minus zero and zero have different bit * patterns but should compare as equal. We must ensure that they * have the same hash value, which is most easily done this way: */ if (buf_f4 == (float4) 0) buf_f4 = 0.0; buf = &buf_f4; len = sizeof(buf_f4); break; case FLOAT8OID: /* double-precision floating point number, * 8-byte storage */ buf_f8 = DatumGetFloat8(datum); /* * On IEEE-float machines, minus zero and zero have different bit * patterns but should compare as equal. We must ensure that they * have the same hash value, which is most easily done this way: */ if (buf_f8 == (float8) 0) buf_f8 = 0.0; buf = &buf_f8; len = sizeof(buf_f8); break; case NUMERICOID: num = DatumGetNumeric(datum); if (NUMERIC_IS_NAN(num)) { nanbuf = NAN_VAL; buf = &nanbuf; len = sizeof(nanbuf); } else /* not a nan */ { buf = num->n_data; len = (VARSIZE(num) - NUMERIC_HDRSZ); } /* * If we did a pg_detoast_datum, we need to remember to pfree, * or we will leak memory. Because of the 1-byte varlena header stuff. */ if (num != DatumGetPointer(datum)) tofree = num; break; /* * ====== CHARACTER TYPES ======= */ case CHAROID: /* char(1), single character */ char_buf = DatumGetChar(datum); buf = &char_buf; len = 1; break; case BPCHAROID: /* char(n), blank-padded string, fixed storage */ case TEXTOID: /* text */ case VARCHAROID: /* varchar */ case BYTEAOID: /* bytea */ { int tmplen; varattrib_untoast_ptr_len(datum, (char **) &buf, &tmplen, &tofree); /* adjust length to not include trailing blanks */ if (type != BYTEAOID && tmplen > 1) tmplen = ignoreblanks((char *) buf, tmplen); len = tmplen; break; } case NAMEOID: namebuf = DatumGetName(datum); len = NAMEDATALEN; buf = NameStr(*namebuf); /* adjust length to not include trailing blanks */ if (len > 1) len = ignoreblanks((char *) buf, len); break; /* * ====== OBJECT IDENTIFIER TYPES ====== */ case OIDOID: /* object identifier(oid), maximum 4 billion */ case REGPROCOID: /* function name */ case REGPROCEDUREOID: /* function name with argument types */ case REGOPEROID: /* operator name */ case REGOPERATOROID: /* operator with argument types */ case REGCLASSOID: /* relation name */ case REGTYPEOID: /* data type name */ intbuf = (int64) DatumGetUInt32(datum); /* cast to 8 byte before hashing */ buf = &intbuf; len = sizeof(intbuf); break; case TIDOID: /* tuple id (6 bytes) */ buf = DatumGetPointer(datum); len = SizeOfIptrData; break; /* * ====== DATE/TIME TYPES ====== */ case TIMESTAMPOID: /* date and time */ tsbuf = DatumGetTimestamp(datum); buf = &tsbuf; len = sizeof(tsbuf); break; case TIMESTAMPTZOID: /* date and time with time zone */ tstzbuf = DatumGetTimestampTz(datum); buf = &tstzbuf; len = sizeof(tstzbuf); break; case DATEOID: /* ANSI SQL date */ datebuf = DatumGetDateADT(datum); buf = &datebuf; len = sizeof(datebuf); break; case TIMEOID: /* hh:mm:ss, ANSI SQL time */ timebuf = DatumGetTimeADT(datum); buf = &timebuf; len = sizeof(timebuf); break; case TIMETZOID: /* time with time zone */ /* * will not compare to TIMEOID on equal values. * Postgres never attempts to compare the two as well. */ timetzptr = DatumGetTimeTzADTP(datum); buf = (unsigned char *) timetzptr; /* * Specify hash length as sizeof(double) + sizeof(int4), not as * sizeof(TimeTzADT), so that any garbage pad bytes in the structure * won't be included in the hash! */ len = sizeof(timetzptr->time) + sizeof(timetzptr->zone); break; case INTERVALOID: /* @ <number> <units>, time interval */ intervalptr = DatumGetIntervalP(datum); buf = (unsigned char *) intervalptr; /* * Specify hash length as sizeof(double) + sizeof(int4), not as * sizeof(Interval), so that any garbage pad bytes in the structure * won't be included in the hash! */ len = sizeof(intervalptr->time) + sizeof(intervalptr->month); break; case ABSTIMEOID: abstime_buf = DatumGetAbsoluteTime(datum); if (abstime_buf == INVALID_ABSTIME) { /* hash to a constant value */ invalidbuf = INVALID_VAL; len = sizeof(invalidbuf); buf = &invalidbuf; } else { len = sizeof(abstime_buf); buf = &abstime_buf; } break; case RELTIMEOID: reltime_buf = DatumGetRelativeTime(datum); if (reltime_buf == INVALID_RELTIME) { /* hash to a constant value */ invalidbuf = INVALID_VAL; len = sizeof(invalidbuf); buf = &invalidbuf; } else { len = sizeof(reltime_buf); buf = &reltime_buf; } break; case TINTERVALOID: tinterval = DatumGetTimeInterval(datum); /* * check if a valid interval. the '0' status code * stands for T_INTERVAL_INVAL which is defined in * nabstime.c. We use the actual value instead * of defining it again here. */ if(tinterval->status == 0 || tinterval->data[0] == INVALID_ABSTIME || tinterval->data[1] == INVALID_ABSTIME) { /* hash to a constant value */ invalidbuf = INVALID_VAL; len = sizeof(invalidbuf); buf = &invalidbuf; } else { /* normalize on length of the time interval */ tinterval_len = tinterval->data[1] - tinterval->data[0]; len = sizeof(tinterval_len); buf = &tinterval_len; } break; /* * ======= NETWORK TYPES ======== */ case INETOID: case CIDROID: inetptr = DatumGetInetP(datum); len = inet_getkey(inetptr, inet_hkey, sizeof(inet_hkey)); /* fill-in inet_key & get len */ buf = inet_hkey; break; case MACADDROID: macptr = DatumGetMacaddrP(datum); len = sizeof(macaddr); buf = (unsigned char *) macptr; break; /* * ======== BIT STRINGS ======== */ case BITOID: case VARBITOID: /* * Note that these are essentially strings. * we don't need to worry about '10' and '010' * to compare, b/c they will not, by design. * (see SQL standard, and varbit.c) */ vbitptr = DatumGetVarBitP(datum); len = VARBITBYTES(vbitptr); buf = (char *) VARBITS(vbitptr); break; /* * ======= other types ======= */ case BOOLOID: /* boolean, 'true'/'false' */ bool_buf = DatumGetBool(datum); buf = &bool_buf; len = sizeof(bool_buf); break; /* * We prepare the hash key for aclitems just like postgresql does. * (see code and comment in acl.c: hash_aclitem() ). */ case ACLITEMOID: aclitem_ptr = DatumGetAclItemP(datum); aclitem_buf = (uint32) (aclitem_ptr->ai_privs + aclitem_ptr->ai_grantee + aclitem_ptr->ai_grantor); buf = &aclitem_buf; len = sizeof(aclitem_buf); break; /* * ANYARRAY is a pseudo-type. We use it to include * any of the array types (OIDs 1007-1033 in pg_type.h). * caller needs to be sure the type is ANYARRAYOID * before calling cdbhash on an array (INSERT and COPY do so). */ case ANYARRAYOID: arrbuf = DatumGetArrayTypeP(datum); len = VARSIZE(arrbuf) - VARHDRSZ; buf = VARDATA(arrbuf); break; case INT2VECTOROID: i2vec_buf = (int2vector *) DatumGetPointer(datum); len = i2vec_buf->dim1 * sizeof(int2); buf = (void *)i2vec_buf->values; break; case OIDVECTOROID: oidvec_buf = (oidvector *) DatumGetPointer(datum); len = oidvec_buf->dim1 * sizeof(Oid); buf = oidvec_buf->values; break; case CASHOID: /* cash is stored in int32 internally */ cash_buf = (* (Cash *)DatumGetPointer(datum)); len = sizeof(Cash); buf = &cash_buf; break; default: ereport(ERROR, (errcode(ERRCODE_CDB_FEATURE_NOT_YET), errmsg("Type %u is not hashable.", type))); } /* switch(type) */ /* do the hash using the selected algorithm */ hashFn(clientData, buf, len); if(tofree) pfree(tofree); }
/* * jsonb_agg aggregate function */ Datum jsonb_agg_transfn(PG_FUNCTION_ARGS) { MemoryContext oldcontext, aggcontext; JsonbAggState *state; JsonbInState elem; Datum val; JsonbInState *result; bool single_scalar = false; JsonbIterator *it; Jsonb *jbelem; JsonbValue v; JsonbIteratorToken type; if (!AggCheckCallContext(fcinfo, &aggcontext)) { /* cannot be called directly because of internal-type argument */ elog(ERROR, "jsonb_agg_transfn called in non-aggregate context"); } /* set up the accumulator on the first go round */ if (PG_ARGISNULL(0)) { Oid arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1); if (arg_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); oldcontext = MemoryContextSwitchTo(aggcontext); state = palloc(sizeof(JsonbAggState)); result = palloc0(sizeof(JsonbInState)); state->res = result; result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, NULL); MemoryContextSwitchTo(oldcontext); jsonb_categorize_type(arg_type, &state->val_category, &state->val_output_func); } else { state = (JsonbAggState *) PG_GETARG_POINTER(0); result = state->res; } /* turn the argument into jsonb in the normal function context */ val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1); memset(&elem, 0, sizeof(JsonbInState)); datum_to_jsonb(val, PG_ARGISNULL(1), &elem, state->val_category, state->val_output_func, false); jbelem = JsonbValueToJsonb(elem.res); /* switch to the aggregate context for accumulation operations */ oldcontext = MemoryContextSwitchTo(aggcontext); it = JsonbIteratorInit(&jbelem->root); while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { switch (type) { case WJB_BEGIN_ARRAY: if (v.val.array.rawScalar) single_scalar = true; else result->res = pushJsonbValue(&result->parseState, type, NULL); break; case WJB_END_ARRAY: if (!single_scalar) result->res = pushJsonbValue(&result->parseState, type, NULL); break; case WJB_BEGIN_OBJECT: case WJB_END_OBJECT: result->res = pushJsonbValue(&result->parseState, type, NULL); break; case WJB_ELEM: case WJB_KEY: case WJB_VALUE: if (v.type == jbvString) { /* copy string values in the aggregate context */ char *buf = palloc(v.val.string.len + 1); snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val); v.val.string.val = buf; } else if (v.type == jbvNumeric) { /* same for numeric */ v.val.numeric = DatumGetNumeric(DirectFunctionCall1(numeric_uplus, NumericGetDatum(v.val.numeric))); } result->res = pushJsonbValue(&result->parseState, type, &v); break; default: elog(ERROR, "unknown jsonb iterator token type"); } } MemoryContextSwitchTo(oldcontext); PG_RETURN_POINTER(state); }
/* * 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"); } } }
/* * jsonb_object_agg aggregate function */ Datum jsonb_object_agg_transfn(PG_FUNCTION_ARGS) { MemoryContext oldcontext, aggcontext; JsonbInState elem; JsonbAggState *state; Datum val; JsonbInState *result; bool single_scalar; JsonbIterator *it; Jsonb *jbkey, *jbval; JsonbValue v; JsonbIteratorToken type; if (!AggCheckCallContext(fcinfo, &aggcontext)) { /* cannot be called directly because of internal-type argument */ elog(ERROR, "jsonb_object_agg_transfn called in non-aggregate context"); } /* set up the accumulator on the first go round */ if (PG_ARGISNULL(0)) { Oid arg_type; oldcontext = MemoryContextSwitchTo(aggcontext); state = palloc(sizeof(JsonbAggState)); result = palloc0(sizeof(JsonbInState)); state->res = result; result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_OBJECT, NULL); MemoryContextSwitchTo(oldcontext); arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1); if (arg_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); jsonb_categorize_type(arg_type, &state->key_category, &state->key_output_func); arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2); if (arg_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); jsonb_categorize_type(arg_type, &state->val_category, &state->val_output_func); } else { state = (JsonbAggState *) PG_GETARG_POINTER(0); result = state->res; } /* turn the argument into jsonb in the normal function context */ if (PG_ARGISNULL(1)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("field name must not be null"))); val = PG_GETARG_DATUM(1); memset(&elem, 0, sizeof(JsonbInState)); datum_to_jsonb(val, false, &elem, state->key_category, state->key_output_func, true); jbkey = JsonbValueToJsonb(elem.res); val = PG_ARGISNULL(2) ? (Datum) 0 : PG_GETARG_DATUM(2); memset(&elem, 0, sizeof(JsonbInState)); datum_to_jsonb(val, PG_ARGISNULL(2), &elem, state->val_category, state->val_output_func, false); jbval = JsonbValueToJsonb(elem.res); it = JsonbIteratorInit(&jbkey->root); /* switch to the aggregate context for accumulation operations */ oldcontext = MemoryContextSwitchTo(aggcontext); /* * keys should be scalar, and we should have already checked for that * above when calling datum_to_jsonb, so we only need to look for these * things. */ while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { switch (type) { case WJB_BEGIN_ARRAY: if (!v.val.array.rawScalar) elog(ERROR, "unexpected structure for key"); break; case WJB_ELEM: if (v.type == jbvString) { /* copy string values in the aggregate context */ char *buf = palloc(v.val.string.len + 1); snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val); v.val.string.val = buf; } else { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("object keys must be strings"))); } result->res = pushJsonbValue(&result->parseState, WJB_KEY, &v); break; case WJB_END_ARRAY: break; default: elog(ERROR, "unexpected structure for key"); break; } } it = JsonbIteratorInit(&jbval->root); single_scalar = false; /* * values can be anything, including structured and null, so we treat them * as in json_agg_transfn, except that single scalars are always pushed as * WJB_VALUE items. */ while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { switch (type) { case WJB_BEGIN_ARRAY: if (v.val.array.rawScalar) single_scalar = true; else result->res = pushJsonbValue(&result->parseState, type, NULL); break; case WJB_END_ARRAY: if (!single_scalar) result->res = pushJsonbValue(&result->parseState, type, NULL); break; case WJB_BEGIN_OBJECT: case WJB_END_OBJECT: result->res = pushJsonbValue(&result->parseState, type, NULL); break; case WJB_ELEM: case WJB_KEY: case WJB_VALUE: if (v.type == jbvString) { /* copy string values in the aggregate context */ char *buf = palloc(v.val.string.len + 1); snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val); v.val.string.val = buf; } else if (v.type == jbvNumeric) { /* same for numeric */ v.val.numeric = DatumGetNumeric(DirectFunctionCall1(numeric_uplus, NumericGetDatum(v.val.numeric))); } result->res = pushJsonbValue(&result->parseState, single_scalar ? WJB_VALUE : type, &v); break; default: elog(ERROR, "unknown jsonb iterator token type"); } } MemoryContextSwitchTo(oldcontext); PG_RETURN_POINTER(state); }