vacuumlo deals with large objects not referenced by any tables and removes them.

This commit is contained in:
Peter Mount 1999-04-10 16:48:05 +00:00
parent 098e043849
commit 94bb87f94b
3 changed files with 263 additions and 0 deletions

24
contrib/vacuumlo/Makefile Normal file
View File

@ -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

38
contrib/vacuumlo/README Normal file
View File

@ -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

201
contrib/vacuumlo/vacuumlo.c Normal file
View File

@ -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;
}