From 94bb87f94b181898c2833a628744c67035bfb628 Mon Sep 17 00:00:00 2001 From: Peter Mount <peter@retep.org.uk> Date: Sat, 10 Apr 1999 16:48:05 +0000 Subject: [PATCH] vacuumlo deals with large objects not referenced by any tables and removes them. --- contrib/vacuumlo/Makefile | 24 +++++ contrib/vacuumlo/README | 38 +++++++ contrib/vacuumlo/vacuumlo.c | 201 ++++++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 contrib/vacuumlo/Makefile create mode 100644 contrib/vacuumlo/README create mode 100644 contrib/vacuumlo/vacuumlo.c diff --git a/contrib/vacuumlo/Makefile b/contrib/vacuumlo/Makefile new file mode 100644 index 00000000000..95214acb982 --- /dev/null +++ b/contrib/vacuumlo/Makefile @@ -0,0 +1,24 @@ +# $Header: /cvsroot/pgsql/contrib/vacuumlo/Makefile,v 1.1 1999/04/10 16:48:04 peter Exp $ + +SRCDIR= ../../src + +include $(SRCDIR)/Makefile.global + +CONTRIBDIR=$(LIBDIR)/contrib + +CFLAGS+= -I$(HEADERDIR) + +TARGETS= vacuumlo +CLEANFILES+= $(TARGETS) +CURDIR=`pwd` + +all:: $(TARGETS) + +$(TARGETS): vacuumlo.o + $(CC) -o vacuumlo -L $(LIBDIR) -lpq -lcrypt vacuumlo.o + +clean: + rm -f $(TARGETS) *.o + +dist: + tar cf vacuumlo.tar README Makefile vacuumlo.c diff --git a/contrib/vacuumlo/README b/contrib/vacuumlo/README new file mode 100644 index 00000000000..2c3c93de154 --- /dev/null +++ b/contrib/vacuumlo/README @@ -0,0 +1,38 @@ +$Header: /cvsroot/pgsql/contrib/vacuumlo/Attic/README,v 1.1 1999/04/10 16:48:04 peter Exp $ + +This is a simple utility that will remove any orphaned large objects out of a +PostgreSQL database. + +Compiling +-------- + +Simply run make. A single executable "vacuumlo" is created. + +Useage +------ + +vacuumlo [-v] database [db2 ... dbn] + +The -v flag outputs some progress messages to stdout. + +Method +------ + +First, it builds a temporary table which contains all of the oid's of the +large objects in that database. + +It then scans through any columns in the database that are of type 'oid', and +removes any entries from the temporary table. + +Finally, it runs through the first table, and removes from the second table, any +oid's it finds. What is left are the orphans, and these are removed. + +I decided to place this in contrib as it needs further testing, but hopefully, +this (or a variant of it) would make it into the backed as a "vacuum lo" command +in a later release. + +Peter Mount <peter@retep.org.uk> +http://www.retep.org.uk +March 21 1999 + +Committed April 10 1999 Peter diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c new file mode 100644 index 00000000000..8b893f26e2c --- /dev/null +++ b/contrib/vacuumlo/vacuumlo.c @@ -0,0 +1,201 @@ +/*------------------------------------------------------------------------- + * + * vacuumlo.c + * This removes orphaned large objects from a database. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/contrib/vacuumlo/vacuumlo.c,v 1.1 1999/04/10 16:48:05 peter Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <stdlib.h> +#include <strings.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 + +int vacuumlo(char *,int); + + +/* + * This vacuums a database. It returns 1 on success, -1 on failure. + */ +int vacuumlo(char *database,int verbose) +{ + PGconn *conn; + PGresult *res, *res2; + char buf[BUFSIZE]; + int matched=0; /* Number matched per scan */ + int i; + + conn = PQsetdb(NULL, NULL, NULL, NULL, database); + + /* check to see that the backend connection was successfully made */ + if (PQstatus(conn) == CONNECTION_BAD) + { + fprintf(stderr, "Connection to database '%s' failed.\n", database); + fprintf(stderr, "%s", PQerrorMessage(conn)); + return -1; + } + + if(verbose) + fprintf(stdout,"Connected to %s\n",database); + + /* + * First we create and populate the lo temp table + */ + buf[0]='\0'; + strcat(buf,"SELECT oid AS lo "); + strcat(buf,"INTO TEMP TABLE vacuum_l "); + strcat(buf,"FROM pg_class "); + strcat(buf,"WHERE relkind='l'"); + if(!(res = PQexec(conn,buf))) { + fprintf(stderr,"Failed to create temp table.\n"); + PQfinish(conn); + return -1; + } + PQclear(res); + + /* + * Now find any candidate tables who have columns of type oid (the column + * oid is ignored, as it has attnum < 1) + */ + buf[0]='\0'; + strcat(buf,"SELECT c.relname, a.attname "); + strcat(buf,"FROM pg_class c, pg_attribute a, pg_type t "); + strcat(buf,"WHERE a.attnum > 0 "); + strcat(buf," AND a.attrelid = c.oid "); + strcat(buf," AND a.atttypid = t.oid "); + strcat(buf," AND t.typname = 'oid' "); + strcat(buf," AND c.relname NOT LIKE 'pg_%'"); + if(!(res = PQexec(conn,buf))) { + fprintf(stderr,"Failed to create temp table.\n"); + PQfinish(conn); + return -1; + } + for(i=0;i<PQntuples(res);i++) + { + char *table,*field; + + table = PQgetvalue(res,i,0); + field = PQgetvalue(res,i,1); + + if(verbose) { + fprintf(stdout,"Checking %s in %s: ",field,table); + fflush(stdout); + } + + res2 = PQexec(conn, "begin"); + PQclear(res2); + + buf[0] = '\0'; + strcat(buf,"DELETE FROM vacuum_l "); + strcat(buf,"WHERE lo IN ("); + strcat(buf,"SELECT "); + strcat(buf,field); + strcat(buf," FROM "); + strcat(buf,table); + strcat(buf,");"); + if(!(res2 = PQexec(conn,buf))) { + fprintf(stderr,"Failed to check %s in table %s\n",field,table); + PQclear(res); + PQfinish(conn); + return -1; + } + if(PQresultStatus(res2)!=PGRES_COMMAND_OK) { + fprintf(stderr, + "Failed to check %s in table %s\n%s\n", + field,table, + PQerrorMessage(conn) + ); + PQclear(res2); + PQclear(res); + PQfinish(conn); + return -1; + } + PQclear(res2); + + res2 = PQexec(conn, "end"); + PQclear(res2); + + } + PQclear(res); + + /* Start the transaction */ + res = PQexec(conn, "begin"); + PQclear(res); + + /* + * Finally, those entries remaining in vacuum_l are orphans. + */ + buf[0]='\0'; + strcat(buf,"SELECT lo "); + strcat(buf,"FROM vacuum_l"); + if(!(res = PQexec(conn,buf))) { + fprintf(stderr,"Failed to read temp table.\n"); + PQfinish(conn); + return -1; + } + matched=PQntuples(res); + for(i=0;i<matched;i++) + { + Oid lo = (Oid) atoi(PQgetvalue(res,i,0)); + + if(verbose) { + fprintf(stdout,"\rRemoving lo %6d \n",lo); + fflush(stdout); + } + + if(lo_unlink(conn,lo)<0) { + fprintf(stderr,"Failed to remove lo %d\n",lo); + } + } + PQclear(res); + + /* + * That's all folks! + */ + res = PQexec(conn, "end"); + PQclear(res); + PQfinish(conn); + + if(verbose) + fprintf(stdout,"\rRemoved %d large objects from %s.\n",matched,database); + + return 0; +} + +int +main(int argc, char **argv) +{ + int verbose = 0; + int arg; + int rc=0; + + if (argc < 2) + { + fprintf(stderr, "Usage: %s [-v] database_name [db2 ... dbn]\n", + argv[0]); + exit(1); + } + + for(arg=1;arg<argc;arg++) { + if(strcmp("-v",argv[arg])==0) + verbose=!verbose; + else + rc += vacuumlo(argv[arg],verbose); + } + + return rc; +} -- GitLab