/*-------------------------------------------------------------------------
 *
 * testlo64.c
 *	  test using large objects with libpq using 64-bit APIs
 *
 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/test/examples/testlo64.c
 *
 *-------------------------------------------------------------------------
 */
#include <stdio.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "libpq-fe.h"
#include "libpq/libpq-fs.h"

#define BUFSIZE			1024

/*
 * importFile -
 *	  import file "in_filename" into database as large object "lobjOid"
 *
 */
static Oid
importFile(PGconn *conn, char *filename)
{
	Oid			lobjId;
	int			lobj_fd;
	char		buf[BUFSIZE];
	int			nbytes,
				tmp;
	int			fd;

	/*
	 * open the file to be read in
	 */
	fd = open(filename, O_RDONLY, 0666);
	if (fd < 0)
	{							/* error */
		fprintf(stderr, "cannot open unix file\"%s\"\n", filename);
	}

	/*
	 * create the large object
	 */
	lobjId = lo_creat(conn, INV_READ | INV_WRITE);
	if (lobjId == 0)
		fprintf(stderr, "cannot create large object");

	lobj_fd = lo_open(conn, lobjId, INV_WRITE);

	/*
	 * read in from the Unix file and write to the inversion file
	 */
	while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
	{
		tmp = lo_write(conn, lobj_fd, buf, nbytes);
		if (tmp < nbytes)
			fprintf(stderr, "error while reading \"%s\"", filename);
	}

	close(fd);
	lo_close(conn, lobj_fd);

	return lobjId;
}

static void
pickout(PGconn *conn, Oid lobjId, pg_int64 start, int len)
{
	int			lobj_fd;
	char	   *buf;
	int			nbytes;
	int			nread;

	lobj_fd = lo_open(conn, lobjId, INV_READ);
	if (lobj_fd < 0)
		fprintf(stderr, "cannot open large object %u", lobjId);

	if (lo_lseek64(conn, lobj_fd, start, SEEK_SET) < 0)
		fprintf(stderr, "error in lo_lseek64: %s", PQerrorMessage(conn));

	if (lo_tell64(conn, lobj_fd) != start)
		fprintf(stderr, "error in lo_tell64: %s", PQerrorMessage(conn));

	buf = malloc(len + 1);

	nread = 0;
	while (len - nread > 0)
	{
		nbytes = lo_read(conn, lobj_fd, buf, len - nread);
		buf[nbytes] = '\0';
		fprintf(stderr, ">>> %s", buf);
		nread += nbytes;
		if (nbytes <= 0)
			break;				/* no more data? */
	}
	free(buf);
	fprintf(stderr, "\n");
	lo_close(conn, lobj_fd);
}

static void
overwrite(PGconn *conn, Oid lobjId, pg_int64 start, int len)
{
	int			lobj_fd;
	char	   *buf;
	int			nbytes;
	int			nwritten;
	int			i;

	lobj_fd = lo_open(conn, lobjId, INV_WRITE);
	if (lobj_fd < 0)
		fprintf(stderr, "cannot open large object %u", lobjId);

	if (lo_lseek64(conn, lobj_fd, start, SEEK_SET) < 0)
		fprintf(stderr, "error in lo_lseek64: %s", PQerrorMessage(conn));

	buf = malloc(len + 1);

	for (i = 0; i < len; i++)
		buf[i] = 'X';
	buf[i] = '\0';

	nwritten = 0;
	while (len - nwritten > 0)
	{
		nbytes = lo_write(conn, lobj_fd, buf + nwritten, len - nwritten);
		nwritten += nbytes;
		if (nbytes <= 0)
		{
			fprintf(stderr, "\nWRITE FAILED!\n");
			break;
		}
	}
	free(buf);
	fprintf(stderr, "\n");
	lo_close(conn, lobj_fd);
}

