diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 13:44:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 13:44:03 +0000 |
commit | 293913568e6a7a86fd1479e1cff8e2ecb58d6568 (patch) | |
tree | fc3b469a3ec5ab71b36ea97cc7aaddb838423a0c /src/test/examples | |
parent | Initial commit. (diff) | |
download | postgresql-16-293913568e6a7a86fd1479e1cff8e2ecb58d6568.tar.xz postgresql-16-293913568e6a7a86fd1479e1cff8e2ecb58d6568.zip |
Adding upstream version 16.2.upstream/16.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/examples')
-rw-r--r-- | src/test/examples/.gitignore | 6 | ||||
-rw-r--r-- | src/test/examples/Makefile | 22 | ||||
-rw-r--r-- | src/test/examples/testlibpq.c | 131 | ||||
-rw-r--r-- | src/test/examples/testlibpq2.c | 150 | ||||
-rw-r--r-- | src/test/examples/testlibpq2.sql | 6 | ||||
-rw-r--r-- | src/test/examples/testlibpq3.c | 231 | ||||
-rw-r--r-- | src/test/examples/testlibpq3.sql | 6 | ||||
-rw-r--r-- | src/test/examples/testlibpq4.c | 163 | ||||
-rw-r--r-- | src/test/examples/testlo.c | 270 | ||||
-rw-r--r-- | src/test/examples/testlo64.c | 301 |
10 files changed, 1286 insertions, 0 deletions
diff --git a/src/test/examples/.gitignore b/src/test/examples/.gitignore new file mode 100644 index 0000000..1957ec1 --- /dev/null +++ b/src/test/examples/.gitignore @@ -0,0 +1,6 @@ +/testlibpq +/testlibpq2 +/testlibpq3 +/testlibpq4 +/testlo +/testlo64 diff --git a/src/test/examples/Makefile b/src/test/examples/Makefile new file mode 100644 index 0000000..a67f456 --- /dev/null +++ b/src/test/examples/Makefile @@ -0,0 +1,22 @@ +# +# Makefile for example programs +# + +subdir = src/test/examples +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +ifeq ($(PORTNAME), win32) +LDFLAGS += -lws2_32 +endif + +override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) +LDFLAGS_INTERNAL += $(libpq_pgport) + + +PROGS = testlibpq testlibpq2 testlibpq3 testlibpq4 testlo testlo64 + +all: $(PROGS) + +clean distclean maintainer-clean: + rm -f $(PROGS) *.o diff --git a/src/test/examples/testlibpq.c b/src/test/examples/testlibpq.c new file mode 100644 index 0000000..0372781 --- /dev/null +++ b/src/test/examples/testlibpq.c @@ -0,0 +1,131 @@ +/* + * src/test/examples/testlibpq.c + * + * + * testlibpq.c + * + * Test the C version of libpq, the PostgreSQL frontend library. + */ +#include <stdio.h> +#include <stdlib.h> +#include "libpq-fe.h" + +static void +exit_nicely(PGconn *conn) +{ + PQfinish(conn); + exit(1); +} + +int +main(int argc, char **argv) +{ + const char *conninfo; + PGconn *conn; + PGresult *res; + int nFields; + int i, + j; + + /* + * If the user supplies a parameter on the command line, use it as the + * conninfo string; otherwise default to setting dbname=postgres and using + * environment variables or defaults for all other connection parameters. + */ + if (argc > 1) + conninfo = argv[1]; + else + conninfo = "dbname = postgres"; + + /* Make a connection to the database */ + conn = PQconnectdb(conninfo); + + /* Check to see that the backend connection was successfully made */ + if (PQstatus(conn) != CONNECTION_OK) + { + fprintf(stderr, "%s", PQerrorMessage(conn)); + exit_nicely(conn); + } + + /* Set always-secure search path, so malicious 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); + } + + /* + * Should PQclear PGresult whenever it is no longer needed to avoid memory + * leaks + */ + PQclear(res); + + /* + * Our test case here involves using a cursor, for which we must be inside + * a transaction block. We could do the whole thing with a single + * PQexec() of "select * from pg_database", but that's too trivial to make + * a good example. + */ + + /* Start a transaction block */ + res = PQexec(conn, "BEGIN"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + fprintf(stderr, "BEGIN command failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + PQclear(res); + + /* + * Fetch rows from pg_database, the system catalog of databases + */ + res = PQexec(conn, "DECLARE myportal CURSOR FOR select * from pg_database"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + fprintf(stderr, "DECLARE CURSOR failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + PQclear(res); + + res = PQexec(conn, "FETCH ALL in myportal"); + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + fprintf(stderr, "FETCH ALL failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + + /* first, print out the attribute names */ + nFields = PQnfields(res); + for (i = 0; i < nFields; i++) + printf("%-15s", PQfname(res, i)); + printf("\n\n"); + + /* next, print out the rows */ + for (i = 0; i < PQntuples(res); i++) + { + for (j = 0; j < nFields; j++) + printf("%-15s", PQgetvalue(res, i, j)); + printf("\n"); + } + + PQclear(res); + + /* close the portal ... we don't bother to check for errors ... */ + res = PQexec(conn, "CLOSE myportal"); + PQclear(res); + + /* end the transaction */ + res = PQexec(conn, "END"); + PQclear(res); + + /* close the connection to the database and cleanup */ + PQfinish(conn); + + return 0; +} diff --git a/src/test/examples/testlibpq2.c b/src/test/examples/testlibpq2.c new file mode 100644 index 0000000..05ce8c3 --- /dev/null +++ b/src/test/examples/testlibpq2.c @@ -0,0 +1,150 @@ +/* + * src/test/examples/testlibpq2.c + * + * + * testlibpq2.c + * Test of the asynchronous notification interface + * + * Start this program, then from psql in another window do + * NOTIFY TBL2; + * Repeat four times to get this program to exit. + * + * Or, if you want to get fancy, try this: + * populate a database with the following commands + * (provided in src/test/examples/testlibpq2.sql): + * + * CREATE SCHEMA TESTLIBPQ2; + * SET search_path = TESTLIBPQ2; + * CREATE TABLE TBL1 (i int4); + * CREATE TABLE TBL2 (i int4); + * CREATE RULE r1 AS ON INSERT TO TBL1 DO + * (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2); + * + * Start this program, then from psql do this four times: + * + * INSERT INTO TESTLIBPQ2.TBL1 VALUES (10); + */ + +#ifdef WIN32 +#include <windows.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/select.h> +#include <sys/time.h> +#include <sys/types.h> + +#include "libpq-fe.h" + +static void +exit_nicely(PGconn *conn) +{ + PQfinish(conn); + exit(1); +} + +int +main(int argc, char **argv) +{ + const char *conninfo; + PGconn *conn; + PGresult *res; + PGnotify *notify; + int nnotifies; + + /* + * If the user supplies a parameter on the command line, use it as the + * conninfo string; otherwise default to setting dbname=postgres and using + * environment variables or defaults for all other connection parameters. + */ + if (argc > 1) + conninfo = argv[1]; + else + conninfo = "dbname = postgres"; + + /* Make a connection to the database */ + conn = PQconnectdb(conninfo); + + /* Check to see that the backend connection was successfully made */ + if (PQstatus(conn) != CONNECTION_OK) + { + fprintf(stderr, "%s", PQerrorMessage(conn)); + exit_nicely(conn); + } + + /* Set always-secure search path, so malicious 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); + } + + /* + * Should PQclear PGresult whenever it is no longer needed to avoid memory + * leaks + */ + PQclear(res); + + /* + * Issue LISTEN command to enable notifications from the rule's NOTIFY. + */ + res = PQexec(conn, "LISTEN TBL2"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + fprintf(stderr, "LISTEN command failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + PQclear(res); + + /* Quit after four notifies are received. */ + nnotifies = 0; + while (nnotifies < 4) + { + /* + * Sleep until something happens on the connection. We use select(2) + * to wait for input, but you could also use poll() or similar + * facilities. + */ + int sock; + fd_set input_mask; + + sock = PQsocket(conn); + + if (sock < 0) + break; /* shouldn't happen */ + + FD_ZERO(&input_mask); + FD_SET(sock, &input_mask); + + if (select(sock + 1, &input_mask, NULL, NULL, NULL) < 0) + { + fprintf(stderr, "select() failed: %s\n", strerror(errno)); + exit_nicely(conn); + } + + /* Now check for input */ + PQconsumeInput(conn); + while ((notify = PQnotifies(conn)) != NULL) + { + fprintf(stderr, + "ASYNC NOTIFY of '%s' received from backend PID %d\n", + notify->relname, notify->be_pid); + PQfreemem(notify); + nnotifies++; + PQconsumeInput(conn); + } + } + + fprintf(stderr, "Done.\n"); + + /* close the connection to the database and cleanup */ + PQfinish(conn); + + return 0; +} diff --git a/src/test/examples/testlibpq2.sql b/src/test/examples/testlibpq2.sql new file mode 100644 index 0000000..e8173e4 --- /dev/null +++ b/src/test/examples/testlibpq2.sql @@ -0,0 +1,6 @@ +CREATE SCHEMA TESTLIBPQ2; +SET search_path = TESTLIBPQ2; +CREATE TABLE TBL1 (i int4); +CREATE TABLE TBL2 (i int4); +CREATE RULE r1 AS ON INSERT TO TBL1 DO + (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2); diff --git a/src/test/examples/testlibpq3.c b/src/test/examples/testlibpq3.c new file mode 100644 index 0000000..4f7b791 --- /dev/null +++ b/src/test/examples/testlibpq3.c @@ -0,0 +1,231 @@ +/* + * src/test/examples/testlibpq3.c + * + * + * testlibpq3.c + * Test out-of-line parameters and binary I/O. + * + * Before running this, populate a database with the following commands + * (provided in src/test/examples/testlibpq3.sql): + * + * CREATE SCHEMA testlibpq3; + * SET search_path = testlibpq3; + * SET standard_conforming_strings = ON; + * CREATE TABLE test1 (i int4, t text, b bytea); + * INSERT INTO test1 values (1, 'joe''s place', '\000\001\002\003\004'); + * INSERT INTO test1 values (2, 'ho there', '\004\003\002\001\000'); + * + * The expected output is: + * + * tuple 0: got + * i = (4 bytes) 1 + * t = (11 bytes) 'joe's place' + * b = (5 bytes) \000\001\002\003\004 + * + * tuple 0: got + * i = (4 bytes) 2 + * t = (8 bytes) 'ho there' + * b = (5 bytes) \004\003\002\001\000 + */ + +#ifdef WIN32 +#include <windows.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> +#include "libpq-fe.h" + +/* for ntohl/htonl */ +#include <netinet/in.h> +#include <arpa/inet.h> + + +static void +exit_nicely(PGconn *conn) +{ + PQfinish(conn); + exit(1); +} + +/* + * This function prints a query result that is a binary-format fetch from + * a table defined as in the comment above. We split it out because the + * main() function uses it twice. + */ +static void +show_binary_results(PGresult *res) +{ + int i, + j; + int i_fnum, + t_fnum, + b_fnum; + + /* Use PQfnumber to avoid assumptions about field order in result */ + i_fnum = PQfnumber(res, "i"); + t_fnum = PQfnumber(res, "t"); + b_fnum = PQfnumber(res, "b"); + + for (i = 0; i < PQntuples(res); i++) + { + char *iptr; + char *tptr; + char *bptr; + int blen; + int ival; + + /* Get the field values (we ignore possibility they are null!) */ + iptr = PQgetvalue(res, i, i_fnum); + tptr = PQgetvalue(res, i, t_fnum); + bptr = PQgetvalue(res, i, b_fnum); + + /* + * The binary representation of INT4 is in network byte order, which + * we'd better coerce to the local byte order. + */ + ival = ntohl(*((uint32_t *) iptr)); + + /* + * The binary representation of TEXT is, well, text, and since libpq + * was nice enough to append a zero byte to it, it'll work just fine + * as a C string. + * + * The binary representation of BYTEA is a bunch of bytes, which could + * include embedded nulls so we have to pay attention to field length. + */ + blen = PQgetlength(res, i, b_fnum); + + printf("tuple %d: got\n", i); + printf(" i = (%d bytes) %d\n", + PQgetlength(res, i, i_fnum), ival); + printf(" t = (%d bytes) '%s'\n", + PQgetlength(res, i, t_fnum), tptr); + printf(" b = (%d bytes) ", blen); + for (j = 0; j < blen; j++) + printf("\\%03o", bptr[j]); + printf("\n\n"); + } +} + +int +main(int argc, char **argv) +{ + const char *conninfo; + PGconn *conn; + PGresult *res; + const char *paramValues[1]; + int paramLengths[1]; + int paramFormats[1]; + uint32_t binaryIntVal; + + /* + * If the user supplies a parameter on the command line, use it as the + * conninfo string; otherwise default to setting dbname=postgres and using + * environment variables or defaults for all other connection parameters. + */ + if (argc > 1) + conninfo = argv[1]; + else + conninfo = "dbname = postgres"; + + /* Make a connection to the database */ + conn = PQconnectdb(conninfo); + + /* Check to see that the backend connection was successfully made */ + if (PQstatus(conn) != CONNECTION_OK) + { + fprintf(stderr, "%s", PQerrorMessage(conn)); + exit_nicely(conn); + } + + /* Set always-secure search path, so malicious users can't take control. */ + res = PQexec(conn, "SET search_path = testlibpq3"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + fprintf(stderr, "SET failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + PQclear(res); + + /* + * The point of this program is to illustrate use of PQexecParams() with + * out-of-line parameters, as well as binary transmission of data. + * + * This first example transmits the parameters as text, but receives the + * results in binary format. By using out-of-line parameters we can avoid + * a lot of tedious mucking about with quoting and escaping, even though + * the data is text. Notice how we don't have to do anything special with + * the quote mark in the parameter value. + */ + + /* Here is our out-of-line parameter value */ + paramValues[0] = "joe's place"; + + res = PQexecParams(conn, + "SELECT * FROM test1 WHERE t = $1", + 1, /* one param */ + NULL, /* let the backend deduce param type */ + paramValues, + NULL, /* don't need param lengths since text */ + NULL, /* default to all text params */ + 1); /* ask for binary results */ + + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + + show_binary_results(res); + + PQclear(res); + + /* + * In this second example we transmit an integer parameter in binary form, + * and again retrieve the results in binary form. + * + * Although we tell PQexecParams we are letting the backend deduce + * parameter type, we really force the decision by casting the parameter + * symbol in the query text. This is a good safety measure when sending + * binary parameters. + */ + + /* Convert integer value "2" to network byte order */ + binaryIntVal = htonl((uint32_t) 2); + + /* Set up parameter arrays for PQexecParams */ + paramValues[0] = (char *) &binaryIntVal; + paramLengths[0] = sizeof(binaryIntVal); + paramFormats[0] = 1; /* binary */ + + res = PQexecParams(conn, + "SELECT * FROM test1 WHERE i = $1::int4", + 1, /* one param */ + NULL, /* let the backend deduce param type */ + paramValues, + paramLengths, + paramFormats, + 1); /* ask for binary results */ + + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + + show_binary_results(res); + + PQclear(res); + + /* close the connection to the database and cleanup */ + PQfinish(conn); + + return 0; +} diff --git a/src/test/examples/testlibpq3.sql b/src/test/examples/testlibpq3.sql new file mode 100644 index 0000000..35a95ca --- /dev/null +++ b/src/test/examples/testlibpq3.sql @@ -0,0 +1,6 @@ +CREATE SCHEMA testlibpq3; +SET search_path = testlibpq3; +SET standard_conforming_strings = ON; +CREATE TABLE test1 (i int4, t text, b bytea); +INSERT INTO test1 values (1, 'joe''s place', '\000\001\002\003\004'); +INSERT INTO test1 values (2, 'ho there', '\004\003\002\001\000'); diff --git a/src/test/examples/testlibpq4.c b/src/test/examples/testlibpq4.c new file mode 100644 index 0000000..da44430 --- /dev/null +++ b/src/test/examples/testlibpq4.c @@ -0,0 +1,163 @@ +/* + * src/test/examples/testlibpq4.c + * + * + * testlibpq4.c + * this test program shows to use LIBPQ to make multiple backend + * connections + * + */ +#include <stdio.h> +#include <stdlib.h> +#include "libpq-fe.h" + +static void +exit_nicely(PGconn *conn1, PGconn *conn2) +{ + if (conn1) + PQfinish(conn1); + if (conn2) + PQfinish(conn2); + exit(1); +} + +static void +check_prepare_conn(PGconn *conn, const char *dbName) +{ + PGresult *res; + + /* check to see that the backend connection was successfully made */ + if (PQstatus(conn) != CONNECTION_OK) + { + fprintf(stderr, "%s", PQerrorMessage(conn)); + exit(1); + } + + /* Set always-secure search path, so malicious 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(1); + } + PQclear(res); +} + +int +main(int argc, char **argv) +{ + char *pghost, + *pgport, + *pgoptions; + char *dbName1, + *dbName2; + char *tblName; + int nFields; + int i, + j; + + PGconn *conn1, + *conn2; + + /* + * PGresult *res1, *res2; + */ + PGresult *res1; + + if (argc != 4) + { + fprintf(stderr, "usage: %s tableName dbName1 dbName2\n", argv[0]); + fprintf(stderr, " compares two tables in two databases\n"); + exit(1); + } + tblName = argv[1]; + dbName1 = argv[2]; + dbName2 = argv[3]; + + + /* + * begin, by setting the parameters for a backend connection if the + * parameters are null, then the system will try to use reasonable + * defaults by looking up environment variables or, failing that, using + * hardwired constants + */ + pghost = NULL; /* host name of the backend */ + pgport = NULL; /* port of the backend */ + pgoptions = NULL; /* special options to start up the backend + * server */ + + /* make a connection to the database */ + conn1 = PQsetdb(pghost, pgport, pgoptions, NULL, dbName1); + check_prepare_conn(conn1, dbName1); + + conn2 = PQsetdb(pghost, pgport, pgoptions, NULL, dbName2); + check_prepare_conn(conn2, dbName2); + + /* start a transaction block */ + res1 = PQexec(conn1, "BEGIN"); + if (PQresultStatus(res1) != PGRES_COMMAND_OK) + { + fprintf(stderr, "BEGIN command failed\n"); + PQclear(res1); + exit_nicely(conn1, conn2); + } + + /* + * make sure to PQclear() a PGresult whenever it is no longer needed to + * avoid memory leaks + */ + PQclear(res1); + + /* + * fetch instances from the pg_database, the system catalog of databases + */ + res1 = PQexec(conn1, "DECLARE myportal CURSOR FOR select * from pg_database"); + if (PQresultStatus(res1) != PGRES_COMMAND_OK) + { + fprintf(stderr, "DECLARE CURSOR command failed\n"); + PQclear(res1); + exit_nicely(conn1, conn2); + } + PQclear(res1); + + res1 = PQexec(conn1, "FETCH ALL in myportal"); + if (PQresultStatus(res1) != PGRES_TUPLES_OK) + { + fprintf(stderr, "FETCH ALL command didn't return tuples properly\n"); + PQclear(res1); + exit_nicely(conn1, conn2); + } + + /* first, print out the attribute names */ + nFields = PQnfields(res1); + for (i = 0; i < nFields; i++) + printf("%-15s", PQfname(res1, i)); + printf("\n\n"); + + /* next, print out the instances */ + for (i = 0; i < PQntuples(res1); i++) + { + for (j = 0; j < nFields; j++) + printf("%-15s", PQgetvalue(res1, i, j)); + printf("\n"); + } + + PQclear(res1); + + /* close the portal */ + res1 = PQexec(conn1, "CLOSE myportal"); + PQclear(res1); + + /* end the transaction */ + res1 = PQexec(conn1, "END"); + PQclear(res1); + + /* close the connections to the database and cleanup */ + PQfinish(conn1); + PQfinish(conn2); + +/* fclose(debug); */ + return 0; +} diff --git a/src/test/examples/testlo.c b/src/test/examples/testlo.c new file mode 100644 index 0000000..b20659d --- /dev/null +++ b/src/test/examples/testlo.c @@ -0,0 +1,270 @@ +/*------------------------------------------------------------------------- + * + * testlo.c + * test using large objects with libpq + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/test/examples/testlo.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, int 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); + + lo_lseek(conn, lobj_fd, start, SEEK_SET); + 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, int 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); + + lo_lseek(conn, lobj_fd, start, SEEK_SET); + 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); +} + + +/* + * 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); +} + +static void +exit_nicely(PGconn *conn) +{ + PQfinish(conn); + exit(1); +} + +int +main(int argc, char **argv) +{ + char *in_filename, + *out_filename; + char *database; + Oid lobjOid; + PGconn *conn; + PGresult *res; + + if (argc != 4) + { + fprintf(stderr, "Usage: %s database_name in_filename out_filename\n", + argv[0]); + exit(1); + } + + database = argv[1]; + in_filename = argv[2]; + out_filename = argv[3]; + + /* + * 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, "%s", PQerrorMessage(conn)); + exit_nicely(conn); + } + + /* Set always-secure search path, so malicious 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 1000-2000 of the large object\n"); + pickout(conn, lobjOid, 1000, 1000); + + printf("overwriting bytes 1000-2000 of the large object with X's\n"); + overwrite(conn, lobjOid, 1000, 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)); + } + + res = PQexec(conn, "end"); + PQclear(res); + PQfinish(conn); + return 0; +} diff --git a/src/test/examples/testlo64.c b/src/test/examples/testlo64.c new file mode 100644 index 0000000..903d279 --- /dev/null +++ b/src/test/examples/testlo64.c @@ -0,0 +1,301 @@ +/*------------------------------------------------------------------------- + * + * testlo64.c + * test using large objects with libpq using 64-bit APIs + * + * Portions Copyright (c) 1996-2023, 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); +} + +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, "%s", PQerrorMessage(conn)); + exit_nicely(conn); + } + + /* Set always-secure search path, so malicious 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; +} |