#include <cstring>
#include "postgres.h"
#include "plugin_postgres.h"
#include "fmgr.h"
#include "funcapi.h"
#include "utils/numeric.h"
#include "utils/builtins.h"
#include "utils/typcache.h"
#include "utils/lsyscache.h"
#include "utils/date.h"
#include "utils/varbit.h"
#include "plugin_commands/mysqlmode.h"
#ifdef DOLPHIN
#define WEIGHTSTRING_DESC_LEVEL1 0x00000100
#define WEIGHTSTRING_REVERSE_LEVEL1 0x00010000
#define SINGLE_BYTE_LENGTH 1
#define DOUBLE_BYTE_LENGTH 2
#define TRIPLE_BYTE_LENGTH 3
#define QURADRUPLE_BYTE_LENGTH 4
#define BUFFER_TO_ULONG_BYTE1_SHIFT 8
#define INVALID_LEN -1
#define TWO_NUM_ARGS 2
#define THREE_NUM_ARGS 3
#define MIN_HEX_LOOP 0
#define MAX_HEX_LOOP 127
PG_FUNCTION_INFO_V1_PUBLIC(weight_string);
extern "C" DLL_PUBLIC Datum weight_string(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1_PUBLIC(weight_string_single);
extern "C" DLL_PUBLIC Datum weight_string_single(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1_PUBLIC(weight_string_bytea);
extern "C" DLL_PUBLIC Datum weight_string_bytea(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1_PUBLIC(weight_string_boolean);
extern "C" DLL_PUBLIC Datum weight_string_boolean(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1_PUBLIC(weight_string_date);
extern "C" DLL_PUBLIC Datum weight_string_date(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1_PUBLIC(weight_string_time);
extern "C" DLL_PUBLIC Datum weight_string_time(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1_PUBLIC(weight_string_timestamp);
extern "C" DLL_PUBLIC Datum weight_string_timestamp(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1_PUBLIC(weight_string_timestamptz);
extern "C" DLL_PUBLIC Datum weight_string_timestamptz(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1_PUBLIC(weight_string_interval);
extern "C" DLL_PUBLIC Datum weight_string_interval(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1_PUBLIC(weight_string_bit);
extern "C" DLL_PUBLIC Datum weight_string_bit(PG_FUNCTION_ARGS);
static void store16be(pg_wchar u, char *res)
{
if (u >= 'a' && u <= 'y') {
u &= ~0x20;
}
*res = (char)((u >> BUFFER_TO_ULONG_BYTE1_SHIFT) & 0xff);
*(res + 1) = (char)(u & 0xff);
}
static size_t char_to_unicode(char *src, size_t src_len, char *output, int output_len, int encoding)
{
char *utf8_string = (char *)pg_do_encoding_conversion((unsigned char *)src, (int)src_len, encoding, PG_UTF8);
pg_wchar *wchar = (pg_wchar *)palloc0((src_len + SINGLE_BYTE_LENGTH) * sizeof(pg_wchar));
int wchar_size = pg_encoding_mb2wchar_with_len(PG_UTF8, utf8_string, wchar, (int)src_len);
int complete_len = 0;
if (output_len == INVALID_LEN) {
output_len = wchar_size * DOUBLE_BYTE_LENGTH;
}
for (int i = 0; complete_len < output_len; i++) {
if (i < wchar_size) {
store16be(*(wchar + i), output);
} else {
store16be(0x20, output);
}
output += DOUBLE_BYTE_LENGTH;
complete_len += DOUBLE_BYTE_LENGTH;
}
if (utf8_string != src)
pfree_ext(utf8_string);
pfree_ext(wchar);
return complete_len;
}
static void reverse_and_transform(char *start, char *end, bool flag_dsc, bool flag_rev)
{
if (flag_dsc) {
if (flag_rev) {
for (end--; start <= end;) {
u_char tmp = *start;
*start++ = ~*end;
*end-- = ~tmp;
}
} else {
for (; start < end; start++)
*start = ~*start;
}
} else if (flag_rev) {
for (end--; start < end;) {
u_char tmp = *start;
*start++ = *end;
*end-- = tmp;
}
}
}
static const char hextbl[] = "0123456789abcdef";
static const int8 hexlookup[128] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
static inline char get_hex(char c, bool flag, bool &isValid)
{
int res = -1;
isValid = true;
if (c > MIN_HEX_LOOP && c < MAX_HEX_LOOP)
res = hexlookup[(unsigned char)c];
if (res < 0) {
if (flag)
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid hexadecimal digit: \"%c\"", c)));
else {
isValid = false;
return 0;
}
}
return (char)res;
}
static inline unsigned hex_decode(const char *src, unsigned len, char *dst, bool flag)
{
const char *s = NULL;
const char *srcend;
char v1;
char v2;
char *p;
srcend = src + len;
s = src;
p = dst;
while (s < srcend) {
if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r') {
s++;
continue;
}
bool isValid;
const int shiftLeft = 4;
v1 = get_hex(*s++, flag, isValid) << shiftLeft;
if (!isValid) {
*dst = 0;
return 0;
}
if (s >= srcend) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid hexadecimal data: odd number of digits")));
}
v2 = get_hex(*s++, flag, isValid);
if (!isValid) {
*dst = 0;
return 0;
}
*p++ = v1 | v2;
}
return p - dst;
}
static bytea* hex_decode_internal(char* data, int dataLength)
{
int resultLength;
int res;
resultLength = dataLength >> SINGLE_BYTE_LENGTH;
errno_t rc = EOK;
char* originData = (char*)palloc(dataLength + 1);
rc = memcpy_s(originData, dataLength, data, dataLength);
securec_check(rc, "\0", "\0");
originData[dataLength] = '\0';
const int evenLength = 2;
char* newData;
if (dataLength % evenLength != 0) {
resultLength += 1;
dataLength += 1;
newData = (char*)palloc(dataLength + 1);
*newData = '0';
rc = memcpy_s(newData + 1, dataLength - 1, data, dataLength - 1);
securec_check(rc, "\0", "\0");
} else {
newData = (char*)palloc(dataLength + 1);
rc = memcpy_s(newData, dataLength, data, dataLength);
securec_check(rc, "\0", "\0");
}
newData[dataLength] = '\0';
bytea* result = (bytea*)palloc(VARHDRSZ + resultLength);
res = hex_decode(newData, dataLength, VARDATA(result), false);
if (dataLength != 0 && res == 0) {
int errlevel = SQL_MODE_STRICT() ? ERROR : WARNING;
ereport(errlevel,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Incorrect string value: \"%s\" for function unhex ", originData)));
}
pfree(newData);
pfree(originData);
SET_VARSIZE(result, VARHDRSZ + res);
return result;
}
static bytea *bit_to_bytea(int64 arg_int, int len)
{
char *hexResult = (char *)palloc(len + 1);
hexResult[len] = '\0';
const int andBits = 15;
const int rightBits = 4;
do {
hexResult[--len] = hextbl[arg_int & andBits];
arg_int >>= rightBits;
} while (len > 0);
bytea *decodeResult = hex_decode_internal(hexResult, strlen(hexResult));
pfree(hexResult);
return decodeResult;
}
Datum weight_string(PG_FUNCTION_ARGS)
{
int encoding;
char *value = (char *)VARDATA(PG_GETARG_TEXT_P(0));
size_t value_len = VARSIZE(PG_GETARG_TEXT_P(0)) - VARHDRSZ;
char *type = (char *)VARDATA(PG_GETARG_TEXT_P(1));
size_t type_len = VARSIZE(PG_GETARG_TEXT_P(1)) - VARHDRSZ;
size_t data_len = PG_GETARG_UINT32(2);
int err_level = !fcinfo->can_ignore && SQL_MODE_STRICT() ? ERROR : WARNING;
if (VARATT_IS_HUGE_TOAST_POINTER(value)) {
ereport(err_level,
(errcode(ERRCODE_DATA_EXCEPTION), errmsg("weight_string data size too large; the maximum size is 1GB "
"(probably, length of weight_string data was corrupted)")));
PG_RETURN_NULL();
}
if (!strncmp(type, "BINARY", type_len)) {
if (VARATT_IS_HUGE_TOAST_POINTER(value) || !AllocSizeIsValid(data_len)) {
ereport(err_level, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("weight_string could not support more than 1GB data")));
PG_RETURN_NULL();
}
if (value_len > data_len)
value_len = data_len;
text *result = (text *)palloc0(VARHDRSZ + data_len + SINGLE_BYTE_LENGTH);
errno_t rc = strncpy_s((char *)VARDATA(result), data_len + SINGLE_BYTE_LENGTH, value, value_len);
securec_check(rc, "\0", "\0");
SET_VARSIZE(result, VARHDRSZ + data_len);
PG_RETURN_TEXT_P(result);
} else if (!strncmp(type, "CHAR", type_len)) {
size_t retlen = data_len * 2;
if (VARATT_IS_HUGE_TOAST_POINTER(value) || !AllocSizeIsValid(retlen)) {
ereport(err_level, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("weight_string could not support more than 1GB data")));
PG_RETURN_NULL();
}
text *result = (text *)palloc0(VARHDRSZ + retlen + 1);
char *result_string = (char *)VARDATA(result);
char *start = result_string;
char *end = result_string + retlen;
size_t flags = PG_GETARG_UINT32(3);
uint flag_dsc = flags & WEIGHTSTRING_DESC_LEVEL1;
uint flag_rev = flags & WEIGHTSTRING_REVERSE_LEVEL1;
encoding = pg_get_client_encoding();
if (encoding < 0) {
encoding = PG_SQL_ASCII;
}
char_to_unicode(value, value_len, result_string, retlen, encoding);
if (flag_dsc || flag_rev) {
reverse_and_transform(start, end, flag_dsc, flag_rev);
}
SET_VARSIZE(result, VARHDRSZ + retlen);
PG_RETURN_TEXT_P(result);
} else {
ereport(err_level,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("weight_string function does not support syntax other than 'AS CHAR' and 'AS BINARY'.")));
}
PG_RETURN_NULL();
}
Datum weight_string_single(PG_FUNCTION_ARGS)
{
int encoding;
char *value = (char *)VARDATA(PG_GETARG_TEXT_P(0));
size_t data_len = VARSIZE(PG_GETARG_TEXT_P(0)) - VARHDRSZ;
size_t retlen = data_len * 2;
text *result = (text *)palloc(VARHDRSZ + retlen + 1);
char *result_string = (char *)VARDATA(result);
int rc = memset_s(result_string, retlen + 1, ' ', retlen + 1);
securec_check(rc, "\0", "\0");
char *start = result_string;
char *end = NULL;
size_t flags = PG_GETARG_UINT32(1);
uint flag_dsc = flags & WEIGHTSTRING_DESC_LEVEL1;
uint flag_rev = flags & WEIGHTSTRING_REVERSE_LEVEL1;
int err_level = !fcinfo->can_ignore && SQL_MODE_STRICT() ? ERROR : WARNING;
if (VARATT_IS_HUGE_TOAST_POINTER(value)) {
ereport(err_level,
(errcode(ERRCODE_DATA_EXCEPTION), errmsg("weight_string data size too large; the maximum size is 1GB "
"(probably, length of weight_string data was corrupted)")));
PG_RETURN_NULL();
}
encoding = pg_get_client_encoding();
if (encoding < 0) {
encoding = PG_SQL_ASCII;
}
end = start + retlen;
retlen = char_to_unicode(value, data_len, result_string, INVALID_LEN, encoding);
if (flag_dsc || flag_rev) {
reverse_and_transform(start, end, flag_dsc, flag_rev);
}
SET_VARSIZE(result, VARHDRSZ + retlen);
PG_RETURN_TEXT_P(result);
}
static text* weight_string_bit_bytea_common(char* value, FunctionCallInfoData *fcinfo, size_t value_len, bool toUnicode)
{
if (fcinfo->nargs >= THREE_NUM_ARGS) {
char *type = (char *)VARDATA(PG_GETARG_TEXT_P(1));
size_t type_len = VARSIZE(PG_GETARG_TEXT_P(1)) - VARHDRSZ;
size_t data_len = PG_GETARG_UINT32(2);
int err_level = !fcinfo->can_ignore && SQL_MODE_STRICT() ? ERROR : WARNING;
if (!strncmp(type, "BINARY", type_len)) {
if (VARATT_IS_HUGE_TOAST_POINTER(value) || !AllocSizeIsValid(data_len)) {
ereport(err_level, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("weight_string could not support more than 1GB data")));
return cstring_to_text("");
}
if (value_len > data_len)
value_len = data_len;
text *result = (text *)palloc0(VARHDRSZ + data_len + SINGLE_BYTE_LENGTH);
errno_t rc = strncpy_s((char *)VARDATA(result), data_len + SINGLE_BYTE_LENGTH, value, value_len);
securec_check(rc, "\0", "\0");
SET_VARSIZE(result, VARHDRSZ + data_len);
return result;
} else if (!strncmp(type, "CHAR", type_len)) {
size_t retlen = data_len * 2;
if (VARATT_IS_HUGE_TOAST_POINTER(value) || !AllocSizeIsValid(retlen)) {
ereport(err_level, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("weight_string could not support more than 1GB data")));
return cstring_to_text("");
}
text *result = (text *)palloc0(VARHDRSZ + retlen + 1);
char *result_string = (char *)VARDATA(result);
char *start = result_string;
size_t flags = PG_GETARG_UINT32(3);
uint flag_dsc = flags & WEIGHTSTRING_DESC_LEVEL1;
uint flag_rev = flags & WEIGHTSTRING_REVERSE_LEVEL1;
int encoding = pg_get_client_encoding();
if (encoding < 0) {
encoding = PG_SQL_ASCII;
}
if (toUnicode) {
char_to_unicode(value, value_len, result_string, retlen, encoding);
} else {
errno_t rc = strncpy_s((char *)VARDATA(result), data_len + SINGLE_BYTE_LENGTH, value, value_len);
securec_check(rc, "\0", "\0");
retlen = data_len;
}
char *end = result_string + retlen;
if (flag_dsc || flag_rev) {
reverse_and_transform(start, end, flag_dsc, flag_rev);
}
SET_VARSIZE(result, VARHDRSZ + retlen);
return result;
} else {
ereport(err_level,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("weight_string function does not support syntax other than 'AS CHAR' and 'AS BINARY'.")));
}
return cstring_to_text("");
}
text *result = (text *)palloc0(VARHDRSZ + value_len + SINGLE_BYTE_LENGTH);
errno_t rc = strncpy_s((char *)VARDATA(result), value_len + SINGLE_BYTE_LENGTH, value, value_len);
securec_check(rc, "\0", "\0");
SET_VARSIZE(result, VARHDRSZ + value_len);
return result;
}
Datum weight_string_bytea(PG_FUNCTION_ARGS)
{
bytea *in = PG_GETARG_BYTEA_PP(0);
char *value = VARDATA_ANY(in);
size_t value_len = VARSIZE_ANY_EXHDR(in);
text* result = weight_string_bit_bytea_common(value, fcinfo, value_len, true);
PG_RETURN_TEXT_P(result);
}
Datum weight_string_boolean(PG_FUNCTION_ARGS)
{
if (fcinfo->nargs == TWO_NUM_ARGS)
PG_RETURN_NULL();
char *type = (char *)VARDATA(PG_GETARG_TEXT_P(1));
size_t type_len = VARSIZE(PG_GETARG_TEXT_P(1)) - VARHDRSZ;
if (!strncmp(type, "BINARY", type_len)) {
bool istrue = PG_GETARG_BOOL(0);
size_t data_len = PG_GETARG_UINT32(2);
text *result = (text *)palloc0(VARHDRSZ + data_len);
*((char *)VARDATA(result)) = istrue ? '1' : '0';
SET_VARSIZE(result, VARHDRSZ + data_len);
PG_RETURN_TEXT_P(result);
} else {
PG_RETURN_NULL();
}
}
static text* date_for_binary(char *value, size_t value_len, size_t data_len)
{
if (value_len > data_len)
value_len = data_len;
text *result = (text *)palloc0(VARHDRSZ + data_len + SINGLE_BYTE_LENGTH);
error_t rc = strncpy_s((char *)VARDATA(result), value_len + SINGLE_BYTE_LENGTH, (char *)value, value_len);
securec_check(rc, "\0", "\0");
SET_VARSIZE(result, VARHDRSZ + data_len);
return result;
}
static text* date_for_char(char *value, size_t value_len, size_t data_len, size_t flags)
{
if (value_len > data_len)
value_len = data_len;
uint flag_dsc = flags & WEIGHTSTRING_DESC_LEVEL1;
uint flag_rev = flags & WEIGHTSTRING_REVERSE_LEVEL1;
text *result = (text *)palloc(VARHDRSZ + data_len + SINGLE_BYTE_LENGTH);
size_t i = 0;
char *result_string = (char *)VARDATA(result);
for (; i < value_len; i++) {
result_string[i] = value[i];
}
while (data_len > value_len) {
result_string[value_len] = ' ';
value_len++;
}
result_string[value_len] = '\0';
if (flag_dsc || flag_rev) {
char *start = result_string;
char *end = start + data_len;
reverse_and_transform(start, end, flag_dsc, flag_rev);
}
SET_VARSIZE(result, VARHDRSZ + data_len);
return result;
}
static text* weight_string_date_common(char* value, FunctionCallInfoData *fcinfo)
{
if (fcinfo->nargs == TWO_NUM_ARGS) {
return cstring_to_text(value);
} else if (fcinfo->nargs == THREE_NUM_ARGS) {
size_t value_len = strlen(value);
size_t data_len = PG_GETARG_UINT32(2);
text* result = date_for_binary(value, value_len, data_len);
return result;
} else {
size_t value_len = strlen(value);
size_t data_len = PG_GETARG_UINT32(2);
size_t flags = PG_GETARG_UINT32(3);
text* result = date_for_char(value, value_len, data_len, flags);
return result;
}
}
Datum weight_string_date(PG_FUNCTION_ARGS)
{
char* value = NULL;
DateADT dateVal = PG_GETARG_DATEADT(0);
value = DatumGetCString(DirectFunctionCall1(date_out, dateVal));
text* result = weight_string_date_common(value, fcinfo);
pfree(value);
PG_RETURN_TEXT_P(result);
}
Datum weight_string_time(PG_FUNCTION_ARGS)
{
char* value = NULL;
TimeADT time = PG_GETARG_TIMEADT(0);
value = DatumGetCString(DirectFunctionCall1(time_out, time));
text* result = weight_string_date_common(value, fcinfo);
pfree(value);
PG_RETURN_TEXT_P(result);
}
Datum weight_string_timestamp(PG_FUNCTION_ARGS)
{
char* value = NULL;
Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
value = DatumGetCString(DirectFunctionCall1(timestamp_out, timestamp));
text* result = weight_string_date_common(value, fcinfo);
pfree(value);
PG_RETURN_TEXT_P(result);
}
Datum weight_string_timestamptz(PG_FUNCTION_ARGS)
{
char* value = NULL;
TimestampTz timestamptz = PG_GETARG_TIMESTAMPTZ(0);
value = DatumGetCString(DirectFunctionCall1(timestamptz_out, timestamptz));
text* result = weight_string_date_common(value, fcinfo);
pfree(value);
PG_RETURN_TEXT_P(result);
}
Datum weight_string_interval(PG_FUNCTION_ARGS)
{
char* value = NULL;
Interval* span = PG_GETARG_INTERVAL_P(0);
value = DatumGetCString(DirectFunctionCall1(interval_out, PointerGetDatum(span)));
text* result = weight_string_date_common(value, fcinfo);
pfree(value);
PG_RETURN_TEXT_P(result);
}
Datum weight_string_bit(PG_FUNCTION_ARGS)
{
int64 arg_int = DatumGetInt64(DirectFunctionCall1(bittoint8, PG_GETARG_DATUM(0)));
int bit_len = VARBITBYTES(PG_GETARG_VARBIT_P(0));
bytea* bvalue = bit_to_bytea(arg_int, bit_len);
char* value = VARDATA_ANY(bvalue);
size_t value_len = VARSIZE_ANY_EXHDR(value);
text* result = weight_string_bit_bytea_common(value, fcinfo, value_len, false);
pfree(bvalue);
PG_RETURN_TEXT_P(result);
}
#endif