static void
my_truncate(PGconn *conn, Oid lobjId, pg_int64 len)
{
	int			lobj_fd;

	lobj_fd = lo_open(conn, lobjId, INV_READ | INV_WRITE);
	if (lobj_fd < 0)
		fprintf(stderr, "cannot open large object %u", lobjId);

	if (lo_truncate64(conn, lobj_fd, len) < 0)
		fprintf(stderr, "error in lo_truncate64: %s", PQerrorMessage(conn));

	lo_close(conn, lobj_fd);
}


/*
 * exportFile -
 *	  export large object "lobjOid" to file "out_filename"
 *
 */
static void
exportFile(PGconn *conn, Oid lobjId, char *filename)
{
	int			lobj_fd;
	char		buf[BUFSIZE];
	int			nbytes,
				tmp;
	int			fd;

	/*
	 * open the large object
	 */
	lobj_fd = lo_open(conn, lobjId, INV_READ);
	if (lobj_fd < 0)
		fprintf(stderr, "cannot open large object %u", lobjId);

	/*
	 * open the file to be written to
	 */
	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
	if (fd < 0)
	{							/* error */
		fprintf(stderr, "cannot open unix file\"%s\"",
				filename);
	}

	/*
	 * read in from the inversion file and write to the Unix file
	 */
	while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0)
	{
		tmp = write(fd, buf, nbytes);
		if (tmp < nbytes)
		{
			fprintf(stderr, "error while writing \"%s\"",
					filename);
		}
	}

	lo_close(conn, lobj_fd);
	close(fd);

	return;
}

static void
exit_nicely(PGconn *conn)
{
	PQfinish(conn);
	exit(1);
}

int
main(int argc, char **argv)
{
	char	   *in_filename,
			   *out_filename,
			   *out_filename2;
	char	   *database;
	Oid			lobjOid;
	PGconn	   *conn;
	PGresult   *res;

	if (argc != 5)
	{
		fprintf(stderr, "Usage: %s database_name in_filename out_filename out_filename2\n",
				argv[0]);
		exit(1);
	}

	database = argv[1];
	in_filename = argv[2];
	out_filename = argv[3];
	out_filename2 = argv[4];

	/*
	 * set up the connection
	 */
	conn = PQsetdb(NULL, NULL, NULL, NULL, database);

	/* check to see that the backend connection was successfully made */
	if (PQstatus(conn) != CONNECTION_OK)
	{
		fprintf(stderr, "Connection to database failed: %s",
				PQerrorMessage(conn));
		exit_nicely(conn);
	}

	/* Set always-secure search path, so malicous users can't take control. */
	res = PQexec(conn,
				 "SELECT pg_catalog.set_config('search_path', '', false)");
	if (PQresultStatus(res) != PGRES_TUPLES_OK)
	{
		fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
		PQclear(res);
		exit_nicely(conn);
	}
	PQclear(res);

	res = PQexec(conn, "begin");
	PQclear(res);
	printf("importing file \"%s\" ...\n", in_filename);
/*	lobjOid = importFile(conn, in_filename); */
	lobjOid = lo_import(conn, in_filename);
	if (lobjOid == 0)
		fprintf(stderr, "%s\n", PQerrorMessage(conn));
	else
	{
		printf("\tas large object %u.\n", lobjOid);

		printf("picking out bytes 4294967000-4294968000 of the large object\n");
		pickout(conn, lobjOid, 4294967000U, 1000);

		printf("overwriting bytes 4294967000-4294968000 of the large object with X's\n");
		overwrite(conn, lobjOid, 4294967000U, 1000);

		printf("exporting large object to file \"%s\" ...\n", out_filename);
/*		exportFile(conn, lobjOid, out_filename); */
		if (lo_export(conn, lobjOid, out_filename) < 0)
			fprintf(stderr, "%s\n", PQerrorMessage(conn));

		printf("truncating to 3294968000 bytes\n");
		my_truncate(conn, lobjOid, 3294968000U);

		printf("exporting truncated large object to file \"%s\" ...\n", out_filename2);
		if (lo_export(conn, lobjOid, out_filename2) < 0)
			fprintf(stderr, "%s\n", PQerrorMessage(conn));
	}

	res = PQexec(conn, "end");
	PQclear(res);
	PQfinish(conn);
	return 0;
}
