/*
 * Portions Copyright (C) 2001-2003 FhG FOKUS
 * Copyright (C) 2003 August.Net Services, LLC
 * Portions Copyright (C) 2005-2008 iptelorg GmbH
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Kamailio is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version
 *
 * Kamailio is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */


/*!
 * \file
 * \brief DB_POSTGRES :: Data field conversion and type checking functions.
 * \ingroup db_postgres
 * Module: \ref db_postgres
 */

#include "pg_fld.h"
#include "pg_con.h" /* flags */
#include "pg_mod.h"

#include "../../lib/srdb2/db_drv.h"
#include "../../core/mem/mem.h"
#include "../../core/dprint.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdint.h>
#include <string.h>

/**
 * This is the epoch time in time_t format, this value is used to convert
 * timestamp values to/from PostgreSQL format.
 *  2000-01-01 00:00:00 +0000 as the value of time_t in UTC
 */
#define PG_EPOCH_TIME ((int64_t)946684800)


/** Frees memory used by a pg_fld structure.
 * This function frees all memory used by a pg_fld structure
 * @param fld Generic db_fld_t* structure being freed.
 * @param payload The postgresql extension structure to be freed
 */
static void pg_fld_free(db_fld_t *fld, struct pg_fld *payload)
{
	db_drv_free(&payload->gen);
	if(payload->name)
		pkg_free(payload->name);
	pkg_free(payload);
}


int pg_fld(db_fld_t *fld, char *table)
{
	struct pg_fld *res;

	res = (struct pg_fld *)pkg_malloc(sizeof(struct pg_fld));
	if(res == NULL) {
		PKG_MEM_ERROR;
		return -1;
	}
	memset(res, '\0', sizeof(struct pg_fld));
	if(db_drv_init(&res->gen, pg_fld_free) < 0)
		goto error;

	DB_SET_PAYLOAD(fld, res);
	return 0;

error:
	if(res)
		pkg_free(res);
	return -1;
}


union ull
{
	uint64_t ui64;
	uint32_t ui32[2];
};

static inline uint64_t _htonll(uint64_t in)
{
	union ull *p = (union ull *)&in;

	return ((uint64_t)htonl(p->ui32[0]) << 32) + (uint64_t)htonl(p->ui32[1]);
}


static inline uint64_t _ntohll(uint64_t in)
{
	union ull *p = (union ull *)&in;
	return ((uint64_t)ntohl(p->ui32[0]) << 32) + (uint64_t)ntohl(p->ui32[1]);
}


static inline void db_int2pg_int4(struct pg_params *dst, int i, db_fld_t *src)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(src);
	pfld->v.int4[0] = htonl(src->v.int4);

	dst->fmt[i] = 1;
	dst->val[i] = pfld->v.byte;
	dst->len[i] = 4;
}


static inline void db_int2pg_int2(struct pg_params *dst, int i, db_fld_t *src)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(src);
	pfld->v.int2[0] = htons(src->v.int4);

	dst->fmt[i] = 1;
	dst->val[i] = pfld->v.byte;
	dst->len[i] = 2;
}


static inline void db_int2pg_timestamp(
		struct pg_params *dst, int i, db_fld_t *src, unsigned int flags)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(src);
	if(flags & PG_INT8_TIMESTAMP) {
		pfld->v.int8 = ((int64_t)src->v.int4 - PG_EPOCH_TIME) * 1000000;
	} else {
		pfld->v.dbl = (double)src->v.int4 - (double)PG_EPOCH_TIME;
	}
	pfld->v.int8 = _htonll(pfld->v.int8);

	dst->fmt[i] = 1;
	dst->val[i] = pfld->v.byte;
	dst->len[i] = 8;
}


static inline void db_int2pg_int8(struct pg_params *dst, int i, db_fld_t *src)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(src);
	pfld->v.int4[0] = 0;
	pfld->v.int4[1] = htonl(src->v.int4);

	dst->fmt[i] = 1;
	dst->val[i] = pfld->v.byte;
	dst->len[i] = 8;
}


static inline void db_int2pg_bool(struct pg_params *dst, int i, db_fld_t *src)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(src);
	if(src->v.int4)
		pfld->v.byte[0] = 1;
	else
		pfld->v.byte[0] = 0;

	dst->fmt[i] = 1;
	dst->val[i] = pfld->v.byte;
	dst->len[i] = 1;
}


