/*------------------------------------------------------------------------- * * bytea.c * Functions for the bytea type. * * Portions Copyright (c) 2025, PostgreSQL Global Development Group * * * IDENTIFICATION * src/backend/utils/adt/bytea.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/detoast.h" #include "catalog/pg_collation_d.h" #include "catalog/pg_type_d.h" #include "common/int.h" #include "fmgr.h" #include "libpq/pqformat.h" #include "port/pg_bitutils.h" #include "utils/builtins.h" #include "utils/bytea.h" #include "utils/fmgrprotos.h" #include "utils/memutils.h" #include "utils/sortsupport.h" #include "utils/varlena.h" #include "varatt.h" /* GUC variable */ int bytea_output = BYTEA_OUTPUT_HEX; static bytea *bytea_catenate(bytea *t1, bytea *t2); static bytea *bytea_substring(Datum str, int S, int L, bool length_not_specified); static bytea *bytea_overlay(bytea *t1, bytea *t2, int sp, int sl); /* * bytea_catenate * Guts of byteacat(), broken out so it can be used by other functions * * Arguments can be in short-header form, but not compressed or out-of-line */ static bytea * bytea_catenate(bytea *t1, bytea *t2) { bytea *result; int len1, len2, len; char *ptr; len1 = VARSIZE_ANY_EXHDR(t1); len2 = VARSIZE_ANY_EXHDR(t2); /* paranoia ... probably should throw error instead? */ if (len1 < 0) len1 = 0; if (len2 < 0) len2 = 0; len = len1 + len2 + VARHDRSZ; result = (bytea *) palloc(len); /* Set size of result string... */ SET_VARSIZE(result, len); /* Fill data field of result string... */ ptr = VARDATA(result); if (len1 > 0) memcpy(ptr, VARDATA_ANY(t1), len1); if (len2 > 0) memcpy(ptr + len1, VARDATA_ANY(t2), len2); return result; } #define PG_STR_GET_BYTEA(str_) \ DatumGetByteaPP(DirectFunctionCall1(byteain, CStringGetDatum(str_))) static bytea * bytea_substring(Datum str, int S, int L, bool length_not_specified) { int32 S1; /* adjusted start position */ int32 L1; /* adjusted substring length */ int32 E; /* end position */ /* * The logic here should generally match text_substring(). */ S1 = Max(S, 1); if (length_not_specified) { /* * Not passed a length - DatumGetByteaPSlice() grabs everything to the * end of the string if we pass it a negative value for length. */ L1 = -1; } else if (L < 0) { /* SQL99 says to throw an error for E < S, i.e., negative length */ ereport(ERROR, (errcode(ERRCODE_SUBSTRING_ERROR), errmsg("negative substring length not allowed"))); L1 = -1; /* silence stupider compilers */ } else if (pg_add_s32_overflow(S, L, &E)) { /* * L could be large enough for S + L to overflow, in which case the * substring must run to end of string. */ L1 = -1; } else { /* * A zero or negative value for the end position can happen if the * start was negative or one. SQL99 says to return a zero-length * string. */ if (E < 1) return PG_STR_GET_BYTEA(""); L1 = E - S1; } /* * If the start position is past the end of the string, SQL99 says to * return a zero-length string -- DatumGetByteaPSlice() will do that for * us. We need only convert S1 to zero-based starting position. */ return DatumGetByteaPSlice(str, S1 - 1, L1); } static bytea * bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) { bytea *result; bytea *s1; bytea *s2; int sp_pl_sl; /* * Check for possible integer-overflow cases. For negative sp, throw a * "substring length" error because that's what should be expected * according to the spec's definition of OVERLAY(). */ if (sp <= 0) ereport(ERROR, (errcode(ERRCODE_SUBSTRING_ERROR), errmsg("negative substring length not allowed"))); if (pg_add_s32_overflow(sp, sl, &sp_pl_sl)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); s1 = bytea_substring(PointerGetDatum(t1), 1, sp - 1, false); s2 = bytea_substring(PointerGetDatum(t1), sp_pl_sl, -1, true); result = bytea_catenate(s1, t2); result = bytea_catenate(result, s2); return result; } /***************************************************************************** * USER I/O ROUTINES * *****************************************************************************/ #define VAL(CH) ((CH) - '0') #define DIG(VAL) ((VAL) + '0') /* * byteain - converts from printable representation of byte array * * Non-printable characters must be passed as '\nnn' (octal) and are * converted to internal form. '\' must be passed as '\\'. * ereport(ERROR, ...) if bad form. * * BUGS: * The input is scanned twice. * The error checking of input is minimal. */ Datum byteain(PG_FUNCTION_ARGS) { char *inputText = PG_GETARG_CSTRING(0); Node *escontext = fcinfo->context; char *tp; char *rp; int bc; bytea *result; /* Recognize hex input */ if (inputText[0] == '\\' && inputText[1] == 'x') { size_t len = strlen(inputText); bc = (len - 2) / 2 + VARHDRSZ; /* maximum possible length */ result = palloc(bc); bc = hex_decode_safe(inputText + 2, len - 2, VARDATA(result), escontext); SET_VARSIZE(result, bc + VARHDRSZ); /* actual length */ PG_RETURN_BYTEA_P(result); } /* Else, it's the traditional escaped style */ for (bc = 0, tp = inputText; *tp != '\0'; bc++) { if (tp[0] != '\\') tp++; else if ((tp[0] == '\\') && (tp[1] >= '0' && tp[1] <= '3') && (tp[2] >= '0' && tp[2] <= '7') && (tp[3] >= '0' && tp[3] <= '7')) tp += 4; else if ((tp[0] == '\\') && (tp[1] == '\\')) tp += 2; else { /* * one backslash, not followed by another or ### valid octal */ ereturn(escontext, (Datum) 0, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type %s", "bytea"))); } } bc += VARHDRSZ; result = (bytea *) palloc(bc); SET_VARSIZE(result, bc); tp = inputText; rp = VARDATA(result); while (*tp != '\0') { if (tp[0] != '\\') *rp++ = *tp++; else if ((tp[0] == '\\') && (tp[1] >= '0' && tp[1] <= '3') && (tp[2] >= '0' && tp[2] <= '7') && (tp[3] >= '0' && tp[3] <= '7')) { bc = VAL(tp[1]); bc <<= 3; bc += VAL(tp[2]); bc <<= 3; *rp++ = bc + VAL(tp[3]); tp += 4; } else if ((tp[0] == '\\') && (tp[1] == '\\')) { *rp++ = '\\'; tp += 2; } else { /* * We should never get here. The first pass should not allow it. */ ereturn(escontext, (Datum) 0, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type %s", "bytea"))); } } PG_RETURN_BYTEA_P(result); } /* * byteaout - converts to printable representation of byte array * * In the traditional escaped format, non-printable characters are * printed as '\nnn' (octal) and '\' as '\\'. */ Datum byteaout(PG_FUNCTION_ARGS) { bytea *vlena = PG_GETARG_BYTEA_PP(0); char *result; char *rp; if (bytea_output == BYTEA_OUTPUT_HEX) { /* Print hex format */ rp = result = palloc(VARSIZE_ANY_EXHDR(vlena) * 2 + 2 + 1); *rp++ = '\\'; *rp++ = 'x'; rp += hex_encode(VARDATA_ANY(vlena), VARSIZE_ANY_EXHDR(vlena), rp); } else if (bytea_output == BYTEA_OUTPUT_ESCAPE) { /* Print traditional escaped format */ char *vp; uint64 len; int i; len = 1; /* empty string has 1 char */ vp = VARDATA_ANY(vlena); for (i = VARSIZE_ANY_EXHDR(vlena); i != 0; i--, vp++) { if (*vp == '\\') len += 2; else if ((unsigned char) *vp < 0x20 || (unsigned char) *vp > 0x7e) len += 4; else len++; } /* * In principle len can't overflow uint32 if the input fit in 1GB, but * for safety let's check rather than relying on palloc's internal * check. */ if (len > MaxAllocSize) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg_internal("result of bytea output conversion is too large"))); rp = result = (char *) palloc(len); vp = VARDATA_ANY(vlena); for (i = VARSIZE_ANY_EXHDR(vlena); i != 0; i--, vp++) { if (*vp == '\\') { *rp++ = '\\'; *rp++ = '\\'; } else if ((unsigned char) *vp < 0x20 || (unsigned char) *vp > 0x7e) { int val; /* holds unprintable chars */ val = *vp; rp[0] = '\\'; rp[3] = DIG(val & 07); val >>= 3; rp[2] = DIG(val & 07); val >>= 3; rp[1] = DIG(val & 03); rp += 4; } else *rp++ = *vp; } } else { elog(ERROR, "unrecognized \"bytea_output\" setting: %d", bytea_output); rp = result = NULL; /* keep compiler quiet */ } *rp = '\0'; PG_RETURN_CSTRING(result); } /* * bytearecv - converts external binary format to bytea */ Datum bytearecv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); bytea *result; int nbytes; nbytes = buf->len - buf->cursor; result = (bytea *) palloc(nbytes + VARHDRSZ); SET_VARSIZE(result, nbytes + VARHDRSZ); pq_copymsgbytes(buf, VARDATA(result), nbytes); PG_RETURN_BYTEA_P(result); } /* * byteasend - converts bytea to binary format * * This is a special case: just copy the input... */ Datum byteasend(PG_FUNCTION_ARGS) { bytea *vlena = PG_GETARG_BYTEA_P_COPY(0); PG_RETURN_BYTEA_P(vlena); } Datum bytea_string_agg_transfn(PG_FUNCTION_ARGS) { StringInfo state; state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0); /* Append the value unless null, preceding it with the delimiter. */ if (!PG_ARGISNULL(1)) { bytea *value = PG_GETARG_BYTEA_PP(1); bool isfirst = false; /* * You might think we can just throw away the first delimiter, however * we must keep it as we may be a parallel worker doing partial * aggregation building a state to send to the main process. We need * to keep the delimiter of every aggregation so that the combine * function can properly join up the strings of two separately * partially aggregated results. The first delimiter is only stripped * off in the final function. To know how much to strip off the front * of the string, we store the length of the first delimiter in the * StringInfo's cursor field, which we don't otherwise need here. */ if (state == NULL) { MemoryContext aggcontext; MemoryContext oldcontext; if (!AggCheckCallContext(fcinfo, &aggcontext)) { /* cannot be called directly because of internal-type argument */ elog(ERROR, "bytea_string_agg_transfn called in non-aggregate context"); } /* * Create state in aggregate context. It'll stay there across * subsequent calls. */ oldcontext = MemoryContextSwitchTo(aggcontext); state = makeStringInfo(); MemoryContextSwitchTo(oldcontext); isfirst = true; } if (!PG_ARGISNULL(2)) { bytea *delim = PG_GETARG_BYTEA_PP(2); appendBinaryStringInfo(state, VARDATA_ANY(delim), VARSIZE_ANY_EXHDR(delim)); if (isfirst) state->cursor = VARSIZE_ANY_EXHDR(delim); } appendBinaryStringInfo(state, VARDATA_ANY(value), VARSIZE_ANY_EXHDR(value)); } /* * The transition type for string_agg() is declared to be "internal", * which is a pass-by-value type the same size as a pointer. */ if (state) PG_RETURN_POINTER(state); PG_RETURN_NULL(); } Datum bytea_string_agg_finalfn(PG_FUNCTION_ARGS) { StringInfo state; /* cannot be called directly because of internal-type argument */ Assert(AggCheckCallContext(fcinfo, NULL)); state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0); if (state != NULL) { /* As per comment in transfn, strip data before the cursor position */ bytea *result; int strippedlen = state->len - state->cursor; result = (bytea *) palloc(strippedlen + VARHDRSZ); SET_VARSIZE(result, strippedlen + VARHDRSZ); memcpy(VARDATA(result), &state->data[state->cursor], strippedlen); PG_RETURN_BYTEA_P(result); } else PG_RETURN_NULL(); } /*------------------------------------------------------------- * byteaoctetlen * * get the number of bytes contained in an instance of type 'bytea' *------------------------------------------------------------- */ Datum byteaoctetlen(PG_FUNCTION_ARGS) { Datum str = PG_GETARG_DATUM(0); /* We need not detoast the input at all */ PG_RETURN_INT32(toast_raw_datum_size(str) - VARHDRSZ); } /* * byteacat - * takes two bytea* and returns a bytea* that is the concatenation of * the two. * * Cloned from textcat and modified as required. */ Datum byteacat(PG_FUNCTION_ARGS) { bytea *t1 = PG_GETARG_BYTEA_PP(0); bytea *t2 = PG_GETARG_BYTEA_PP(1); PG_RETURN_BYTEA_P(bytea_catenate(t1, t2)); } /* * byteaoverlay * Replace specified substring of first string with second * * The SQL standard defines OVERLAY() in terms of substring and concatenation. * This code is a direct implementation of what the standard says. */ Datum byteaoverlay(PG_FUNCTION_ARGS) { bytea *t1 = PG_GETARG_BYTEA_PP(0); bytea *t2 = PG_GETARG_BYTEA_PP(1); int sp = PG_GETARG_INT32(2); /* substring start position */ int sl = PG_GETARG_INT32(3); /* substring length */ PG_RETURN_BYTEA_P(bytea_overlay(t1, t2, sp, sl)); } Datum byteaoverlay_no_len(PG_FUNCTION_ARGS) { bytea *t1 = PG_GETARG_BYTEA_PP(0); bytea *t2 = PG_GETARG_BYTEA_PP(1); int sp = PG_GETARG_INT32(2); /* substring start position */ int sl; sl = VARSIZE_ANY_EXHDR(t2); /* defaults to length(t2) */ PG_RETURN_BYTEA_P(bytea_overlay(t1, t2, sp, sl)); } /* * bytea_substr() * Return a substring starting at the specified position. * Cloned from text_substr and modified as required. * * Input: * - string * - starting position (is one-based) * - string length (optional) * * If the starting position is zero or less, then return from the start of the string * adjusting the length to be consistent with the "negative start" per SQL. * If the length is less than zero, an ERROR is thrown. If no third argument * (length) is provided, the length to the end of the string is assumed. */ Datum bytea_substr(PG_FUNCTION_ARGS) { PG_RETURN_BYTEA_P(bytea_substring(PG_GETARG_DATUM(0), PG_GETARG_INT32(1), PG_GETARG_INT32(2), false)); } /* * bytea_substr_no_len - * Wrapper to avoid opr_sanity failure due to * one function accepting a different number of args. */ Datum bytea_substr_no_len(PG_FUNCTION_ARGS) { PG_RETURN_BYTEA_P(bytea_substring(PG_GETARG_DATUM(0), PG_GETARG_INT32(1), -1, true)); } /* * bit_count */ Datum bytea_bit_count(PG_FUNCTION_ARGS) { bytea *t1 = PG_GETARG_BYTEA_PP(0); PG_RETURN_INT64(pg_popcount(VARDATA_ANY(t1), VARSIZE_ANY_EXHDR(t1))); } /* * byteapos - * Return the position of the specified substring. * Implements the SQL POSITION() function. * Cloned from textpos and modified as required. */ Datum byteapos(PG_FUNCTION_ARGS) { bytea *t1 = PG_GETARG_BYTEA_PP(0); bytea *t2 = PG_GETARG_BYTEA_PP(1); int pos; int px, p; int len1, len2; char *p1, *p2; len1 = VARSIZE_ANY_EXHDR(t1); len2 = VARSIZE_ANY_EXHDR(t2); if (len2 <= 0) PG_RETURN_INT32(1); /* result for empty pattern */ p1 = VARDATA_ANY(t1); p2 = VARDATA_ANY(t2); pos = 0; px = (len1 - len2); for (p = 0; p <= px; p++) { if ((*p2 == *p1) && (memcmp(p1, p2, len2) == 0)) { pos = p + 1; break; }; p1++; }; PG_RETURN_INT32(pos); } /*------------------------------------------------------------- * byteaGetByte * * this routine treats "bytea" as an array of bytes. * It returns the Nth byte (a number between 0 and 255). *------------------------------------------------------------- */ Datum byteaGetByte(PG_FUNCTION_ARGS) { bytea *v = PG_GETARG_BYTEA_PP(0); int32 n = PG_GETARG_INT32(1); int len; int byte; len = VARSIZE_ANY_EXHDR(v); if (n < 0 || n >= len) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("index %d out of valid range, 0..%d", n, len - 1))); byte = ((unsigned char *) VARDATA_ANY(v))[n]; PG_RETURN_INT32(byte); } /*------------------------------------------------------------- * byteaGetBit * * This routine treats a "bytea" type like an array of bits. * It returns the value of the Nth bit (0 or 1). * *------------------------------------------------------------- */ Datum byteaGetBit(PG_FUNCTION_ARGS) { bytea *v = PG_GETARG_BYTEA_PP(0); int64 n = PG_GETARG_INT64(1); int byteNo, bitNo; int len; int byte; len = VARSIZE_ANY_EXHDR(v); if (n < 0 || n >= (int64) len * 8) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("index %" PRId64 " out of valid range, 0..%" PRId64, n, (int64) len * 8 - 1))); /* n/8 is now known < len, so safe to cast to int */ byteNo = (int) (n / 8); bitNo = (int) (n % 8); byte = ((unsigned char *) VARDATA_ANY(v))[byteNo]; if (byte & (1 << bitNo)) PG_RETURN_INT32(1); else PG_RETURN_INT32(0); } /*------------------------------------------------------------- * byteaSetByte * * Given an instance of type 'bytea' creates a new one with * the Nth byte set to the given value. * *------------------------------------------------------------- */ Datum byteaSetByte(PG_FUNCTION_ARGS) { bytea *res = PG_GETARG_BYTEA_P_COPY(0); int32 n = PG_GETARG_INT32(1); int32 newByte = PG_GETARG_INT32(2); int len; len = VARSIZE(res) - VARHDRSZ; if (n < 0 || n >= len) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("index %d out of valid range, 0..%d", n, len - 1))); /* * Now set the byte. */ ((unsigned char *) VARDATA(res))[n] = newByte; PG_RETURN_BYTEA_P(res); } /*------------------------------------------------------------- * byteaSetBit * * Given an instance of type 'bytea' creates a new one with * the Nth bit set to the given value. * *------------------------------------------------------------- */ Datum byteaSetBit(PG_FUNCTION_ARGS) { bytea *res = PG_GETARG_BYTEA_P_COPY(0); int64 n = PG_GETARG_INT64(1); int32 newBit = PG_GETARG_INT32(2); int len; int oldByte, newByte; int byteNo, bitNo; len = VARSIZE(res) - VARHDRSZ; if (n < 0 || n >= (int64) len * 8) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("index %" PRId64 " out of valid range, 0..%" PRId64, n, (int64) len * 8 - 1))); /* n/8 is now known < len, so safe to cast to int */ byteNo = (int) (n / 8); bitNo = (int) (n % 8); /* * sanity check! */ if (newBit != 0 && newBit != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("new bit must be 0 or 1"))); /* * Update the byte. */ oldByte = ((unsigned char *) VARDATA(res))[byteNo]; if (newBit == 0) newByte = oldByte & (~(1 << bitNo)); else newByte = oldByte | (1 << bitNo); ((unsigned char *) VARDATA(res))[byteNo] = newByte; PG_RETURN_BYTEA_P(res); } /* * Return reversed bytea */ Datum bytea_reverse(PG_FUNCTION_ARGS) { bytea *v = PG_GETARG_BYTEA_PP(0); const char *p = VARDATA_ANY(v); int len = VARSIZE_ANY_EXHDR(v); const char *endp = p + len; bytea *result = palloc(len + VARHDRSZ); char *dst = (char *) VARDATA(result) + len; SET_VARSIZE(result, len + VARHDRSZ); while (p < endp) *(--dst) = *p++; PG_RETURN_BYTEA_P(result); } /***************************************************************************** * Comparison Functions used for bytea * * Note: btree indexes need these routines not to leak memory; therefore, * be careful to free working copies of toasted datums. Most places don't * need to be so careful. *****************************************************************************/ Datum byteaeq(PG_FUNCTION_ARGS) { Datum arg1 = PG_GETARG_DATUM(0); Datum arg2 = PG_GETARG_DATUM(1); bool result; Size len1, len2; /* * We can use a fast path for unequal lengths, which might save us from * having to detoast one or both values. */ len1 = toast_raw_datum_size(arg1); len2 = toast_raw_datum_size(arg2); if (len1 != len2) result = false; else { bytea *barg1 = DatumGetByteaPP(arg1); bytea *barg2 = DatumGetByteaPP(arg2); result = (memcmp(VARDATA_ANY(barg1), VARDATA_ANY(barg2), len1 - VARHDRSZ) == 0); PG_FREE_IF_COPY(barg1, 0); PG_FREE_IF_COPY(barg2, 1); } PG_RETURN_BOOL(result); } Datum byteane(PG_FUNCTION_ARGS) { Datum arg1 = PG_GETARG_DATUM(0); Datum arg2 = PG_GETARG_DATUM(1); bool result; Size len1, len2; /* * We can use a fast path for unequal lengths, which might save us from * having to detoast one or both values. */ len1 = toast_raw_datum_size(arg1); len2 = toast_raw_datum_size(arg2); if (len1 != len2) result = true; else { bytea *barg1 = DatumGetByteaPP(arg1); bytea *barg2 = DatumGetByteaPP(arg2); result = (memcmp(VARDATA_ANY(barg1), VARDATA_ANY(barg2), len1 - VARHDRSZ) != 0); PG_FREE_IF_COPY(barg1, 0); PG_FREE_IF_COPY(barg2, 1); } PG_RETURN_BOOL(result); } Datum bytealt(PG_FUNCTION_ARGS) { bytea *arg1 = PG_GETARG_BYTEA_PP(0); bytea *arg2 = PG_GETARG_BYTEA_PP(1); int len1, len2; int cmp; len1 = VARSIZE_ANY_EXHDR(arg1); len2 = VARSIZE_ANY_EXHDR(arg2); cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2)); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); PG_RETURN_BOOL((cmp < 0) || ((cmp == 0) && (len1 < len2))); } Datum byteale(PG_FUNCTION_ARGS) { bytea *arg1 = PG_GETARG_BYTEA_PP(0); bytea *arg2 = PG_GETARG_BYTEA_PP(1); int len1, len2; int cmp; len1 = VARSIZE_ANY_EXHDR(arg1); len2 = VARSIZE_ANY_EXHDR(arg2); cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2)); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); PG_RETURN_BOOL((cmp < 0) || ((cmp == 0) && (len1 <= len2))); } Datum byteagt(PG_FUNCTION_ARGS) { bytea *arg1 = PG_GETARG_BYTEA_PP(0); bytea *arg2 = PG_GETARG_BYTEA_PP(1); int len1, len2; int cmp; len1 = VARSIZE_ANY_EXHDR(arg1); len2 = VARSIZE_ANY_EXHDR(arg2); cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2)); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); PG_RETURN_BOOL((cmp > 0) || ((cmp == 0) && (len1 > len2))); } Datum byteage(PG_FUNCTION_ARGS) { bytea *arg1 = PG_GETARG_BYTEA_PP(0); bytea *arg2 = PG_GETARG_BYTEA_PP(1); int len1, len2; int cmp; len1 = VARSIZE_ANY_EXHDR(arg1); len2 = VARSIZE_ANY_EXHDR(arg2); cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2)); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); PG_RETURN_BOOL((cmp > 0) || ((cmp == 0) && (len1 >= len2))); } Datum byteacmp(PG_FUNCTION_ARGS) { bytea *arg1 = PG_GETARG_BYTEA_PP(0); bytea *arg2 = PG_GETARG_BYTEA_PP(1); int len1, len2; int cmp; len1 = VARSIZE_ANY_EXHDR(arg1); len2 = VARSIZE_ANY_EXHDR(arg2); cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2)); if ((cmp == 0) && (len1 != len2)) cmp = (len1 < len2) ? -1 : 1; PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); PG_RETURN_INT32(cmp); } Datum bytea_larger(PG_FUNCTION_ARGS) { bytea *arg1 = PG_GETARG_BYTEA_PP(0); bytea *arg2 = PG_GETARG_BYTEA_PP(1); bytea *result; int len1, len2; int cmp; len1 = VARSIZE_ANY_EXHDR(arg1); len2 = VARSIZE_ANY_EXHDR(arg2); cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2)); result = ((cmp > 0) || ((cmp == 0) && (len1 > len2)) ? arg1 : arg2); PG_RETURN_BYTEA_P(result); } Datum bytea_smaller(PG_FUNCTION_ARGS) { bytea *arg1 = PG_GETARG_BYTEA_PP(0); bytea *arg2 = PG_GETARG_BYTEA_PP(1); bytea *result; int len1, len2; int cmp; len1 = VARSIZE_ANY_EXHDR(arg1); len2 = VARSIZE_ANY_EXHDR(arg2); cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2)); result = ((cmp < 0) || ((cmp == 0) && (len1 < len2)) ? arg1 : arg2); PG_RETURN_BYTEA_P(result); } Datum bytea_sortsupport(PG_FUNCTION_ARGS) { SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt); /* Use generic string SortSupport, forcing "C" collation */ varstr_sortsupport(ssup, BYTEAOID, C_COLLATION_OID); MemoryContextSwitchTo(oldcontext); PG_RETURN_VOID(); } /* Cast bytea -> int2 */ Datum bytea_int2(PG_FUNCTION_ARGS) { bytea *v = PG_GETARG_BYTEA_PP(0); int len = VARSIZE_ANY_EXHDR(v); uint16 result; /* Check that the byte array is not too long */ if (len > sizeof(result)) ereport(ERROR, errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("smallint out of range")); /* Convert it to an integer; most significant bytes come first */ result = 0; for (int i = 0; i < len; i++) { result <<= BITS_PER_BYTE; result |= ((unsigned char *) VARDATA_ANY(v))[i]; } PG_RETURN_INT16(result); } /* Cast bytea -> int4 */ Datum bytea_int4(PG_FUNCTION_ARGS) { bytea *v = PG_GETARG_BYTEA_PP(0); int len = VARSIZE_ANY_EXHDR(v); uint32 result; /* Check that the byte array is not too long */ if (len > sizeof(result)) ereport(ERROR, errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range")); /* Convert it to an integer; most significant bytes come first */ result = 0; for (int i = 0; i < len; i++) { result <<= BITS_PER_BYTE; result |= ((unsigned char *) VARDATA_ANY(v))[i]; } PG_RETURN_INT32(result); } /* Cast bytea -> int8 */ Datum bytea_int8(PG_FUNCTION_ARGS) { bytea *v = PG_GETARG_BYTEA_PP(0); int len = VARSIZE_ANY_EXHDR(v); uint64 result; /* Check that the byte array is not too long */ if (len > sizeof(result)) ereport(ERROR, errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("bigint out of range")); /* Convert it to an integer; most significant bytes come first */ result = 0; for (int i = 0; i < len; i++) { result <<= BITS_PER_BYTE; result |= ((unsigned char *) VARDATA_ANY(v))[i]; } PG_RETURN_INT64(result); } /* Cast int2 -> bytea; can just use int2send() */ Datum int2_bytea(PG_FUNCTION_ARGS) { return int2send(fcinfo); } /* Cast int4 -> bytea; can just use int4send() */ Datum int4_bytea(PG_FUNCTION_ARGS) { return int4send(fcinfo); } /* Cast int8 -> bytea; can just use int8send() */ Datum int8_bytea(PG_FUNCTION_ARGS) { return int8send(fcinfo); }