2017-01-17 16:01:12 +03:00
|
|
|
#include "postgres.h"
|
|
|
|
#include "decode.h"
|
|
|
|
#include <lib/stringinfo.h>
|
|
|
|
#include <access/htup_details.h>
|
|
|
|
#include <access/tupmacs.h>
|
|
|
|
#include <datatype/timestamp.h>
|
|
|
|
#include <common/pg_lzcompress.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <assert.h>
|
2017-11-27 14:53:51 +03:30
|
|
|
#include <sys/socket.h>
|
2017-01-17 16:01:12 +03:00
|
|
|
|
|
|
|
#define ATTRTYPES_STR_MAX_LEN (1024-1)
|
2017-11-27 14:53:51 +03:30
|
|
|
#define PGSQL_AF_INET (AF_INET + 0)
|
|
|
|
#define PGSQL_AF_INET6 (AF_INET + 1)
|
2017-01-17 16:01:12 +03:00
|
|
|
|
|
|
|
typedef int (*decode_callback_t)(const char* buffer, unsigned int buff_size,
|
|
|
|
unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_smallint(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_int(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_bigint(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_time(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_timetz(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_date(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_timestamp(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_float4(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_float8(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_bool(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_uuid(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_macaddr(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_string(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
2017-03-01 15:08:09 +03:00
|
|
|
static int
|
|
|
|
decode_char(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_name(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
2017-11-27 14:53:51 +03:30
|
|
|
static int
|
|
|
|
decode_inet(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
2017-03-01 15:08:09 +03:00
|
|
|
static int
|
|
|
|
decode_ignore(const char* buffer, unsigned int buff_size, unsigned int* out_size);
|
|
|
|
|
2017-01-17 16:01:12 +03:00
|
|
|
static int ncallbacks = 0;
|
|
|
|
static decode_callback_t callbacks[ATTRTYPES_STR_MAX_LEN / 2] = { NULL };
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char* name;
|
|
|
|
decode_callback_t callback;
|
|
|
|
} ParseCallbackTableItem;
|
|
|
|
|
|
|
|
static ParseCallbackTableItem callback_table[] = {
|
|
|
|
{ "smallserial", &decode_smallint },
|
|
|
|
{ "smallint", &decode_smallint },
|
|
|
|
{ "int", &decode_int },
|
2017-03-01 15:08:09 +03:00
|
|
|
{ "oid", &decode_int },
|
|
|
|
{ "xid", &decode_int },
|
2017-01-17 16:01:12 +03:00
|
|
|
{ "serial", &decode_int },
|
|
|
|
{ "bigint", &decode_bigint },
|
|
|
|
{ "bigserial", &decode_bigint },
|
|
|
|
{ "time", &decode_time },
|
|
|
|
{ "timetz", &decode_timetz },
|
|
|
|
{ "date", &decode_date },
|
|
|
|
{ "timestamp", &decode_timestamp },
|
2017-03-01 15:08:09 +03:00
|
|
|
{ "real", &decode_float4 },
|
2017-01-17 16:01:12 +03:00
|
|
|
{ "float4", &decode_float4 },
|
|
|
|
{ "float8", &decode_float8 },
|
|
|
|
{ "float", &decode_float8 },
|
|
|
|
{ "bool", &decode_bool },
|
|
|
|
{ "uuid", &decode_uuid },
|
|
|
|
{ "macaddr", &decode_macaddr },
|
2017-03-01 15:08:09 +03:00
|
|
|
{ "name", &decode_name },
|
|
|
|
{ "char", &decode_char },
|
2017-11-27 14:53:51 +03:30
|
|
|
{ "inet", &decode_inet },
|
2017-03-01 15:08:09 +03:00
|
|
|
{ "~", &decode_ignore },
|
2017-01-17 16:01:12 +03:00
|
|
|
|
|
|
|
/* internally all string types are stored the same way */
|
2017-03-01 15:08:09 +03:00
|
|
|
{ "charN", &decode_string },
|
2017-01-17 16:01:12 +03:00
|
|
|
{ "varchar", &decode_string },
|
2017-03-01 15:08:09 +03:00
|
|
|
{ "varcharN", &decode_string },
|
2017-01-17 16:01:12 +03:00
|
|
|
{ "text", &decode_string },
|
|
|
|
{ "json", &decode_string },
|
|
|
|
{ "xml", &decode_string },
|
|
|
|
{ NULL, NULL},
|
|
|
|
};
|
|
|
|
|
|
|
|
static StringInfoData copyString;
|
|
|
|
static bool copyStringInitDone = false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Temporary buffer for storing decompressed data.
|
|
|
|
*
|
|
|
|
* 64K should be enough in most cases. If it's not user can manually change
|
|
|
|
* this limit. Unfortunately there is no way to know how much memory user
|
|
|
|
* is willing to allocate.
|
|
|
|
*/
|
|
|
|
static char decompress_tmp_buff[64*1024];
|
|
|
|
|
|
|
|
/* Used by some PostgreSQL macro definitions */
|
|
|
|
void
|
|
|
|
ExceptionalCondition(const char *conditionName,
|
|
|
|
const char *errorType,
|
|
|
|
const char *fileName,
|
|
|
|
int lineNumber)
|
|
|
|
{
|
|
|
|
printf("Exceptional condition: name = %s, type = %s, fname = %s, line = %d\n",
|
|
|
|
conditionName ? conditionName : "(NULL)",
|
|
|
|
errorType ? errorType : "(NULL)",
|
|
|
|
fileName ? fileName : "(NULL)",
|
|
|
|
lineNumber);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Append given string to current COPY line */
|
|
|
|
static void
|
|
|
|
CopyAppend(const char* str)
|
|
|
|
{
|
|
|
|
if(!copyStringInitDone)
|
|
|
|
{
|
|
|
|
initStringInfo(©String);
|
|
|
|
copyStringInitDone = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Caller probably wanted just to init copyString */
|
|
|
|
if(str == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(copyString.data[0] != '\0')
|
2017-11-27 14:53:51 +03:30
|
|
|
appendStringInfoString(©String, ",");
|
2017-01-17 16:01:12 +03:00
|
|
|
|
|
|
|
appendStringInfoString(©String, str);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Append given string to current COPY line and encode special symbols
|
|
|
|
* like \r, \n, \t and \\.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
CopyAppendEncode(const char* str, int orig_len)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Should be enough in most cases. If it's not user can manually change
|
|
|
|
* this limit. Unfortunately there is no way to know how much memory user
|
|
|
|
* is willing to allocate.
|
|
|
|
*/
|
|
|
|
static char tmp_buff[64*1024];
|
|
|
|
/* Reserve one byte for a trailing zero. */
|
|
|
|
const int max_offset = sizeof(tmp_buff) - 2;
|
|
|
|
int curr_offset = 0;
|
|
|
|
int len = orig_len;
|
|
|
|
|
|
|
|
while(len > 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Make sure there is enough free space for at least one special symbol
|
|
|
|
* and a trailing zero.
|
|
|
|
*/
|
|
|
|
if(curr_offset > max_offset - 2)
|
|
|
|
{
|
|
|
|
printf("ERROR: Unable to properly encode a string since it's too "
|
|
|
|
"large (%d bytes). Try to increase tmp_buff size in CopyAppendEncode "
|
|
|
|
"procedure.\n", orig_len);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since we are working with potentially corrupted data we can encounter
|
|
|
|
* \0 as well.
|
|
|
|
*/
|
|
|
|
if(*str == '\0')
|
|
|
|
{
|
|
|
|
tmp_buff[curr_offset] = '\\';
|
|
|
|
tmp_buff[curr_offset+1] = '0';
|
|
|
|
curr_offset += 2;
|
|
|
|
}
|
|
|
|
else if(*str == '\r')
|
|
|
|
{
|
|
|
|
tmp_buff[curr_offset] = '\\';
|
|
|
|
tmp_buff[curr_offset+1] = 'r';
|
|
|
|
curr_offset += 2;
|
|
|
|
}
|
|
|
|
else if(*str == '\n')
|
|
|
|
{
|
|
|
|
tmp_buff[curr_offset] = '\\';
|
|
|
|
tmp_buff[curr_offset+1] = 'n';
|
|
|
|
curr_offset += 2;
|
|
|
|
}
|
|
|
|
else if(*str == '\t')
|
|
|
|
{
|
|
|
|
tmp_buff[curr_offset] = '\\';
|
|
|
|
tmp_buff[curr_offset+1] = 'r';
|
|
|
|
curr_offset += 2;
|
|
|
|
}
|
|
|
|
else if(*str == '\\')
|
|
|
|
{
|
|
|
|
tmp_buff[curr_offset] = '\\';
|
|
|
|
tmp_buff[curr_offset+1] = '\\';
|
|
|
|
curr_offset += 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* It's a regular symbol. */
|
|
|
|
tmp_buff[curr_offset] = *str;
|
|
|
|
curr_offset++;
|
|
|
|
}
|
|
|
|
|
|
|
|
str++;
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp_buff[curr_offset] = '\0';
|
|
|
|
CopyAppend(tmp_buff);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* CopyAppend version with format string support */
|
|
|
|
#define CopyAppendFmt(fmt, ...) do { \
|
|
|
|
char __copy_format_buff[512]; \
|
|
|
|
snprintf(__copy_format_buff, sizeof(__copy_format_buff), fmt, ##__VA_ARGS__); \
|
|
|
|
CopyAppend(__copy_format_buff); \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
/* Discard accumulated COPY line */
|
|
|
|
static void
|
|
|
|
CopyClear(void)
|
|
|
|
{
|
|
|
|
/* Make sure init is done */
|
|
|
|
CopyAppend(NULL);
|
|
|
|
|
|
|
|
resetStringInfo(©String);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Output and then clear accumulated COPY line */
|
|
|
|
static void
|
|
|
|
CopyFlush(void)
|
|
|
|
{
|
|
|
|
/* Make sure init is done */
|
|
|
|
CopyAppend(NULL);
|
|
|
|
|
2017-11-27 14:53:51 +03:30
|
|
|
fprintf(stderr, "%s\n", copyString.data);
|
2017-01-17 16:01:12 +03:00
|
|
|
CopyClear();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a callback to `callbacks` table for given type name
|
|
|
|
*
|
|
|
|
* Arguments:
|
|
|
|
* type - name of a single type, always lowercase
|
|
|
|
*
|
|
|
|
* Return value is:
|
|
|
|
* == 0 - no error
|
|
|
|
* < 0 - invalid type name
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
AddTypeCallback(const char* type)
|
|
|
|
{
|
|
|
|
int idx = 0;
|
|
|
|
|
|
|
|
if(*type == '\0') /* ignore empty strings */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while(callback_table[idx].name != NULL)
|
|
|
|
{
|
|
|
|
if(strcmp(callback_table[idx].name, type) == 0)
|
|
|
|
{
|
|
|
|
callbacks[ncallbacks] = callback_table[idx].callback;
|
|
|
|
ncallbacks++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Error: type <%s> doesn't exist or is not currently supported\n", type);
|
|
|
|
printf("Full list of known types: ");
|
|
|
|
idx = 0;
|
|
|
|
while(callback_table[idx].name != NULL)
|
|
|
|
{
|
|
|
|
printf("%s ", callback_table[idx].name);
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode attribute types string like "int,timestamp,bool,uuid"
|
|
|
|
*
|
|
|
|
* Arguments:
|
|
|
|
* str - types string
|
|
|
|
* Return value is:
|
|
|
|
* == 0 - if string is valid
|
|
|
|
* < 0 - if string is invalid
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ParseAttributeTypesString(const char* str)
|
|
|
|
{
|
|
|
|
char *curr_type, *next_type;
|
|
|
|
char attrtypes[ATTRTYPES_STR_MAX_LEN+1];
|
|
|
|
int i, len = strlen(str);
|
|
|
|
|
|
|
|
if(len > ATTRTYPES_STR_MAX_LEN)
|
|
|
|
{
|
|
|
|
printf("Error: attribute types string is longer then %u characters!\n",
|
|
|
|
ATTRTYPES_STR_MAX_LEN);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(attrtypes, str);
|
|
|
|
for(i = 0; i < len; i++)
|
|
|
|
attrtypes[i] = tolower(attrtypes[i]);
|
|
|
|
|
|
|
|
curr_type = attrtypes;
|
|
|
|
while(curr_type)
|
|
|
|
{
|
|
|
|
next_type = strstr(curr_type, ",");
|
|
|
|
if(next_type)
|
|
|
|
{
|
|
|
|
*next_type = '\0';
|
|
|
|
next_type++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(AddTypeCallback(curr_type) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
curr_type = next_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert Julian day number (JDN) to a date.
|
|
|
|
* Copy-pasted from src/backend/utils/adt/datetime.c
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
j2date(int jd, int *year, int *month, int *day)
|
|
|
|
{
|
|
|
|
unsigned int julian;
|
|
|
|
unsigned int quad;
|
|
|
|
unsigned int extra;
|
|
|
|
int y;
|
|
|
|
|
|
|
|
julian = jd;
|
|
|
|
julian += 32044;
|
|
|
|
quad = julian / 146097;
|
|
|
|
extra = (julian - quad * 146097) * 4 + 3;
|
|
|
|
julian += 60 + quad * 3 + extra / 146097;
|
|
|
|
quad = julian / 1461;
|
|
|
|
julian -= quad * 1461;
|
|
|
|
y = julian * 4 / 1461;
|
|
|
|
julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
|
|
|
|
+ 123;
|
|
|
|
y += quad * 4;
|
|
|
|
*year = y - 4800;
|
|
|
|
quad = julian * 2141 / 65536;
|
|
|
|
*day = julian - 7834 * quad / 256;
|
|
|
|
*month = (quad + 10) % MONTHS_PER_YEAR + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode a smallint type */
|
|
|
|
static int
|
|
|
|
decode_smallint(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
const char* new_buffer = (const char*)TYPEALIGN(sizeof(int16), (uintptr_t)buffer);
|
|
|
|
unsigned int delta = (unsigned int)( (uintptr_t)new_buffer - (uintptr_t)buffer );
|
|
|
|
|
|
|
|
if(buff_size < delta)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buff_size -= delta;
|
|
|
|
buffer = new_buffer;
|
|
|
|
|
|
|
|
if(buff_size < sizeof(int16))
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
CopyAppendFmt("%d", (int)(*(int16*)buffer));
|
|
|
|
*out_size = sizeof(int16) + delta;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Decode an int type */
|
|
|
|
static int
|
|
|
|
decode_int(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
const char* new_buffer = (const char*)TYPEALIGN(sizeof(int32), (uintptr_t)buffer);
|
|
|
|
unsigned int delta = (unsigned int)( (uintptr_t)new_buffer - (uintptr_t)buffer );
|
|
|
|
|
|
|
|
if(buff_size < delta)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buff_size -= delta;
|
|
|
|
buffer = new_buffer;
|
|
|
|
|
|
|
|
if(buff_size < sizeof(int32))
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
CopyAppendFmt("%d", *(int32*)buffer);
|
|
|
|
*out_size = sizeof(int32) + delta;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode a bigint type */
|
|
|
|
static int
|
|
|
|
decode_bigint(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
const char* new_buffer = (const char*)TYPEALIGN(sizeof(int64), (uintptr_t)buffer);
|
|
|
|
unsigned int delta = (unsigned int)( (uintptr_t)new_buffer - (uintptr_t)buffer );
|
|
|
|
|
|
|
|
if(buff_size < delta)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buff_size -= delta;
|
|
|
|
buffer = new_buffer;
|
|
|
|
|
|
|
|
if(buff_size < sizeof(int64))
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
CopyAppendFmt("%ld", *(int64*)buffer);
|
|
|
|
*out_size = sizeof(int64) + delta;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode a time type */
|
|
|
|
static int
|
|
|
|
decode_time(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
const char* new_buffer = (const char*)TYPEALIGN(sizeof(int64), (uintptr_t)buffer);
|
|
|
|
unsigned int delta = (unsigned int)( (uintptr_t)new_buffer - (uintptr_t)buffer );
|
|
|
|
int64 timestamp, timestamp_sec;
|
|
|
|
|
|
|
|
if(buff_size < delta)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buff_size -= delta;
|
|
|
|
buffer = new_buffer;
|
|
|
|
|
|
|
|
if(buff_size < sizeof(int64))
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
timestamp = *(int64*)buffer;
|
|
|
|
timestamp_sec = timestamp / 1000000;
|
|
|
|
*out_size = sizeof(int64) + delta;
|
|
|
|
|
|
|
|
CopyAppendFmt("%02ld:%02ld:%02ld.%06ld",
|
|
|
|
timestamp_sec / 60 / 60, (timestamp_sec / 60) % 60, timestamp_sec % 60,
|
|
|
|
timestamp % 1000000);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode a timetz type */
|
|
|
|
static int
|
|
|
|
decode_timetz(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
const char* new_buffer = (const char*)TYPEALIGN(sizeof(int64), (uintptr_t)buffer);
|
|
|
|
unsigned int delta = (unsigned int)( (uintptr_t)new_buffer - (uintptr_t)buffer );
|
|
|
|
int64 timestamp, timestamp_sec;
|
|
|
|
int32 tz_sec, tz_min;
|
|
|
|
|
|
|
|
if(buff_size < delta)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buff_size -= delta;
|
|
|
|
buffer = new_buffer;
|
|
|
|
|
|
|
|
if(buff_size < (sizeof(int64) + sizeof(int32)))
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
timestamp = *(int64*)buffer;
|
|
|
|
tz_sec = *(int32*)(buffer + sizeof(int64));
|
|
|
|
timestamp_sec = timestamp / 1000000;
|
|
|
|
tz_min = - (tz_sec / 60);
|
|
|
|
*out_size = sizeof(int64) + sizeof(int32) + delta;
|
|
|
|
|
|
|
|
CopyAppendFmt("%02ld:%02ld:%02ld.%06ld%c%02d:%02d",
|
|
|
|
timestamp_sec / 60 / 60, (timestamp_sec / 60) % 60, timestamp_sec % 60,
|
|
|
|
timestamp % 1000000, (tz_min > 0 ? '+' : '-'), abs(tz_min / 60), abs(tz_min % 60));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode a date type */
|
|
|
|
static int
|
|
|
|
decode_date(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
const char* new_buffer = (const char*)TYPEALIGN(sizeof(int32), (uintptr_t)buffer);
|
|
|
|
unsigned int delta = (unsigned int)( (uintptr_t)new_buffer - (uintptr_t)buffer );
|
|
|
|
int32 jd, year, month, day;
|
|
|
|
|
|
|
|
if(buff_size < delta)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buff_size -= delta;
|
|
|
|
buffer = new_buffer;
|
|
|
|
|
|
|
|
if(buff_size < sizeof(int32))
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
*out_size = sizeof(int32) + delta;
|
|
|
|
|
|
|
|
jd = *(int32*)buffer + POSTGRES_EPOCH_JDATE;
|
|
|
|
j2date(jd, &year, &month, &day);
|
|
|
|
|
|
|
|
CopyAppendFmt("%04d-%02d-%02d%s", (year <= 0) ? -year + 1 : year, month, day, (year <= 0) ? " BC" : "");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode a timestamp type */
|
|
|
|
static int
|
|
|
|
decode_timestamp(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
const char* new_buffer = (const char*)TYPEALIGN(sizeof(int64), (uintptr_t)buffer);
|
|
|
|
unsigned int delta = (unsigned int)( (uintptr_t)new_buffer - (uintptr_t)buffer );
|
|
|
|
int64 timestamp, timestamp_sec;
|
|
|
|
int32 jd, year, month, day;
|
|
|
|
|
|
|
|
if(buff_size < delta)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buff_size -= delta;
|
|
|
|
buffer = new_buffer;
|
|
|
|
|
|
|
|
if(buff_size < sizeof(int64))
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
*out_size = sizeof(int64) + delta;
|
|
|
|
timestamp = *(int64*)buffer;
|
|
|
|
|
|
|
|
jd = timestamp / USECS_PER_DAY;
|
|
|
|
if (jd != 0)
|
|
|
|
timestamp -= jd * USECS_PER_DAY;
|
|
|
|
|
|
|
|
if (timestamp < INT64CONST(0))
|
|
|
|
{
|
|
|
|
timestamp += USECS_PER_DAY;
|
|
|
|
jd -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add offset to go from J2000 back to standard Julian date */
|
|
|
|
jd += POSTGRES_EPOCH_JDATE;
|
|
|
|
|
|
|
|
j2date(jd, &year, &month, &day);
|
|
|
|
timestamp_sec = timestamp / 1000000;
|
|
|
|
|
|
|
|
CopyAppendFmt("%04d-%02d-%02d %02ld:%02ld:%02ld.%06ld%s",
|
|
|
|
(year <= 0) ? -year + 1 : year, month, day,
|
|
|
|
timestamp_sec / 60 / 60, (timestamp_sec / 60) % 60, timestamp_sec % 60,
|
|
|
|
timestamp % 1000000,
|
|
|
|
(year <= 0) ? " BC" : "");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode a float4 type */
|
|
|
|
static int
|
|
|
|
decode_float4(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
const char* new_buffer = (const char*)TYPEALIGN(sizeof(float), (uintptr_t)buffer);
|
|
|
|
unsigned int delta = (unsigned int)( (uintptr_t)new_buffer - (uintptr_t)buffer );
|
|
|
|
|
|
|
|
if(buff_size < delta)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buff_size -= delta;
|
|
|
|
buffer = new_buffer;
|
|
|
|
|
|
|
|
if(buff_size < sizeof(float))
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
CopyAppendFmt("%.12f", *(float*)buffer);
|
|
|
|
*out_size = sizeof(float) + delta;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode a float8 type */
|
|
|
|
static int
|
|
|
|
decode_float8(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
const char* new_buffer = (const char*)TYPEALIGN(sizeof(double), (uintptr_t)buffer);
|
|
|
|
unsigned int delta = (unsigned int)( (uintptr_t)new_buffer - (uintptr_t)buffer );
|
|
|
|
|
|
|
|
if(buff_size < delta)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buff_size -= delta;
|
|
|
|
buffer = new_buffer;
|
|
|
|
|
|
|
|
if(buff_size < sizeof(double))
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
CopyAppendFmt("%.12lf", *(double*)buffer);
|
|
|
|
*out_size = sizeof(double) + delta;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode an uuid type */
|
|
|
|
static int
|
|
|
|
decode_uuid(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
unsigned char uuid[16];
|
|
|
|
|
|
|
|
if(buff_size < sizeof(uuid))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
memcpy(uuid, buffer, sizeof(uuid));
|
|
|
|
CopyAppendFmt("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
|
|
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
|
|
|
|
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]
|
|
|
|
);
|
|
|
|
*out_size = sizeof(uuid);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode a macaddr type */
|
|
|
|
static int
|
|
|
|
decode_macaddr(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
unsigned char macaddr[6];
|
|
|
|
const char* new_buffer = (const char*)TYPEALIGN(sizeof(int32), (uintptr_t)buffer);
|
|
|
|
unsigned int delta = (unsigned int)( (uintptr_t)new_buffer - (uintptr_t)buffer );
|
|
|
|
|
|
|
|
if(buff_size < delta)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buff_size -= delta;
|
|
|
|
buffer = new_buffer;
|
|
|
|
|
|
|
|
if(buff_size < sizeof(macaddr))
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
memcpy(macaddr, buffer, sizeof(macaddr));
|
|
|
|
CopyAppendFmt("%02x:%02x:%02x:%02x:%02x:%02x",
|
|
|
|
macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]
|
|
|
|
);
|
|
|
|
*out_size = sizeof(macaddr) + delta;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-11-27 14:53:51 +03:30
|
|
|
/* Decode a inet type */
|
|
|
|
static int
|
|
|
|
decode_inet(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
struct __attribute__((packed)) inet_t // FIXME: Only AF_INET is supported for now
|
|
|
|
{
|
|
|
|
unsigned char varlen;
|
|
|
|
unsigned char family;
|
|
|
|
unsigned char bits;
|
|
|
|
unsigned char ipaddr[4];
|
|
|
|
} inet;
|
2017-11-27 14:57:08 +03:30
|
|
|
|
|
|
|
if(buff_size < sizeof(inet))
|
2017-11-27 14:53:51 +03:30
|
|
|
return -2;
|
|
|
|
|
|
|
|
memcpy(&inet, buffer, sizeof(inet));
|
|
|
|
if (inet.family == PGSQL_AF_INET) {
|
|
|
|
CopyAppendFmt("%d.%d.%d.%d",
|
|
|
|
inet.ipaddr[0], inet.ipaddr[1], inet.ipaddr[2], inet.ipaddr[3]
|
|
|
|
);
|
|
|
|
*out_size = sizeof(inet);
|
|
|
|
return 0;
|
|
|
|
} else { // This basically dumps the stuff, now decodes them (perhaps AF_INET6)
|
|
|
|
CopyAppendFmt("[%d.%d.%d.%d.%d.%d.%d]",
|
|
|
|
inet.varlen, inet.family, inet.bits,
|
|
|
|
inet.ipaddr[0], inet.ipaddr[1], inet.ipaddr[2], inet.ipaddr[3]
|
|
|
|
);
|
|
|
|
*out_size = sizeof(inet);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-17 16:01:12 +03:00
|
|
|
/* Decode a bool type */
|
|
|
|
static int
|
|
|
|
decode_bool(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
if(buff_size < sizeof(bool))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
CopyAppend(*(bool*)buffer ? "t" : "f");
|
|
|
|
*out_size = sizeof(bool);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-01 15:08:09 +03:00
|
|
|
/* Decode a name type (used mostly in catalog tables) */
|
|
|
|
static int
|
|
|
|
decode_name(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
const char* new_buffer = (const char*)TYPEALIGN(sizeof(uint32), (uintptr_t)buffer);
|
|
|
|
unsigned int delta = (unsigned int)( (uintptr_t)new_buffer - (uintptr_t)buffer );
|
|
|
|
|
|
|
|
if(buff_size < delta)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buff_size -= delta;
|
|
|
|
buffer = new_buffer;
|
|
|
|
|
|
|
|
if(buff_size < NAMEDATALEN)
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
CopyAppendEncode(buffer, strnlen(buffer, NAMEDATALEN));
|
|
|
|
*out_size = NAMEDATALEN + delta;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode a char type */
|
|
|
|
static int
|
|
|
|
decode_char(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
if(buff_size < sizeof(char))
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
CopyAppendEncode(buffer, 1);
|
|
|
|
*out_size = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ignore all data left */
|
|
|
|
static int
|
|
|
|
decode_ignore(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
*out_size = buff_size;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-17 16:01:12 +03:00
|
|
|
/* Decode char(N), varchar(N), text, json or xml types */
|
|
|
|
static int
|
|
|
|
decode_string(const char* buffer, unsigned int buff_size, unsigned int* out_size)
|
|
|
|
{
|
|
|
|
int padding = 0;
|
|
|
|
|
|
|
|
/* Skip padding bytes. */
|
|
|
|
while(*buffer == 0x00)
|
|
|
|
{
|
|
|
|
if(buff_size == 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buff_size--;
|
|
|
|
buffer++;
|
|
|
|
padding++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(VARATT_IS_1B_E(buffer))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 00000001 1-byte length word, unaligned, TOAST pointer
|
|
|
|
*/
|
|
|
|
uint8 tag = VARTAG_1B_E(buffer);
|
|
|
|
uint32 len = VARTAG_SIZE(tag);
|
|
|
|
if(len > buff_size)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
CopyAppend("(TOASTED)");
|
|
|
|
*out_size = padding + len;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(VARATT_IS_1B(buffer))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* xxxxxxx1 1-byte length word, unaligned, uncompressed data (up to 126b)
|
|
|
|
* xxxxxxx is 1 + string length
|
|
|
|
*/
|
|
|
|
uint8 len = VARSIZE_1B(buffer);
|
|
|
|
if(len > buff_size)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
CopyAppendEncode(buffer + 1, len - 1);
|
|
|
|
*out_size = padding + len;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(VARATT_IS_4B_U(buffer) && buff_size >= 4)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* xxxxxx00 4-byte length word, aligned, uncompressed data (up to 1G)
|
|
|
|
*/
|
|
|
|
uint32 len = VARSIZE_4B(buffer);
|
|
|
|
if(len > buff_size)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
CopyAppendEncode(buffer + 4, len - 4);
|
|
|
|
*out_size = padding + len;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(VARATT_IS_4B_C(buffer) && buff_size >= 8)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* xxxxxx10 4-byte length word, aligned, *compressed* data (up to 1G)
|
|
|
|
*/
|
|
|
|
int decompress_ret;
|
|
|
|
uint32 len = VARSIZE_4B(buffer);
|
|
|
|
uint32 decompressed_len = VARRAWSIZE_4B_C(buffer);
|
|
|
|
|
|
|
|
if(len > buff_size)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if(decompressed_len > sizeof(decompress_tmp_buff))
|
|
|
|
{
|
|
|
|
printf("WARNING: Unable to decompress a string since it's too "
|
|
|
|
"large (%d bytes after decompressing). Consider increasing "
|
|
|
|
"decompress_tmp_buff size.\n", decompressed_len);
|
|
|
|
|
|
|
|
CopyAppend("(COMPRESSED)");
|
|
|
|
*out_size = padding + len;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
decompress_ret = pglz_decompress(VARDATA_4B_C(buffer), len - 2*sizeof(uint32),
|
|
|
|
decompress_tmp_buff, decompressed_len);
|
|
|
|
if((decompress_ret != decompressed_len) || (decompress_ret < 0))
|
|
|
|
{
|
|
|
|
printf("WARNING: Unable to decompress a string. Data is corrupted.\n");
|
|
|
|
CopyAppend("(COMPRESSED)");
|
|
|
|
*out_size = padding + len;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CopyAppendEncode(decompress_tmp_buff, decompressed_len);
|
|
|
|
*out_size = padding + len;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -9;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to decode a tuple using a types string provided previously.
|
|
|
|
*
|
|
|
|
* Arguments:
|
|
|
|
* tupleData - pointer to the tuple data
|
|
|
|
* tupleSize - tuple size in bytes
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
FormatDecode(const char* tupleData, unsigned int tupleSize)
|
|
|
|
{
|
|
|
|
HeapTupleHeader header = (HeapTupleHeader)tupleData;
|
|
|
|
const char* data = tupleData + header->t_hoff;
|
|
|
|
unsigned int size = tupleSize - header->t_hoff;
|
|
|
|
int curr_attr;
|
|
|
|
|
|
|
|
CopyClear();
|
|
|
|
|
|
|
|
for(curr_attr = 0; curr_attr < ncallbacks; curr_attr++)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
unsigned int processed_size = 0;
|
|
|
|
|
|
|
|
if( (header->t_infomask & HEAP_HASNULL) && att_isnull(curr_attr, header->t_bits) )
|
|
|
|
{
|
|
|
|
CopyAppend("\\N");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(size <= 0)
|
|
|
|
{
|
|
|
|
printf("Error: unable to decode a tuple, no more bytes left. Partial data: %s\n",
|
|
|
|
copyString.data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = callbacks[curr_attr](data, size, &processed_size);
|
|
|
|
if(ret < 0)
|
|
|
|
{
|
|
|
|
printf("Error: unable to decode a tuple, callback #%d returned %d. Partial data: %s\n",
|
|
|
|
curr_attr+1, ret, copyString.data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
size -= processed_size;
|
|
|
|
data += processed_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(size != 0)
|
|
|
|
{
|
|
|
|
printf("Error: unable to decode a tuple, %d bytes left, 0 expected. Partial data: %s\n",
|
|
|
|
size, copyString.data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CopyFlush();
|
|
|
|
}
|