static inline void db_int2pg_inet(struct pg_params *dst, int i, db_fld_t *src)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(src);
	pfld->v.byte[0] = AF_INET;			  /* Address family */
	pfld->v.byte[1] = 32;				  /* Netmask */
	pfld->v.byte[2] = 0;				  /* is CIDR */
	pfld->v.byte[3] = 4;				  /* Number of bytes */
	pfld->v.int4[1] = htonl(src->v.int4); /* Actuall IP address */

	dst->fmt[i] = 1;
	dst->val[i] = pfld->v.byte;
	dst->len[i] = 8;
}


static inline void db_float2pg_float4(
		struct pg_params *dst, int i, db_fld_t *src)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(src);
	pfld->v.int4[0] = htonl(src->v.int4);

	dst->fmt[i] = 1;
	dst->val[i] = pfld->v.byte;
	dst->len[i] = 4;
}


static inline void db_float2pg_float8(
		struct pg_params *dst, int i, db_fld_t *src)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(src);
	pfld->v.dbl = src->v.flt;
	pfld->v.int8 = _htonll(pfld->v.int8);

	dst->fmt[i] = 1;
	dst->val[i] = pfld->v.byte;
	dst->len[i] = 8;
}


static inline void db_double2pg_float8(
		struct pg_params *dst, int i, db_fld_t *src)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(src);
	pfld->v.int8 = _htonll(src->v.int8);

	dst->fmt[i] = 1;
	dst->val[i] = pfld->v.byte;
	dst->len[i] = 8;
}


static inline void db_double2pg_float4(
		struct pg_params *dst, int i, db_fld_t *src)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(src);
	pfld->v.flt = src->v.dbl;
	pfld->v.int4[0] = htonl(pfld->v.int4[0]);

	dst->fmt[i] = 1;
	dst->val[i] = pfld->v.byte;
	dst->len[i] = 4;
}


static inline void db_int2pg_bit(struct pg_params *dst, int i, db_fld_t *src)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(src);

	pfld->v.int4[0] = htonl(32);
	pfld->v.int4[1] = htonl(src->v.int4);

	dst->fmt[i] = 1;
	dst->val[i] = pfld->v.byte;
	dst->len[i] = 8;
}


static inline void db_str2pg_string(struct pg_params *dst, int i, db_fld_t *src)
{
	dst->fmt[i] = 1;
	dst->val[i] = src->v.lstr.s;
	dst->len[i] = src->v.lstr.len;
}


static inline void db_cstr2pg_string(
		struct pg_params *dst, int i, db_fld_t *src)
{
	dst->fmt[i] = 0;
	dst->val[i] = src->v.cstr;
}


