Commit 85c165cd authored by Bruce Momjian's avatar Bruce Momjian

New findoidjoins examines oid columns to find join relationships.

parent ffb120ec
...@@ -18,6 +18,11 @@ earthdistance - ...@@ -18,6 +18,11 @@ earthdistance -
Operator for computing earth distance for two points Operator for computing earth distance for two points
by Hal Snyder <hal@vailsys.com> by Hal Snyder <hal@vailsys.com>
findoidjoins -
Finds the joins used by oid columns by examining the actual
values in the oid columns and row oids.
by Bruce Momjian <root@candle.pha.pa.us>
fulltextindex - fulltextindex -
Full text indexing using triggers Full text indexing using triggers
by Maarten Boekhold <maartenb@dutepp0.et.tudelft.nl> by Maarten Boekhold <maartenb@dutepp0.et.tudelft.nl>
......
#
# Makefile, requires pgsql/contrib/pginterface
#
#
PGINTERFACE = pginterface.o halt.o # these have to be in your library search path
TARGET = findoidjoins
CFLAGS = -g -Wall -I. -I../../src/interfaces/libpq -I/usr/local/pgsql/include
LDFLAGS = -L/usr/local/pgsql/lib -lpq
all : $(TARGET)
findoidjoins: $(PGINTERFACE) findoidjoins.c
gcc -o $@ $(CFLAGS) $@.c $(PGINTERFACE) $(LDFLAGS)
clean:
rm -f *.o $(TARGET) log core
install:
install -s -o bin -g bin $(TARGET) /usr/local/pgsql/bin
findoidjoins
This program scans the a database, and prints oid fields, and the tables
they join to. PostgreSQL version 6.3.2 crashes with aggregates on
views, so I have removed the view pg_user from the list of relations to
examine.
Run on am empty database, it returns the system join relationships:
---------------------------------------------------------------------------
Join pg_aggregate.aggfinaltype => pg_proc.oid
Join pg_aggregate.aggfinaltype => pg_type.oid
Join pg_aggregate.aggowner => pg_proc.oid
Join pg_aggregate.aggbasetype => pg_proc.oid
Join pg_aggregate.aggbasetype => pg_type.oid
Join pg_aggregate.aggtranstype1 => pg_proc.oid
Join pg_aggregate.aggtranstype1 => pg_type.oid
Join pg_aggregate.aggtranstype2 => pg_type.oid
Join pg_am.amowner => pg_proc.oid
Join pg_amop.amopid => pg_am.oid
Join pg_amop.amopopr => pg_operator.oid
Join pg_amop.amopclaid => pg_opclass.oid
Join pg_amproc.amproc => pg_operator.oid
Join pg_amproc.amproc => pg_proc.oid
Join pg_amproc.amopclaid => pg_opclass.oid
Join pg_amproc.amopclaid => pg_operator.oid
Join pg_amproc.amopclaid => pg_proc.oid
Join pg_amproc.amid => pg_am.oid
Join pg_attribute.attrelid => pg_class.oid
Join pg_attribute.atttypid => pg_type.oid
Join pg_class.relam => pg_am.oid
Join pg_class.reltype => pg_type.oid
Join pg_class.relowner => pg_proc.oid
Join pg_description.objoid => pg_proc.oid
Join pg_description.objoid => pg_type.oid
Join pg_index.indexrelid => pg_class.oid
Join pg_index.indrelid => pg_class.oid
Join pg_index.indproc => pg_proc.oid
Join pg_opclass.opcdeftype => pg_type.oid
Join pg_operator.oprcom => pg_operator.oid
Join pg_operator.oprrsortop => pg_operator.oid
Join pg_operator.oprlsortop => pg_operator.oid
Join pg_operator.oprnegate => pg_operator.oid
Join pg_operator.oprresult => pg_type.oid
Join pg_operator.oprright => pg_type.oid
Join pg_operator.oprleft => pg_type.oid
Join pg_operator.oprowner => pg_proc.oid
Join pg_parg.partype => pg_type.oid
Join pg_parg.parproid => pg_operator.oid
Join pg_parg.parproid => pg_proc.oid
Join pg_proc.prolang => pg_language.oid
Join pg_proc.prorettype => pg_type.oid
Join pg_proc.proowner => pg_proc.oid
Join pg_rewrite.ev_class => pg_class.oid
Join pg_statistic.starelid => pg_class.oid
Join pg_type.typrelid => pg_class.oid
Join pg_type.typowner => pg_proc.oid
Join pg_type.typelem => pg_operator.oid
Join pg_type.typelem => pg_proc.oid
Join pg_type.typelem => pg_type.oid
---------------------------------------------------------------------------
Bruce Momjian (root@candle.pha.pa.us)
/*
* findoidjoins.c, required pgsql/contrib/pginterface
*
*/
#include <stdio.h>
#include "halt.h"
#include <libpq-fe.h>
#include "pginterface.h"
PGresult *attres, *relres;
int
main(int argc, char **argv)
{
char query[4000];
char relname[256];
char relname2[256];
char attname[256];
int count;
if (argc != 2)
halt("Usage: %s database\n", argv[0]);
connectdb(argv[1], NULL, NULL, NULL, NULL);
on_error_continue();
on_error_stop();
doquery("BEGIN WORK");
doquery("\
DECLARE c_attributes BINARY CURSOR FOR \
SELECT relname, a.attname \
FROM pg_class c, pg_attribute a, pg_type t \
WHERE a.attnum > 0 AND \
relkind = 'r' AND \
typname = 'oid' AND \
a.attrelid = c.oid AND \
a.atttypid = t.oid \
ORDER BY 1; \
");
doquery("FETCH ALL IN c_attributes");
attres = get_result();
doquery("\
DECLARE c_relations BINARY CURSOR FOR \
SELECT relname \
FROM pg_class c \
WHERE relkind = 'r' AND \
relname != 'pg_user' \
ORDER BY 1; \
");
doquery("FETCH ALL IN c_relations");
relres = get_result();
set_result(attres);
while (fetch(relname, attname) != END_OF_TUPLES)
{
set_result(relres);
reset_fetch();
while (fetch(relname2) != END_OF_TUPLES)
{
unset_result(relres);
sprintf(query,"\
DECLARE c_matches BINARY CURSOR FOR \
SELECT count(*)
FROM %s t1, %s t2 \
WHERE t1.%s = t2.oid", relname, relname2, attname);
doquery(query);
doquery("FETCH ALL IN c_matches");
fetch(&count);
if (count != 0)
printf("Join %s.%s => %s.oid\n", relname, attname, relname2);
doquery("CLOSE c_matches");
set_result(relres);
}
set_result(attres);
}
set_result(relres);
doquery("CLOSE c_relations");
PQclear(relres);
set_result(attres);
doquery("CLOSE c_attributes");
PQclear(attres);
unset_result(attres);
doquery("COMMIT WORK");
disconnectdb();
return 0;
}
...@@ -22,6 +22,11 @@ useful if you are running the query engine on a system with a different ...@@ -22,6 +22,11 @@ useful if you are running the query engine on a system with a different
architecture than the database server. If you pass a NULL pointer, the architecture than the database server. If you pass a NULL pointer, the
column is skipped, and you can use libpq to handle it as you wish. column is skipped, and you can use libpq to handle it as you wish.
There are two functions, get_result() and set_result, that allow you to
handle multiple result sets at the same time.
There is a reset_fetch() that starts the fetch back at the beginning.
There is a demo program called pginsert that demonstrates how the There is a demo program called pginsert that demonstrates how the
library can be used. library can be used.
......
...@@ -22,6 +22,9 @@ static PGresult *res = NULL; ...@@ -22,6 +22,9 @@ static PGresult *res = NULL;
static int on_error_state = ON_ERROR_STOP; static int on_error_state = ON_ERROR_STOP;
static in_result_block = false;
static was_get_unset_result = false;
/* LOCAL VARIABLES */ /* LOCAL VARIABLES */
static int tuple; static int tuple;
...@@ -64,9 +67,10 @@ disconnectdb() ...@@ -64,9 +67,10 @@ disconnectdb()
PGresult * PGresult *
doquery(char *query) doquery(char *query)
{ {
if (res != NULL) if (res != NULL && in_result_block == false && was_get_unset_result == false)
PQclear(res); PQclear(res);
was_get_unset_result = false;
res = PQexec(conn, query); res = PQexec(conn, query);
if (on_error_state == ON_ERROR_STOP && if (on_error_state == ON_ERROR_STOP &&
...@@ -187,3 +191,69 @@ on_error_continue() ...@@ -187,3 +191,69 @@ on_error_continue()
{ {
on_error_state = ON_ERROR_CONTINUE; on_error_state = ON_ERROR_CONTINUE;
} }
/*
**
** get_result
**
*/
PGresult *get_result()
{
was_get_unset_result = true;
/* we have to store the fetch location somewhere */
memcpy(&res->cmdStatus[CMDSTATUS_LEN-sizeof(tuple)],&tuple, sizeof(tuple));
return res;
}
/*
**
** set_result
**
*/
void set_result(PGresult *newres)
{
if (newres == NULL)
halt("set_result called with null result pointer\n");
if (res != NULL && was_get_unset_result == false)
if (in_result_block == false)
PQclear(res);
else
memcpy(&res->cmdStatus[CMDSTATUS_LEN-sizeof(tuple)], &tuple, sizeof(tuple));
in_result_block = true;
was_get_unset_result = false;
memcpy(&tuple, &newres->cmdStatus[CMDSTATUS_LEN-sizeof(tuple)], sizeof(tuple));
res = newres;
}
/*
**
** unset_result
**
*/
void unset_result(PGresult *oldres)
{
if (oldres == NULL)
halt("unset_result called with null result pointer\n");
if (in_result_block == false)
halt("Unset of result without being set.\n");
was_get_unset_result = true;
memcpy(&oldres->cmdStatus[CMDSTATUS_LEN-sizeof(tuple)], &tuple, sizeof(tuple));
in_result_block = false;
}
/*
**
** reset_fetch
**
*/
void reset_fetch()
{
tuple = 0;
}
...@@ -10,5 +10,9 @@ int fetch(void *param,...); ...@@ -10,5 +10,9 @@ int fetch(void *param,...);
int fetchwithnulls(void *param,...); int fetchwithnulls(void *param,...);
void on_error_continue(); void on_error_continue();
void on_error_stop(); void on_error_stop();
PGresult *get_result();
void set_result(PGresult *newres);
void unset_result(PGresult *oldres);
void reset_fetch();
#define END_OF_TUPLES (-1) #define END_OF_TUPLES (-1)
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment