diff -cpr pgsql-orig/src/backend/access/common/heaptuple.c pgsql/src/backend/access/common/heaptuple.c *** pgsql-orig/src/backend/access/common/heaptuple.c 2005-09-08 11:10:16.253332616 +0900 --- pgsql/src/backend/access/common/heaptuple.c 2005-09-08 11:11:04.884939488 +0900 *************** heap_fill_tuple(TupleDesc tupleDesc, *** 173,178 **** --- 173,185 ---- data_length = strlen(DatumGetCString(values[i])) + 1; memcpy(data, DatumGetPointer(values[i]), data_length); } + else if (att[i]->attlen == -3) + { + /* varlena2 */ + *infomask |= HEAP_HASVARWIDTH; + data_length = varlena2size(DatumGetRawVarlena2P(values[i])); + memcpy(data, DatumGetPointer(values[i]), data_length); + } else { /* fixed-length pass-by-reference */ *************** DataFill(char *data, *** 272,277 **** --- 279,291 ---- data_length = strlen(DatumGetCString(values[i])) + 1; memcpy(data, DatumGetPointer(values[i]), data_length); } + else if (att[i]->attlen == -3) + { + /* varlena2 */ + *infomask |= HEAP_HASVARWIDTH; + data_length = varlena2size(DatumGetRawVarlena2P(values[i])); + memcpy(data, DatumGetPointer(values[i]), data_length); + } else { /* fixed-length pass-by-reference */ diff -cpr pgsql-orig/src/backend/catalog/pg_type.c pgsql/src/backend/catalog/pg_type.c *** pgsql-orig/src/backend/catalog/pg_type.c 2005-09-08 11:10:16.343318936 +0900 --- pgsql/src/backend/catalog/pg_type.c 2005-09-08 11:11:04.885939336 +0900 *************** TypeCreate(const char *typeName, *** 189,195 **** */ if (!(internalSize > 0 || internalSize == -1 || ! internalSize == -2)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("invalid type internal size %d", --- 189,196 ---- */ if (!(internalSize > 0 || internalSize == -1 || ! internalSize == -2 || ! internalSize == -3)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("invalid type internal size %d", diff -cpr pgsql-orig/src/backend/utils/adt/arrayfuncs.c pgsql/src/backend/utils/adt/arrayfuncs.c *** pgsql-orig/src/backend/utils/adt/arrayfuncs.c 2005-09-08 11:10:16.217338088 +0900 --- pgsql/src/backend/utils/adt/arrayfuncs.c 2005-09-08 11:11:04.888938880 +0900 *************** ReadArrayStr(char *arrayStr, *** 817,831 **** totbytes += sizeof(int32); totbytes = att_align(totbytes, typalign); } ! else { /* dummy cstring value */ - Assert(typlen == -2); values[i] = PointerGetDatum(palloc(1)); *((char *) DatumGetPointer(values[i])) = '\0'; totbytes += 1; totbytes = att_align(totbytes, typalign); } } } *nbytes = totbytes; --- 817,839 ---- totbytes += sizeof(int32); totbytes = att_align(totbytes, typalign); } ! else if (typlen == -2) { /* dummy cstring value */ values[i] = PointerGetDatum(palloc(1)); *((char *) DatumGetPointer(values[i])) = '\0'; totbytes += 1; totbytes = att_align(totbytes, typalign); } + else + { + /* dummy varlena2 value */ + Assert(typlen == -3); + values[i] = PointerGetDatum(palloc(1)); + *((char *) DatumGetPointer(values[i])) = 0; + totbytes += 1; + totbytes = att_align(totbytes, typalign); + } } } *nbytes = totbytes; diff -cpr pgsql-orig/src/backend/utils/adt/datum.c pgsql/src/backend/utils/adt/datum.c *** pgsql-orig/src/backend/utils/adt/datum.c 2005-09-08 11:10:16.201340520 +0900 --- pgsql/src/backend/utils/adt/datum.c 2005-09-08 11:11:04.889938728 +0900 *************** datumGetSize(Datum value, bool typByVal, *** 93,98 **** --- 93,110 ---- size = (Size) (strlen(s) + 1); } + else if (typLen == -3) + { + /* It is a varlena2 datatype */ + struct varlena2 *s = (struct varlena2 *) DatumGetPointer(value); + + if (!PointerIsValid(s)) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid Datum pointer"))); + + size = (Size) varlena2size(s); + } else { elog(ERROR, "invalid typLen: %d", typLen); diff -cpr pgsql-orig/src/backend/utils/adt/varlena.c pgsql/src/backend/utils/adt/varlena.c *** pgsql-orig/src/backend/utils/adt/varlena.c 2005-09-08 11:10:16.210339152 +0900 --- pgsql/src/backend/utils/adt/varlena.c 2005-09-08 11:44:36.785084176 +0900 *************** pg_column_size(PG_FUNCTION_ARGS) *** 2684,2689 **** --- 2684,2694 ---- /* cstring */ result = strlen(DatumGetCString(value)) + 1; } + else if (typlen == -3) + { + /* varlena2 type */ + result = varlena2size(DatumGetRawVarlena2P(value)); + } else { /* ordinary fixed-width type */ *************** pg_column_size(PG_FUNCTION_ARGS) *** 2692,2694 **** --- 2697,2951 ---- PG_RETURN_INT32(result); } + + /* varlena2 */ + + /* + * The length of varlena2 is encoded as follows: + * + * | First | Trailing | Total | Max | + * | byte | bytes | bits | length | + * +----------+----------+-------+---------+ + * | 0******* | 0 | 7 | 127 | + * | 10****** | 1 | 14 | 16K -1 | + * | 110***** | 2 | 21 | 2M -1 | + * | 1110**** | 3 | 28 | 256M -1 | + * | 1111---- | 4 | 32 | 4G -1 | + */ + + static const uint8 VARLENA2HDRMAP[16] = + { + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 5, + }; + + /* Return the header size of a varlena2 */ + #define VARLENA2HDRSZ(n) VARLENA2HDRMAP[(uint8)n[0] >> 4] + + /* Decode the byte size of a varlena2 */ + #define VARLENA2LENGTH_1(n) \ + ((uint32)n[0]) + #define VARLENA2LENGTH_2(n) \ + (((uint32)(n[0] & 0x3F) << 8) \ + + n[1]) + #define VARLENA2LENGTH_3(n) \ + (((uint32)(n[0] & 0x1F) << 16) \ + + ((uint32)n[1] << 8) \ + + n[2]) + #define VARLENA2LENGTH_4(n) \ + (((uint32)(n[0] & 0x0F) << 24) \ + + ((uint32)n[1] << 16) \ + + ((uint32)n[2] << 8) \ + + n[3]) + #define VARLENA2LENGTH_5(n) \ + ( ((uint32)n[1] << 24) \ + + ((uint32)n[2] << 16) \ + + ((uint32)n[3] << 8) \ + + n[4]) + + + /* Return the total byte size of a varlena2 */ + int varlena2size(const struct varlena2 *v) + { + const uint8* len = v->vl2_len; + switch(VARLENA2HDRSZ(len)) + { + case 1: return 1 + VARLENA2LENGTH_1(len); + case 2: return 2 + VARLENA2LENGTH_2(len); + case 3: return 3 + VARLENA2LENGTH_3(len); + case 4: return 4 + VARLENA2LENGTH_4(len); + case 5: return 5 + VARLENA2LENGTH_5(len); + default: + elog(ERROR, "varlena2size"); + return -1; + } + } + + /* Return the data buffer and the data length of a varlena2 */ + static char *varlena2extract(struct varlena2 *v, int* length) + { + const uint8* len = v->vl2_len; + switch(VARLENA2HDRSZ(len)) + { + case 1: + *length = VARLENA2LENGTH_1(len); + return (char *) v + 1; + case 2: + *length = VARLENA2LENGTH_2(len); + return (char *) v + 2; + case 3: + *length = VARLENA2LENGTH_3(len); + return (char *) v + 3; + case 4: + *length = VARLENA2LENGTH_4(len); + return (char *) v + 4; + case 5: + *length = VARLENA2LENGTH_5(len); + return (char *) v + 5; + default: + elog(ERROR, "varlena2extract"); + return NULL; + } + } + + /* Return a new varlena2. */ + static struct varlena2 *varlena2new(int length, char** data) + { + struct varlena2 *v; + if(length < (1 << 7)) + { + v = (struct varlena2*) palloc(length + 1); + v->vl2_len[0] = (uint8)length; + *data = (char *) v + 1; + } + else if(length < (1 << 14)) + { + v = (struct varlena2*) palloc(length + 2); + v->vl2_len[0] = (uint8)((length & 0x3F00) >> 8) | 0x80; + v->vl2_len[1] = (uint8)((length & 0x00FF)); + *data = (char *) v + 2; + } + else if(length < (1 << 21)) + { + v = (struct varlena2*) palloc(length + 3); + v->vl2_len[0] = (uint8)((length & 0x1F0000) >> 16) | 0xC0; + v->vl2_len[1] = (uint8)((length & 0x00FF00) >> 8); + v->vl2_len[2] = (uint8)((length & 0x0000FF)); + *data = (char *) v + 3; + } + else if(length < (1 << 28)) + { + v = (struct varlena2*) palloc(length + 4); + v->vl2_len[0] = (uint8)((length & 0x0F000000) >> 24) | 0xE0; + v->vl2_len[1] = (uint8)((length & 0x00FF0000) >> 16); + v->vl2_len[2] = (uint8)((length & 0x0000FF00) >> 8); + v->vl2_len[3] = (uint8)((length & 0x000000FF)); + *data = (char *) v + 4; + } + else + { + v = (struct varlena2*) palloc(length + 5); + v->vl2_len[0] = 0xF0; + v->vl2_len[1] = (uint8)((length & 0xFF000000) >> 24); + v->vl2_len[2] = (uint8)((length & 0x00FF0000) >> 16); + v->vl2_len[3] = (uint8)((length & 0x0000FF00) >> 8); + v->vl2_len[4] = (uint8)((length & 0x000000FF)); + *data = (char *) v + 5; + } + return v; + } + + #define StringNew(length, data) ((String *) varlena2new((length), (data))) + #define StringExtract varlena2extract + + #define BinaryNew(length, data) ((Binary *) varlena2new((length), (data))) + #define BinaryExtract varlena2extract + + /* + * stringin - converts "..." to internal representation + */ + Datum + stringin(PG_FUNCTION_ARGS) + { + char *inputText = PG_GETARG_CSTRING(0); + String *result; + char *data; + int len; + + /* verify encoding */ + len = strlen(inputText); + pg_verifymbstr(inputText, len, false); + + result = StringNew(len, &data); + memcpy(data, inputText, len); + + PG_RETURN_STRING_P(result); + } + + /* + * stringout - converts internal representation to "..." + */ + Datum + stringout(PG_FUNCTION_ARGS) + { + String *t = PG_GETARG_STRING_P(0); + int len; + char *data; + char *result; + + data = StringExtract(t, &len); + result = (char *) palloc(len + 1); + memcpy(result, data, len); + result[len] = '\0'; + + PG_RETURN_CSTRING(result); + } + + /* + * stringrecv - converts external binary format to string + */ + Datum + stringrecv(PG_FUNCTION_ARGS) + { + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + String *result; + char *data; + char *inputText; + int nbytes; + + inputText = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); + + /* verify encoding */ + pg_verifymbstr(inputText, nbytes, false); + + result = StringNew(nbytes, &data); + memcpy(data, inputText, nbytes); + pfree(inputText); + + PG_RETURN_STRING_P(result); + } + + /* + * stringsend - converts string to binary format + */ + Datum + stringsend(PG_FUNCTION_ARGS) + { + String *t = PG_GETARG_STRING_P(0); + StringInfoData buf; + char *data; + int len; + + data = StringExtract(t, &len); + + pq_begintypsend(&buf); + pq_sendtext(&buf, data, len); + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); + } + + Datum + binaryin(PG_FUNCTION_ARGS) + { + elog(ERROR, "binaryin not implemented"); + PG_RETURN_VOID(); + } + + Datum + binaryout(PG_FUNCTION_ARGS) + { + elog(ERROR, "binaryout not implemented"); + PG_RETURN_VOID(); + } + + Datum + binaryrecv(PG_FUNCTION_ARGS) + { + elog(ERROR, "binaryrecv not implemented"); + PG_RETURN_VOID(); + } + + Datum + binarysend(PG_FUNCTION_ARGS) + { + elog(ERROR, "binarysend not implemented"); + PG_RETURN_VOID(); + } diff -cpr pgsql-orig/src/backend/utils/fmgr/fmgr.c pgsql/src/backend/utils/fmgr/fmgr.c *** pgsql-orig/src/backend/utils/fmgr/fmgr.c 2005-09-08 11:10:16.221337480 +0900 --- pgsql/src/backend/utils/fmgr/fmgr.c 2005-09-08 11:11:04.894937968 +0900 *************** pg_detoast_datum_slice(struct varlena * *** 1828,1833 **** --- 1828,1851 ---- } /*------------------------------------------------------------------------- + * Support routines for toastable datatypes (varlena2) + *------------------------------------------------------------------------- + */ + + struct varlena2 * + pg_detoast_datum2(struct varlena2 * datum) + { + #if 1 + return datum; + #else /* Not Implementd */ + if (VARATT2_IS_EXTENDED(datum)) + return (struct varlena2 *) heap_tuple_untoast_attr2((varattrib *) datum); + else + return datum; + #endif + } + + /*------------------------------------------------------------------------- * Support routines for extracting info from fn_expr parse tree * * These are needed by polymorphic functions, which accept multiple possible diff -cpr pgsql-orig/src/include/access/tupmacs.h pgsql/src/include/access/tupmacs.h *** pgsql-orig/src/include/access/tupmacs.h 2005-09-08 11:10:16.397310728 +0900 --- pgsql/src/include/access/tupmacs.h 2005-09-08 11:11:04.895937816 +0900 *************** *** 121,131 **** ( \ (cur_offset) + VARATT_SIZE(DatumGetPointer(attval)) \ ) \ ! : \ ( \ - AssertMacro((attlen) == -2), \ (cur_offset) + (strlen(DatumGetCString(attval)) + 1) \ ! )) \ ) /* --- 121,135 ---- ( \ (cur_offset) + VARATT_SIZE(DatumGetPointer(attval)) \ ) \ ! : (((attlen) == -2) ? \ ( \ (cur_offset) + (strlen(DatumGetCString(attval)) + 1) \ ! ) \ ! : \ ! ( \ ! AssertMacro((attlen) == -3), \ ! (cur_offset) + varlena2size(DatumGetRawVarlena2P(attval)) \ ! ))) \ ) /* diff -cpr pgsql-orig/src/include/c.h pgsql/src/include/c.h *** pgsql-orig/src/include/c.h 2005-09-08 11:10:16.361316200 +0900 --- pgsql/src/include/c.h 2005-09-08 11:11:04.896937664 +0900 *************** typedef struct varlena text; *** 431,436 **** --- 431,451 ---- typedef struct varlena BpChar; /* blank-padded char, ie SQL char(n) */ typedef struct varlena VarChar; /* var-length char, ie SQL varchar(n) */ + /* ---------------- + * Variable-length V2 datatypes all share the 'struct varlena2' header. + * ---------------- + */ + struct varlena2 + { + uint8 vl2_len[1]; + /* vl2_data[] */ + }; + + extern int varlena2size(const struct varlena2 *v); + + typedef struct varlena2 Binary; + typedef struct varlena2 String; + /* * Specialized array types. These are physically laid out just the same * as regular arrays (so that the regular array subscripting code works diff -cpr pgsql-orig/src/include/catalog/pg_proc.h pgsql/src/include/catalog/pg_proc.h *** pgsql-orig/src/include/catalog/pg_proc.h 2005-09-08 11:10:16.446303280 +0900 --- pgsql/src/include/catalog/pg_proc.h 2005-09-08 11:11:04.902936752 +0900 *************** DESCR("GiST support"); *** 3763,3768 **** --- 3763,3785 ---- DATA(insert OID = 2592 ( gist_circle_compress PGNSP PGUID 12 f f t f i 1 2281 "2281" _null_ _null_ _null_ gist_circle_compress - _null_ )); DESCR("GiST support"); + DATA(insert OID = 2593 ( stringin PGNSP PGUID 12 f f t f i 1 25 "2275" _null_ _null_ _null_ stringin - _null_ )); + DESCR("I/O"); + DATA(insert OID = 2594 ( stringout PGNSP PGUID 12 f f t f i 1 2275 "31" _null_ _null_ _null_ stringout - _null_ )); + DESCR("I/O"); + DATA(insert OID = 2595 ( stringrecv PGNSP PGUID 12 f f t f s 1 25 "2281" _null_ _null_ _null_ stringrecv - _null_ )); + DESCR("I/O"); + DATA(insert OID = 2596 ( stringsend PGNSP PGUID 12 f f t f s 1 17 "31" _null_ _null_ _null_ stringsend - _null_ )); + DESCR("I/O"); + DATA(insert OID = 2597 ( binaryin PGNSP PGUID 12 f f t f i 1 25 "2275" _null_ _null_ _null_ binaryin - _null_ )); + DESCR("I/O"); + DATA(insert OID = 2598 ( binaryout PGNSP PGUID 12 f f t f i 1 2275 "21" _null_ _null_ _null_ binaryout - _null_ )); + DESCR("I/O"); + DATA(insert OID = 2599 ( binaryrecv PGNSP PGUID 12 f f t f s 1 25 "2281" _null_ _null_ _null_ binaryrecv - _null_ )); + DESCR("I/O"); + DATA(insert OID = 2600 ( binarysend PGNSP PGUID 12 f f t f s 1 17 "32" _null_ _null_ _null_ binarysend - _null_ )); + DESCR("I/O"); + /* * Symbolic values for provolatile column: these indicate whether the result diff -cpr pgsql-orig/src/include/catalog/pg_type.h pgsql/src/include/catalog/pg_type.h *** pgsql-orig/src/include/catalog/pg_type.h 2005-09-08 11:10:16.447303128 +0900 --- pgsql/src/include/catalog/pg_type.h 2005-09-08 11:11:04.903936600 +0900 *************** DATA(insert OID = 30 ( oidvector PGNSP *** 306,311 **** --- 306,319 ---- DESCR("array of oids, used in system tables"); #define OIDVECTOROID 30 + DATA(insert OID = 31 ( string PGNSP PGUID -3 f b t \054 0 0 stringin stringout stringrecv stringsend - c p f 0 -1 0 _null_ _null_ )); + DESCR("variable-length string, no limit specified"); + #define STRINGOID 31 + + DATA(insert OID = 32 ( binary PGNSP PGUID -3 f b t \054 0 0 binaryin binaryout binaryrecv binarysend - c p f 0 -1 0 _null_ _null_ )); + DESCR("variable-length string, binary values escaped"); + #define BINARYOID 32 + /* hand-built rowtype entries for bootstrapped catalogs: */ DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c t \054 1247 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ )); diff -cpr pgsql-orig/src/include/fmgr.h pgsql/src/include/fmgr.h *** pgsql-orig/src/include/fmgr.h 2005-09-08 11:10:16.418307536 +0900 --- pgsql/src/include/fmgr.h 2005-09-08 11:11:04.904936448 +0900 *************** extern struct varlena *pg_detoast_datum_ *** 171,176 **** --- 171,186 ---- (int32) f, (int32) c) /* + * Support for fetching detoasted copies of toastable varlena2 datatypes. + * pg_detoast_datum2() gives you either the input datum (if not toasted) + * or a detoasted copy allocated with palloc(). + */ + extern struct varlena2 *pg_detoast_datum2(struct varlena2 * datum); + + #define PG_DETOAST_DATUM2(datum) \ + pg_detoast_datum2((struct varlena2 *) DatumGetPointer(datum)) + + /* * Support for cleaning up detoasted copies of inputs. This must only * be used for pass-by-ref datatypes, and normally would only be used * for toastable types. If the given pointer is different from the *************** extern struct varlena *pg_detoast_datum_ *** 204,217 **** --- 214,234 ---- #define PG_GETARG_INT64(n) DatumGetInt64(PG_GETARG_DATUM(n)) /* use this if you want the raw, possibly-toasted input datum: */ #define PG_GETARG_RAW_VARLENA_P(n) ((struct varlena *) PG_GETARG_POINTER(n)) + #define PG_GETARG_RAW_VARLENA2_P(n) ((struct varlena2 *) PG_GETARG_POINTER(n)) /* use this if you want the input datum de-toasted: */ #define PG_GETARG_VARLENA_P(n) PG_DETOAST_DATUM(PG_GETARG_DATUM(n)) + #define PG_GETARG_VARLENA2_P(n) PG_DETOAST_DATUM2(PG_GETARG_DATUM(n)) /* DatumGetFoo macros for varlena types will typically look like this: */ #define DatumGetByteaP(X) ((bytea *) PG_DETOAST_DATUM(X)) #define DatumGetTextP(X) ((text *) PG_DETOAST_DATUM(X)) #define DatumGetBpCharP(X) ((BpChar *) PG_DETOAST_DATUM(X)) #define DatumGetVarCharP(X) ((VarChar *) PG_DETOAST_DATUM(X)) #define DatumGetHeapTupleHeader(X) ((HeapTupleHeader) PG_DETOAST_DATUM(X)) + /* DatumGetFoo macros for varlena2 types will typically look like this: */ + #define DatumGetRawVarlena2P(X) ((struct varlena2 *) DatumGetPointer(X)) + #define DatumGetVarlena2P(X) PG_DETOAST_DATUM2(X) + #define DatumGetStringP(X) ((String *) DatumGetVarlena2P(X)) + #define DatumGetBinaryP(X) ((Binary *) DatumGetVarlena2P(X)) /* And we also offer variants that return an OK-to-write copy */ #define DatumGetByteaPCopy(X) ((bytea *) PG_DETOAST_DATUM_COPY(X)) #define DatumGetTextPCopy(X) ((text *) PG_DETOAST_DATUM_COPY(X)) *************** extern struct varlena *pg_detoast_datum_ *** 226,231 **** --- 243,250 ---- /* GETARG macros for varlena types will typically look like this: */ #define PG_GETARG_BYTEA_P(n) DatumGetByteaP(PG_GETARG_DATUM(n)) #define PG_GETARG_TEXT_P(n) DatumGetTextP(PG_GETARG_DATUM(n)) + #define PG_GETARG_STRING_P(n) DatumGetStringP(PG_GETARG_DATUM(n)) + #define PG_GETARG_BINARY_P(n) DatumGetBinaryP(PG_GETARG_DATUM(n)) #define PG_GETARG_BPCHAR_P(n) DatumGetBpCharP(PG_GETARG_DATUM(n)) #define PG_GETARG_VARCHAR_P(n) DatumGetVarCharP(PG_GETARG_DATUM(n)) #define PG_GETARG_HEAPTUPLEHEADER(n) DatumGetHeapTupleHeader(PG_GETARG_DATUM(n)) *************** extern struct varlena *pg_detoast_datum_ *** 267,272 **** --- 286,293 ---- /* RETURN macros for other pass-by-ref types will typically look like this: */ #define PG_RETURN_BYTEA_P(x) PG_RETURN_POINTER(x) #define PG_RETURN_TEXT_P(x) PG_RETURN_POINTER(x) + #define PG_RETURN_STRING_P(x) PG_RETURN_POINTER(x) + #define PG_RETURN_BINARY_P(x) PG_RETURN_POINTER(x) #define PG_RETURN_BPCHAR_P(x) PG_RETURN_POINTER(x) #define PG_RETURN_VARCHAR_P(x) PG_RETURN_POINTER(x) #define PG_RETURN_HEAPTUPLEHEADER(x) PG_RETURN_POINTER(x) diff -cpr pgsql-orig/src/include/utils/builtins.h pgsql/src/include/utils/builtins.h *** pgsql-orig/src/include/utils/builtins.h 2005-09-08 11:10:16.392311488 +0900 --- pgsql/src/include/utils/builtins.h 2005-09-08 11:11:04.906936144 +0900 *************** extern Datum to_hex64(PG_FUNCTION_ARGS); *** 601,606 **** --- 601,616 ---- extern Datum md5_text(PG_FUNCTION_ARGS); extern Datum md5_bytea(PG_FUNCTION_ARGS); + extern Datum stringin(PG_FUNCTION_ARGS); + extern Datum stringout(PG_FUNCTION_ARGS); + extern Datum stringrecv(PG_FUNCTION_ARGS); + extern Datum stringsend(PG_FUNCTION_ARGS); + + extern Datum binaryin(PG_FUNCTION_ARGS); + extern Datum binaryout(PG_FUNCTION_ARGS); + extern Datum binaryrecv(PG_FUNCTION_ARGS); + extern Datum binarysend(PG_FUNCTION_ARGS); + extern Datum unknownin(PG_FUNCTION_ARGS); extern Datum unknownout(PG_FUNCTION_ARGS); extern Datum unknownrecv(PG_FUNCTION_ARGS);