int pg_fld2pg(struct pg_params *dst, int off, pg_type_t *types, db_fld_t *src,
		unsigned int flags)
{
	int i;
	struct pg_fld *pfld;

	if(src == NULL)
		return 0;

	for(i = 0; !DB_FLD_EMPTY(src) && !DB_FLD_LAST(src[i]); i++) {
		pfld = DB_GET_PAYLOAD(src + i);

		/* NULL value */
		if(src[i].flags & DB_NULL) {
			dst->val[off + i] = NULL;
			dst->len[off + i] = 0;
			continue;
		}

		switch(src[i].type) {
			case DB_INT:
				if(pfld->oid == types[PG_INT2].oid)
					db_int2pg_int2(dst, off + i, src + i);
				else if(pfld->oid == types[PG_INT4].oid)
					db_int2pg_int4(dst, off + i, src + i);
				else if((pfld->oid == types[PG_TIMESTAMP].oid)
						|| (pfld->oid == types[PG_TIMESTAMPTZ].oid))
					db_int2pg_timestamp(dst, off + i, src + i, flags);
				else if(pfld->oid == types[PG_INT8].oid)
					db_int2pg_int8(dst, off + i, src + i);
				else if(pfld->oid == types[PG_INET].oid)
					db_int2pg_inet(dst, off + i, src + i);
				else if(pfld->oid == types[PG_BOOL].oid)
					db_int2pg_bool(dst, off + i, src + i);
				else if(pfld->oid == types[PG_BIT].oid)
					db_int2pg_bit(dst, off + i, src + i);
				else if(pfld->oid == types[PG_VARBIT].oid)
					db_int2pg_bit(dst, off + i, src + i);
				else
					goto bug;
				break;

			case DB_BITMAP:
				if(pfld->oid == types[PG_INT4].oid)
					db_int2pg_int4(dst, off + i, src + i);
				else if(pfld->oid == types[PG_INT8].oid)
					db_int2pg_int8(dst, off + i, src + i);
				else if(pfld->oid == types[PG_BIT].oid)
					db_int2pg_bit(dst, off + i, src + i);
				else if(pfld->oid == types[PG_VARBIT].oid)
					db_int2pg_bit(dst, off + i, src + i);
				else
					goto bug;
				break;

			case DB_DATETIME:
				if(pfld->oid == types[PG_INT4].oid)
					db_int2pg_int4(dst, off + i, src + i);
				else if((pfld->oid == types[PG_TIMESTAMP].oid)
						|| (pfld->oid == types[PG_TIMESTAMPTZ].oid))
					db_int2pg_timestamp(dst, off + i, src + i, flags);
				else if(pfld->oid == types[PG_INT8].oid)
					db_int2pg_int8(dst, off + i, src + i);
				else
					goto bug;
				break;

			case DB_FLOAT:
				if(pfld->oid == types[PG_FLOAT4].oid)
					db_float2pg_float4(dst, off + i, src + i);
				else if(pfld->oid == types[PG_FLOAT8].oid)
					db_float2pg_float8(dst, off + i, src + i);
				else
					goto bug;
				break;

			case DB_DOUBLE:
				if(pfld->oid == types[PG_FLOAT4].oid)
					db_double2pg_float4(dst, off + i, src + i);
				else if(pfld->oid == types[PG_FLOAT8].oid)
					db_double2pg_float8(dst, off + i, src + i);
				else
					goto bug;
				break;

			case DB_STR:
				if(pfld->oid == types[PG_VARCHAR].oid
						|| pfld->oid == types[PG_BYTE].oid
						|| pfld->oid == types[PG_CHAR].oid
						|| pfld->oid == types[PG_TEXT].oid
						|| pfld->oid == types[PG_BPCHAR].oid)
					db_str2pg_string(dst, off + i, src + i);
				else
					goto bug;
				break;

			case DB_CSTR:
				if(pfld->oid == types[PG_VARCHAR].oid
						|| pfld->oid == types[PG_BYTE].oid
						|| pfld->oid == types[PG_CHAR].oid
						|| pfld->oid == types[PG_TEXT].oid
						|| pfld->oid == types[PG_BPCHAR].oid)
					db_cstr2pg_string(dst, off + i, src + i);
				else
					goto bug;
				break;

			case DB_BLOB:
				if(pfld->oid == types[PG_BYTE].oid)
					db_str2pg_string(dst, off + i, src + i);
				else
					goto bug;
				break;

			default:
				BUG("postgres: Unsupported field type %d in field %s\n",
						src[i].type, src[i].name);
				return -1;
		}
	}

	return 0;

bug:
	BUG("postgres: Error while converting DB API type %d to Postgres Oid %d\n",
			src[i].type, pfld->oid);
	return -1;
}


