From aaf19c0e25715c2b46164798ff017d688487763b Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Mon, 12 Jun 2000 04:01:52 +0000
Subject: [PATCH]  Well, pg_dumplo is in attache. It is really simple program
 and now is not prepared for dirtribution (it needs a little changes). I can
 change and work on this, but I need motivation :-)

And Peter, I know and I agree that standard PG tree is not good space for
all interfaces and for all tools based on PG, but LO is PG feature and we
haven't backup tool for LO.


Karel Zak
---
 contrib/README                |   3 +
 contrib/pg_dumplo/Makefile    |  26 +++
 contrib/pg_dumplo/README      |  18 ++
 contrib/pg_dumplo/VERSION     |   1 +
 contrib/pg_dumplo/pg_dumplo.c | 379 ++++++++++++++++++++++++++++++++++
 5 files changed, 427 insertions(+)
 create mode 100644 contrib/pg_dumplo/Makefile
 create mode 100644 contrib/pg_dumplo/README
 create mode 100644 contrib/pg_dumplo/VERSION
 create mode 100644 contrib/pg_dumplo/pg_dumplo.c

diff --git a/contrib/README b/contrib/README
index 773b0e30c3b..7dcd1ca1fe9 100644
--- a/contrib/README
+++ b/contrib/README
@@ -60,6 +60,9 @@ mSQL-interface -
 noupdate -
 	trigger to prevent updates on single columns
 
+pg_dumplo -
+	Dump large objects
+
 soundex -
 	Prototype for soundex function
 