int pg_check_fld2pg(db_fld_t *fld, pg_type_t *types)
{
	int i;
	const char *name = "UNKNOWN";
	struct pg_fld *pfld;

	if(fld == NULL)
		return 0;

	for(i = 0; !DB_FLD_EMPTY(fld) && !DB_FLD_LAST(fld[i]); i++) {
		pfld = DB_GET_PAYLOAD(fld + i);
		switch(fld[i].type) {
			case DB_INT:
				if(pfld->oid == types[PG_INT2].oid)
					continue;
				if(pfld->oid == types[PG_INT4].oid)
					continue;
				if(pfld->oid == types[PG_INT8].oid)
					continue;
				if(pfld->oid == types[PG_BOOL].oid)
					continue;
				if(pfld->oid == types[PG_INET].oid)
					continue;
				if(pfld->oid == types[PG_TIMESTAMP].oid)
					continue;
				if(pfld->oid == types[PG_TIMESTAMPTZ].oid)
					continue;
				if(pfld->oid == types[PG_BIT].oid)
					continue;
				if(pfld->oid == types[PG_VARBIT].oid)
					continue;
				break;

			case DB_BITMAP:
				if(pfld->oid == types[PG_INT4].oid)
					continue;
				if(pfld->oid == types[PG_INT8].oid)
					continue;
				if(pfld->oid == types[PG_BIT].oid)
					continue;
				if(pfld->oid == types[PG_VARBIT].oid)
					continue;
				break;

			case DB_FLOAT:
			case DB_DOUBLE:
				if(pfld->oid == types[PG_FLOAT4].oid)
					continue;
				if(pfld->oid == types[PG_FLOAT8].oid)
					continue;
				break;

			case DB_CSTR:
			case DB_STR:
				if(pfld->oid == types[PG_BYTE].oid)
					continue;
				if(pfld->oid == types[PG_CHAR].oid)
					continue;
				if(pfld->oid == types[PG_TEXT].oid)
					continue;
				if(pfld->oid == types[PG_BPCHAR].oid)
					continue;
				if(pfld->oid == types[PG_VARCHAR].oid)
					continue;
				break;

			case DB_BLOB:
				if(pfld->oid == types[PG_BYTE].oid)
					continue;
				break;

			case DB_DATETIME:
				if(pfld->oid == types[PG_INT4].oid)
					continue;
				if(pfld->oid == types[PG_INT8].oid)
					continue;
				if(pfld->oid == types[PG_TIMESTAMP].oid)
					continue;
				if(pfld->oid == types[PG_TIMESTAMPTZ].oid)
					continue;
				break;

			default:
				BUG("postgres: Unsupported field type %d, bug in postgres "
					"module\n",
						fld[i].type);
				return -1;
		}

		pg_oid2name(&name, types, pfld->oid);
		ERR("postgres: Cannot convert column '%s' of type %s "
			"to PostgreSQL column type '%s'\n",
				fld[i].name, db_fld_str[fld[i].type], name);
		return -1;
	}
	return 0;
}


int pg_resolve_param_oids(
		db_fld_t *vals, db_fld_t *match, int n1, int n2, PGresult *types)
{
	struct pg_fld *pfld;
	int i;

	if(n1 + n2 != PQnparams(types)) {
		ERR("postgres: Number of command parameters do not match\n");
		return -1;
	}

	for(i = 0; i < n1; i++) {
		pfld = DB_GET_PAYLOAD(vals + i);
		pfld->oid = PQparamtype(types, i);
	}

	for(i = 0; i < n2; i++) {
		pfld = DB_GET_PAYLOAD(match + i);
		pfld->oid = PQparamtype(types, n1 + i);
	}

	return 0;
}


int pg_resolve_result_oids(db_fld_t *fld, int n, PGresult *types)
{
	struct pg_fld *pfld;
	int i;
	if(fld == NULL)
		return 0;

	if(n != PQnfields(types)) {
		ERR("postgres: Result field numbers do not match\n");
		return -1;
	}

	for(i = 0; i < n; i++) {
		pfld = DB_GET_PAYLOAD(fld + i);
		pfld->oid = PQftype(types, i);
	}

	return 0;
}


int pg_check_pg2fld(db_fld_t *fld, pg_type_t *types)
{
	int i;
	const char *name = "UNKNOWN";
	struct pg_fld *pfld;

	if(fld == NULL)
		return 0;

	for(i = 0; !DB_FLD_EMPTY(fld) && !DB_FLD_LAST(fld[i]); i++) {
		pfld = DB_GET_PAYLOAD(fld + i);

		if(pfld->oid == 0) {
			ERR("postgres: Unknown type fields not supported\n");
			return -1;
		}

		switch(fld[i].type) {
			case DB_INT:
				if(pfld->oid == types[PG_INT2].oid)
					continue;
				if(pfld->oid == types[PG_INT4].oid)
					continue;
				if(pfld->oid == types[PG_INT8].oid)
					continue;
				if(pfld->oid == types[PG_BOOL].oid)
					continue;
				if(pfld->oid == types[PG_INET].oid)
					continue;
				if(pfld->oid == types[PG_TIMESTAMP].oid)
					continue;
				if(pfld->oid == types[PG_TIMESTAMPTZ].oid)
					continue;
				if(pfld->oid == types[PG_BIT].oid)
					continue;
				if(pfld->oid == types[PG_VARBIT].oid)
					continue;
				break;

			case DB_BITMAP:
				if(pfld->oid == types[PG_INT2].oid)
					continue;
				if(pfld->oid == types[PG_INT4].oid)
					continue;
				if(pfld->oid == types[PG_INT8].oid)
					continue;
				if(pfld->oid == types[PG_BIT].oid)
					continue;
				if(pfld->oid == types[PG_VARBIT].oid)
					continue;
				break;

			case DB_FLOAT:
				if(pfld->oid == types[PG_FLOAT4].oid)
					continue;
				break;

			case DB_DOUBLE:
				if(pfld->oid == types[PG_FLOAT4].oid)
					continue;
				if(pfld->oid == types[PG_FLOAT8].oid)
					continue;
				break;

			case DB_CSTR:
				if(pfld->oid == types[PG_CHAR].oid)
					continue;
				if(pfld->oid == types[PG_TEXT].oid)
					continue;
				if(pfld->oid == types[PG_BPCHAR].oid)
					continue;
				if(pfld->oid == types[PG_VARCHAR].oid)
					continue;
				if(pfld->oid == types[PG_INT2].oid)
					continue;
				if(pfld->oid == types[PG_INT4].oid)
					continue;
				break;

			case DB_STR:
			case DB_BLOB:
				if(pfld->oid == types[PG_BYTE].oid)
					continue;
				if(pfld->oid == types[PG_CHAR].oid)
					continue;
				if(pfld->oid == types[PG_TEXT].oid)
					continue;
				if(pfld->oid == types[PG_BPCHAR].oid)
					continue;
				if(pfld->oid == types[PG_VARCHAR].oid)
					continue;
				if(pfld->oid == types[PG_INT2].oid)
					continue;
				if(pfld->oid == types[PG_INT4].oid)
					continue;
				break;

			case DB_DATETIME:
				if(pfld->oid == types[PG_INT2].oid)
					continue;
				if(pfld->oid == types[PG_INT4].oid)
					continue;
				if(pfld->oid == types[PG_TIMESTAMP].oid)
					continue;
				if(pfld->oid == types[PG_TIMESTAMPTZ].oid)
					continue;
				break;

			default:
				BUG("postgres: Unsupported field type %d, bug in postgres "
					"module\n",
						fld[i].type);
				return -1;
		}

		pg_oid2name(&name, types, pfld->oid);
		ERR("postgres: Cannot convert column '%s' of type %s "
			"to DB API field of type %s\n",
				fld[i].name, name, db_fld_str[fld[i].type]);
		return -1;
	}
	return 0;
}


static inline int pg_int2_2_db_cstr(db_fld_t *fld, char *val, int len)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(fld);
	int size, v;

	v = (int16_t)ntohs(*((int16_t *)val));

	size = snprintf(pfld->buf, INT2STR_MAX_LEN, "%-d", v);
	if(size < 0 || size >= INT2STR_MAX_LEN) {
		BUG("postgres: Error while converting integer to string\n");
		return -1;
	}

	fld->v.cstr = pfld->buf;
	return 0;
}


static inline int pg_int4_2_db_cstr(db_fld_t *fld, char *val, int len)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(fld);
	int size, v;

	v = (int32_t)ntohl(*((int32_t *)val));

	size = snprintf(pfld->buf, INT2STR_MAX_LEN, "%-d", v);
	if(len < 0 || size >= INT2STR_MAX_LEN) {
		BUG("postgres: Error while converting integer to string\n");
		return -1;
	}

	fld->v.cstr = pfld->buf;
	return 0;
}


static inline int pg_int2_2_db_str(db_fld_t *fld, char *val, int len)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(fld);
	int size, v;

	v = (int16_t)ntohs(*((int16_t *)val));

	size = snprintf(pfld->buf, INT2STR_MAX_LEN, "%-d", v);
	if(size < 0 || size >= INT2STR_MAX_LEN) {
		BUG("postgres: Error while converting integer to string\n");
		return -1;
	}

	fld->v.lstr.s = pfld->buf;
	fld->v.lstr.len = size;
	return 0;
}