diff --git a/contrib/pg_dumplo/Makefile b/contrib/pg_dumplo/Makefile
new file mode 100644
index 00000000000..0d4fb834243
--- /dev/null
+++ b/contrib/pg_dumplo/Makefile
@@ -0,0 +1,26 @@
+
+PROGRAM	= pg_dumplo
+	
+OBJECTS	= pg_dumplo.o
+
+CFLAGS 	= -Wall -fpic -g
+CC	= gcc
+RM	= rm -f
+INCLUDE = -I/usr/include/postgresql
+LIBS	=-lpq
+
+COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDE)
+LINK    = $(CC) $(CFLAGS) -o $@ $(LIBS)
+
+
+all: $(PROGRAM) 	
+
+$(PROGRAM): $(OBJECTS)
+	$(LINK) $(OBJECTS)
+
+.c.o: $<
+	$(COMPILE) -c $<
+
+clean:
+	$(RM) -f *~ $(OBJECTS) $(PROGRAM)
+
diff --git a/contrib/pg_dumplo/README b/contrib/pg_dumplo/README
new file mode 100644
index 00000000000..b36cdd01d0b
--- /dev/null
+++ b/contrib/pg_dumplo/README
@@ -0,0 +1,18 @@
+
+	pg_dumplo - PostgreSQL large object dumper
+        
+
+	Hmm... documentation is not available. For more information
+	see the help ( pg_dumplo -h ) and examples in this help.
+
+	Compilation:
+			- you need the PostgreSQL's devel. libs 
+			- and type: 'make'
+
+					
+				        Karel Zak <zakkr@zf.jcu.cz>		
+		
+				
+
+	
+
diff --git a/contrib/pg_dumplo/VERSION b/contrib/pg_dumplo/VERSION
new file mode 100644
index 00000000000..05b19b1f76e
--- /dev/null
+++ b/contrib/pg_dumplo/VERSION
@@ -0,0 +1 @@
+0.0.4
\ No newline at end of file
diff --git a/contrib/pg_dumplo/pg_dumplo.c b/contrib/pg_dumplo/pg_dumplo.c
new file mode 100644
index 00000000000..6e6a20bef55
--- /dev/null
+++ b/contrib/pg_dumplo/pg_dumplo.c
@@ -0,0 +1,379 @@
+
+#include <stdio.h>	
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>                            
+#include <time.h>
+
+#include <libpq-fe.h>
+#include <libpq/libpq-fs.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+        
+extern int 	errno;        
+        
+#define QUERY_BUFSIZ	(8*1024)
+#define DIR_UMASK	0755
+#define FILE_UMASK	0666 
+
+#define	TRUE		1
+#define FALSE		0
+#define RE_OK		0
+#define RE_ERROR	1
+
+typedef struct { 
+	char	*table,
+		*attr;
+	long	lo_oid;
+} lo_attr;
+
+void usage()
+{
+	printf("\nPostgreSQL large objects dump");
+	printf("\npg_lo_dump <option>\n\n");
+	printf("-h --help			this help\n");	
+	printf("-u --user='username'		username for connection to server\n");	
+	printf("-p --password='password'	password for connection to server\n");	
+	printf("-d --db='database'		database name\n");	
+	printf("-t --host='hostname'		server hostname\n");		
+	printf("-l <table.attr ...>		dump attribute (columns) with LO to dump tree\n");
+	printf("-i --import			import large obj dump tree to DB\n");
+	printf("-s --space=<dir>		directory with dupm tree (for dump/import)\n");
+	printf("\nExample (dump):   pg_lo_dump -d my_db -s /my_dump/dir/ -l t1.a t1.b t2.a\n");	
+	printf("Example (import): pg_lo_dump -i -d my_db -s /my_dump/dir/\n");	
+	printf("\nNote:  * option '-l' must be last option!\n");	
+	printf("       * option '-i' (--import) make new large obj in DB, not rewrite old,\n");	
+	printf("         import UPDATE oid numbers in table.attr only.\n");	
+	printf("\n\n"); 
+}
+
+typedef enum {
+	ARG_USER,
+	ARG_PWD,
+	ARG_DB,
+	ARG_HELP,
+	ARG_HOST
+} _ARG_;
+
+/*-----
+ *	Init and allocate lo_attr structs
+ *
+ *	!	data is **argv 
+ *-----
+ */
+lo_attr *init_loa(char **data, int max, int start)
+{
+	lo_attr 	*l, 
+			*ll;
+	char		**d, 
+			*loc,
+			buff[128];
+
+	if ((l = (lo_attr *) malloc(max * sizeof(lo_attr))) == NULL) {
+		fprintf(stderr, "%s: can't allocate memory\n", data[0]);
+		exit(RE_ERROR);
+	}
+	for(d=data+start, ll=l; *d != NULL; d++, ll++) {
+		strncpy(buff, *d, 128);
+		if ((loc = strchr(buff, '.')) == NULL) {
+			fprintf(stderr, "%s: '%s' is bad 'table.attr'\n", data[0], buff);
+			exit(RE_ERROR);	
+		}
+		*loc = '\0';
+		ll->table = strdup(buff);
+		ll->attr = strdup(++loc);
+	}
+	ll++;
+	ll->table = ll->attr = (char *) NULL;
+	return l;
+}
+
+/*-----
+ *	Check PG result
+ *-----
+ */
+int check_res(PGresult *res, PGconn *conn) 
+{
+	if (!res && PQresultStatus(res) != PGRES_COMMAND_OK) {
+        	fprintf(stderr, "%s\n",PQerrorMessage(conn));
+                PQclear(res);
+                return FALSE;
+        }
+        if (PQresultStatus(res) != PGRES_TUPLES_OK) {
+                fprintf(stderr, "Tuples is not OK.\n");
+                PQclear(res);
+                return FALSE;
+        }
+        return TRUE;        
+}
+
+
+/*-----
+ *	LO dump
+ *-----
+ */
+ void dump_lo(PGconn *conn, char *space, lo_attr *loa, char *db, char *prog)
+ {
+	PGresult	*res;		
+	lo_attr		*ploa;
+	FILE		*majorfile;
+	char		*dir,
+			path[BUFSIZ],
+			Qbuff[QUERY_BUFSIZ];
+	
+	dir = space ? space : getenv("PWD");		
+	sprintf(path, "%s/%s", dir, db); 
+	if (mkdir(path, DIR_UMASK) == -1) {
+		if (errno != EEXIST) {
+			perror(path);
+			exit(RE_ERROR);					
+		}	
+	}
+	
+	sprintf(path, "%s/lo_dump.index", path);          
+	if ((majorfile = fopen(path, "w")) == NULL) {
+		perror(path);
+		exit(RE_ERROR);
+	} else {
+		time_t	t;
+		time(&t);
+		fprintf(majorfile, "#\n# This is the PostgreSQL large object dump index\n#\n");
+		fprintf(majorfile, "#\tDate:     %s", ctime(&t));
+		fprintf(majorfile, "#\tHost:     %s\n", PQhost(conn) ? PQhost(conn) : "localhost");
+		fprintf(majorfile, "#\tDatabase: %s\n", db);
+		fprintf(majorfile, "#\tUser:     %s\n", PQuser(conn));
+		fprintf(majorfile, "#\n# oid\ttable\tattribut\tinfile\n");
+	}
+
+	for(ploa=loa; ploa->table != NULL; ploa++) {
+	
+		/* query */
+		sprintf(Qbuff, "SELECT %s FROM %s WHERE %s!=0", 
+			ploa->attr, ploa->table, ploa->attr);
+			
+		res = PQexec(conn, Qbuff);
+	
+		if (check_res(res, conn)) {
+			int	tuples	= PQntuples(res),
+				t;
+			char	*val;
+	
+			/* Make DIR/FILE */
+			if (tuples) {
+				sprintf(path, "%s/%s/%s", dir, db, ploa->table);          
+				if (mkdir(path, DIR_UMASK) == -1) {
+					if (errno != EEXIST) {
+						perror(path);
+						exit(RE_ERROR);					
+					}	
+				}
+				sprintf(path, "%s/%s", path, ploa->attr);          
+				if (mkdir(path, DIR_UMASK) == -1) {
+					if (errno != EEXIST) {
+						perror(path);
+						exit(RE_ERROR);					
+					}	
+				}
+				fprintf(stderr, "%s: dump %s.%s (%d lagre obj)\n", prog, 
+					ploa->table, ploa->attr, tuples);
+			}
+
+			for(t=0; t<tuples; t++) {
+				val = PQgetvalue(res, t, 0);
+				if (!val)
+					continue;
+				
+				sprintf(path, "%s/%s/%s/%s/%s", dir, db, ploa->table, ploa->attr, val);
+				
+				if (lo_export(conn, (Oid) atol(val), path) < 0) 
+					fprintf(stderr, "%s\n", PQerrorMessage(conn));
+				else
+					fprintf(majorfile, "%s\t%s\t%s\t%s/%s/%s/%s\n", val, 
+						ploa->table, ploa->attr, db, ploa->table, ploa->attr, val);
+			}
+		}
+	}	
+	fclose(majorfile);
+ }
+
+
+/*-----
+ *	LO import
+ *-----
+ */
+ void import_lo(PGconn *conn, char *space, char *db, char *prog)
+ {
+	PGresult	*res;		
+	lo_attr		loa;
+	FILE		*majorfile;
+	long 		new_oid;
+	char		*dir,
+			tab[128], attr[128],
+			path[BUFSIZ], lo_path[BUFSIZ],
+			Qbuff[QUERY_BUFSIZ];
+	
+	dir = space ? space : getenv("PWD");
+	sprintf(path, "%s/%s", dir, db);          
+
+	sprintf(path, "%s/lo_dump.index", path);          
+	if ((majorfile = fopen(path, "r")) == NULL) {
+		perror(path);
+		exit(RE_ERROR);
+	} 
+
+	while(fgets(Qbuff, QUERY_BUFSIZ, majorfile)) {
+		
+		if (*Qbuff == '#')
+			continue;
+
+		fprintf(stdout, Qbuff);
+		
+		sscanf(Qbuff, "%ld\t%s\t%s\t%s\n", &loa.lo_oid, tab, attr, path); 
+		loa.table = tab;
+		loa.attr  = attr;
+
+		sprintf(lo_path, "%s/%s", dir, path); 
+				
+		/* import large obj */
+		if ((new_oid = lo_import(conn, lo_path)) <= 0) {
+			fprintf(stderr, "%s\n",PQerrorMessage(conn));
+			PQexec(conn, "ROLLBACK");
+			fprintf(stderr, "\nROLLBACK\n");
+			exit(RE_ERROR);
+		}
+	
+		/* query */
+		sprintf(Qbuff, "UPDATE %s SET %s=%ld WHERE %s=%ld", 
+			loa.table, loa.attr, new_oid, loa.attr, loa.lo_oid);
+
+		/*fprintf(stderr, Qbuff);*/
+			
+		res = PQexec(conn, Qbuff);
+	
+		if (!res && PQresultStatus(res) != PGRES_COMMAND_OK) {
+        		fprintf(stderr, "%s\n",PQerrorMessage(conn));
+                	PQclear(res);
+                	PQexec(conn, "ROLLBACK");
+			fprintf(stderr, "\nROLLBACK\n");
+			exit(RE_ERROR);
+        	}	
+        	
+	}	
+	fclose(majorfile);
+ }
+
+/*-----
+ *	The mother of all C functions
+ *-----
+ */
+int main(int argc, char **argv)
+{
+	PGconn		*conn;
+	lo_attr		*loa	=NULL;
+	char		*user	=NULL, 
+			*pwd	=NULL, 
+			*db	=NULL,
+			*host	=NULL,	
+			*space	=NULL;
+	int		import	=FALSE;
+
+	/* Parse argv */
+	if (argc) {
+      		int	arg, l_index=0;
+      		extern int optind;
+      		typedef enum {
+			ARG_USER,
+			ARG_PWD,
+			ARG_DB,
+			ARG_HELP,
+			ARG_IMPORT,
+			ARG_SPACE,
+			ARG_HOST
+		} _ARG_;
+      		
+      		struct option l_opt[] = {
+      			{ "help",	0, 0, ARG_HELP	 	},		
+			{ "user",	1, 0, ARG_USER 		},
+			{ "pwd",	1, 0, ARG_PWD 		},
+			{ "db",		1, 0, ARG_DB 		},
+			{ "host",	1, 0, ARG_HOST 		},
+			{ "space",	1, 0, ARG_SPACE		},
+			{ "import",	0, 0, ARG_IMPORT	},
+			{ NULL, 0, 0, 0 }
+      		};     	
+
+		while((arg = getopt_long(argc, argv, "hu:p:d:l:t:is:", l_opt, &l_index)) != -1) {
+        		switch(arg) {
+      			case ARG_HELP:
+      			case 'h':
+      				usage();
+        		    	exit(RE_OK);
+        		case ARG_USER:
+        		case 'u':    	
+        			user = strdup(optarg);
+        			break;	 
+        		case ARG_HOST:
+        		case 't':    	
+        			host = strdup(optarg);
+        			break;	 	
+        		case ARG_PWD:
+        		case 'p':    	
+        			pwd = strdup(optarg);
+        			break;	          		
+        		case ARG_DB:
+        		case 'd':    	
+        			db = strdup(optarg);
+        			break;
+        		case ARG_SPACE:
+        		case 's':    	
+        			space = strdup(optarg);
+        			break;	
+        		case ARG_IMPORT:
+        		case 'i':    	
+        			import = TRUE;
+        			break;	
+        		case 'l':
+        			loa = init_loa(argv, argc-1, optind-1);
+        			break;		        		
+        		}
+		}	
+	}  	
+
+	if (!space && !getenv("PWD")) {
+		fprintf(stderr, "%s: can't set directory (not set '-s' or $PWD).\n", argv[0]);
+		exit(RE_ERROR);
+	}
+
+	/* Make PG connection */
+	conn = PQsetdbLogin(host, NULL, NULL, NULL, db, user, pwd);
+        
+	/* check to see that the backend connection was successfully made */
+        if (PQstatus(conn) == CONNECTION_BAD) {
+                fprintf(stderr, "%s\n",PQerrorMessage(conn));
+                exit(RE_ERROR);
+        }
+
+	PQexec(conn, "BEGIN");
+
+	if (import) {
+		/* import obj */
+		import_lo(conn, space, db, argv[0]);
+	} else if (loa)	{
+		/* Dump obj */
+		dump_lo(conn, space, loa, db, argv[0]);
+	} else {
+		fprintf(stderr, "%s: ERROR: bad arg!\n", argv[0]);
+		usage();
+	}	
+
+	PQexec(conn, "COMMIT");
+	
+	/* bye PG */ 
+	PQfinish(conn);	
+	exit(RE_OK);
+}
\ No newline at end of file
-- 
GitLab