static inline int pg_int4_2_db_str(db_fld_t *fld, char *val, int len)
{
	struct pg_fld *pfld = DB_GET_PAYLOAD(fld);
	int size, v;

	v = (int32_t)ntohl(*((int32_t *)val));

	size = snprintf(pfld->buf, INT2STR_MAX_LEN, "%-d", v);
	if(size < 0 || size >= INT2STR_MAX_LEN) {
		BUG("postgres: Error while converting integer to string\n");
		return -1;
	}

	fld->v.lstr.s = pfld->buf;
	fld->v.lstr.len = size;
	return 0;
}


static inline int pg_int2_2_db_int(db_fld_t *fld, char *val, int len)
{
	fld->v.int4 = (int16_t)ntohs(*((int16_t *)val));
	return 0;
}


static inline int pg_int4_2_db_int(db_fld_t *fld, char *val, int len)
{
	fld->v.int4 = (int32_t)ntohl(*((int32_t *)val));
	return 0;
}


static inline int pg_int8_2_db_int(db_fld_t *fld, char *val, int len)
{
	fld->v.int8 = (int64_t)_ntohll(*((int64_t *)val));
	return 0;
}


static inline int pg_bool2db_int(db_fld_t *fld, char *val, int len)
{
	fld->v.int4 = val[0];
	return 0;
}


static inline int pg_inet2db_int(db_fld_t *fld, char *val, int len)
{
	if(len != 8 || val[2] != 0) {
		ERR("postgres: Unsupported 'inet' format, column %s\n", fld->name);
		return -1;
	}

	if(val[0] != AF_INET) {
		ERR("postgres: Unsupported address family %d in field %s\n", val[0],
				fld->name);
		return -1;
	}

	if(val[1] != 32) {
		WARN("postgres: Netmasks shorter than 32-bits not supported, "
			 "column %s\n",
				fld->name);
	}

	if(val[3] != 4) {
		ERR("postgres: Unsupported IP address size %d in column %s\n", val[3],
				fld->name);
		return -1;
	}

	fld->v.int4 = (int32_t)ntohl(((int32_t *)val)[1]);
	return 0;
}


static inline int pg_timestamp2db_int(
		db_fld_t *fld, char *val, int len, unsigned int flags)
{
	if(flags & PG_INT8_TIMESTAMP) {
		/* int8 format */
		fld->v.int4 = (int64_t)_ntohll(((int64_t *)val)[0]) / (int64_t)1000000
					  + PG_EPOCH_TIME;
	} else {
		/* double format */
		fld->v.int4 = PG_EPOCH_TIME + _ntohll(((int64_t *)val)[0]);
	}
	return 0;
}


static inline int pg_bit2db_int(db_fld_t *fld, char *val, int len)
{
	int size;

	size = ntohl(*(uint32_t *)val);
	if(size != 32) {
		ERR("postgres: Unsupported bit field size (%d), column %s\n", size,
				fld->name);
		return -1;
	}
	fld->v.int4 = ntohl(((uint32_t *)val)[1]);
	return 0;
}


static inline int pg_float42db_float(db_fld_t *fld, char *val, int len)
{
	fld->v.int4 = (uint32_t)ntohl(*(uint32_t *)val);
	return 0;
}


static inline int pg_float42db_double(db_fld_t *fld, char *val, int len)
{
	float tmp;

	tmp = ntohl(*(uint32_t *)val);
	fld->v.dbl = tmp;
	return 0;
}


static inline int pg_float82db_double(db_fld_t *fld, char *val, int len)
{
	fld->v.int8 = _ntohll(*(uint64_t *)val);
	return 0;
}


static inline int pg_string2db_cstr(db_fld_t *fld, char *val, int len)
{
	fld->v.cstr = val;
	return 0;
}


static inline int pg_string2db_str(db_fld_t *fld, char *val, int len)
{
	fld->v.lstr.s = val;
	fld->v.lstr.len = len;
	return 0;
}


int pg_pg2fld(db_fld_t *dst, PGresult *src, int row, pg_type_t *types,
		unsigned int flags)
{
	char *val;
	int i, len, ret;
	Oid type;

	if(dst == NULL || src == NULL)
		return 0;
	ret = 0;

	for(i = 0; !DB_FLD_EMPTY(dst) && !DB_FLD_LAST(dst[i]); i++) {
		if(PQgetisnull(src, row, i)) {
			dst[i].flags |= DB_NULL;
			continue;
		} else {
			dst[i].flags &= ~DB_NULL;
		}

		type = PQftype(src, i);
		val = PQgetvalue(src, row, i);
		len = PQgetlength(src, row, i);

		switch(dst[i].type) {
			case DB_INT:
				if(type == types[PG_INT2].oid)
					ret |= pg_int2_2_db_int(dst + i, val, len);
				else if(type == types[PG_INT4].oid)
					ret |= pg_int4_2_db_int(dst + i, val, len);
				else if(type == types[PG_INT8].oid)
					ret |= pg_int8_2_db_int(dst + i, val, len);
				else if(type == types[PG_BOOL].oid)
					ret |= pg_bool2db_int(dst + i, val, len);
				else if(type == types[PG_INET].oid)
					ret |= pg_inet2db_int(dst + i, val, len);
				else if((type == types[PG_TIMESTAMP].oid)
						|| (type == types[PG_TIMESTAMPTZ].oid))
					ret |= pg_timestamp2db_int(dst + i, val, len, flags);
				else if(type == types[PG_BIT].oid)
					ret |= pg_bit2db_int(dst + i, val, len);
				else if(type == types[PG_VARBIT].oid)
					ret |= pg_bit2db_int(dst + i, val, len);
				else
					goto bug;
				break;

			case DB_FLOAT:
				if(type == types[PG_FLOAT4].oid)
					ret |= pg_float42db_float(dst + i, val, len);
				else
					goto bug;
				break;

			case DB_DOUBLE:
				if(type == types[PG_FLOAT4].oid)
					ret |= pg_float42db_double(dst + i, val, len);
				else if(type == types[PG_FLOAT8].oid)
					ret |= pg_float82db_double(dst + i, val, len);
				else
					goto bug;
				break;

			case DB_DATETIME:
				if(type == types[PG_INT2].oid)
					ret |= pg_int2_2_db_int(dst + i, val, len);
				else if(type == types[PG_INT4].oid)
					ret |= pg_int4_2_db_int(dst + i, val, len);
				else if((type == types[PG_TIMESTAMP].oid)
						|| (type == types[PG_TIMESTAMPTZ].oid))
					ret |= pg_timestamp2db_int(dst + i, val, len, flags);
				else
					goto bug;
				break;

			case DB_CSTR:
				if((type == types[PG_CHAR].oid) || (type == types[PG_TEXT].oid)
						|| (type == types[PG_BPCHAR].oid)
						|| (type == types[PG_VARCHAR].oid))
					ret |= pg_string2db_cstr(dst + i, val, len);
				else if(type == types[PG_INT2].oid)
					ret |= pg_int2_2_db_cstr(dst + i, val, len);
				else if(type == types[PG_INT4].oid)
					ret |= pg_int4_2_db_cstr(dst + i, val, len);
				else
					goto bug;
				break;

			case DB_STR:
			case DB_BLOB:
				if((type == types[PG_BYTE].oid) || (type == types[PG_CHAR].oid)
						|| (type == types[PG_TEXT].oid)
						|| (type == types[PG_BPCHAR].oid)
						|| (type == types[PG_VARCHAR].oid))
					ret |= pg_string2db_str(dst + i, val, len);
				else if(type == types[PG_INT2].oid)
					ret |= pg_int2_2_db_str(dst + i, val, len);
				else if(type == types[PG_INT4].oid)
					ret |= pg_int4_2_db_str(dst + i, val, len);
				else
					goto bug;
				break;

			case DB_BITMAP:
				if(type == types[PG_INT2].oid)
					ret |= pg_int2_2_db_int(dst + i, val, len);
				else if(type == types[PG_INT4].oid)
					ret |= pg_int4_2_db_int(dst + i, val, len);
				else if(type == types[PG_INT8].oid)
					ret |= pg_int8_2_db_int(dst + i, val, len);
				else if(type == types[PG_BIT].oid)
					ret |= pg_bit2db_int(dst + i, val, len);
				else if(type == types[PG_VARBIT].oid)
					ret |= pg_bit2db_int(dst + i, val, len);
				else
					goto bug;
				break;

			default:
				BUG("postgres: Unsupported field type %d in field %s\n",
						dst[i].type, dst[i].name);
				return -1;
		}
	}
	return ret;

bug:
	BUG("postgres: Error while converting Postgres Oid %d to DB API type %d\n",
			type, dst[i].type);
	return -1;
}
