diff options
author | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
---|---|---|
committer | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
commit | d31084e9d1118b25fd16580d9d8c2924b5740dff (patch) | |
tree | 3179e66307d54df9c7b966543550e601eb55e668 /src/bin | |
download | postgresql-d31084e9d1118b25fd16580d9d8c2924b5740dff.tar.gz postgresql-d31084e9d1118b25fd16580d9d8c2924b5740dff.zip |
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/bin')
44 files changed, 8735 insertions, 0 deletions
diff --git a/src/bin/Makefile b/src/bin/Makefile new file mode 100644 index 00000000000..67ed0b86ad3 --- /dev/null +++ b/src/bin/Makefile @@ -0,0 +1,30 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for src/bin (utility programs) +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/Makefile,v 1.1.1.1 1996/07/09 06:22:11 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# +# C programs +# +SUBDIR= monitor pg_id pg_version psql pg_dump + +ifeq ($(USE_TCL), true) +SUBDIR += pgtclsh +endif + +# +# Shell scripts +# +SUBDIR+= cleardbdir createdb createuser destroydb destroyuser initdb + + +include ../mk/postgres.subdir.mk + diff --git a/src/bin/Makefile.global b/src/bin/Makefile.global new file mode 100644 index 00000000000..dc6ed375b68 --- /dev/null +++ b/src/bin/Makefile.global @@ -0,0 +1,33 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# global configurations for Makefiles in src/bin +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/Attic/Makefile.global,v 1.1.1.1 1996/07/09 06:22:11 scrappy Exp $ +# +#------------------------------------------------------------------------- + +CFLAGS+= -I$(HEADERDIR) -I$(srcdir)/backend -I$(srcdir)/backend/include -I$(srcdir)/libpq + +# +# try locating libpq.a in the following places +# Almost all (all?) the C programs in this directory +# link with libpq, so we put it here. +# +LIBPQ:= -L$(srcdir)/libpq/$(objdir) -L$(LIBDIR) -lpq + +LD_ADD+= $(LIBPQ) +DPADD+= $(LIBPQ) + + +# +# And where libpq goes, so goes the authentication stuff... +# +ifdef KRBVERS +LD_ADD+= $(KRBLIBS) +CFLAGS+= $(KRBFLAGS) +endif diff --git a/src/bin/cleardbdir/Makefile b/src/bin/cleardbdir/Makefile new file mode 100644 index 00000000000..43d78487c42 --- /dev/null +++ b/src/bin/cleardbdir/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/cleardbdir +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/cleardbdir/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:11 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SHPROG= cleardbdir + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +include $(MKDIR)/postgres.shell.mk + diff --git a/src/bin/cleardbdir/cleardbdir.sh b/src/bin/cleardbdir/cleardbdir.sh new file mode 100644 index 00000000000..d9c03ab75cb --- /dev/null +++ b/src/bin/cleardbdir/cleardbdir.sh @@ -0,0 +1,37 @@ +#!/bin/sh +#------------------------------------------------------------------------- +# +# cleardbdir.sh-- +# completely clear out the database directory +# +# this program clears out the database directory, but leaves the .bki +# files so that initdb(1) can be run again. +# +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/cleardbdir/Attic/cleardbdir.sh,v 1.1.1.1 1996/07/09 06:22:11 scrappy Exp $ +# +#------------------------------------------------------------------------- + +[ -z "$PGDATA" ] && PGDATA=_fUnKy_DATADIR_sTuFf_ + +echo "This program completely destroys all the databases in the directory" +echo "$PGDATA" +echo _fUnKy_DASH_N_sTuFf_ "Are you sure you want to do this (y/n) [n]? "_fUnKy_BACKSLASH_C_sTuFf_ +read resp || exit +case $resp in + y*) : ;; + *) exit ;; +esac + +cd $PGDATA || exit +for i in * +do +if [ $i != "files" -a $i != "pg_hba" ] +then + /bin/rm -rf $i +fi +done diff --git a/src/bin/createdb/Makefile b/src/bin/createdb/Makefile new file mode 100644 index 00000000000..db5a63484ff --- /dev/null +++ b/src/bin/createdb/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/createdb +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/createdb/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SHPROG= createdb + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +include $(MKDIR)/postgres.shell.mk + diff --git a/src/bin/createdb/createdb.sh b/src/bin/createdb/createdb.sh new file mode 100644 index 00000000000..2d2116d4699 --- /dev/null +++ b/src/bin/createdb/createdb.sh @@ -0,0 +1,66 @@ +#!/bin/sh +#------------------------------------------------------------------------- +# +# createdb.sh-- +# create a postgres database +# +# this program runs the monitor with the "-c" option to create +# the requested database. +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/createdb/Attic/createdb.sh,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# ---------------- +# Set paths from environment or default values. +# The _fUnKy_..._sTuFf_ gets set when the script is installed +# from the default value for this build. +# Currently the only thing we look for from the environment is +# PGDATA, PGHOST, and PGPORT +# +# ---------------- +[ -z "$PGPORT" ] && PGPORT=5432 +[ -z "$PGHOST" ] && PGHOST=localhost +BINDIR=_fUnKy_BINDIR_sTuFf_ +PATH=$BINDIR:$PATH + +CMDNAME=`basename $0` + +if [ -z "$USER" ]; then + if [ -z "$LOGNAME" ]; then + if [ -z "`whoami`" ]; then + echo "$CMDNAME: cannot determine user name" + exit 1 + fi + else + USER=$LOGNAME + export USER + fi +fi + +dbname=$USER + +while test -n "$1" +do + case $1 in + -a) AUTHSYS=$2; shift;; + -h) PGHOST=$2; shift;; + -p) PGPORT=$2; shift;; + *) dbname=$1;; + esac + shift; +done + +AUTHOPT="-a $AUTHSYS" +[ -z "$AUTHSYS" ] && AUTHOPT="" + +monitor -TN $AUTHOPT -h $PGHOST -p $PGPORT -c "create database $dbname" template1 || { + echo "$CMDNAME: database creation failed on $dbname." + exit 1 +} + +exit 0 diff --git a/src/bin/createuser/Makefile b/src/bin/createuser/Makefile new file mode 100644 index 00000000000..7c0c3e13eef --- /dev/null +++ b/src/bin/createuser/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/createuser +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/createuser/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SHPROG= createuser + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +include $(MKDIR)/postgres.shell.mk + diff --git a/src/bin/createuser/createuser.sh b/src/bin/createuser/createuser.sh new file mode 100644 index 00000000000..4178f945961 --- /dev/null +++ b/src/bin/createuser/createuser.sh @@ -0,0 +1,225 @@ +#!/bin/sh +#------------------------------------------------------------------------- +# +# createuser.sh-- +# utility for creating a user in the POSTGRES database +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/createuser/Attic/createuser.sh,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +# Note - this should NOT be setuid. +# +#------------------------------------------------------------------------- + +# ---------------- +# Set paths from environment or default values. +# The _fUnKy_..._sTuFf_ gets set when the script is installed +# from the default value for this build. +# Currently the only thing we look for from the environment is +# PGDATA, PGHOST, and PGPORT +# +# ---------------- +[ -z "$PGPORT" ] && PGPORT=5432 +[ -z "$PGHOST" ] && PGHOST=localhost +BINDIR=_fUnKy_BINDIR_sTuFf_ +PATH=$BINDIR:$PATH + +CMDNAME=`basename $0` + +if [ -z "$USER" ]; then + if [ -z "$LOGNAME" ]; then + if [ -z "`whoami`" ]; then + echo "$CMDNAME: cannot determine user name" + exit 1 + fi + else + USER=$LOGNAME + export USER + fi +fi + +while [ -n "$1" ] +do + case $1 in + -a) AUTHSYS=$2; shift;; + -h) PGHOST=$2; shift;; + -p) PGPORT=$2; shift;; + *) NEWUSER=$1;; + esac + shift; +done + +AUTHOPT="-a $AUTHSYS" +[ -z "$AUTHSYS" ] && AUTHOPT="" + +MARGS="-TN $AUTHOPT -h $PGHOST -p $PGPORT" + +# +# generate the first part of the actual monitor command +# + +MONITOR="monitor $MARGS" + +# +# see if user $USER is allowed to create new users +# + +QUERY="select usesuper from pg_user where usename = '$USER'" +#echo $QUERY + +ADDUSER=`$MONITOR -TN -c "$QUERY" template1` + +if [ $? -ne 0 ] +then + echo "$CMDNAME: database access failed." 1>&2 + exit 1 +fi + +if [ -n "$ADDUSER" ] +then + +if [ $ADDUSER != "t" ] +then + echo "$CMDNAME: $USER cannot create users." 1>&2 + exit 1 +fi +fi + +# +# get the user name of the new user. Make sure it doesn't already exist. +# + +if [ -z "$NEWUSER" ] +then + echo _fUnKy_DASH_N_sTuFf_ "Enter name of user to add ---> "_fUnKy_BACKSLASH_C_sTuFf_ + read NEWUSER +fi + +QUERY="select usesysid from pg_user where usename = '$NEWUSER'" + +RES=`$MONITOR -TN -c "$QUERY" template1` + +if [ $? -ne 0 ] +then + echo "$CMDNAME: database access failed." 1>&2 + exit 1 +fi + +if [ -n "$RES" ] +then + echo "$CMDNAME: user "\"$NEWUSER\"" already exists" 1>&2 + exit 1 +fi + +done=0 + +# +# get the system id of the new user. Make sure it is unique. +# + +while [ $done -ne 1 ] +do + SYSID= + DEFSYSID=`pg_id $NEWUSER 2>/dev/null` + if [ $? -eq 0 ]; then + DEFMSG=" or RETURN to use unix user ID: $DEFSYSID" + else + DEFMSG= + DEFSYSID= + fi + while [ -z "$SYSID" ] + do + echo _fUnKy_DASH_N_sTuFf_ "Enter user's postgres ID$DEFMSG -> "_fUnKy_BACKSLASH_C_sTuFf_ + read SYSID + [ -z "$SYSID" ] && SYSID=$DEFSYSID; + SYSIDISNUM=`echo $SYSID | egrep '^[0-9]+$'` + if [ -z "$SYSIDISNUM" ] + then + echo "$CMDNAME: the postgres ID must be a number" + exit 1 + fi + QUERY="select usename from pg_user where usesysid = '$SYSID'::int4" + RES=`$MONITOR -TN -c "$QUERY" template1` + if [ $? -ne 0 ] + then + echo "$CMDNAME: database access failed." + exit 1 + fi + if [ -n "$RES" ] + then + echo + echo "$CMDNAME: $SYSID already belongs to $RES, pick another" + DEFMSG= DEFSYSID= SYSID= + else + done=1 + fi + done +done + +# +# get the rest of the user info... +# + +# +# can the user create databases? +# + +yn=f + +while [ "$yn" != y -a "$yn" != n ] +do + echo _fUnKy_DASH_N_sTuFf_ "Is user \"$NEWUSER\" allowed to create databases (y/n) "_fUnKy_BACKSLASH_C_sTuFf_ + read yn +done + +if [ "$yn" = y ] +then + CANCREATE=t +else + CANCREATE=f +fi + +# +# can the user add users? +# + +yn=f + +while [ "$yn" != y -a "$yn" != n ] +do + echo _fUnKy_DASH_N_sTuFf_ "Is user \"$NEWUSER\" allowed to add users? (y/n) "_fUnKy_BACKSLASH_C_sTuFf_ + read yn +done + +if (test "$yn" = y) +then + CANADDUSER=t +else + CANADDUSER=f +fi + +QUERY="insert into pg_user \ + (usename, usesysid, usecreatedb, usetrace, usesuper, usecatupd) \ + values \ + ('$NEWUSER', $SYSID, '$CANCREATE', 't', '$CANADDUSER','t')" + +RES=`$MONITOR -TN -c "$QUERY" template1` + +# +# Wrap things up. If the user was created successfully, AND the user was +# NOT allowed to create databases, remind the DBA to create one for the user. +# + +if [ $? -ne 0 ] +then + echo "$CMDNAME: $NEWUSER was NOT added successfully" +else + echo "$CMDNAME: $NEWUSER was successfully added" + if [ "$CANCREATE" = f ] + then + echo "don't forget to create a database for $NEWUSER" + fi +fi diff --git a/src/bin/destroydb/Makefile b/src/bin/destroydb/Makefile new file mode 100644 index 00000000000..4dadef6eb88 --- /dev/null +++ b/src/bin/destroydb/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/destroydb +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/destroydb/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SHPROG= destroydb + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +include $(MKDIR)/postgres.shell.mk + diff --git a/src/bin/destroydb/destroydb.sh b/src/bin/destroydb/destroydb.sh new file mode 100644 index 00000000000..564d503a1d3 --- /dev/null +++ b/src/bin/destroydb/destroydb.sh @@ -0,0 +1,69 @@ +#!/bin/sh +#------------------------------------------------------------------------- +# +# destroydb.sh-- +# destroy a postgres database +# +# this program runs the monitor with the ? option to destroy +# the requested database. +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/destroydb/Attic/destroydb.sh,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# ---------------- +# Set paths from environment or default values. +# The _fUnKy_..._sTuFf_ gets set when the script is installed +# from the default value for this build. +# Currently the only thing we look for from the environment is +# PGDATA, PGHOST, and PGPORT +# +# ---------------- +[ -z "$PGPORT" ] && PGPORT=5432 +[ -z "$PGHOST" ] && PGHOST=localhost +BINDIR=_fUnKy_BINDIR_sTuFf_ +PATH=$BINDIR:$PATH + +CMDNAME=`basename $0` + +if [ -z "$USER" ]; then + if [ -z "$LOGNAME" ]; then + if [ -z "`whoami`" ]; then + echo "$CMDNAME: cannot determine user name" + exit 1 + fi + else + USER=$LOGNAME + export USER + fi +fi + +dbname=$USER + +while [ -n "$1" ] +do + case $1 in + -a) AUTHSYS=$2; shift;; + -h) PGHOST=$2; shift;; + -p) PGPORT=$2; shift;; + *) dbname=$1;; + esac + shift; +done + +AUTHOPT="-a $AUTHSYS" +[ -z "$AUTHSYS" ] && AUTHOPT="" + +monitor -TN -h $PGHOST -p $PGPORT -c "drop database $dbname" template1 + +if [ $? -ne 0 ] +then + echo "$CMDNAME: database destroy failed on $dbname." + exit 1 +fi + +exit 0 diff --git a/src/bin/destroyuser/Makefile b/src/bin/destroyuser/Makefile new file mode 100644 index 00000000000..7e5d5014f77 --- /dev/null +++ b/src/bin/destroyuser/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/destroyuser +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/destroyuser/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SHPROG= destroyuser + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +include $(MKDIR)/postgres.shell.mk + diff --git a/src/bin/destroyuser/destroyuser.sh b/src/bin/destroyuser/destroyuser.sh new file mode 100644 index 00000000000..e600748f14f --- /dev/null +++ b/src/bin/destroyuser/destroyuser.sh @@ -0,0 +1,192 @@ +#!/bin/sh +#------------------------------------------------------------------------- +# +# destroyuser.sh-- +# utility for destroying a user from the POSTGRES database. +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/destroyuser/Attic/destroyuser.sh,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +# Note - this should NOT be setuid. +# +#------------------------------------------------------------------------- + +# ---------------- +# Set paths from environment or default values. +# The _fUnKy_..._sTuFf_ gets set when the script is installed +# from the default value for this build. +# Currently the only thing we look for from the environment is +# PGDATA, PGHOST, and PGPORT +# +# ---------------- +[ -z "$PGPORT" ] && PGPORT=5432 +[ -z "$PGHOST" ] && PGHOST=localhost +BINDIR=_fUnKy_BINDIR_sTuFf_ +PATH=$BINDIR:$PATH + +CMDNAME=`basename $0` + +if [ -z "$USER" ]; then + if [ -z "$LOGNAME" ]; then + if [ -z "`whoami`" ]; then + echo "$CMDNAME: cannot determine user name" + exit 1 + fi + else + USER=$LOGNAME + export USER + fi +fi + +while (test -n "$1") +do + case $1 in + -a) AUTHSYS=$2; shift;; + -h) PGHOST=$2; shift;; + -p) PGPORT=$2; shift;; + *) DELUSER=$1;; + esac + shift; +done + +AUTHOPT="-a $AUTHSYS" +[ -z "$AUTHSYS" ] && AUTHOPT="" + +MARGS="-TN $AUTHOPT -p $PGPORT -h $PGHOST" + +# +# generate the first part of the actual monitor command +# +MONITOR="monitor $MARGS" + +# +# see if user $USER is allowed to create new users. Only a user who can +# create users can delete them. +# + +QUERY="select usesuper from pg_user where usename = '$USER'" +ADDUSER=`$MONITOR -c "$QUERY" template1` + +if [ $? -ne 0 ] +then + echo "$CMDNAME: database access failed." + exit 1 +fi + +if [ $ADDUSER != "t" ] +then + echo "$CMDNAME: $USER cannot delete users." +fi + +# +# get the user name of the user to delete. Make sure it exists. +# + +if [ -z "$DELUSER" ] +then + echo _fUnKy_DASH_N_sTuFf_ "Enter name of user to delete ---> "_fUnKy_BACKSLASH_C_sTuFf_ + read DELUSER +fi + +QUERY="select usesysid from pg_user where usename = '$DELUSER'" + +RES=`$MONITOR -c "$QUERY" template1` + +if [ $? -ne 0 ] +then + echo "$CMDNAME: database access failed." + exit 1 +fi + +if [ ! -n "$RES" ] +then + echo "$CMDNAME: user "\"$DELUSER\"" does not exist." + exit 1 +fi + +SYSID=`echo $RES | sed 's/ //g'` + +# +# destroy the databases owned by the deleted user. First, use this query +# to find out what they are. +# + +QUERY="select datname from pg_database where datdba = '$SYSID'::oid" + + +ALLDBS=`$MONITOR -c "$QUERY" template1` + +if [ $? -ne 0 ] +then + echo "$CMDNAME: database access failed - exiting..." + exit 1 +fi + + +# +# don't try to delete template1! +# + +for i in $ALLDBS +do + if [ $i != "template1" ] + then + DBLIST="$DBLIST $i" + fi +done + +if [ -n "$DBLIST" ] +then + echo "User $DELUSER owned the following databases:" + echo $DBLIST + echo + +# +# Now we warn the DBA that deleting this user will destroy a bunch of databases +# + + yn=f + while [ $yn != y -a $yn != n ] + do + echo _fUnKy_DASH_N_sTuFf_ "Deleting user $DELUSER will destroy them. Continue (y/n)? "_fUnKy_BACKSLASH_C_sTuFf_ + read yn + done + + if [ $yn = n ] + then + echo "$CMDNAME: exiting" + exit 1 + fi + + # + # now actually destroy the databases + # + + for i in $DBLIST + do + echo "destroying database $i" + + QUERY="drop database $i" + $MONITOR -c "$QUERY" template1 + if [ $? -ne 0 ] + then + echo "$CMDNAME: drop database on $i failed - exiting" + exit 1 + fi + done +fi + +QUERY="delete from pg_user where usename = '$DELUSER'" + +$MONITOR -c "$QUERY" template1 +if [ $? -ne 0 ] +then + echo "$CMDNAME: delete of user $DELUSER was UNSUCCESSFUL" +else + echo "$CMDNAME: delete of user $DELUSER was successful." +fi + +exit 0 diff --git a/src/bin/initdb/Makefile b/src/bin/initdb/Makefile new file mode 100644 index 00000000000..a63236aaff2 --- /dev/null +++ b/src/bin/initdb/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/initdb +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/initdb/Makefile,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SHPROG= initdb + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +include $(MKDIR)/postgres.shell.mk + diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh new file mode 100644 index 00000000000..8e9044981f3 --- /dev/null +++ b/src/bin/initdb/initdb.sh @@ -0,0 +1,222 @@ +#!/bin/sh +#------------------------------------------------------------------------- +# +# initdb.sh-- +# create a postgres template database +# +# this program feeds the proper input to the ``postgres'' program +# to create a postgres database and register it in the +# shared ``pg_database'' database. +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# ---------------- +# Set paths from environment or default values. +# The _fUnKy_..._sTuFf_ gets set when the script is installed +# from the default value for this build. +# Currently the only thing wee look for from the environment is +# PGDATA, PGHOST, and PGPORT +# +# ---------------- +[ -z "$PGDATA" ] && { PGDATA=_fUnKy_DATADIR_sTuFf_; export PGDATA; } +[ -z "$PGPORT" ] && { PGPORT=5432; export PGPORT; } +[ -z "$PGHOST" ] && { PGHOST=localhost; export PGHOST; } +POSTGRESDIR=_fUnKy_POSTGRESDIR_sTuFf_ +BINDIR=_fUnKy_BINDIR_sTuFf_ +FILESDIR=$PGDATA/files +PATH=$BINDIR:$PATH +export PATH + +CMDNAME=`basename $0` + +# ---------------- +# check arguments: +# -d indicates debug mode. +# -n means don't clean up on error (so your cores don't go away) +# ---------------- +debug=0 +noclean=0 +verbose=0 + +for ARG +do + case "$ARG" in + -d) debug=1; echo "$CMDNAME: debug mode on";; + -n) noclean=1; echo "$CMDNAME: noclean mode on";; + -v) verbose=1; echo "$CMDNAME: verbose mode on";; + *) echo "initdb [-d][-n][-v]\n -d : debug mode\n -n : noclean mode, leaves temp files around \n -v : verbose mode"; exit 0; + esac +done + +# ---------------- +# if the debug flag is set, then +# ---------------- +if test "$debug" -eq 1 +then + BACKENDARGS="-boot -C -d" +else + BACKENDARGS="-boot -C -Q" +fi + + +TEMPLATE=$FILESDIR/local1_template1.bki +GLOBAL=$FILESDIR/global1.bki +if [ ! -f $TEMPLATE -o ! -f $GLOBAL ] +then + echo "$CMDNAME: error: database initialization files not found." + echo "$CMDNAME: either gmake install has not been run or you're trying to" + echo "$CMDNAME: run this program on a machine that does not store the" + echo "$CMDNAME: database (PGHOST doesn't work for this)." + exit 1 +fi + +if test "$verbose" -eq 1 +then + echo "$CMDNAME: using $TEMPLATE" + echo "$CMDNAME: using $GLOBAL" +fi + +# +# Figure out who I am... +# + +PG_UID=`pg_id` + +if test $PG_UID -eq 0 +then + echo "$CMDNAME: do not install POSTGRES as root" + exit 1 +fi + +# ---------------- +# create the template database if necessary +# the first we do is create data/base, so we'll check for that. +# ---------------- + +if test -d "$PGDATA/base" +then + echo "$CMDNAME: error: it looks like initdb has already been run. You must" + echo "clean out the database directory first with the cleardbdir program" + exit 1 +fi + +# umask must disallow access to group, other for files and dirs +umask 077 + +mkdir $PGDATA/base $PGDATA/base/template1 + +if test "$verbose" -eq 1 +then + echo "$CMDNAME: creating SHARED relations in $PGDATA" + echo "$CMDNAME: creating template database in $PGDATA/base/template1" + echo "postgres $BACKENDARGS template1 < $TEMPLATE " +fi + +postgres $BACKENDARGS template1 < $TEMPLATE + + +if test $? -ne 0 +then + echo "$CMDNAME: could not create template database" + if test $noclean -eq 0 + then + echo "$CMDNAME: cleaning up." + cd $PGDATA + for i in * + do + if [ $i != "files" -a $i != "pg_hba" ] + then + /bin/rm -rf $i + fi + done + else + echo "$CMDNAME: cleanup not done (noclean mode set)." + fi + exit 1; +fi + +pg_version $PGDATA/base/template1 + +# +# Add the template database to pg_database +# + +echo "open pg_database" > /tmp/create.$$ +echo "insert (template1 $PG_UID template1)" >> /tmp/create.$$ +#echo "show" >> /tmp/create.$$ +echo "close pg_database" >> /tmp/create.$$ + +if test "$verbose" -eq 1 +then + echo "postgres $BACKENDARGS template1 < $GLOBAL" +fi + +postgres $BACKENDARGS template1 < $GLOBAL + +if (test $? -ne 0) +then + echo "$CMDNAME: could create shared relations" + if (test $noclean -eq 0) + then + echo "$CMDNAME: cleaning up." + cd $PGDATA + for i in * + do + if [ $i != "files" ] + then + /bin/rm -rf $i + fi + done + else + echo "$CMDNAME: cleanup not done (noclean mode set)." + fi + exit 1; +fi + +pg_version $PGDATA + +if test "$verbose" -eq 1 +then + echo "postgres $BACKENDARGS template1 < /tmp/create.$$" +fi + +postgres $BACKENDARGS template1 < /tmp/create.$$ + +if test $? -ne 0 +then + echo "$CMDNAME: could not log template database" + if (test $noclean -eq 0) + then + echo "$CMDNAME: cleaning up." + cd $PGDATA + for i in * + do + if [ $i != "files" ] + then + /bin/rm -rf $i + fi + done + else + echo "$CMDNAME: cleanup not done (noclean mode set)." + fi + exit 1; +fi + +if test $debug -eq 0 +then + +if test "$verbose" -eq 1 +then + echo "vacuuming template1" +fi + + echo "vacuum" | postgres -Q template1 > /dev/null +fi + +rm -f /tmp/create.$$ diff --git a/src/bin/ipcclean/Makefile b/src/bin/ipcclean/Makefile new file mode 100644 index 00000000000..5f7993b9ba4 --- /dev/null +++ b/src/bin/ipcclean/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/initdb +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/ipcclean/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SHPROG= ipcclean + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +include $(MKDIR)/postgres.shell.mk + diff --git a/src/bin/ipcclean/ipcclean.sh b/src/bin/ipcclean/ipcclean.sh new file mode 100644 index 00000000000..d3ea2fc576c --- /dev/null +++ b/src/bin/ipcclean/ipcclean.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# +# $Header: /cvsroot/pgsql/src/bin/ipcclean/Attic/ipcclean.sh,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $ +# +PATH=_fUnKy_IPCCLEANPATH_sTuFf_:$PATH +export PATH +ipcs | egrep '^m .*|^s .*' | egrep "`whoami`|postgres" | \ +awk '{printf "ipcrm -%s %s\n", $1, $2}' '-' | sh diff --git a/src/bin/monitor/Makefile b/src/bin/monitor/Makefile new file mode 100644 index 00000000000..53e6f0d514d --- /dev/null +++ b/src/bin/monitor/Makefile @@ -0,0 +1,23 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/monitor +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/monitor/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $ +# +#------------------------------------------------------------------------- + +PROG= monitor + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +SRCS= monitor.c + +include $(MKDIR)/postgres.prog.mk + diff --git a/src/bin/monitor/monitor.c b/src/bin/monitor/monitor.c new file mode 100644 index 00000000000..f9bfa92237b --- /dev/null +++ b/src/bin/monitor/monitor.c @@ -0,0 +1,1058 @@ +/*------------------------------------------------------------------------- + * + * monitor.c-- + * POSTGRES Terminal Monitor + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/bin/monitor/Attic/monitor.c,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <errno.h> +#include "libpq/pqsignal.h" /* substitute for <signal.h> */ +#include <stdio.h> +#include <string.h> +#include <sys/file.h> +#include <sys/param.h> /* for MAXHOSTNAMELEN on most */ +#ifndef WIN32 +#include <unistd.h> +#endif +#ifdef PORTNAME_sparc_solaris +#include <netdb.h> /* for MAXHOSTNAMELEN on some */ +#endif +#include <sys/types.h> +/* #include <sys/uio.h> */ +#include <time.h> + +#include "libpq-fe.h" +#include "libpq/libpq-fs.h" + +extern char *getenv(); + +/* + * monitor.c -- function prototypes (all private) + */ +static void do_input(FILE *ifp); +static void init_tmon(); +static void welcome(); +static void handle_editor(); +static void handle_shell(); +static void handle_send(); +static int handle_execution(char *query); +static void handle_file_insert(FILE *ifp); +static void handle_print(); +static void handle_exit(int exit_status); +static void handle_clear(); +static void handle_print_time(); +static int handle_write_to_file(); +static void handle_help(); +static void stuff_buffer(char c); +static void argsetup(int *argcP, char ***argvP); +static void handle_copy_out(PGresult *res); +static void handle_copy_in(PGresult *res); + + +/* + * Functions which maintain the logical query buffer in + * /tmp/PQxxxxx. It in general just does a copy from input + * to query buffer, unless it gets a backslash escape character. + * It recognizes the following escapes: + * + * \e -- enter editor + * \g -- "GO": submit query to POSTGRES + * \i -- include (switch input to external file) + * \p -- print query buffer + * \q -- quit POSTGRES + * \r -- force reset (clear) of query buffer + * \s -- call shell + * \t -- print current time + * \w -- write query buffer to external file + * \h -- print the list of commands + * \? -- print the list of commands + * \\ -- produce a single backslash in query buffer + * + */ + +/* + * Declaration of global variables (but only to the file monitor.c + */ + +#define DEFAULT_EDITOR "/usr/ucb/vi" +#define COPYBUFSIZ 8192 +static char *user_editor; /* user's desired editor */ +static int tmon_temp; /* file descriptor for temp. buffer file */ +static char *tmon_temp_filename; +static char query_buffer[8192]; /* Max postgres buffer size */ +static char *RunOneFile = NULL; +bool RunOneCommand = false; +bool Debugging; +bool Verbose; +bool Silent; +bool TerseOutput = false; +bool PrintAttNames = true; +bool SingleStepMode = false; +bool SemicolonIsGo = true; + +#define COLWIDTH 12 + +extern char *optarg; +extern int optind,opterr; +FILE *debug_port; + +/* + * As of release 4, we allow the user to specify options in the environment + * variable PGOPTION. These are treated as command-line options to the + * terminal monitor, and are parsed before the actual command-line args. + * The arge struct is used to construct an argv we can pass to getopt() + * containing the union of the environment and command line arguments. + */ + +typedef struct arge { + char *a_arg; + struct arge *a_next; +} arge; + +/* the connection to the backend */ +PGconn *conn; + +void +main(int argc, char **argv) +{ + int c; + int errflag = 0; + char *progname; + char *debug_file; + char *dbname; + char *command; + int exit_status = 0; + char errbuf[ERROR_MSG_LENGTH]; + char *username, usernamebuf[NAMEDATALEN + 1]; + + char *pghost = NULL; + char *pgtty = NULL; + char *pgoptions = NULL; + char *pgport = NULL; + int pgtracep = 0; + + /* + * Processing command line arguments. + * + * h : sets the hostname. + * p : sets the coom. port + * t : sets the tty. + * o : sets the other options. (see doc/libpq) + * d : enable debugging mode. + * q : run in quiet mode + * Q : run in VERY quiet mode (no output except on errors) + * c : monitor will run one POSTQUEL command and exit + * + * s : step mode (pauses after each command) + * S : don't use semi colon as \g + * + * T : terse mode - no formatting + * N : no attribute names - only columns of data + * (these two options are useful in conjunction with the "-c" option + * in scripts.) + */ + + progname = *argv; + Debugging = false; + Verbose = true; + Silent = false; + + /* prepend PGOPTION, if any */ + argsetup(&argc, &argv); + + while ((c = getopt(argc, argv, "a:h:f:p:t:d:qsSTNQc:")) != EOF) { + switch (c) { + case 'a': + fe_setauthsvc(optarg, errbuf); + break; + case 'h' : + pghost = optarg; + break; + case 'f' : + RunOneFile = optarg; + break; + case 'p' : + pgport = optarg; + break; + case 't' : + pgtty = optarg; + break; + case 'T' : + TerseOutput = true; + break; + case 'N' : + PrintAttNames = false; + break; + case 'd' : + + /* + * When debugging is turned on, the debugging messages + * will be sent to the specified debug file, which + * can be a tty .. + */ + + Debugging = true; + debug_file = optarg; + debug_port = fopen(debug_file,"w+"); + if (debug_port == NULL) { + fprintf(stderr,"Unable to open debug file %s \n", debug_file); + exit(1); + } + pgtracep = 1; + break; + case 'q' : + Verbose = false; + break; + case 's' : + SingleStepMode = true; + SemicolonIsGo = true; + break; + case 'S' : + SemicolonIsGo = false; + break; + case 'Q' : + Verbose = false; + Silent = true; + break; + case 'c' : + Verbose = false; + Silent = true; + RunOneCommand = true; + command = optarg; + break; + case '?' : + default : + errflag++; + break; + } + } + + if (errflag ) { + fprintf(stderr, "usage: %s [options...] [dbname]\n", progname); + fprintf(stderr, "\t-a authsvc\tset authentication service\n"); + fprintf(stderr, "\t-c command\t\texecute one command\n"); + fprintf(stderr, "\t-d debugfile\t\tdebugging output file\n"); + fprintf(stderr, "\t-h host\t\t\tserver host name\n"); + fprintf(stderr, "\t-f file\t\t\trun query from file\n"); + fprintf(stderr, "\t-p port\t\t\tserver port number\n"); + fprintf(stderr, "\t-q\t\t\tquiet output\n"); + fprintf(stderr, "\t-t logfile\t\terror-logging tty\n"); + fprintf(stderr, "\t-N\t\t\toutput without attribute names\n"); + fprintf(stderr, "\t-Q\t\t\tREALLY quiet output\n"); + fprintf(stderr, "\t-T\t\t\tterse output\n"); + exit(2); + } + + /* Determine our username (according to the authentication system, if + * there is one). + */ + if ((username = fe_getauthname(errbuf)) == (char *) NULL) { + fprintf(stderr, "%s: could not find a valid user name\n", + progname); + exit(2); + } + memset(usernamebuf, 0, sizeof(usernamebuf)); + (void) strncpy(usernamebuf, username, NAMEDATALEN); + username = usernamebuf; + + /* find database */ + if (!(dbname = argv[optind]) && + !(dbname = getenv("DATABASE")) && + !(dbname = username)) { + fprintf(stderr, "%s: no database name specified\n", progname); + exit (2); + } + + conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbname); + if (PQstatus(conn) == CONNECTION_BAD) { + fprintf(stderr,"Connection to database '%s' failed.\n", dbname); + fprintf(stderr,"%s",PQerrorMessage(conn)); + exit(1); + } + + if (pgtracep) + PQtrace(conn,debug_port); + + /* print out welcome message and start up */ + welcome(); + init_tmon(); + + /* parse input */ + if (RunOneCommand) { + exit_status = handle_execution(command); + } else if (RunOneFile) { + bool oldVerbose; + FILE *ifp; + + if ((ifp = fopen(RunOneFile, "r")) == NULL) { + fprintf(stderr, "Cannot open %s\n", RunOneFile); + } + + if (SingleStepMode) { + oldVerbose = Verbose; + Verbose = false; + } + do_input(ifp); + fclose(ifp); + if (SingleStepMode) + Verbose = oldVerbose; + } else { + do_input(stdin); + } + + handle_exit(exit_status); +} + +static void +do_input(FILE *ifp) +{ + int c; + char escape; + + /* + * Processing user input. + * Basically we stuff the user input to a temp. file until + * an escape char. is detected, after which we switch + * to the appropriate routine to handle the escape. + */ + + if (ifp == stdin) { + if (Verbose) + fprintf(stdout,"\nGo \n* "); + else { + if (!Silent) + fprintf(stdout, "* "); + } + } + while ((c = getc(ifp)) != EOF ) { + if ( c == '\\') { + /* handle escapes */ + escape = getc(ifp); + switch( escape ) { + case 'e': + handle_editor(); + break; + case 'g': + handle_send(); + break; + case 'i': + { + bool oldVerbose; + + if (SingleStepMode) { + oldVerbose = Verbose; + Verbose = false; + } + handle_file_insert(ifp); + if (SingleStepMode) + Verbose = oldVerbose; + } + break; + case 'p': + handle_print(); + break; + case 'q': + handle_exit(0); + break; + case 'r': + handle_clear(); + break; + case 's': + handle_shell(); + break; + case 't': + handle_print_time(); + break; + case 'w': + handle_write_to_file(); + break; + case '?': + case 'h': + handle_help(); + break; + case '\\': + c = escape; + stuff_buffer(c); + break; + case ';': + c = escape; + stuff_buffer(c); + break; + default: + fprintf(stderr, "unknown escape given\n"); + break; + } /* end-of-switch */ + if (ifp == stdin && escape != '\\') { + if (Verbose) + fprintf(stdout,"\nGo \n* "); + else { + if (!Silent) + fprintf(stdout, "* "); + } + } + } else { + stuff_buffer(c); + if (c == ';' && SemicolonIsGo) { + handle_send(); + if (Verbose) + fprintf(stdout,"\nGo \n* "); + else { + if (!Silent) + fprintf(stdout, "* "); + } + } + } + } +} + +/* + * init_tmon() + * + * set the following : + * user_editor, defaults to DEFAULT_EDITOR if env var is not set + */ +static void +init_tmon() +{ + if (!RunOneCommand) + { + char *temp_editor = getenv("EDITOR"); + + if (temp_editor != NULL) + user_editor = temp_editor; + else + user_editor = DEFAULT_EDITOR; + + tmon_temp_filename = malloc(20); + sprintf(tmon_temp_filename, "/tmp/PQ%d", getpid()); + tmon_temp = open(tmon_temp_filename,O_CREAT | O_RDWR | O_APPEND,0666); + } + + /* + * Catch signals so we can delete the scratch file GK + * but only if we aren't already ignoring them -mer + */ + +#ifndef WIN32 + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + signal(SIGHUP, handle_exit); + if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) + signal(SIGQUIT, handle_exit); +#endif /* WIN32 we'll have to figure out how to handle these */ + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, handle_exit); + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, handle_exit); +} + +/* + * welcome simply prints the Postgres welcome mesg. + */ +static +void welcome() +{ + if (Verbose) { + fprintf(stdout,"Welcome to the POSTGRES95 terminal monitor\n"); + fprintf(stdout," Please read the file COPYRIGHT for copyright terms of POSTGRES95\n"); + } +} + + +/* + * handle_editor() + * + * puts the user into edit mode using the editor specified + * by the variable "user_editor". + */ +static void +handle_editor() +{ + char edit_line[100]; + + close(tmon_temp); + sprintf(edit_line,"%s %s",user_editor,tmon_temp_filename); + system(edit_line); + tmon_temp = open(tmon_temp_filename,O_CREAT | O_RDWR | O_APPEND,0666); +} + +static void +handle_shell() +{ + char *user_shell; + + user_shell = getenv("SHELL"); + if (user_shell != NULL) { + system(user_shell); + } else { + system("/bin/sh"); + } +} + +/* + * handle_send() + * + * This is the routine that initialises the comm. with the + * backend. After the tuples have been returned and + * displayed, the query_buffer is cleared for the + * next query. + * + */ + +#include <ctype.h> + +static void +handle_send() +{ + char c = (char)0; + off_t pos; + int cc = 0; + int i = 0; + + pos = lseek(tmon_temp, (off_t) 0, SEEK_SET); + + if (pos != 0) + fprintf(stderr, "Bogus file position\n"); + + if (Verbose) + printf("\n"); + + /* discard leading white space */ + while ( ( cc = read(tmon_temp,&c,1) ) != 0 && isspace((int)c)) + continue; + + if ( cc != 0 ) { + pos = lseek(tmon_temp, (off_t) -1, SEEK_CUR); + } + + if (SingleStepMode) { + char buf[1024]; + fprintf(stdout, "\n*******************************************************************************\n"); + while ((cc = read(tmon_temp,buf,1024))>0) { + buf[cc] = '\0'; + fprintf(stdout, "%s", buf); + } + fprintf(stdout, "\n*******************************************************************************\n\n"); + (void)lseek(tmon_temp, (off_t)pos, SEEK_SET); + } + + query_buffer[0] = 0; + + /* + * Stripping out comments (if any) from the query (should really be + * handled in the parser, of course). + */ + while ( ( cc = read(tmon_temp,&c,1) ) != 0) { + switch(c) { + case '\n': + query_buffer[i++] = ' '; + break; + case '-': { + int temp; + char temp_c; + if ((temp = read(tmon_temp,&temp_c,1)) > 0) { + if (temp_c == '-' ) { + /* read till end of line */ + while ((temp = read(tmon_temp,&temp_c,1)) != 0) { + if (temp_c=='\n') + break; + } + }else { + query_buffer[i++] = c; + query_buffer[i++] = temp_c; + } + } else { + query_buffer[i++] = c; + } + break; + } + case '$': { + int temp; + char temp_c[4]; + /* + * monitor feature, not POSTGRES SQL. When monitor sees $PWD, + * it will substitute in the current directory. + */ + if ((temp = read(tmon_temp,temp_c,3)) > 0) { + temp_c[temp] = '\0'; + if (!strncmp(temp_c, "PWD", 3)) { + int len; + char cwdPath[MAXPATHLEN]; + if (getcwd(cwdPath, MAXPATHLEN)==NULL) { + fprintf(stderr, + "cannot get current working directory\n"); + break; + } + len = strlen(cwdPath); + query_buffer[i] = '\0'; + strcat(query_buffer, cwdPath); + i += len; + } else { + int j; + query_buffer[i++] = c; + for(j = 0; j < temp; j++) { + query_buffer[i++] = temp_c[j]; + } + } + } else { + query_buffer[i++] = c; + } + break; + } + default: + query_buffer[i++] = c; + break; + } + } + + if (query_buffer[0] == 0) { + query_buffer[0] = ' '; + query_buffer[1] = 0; + } + + if (Verbose && !SingleStepMode) + fprintf(stdout,"Query sent to backend is \"%s\"\n", query_buffer); + + fflush(stderr); + fflush(stdout); + + /* + * Repeat commands until done. + */ + + handle_execution(query_buffer); + + /* clear the query buffer and temp file -- this is very expensive */ + handle_clear(); + memset(query_buffer,0,i); +} + +/* + * Actually execute the query in *query. + * + * Returns 0 if the query finished successfully, 1 otherwise. + */ +static int +handle_execution(char *query) +{ + PGresult *result; + int retval = 0; + + result = PQexec(conn, query); + + if (result == NULL) { + fprintf(stderr,"%s", PQerrorMessage(conn)); + return 1; + } + + switch (PQresultStatus(result)) { + case PGRES_EMPTY_QUERY: + break; + case PGRES_COMMAND_OK: + break; + case PGRES_TUPLES_OK: +/* PQprintTuples(result,stdout,PrintAttNames,TerseOutput,COLWIDTH); */ + if (TerseOutput) + PQdisplayTuples(result,stdout,1,"",PrintAttNames,TerseOutput); + else + PQdisplayTuples(result,stdout,1,"|",PrintAttNames,TerseOutput); + break; + case PGRES_COPY_OUT: + handle_copy_out(result); + break; + case PGRES_COPY_IN: + handle_copy_in(result); + break; + case PGRES_BAD_RESPONSE: + retval = 1; + break; + case PGRES_NONFATAL_ERROR: + retval = 1; + break; + case PGRES_FATAL_ERROR: + retval = 1; + break; + } + + if (SingleStepMode) { + fflush(stdin); + printf("\npress return to continue ...\n"); + getc(stdin); /* assume stdin is not a file! */ + } + return(retval); +} + +/* + * handle_file_insert() + * + * allows the user to insert a query file and execute it. + * NOTE: right now the full path name must be specified. + */ +static void +handle_file_insert(FILE *ifp) +{ + char user_filename[50]; + FILE *nifp; + + fscanf(ifp, "%s",user_filename); + nifp = fopen(user_filename, "r"); + if (nifp == (FILE *) NULL) { + fprintf(stderr, "Cannot open %s\n", user_filename); + } else { + do_input(nifp); + fclose (nifp); + } +} + +/* + * handle_print() + * + * This routine prints out the contents (query) of the temp. file + * onto stdout. + */ +static void +handle_print() +{ + char c; + off_t pos; + int cc; + + pos = lseek(tmon_temp, (off_t) 0, SEEK_SET); + + if (pos != 0 ) + fprintf(stderr, "Bogus file position\n"); + + printf("\n"); + + while ( ( cc = read(tmon_temp,&c,1) ) != 0) + putchar(c); + + printf("\n"); +} + + +/* + * handle_exit() + * + * ends the comm. with the backend and exit the tm. + */ +static void +handle_exit(int exit_status) +{ + if (!RunOneCommand) { + close(tmon_temp); + unlink(tmon_temp_filename); + } + PQfinish(conn); + exit(exit_status); +} + +/* + * handle_clear() + * + * This routine clears the temp. file. + */ +static void +handle_clear() +{ + /* high cost */ + close(tmon_temp); + tmon_temp = open(tmon_temp_filename,O_TRUNC|O_RDWR|O_CREAT ,0666); +} + +/* + * handle_print_time() + * prints out the date using the "date" command. + */ +static void +handle_print_time() +{ + system("date"); +} + +/* + * handle_write_to_file() + * + * writes the contents of the temp. file to the + * specified file. + */ +static int +handle_write_to_file() +{ + char filename[50]; + static char command_line[512]; + int status; + + status = scanf("%s", filename); + if (status < 1 || !filename[0]) { + fprintf(stderr, "error: filename is empty\n"); + return(-1); + } + + /* XXX portable way to check return status? $%&! ultrix ... */ + (void) sprintf(command_line, "rm -f %s", filename); + (void) system(command_line); + (void) sprintf(command_line, "cp %s %s", tmon_temp_filename, filename); + (void) system(command_line); + + return(0); +} + +/* + * + * Prints out a help message. + * + */ +static void +handle_help() +{ + printf("Available commands include \n\n"); + printf("\\e -- enter editor\n"); + printf("\\g -- \"GO\": submit query to POSTGRES\n"); + printf("\\i -- include (switch input to external file)\n"); + printf("\\p -- print query buffer\n"); + printf("\\q -- quit POSTGRES\n"); + printf("\\r -- force reset (clear) of query buffer\n"); + printf("\\s -- shell escape \n"); + printf("\\t -- print current time\n"); + printf("\\w -- write query buffer to external file\n"); + printf("\\h -- print the list of commands\n"); + printf("\\? -- print the list of commands\n"); + printf("\\\\ -- produce a single backslash in query buffer\n"); + fflush(stdin); +} + +/* + * stuff_buffer() + * + * writes the user input into the temp. file. + */ +static void +stuff_buffer(char c) +{ + int cc; + + cc = write(tmon_temp,&c,1); + + if(cc == -1) + fprintf(stderr, "error writing to temp file\n"); +} + +static void +argsetup(int *argcP, char ***argvP) +{ + int argc; + char **argv, **curarg; + char *eopts; + char *envopts; + int neopts; + char *start, *end; + arge *head, *tail, *cur; + + /* if no options specified in environment, we're done */ + if ((envopts = getenv("PGOPTION")) == (char *) NULL) + return; + + if ((eopts = (char *) malloc(strlen(envopts) + 1)) == (char *) NULL) { + fprintf(stderr, "cannot malloc copy space for PGOPTION\n"); + fflush(stderr); + exit (2); + } + + (void) strcpy(eopts, envopts); + + /* + * okay, we have PGOPTION from the environment, and we want to treat + * them as user-specified options. to do this, we construct a new + * argv that has argv[0] followed by the arguments from the environment + * followed by the arguments on the command line. + */ + + head = cur = (arge *) NULL; + neopts = 0; + + for (;;) { + while (isspace(*eopts) && *eopts) + eopts++; + + if (*eopts == '\0') + break; + + if ((cur = (arge *) malloc(sizeof(arge))) == (arge *) NULL) { + fprintf(stderr, "cannot malloc space for arge\n"); + fflush(stderr); + exit (2); + } + + end = start = eopts; + + if (*start == '"') { + start++; + while (*++end != '\0' && *end != '"') + continue; + if (*end == '\0') { + fprintf(stderr, "unterminated string constant in env var PGOPTION\n"); + fflush(stderr); + exit (2); + } + eopts = end + 1; + } else if (*start == '\'') { + start++; + while (*++end != '\0' && *end != '\'') + continue; + if (*end == '\0') { + fprintf(stderr, "unterminated string constant in env var PGOPTION\n"); + fflush(stderr); + exit (2); + } + eopts = end + 1; + } else { + while (!isspace(*end) && *end) + end++; + if (isspace(*end)) + eopts = end + 1; + else + eopts = end; + } + + if (head == (arge *) NULL) { + head = tail = cur; + } else { + tail->a_next = cur; + tail = cur; + } + + cur->a_arg = start; + cur->a_next = (arge *) NULL; + + *end = '\0'; + neopts++; + } + + argc = *argcP + neopts; + + if ((argv = (char **) malloc(argc * sizeof(char *))) == (char **) NULL) { + fprintf(stderr, "can't malloc space for modified argv\n"); + fflush(stderr); + exit (2); + } + + curarg = argv; + *curarg++ = *(*argvP)++; + + /* copy env args */ + while (head != (arge *) NULL) { + cur = head; + *curarg++ = head->a_arg; + head = head->a_next; + free(cur); + } + + /* copy rest of args from command line */ + while (--(*argcP)) + *curarg++ = *(*argvP)++; + + /* all done */ + *argvP = argv; + *argcP = argc; +} + +static void +handle_copy_out(PGresult *res) +{ + bool copydone = false; + char copybuf[COPYBUFSIZ]; + int ret; + + if (!Silent) + fprintf(stdout, "Copy command returns...\n"); + + while (!copydone) { + ret = PQgetline(res->conn, copybuf, COPYBUFSIZ); + + if (copybuf[0] == '.' && copybuf[1] =='\0') { + copydone = true; /* don't print this... */ + } else { + fputs(copybuf, stdout); + switch (ret) { + case EOF: + copydone = true; + /*FALLTHROUGH*/ + case 0: + fputc('\n', stdout); + break; + case 1: + break; + } + } + } + fflush(stdout); + PQendcopy(res->conn); +} + +static void +handle_copy_in(PGresult *res) +{ + bool copydone = false; + bool firstload; + bool linedone; + char copybuf[COPYBUFSIZ]; + char *s; + int buflen; + int c; + + if (!Silent) { + fputs("Enter info followed by a newline\n", stdout); + fputs("End with a dot on a line by itself.\n", stdout); + } + + /* + * eat inevitable newline still in input buffer + * + * XXX the 'inevitable newline' is not always present + * for example `cat file | monitor -c "copy from stdin"' + */ + fflush(stdin); + if ((c = getc(stdin)) != '\n' && c != EOF) { + (void) ungetc(c, stdin); + } + + while (!copydone) { /* for each input line ... */ + if (!Silent) { + fputs(">> ", stdout); + fflush(stdout); + } + firstload = true; + linedone = false; + while (!linedone) { /* for each buffer ... */ + s = copybuf; + buflen = COPYBUFSIZ; + for (; buflen > 1 && + !(linedone = (c = getc(stdin)) == '\n' || c == EOF); + --buflen) { + *s++ = c; + } + if (c == EOF) { + /* reading from stdin, but from a file */ + PQputline(res->conn, "."); + copydone = true; + break; + } + *s = '\0'; + PQputline(res->conn, copybuf); + if (firstload) { + if (!strcmp(copybuf, ".")) { + copydone = true; + } + firstload = false; + } + } + PQputline(res->conn, "\n"); + } + PQendcopy(res->conn); +} diff --git a/src/bin/pg4_dump/Makefile b/src/bin/pg4_dump/Makefile new file mode 100644 index 00000000000..286f96ea299 --- /dev/null +++ b/src/bin/pg4_dump/Makefile @@ -0,0 +1,13 @@ +# +# /usr/local/devel/pglite/cvs/src/bin/pg_dump/Makefile.v4r2,v 1.1 1995/05/17 18:57:10 jolly Exp +# + +.include <postgres.global.mk> + +CFLAGS+= -I${.CURDIR}/../../backend/tmp + +PROG= pg4_dump + +SRCS= pg4_dump.c common.c + +.include <postgres.prog.mk> diff --git a/src/bin/pg4_dump/README b/src/bin/pg4_dump/README new file mode 100644 index 00000000000..4ac54517096 --- /dev/null +++ b/src/bin/pg4_dump/README @@ -0,0 +1,87 @@ +pg4_dump is a utility for dumping out a postgres (version 4, release 2) +database into a script file containing query commands. The script +files are in a ASCII format and can be used to reconstruct the +database, even on other machines and other architectures. pg_dump +will produce the queries necessary to re-generate all user-defined +types, functions, tables, indices, aggregates, and operators. In +addition, all the data is copied out in ASCII format so that it can be +readily copied in again. + +The sources in this directory can be used to build two different +versions of the program. The two versions require different +versions of libpq, and the same binary cannot serve both purposes. + + + To build: + + % bmake clean install + + This version of the program will read in your postgres v4r2 +database and output the schema and the data tuples in one of two +formats: POSTQUEL or SQL. The POSTQUEL->POSTQUEL dumps are useful +for moving from one v4r2 installation to another. The POSTQUEL->SQL +dumps are useful for migrating from v4r2 to postgres95. + +Use the -o [SQL|POSTQUEL] option to specify output query language. + + +How to use pg4_dump: +------------------- + +The command line options are fairly self explanatory. Use -help to +see the command line options. I recommend using -v to get more +verbose descriptions of what pg_dump is doing. + +After running pg4_dump, one should examine the output script file for any +warnings, especially in light of the limitations listed below. + +A typical use of pg4_dump: + + % pg4_dump -v -f oldDB.dump oldDB + % createdb newDB + % monitor newDB < oldDB.dump + + +Caveats and limitations: +------------------------ + +pg4_dump has a few limitations. The limitations mostly stem from +difficulty in extracting certain meta-information from the system +catalogs. + + rules and views: + pg4_dump does not understand user-defined rules and views and + will fail to dump them properly. (This is due to the fact that + rules are stored as plans in the catalogs and not textually) + + partial indices: + pg4_dump does not understand partial indices. (The reason is + the same as above. Partial index predicates are stored as plans) + + source text of POSTQUEL functions: + pg4_dump does not convert the source text of a user-defined + POSTQUEL function into SQL. Manual intervention is required. + + large objects: + pg4_dump does not handle large objects. Inversion large + objects are ignored and must be dealt with manually. + + oid preservation: + pg4_dump does not preserve oid's while dumping. If you have + stored oid's explicitly in tables in user-defined attributes, + and are using them as keys, then the output scripts will not + regenerate your database correctly. + +pg4_dump has not been tested and will probably not work properly for +versions of postgres prior to 4.2. + +Bug-reporting +-------------- + +If you should find a problem with pg4_dump, it is very important that +you provide a (small) sample database which illustrates the problem. +Please send bugs, questions, and feedback to the + postgres95@postgres.berkeley.edu + + + diff --git a/src/bin/pg4_dump/common.c b/src/bin/pg4_dump/common.c new file mode 100644 index 00000000000..f485c152c67 --- /dev/null +++ b/src/bin/pg4_dump/common.c @@ -0,0 +1,417 @@ +/*------------------------------------------------------------------------- + * + * common.c-- + * common routines between pg_dump and pg4_dump + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * /usr/local/devel/pglite/cvs/src/bin/pg_dump/common.c,v 1.5 1995/06/28 22:32:35 jolly Exp + * + *------------------------------------------------------------------------- + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <sys/param.h> /* for MAXHOSTNAMELEN on most */ +#ifdef PORTNAME_sparc_solaris +#include <netdb.h> /* for MAXHOSTNAMELEN on some */ +#endif + +#include "postgres.h" +#include "libpq-fe.h" +#include "libpq/auth.h" + +#include "pg_dump.h" + +/* + * check_conn_and_db checks the connection and the database + */ +void +check_conn_and_db() +{ + char *string= PQexec(" "); + switch(*string) { + case 'E': + case 'R': + PQfinish(); + exit(2); + break; + } +} + + +/* dupstr : copies a string, while allocating space for it. + the CALLER is responsible for freeing the space + returns NULL if the argument is NULL*/ +char* +dupstr(char *s) +{ + char* result; + + if (s == NULL) + return NULL; + + result = (char*)malloc(strlen(s)+1); + strcpy(result, s); + return result; +} + + +/* + * findTypeByOid + * given an oid of a type, return its typename + * + * if oid is "0", return "opaque" -- this is a special case + * + * NOTE: should hash this, but just do linear search for now + */ + +char* +findTypeByOid(TypeInfo* tinfo, int numTypes, char* oid) +{ + int i; + + if (strcmp(oid, "0") == 0) return g_opaque_type; + + for (i=0;i<numTypes;i++) { + if (strcmp(tinfo[i].oid, oid) == 0) + return tinfo[i].typname; + } + + /* should never get here */ + fprintf(stderr,"failed sanity check, type with oid %s was not found\n", + oid); + exit(2); +} + +/* + * findOprByOid + * given the oid of an operator, return the name of the operator + * + * + * NOTE: should hash this, but just do linear search for now + * + */ +char* +findOprByOid(OprInfo *oprinfo, int numOprs, char *oid) +{ + int i; + for (i=0;i<numOprs;i++) { + if (strcmp(oprinfo[i].oid, oid) == 0) + return oprinfo[i].oprname; + } + + /* should never get here */ + fprintf(stderr,"failed sanity check, opr with oid %s was not found\n", + oid); + exit(2); +} + + +/* + * findParentsByOid -- + * given the oid of a class, return the names of its parent classes + * and assign the number of parents to the last argument. + * + * + * returns NULL if none + */ + +char** +findParentsByOid(TableInfo* tblinfo, int numTables, + InhInfo* inhinfo, int numInherits, char *oid, + int *numParentsPtr) +{ + int i,j; + int parentInd; + char** result; + int numParents; + + numParents = 0; + for (i=0;i<numInherits;i++) { + if ( strcmp(inhinfo[i].inhrel, oid) == 0) { + numParents++; + } + } + + *numParentsPtr = numParents; + + if (numParents > 0) { + result = (char**)malloc(sizeof(char*) * numParents); + j = 0; + for (i=0;i<numInherits;i++) { + if ( strcmp(inhinfo[i].inhrel, oid) == 0) { + parentInd = findTableByOid(tblinfo, numTables, + inhinfo[i].inhparent); + result[j++] = tblinfo[parentInd].relname; + } + } + return result; + } + else + return NULL; +} + +/* + * parseArgTypes + * parse a string of eight numbers delimited by spaces + * into a character array + */ + +void +parseArgTypes(char **argtypes, char* str) +{ + int i, j, argNum; + char temp[100]; + char s; + + argNum = 0; + j = 0; + while ( (s = *str) != '\0') { + if (s == ' ') { + temp[j] = '\0'; + argtypes[argNum] = dupstr(temp); + argNum++; + j = 0; + } else { + temp[j] = s; + j++; + } + str++; + } + if (j != 0) { + temp[j] = '\0'; + argtypes[argNum] = dupstr(temp); + } + +} + + +/* + * strInArray: + * takes in a string and a string array and the number of elements in the + * string array. + * returns the index if the string is somewhere in the array, -1 otherwise + * + */ + +int +strInArray(char* pattern, char** arr, int arr_size) +{ + int i; + for (i=0;i<arr_size;i++) { + if (strcmp(pattern, arr[i]) == 0) + return i; + } + return -1; +} + +/* + * dumpSchema: + * we have a valid connection, we are now going to dump the schema + * into the file + * + */ + +TableInfo * +dumpSchema(FILE* fout, int *numTablesPtr) +{ + int numTypes; + int numFuncs; + int numTables; + int numInherits; + int numIndices; + int numAggregates; + int numOperators; + TypeInfo *tinfo; + FuncInfo *finfo; + AggInfo *agginfo; + TableInfo *tblinfo; + InhInfo *inhinfo; + IndInfo *indinfo; + OprInfo *oprinfo; + +if (g_verbose) fprintf(stderr,"%s reading user-defined types %s\n", + g_comment_start, g_comment_end); + tinfo = getTypes(&numTypes); + +if (g_verbose) fprintf(stderr,"%s reading user-defined functions %s\n", + g_comment_start, g_comment_end); + finfo = getFuncs(&numFuncs); + +if (g_verbose) fprintf(stderr,"%s reading user-defined aggregates %s\n", + g_comment_start, g_comment_end); + agginfo = getAggregates(&numAggregates); + +if (g_verbose) fprintf(stderr,"%s reading user-defined operators %s\n", + g_comment_start, g_comment_end); + oprinfo = getOperators(&numOperators); + +if (g_verbose) fprintf(stderr,"%s reading user-defined tables %s\n", + g_comment_start, g_comment_end); + tblinfo = getTables(&numTables); + +if (g_verbose) fprintf(stderr,"%s reading table inheritance information %s\n", + g_comment_start, g_comment_end); + inhinfo = getInherits(&numInherits); + +if (g_verbose) fprintf(stderr, "%s finding the attribute names and types for each table %s\n", + g_comment_start, g_comment_end); + getTableAttrs(tblinfo, numTables); + +if (g_verbose) fprintf(stderr, "%s flagging inherited attributes in subtables %s\n", + g_comment_start, g_comment_end); + flagInhAttrs(tblinfo, numTables, inhinfo, numInherits); + +if (g_verbose) fprintf(stderr,"%s reading indices information %s\n", + g_comment_start, g_comment_end); + indinfo = getIndices(&numIndices); + +if (g_verbose) fprintf(stderr,"%s dumping out user-defined types %s\n", + g_comment_start, g_comment_end); + dumpTypes(fout, finfo, numFuncs, tinfo, numTypes); + +if (g_verbose) fprintf(stderr,"%s dumping out tables %s\n", + g_comment_start, g_comment_end); + dumpTables(fout, tblinfo, numTables, inhinfo, numInherits, + tinfo, numTypes); + +if (g_verbose) fprintf(stderr,"%s dumping out user-defined functions %s\n", + g_comment_start, g_comment_end); + dumpFuncs(fout, finfo, numFuncs, tinfo, numTypes); + +if (g_verbose) fprintf(stderr,"%s dumping out user-defined functions %s\n", + g_comment_start, g_comment_end); + dumpAggs(fout, agginfo, numAggregates, tinfo, numTypes); + +if (g_verbose) fprintf(stderr,"%s dumping out user-defined operators %s\n", + g_comment_start, g_comment_end); + dumpOprs(fout, oprinfo, numOperators, tinfo, numTypes); + +if (g_verbose) fprintf(stderr,"%s dumping out indices %s\n", + g_comment_start, g_comment_end); + dumpIndices(fout, indinfo, numIndices, tblinfo, numTables); + + *numTablesPtr = numTables; + return tblinfo; +} + + +/* flagInhAttrs - + * for each table in tblinfo, flag its inherited attributes + * so when we dump the table out, we don't dump out the inherited attributes + * + * initializes the parentRels field of each table + * + * modifies tblinfo + * + */ +void +flagInhAttrs(TableInfo* tblinfo, int numTables, + InhInfo* inhinfo, int numInherits) +{ + int i,j,k; + int parentInd; + char *parentRels; + int numParents; + + /* we go backwards because the tables in tblinfo are in OID + order, meaning the subtables are after the parent tables + we flag inherited attributes from child tables first */ + for (i = numTables-1; i >= 0; i--) { + tblinfo[i].parentRels = findParentsByOid(tblinfo, numTables, + inhinfo, numInherits, + tblinfo[i].oid, + &tblinfo[i].numParents); + for (k=0;k<tblinfo[i].numParents;k++) { + parentInd = findTableByName(tblinfo, numTables, + tblinfo[i].parentRels[k]); + for (j=0;j<tblinfo[i].numatts;j++) { + if (strInArray(tblinfo[i].attnames[j], + tblinfo[parentInd].attnames, + tblinfo[parentInd].numatts) != -1) { + tblinfo[i].inhAttrs[j] = 1; + } + } + } + } +} + + +/* + * findTableByName + * finds the index (in tblinfo) of the table with the given relname + * returns -1 if not found + * + * NOTE: should hash this, but just do linear search for now + */ + +int +findTableByName(TableInfo* tblinfo, int numTables, char* relname) +{ + int i; + for (i=0;i<numTables;i++) { + if (strcmp(tblinfo[i].relname, relname) == 0) + return i; + } + return -1; +} + +/* + * findTableByOid + * finds the index (in tblinfo) of the table with the given oid + * returns -1 if not found + * + * NOTE: should hash this, but just do linear search for now + */ + +int +findTableByOid(TableInfo* tblinfo, int numTables, char* oid) +{ + int i; + for (i=0;i<numTables;i++) { + if (strcmp(tblinfo[i].oid, oid) == 0) + return i; + } + return -1; +} + + +/* + * findFuncByName + * finds the index (in finfo) of the function with the given name + * returns -1 if not found + * + * NOTE: should hash this, but just do linear search for now + */ + +int +findFuncByName(FuncInfo* finfo, int numFuncs, char* name) +{ + int i; + for (i=0;i<numFuncs;i++) { + if (strcmp(finfo[i].proname, name) == 0) + return i; + } + return -1; +} + +/* + * isArchiveName + * + * returns true if the relation name is an archive name, false otherwise + */ +int +isArchiveName(char* relname) +{ + return (strlen(relname) > 1 && relname[1] == ','); +} + + + + + + diff --git a/src/bin/pg4_dump/pg4_dump.c b/src/bin/pg4_dump/pg4_dump.c new file mode 100644 index 00000000000..6e6ee6fa573 --- /dev/null +++ b/src/bin/pg4_dump/pg4_dump.c @@ -0,0 +1,1602 @@ +/*------------------------------------------------------------------------- + * + * pg4_dump.c-- + * pg4_dump is an utility for dumping out a postgres database + * into a script file. + * + * pg4_dump will read the system catalogs from a postgresV4r2 database and + * dump out a script that reproduces the schema of the database in terms of + * user-defined types + * user-defined functions + * tables + * indices + * aggregates + * operators + * + * the output script is either POSTQUEL or SQL + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * /usr/local/devel/pglite/cvs/src/bin/pg_dump/pg4_dump.c,v 1.1 1995/05/18 19:23:53 jolly Exp + * + *------------------------------------------------------------------------- + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <sys/param.h> /* for MAXHOSTNAMELEN on most */ +#ifdef PORTNAME_sparc_solaris +#include <netdb.h> /* for MAXHOSTNAMELEN on some */ +#endif + +#include "tmp/postgres.h" +#include "tmp/libpq-fe.h" +#include "libpq/auth.h" + +#include "pg_dump.h" + +extern char *optarg; +extern int optind, opterr; + +/* these are used in libpq */ +extern char *PQhost; /* machine on which the backend is running */ +extern char *PQport; /* comm. port with the postgres backend. */ +extern char *PQtty; /* the tty where postgres msgs are displayed */ +extern char *PQdatabase; /* the postgres db to access. */ + +/* global decls */ +int g_verbose; /* verbose flag */ +int g_last_builtin_oid; /* value of the last builtin oid */ +FILE *g_fout; /* the script file */ + +char g_opaque_type[10]; /* name for the opaque type */ + +/* placeholders for the delimiters for comments */ +char g_comment_start[10]; +char g_comment_end[10]; + +int g_outputSQL; /* if 1, output SQL, otherwise , output Postquel */ + +static +usage(char* progname) +{ + fprintf(stderr, "usage: %s [options] [dbname]\n",progname); + fprintf(stderr, "\t -f filename \t\t script output filename\n"); + fprintf(stderr, "\t -H hostname \t\t server host name\n"); + fprintf(stderr, "\t -o [SQL|POSTQUEL} \t\t output format\n"); + fprintf(stderr, "\t -p port \t\t server port number\n"); + fprintf(stderr, "\t -v \t\t verbose\n"); + fprintf(stderr, "\t -S \t\t dump out only the schema, no data\n"); + fprintf(stderr, "\n if dbname is not supplied, then the DATABASE environment name is used\n"); + fprintf(stderr, "\n"); + + fprintf(stderr, "\tpg4_dump dumps out postgres databases and produces a script file\n"); + fprintf(stderr, "\tof query commands to regenerate the schema\n"); + fprintf(stderr, "\tThe output format is either POSTQUEL or SQL. The default is SQL\n"); + exit(1); +} + +void +main(int argc, char** argv) +{ + int c; + char* progname; + char* filename; + char* dbname; + char *username, usernamebuf[NAMEDATALEN + 1]; + char hostbuf[MAXHOSTNAMELEN]; + int schemaOnly; + + TableInfo *tblinfo; + int numTables; + + + dbname = NULL; + filename = NULL; + g_verbose = 0; + g_outputSQL = 1; + schemaOnly = 0; + + progname = *argv; + + while ((c = getopt(argc, argv,"f:H:o:p:vSD")) != EOF) { + switch(c) { + case 'f': /* output file name */ + filename = optarg; + break; + case 'H' : /* server host */ + PQhost = optarg; + break; + case 'o': + { + char *lang = optarg; + if (lang) { + if (strcmp(lang,"SQL") != 0) + g_outputSQL = 0; + } + } + break; + case 'p' : /* server port */ + PQport = optarg; + break; + case 'v': /* verbose */ + g_verbose = 1; + break; + case 'S': /* dump schema only */ + schemaOnly = 1; + break; + default: + usage(progname); + break; + } + } + + /* open the output file */ + if (filename == NULL) { + g_fout = stdout; + } else { + g_fout = fopen(filename, "w"); + if (g_fout == NULL) { + fprintf(stderr,"%s: could not open output file named %s for writing\n", + progname, filename); + exit(2); + } + } + + /* Determine our username (according to the authentication system, if + * there is one). + */ + if ((username = fe_getauthname()) == (char *) NULL) { + fprintf(stderr, "%s: could not find a valid user name\n",progname); + exit(2); + } + memset(usernamebuf, 0, sizeof(usernamebuf)); + (void) strncpy(usernamebuf, username, NAMEDATALEN); + username = usernamebuf; + + /* + * Determine the hostname of the database server. Try to avoid using + * "localhost" if at all possible. + */ + if (!PQhost && !(PQhost = getenv("PGHOST"))) + PQhost = "localhost"; + if (!strcmp(PQhost, "localhost")) { + if (gethostname(hostbuf, MAXHOSTNAMELEN) != -1) + PQhost = hostbuf; + } + + + /* find database */ + if (!(dbname = argv[optind]) && + !(dbname = getenv("DATABASE")) && + !(dbname = username)) { + fprintf(stderr, "%s: no database name specified\n",progname); + exit (2); + } + + PQsetdb(dbname); + + /* make sure things are ok before giving users a warm welcome! */ + check_conn_and_db(); + + if (g_outputSQL) { + strcpy(g_comment_start,"-- "); + g_comment_end[0] = '\0'; + strcpy(g_opaque_type, "opaque"); + } else { + strcpy(g_comment_start,"/* "); + strcpy(g_comment_end,"*/ "); + strcpy(g_opaque_type, "any"); + } + + g_last_builtin_oid = findLastBuiltinOid(); + + +if (g_verbose) + fprintf(stderr, "%s last builtin oid is %d %s\n", + g_comment_start, g_last_builtin_oid, g_comment_end); + + tblinfo = dumpSchema(g_fout, &numTables); + + if (!schemaOnly) { + +if (g_verbose) { + fprintf(stderr, "%s dumping out the contents of each table %s\n", + g_comment_start, g_comment_end ); + fprintf(stderr, "%s the output language is %s %s\n", + g_comment_start, + (g_outputSQL) ? "SQL" : "POSTQUEL", + g_comment_end); +} + + dumpClasses(tblinfo, numTables, g_fout); + } + + fflush(g_fout); + fclose(g_fout); + exit(0); +} + +/* + * getTypes: + * read all base types in the system catalogs and return them in the + * TypeInfo* structure + * + * numTypes is set to the number of types read in + * + */ +TypeInfo* +getTypes(int *numTypes) +{ + char* res; + PortalBuffer* pbuf; + int ntups; + int i; + char query[MAXQUERYLEN]; + TypeInfo *tinfo; + + int i_oid; + int i_typowner; + int i_typname; + int i_typlen; + int i_typprtlen; + int i_typinput; + int i_typoutput; + int i_typreceive; + int i_typsend; + int i_typelem; + int i_typdelim; + int i_typdefault; + int i_typrelid; + int i_typbyval; + + PQexec("begin"); + + /* find all base types */ + /* we include even the built-in types + because those may be used as array elements by user-defined types */ + /* we filter out the built-in types when + we dump out the types */ + +/* + sprintf(query, "SELECT oid, typowner,typname, typlen, typprtlen, typinput, typoutput, typreceive, typsend, typelem, typdelim, typdefault, typrelid,typbyval from pg_type"); +*/ + sprintf(query, "retrieve (t.oid, t.typowner, t.typname, t.typlen, t.typprtlen, t.typinput, t.typoutput, t.typreceive, t.typsend, t.typelem, t.typdelim, t.typdefault, t.typrelid, t.typbyval) from t in pg_type"); + + + res = PQexec(query); + pbuf = PQparray(res+1); + ntups = PQntuplesGroup(pbuf,0); + + tinfo = (TypeInfo*)malloc(ntups * sizeof(TypeInfo)); + + i_oid = PQfnumberGroup(pbuf,0,"oid"); + i_typowner = PQfnumberGroup(pbuf,0,"typowner"); + i_typname = PQfnumberGroup(pbuf,0,"typname"); + i_typlen = PQfnumberGroup(pbuf,0,"typlen"); + i_typprtlen = PQfnumberGroup(pbuf,0,"typprtlen"); + i_typinput = PQfnumberGroup(pbuf,0,"typinput"); + i_typoutput = PQfnumberGroup(pbuf,0,"typoutput"); + i_typreceive = PQfnumberGroup(pbuf,0,"typreceive"); + i_typsend = PQfnumberGroup(pbuf,0,"typsend"); + i_typelem = PQfnumberGroup(pbuf,0,"typelem"); + i_typdelim = PQfnumberGroup(pbuf,0,"typdelim"); + i_typdefault = PQfnumberGroup(pbuf,0,"typdefault"); + i_typrelid = PQfnumberGroup(pbuf,0,"typrelid"); + i_typbyval = PQfnumberGroup(pbuf,0,"typbyval"); + + for (i=0;i<ntups;i++) { + tinfo[i].oid = dupstr(PQgetvalue(pbuf,i,i_oid)); + tinfo[i].typowner = dupstr(PQgetvalue(pbuf,i,i_typowner)); + tinfo[i].typname = dupstr(PQgetvalue(pbuf,i,i_typname)); + tinfo[i].typlen = dupstr(PQgetvalue(pbuf,i,i_typlen)); + tinfo[i].typprtlen = dupstr(PQgetvalue(pbuf,i,i_typprtlen)); + tinfo[i].typinput = dupstr(PQgetvalue(pbuf,i,i_typinput)); + tinfo[i].typoutput = dupstr(PQgetvalue(pbuf,i,i_typoutput)); + tinfo[i].typreceive = dupstr(PQgetvalue(pbuf,i,i_typreceive)); + tinfo[i].typsend = dupstr(PQgetvalue(pbuf,i,i_typsend)); + tinfo[i].typelem = dupstr(PQgetvalue(pbuf,i,i_typelem)); + tinfo[i].typdelim = dupstr(PQgetvalue(pbuf,i,i_typdelim)); + tinfo[i].typdefault = dupstr(PQgetvalue(pbuf,i,i_typdefault)); + tinfo[i].typrelid = dupstr(PQgetvalue(pbuf,i,i_typrelid)); + + if (strcmp(PQgetvalue(pbuf,i,i_typbyval), "f") == 0) + tinfo[i].passedbyvalue = 0; + else + tinfo[i].passedbyvalue = 1; + + /* check for user-defined array types, + omit system generated ones */ + if ( (strcmp(tinfo[i].typelem, "0") != 0) && + tinfo[i].typname[0] != '_') + tinfo[i].isArray = 1; + else + tinfo[i].isArray = 0; + } + + *numTypes = ntups; + + PQexec("end"); + PQclear(res+1); + return tinfo; +} + +/* + * getOperators: + * read all operators in the system catalogs and return them in the + * OprInfo* structure + * + * numOprs is set to the number of operators read in + * + * + */ + +OprInfo* +getOperators(int *numOprs) +{ + char *res; + PortalBuffer *pbuf; + int ntups; + int i; + char query[MAXQUERYLEN]; + + OprInfo* oprinfo; + + int i_oid; + int i_oprname; + int i_oprkind; + int i_oprcode; + int i_oprleft; + int i_oprright; + int i_oprcom; + int i_oprnegate; + int i_oprrest; + int i_oprjoin; + int i_oprcanhash; + int i_oprlsortop; + int i_oprrsortop; + + /* find all operators, including builtin operators, + filter out system-defined operators at dump-out time */ + PQexec("begin"); +/* + sprintf(query, "SELECT oid, oprname, oprkind, oprcode, oprleft, oprright, oprcom, oprnegate, oprrest, oprjoin, oprcanhash, oprlsortop, oprrsortop from pg_operator"); +*/ + sprintf(query, "retrieve (o.oid, o.oprname, o.oprkind, o.oprcode, o.oprleft, o.oprright, o.oprcom, o.oprnegate, o.oprrest, o.oprjoin, o.oprcanhash, o.oprlsortop, o.oprrsortop) from o in pg_operator"); + + + res = PQexec(query); + pbuf = PQparray(res+1); + ntups = PQntuplesGroup(pbuf,0); + *numOprs = ntups; + + oprinfo = (OprInfo*)malloc(ntups * sizeof(OprInfo)); + + i_oid = PQfnumberGroup(pbuf,0,"oid"); + i_oprname = PQfnumberGroup(pbuf,0,"oprname"); + i_oprkind = PQfnumberGroup(pbuf,0,"oprkind"); + i_oprcode = PQfnumberGroup(pbuf,0,"oprcode"); + i_oprleft = PQfnumberGroup(pbuf,0,"oprleft"); + i_oprright = PQfnumberGroup(pbuf,0,"oprright"); + i_oprcom = PQfnumberGroup(pbuf,0,"oprcom"); + i_oprnegate = PQfnumberGroup(pbuf,0,"oprnegate"); + i_oprrest = PQfnumberGroup(pbuf,0,"oprrest"); + i_oprjoin = PQfnumberGroup(pbuf,0,"oprjoin"); + i_oprcanhash = PQfnumberGroup(pbuf,0,"oprcanhash"); + i_oprlsortop = PQfnumberGroup(pbuf,0,"oprlsortop"); + i_oprrsortop = PQfnumberGroup(pbuf,0,"oprrsortop"); + + for (i=0;i<ntups;i++) { + oprinfo[i].oid = dupstr(PQgetvalue(pbuf,i,i_oid)); + oprinfo[i].oprname = dupstr(PQgetvalue(pbuf,i,i_oprname)); + oprinfo[i].oprkind = dupstr(PQgetvalue(pbuf,i,i_oprkind)); + oprinfo[i].oprcode = dupstr(PQgetvalue(pbuf,i,i_oprcode)); + oprinfo[i].oprleft = dupstr(PQgetvalue(pbuf,i,i_oprleft)); + oprinfo[i].oprright = dupstr(PQgetvalue(pbuf,i,i_oprright)); + oprinfo[i].oprcom = dupstr(PQgetvalue(pbuf,i,i_oprcom)); + oprinfo[i].oprnegate = dupstr(PQgetvalue(pbuf,i,i_oprnegate)); + oprinfo[i].oprrest = dupstr(PQgetvalue(pbuf,i,i_oprrest)); + oprinfo[i].oprjoin = dupstr(PQgetvalue(pbuf,i,i_oprjoin)); + oprinfo[i].oprcanhash = dupstr(PQgetvalue(pbuf,i,i_oprcanhash)); + oprinfo[i].oprlsortop = dupstr(PQgetvalue(pbuf,i,i_oprlsortop)); + oprinfo[i].oprrsortop = dupstr(PQgetvalue(pbuf,i,i_oprrsortop)); + } + + PQclear(res+1); + PQexec("end"); + + return oprinfo; +} + + +/* + * getAggregates: + * read all the user-defined aggregates in the system catalogs and + * return them in the AggInfo* structure + * + * numAggs is set to the number of aggregates read in + * + * + */ +AggInfo* +getAggregates(int *numAggs) +{ + char* res; + PortalBuffer *pbuf; + int ntups; + int i; + char query[MAXQUERYLEN]; + AggInfo *agginfo; + + int i_oid; + int i_aggname; + int i_aggtransfn1; + int i_aggtransfn2; + int i_aggfinalfn; + int i_aggtranstype1; + int i_aggbasetype; + int i_aggtranstype2; + int i_agginitval1; + int i_agginitval2; + + /* find all user-defined aggregates */ + + PQexec("begin"); +/* + sprintf(query, + "SELECT oid, aggname, aggtransfn1, aggtransfn2, aggfinalfn, aggtranstype1, aggbasetype, aggtranstype2, agginitval1, agginitval2 from pg_aggregate;"); +*/ + sprintf(query, + "retrieve (a.oid, a.aggname, a.aggtransfn1, a.aggtransfn2, a.aggfinalfn, a.aggtranstype1, a.aggbasetype, a.aggtranstype2, a.agginitval1, a.agginitval2) from a in pg_aggregate"); + + res = PQexec(query); + pbuf = PQparray(res+1); + ntups = PQntuplesGroup(pbuf,0); + *numAggs = ntups; + + agginfo = (AggInfo*)malloc(ntups * sizeof(AggInfo)); + + i_oid = PQfnumberGroup(pbuf,0,"oid"); + i_aggname = PQfnumberGroup(pbuf,0,"aggname"); + i_aggtransfn1 = PQfnumberGroup(pbuf,0,"aggtransfn1"); + i_aggtransfn2 = PQfnumberGroup(pbuf,0,"aggtransfn2"); + i_aggfinalfn = PQfnumberGroup(pbuf,0,"aggfinalfn"); + i_aggtranstype1 = PQfnumberGroup(pbuf,0,"aggtranstype1"); + i_aggbasetype = PQfnumberGroup(pbuf,0,"aggbasetype"); + i_aggtranstype2 = PQfnumberGroup(pbuf,0,"aggtranstype2"); + i_agginitval1 = PQfnumberGroup(pbuf,0,"agginitval1"); + i_agginitval2 = PQfnumberGroup(pbuf,0,"agginitval2"); + + for (i=0;i<ntups;i++) { + agginfo[i].oid = dupstr(PQgetvalue(pbuf,i,i_oid)); + agginfo[i].aggname = dupstr(PQgetvalue(pbuf,i,i_aggname)); + agginfo[i].aggtransfn1 = dupstr(PQgetvalue(pbuf,i,i_aggtransfn1)); + agginfo[i].aggtransfn2 = dupstr(PQgetvalue(pbuf,i,i_aggtransfn2)); + agginfo[i].aggfinalfn = dupstr(PQgetvalue(pbuf,i,i_aggfinalfn)); + agginfo[i].aggtranstype1 = dupstr(PQgetvalue(pbuf,i,i_aggtranstype1)); + agginfo[i].aggbasetype = dupstr(PQgetvalue(pbuf,i,i_aggbasetype)); + agginfo[i].aggtranstype2 = dupstr(PQgetvalue(pbuf,i,i_aggtranstype2)); + agginfo[i].agginitval1 = dupstr(PQgetvalue(pbuf,i,i_agginitval1)); + agginfo[i].agginitval2 = dupstr(PQgetvalue(pbuf,i,i_agginitval2)); + } + + PQclear(res+1); + PQexec("end"); + + return agginfo; +} + +/* + * getFuncs: + * read all the user-defined functions in the system catalogs and + * return them in the FuncInfo* structure + * + * numFuncs is set to the number of functions read in + * + * + */ +FuncInfo* +getFuncs(int *numFuncs) +{ + char* res; + PortalBuffer *pbuf; + int ntups; + int i, j; + char query[MAXQUERYLEN]; + FuncInfo *finfo; + char *proargtypes; + + int i_oid; + int i_proname; + int i_proowner; + int i_prolang; + int i_pronargs; + int i_proargtypes; + int i_prorettype; + int i_proretset; + int i_prosrc; + int i_probin; + + /* find all user-defined funcs */ + + PQexec("begin"); + +/* + sprintf(query, + "SELECT oid, proname, proowner, prolang, pronargs, prorettype, proretset, proargtypes, prosrc, probin from pg_proc where oid > '%d'::oid", + g_last_builtin_oid); +*/ + sprintf(query, + "retrieve (f.oid, f.proname, f.proowner, f.prolang, f.pronargs, f.prorettype, f.proretset, f.proargtypes, f.prosrc, f.probin) from f in pg_proc where f.oid > \"%d\"::oid", + g_last_builtin_oid); + + res = PQexec(query); + pbuf = PQparray(res+1); + ntups = PQntuplesGroup(pbuf,0); + + *numFuncs = ntups; + + finfo = (FuncInfo*)malloc(ntups * sizeof(FuncInfo)); + + i_oid = PQfnumberGroup(pbuf,0,"oid"); + i_proname = PQfnumberGroup(pbuf,0,"proname"); + i_proowner = PQfnumberGroup(pbuf,0,"proowner"); + i_prolang = PQfnumberGroup(pbuf,0,"prolang"); + i_pronargs = PQfnumberGroup(pbuf,0,"pronargs"); + i_proargtypes = PQfnumberGroup(pbuf,0,"proargtypes"); + i_prorettype = PQfnumberGroup(pbuf,0,"prorettype"); + i_proretset = PQfnumberGroup(pbuf,0,"proretset"); + i_prosrc = PQfnumberGroup(pbuf,0,"prosrc"); + i_probin = PQfnumberGroup(pbuf,0,"probin"); + + for (i=0;i<ntups;i++) { + finfo[i].oid = dupstr(PQgetvalue(pbuf,i,i_oid)); + finfo[i].proname = dupstr(PQgetvalue(pbuf,i,i_proname)); + finfo[i].proowner = dupstr(PQgetvalue(pbuf,i,i_proowner)); + + finfo[i].prosrc = checkForQuote(PQgetvalue(pbuf,i,i_prosrc)); + finfo[i].probin = dupstr(PQgetvalue(pbuf,i,i_probin)); + + finfo[i].prorettype = dupstr(PQgetvalue(pbuf,i,i_prorettype)); + finfo[i].retset = (strcmp(PQgetvalue(pbuf,i,i_proretset),"t") == 0); + finfo[i].nargs = atoi(PQgetvalue(pbuf,i,i_pronargs)); + finfo[i].lang = (atoi(PQgetvalue(pbuf,i,i_prolang)) == C_PROLANG_OID); + + parseArgTypes(finfo[i].argtypes, PQgetvalue(pbuf,i,i_proargtypes)); + + finfo[i].dumped = 0; + } + + PQclear(res+1); + PQexec("end"); + + return finfo; + +} + +/* + * getTables + * read all the user-defined tables (no indices, no catalogs) + * in the system catalogs return them in the TableInfo* structure + * + * numTables is set to the number of tables read in + * + * + */ +TableInfo* +getTables(int *numTables) +{ + char* res; + PortalBuffer* pbuf; + int ntups; + int i, j; + char query[MAXQUERYLEN]; + TableInfo *tblinfo; + + int i_oid; + int i_relname; + int i_relarch; + + /* find all the user-defined tables (no indices and no catalogs), + ordering by oid is important so that we always process the parent + tables before the child tables when traversing the tblinfo* */ + PQexec("begin"); +/* + sprintf(query, + "SELECT oid, relname, relarch from pg_class where relkind = 'r' and relname !~ '^pg_' order by oid;"); +*/ + sprintf(query, + "retrieve (r.oid, r.relname, r.relarch) from r in pg_class where r.relkind = \"r\" and r.relname !~ \"^pg_\" and r.relname !~ \"^Xinv\" sort by oid"); + + res = PQexec(query); + pbuf = PQparray(res+1); + ntups = PQntuplesGroup(pbuf,0); + + *numTables = ntups; + + tblinfo = (TableInfo*)malloc(ntups * sizeof(TableInfo)); + + i_oid = PQfnumberGroup(pbuf,0,"oid"); + i_relname = PQfnumberGroup(pbuf,0,"relname"); + i_relarch = PQfnumberGroup(pbuf,0,"relarch"); + + for (i=0;i<ntups;i++) { + tblinfo[i].oid = dupstr(PQgetvalue(pbuf,i,i_oid)); + tblinfo[i].relname = dupstr(PQgetvalue(pbuf,i,i_relname)); + tblinfo[i].relarch = dupstr(PQgetvalue(pbuf,i,i_relarch)); + } + + PQclear(res+1); + PQexec("end"); + + return tblinfo; + +} + +/* + * getInherits + * read all the inheritance information + * from the system catalogs return them in the InhInfo* structure + * + * numInherits is set to the number of tables read in + * + * + */ +InhInfo* +getInherits(int *numInherits) +{ + char* res; + PortalBuffer* pbuf; + int ntups; + int i; + char query[MAXQUERYLEN]; + InhInfo *inhinfo; + + int i_inhrel; + int i_inhparent; + + /* find all the inheritance information */ + PQexec("begin"); +/* + sprintf(query, "SELECT inhrel, inhparent from pg_inherits"); +*/ + sprintf(query, "retrieve (i.inhrel, i.inhparent) from i in pg_inherits"); + + res = PQexec(query); + pbuf = PQparray(res+1); + ntups = PQntuplesGroup(pbuf,0); + + *numInherits = ntups; + + inhinfo = (InhInfo*)malloc(ntups * sizeof(InhInfo)); + + i_inhrel = PQfnumberGroup(pbuf,0,"inhrel"); + i_inhparent = PQfnumberGroup(pbuf,0,"inhparent"); + + for (i=0;i<ntups;i++) { + inhinfo[i].inhrel = dupstr(PQgetvalue(pbuf,i,i_inhrel)); + inhinfo[i].inhparent = dupstr(PQgetvalue(pbuf,i,i_inhparent)); + } + + PQclear(res+1); + PQexec("end"); + return inhinfo; +} + +/* + * getTableAttrs - + * for each table in tblinfo, read its attributes types and names + * + * this is implemented in a very inefficient way right now, looping + * through the tblinfo and doing a join per table to find the attrs and their + * types + * + * modifies tblinfo + */ +void +getTableAttrs(TableInfo* tblinfo, int numTables) +{ + int i,j; + char q[MAXQUERYLEN]; + int i_attname; + int i_typname; + char *res; + PortalBuffer *pbuf; + int ntups; + + for (i=0;i<numTables;i++) { + + /* find all the user attributes and their types*/ + /* we must read the attribute names in attribute number order! */ + /* because we will use the attnum to index into the attnames array + later */ +/* + sprintf(q,"SELECT a.attnum, a.attname, t.typname from pg_attribute a, pg_type t where a.attrelid = '%s' and a.atttypid = t.oid and a.attnum > 0 order by attnum",tblinfo[i].oid); +*/ +if (g_verbose) + fprintf(stderr,"%s finding the attrs and types for table: %s %s\n", + g_comment_start, + tblinfo[i].relname, + g_comment_end); + + + sprintf(q,"retrieve (a.attnum, a.attname, t.typname) from a in pg_attribute, t in pg_type where a.attrelid = \"%s\" and a.atttypid = t.oid and a.attnum > 0 sort by attnum",tblinfo[i].oid); + + res = PQexec(q); + pbuf = PQparray(res+1); + ntups = PQntuplesGroup(pbuf,0); + + i_attname = PQfnumberGroup(pbuf,0,"attname"); + i_typname = PQfnumberGroup(pbuf,0,"typname"); + + tblinfo[i].numatts = ntups; + tblinfo[i].attnames = (char**) malloc( ntups * sizeof(char*)); + tblinfo[i].out_attnames = (char**) malloc( ntups * sizeof(char*)); + tblinfo[i].typnames = (char**) malloc( ntups * sizeof(char*)); + tblinfo[i].inhAttrs = (int*) malloc (ntups * sizeof(int)); + tblinfo[i].parentRels = NULL; + tblinfo[i].numParents = 0; + for (j=0;j<ntups;j++) { + tblinfo[i].attnames[j] = dupstr(PQgetvalue(pbuf,j,i_attname)); + tblinfo[i].typnames[j] = dupstr(PQgetvalue(pbuf,j,i_typname)); + tblinfo[i].inhAttrs[j] = 0; /* this flag is set in flagInhAttrs()*/ + } + PQclear(res+1); + } +} + + +/* + * getIndices + * read all the user-defined indices information + * from the system catalogs return them in the InhInfo* structure + * + * numIndices is set to the number of indices read in + * + * + */ +IndInfo* +getIndices(int *numIndices) +{ + int i; + char query[MAXQUERYLEN]; + char *res; + PortalBuffer *pbuf; + int ntups; + IndInfo *indinfo; + + int i_indexrelname; + int i_indrelname; + int i_indamname; + int i_indproc; + int i_indkey; + int i_indclassname; + + /* find all the user-define indices. + We do not handle partial indices. + We also assume that only single key indices + + this is a 5-way join !! + */ + + PQexec("begin"); +/* + sprintf(query, + "SELECT t1.relname as indexrelname, t2.relname as indrelname, i.indproc, i.indkey[0], o.opcname as indclassname, a.amname as indamname from pg_index i, pg_class t1, pg_class t2, pg_opclass o, pg_am a where t1.oid = i.indexrelid and t2.oid = i.indrelid and o.oid = i.indclass[0] and t1.relam = a.oid and i.indexrelid > '%d'::oid and t2.relname !~ '^pg_';", + g_last_builtin_oid); +*/ + + sprintf(query, + "retrieve (indexrelname = t1.relname, indrelname = t2.relname, i.indproc, i.indkey[0], indclassname = o.opcname, indamname = a.amname) from i in pg_index, t1 in pg_class, t2 in pg_class, o in pg_opclass, a in pg_am where t1.oid = i.indexrelid and t2.oid = i.indrelid and o.oid = i.indclass[0] and t1.relam = a.oid and i.indexrelid > \"%d\"::oid and t2.relname !~ \"^pg_\" and t1.relname !~ \"^Xinx\"", + g_last_builtin_oid); + + res = PQexec(query); + pbuf = PQparray(res+1); + ntups = PQntuplesGroup(pbuf,0); + + *numIndices = ntups; + + indinfo = (IndInfo*)malloc(ntups * sizeof (IndInfo)); + + i_indexrelname = PQfnumberGroup(pbuf,0,"indexrelname"); + i_indrelname = PQfnumberGroup(pbuf,0,"indrelname"); + i_indamname = PQfnumberGroup(pbuf,0,"indamname"); + i_indproc = PQfnumberGroup(pbuf,0,"indproc"); + i_indkey = PQfnumberGroup(pbuf,0,"indkey"); + i_indclassname = PQfnumberGroup(pbuf,0,"indclassname"); + + for (i=0;i<ntups;i++) { + indinfo[i].indexrelname = dupstr(PQgetvalue(pbuf,i,i_indexrelname)); + indinfo[i].indrelname = dupstr(PQgetvalue(pbuf,i,i_indrelname)); + indinfo[i].indamname = dupstr(PQgetvalue(pbuf,i,i_indamname)); + indinfo[i].indproc = dupstr(PQgetvalue(pbuf,i,i_indproc)); + indinfo[i].indkey = dupstr(PQgetvalue(pbuf,i,i_indkey)); + indinfo[i].indclassname = dupstr(PQgetvalue(pbuf,i,i_indclassname)); + } + PQclear(res+1); + PQexec("end"); + + return indinfo; +} + +/* + * dumpTypes + * writes out to fout queries to recreate all the user-defined types + * + */ + +void +dumpTypes(FILE* fout, FuncInfo* finfo, int numFuncs, + TypeInfo* tinfo, int numTypes) +{ + int i; + char q[MAXQUERYLEN]; + int funcInd; + + for (i=0;i<numTypes;i++) { + + /* skip all the builtin types */ + if (atoi(tinfo[i].oid) < g_last_builtin_oid) + continue; + + /* skip relation types */ + if (atoi(tinfo[i].typrelid) != 0) + continue; + + /* skip all array types that start w/ underscore */ + if ( (tinfo[i].typname[0] == '_') && + (strcmp(tinfo[i].typinput, "array_in") == 0)) + continue; + + /* before we create a type, we need to create the input and + output functions for it, if they haven't been created already */ + funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typinput); + if (funcInd != -1) + dumpOneFunc(fout,finfo,funcInd,tinfo,numTypes); + + funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typoutput); + if (funcInd != -1) + dumpOneFunc(fout,finfo,funcInd,tinfo,numTypes); + + if (g_outputSQL) { + sprintf(q, + "CREATE TYPE %s ( internallength = %s, externallength = %s, input = %s, output = %s, send = %s, receive = %s, default = '%s'", + tinfo[i].typname, + tinfo[i].typlen, + tinfo[i].typprtlen, + tinfo[i].typinput, + tinfo[i].typoutput, + tinfo[i].typsend, + tinfo[i].typreceive, + tinfo[i].typdefault); + } else { + sprintf(q, + "define type %s ( internallength = %s, externallength = %s, input = %s, output = %s, send = %s, receive = %s, default = \"%s\"", + tinfo[i].typname, + (strcmp(tinfo[i].typlen, "-1") == 0) ? "variable" : tinfo[i].typlen, + (strcmp(tinfo[i].typprtlen, "-1") == 0) ? "variable " :tinfo[i].typprtlen, + tinfo[i].typinput, + tinfo[i].typoutput, + tinfo[i].typsend, + tinfo[i].typreceive, + tinfo[i].typdefault); + } + + if (tinfo[i].isArray) { + char* elemType; + + elemType = findTypeByOid(tinfo, numTypes, tinfo[i].typelem); + + if (g_outputSQL) + sprintf(q,"%s, element = %s, delimiter = '%s'", + q, elemType,tinfo[i].typdelim); + else + sprintf(q,"%s, element = %s, delimiter = \"%s\"", + q, elemType,tinfo[i].typdelim); + } + if (tinfo[i].passedbyvalue) + strcat(q,",passedbyvalue"); + else + strcat(q,")"); + + if (g_outputSQL) + strcat(q,";\n"); + else + strcat(q,"\\g\n"); + + fputs(q,fout); + } + fflush(fout); +} + +/* + * dumpFuncs + * writes out to fout the queries to recreate all the user-defined functions + * + */ +void +dumpFuncs(FILE* fout, FuncInfo* finfo, int numFuncs, + TypeInfo *tinfo, int numTypes) +{ + int i; + char q[MAXQUERYLEN]; + for (i=0;i<numFuncs;i++) { + dumpOneFunc(fout,finfo,i,tinfo,numTypes); + } +} + +/* + * dumpOneFunc: + * dump out only one function, the index of which is given in the third + * argument + * + */ + +void +dumpOneFunc(FILE* fout, FuncInfo* finfo, int i, + TypeInfo *tinfo, int numTypes) +{ + char q[MAXQUERYLEN]; + int j; + + if (finfo[i].dumped) + return; + else + finfo[i].dumped = 1; + + if (g_outputSQL) { + sprintf(q,"CREATE FUNCTION %s (",finfo[i].proname); + + for (j=0;j<finfo[i].nargs;j++) { + char* typname; + typname = findTypeByOid(tinfo, numTypes, finfo[i].argtypes[j]); + sprintf(q, "%s%s%s", + q, + (j > 0) ? "," : "", + typname); + } + sprintf(q,"%s ) RETURNS %s%s AS '%s' LANGUAGE '%s';\n", + q, + finfo[i].retset ? " SETOF " : "", + findTypeByOid(tinfo, numTypes, finfo[i].prorettype), + (finfo[i].lang) ? finfo[i].probin : finfo[i].prosrc, + (finfo[i].lang) ? "C" : "SQL"); +if (finfo[i].lang != 1) { + fprintf(stderr, + "%s WARNING: text of function named %s is in POSTQUEL %s\n", + g_comment_start, + finfo[i].proname, + g_comment_end); +} + + } else { + sprintf(q,"define function %s ( language = \"%s\", returntype = %s%s) arg is (", + finfo[i].proname, + (finfo[i].lang) ? "c" : "postquel", + finfo[i].retset ? " setof " : "", + findTypeByOid(tinfo, numTypes, finfo[i].prorettype) + ); + + for (j=0;j<finfo[i].nargs;j++) { + char* typname; + typname = findTypeByOid(tinfo, numTypes, finfo[i].argtypes[j]); + sprintf(q, "%s%s%s", + q, + (j > 0) ? "," : "", + typname); + } + sprintf(q,"%s ) as \"%s\"\\g\n", + q, + (finfo[i].lang) ? finfo[i].probin : finfo[i].prosrc); + } + + fputs(q,fout); + fflush(fout); + +} + +/* + * dumpOprs + * writes out to fout the queries to recreate all the user-defined operators + * + */ +void +dumpOprs(FILE* fout, OprInfo* oprinfo, int numOperators, + TypeInfo *tinfo, int numTypes) +{ + int i; + char q[MAXQUERYLEN]; + char leftarg[MAXQUERYLEN]; + char rightarg[MAXQUERYLEN]; + char commutator[MAXQUERYLEN]; + char negator[MAXQUERYLEN]; + char restrict[MAXQUERYLEN]; + char join[MAXQUERYLEN]; + char sortop[MAXQUERYLEN]; + char comma[2]; + + for (i=0;i<numOperators;i++) { + + /* skip all the builtin oids */ + if (atoi(oprinfo[i].oid) < g_last_builtin_oid) + continue; + + /* some operator are invalid because they were the result + of user defining operators before commutators exist */ + if (strcmp(oprinfo[i].oprcode, "-") == 0) + continue; + + leftarg[0] = '\0'; + rightarg[0] = '\0'; + /* right unary means there's a left arg + and left unary means there's a right arg */ + if (strcmp(oprinfo[i].oprkind, "r") == 0 || + strcmp(oprinfo[i].oprkind, "b") == 0 ) { + sprintf(leftarg, ", %s = %s ", + (g_outputSQL) ? "LEFTARG" : "arg1", + findTypeByOid(tinfo, numTypes, oprinfo[i].oprleft)); + } + if (strcmp(oprinfo[i].oprkind, "l") == 0 || + strcmp(oprinfo[i].oprkind, "b") == 0 ) { + sprintf(rightarg, ", %s = %s ", + (g_outputSQL) ? "RIGHTARG" : "arg2", + findTypeByOid(tinfo, numTypes, oprinfo[i].oprright)); + } + if (strcmp(oprinfo[i].oprcom, "0") == 0) + commutator[0] = '\0'; + else + sprintf(commutator,", commutator = %s ", + findOprByOid(oprinfo, numOperators, oprinfo[i].oprcom)); + + if (strcmp(oprinfo[i].oprnegate, "0") == 0) + negator[0] = '\0'; + else + sprintf(negator,", negator = %s ", + findOprByOid(oprinfo, numOperators, oprinfo[i].oprnegate)); + + if (strcmp(oprinfo[i].oprrest, "-") == 0) + restrict[0] = '\0'; + else + sprintf(restrict,", restrict = %s ", oprinfo[i].oprrest); + + if (strcmp(oprinfo[i].oprjoin,"-") == 0) + join[0] = '\0'; + else + sprintf(join,", join = %s ", oprinfo[i].oprjoin); + + if (strcmp(oprinfo[i].oprlsortop, "0") == 0) + sortop[0] = '\0'; + else + { + sprintf(sortop,", SORT = %s ", + findOprByOid(oprinfo, numOperators, + oprinfo[i].oprlsortop)); + if (strcmp(oprinfo[i].oprrsortop, "0") != 0) + sprintf(sortop, "%s , %s", sortop, + findOprByOid(oprinfo, numOperators, + oprinfo[i].oprlsortop)); + } + + if (g_outputSQL) { + sprintf(q, + "CREATE OPERATOR %s (PROCEDURE = %s %s %s %s %s %s %s %s %s);\n ", + oprinfo[i].oprname, + oprinfo[i].oprcode, + leftarg, + rightarg, + commutator, + negator, + restrict, + (strcmp(oprinfo[i].oprcanhash, "t")) ? ", HASHES" : "", + join, + sortop); + } else + sprintf(q, + "define operator %s (procedure = %s %s %s %s %s %s %s %s %s)\\g\n ", + oprinfo[i].oprname, + oprinfo[i].oprcode, + leftarg, + rightarg, + commutator, + negator, + restrict, + (strcmp(oprinfo[i].oprcanhash, "t")) ? ", hashes" : "", + join, + sortop); + + fputs(q,fout); + } + fflush(fout); + +} + +/* + * dumpAggs + * writes out to fout the queries to create all the user-defined aggregates + * + */ +void +dumpAggs(FILE* fout, AggInfo* agginfo, int numAggs, + TypeInfo *tinfo, int numTypes) +{ + int i; + char q[MAXQUERYLEN]; + char sfunc1[MAXQUERYLEN]; + char sfunc2[MAXQUERYLEN]; + char finalfunc[MAXQUERYLEN]; + char *basetype; + char *stype1; + char *stype2; + char comma1[2], comma2[2]; + + for (i=0;i<numAggs;i++) { + /* skip all the builtin oids */ + if (atoi(agginfo[i].oid) < g_last_builtin_oid) + continue; + + if ( strcmp(agginfo[i].aggtransfn1, "-") == 0) + sfunc1[0] = '\0'; + else { + sprintf(sfunc1, + "sfunc1 = %s, basetype = %s, stype1 = %s", + agginfo[i].aggtransfn1, + findTypeByOid(tinfo,numTypes,agginfo[i].aggbasetype), + findTypeByOid(tinfo,numTypes,agginfo[i].aggtranstype1)); + if (agginfo[i].agginitval1) { + if (g_outputSQL) + sprintf(sfunc1, "%s ,INITCOND1 = '%s'", + sfunc1, agginfo[i].agginitval1); + else + sprintf(sfunc1, "%s ,initcond1 = \"%s\"", + sfunc1, agginfo[i].agginitval1); + + } + + } + + if ( strcmp(agginfo[i].aggtransfn2, "-") == 0) + sfunc2[0] = '\0'; + else { + sprintf(sfunc2, + "sfunc2 = %s, stype2 = %s", + agginfo[i].aggtransfn2, + findTypeByOid(tinfo,numTypes,agginfo[i].aggtranstype2)); + if (agginfo[i].agginitval2) { + if (g_outputSQL) + sprintf(sfunc2,"%s ,initcond2 = '%s'", + sfunc2, agginfo[i].agginitval2); + else + sprintf(sfunc2,"%s ,initcond2 = \"%s\"", + sfunc2, agginfo[i].agginitval2); + + } + } + + if ( strcmp(agginfo[i].aggfinalfn, "-") == 0) + finalfunc[0] = '\0'; + else { + sprintf(finalfunc, "finalfunc = %s", agginfo[i].aggfinalfn); + } + if (sfunc1[0] != '\0' && sfunc2[0] != '\0') { + comma1[0] = ','; comma1[1] = '\0'; + } else + comma1[0] = '\0'; + + if (finalfunc[0] != '\0' && (sfunc1[0] != '\0' || sfunc2[0] != '\0')) { + comma2[0] = ',';comma2[1] = '\0'; + } else + comma2[0] = '\0'; + + if (g_outputSQL) { + sprintf(q,"CREATE AGGREGATE %s ( %s %s %s %s %s );\n", + agginfo[i].aggname, + sfunc1, + comma1, + sfunc2, + comma2, + finalfunc); + } else { + sprintf(q,"define aggregate %s ( %s %s %s %s %s )\\g\n", + agginfo[i].aggname, + sfunc1, + comma1, + sfunc2, + comma2, + finalfunc); + } + + fputs(q,fout); + } + fflush(fout); +} + +/* + * dumpTables: + * write out to fout all the user-define tables + */ + +void +dumpTables(FILE* fout, TableInfo *tblinfo, int numTables, + InhInfo *inhinfo, int numInherits, + TypeInfo *tinfo, int numTypes) +{ + int i,j,k; + char q[MAXQUERYLEN]; + char **parentRels; /* list of names of parent relations */ + int numParents; + char *res; + PortalBuffer *pbuf; + int ntups; + int actual_atts; /* number of attrs in this CREATE statment */ + char *archiveMode; + + for (i=0;i<numTables;i++) { + parentRels = tblinfo[i].parentRels; + numParents = tblinfo[i].numParents; + + if (g_outputSQL) { + sprintf(q, "CREATE TABLE %s (", tblinfo[i].relname); + } else { + sprintf(q, "create %s (", tblinfo[i].relname); + } + + actual_atts = 0; + for (j=0;j<tblinfo[i].numatts;j++) { + if (tblinfo[i].inhAttrs[j] == 0) { + if (g_outputSQL) { + sprintf(q, "%s%s%s %s", + q, + (actual_atts > 0) ? ", " : "", + tblinfo[i].attnames[j], + tblinfo[i].typnames[j]); + } + else { + sprintf(q, "%s%s %s = %s", + q, + (actual_atts > 0) ? ", " : "", + tblinfo[i].attnames[j], + tblinfo[i].typnames[j]); + + } + actual_atts++; + } + } + + strcat(q,")"); + + if (numParents > 0) { + int oa = 0; /* index for the out_attnames array */ + int l; + int parentInd; + + sprintf(q, "%s inherits ( ",q); + for (k=0;k<numParents;k++){ + sprintf(q, "%s%s%s", + q, + (k>0) ? ", " : "", + parentRels[k]); + parentInd = findTableByName(tblinfo,numTables,parentRels[k]); + + /* the out_attnames are in order of the out_attnames + of the parent tables */ + for (l=0; l<tblinfo[parentInd].numatts;l++) + tblinfo[i].out_attnames[oa++] = + tblinfo[parentInd].out_attnames[l]; + } + + /* include non-inherited attrs in out_attnames also, + oa should never exceed numatts */ + for (l=0; l < tblinfo[i].numatts && oa < tblinfo[i].numatts ; l++) + if (tblinfo[i].inhAttrs[l] == 0) { + tblinfo[i].out_attnames[oa++] = + tblinfo[i].attnames[l]; + } + + strcat(q,")"); + } else { /* for non-inherited tables, out_attnames + and attnames are the same */ + tblinfo[i].out_attnames = tblinfo[i].attnames; + } + + switch(tblinfo[i].relarch[0]) { + case 'n': + archiveMode = "none"; + break; + case 'h': + archiveMode = "heavy"; + break; + case 'l': + archiveMode = "light"; + break; + default: + fprintf(stderr, "unknown archive mode\n"); + archiveMode = "none"; + break; + } + + if (g_outputSQL) { + sprintf(q, "%s archive = %s;\n", + q, + archiveMode); + } else { + sprintf(q, "%s archive = %s\\g\n", + q, + archiveMode); + } + + fputs(q,fout); + } + fflush(fout); +} + +/* + * dumpIndices: + * write out to fout all the user-define indices + */ +void +dumpIndices(FILE* fout, IndInfo* indinfo, int numIndices, + TableInfo* tblinfo, int numTables) +{ + int i,j; + int tableInd; + char *attname; /* the name of the indexed attribute */ + char *funcname; /* the name of the function to compute the index key from*/ + int indkey; + + char q[MAXQUERYLEN]; + char *res; + PortalBuffer *pbuf; + + for (i=0;i<numIndices;i++) { + tableInd = findTableByName(tblinfo, numTables, + indinfo[i].indrelname); + indkey = atoi(indinfo[i].indkey) - 1; + attname = tblinfo[tableInd].attnames[indkey]; + if (strcmp(indinfo[i].indproc,"0") == 0) { + funcname = NULL; + } else { + /* the funcname is an oid which we use to + find the name of the pg_proc. We need to do this + because getFuncs() only reads in the user-defined funcs + not all the funcs. We might not find what we want + by looking in FuncInfo**/ + sprintf(q, + "retrieve(p.proname) from p in pg_proc where p.oid = \"%s\"::oid", + indinfo[i].indproc); + res = PQexec(q); + pbuf = PQparray(res+1); + funcname = dupstr(PQgetvalue(pbuf,0, + PQfnumberGroup(pbuf,0,"proname"))); + PQclear(res+1); + } + if (g_outputSQL) { + sprintf(q,"CREATE INDEX %s on %s using %s (", + indinfo[i].indexrelname, + indinfo[i].indrelname, + indinfo[i].indamname); + } else { + sprintf(q,"define index %s on %s using %s (", + indinfo[i].indexrelname, + indinfo[i].indrelname, + indinfo[i].indamname); + + } + if (funcname) { + sprintf(q, "%s %s(%s) %s", + q,funcname, attname, indinfo[i].indclassname); + free(funcname); + } else + sprintf(q, "%s %s %s", + q,attname,indinfo[i].indclassname); + + if (g_outputSQL) { + strcat(q,");\n"); + } else + strcat(q,")\\g\n"); + + fputs(q,fout); + } + fflush(fout); +} + + +/* + * dumpClasses - + * dump the contents of all the classes. + */ +void +dumpClasses(TableInfo *tblinfo, int numTables, FILE *fout) +{ + char query[255]; + char *res; + int i,j; + + int *attrmap; /* this is an vector map of how the actual attributes + map to the corresponding output attributes. + This is necessary because of a difference between + SQL and POSTQUEL in the order of inherited attributes */ + + for(i = 0; i < numTables; i++) { + char *classname = tblinfo[i].relname; + + if (g_outputSQL) + fprintf(fout, "copy %s from stdin;\n", classname); + else + fprintf(fout, "copy %s from stdin\\g\n", classname); + + sprintf(query, "retrieve (p.all) from p in %s", classname); + res = PQexec(query); + + attrmap = (int*)malloc(tblinfo[i].numatts * sizeof(int)); + if (tblinfo[i].numParents == 0) { + /* table with no inheritance use an identity mapping */ + for (j=0;j<tblinfo[i].numatts;j++) + attrmap[j] = j; + } else { + int n = tblinfo[i].numatts; + for (j=0;j < n;j++) { + attrmap[j] = strInArray(tblinfo[i].attnames[j], + tblinfo[i].out_attnames, + n); + } + } + +/* + { + int j; + for (j=0;j<tblinfo[i].numatts;j++) { + fprintf(stderr,":%s\t",tblinfo[i].out_attnames[j]); + } + fprintf(stderr,"\n"); + } +*/ + + fflush(stdout); + fflush(stderr); + switch (*res) { + case 'P': + dumpTuples(&(res[1]), fout, attrmap); + PQclear(&(res[1])); + break; + case 'E': + case 'R': + fprintf(stderr, "Error while dumping %s\n", classname); + exit(1); + break; + } + + fprintf(fout, ".\n"); + free(attrmap); + } +} + +/* + * dumpTuples -- + * prints out the tuples in ASCII representaiton. The output is a valid + * input to COPY FROM stdin. + * + * We only need to do this for POSTGRES 4.2 databases since the + * COPY TO statement doesn't escape newlines properly. It's been fixed + * in Postgres95. + * + * the attrmap passed in tells how to map the attributes copied in to the + * attributes copied out + */ +void +dumpTuples(char *portalname, FILE *fout, int* attrmap) +{ + PortalBuffer *pbuf; + int i, j, k; + int m, n, t; + char **outVals = NULL; /* values to copy out */ + + /* Now to examine all tuples fetched. */ + pbuf = PQparray(portalname); + + n = PQntuplesGroup(pbuf,0); /* always assume only one group */ + m = PQnfieldsGroup(pbuf,0); + + if ( m > 0 ) { + /* + * Print out the tuples but only print tuples with at least + * 1 field. + */ + outVals = (char**)malloc(m * sizeof(char*)); + + for (j = 0; j < n; j++) { + for (k = 0; k < m; k++) { + outVals[attrmap[k]] = PQgetvalue(pbuf, j, k); + } + for (k = 0; k < m; k++) { + char *pval = outVals[k]; + + if (k!=0) + fputc('\t', fout); /* delimiter for attribute */ + + if (pval) { + while (*pval != '\0') { + /* escape tabs, newlines and backslashes */ + if (*pval=='\t' || *pval=='\n' || *pval=='\\') + fputc('\\', fout); + fputc(*pval, fout); + pval++; + } + } + } + fputc('\n', fout); /* delimiter for a tuple */ + } + free (outVals); + } + +} + + + +/* + * findLastBuiltInOid - + * find the last built in oid + * we do this by looking up the oid of 'template1' in pg_database, + * this is probably not foolproof but comes close +*/ + +int +findLastBuiltinOid() +{ + char *res; + PortalBuffer* pbuf; + int ntups; + int last_oid; + + res = PQexec("retrieve (d.oid) from d in pg_database where d.datname = \"template1\""); + pbuf = PQparray(res+1); + ntups = PQntuplesGroup(pbuf,0); + if (ntups != 1) { + fprintf(stderr,"pg_dump: couldn't find the template1 database. You are really hosed\nGiving up\n"); + exit(2); + } + return (atoi(PQgetvalue(pbuf,0, PQfnumberGroup(pbuf,0,"oid")))); + +} + + +/* + * checkForQuote: + * checks a string for quote characters and backslashes them + */ +char* +checkForQuote(char* s) +{ + char *r; + char c; + char *result; + + int j = 0; + + r = malloc(strlen(s)*3 + 1); /* definitely long enough */ + + while ( (c = *s) != '\0') { + + if (c == '\"') { + /* backslash the double quotes */ + if (g_outputSQL) { + r[j++] = '\\'; + c = '\''; + } else { + r[j++] = '\\'; + r[j++] = '\\'; + } + } + r[j++] = c; + s++; + } + r[j] = '\0'; + + result = dupstr(r); + free(r); + + return result; + +} diff --git a/src/bin/pg4_dump/pg_dump.h b/src/bin/pg4_dump/pg_dump.h new file mode 100644 index 00000000000..0708f671be9 --- /dev/null +++ b/src/bin/pg4_dump/pg_dump.h @@ -0,0 +1,195 @@ +/*------------------------------------------------------------------------- + * + * pg_dump.h + * header file for the pg_dump utility + * + * Copyright (c) 1994, Regents of the University of California + * + * pg_dump.h,v 1.5 1995/06/28 22:32:36 jolly Exp + * + *------------------------------------------------------------------------- + */ + + +/* The *Info data structures run-time C structures used to store + system catalog information */ + +typedef struct _typeInfo { + char* oid; + char* typowner; + char* typname; + char* typlen; + char* typprtlen; + char* typinput; + char* typoutput; + char* typreceive; + char* typsend; + char* typelem; + char* typdelim; + char* typdefault; + char* typrelid; + int passedbyvalue; + int isArray; +} TypeInfo; + +typedef struct _funcInfo { + char* oid; + char* proname; + char* proowner; + int lang; /* 1 if C, else SQL */ + int nargs; + char* argtypes[8]; /* should be derived from obj/fmgr.h instead of hardwired*/ + char* prorettype; + int retset; /* 1 if the function returns a set, 0 otherwise */ + char* prosrc; + char* probin; + int dumped; /* 1 if already dumped */ +} FuncInfo; + +typedef struct _tableInfo { + char *oid; + char *relname; + char *relarch; + int numatts; /* number of attributes */ + int *inhAttrs; /* an array of flags, one for each attribute + if the value is 1, then this attribute is + an inherited attribute */ + char **attnames; /* the attribute names */ + char **typnames; /* fill out attributes */ + int numParents; /* number of (immediate) parent supertables */ + char **parentRels; /* names of parent relations, NULL + if numParents == 0 */ + char **out_attnames; /* the attribute names, in the order they would + be in, when the table is created in the + target query language. + this is needed because the SQL tables will + not have the same order of attributes as + the POSTQUEL tables */ + +} TableInfo; + +typedef struct _inhInfo { + char *oid; + char *inhrel; + char *inhparent; +} InhInfo; + +typedef struct _indInfo { + char *indexrelname; /* name of the secondary index class */ + char *indrelname; /* name of the indexed heap class */ + char *indamname; /* name of the access method (e.g. btree, rtree, etc.) */ + char *indproc; /* oid of the function to compute the index, 0 if none*/ + char *indkey; /* attribute number of the key attribute */ + char *indclassname; /* name of the opclass of the key */ +} IndInfo; + +typedef struct _aggInfo { + char *oid; + char *aggname; + char *aggtransfn1; + char *aggtransfn2; + char *aggfinalfn; + char *aggtranstype1; + char *aggbasetype; + char *aggtranstype2; + char *agginitval1; + char *agginitval2; +} AggInfo; + +typedef struct _oprInfo { + char *oid; + char *oprname; + char *oprkind; /* "b" = binary, "l" = left unary, "r" = right unary */ + char *oprcode; /* operator function name */ + char *oprleft; /* left operand type */ + char *oprright; /* right operand type */ + char *oprcom; /* oid of the commutator operator */ + char *oprnegate; /* oid of the negator operator */ + char *oprrest; /* name of the function to calculate operator restriction + selectivity */ + char *oprjoin; /* name of the function to calculate operator join + selectivity */ + char *oprcanhash; /* can we use hash join strategy ? */ + char *oprlsortop; /* oid's of the left and right sort operators */ + char *oprrsortop; +} OprInfo; + + +/* global decls */ +extern int g_verbose; /* verbose flag */ +extern int g_last_builtin_oid; /* value of the last builtin oid */ +extern FILE *g_fout; /* the script file */ + +/* placeholders for comment starting and ending delimiters */ +extern char g_comment_start[10]; +extern char g_comment_end[10]; + +extern char g_opaque_type[10]; /* name for the opaque type */ + +/* pg_dump is really two programs in one + one version works with postgres v4r2 + and the other works with postgres95 + the common routines are declared here + +/* + * common utility functions +*/ + +extern TableInfo* dumpSchema(FILE* fout, int *numTablesPtr); + +extern char* findTypeByOid(TypeInfo* tinfo, int numTypes, char* oid); +extern char* findOprByOid(OprInfo *oprinfo, int numOprs, char *oid); +extern int findFuncByName(FuncInfo* finfo, int numFuncs, char* name); +extern char** findParentsByOid(TableInfo* tbinfo, int numTables, + InhInfo* inhinfo, int numInherits, + char *oid, + int *numParents); +extern int findTableByName(TableInfo *tbinfo, int numTables, char *relname); +extern int findTableByOid(TableInfo *tbinfo, int numTables, char *oid); +extern void flagInhAttrs(TableInfo* tbinfo, int numTables, + InhInfo* inhinfo, int numInherits); + +extern void check_conn_and_db(); +extern char* dupstr(char *s); +extern int strInArray(char* pattern, char** arr, int arr_size); +extern void parseArgTypes(char **argtypes, char* str); +extern int isArchiveName(char*); + +/* + * version specific routines + */ +extern TypeInfo* getTypes(int *numTypes); +extern FuncInfo* getFuncs(int *numFuncs); +extern AggInfo* getAggregates(int *numAggregates); +extern OprInfo* getOperators(int *numOperators); +extern TableInfo* getTables(int *numTables); +extern InhInfo* getInherits(int *numInherits); +extern void getTableAttrs(TableInfo* tbinfo, int numTables); +extern IndInfo* getIndices(int *numIndices); +extern void dumpTypes(FILE* fout, FuncInfo* finfo, int numFuncs, + TypeInfo* tinfo, int numTypes); +extern void dumpFuncs(FILE* fout, FuncInfo* finfo, int numFuncs, + TypeInfo *tinfo, int numTypes); +extern void dumpAggs(FILE* fout, AggInfo* agginfo, int numAggregates, + TypeInfo *tinfo, int numTypes); +extern void dumpOprs(FILE* fout, OprInfo* agginfo, int numOperators, + TypeInfo *tinfo, int numTypes); +extern void dumpOneFunc(FILE* fout, FuncInfo* finfo, int i, + TypeInfo *tinfo, int numTypes); +extern void dumpTables(FILE* fout, TableInfo* tbinfo, int numTables, + InhInfo *inhinfo, int numInherits, + TypeInfo *tinfo, int numTypes); +extern void dumpIndices(FILE* fout, IndInfo* indinfo, int numIndices, + TableInfo* tbinfo, int numTables); + +extern void dumpClasses(TableInfo *tbinfo, int numTables, FILE *fout); +extern void dumpTuples(char *portalname, FILE *fout, int *attrmap); +extern char* checkForQuote(char* s); +extern int findLastBuiltinOid(); + + +/* largest query string size */ +#define MAXQUERYLEN 5000 + +/* these voodoo constants are from the backend */ +#define C_PROLANG_OID 13 diff --git a/src/bin/pg_dump/Makefile b/src/bin/pg_dump/Makefile new file mode 100644 index 00000000000..5512f249fa4 --- /dev/null +++ b/src/bin/pg_dump/Makefile @@ -0,0 +1,23 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/pg_dump +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/pg_dump/Makefile,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ +# +#------------------------------------------------------------------------- + +PROG= pg_dump + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +SRCS= pg_dump.c common.c + +include $(MKDIR)/postgres.prog.mk + diff --git a/src/bin/pg_dump/README b/src/bin/pg_dump/README new file mode 100644 index 00000000000..17c433c8981 --- /dev/null +++ b/src/bin/pg_dump/README @@ -0,0 +1,73 @@ +pg_dump is a utility for dumping out a postgres database into a script +file containing query commands. The script files are in a ASCII +format and can be used to reconstruct the database, even on other +machines and other architectures. pg_dump will produce the queries +necessary to re-generate all user-defined types, functions, tables, +indices, aggregates, and operators. In addition, all the data is +copied out in ASCII format so that it can be readily copied in again. + + +To build: + + % gmake clean install + +This version of the program will read in your postgres95 database and +output the schema and the data tuples in SQL. The dumps are useful +for moving from one postgres95 installation to another. + + +How to use pg_dump: +------------------- + +The command line options are fairly self explanatory. Use -help to +see the command line options. recommend using -v to get +more verbose descriptions of what pg_dump is doing. + +After running pg_dump, one should examine the output script file for any +warnings, especially in light of the limitations listed below. + +A typical use of pg_dump: + + % pg_dump -v -f oldDB.dump oldDB + % createdb newDB + % psql newDB < oldDB.dump + + +Caveats and limitations: +------------------------ + +pg_dump has a few limitations. The limitations mostly stem from +difficulty in extracting certain meta-information from the system +catalogs. + + rules and views: + pg_dump does not understand user-defined rules and views and + will fail to dump them properly. (This is due to the fact that + rules are stored as plans in the catalogs and not textually) + + partial indices: + pg_dump does not understand partial indices. (The reason is + the same as above. Partial index predicates are stored as plans) + + large objects: + pg_dump does not handle large objects. Large + objects are ignored and must be dealt with manually. + + oid preservation: + pg_dump does not preserve oid's while dumping. If you have + stored oid's explicitly in tables in user-defined attributes, + and are using them as keys, then the output scripts will not + regenerate your database correctly. + +pg_dump requires postgres95 beta0.03 or later. + +Bug-reporting +-------------- + +If you should find a problem with pg_dump, it is very important that +you provide a (small) sample database which illustrates the problem. +Please send bugs, questions, and feedback to the + postgres95@postgres95.vnet.net + + + diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c new file mode 100644 index 00000000000..96ea7f72951 --- /dev/null +++ b/src/bin/pg_dump/common.c @@ -0,0 +1,397 @@ +/*------------------------------------------------------------------------- + * + * common.c-- + * common routines between pg_dump and pg4_dump + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <sys/param.h> /* for MAXHOSTNAMELEN on most */ +#ifdef PORTNAME_sparc_solaris +#include <netdb.h> /* for MAXHOSTNAMELEN on some */ +#endif + +#include "postgres.h" +#include "libpq-fe.h" + +#include "pg_dump.h" + +/* dupstr : copies a string, while allocating space for it. + the CALLER is responsible for freeing the space + returns NULL if the argument is NULL*/ +char* +dupstr(char *s) +{ + char* result; + + if (s == NULL) + return NULL; + + result = (char*)malloc(strlen(s)+1); + strcpy(result, s); + return result; +} + + +/* + * findTypeByOid + * given an oid of a type, return its typename + * + * if oid is "0", return "opaque" -- this is a special case + * + * NOTE: should hash this, but just do linear search for now + */ + +char* +findTypeByOid(TypeInfo* tinfo, int numTypes, char* oid) +{ + int i; + + if (strcmp(oid, "0") == 0) return g_opaque_type; + + for (i=0;i<numTypes;i++) { + if (strcmp(tinfo[i].oid, oid) == 0) + return tinfo[i].typname; + } + + /* should never get here */ + fprintf(stderr,"failed sanity check, type with oid %s was not found\n", + oid); + exit(2); +} + +/* + * findOprByOid + * given the oid of an operator, return the name of the operator + * + * + * NOTE: should hash this, but just do linear search for now + * + */ +char* +findOprByOid(OprInfo *oprinfo, int numOprs, char *oid) +{ + int i; + for (i=0;i<numOprs;i++) { + if (strcmp(oprinfo[i].oid, oid) == 0) + return oprinfo[i].oprname; + } + + /* should never get here */ + fprintf(stderr,"failed sanity check, opr with oid %s was not found\n", + oid); + exit(2); +} + + +/* + * findParentsByOid -- + * given the oid of a class, return the names of its parent classes + * and assign the number of parents to the last argument. + * + * + * returns NULL if none + */ + +char** +findParentsByOid(TableInfo* tblinfo, int numTables, + InhInfo* inhinfo, int numInherits, char *oid, + int *numParentsPtr) +{ + int i,j; + int parentInd; + char** result; + int numParents; + + numParents = 0; + for (i=0;i<numInherits;i++) { + if ( strcmp(inhinfo[i].inhrel, oid) == 0) { + numParents++; + } + } + + *numParentsPtr = numParents; + + if (numParents > 0) { + result = (char**)malloc(sizeof(char*) * numParents); + j = 0; + for (i=0;i<numInherits;i++) { + if ( strcmp(inhinfo[i].inhrel, oid) == 0) { + parentInd = findTableByOid(tblinfo, numTables, + inhinfo[i].inhparent); + result[j++] = tblinfo[parentInd].relname; + } + } + return result; + } + else + return NULL; +} + +/* + * parseArgTypes + * parse a string of eight numbers delimited by spaces + * into a character array + */ + +void +parseArgTypes(char **argtypes, char* str) +{ + int j, argNum; + char temp[100]; + char s; + + argNum = 0; + j = 0; + while ( (s = *str) != '\0') { + if (s == ' ') { + temp[j] = '\0'; + argtypes[argNum] = dupstr(temp); + argNum++; + j = 0; + } else { + temp[j] = s; + j++; + } + str++; + } + if (j != 0) { + temp[j] = '\0'; + argtypes[argNum] = dupstr(temp); + } + +} + + +/* + * strInArray: + * takes in a string and a string array and the number of elements in the + * string array. + * returns the index if the string is somewhere in the array, -1 otherwise + * + */ + +int +strInArray(char* pattern, char** arr, int arr_size) +{ + int i; + for (i=0;i<arr_size;i++) { + if (strcmp(pattern, arr[i]) == 0) + return i; + } + return -1; +} + +/* + * dumpSchema: + * we have a valid connection, we are now going to dump the schema + * into the file + * + */ + +TableInfo * +dumpSchema(FILE *fout, int *numTablesPtr) +{ + int numTypes; + int numFuncs; + int numTables; + int numInherits; + int numIndices; + int numAggregates; + int numOperators; + TypeInfo *tinfo; + FuncInfo *finfo; + AggInfo *agginfo; + TableInfo *tblinfo; + InhInfo *inhinfo; + IndInfo *indinfo; + OprInfo *oprinfo; + +if (g_verbose) fprintf(stderr,"%s reading user-defined types %s\n", + g_comment_start, g_comment_end); + tinfo = getTypes(&numTypes); + +if (g_verbose) fprintf(stderr,"%s reading user-defined functions %s\n", + g_comment_start, g_comment_end); + finfo = getFuncs(&numFuncs); + +if (g_verbose) fprintf(stderr,"%s reading user-defined aggregates %s\n", + g_comment_start, g_comment_end); + agginfo = getAggregates(&numAggregates); + +if (g_verbose) fprintf(stderr,"%s reading user-defined operators %s\n", + g_comment_start, g_comment_end); + oprinfo = getOperators(&numOperators); + +if (g_verbose) fprintf(stderr,"%s reading user-defined tables %s\n", + g_comment_start, g_comment_end); + tblinfo = getTables(&numTables); + +if (g_verbose) fprintf(stderr,"%s reading table inheritance information %s\n", + g_comment_start, g_comment_end); + inhinfo = getInherits(&numInherits); + +if (g_verbose) fprintf(stderr, "%s finding the attribute names and types for each table %s\n", + g_comment_start, g_comment_end); + getTableAttrs(tblinfo, numTables); + +if (g_verbose) fprintf(stderr, "%s flagging inherited attributes in subtables %s\n", + g_comment_start, g_comment_end); + flagInhAttrs(tblinfo, numTables, inhinfo, numInherits); + +if (g_verbose) fprintf(stderr,"%s reading indices information %s\n", + g_comment_start, g_comment_end); + indinfo = getIndices(&numIndices); + +if (g_verbose) fprintf(stderr,"%s dumping out user-defined types %s\n", + g_comment_start, g_comment_end); + dumpTypes(fout, finfo, numFuncs, tinfo, numTypes); + +if (g_verbose) fprintf(stderr,"%s dumping out tables %s\n", + g_comment_start, g_comment_end); + dumpTables(fout, tblinfo, numTables, inhinfo, numInherits, + tinfo, numTypes); + +if (g_verbose) fprintf(stderr,"%s dumping out user-defined functions %s\n", + g_comment_start, g_comment_end); + dumpFuncs(fout, finfo, numFuncs, tinfo, numTypes); + +if (g_verbose) fprintf(stderr,"%s dumping out user-defined functions %s\n", + g_comment_start, g_comment_end); + dumpAggs(fout, agginfo, numAggregates, tinfo, numTypes); + +if (g_verbose) fprintf(stderr,"%s dumping out user-defined operators %s\n", + g_comment_start, g_comment_end); + dumpOprs(fout, oprinfo, numOperators, tinfo, numTypes); + +if (g_verbose) fprintf(stderr,"%s dumping out indices %s\n", + g_comment_start, g_comment_end); + dumpIndices(fout, indinfo, numIndices, tblinfo, numTables); + + *numTablesPtr = numTables; + return tblinfo; +} + + +/* flagInhAttrs - + * for each table in tblinfo, flag its inherited attributes + * so when we dump the table out, we don't dump out the inherited attributes + * + * initializes the parentRels field of each table + * + * modifies tblinfo + * + */ +void +flagInhAttrs(TableInfo* tblinfo, int numTables, + InhInfo* inhinfo, int numInherits) +{ + int i,j,k; + int parentInd; + + /* we go backwards because the tables in tblinfo are in OID + order, meaning the subtables are after the parent tables + we flag inherited attributes from child tables first */ + for (i = numTables-1; i >= 0; i--) { + tblinfo[i].parentRels = findParentsByOid(tblinfo, numTables, + inhinfo, numInherits, + tblinfo[i].oid, + &tblinfo[i].numParents); + for (k=0;k<tblinfo[i].numParents;k++) { + parentInd = findTableByName(tblinfo, numTables, + tblinfo[i].parentRels[k]); + for (j=0;j<tblinfo[i].numatts;j++) { + if (strInArray(tblinfo[i].attnames[j], + tblinfo[parentInd].attnames, + tblinfo[parentInd].numatts) != -1) { + tblinfo[i].inhAttrs[j] = 1; + } + } + } + } +} + + +/* + * findTableByName + * finds the index (in tblinfo) of the table with the given relname + * returns -1 if not found + * + * NOTE: should hash this, but just do linear search for now + */ + +int +findTableByName(TableInfo* tblinfo, int numTables, char* relname) +{ + int i; + for (i=0;i<numTables;i++) { + if (strcmp(tblinfo[i].relname, relname) == 0) + return i; + } + return -1; +} + +/* + * findTableByOid + * finds the index (in tblinfo) of the table with the given oid + * returns -1 if not found + * + * NOTE: should hash this, but just do linear search for now + */ + +int +findTableByOid(TableInfo* tblinfo, int numTables, char* oid) +{ + int i; + for (i=0;i<numTables;i++) { + if (strcmp(tblinfo[i].oid, oid) == 0) + return i; + } + return -1; +} + + +/* + * findFuncByName + * finds the index (in finfo) of the function with the given name + * returns -1 if not found + * + * NOTE: should hash this, but just do linear search for now + */ + +int +findFuncByName(FuncInfo* finfo, int numFuncs, char* name) +{ + int i; + for (i=0;i<numFuncs;i++) { + if (strcmp(finfo[i].proname, name) == 0) + return i; + } + return -1; +} + +/* + * isArchiveName + * + * returns true if the relation name is an archive name, false otherwise + */ +int +isArchiveName(char* relname) +{ + return (strlen(relname) > 1 && relname[1] == ','); +} + + + + + + diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c new file mode 100644 index 00000000000..9ef8f8f35e8 --- /dev/null +++ b/src/bin/pg_dump/pg_dump.c @@ -0,0 +1,1443 @@ +/*------------------------------------------------------------------------- + * + * pg_dump.c-- + * pg_dump is an utility for dumping out a postgres database + * into a script file. + * + * pg_dump will read the system catalogs in a database and + * dump out a script that reproduces + * the schema of the database in terms of + * user-defined types + * user-defined functions + * tables + * indices + * aggregates + * operators + * + * the output script is SQL that is understood by Postgres95 + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/param.h> /* for MAXHOSTNAMELEN on most */ +#ifdef PORTNAME_sparc_solaris +#include <netdb.h> /* for MAXHOSTNAMELEN on some */ +#endif + +#include "postgres.h" +#include "libpq-fe.h" + +#include "pg_dump.h" + +extern char *optarg; +extern int optind, opterr; + +/* global decls */ +int g_verbose; /* verbose flag */ +int g_last_builtin_oid; /* value of the last builtin oid */ +FILE *g_fout; /* the script file */ +PGconn *g_conn; /* the database connection */ + +char g_opaque_type[10]; /* name for the opaque type */ + +/* placeholders for the delimiters for comments */ +char g_comment_start[10]; +char g_comment_end[10]; + + +static void +usage(char* progname) +{ + fprintf(stderr, "usage: %s [options] [dbname]\n",progname); + fprintf(stderr, "\t -f filename \t\t script output filename\n"); + fprintf(stderr, "\t -H hostname \t\t server host name\n"); + fprintf(stderr, "\t -p port \t\t server port number\n"); + fprintf(stderr, "\t -v \t\t verbose\n"); + fprintf(stderr, "\t -S \t\t dump out only the schema, no data\n"); + fprintf(stderr, "\n if dbname is not supplied, then the DATABASE environment name is used\n"); + fprintf(stderr, "\n"); + + fprintf(stderr, "\tpg_dump dumps out postgres databases and produces a script file\n"); + fprintf(stderr, "\tof SQL commands to regenerate the schema\n"); + fprintf(stderr, "\tThe SQL output is designed for import into Postgres95\n"); + exit(1); +} + +void +exit_nicely(PGconn* conn) +{ + PQfinish(conn); + exit(1); +} + + +void +main(int argc, char** argv) +{ + int c; + char* progname; + char* filename; + char* dbname; + int schemaOnly; + char *pghost = NULL; + char *pgport = NULL; + + TableInfo *tblinfo; + int numTables; + + dbname = NULL; + filename = NULL; + g_verbose = 0; + + strcpy(g_comment_start,"-- "); + g_comment_end[0] = '\0'; + strcpy(g_opaque_type, "opaque"); + + schemaOnly = 0; + + progname = *argv; + + while ((c = getopt(argc, argv,"f:H:p:vSD")) != EOF) { + switch(c) { + case 'f': /* output file name */ + filename = optarg; + break; + case 'H' : /* server host */ + pghost = optarg; + break; + case 'p' : /* server port */ + pgport = optarg; + break; + case 'v': /* verbose */ + g_verbose = 1; + break; + case 'S': /* dump schema only */ + schemaOnly = 1; + break; + default: + usage(progname); + break; + } + } + + /* open the output file */ + if (filename == NULL) { + g_fout = stdout; + } else { + g_fout = fopen(filename, "w"); + if (g_fout == NULL) { + fprintf(stderr,"%s: could not open output file named %s for writing\n", + progname, filename); + exit(2); + } + } + + /* find database */ + if (!(dbname = argv[optind]) && + !(dbname = getenv("DATABASE")) ) { + fprintf(stderr, "%s: no database name specified\n",progname); + exit (2); + } + + g_conn = PQsetdb(pghost, pgport, NULL, NULL, dbname); + /* check to see that the backend connection was successfully made */ + if (PQstatus(g_conn) == CONNECTION_BAD) { + fprintf(stderr,"Connection to database '%s' failed.\n", dbname); + fprintf(stderr,"%s",PQerrorMessage(g_conn)); + exit_nicely(g_conn); + } + + g_last_builtin_oid = findLastBuiltinOid(); + +if (g_verbose) + fprintf(stderr, "%s last builtin oid is %d %s\n", + g_comment_start, g_last_builtin_oid, g_comment_end); + + tblinfo = dumpSchema(g_fout, &numTables); + + if (!schemaOnly) { + +if (g_verbose) fprintf(stderr,"%s dumping out the contents of each table %s\n", + g_comment_start, g_comment_end); + + dumpClasses(tblinfo, numTables, g_fout); + } + + fflush(g_fout); + fclose(g_fout); + + PQfinish(g_conn); + exit(0); +} + + +/* + * getTypes: + * read all base types in the system catalogs and return them in the + * TypeInfo* structure + * + * numTypes is set to the number of types read in + * + */ +TypeInfo* +getTypes(int *numTypes) +{ + PGresult *res; + int ntups; + int i; + char query[MAXQUERYLEN]; + TypeInfo *tinfo; + + int i_oid; + int i_typowner; + int i_typname; + int i_typlen; + int i_typprtlen; + int i_typinput; + int i_typoutput; + int i_typreceive; + int i_typsend; + int i_typelem; + int i_typdelim; + int i_typdefault; + int i_typrelid; + int i_typbyval; + + res = PQexec(g_conn, "begin"); + if (!res || + PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr,"BEGIN command failed\n"); + exit_nicely(g_conn); + } + PQclear(res); + + /* find all base types */ + /* we include even the built-in types + because those may be used as array elements by user-defined types */ + /* we filter out the built-in types when + we dump out the types */ + + sprintf(query, "SELECT oid, typowner,typname, typlen, typprtlen, typinput, typoutput, typreceive, typsend, typelem, typdelim, typdefault, typrelid,typbyval from pg_type"); + + res = PQexec(g_conn,query); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr,"getTypes(): SELECT failed"); + exit_nicely(g_conn); + } + + ntups = PQntuples(res); + + tinfo = (TypeInfo*)malloc(ntups * sizeof(TypeInfo)); + + i_oid = PQfnumber(res,"oid"); + i_typowner = PQfnumber(res,"typowner"); + i_typname = PQfnumber(res,"typname"); + i_typlen = PQfnumber(res,"typlen"); + i_typprtlen = PQfnumber(res,"typprtlen"); + i_typinput = PQfnumber(res,"typinput"); + i_typoutput = PQfnumber(res,"typoutput"); + i_typreceive = PQfnumber(res,"typreceive"); + i_typsend = PQfnumber(res,"typsend"); + i_typelem = PQfnumber(res,"typelem"); + i_typdelim = PQfnumber(res,"typdelim"); + i_typdefault = PQfnumber(res,"typdefault"); + i_typrelid = PQfnumber(res,"typrelid"); + i_typbyval = PQfnumber(res,"typbyval"); + + for (i=0;i<ntups;i++) { + tinfo[i].oid = dupstr(PQgetvalue(res,i,i_oid)); + tinfo[i].typowner = dupstr(PQgetvalue(res,i,i_typowner)); + tinfo[i].typname = dupstr(PQgetvalue(res,i,i_typname)); + tinfo[i].typlen = dupstr(PQgetvalue(res,i,i_typlen)); + tinfo[i].typprtlen = dupstr(PQgetvalue(res,i,i_typprtlen)); + tinfo[i].typinput = dupstr(PQgetvalue(res,i,i_typinput)); + tinfo[i].typoutput = dupstr(PQgetvalue(res,i,i_typoutput)); + tinfo[i].typreceive = dupstr(PQgetvalue(res,i,i_typreceive)); + tinfo[i].typsend = dupstr(PQgetvalue(res,i,i_typsend)); + tinfo[i].typelem = dupstr(PQgetvalue(res,i,i_typelem)); + tinfo[i].typdelim = dupstr(PQgetvalue(res,i,i_typdelim)); + tinfo[i].typdefault = dupstr(PQgetvalue(res,i,i_typdefault)); + tinfo[i].typrelid = dupstr(PQgetvalue(res,i,i_typrelid)); + + if (strcmp(PQgetvalue(res,i,i_typbyval), "f") == 0) + tinfo[i].passedbyvalue = 0; + else + tinfo[i].passedbyvalue = 1; + + /* check for user-defined array types, + omit system generated ones */ + if ( (strcmp(tinfo[i].typelem, "0") != 0) && + tinfo[i].typname[0] != '_') + tinfo[i].isArray = 1; + else + tinfo[i].isArray = 0; + } + + *numTypes = ntups; + + PQclear(res); + + res = PQexec(g_conn,"end"); + PQclear(res); + + return tinfo; +} + +/* + * getOperators: + * read all operators in the system catalogs and return them in the + * OprInfo* structure + * + * numOprs is set to the number of operators read in + * + * + */ +OprInfo* +getOperators(int *numOprs) +{ + PGresult *res; + int ntups; + int i; + char query[MAXQUERYLEN]; + + OprInfo* oprinfo; + + int i_oid; + int i_oprname; + int i_oprkind; + int i_oprcode; + int i_oprleft; + int i_oprright; + int i_oprcom; + int i_oprnegate; + int i_oprrest; + int i_oprjoin; + int i_oprcanhash; + int i_oprlsortop; + int i_oprrsortop; + + /* find all operators, including builtin operators, + filter out system-defined operators at dump-out time */ + res = PQexec(g_conn, "begin"); + if (!res || + PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr,"BEGIN command failed\n"); + exit_nicely(g_conn); + } + PQclear(res); + + sprintf(query, "SELECT oid, oprname, oprkind, oprcode, oprleft, oprright, oprcom, oprnegate, oprrest, oprjoin, oprcanhash, oprlsortop, oprrsortop from pg_operator"); + + res = PQexec(g_conn, query); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr,"getOperators(): SELECT failed"); + exit_nicely(g_conn); + } + + ntups = PQntuples(res); + *numOprs = ntups; + + oprinfo = (OprInfo*)malloc(ntups * sizeof(OprInfo)); + + i_oid = PQfnumber(res,"oid"); + i_oprname = PQfnumber(res,"oprname"); + i_oprkind = PQfnumber(res,"oprkind"); + i_oprcode = PQfnumber(res,"oprcode"); + i_oprleft = PQfnumber(res,"oprleft"); + i_oprright = PQfnumber(res,"oprright"); + i_oprcom = PQfnumber(res,"oprcom"); + i_oprnegate = PQfnumber(res,"oprnegate"); + i_oprrest = PQfnumber(res,"oprrest"); + i_oprjoin = PQfnumber(res,"oprjoin"); + i_oprcanhash = PQfnumber(res,"oprcanhash"); + i_oprlsortop = PQfnumber(res,"oprlsortop"); + i_oprrsortop = PQfnumber(res,"oprrsortop"); + + for (i=0;i<ntups;i++) { + oprinfo[i].oid = dupstr(PQgetvalue(res,i,i_oid)); + oprinfo[i].oprname = dupstr(PQgetvalue(res,i,i_oprname)); + oprinfo[i].oprkind = dupstr(PQgetvalue(res,i,i_oprkind)); + oprinfo[i].oprcode = dupstr(PQgetvalue(res,i,i_oprcode)); + oprinfo[i].oprleft = dupstr(PQgetvalue(res,i,i_oprleft)); + oprinfo[i].oprright = dupstr(PQgetvalue(res,i,i_oprright)); + oprinfo[i].oprcom = dupstr(PQgetvalue(res,i,i_oprcom)); + oprinfo[i].oprnegate = dupstr(PQgetvalue(res,i,i_oprnegate)); + oprinfo[i].oprrest = dupstr(PQgetvalue(res,i,i_oprrest)); + oprinfo[i].oprjoin = dupstr(PQgetvalue(res,i,i_oprjoin)); + oprinfo[i].oprcanhash = dupstr(PQgetvalue(res,i,i_oprcanhash)); + oprinfo[i].oprlsortop = dupstr(PQgetvalue(res,i,i_oprlsortop)); + oprinfo[i].oprrsortop = dupstr(PQgetvalue(res,i,i_oprrsortop)); + } + + PQclear(res); + res = PQexec(g_conn, "end"); + PQclear(res); + + return oprinfo; +} + + +/* + * getAggregates: + * read all the user-defined aggregates in the system catalogs and + * return them in the AggInfo* structure + * + * numAggs is set to the number of aggregates read in + * + * + */ +AggInfo* +getAggregates(int *numAggs) +{ + PGresult* res; + int ntups; + int i; + char query[MAXQUERYLEN]; + AggInfo *agginfo; + + int i_oid; + int i_aggname; + int i_aggtransfn1; + int i_aggtransfn2; + int i_aggfinalfn; + int i_aggtranstype1; + int i_aggbasetype; + int i_aggtranstype2; + int i_agginitval1; + int i_agginitval2; + + /* find all user-defined aggregates */ + + res = PQexec(g_conn, "begin"); + if (!res || + PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr,"BEGIN command failed\n"); + exit_nicely(g_conn); + } + PQclear(res); + + sprintf(query, + "SELECT oid, aggname, aggtransfn1, aggtransfn2, aggfinalfn, aggtranstype1, aggbasetype, aggtranstype2, agginitval1, agginitval2 from pg_aggregate;"); + + res = PQexec(g_conn, query); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr,"getAggregates(): SELECT failed"); + exit_nicely(g_conn); + } + + ntups = PQntuples(res); + *numAggs = ntups; + + agginfo = (AggInfo*)malloc(ntups * sizeof(AggInfo)); + + i_oid = PQfnumber(res,"oid"); + i_aggname = PQfnumber(res,"aggname"); + i_aggtransfn1 = PQfnumber(res,"aggtransfn1"); + i_aggtransfn2 = PQfnumber(res,"aggtransfn2"); + i_aggfinalfn = PQfnumber(res,"aggfinalfn"); + i_aggtranstype1 = PQfnumber(res,"aggtranstype1"); + i_aggbasetype = PQfnumber(res,"aggbasetype"); + i_aggtranstype2 = PQfnumber(res,"aggtranstype2"); + i_agginitval1 = PQfnumber(res,"agginitval1"); + i_agginitval2 = PQfnumber(res,"agginitval2"); + + for (i=0;i<ntups;i++) { + agginfo[i].oid = dupstr(PQgetvalue(res,i,i_oid)); + agginfo[i].aggname = dupstr(PQgetvalue(res,i,i_aggname)); + agginfo[i].aggtransfn1 = dupstr(PQgetvalue(res,i,i_aggtransfn1)); + agginfo[i].aggtransfn2 = dupstr(PQgetvalue(res,i,i_aggtransfn2)); + agginfo[i].aggfinalfn = dupstr(PQgetvalue(res,i,i_aggfinalfn)); + agginfo[i].aggtranstype1 = dupstr(PQgetvalue(res,i,i_aggtranstype1)); + agginfo[i].aggbasetype = dupstr(PQgetvalue(res,i,i_aggbasetype)); + agginfo[i].aggtranstype2 = dupstr(PQgetvalue(res,i,i_aggtranstype2)); + agginfo[i].agginitval1 = dupstr(PQgetvalue(res,i,i_agginitval1)); + agginfo[i].agginitval2 = dupstr(PQgetvalue(res,i,i_agginitval2)); + } + + PQclear(res); + + res = PQexec(g_conn, "end"); + PQclear(res); + return agginfo; +} + +/* + * getFuncs: + * read all the user-defined functions in the system catalogs and + * return them in the FuncInfo* structure + * + * numFuncs is set to the number of functions read in + * + * + */ +FuncInfo* +getFuncs(int *numFuncs) +{ + PGresult *res; + int ntups; + int i; + char query[MAXQUERYLEN]; + FuncInfo *finfo; + + int i_oid; + int i_proname; + int i_proowner; + int i_prolang; + int i_pronargs; + int i_proargtypes; + int i_prorettype; + int i_proretset; + int i_prosrc; + int i_probin; + + /* find all user-defined funcs */ + + res = PQexec(g_conn, "begin"); + if (!res || + PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr,"BEGIN command failed\n"); + exit_nicely(g_conn); + } + PQclear(res); + + sprintf(query, + "SELECT oid, proname, proowner, prolang, pronargs, prorettype, proretset, proargtypes, prosrc, probin from pg_proc where oid > '%d'::oid", + g_last_builtin_oid); + + res = PQexec(g_conn, query); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr,"getFuncs(): SELECT failed"); + exit_nicely(g_conn); + } + + ntups = PQntuples(res); + + *numFuncs = ntups; + + finfo = (FuncInfo*)malloc(ntups * sizeof(FuncInfo)); + + i_oid = PQfnumber(res,"oid"); + i_proname = PQfnumber(res,"proname"); + i_proowner = PQfnumber(res,"proowner"); + i_prolang = PQfnumber(res,"prolang"); + i_pronargs = PQfnumber(res,"pronargs"); + i_proargtypes = PQfnumber(res,"proargtypes"); + i_prorettype = PQfnumber(res,"prorettype"); + i_proretset = PQfnumber(res,"proretset"); + i_prosrc = PQfnumber(res,"prosrc"); + i_probin = PQfnumber(res,"probin"); + + for (i=0;i<ntups;i++) { + finfo[i].oid = dupstr(PQgetvalue(res,i,i_oid)); + finfo[i].proname = dupstr(PQgetvalue(res,i,i_proname)); + finfo[i].proowner = dupstr(PQgetvalue(res,i,i_proowner)); + + finfo[i].prosrc = checkForQuote(PQgetvalue(res,i,i_prosrc)); + finfo[i].probin = dupstr(PQgetvalue(res,i,i_probin)); + + finfo[i].prorettype = dupstr(PQgetvalue(res,i,i_prorettype)); + finfo[i].retset = (strcmp(PQgetvalue(res,i,i_proretset),"t") == 0); + finfo[i].nargs = atoi(PQgetvalue(res,i,i_pronargs)); + finfo[i].lang = (atoi(PQgetvalue(res,i,i_prolang)) == C_PROLANG_OID); + + parseArgTypes(finfo[i].argtypes, PQgetvalue(res,i,i_proargtypes)); + + finfo[i].dumped = 0; + } + + PQclear(res); + res = PQexec(g_conn, "end"); + PQclear(res); + + return finfo; + +} + +/* + * getTables + * read all the user-defined tables (no indices, no catalogs) + * in the system catalogs return them in the TableInfo* structure + * + * numTables is set to the number of tables read in + * + * + */ +TableInfo* +getTables(int *numTables) +{ + PGresult *res; + int ntups; + int i; + char query[MAXQUERYLEN]; + TableInfo *tblinfo; + + int i_oid; + int i_relname; + int i_relarch; + + /* find all the user-defined tables (no indices and no catalogs), + ordering by oid is important so that we always process the parent + tables before the child tables when traversing the tblinfo* + + we ignore tables that start with Xinv */ + + res = PQexec(g_conn, "begin"); + if (!res || + PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr,"BEGIN command failed\n"); + exit_nicely(g_conn); + } + PQclear(res); + + sprintf(query, + "SELECT oid, relname, relarch from pg_class where relkind = 'r' and relname !~ '^pg_' and relname !~ '^Xinv' order by oid;"); + + res = PQexec(g_conn, query); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr,"getTables(): SELECT failed"); + exit_nicely(g_conn); + } + + ntups = PQntuples(res); + + *numTables = ntups; + + tblinfo = (TableInfo*)malloc(ntups * sizeof(TableInfo)); + + i_oid = PQfnumber(res,"oid"); + i_relname = PQfnumber(res,"relname"); + i_relarch = PQfnumber(res,"relarch"); + + for (i=0;i<ntups;i++) { + tblinfo[i].oid = dupstr(PQgetvalue(res,i,i_oid)); + tblinfo[i].relname = dupstr(PQgetvalue(res,i,i_relname)); + tblinfo[i].relarch = dupstr(PQgetvalue(res,i,i_relarch)); + } + + PQclear(res); + res = PQexec(g_conn, "end"); + PQclear(res); + + return tblinfo; + +} + +/* + * getInherits + * read all the inheritance information + * from the system catalogs return them in the InhInfo* structure + * + * numInherits is set to the number of tables read in + * + * + */ +InhInfo* +getInherits(int *numInherits) +{ + PGresult *res; + int ntups; + int i; + char query[MAXQUERYLEN]; + InhInfo *inhinfo; + + int i_inhrel; + int i_inhparent; + + /* find all the inheritance information */ + res = PQexec(g_conn, "begin"); + if (!res || + PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr,"BEGIN command failed\n"); + exit_nicely(g_conn); + } + PQclear(res); + + sprintf(query, "SELECT inhrel, inhparent from pg_inherits"); + + res = PQexec(g_conn, query); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr,"getInherits(): SELECT failed"); + exit_nicely(g_conn); + } + + ntups = PQntuples(res); + + *numInherits = ntups; + + inhinfo = (InhInfo*)malloc(ntups * sizeof(InhInfo)); + + i_inhrel = PQfnumber(res,"inhrel"); + i_inhparent = PQfnumber(res,"inhparent"); + + for (i=0;i<ntups;i++) { + inhinfo[i].inhrel = dupstr(PQgetvalue(res,i,i_inhrel)); + inhinfo[i].inhparent = dupstr(PQgetvalue(res,i,i_inhparent)); + } + + PQclear(res); + res = PQexec(g_conn, "end"); + PQclear(res); + return inhinfo; +} + +/* + * getTableAttrs - + * for each table in tblinfo, read its attributes types and names + * + * this is implemented in a very inefficient way right now, looping + * through the tblinfo and doing a join per table to find the attrs and their + * types + * + * modifies tblinfo + */ +void +getTableAttrs(TableInfo* tblinfo, int numTables) +{ + int i,j; + char q[MAXQUERYLEN]; + int i_attname; + int i_typname; + PGresult *res; + int ntups; + + for (i=0;i<numTables;i++) { + + /* skip archive tables */ + if (isArchiveName(tblinfo[i].relname)) + continue; + + /* find all the user attributes and their types*/ + /* we must read the attribute names in attribute number order! */ + /* because we will use the attnum to index into the attnames array + later */ +if (g_verbose) + fprintf(stderr,"%s finding the attrs and types for table: %s %s\n", + g_comment_start, + tblinfo[i].relname, + g_comment_end); + + sprintf(q,"SELECT a.attnum, a.attname, t.typname from pg_attribute a, pg_type t where a.attrelid = '%s'::oid and a.atttypid = t.oid and a.attnum > 0 order by attnum",tblinfo[i].oid); + res = PQexec(g_conn, q); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr,"getTableAttrs(): SELECT failed"); + exit_nicely(g_conn); + } + + ntups = PQntuples(res); + + i_attname = PQfnumber(res,"attname"); + i_typname = PQfnumber(res,"typname"); + + tblinfo[i].numatts = ntups; + tblinfo[i].attnames = (char**) malloc( ntups * sizeof(char*)); + tblinfo[i].typnames = (char**) malloc( ntups * sizeof(char*)); + tblinfo[i].inhAttrs = (int*) malloc (ntups * sizeof(int)); + tblinfo[i].parentRels = NULL; + tblinfo[i].numParents = 0; + for (j=0;j<ntups;j++) { + tblinfo[i].attnames[j] = dupstr(PQgetvalue(res,j,i_attname)); + tblinfo[i].typnames[j] = dupstr(PQgetvalue(res,j,i_typname)); + tblinfo[i].inhAttrs[j] = 0; /* this flag is set in flagInhAttrs()*/ + } + PQclear(res); + } +} + + +/* + * getIndices + * read all the user-defined indices information + * from the system catalogs return them in the InhInfo* structure + * + * numIndices is set to the number of indices read in + * + * + */ +IndInfo* +getIndices(int *numIndices) +{ + int i; + char query[MAXQUERYLEN]; + PGresult *res; + int ntups; + IndInfo *indinfo; + + int i_indexrelname; + int i_indrelname; + int i_indamname; + int i_indproc; + int i_indkey; + int i_indclassname; + + /* find all the user-defined indices. + We do not handle partial indices. + We also assume that only single key indices + + skip 'Xinx*' - indices on inversion objects + + this is a 5-way join !! + */ + + res = PQexec(g_conn, "begin"); + if (!res || + PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr,"BEGIN command failed\n"); + exit_nicely(g_conn); + } + PQclear(res); + + sprintf(query, + "SELECT t1.relname as indexrelname, t2.relname as indrelname, i.indproc, i.indkey[0], o.opcname as indclassname, a.amname as indamname from pg_index i, pg_class t1, pg_class t2, pg_opclass o, pg_am a where t1.oid = i.indexrelid and t2.oid = i.indrelid and o.oid = i.indclass[0] and t1.relam = a.oid and i.indexrelid > '%d'::oid and t2.relname !~ '^pg_' and t1.relname !~ '^Xinx' ;", + g_last_builtin_oid); + + res = PQexec(g_conn, query); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr,"getIndices(): SELECT failed"); + exit_nicely(g_conn); + } + + ntups = PQntuples(res); + + *numIndices = ntups; + + indinfo = (IndInfo*)malloc(ntups * sizeof (IndInfo)); + + i_indexrelname = PQfnumber(res,"indexrelname"); + i_indrelname = PQfnumber(res,"indrelname"); + i_indamname = PQfnumber(res,"indamname"); + i_indproc = PQfnumber(res,"indproc"); + i_indkey = PQfnumber(res,"indkey"); + i_indclassname = PQfnumber(res,"indclassname"); + + for (i=0;i<ntups;i++) { + indinfo[i].indexrelname = dupstr(PQgetvalue(res,i,i_indexrelname)); + indinfo[i].indrelname = dupstr(PQgetvalue(res,i,i_indrelname)); + indinfo[i].indamname = dupstr(PQgetvalue(res,i,i_indamname)); + indinfo[i].indproc = dupstr(PQgetvalue(res,i,i_indproc)); + indinfo[i].indkey = dupstr(PQgetvalue(res,i,i_indkey)); + indinfo[i].indclassname = dupstr(PQgetvalue(res,i,i_indclassname)); + } + PQclear(res); + res = PQexec(g_conn,"end"); + + return indinfo; +} + +/* + * dumpTypes + * writes out to fout the queries to recreate all the user-defined types + * + */ +void +dumpTypes(FILE* fout, FuncInfo* finfo, int numFuncs, + TypeInfo* tinfo, int numTypes) +{ + int i; + char q[MAXQUERYLEN]; + int funcInd; + + for (i=0;i<numTypes;i++) { + + /* skip all the builtin types */ + if (atoi(tinfo[i].oid) < g_last_builtin_oid) + continue; + + /* skip relation types */ + if (atoi(tinfo[i].typrelid) != 0) + continue; + + /* skip all array types that start w/ underscore */ + if ( (tinfo[i].typname[0] == '_') && + (strcmp(tinfo[i].typinput, "array_in") == 0)) + continue; + + /* before we create a type, we need to create the input and + output functions for it, if they haven't been created already */ + funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typinput); + if (funcInd != -1) + dumpOneFunc(fout,finfo,funcInd,tinfo,numTypes); + + funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typoutput); + if (funcInd != -1) + dumpOneFunc(fout,finfo,funcInd,tinfo,numTypes); + + sprintf(q, + "CREATE TYPE %s ( internallength = %s, externallength = %s, input = %s, output = %s, send = %s, receive = %s, default = '%s'", + tinfo[i].typname, + tinfo[i].typlen, + tinfo[i].typprtlen, + tinfo[i].typinput, + tinfo[i].typoutput, + tinfo[i].typsend, + tinfo[i].typreceive, + tinfo[i].typdefault); + + if (tinfo[i].isArray) { + char* elemType; + + elemType = findTypeByOid(tinfo, numTypes, tinfo[i].typelem); + + sprintf(q,"%s, element = %s, delimiter = '%s'", + q, elemType,tinfo[i].typdelim); + } + if (tinfo[i].passedbyvalue) + strcat(q,",passedbyvalue);\n"); + else + strcat(q,");\n"); + + fputs(q,fout); + } +} + +/* + * dumpFuncs + * writes out to fout the queries to recreate all the user-defined functions + * + */ +void +dumpFuncs(FILE* fout, FuncInfo* finfo, int numFuncs, + TypeInfo *tinfo, int numTypes) +{ + int i; + for (i=0;i<numFuncs;i++) { + dumpOneFunc(fout,finfo,i,tinfo,numTypes); + } +} + +/* + * dumpOneFunc: + * dump out only one function, the index of which is given in the third + * argument + * + */ + +void +dumpOneFunc(FILE* fout, FuncInfo* finfo, int i, + TypeInfo *tinfo, int numTypes) +{ + char q[MAXQUERYLEN]; + int j; + + if (finfo[i].dumped) + return; + else + finfo[i].dumped = 1; + + sprintf(q,"CREATE FUNCTION %s (",finfo[i].proname); + for (j=0;j<finfo[i].nargs;j++) { + char* typname; + typname = findTypeByOid(tinfo, numTypes, finfo[i].argtypes[j]); + sprintf(q, "%s%s%s", + q, + (j > 0) ? "," : "", + typname); + } + sprintf(q,"%s ) RETURNS %s%s AS '%s' LANGUAGE '%s';\n", + q, + finfo[i].retset ? " SETOF " : "", + findTypeByOid(tinfo, numTypes, finfo[i].prorettype), + (finfo[i].lang) ? finfo[i].probin : finfo[i].prosrc, + (finfo[i].lang) ? "C" : "SQL"); + + fputs(q,fout); + +} + +/* + * dumpOprs + * writes out to fout the queries to recreate all the user-defined operators + * + */ +void +dumpOprs(FILE* fout, OprInfo* oprinfo, int numOperators, + TypeInfo *tinfo, int numTypes) +{ + int i; + char q[MAXQUERYLEN]; + char leftarg[MAXQUERYLEN]; + char rightarg[MAXQUERYLEN]; + char commutator[MAXQUERYLEN]; + char negator[MAXQUERYLEN]; + char restrict[MAXQUERYLEN]; + char join[MAXQUERYLEN]; + char sortop[MAXQUERYLEN]; + + for (i=0;i<numOperators;i++) { + + /* skip all the builtin oids */ + if (atoi(oprinfo[i].oid) < g_last_builtin_oid) + continue; + + /* some operator are invalid because they were the result + of user defining operators before commutators exist */ + if (strcmp(oprinfo[i].oprcode, "-") == 0) + continue; + + leftarg[0] = '\0'; + rightarg[0] = '\0'; + /* right unary means there's a left arg + and left unary means there's a right arg */ + if (strcmp(oprinfo[i].oprkind, "r") == 0 || + strcmp(oprinfo[i].oprkind, "b") == 0 ) { + sprintf(leftarg, ", LEFTARG = %s ", + findTypeByOid(tinfo, numTypes, oprinfo[i].oprleft)); + } + if (strcmp(oprinfo[i].oprkind, "l") == 0 || + strcmp(oprinfo[i].oprkind, "b") == 0 ) { + sprintf(rightarg, ", RIGHTARG = %s ", + findTypeByOid(tinfo, numTypes, oprinfo[i].oprright)); + } + if (strcmp(oprinfo[i].oprcom, "0") == 0) + commutator[0] = '\0'; + else + sprintf(commutator,", COMMUTATOR = %s ", + findOprByOid(oprinfo, numOperators, oprinfo[i].oprcom)); + + if (strcmp(oprinfo[i].oprnegate, "0") == 0) + negator[0] = '\0'; + else + sprintf(negator,", NEGATOR = %s ", + findOprByOid(oprinfo, numOperators, oprinfo[i].oprnegate)); + + if (strcmp(oprinfo[i].oprrest, "-") == 0) + restrict[0] = '\0'; + else + sprintf(restrict,", RESTRICT = %s ", oprinfo[i].oprrest); + + if (strcmp(oprinfo[i].oprjoin,"-") == 0) + join[0] = '\0'; + else + sprintf(join,", JOIN = %s ", oprinfo[i].oprjoin); + + if (strcmp(oprinfo[i].oprlsortop, "0") == 0) + sortop[0] = '\0'; + else + { + sprintf(sortop,", SORT = %s ", + findOprByOid(oprinfo, numOperators, + oprinfo[i].oprlsortop)); + if (strcmp(oprinfo[i].oprrsortop, "0") != 0) + sprintf(sortop, "%s , %s", sortop, + findOprByOid(oprinfo, numOperators, + oprinfo[i].oprlsortop)); + } + + sprintf(q, + "CREATE OPERATOR %s (PROCEDURE = %s %s %s %s %s %s %s %s %s);\n ", + oprinfo[i].oprname, + oprinfo[i].oprcode, + leftarg, + rightarg, + commutator, + negator, + restrict, + (strcmp(oprinfo[i].oprcanhash, "t")) ? ", HASHES" : "", + join, + sortop); + + fputs(q,fout); + } +} + +/* + * dumpAggs + * writes out to fout the queries to create all the user-defined aggregates + * + */ +void +dumpAggs(FILE* fout, AggInfo* agginfo, int numAggs, + TypeInfo *tinfo, int numTypes) +{ + int i; + char q[MAXQUERYLEN]; + char sfunc1[MAXQUERYLEN]; + char sfunc2[MAXQUERYLEN]; + char finalfunc[MAXQUERYLEN]; + char comma1[2], comma2[2]; + + for (i=0;i<numAggs;i++) { + /* skip all the builtin oids */ + if (atoi(agginfo[i].oid) < g_last_builtin_oid) + continue; + + if ( strcmp(agginfo[i].aggtransfn1, "-") == 0) + sfunc1[0] = '\0'; + else { + sprintf(sfunc1, + "SFUNC1 = %s, BASETYPE = %s, STYPE1 = %s", + agginfo[i].aggtransfn1, + findTypeByOid(tinfo,numTypes,agginfo[i].aggbasetype), + findTypeByOid(tinfo,numTypes,agginfo[i].aggtranstype1)); + if (agginfo[i].agginitval1) + sprintf(sfunc1, "%s ,INITCOND1 = '%s'", + sfunc1, agginfo[i].agginitval1); + + } + + if ( strcmp(agginfo[i].aggtransfn2, "-") == 0) + sfunc2[0] = '\0'; + else { + sprintf(sfunc2, + "SFUNC2 = %s, STYPE2 = %s", + agginfo[i].aggtransfn2, + findTypeByOid(tinfo,numTypes,agginfo[i].aggtranstype2)); + if (agginfo[i].agginitval2) + sprintf(sfunc2,"%s ,INITCOND2 = '%s'", + sfunc2, agginfo[i].agginitval2); + } + + if ( strcmp(agginfo[i].aggfinalfn, "-") == 0) + finalfunc[0] = '\0'; + else { + sprintf(finalfunc, "FINALFUNC = %s", agginfo[i].aggfinalfn); + } + if (sfunc1[0] != '\0' && sfunc2[0] != '\0') { + comma1[0] = ','; comma1[1] = '\0'; + } else + comma1[0] = '\0'; + + if (finalfunc[0] != '\0' && (sfunc1[0] != '\0' || sfunc2[0] != '\0')) { + comma2[0] = ',';comma2[1] = '\0'; + } else + comma2[0] = '\0'; + + sprintf(q,"CREATE AGGREGATE %s ( %s %s %s %s %s );\n", + agginfo[i].aggname, + sfunc1, + comma1, + sfunc2, + comma2, + finalfunc); + + fputs(q,fout); + } +} + +/* + * dumpTables: + * write out to fout all the user-define tables + */ +void +dumpTables(FILE* fout, TableInfo *tblinfo, int numTables, + InhInfo *inhinfo, int numInherits, + TypeInfo *tinfo, int numTypes) +{ + int i,j,k; + char q[MAXQUERYLEN]; + char **parentRels; /* list of names of parent relations */ + int numParents; + int actual_atts; /* number of attrs in this CREATE statment */ + char *archiveMode; + + for (i=0;i<numTables;i++) { + + /* skip archive names*/ + if (isArchiveName(tblinfo[i].relname)) + continue; + + parentRels = tblinfo[i].parentRels; + numParents = tblinfo[i].numParents; + + sprintf(q, "CREATE TABLE %s (", tblinfo[i].relname); + actual_atts = 0; + for (j=0;j<tblinfo[i].numatts;j++) { + if (tblinfo[i].inhAttrs[j] == 0) { + sprintf(q, "%s%s%s %s", + q, + (actual_atts > 0) ? ", " : "", + tblinfo[i].attnames[j], + tblinfo[i].typnames[j]); + actual_atts++; + } + } + + strcat(q,")"); + + if (numParents > 0) { + sprintf(q, "%s inherits ( ",q); + for (k=0;k<numParents;k++){ + sprintf(q, "%s%s%s", + q, + (k>0) ? ", " : "", + parentRels[k]); + } + strcat(q,")"); + } + + switch(tblinfo[i].relarch[0]) { + case 'n': + archiveMode = "none"; + break; + case 'h': + archiveMode = "heavy"; + break; + case 'l': + archiveMode = "light"; + break; + default: + fprintf(stderr, "unknown archive mode\n"); + archiveMode = "none"; + break; + } + + sprintf(q, "%s archive = %s;\n", + q, + archiveMode); + fputs(q,fout); + } +} + +/* + * dumpIndices: + * write out to fout all the user-define indices + */ +void +dumpIndices(FILE* fout, IndInfo* indinfo, int numIndices, + TableInfo* tblinfo, int numTables) +{ + int i; + int tableInd; + char *attname; /* the name of the indexed attribute */ + char *funcname; /* the name of the function to comput the index key from*/ + int indkey; + + char q[MAXQUERYLEN]; + PGresult *res; + + for (i=0;i<numIndices;i++) { + tableInd = findTableByName(tblinfo, numTables, + indinfo[i].indrelname); + indkey = atoi(indinfo[i].indkey) - 1; + attname = tblinfo[tableInd].attnames[indkey]; + if (strcmp(indinfo[i].indproc,"0") == 0) { + funcname = NULL; + } else { + /* the funcname is an oid which we use to + find the name of the pg_proc. We need to do this + because getFuncs() only reads in the user-defined funcs + not all the funcs. We might not find what we want + by looking in FuncInfo**/ + sprintf(q, + "SELECT proname from pg_proc where pg_proc.oid = '%s'::oid", + indinfo[i].indproc); + res = PQexec(g_conn, q); + funcname = dupstr(PQgetvalue(res, 0, + PQfnumber(res,"proname"))); + PQclear(res); + } + sprintf(q,"CREATE INDEX %s on %s using %s (", + indinfo[i].indexrelname, + indinfo[i].indrelname, + indinfo[i].indamname); + if (funcname) { + sprintf(q, "%s %s(%s) %s);\n", + q,funcname, attname, indinfo[i].indclassname); + free(funcname); + } else + sprintf(q, "%s %s %s);\n", + q,attname,indinfo[i].indclassname); + + fputs(q,fout); + } + +} + + + + + +/* + * DumpClasses - + * dump the contents of all the classes. + */ +void +dumpClasses(TableInfo *tblinfo, int numTables, FILE *fout) +{ + char query[255]; +#define COPYBUFSIZ 8192 + char copybuf[COPYBUFSIZ]; + PGresult *res; + int i; + int ret; + int copydone; + + for(i = 0; i < numTables; i++) { + char *classname = tblinfo[i].relname; + + /* skip archive names*/ + if (isArchiveName(classname)) + continue; + + fprintf(fout, "COPY %s from stdin;\n", classname); + sprintf(query, "COPY %s to stdout;\n", classname); + res = PQexec(g_conn, query); + if (!res || + PQresultStatus(res) != PGRES_COPY_OUT) { + fprintf(stderr,"dumpClasses(): COPY to stdout failed"); + exit_nicely(g_conn); + } + copydone = 0; + while (!copydone) { + ret = PQgetline(res->conn, copybuf, COPYBUFSIZ); + + if (copybuf[0] == '.' && copybuf[1] =='\0') { + copydone = true; /* don't print this... */ + } else { + fputs(copybuf, stdout); + switch (ret) { + case EOF: + copydone = true; + /*FALLTHROUGH*/ + case 0: + fputc('\n', stdout); + break; + case 1: + break; + } + } + } + fprintf(fout, ".\n"); + PQclear(res); + PQendcopy(res->conn); + } + +} + +/* + * dumpTuples -- + * prints out the tuples in ASCII representation. The output is a valid + * input to COPY FROM stdin. + * + * We only need to do this for POSTGRES 4.2 databases since the + * COPY TO statment doesn't escape newlines properly. It's been fixed + * in Postgres95. + * + * the attrmap passed in tells how to map the attributes copied in to the + * attributes copied out + */ +void +dumpTuples(PGresult *res, FILE *fout, int* attrmap) +{ + int j, k; + int m, n; + char **outVals = NULL; /* values to copy out */ + + n = PQntuples(res); + m = PQnfields(res); + + if ( m > 0 ) { + /* + * Print out the tuples but only print tuples with at least + * 1 field. + */ + outVals = (char**)malloc(m * sizeof(char*)); + + for (j = 0; j < n; j++) { + for (k = 0; k < m; k++) { + outVals[attrmap[k]] = PQgetvalue(res, j, k); + } + for (k = 0; k < m; k++) { + char *pval = outVals[k]; + + if (k!=0) + fputc('\t', fout); /* delimiter for attribute */ + + if (pval) { + while (*pval != '\0') { + /* escape tabs, newlines and backslashes */ + if (*pval=='\t' || *pval=='\n' || *pval=='\\') + fputc('\\', fout); + fputc(*pval, fout); + pval++; + } + } + } + fputc('\n', fout); /* delimiter for a tuple */ + } + free (outVals); + } +} + + + +/* + * findLastBuiltInOid - + * find the last built in oid + * we do this by looking up the oid of 'template1' in pg_database, + * this is probably not foolproof but comes close +*/ + +int +findLastBuiltinOid() +{ + PGresult* res; + int ntups; + int last_oid; + + res = PQexec(g_conn, + "SELECT oid from pg_database where datname = 'template1';"); + if (res == NULL || + PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr,"pg_dump error in finding the template1 database"); + exit_nicely(g_conn); + } + ntups = PQntuples(res); + if (ntups != 1) { + fprintf(stderr,"pg_dump: couldn't find the template1 database. You are really hosed\nGiving up\n"); + exit_nicely(g_conn); + } + last_oid = atoi(PQgetvalue(res, 0, PQfnumber(res, "oid"))); + PQclear(res); + return last_oid; +} + + +/* + * checkForQuote: + * checks a string for quote characters and quotes them + */ +char* +checkForQuote(char* s) +{ + char *r; + char c; + char *result; + + int j = 0; + + r = malloc(strlen(s)*3 + 1); /* definitely long enough */ + + while ( (c = *s) != '\0') { + + if (c == '\'') { + r[j++] = '\''; /* quote the single quotes */ + } + r[j++] = c; + s++; + } + r[j] = '\0'; + + result = dupstr(r); + free(r); + + return result; + +} diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h new file mode 100644 index 00000000000..e77960afe4b --- /dev/null +++ b/src/bin/pg_dump/pg_dump.h @@ -0,0 +1,195 @@ +/*------------------------------------------------------------------------- + * + * pg_dump.h + * header file for the pg_dump utility + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pg_dump.h,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + + +/* The *Info data structures run-time C structures used to store + system catalog information */ + +typedef struct _typeInfo { + char* oid; + char* typowner; + char* typname; + char* typlen; + char* typprtlen; + char* typinput; + char* typoutput; + char* typreceive; + char* typsend; + char* typelem; + char* typdelim; + char* typdefault; + char* typrelid; + int passedbyvalue; + int isArray; +} TypeInfo; + +typedef struct _funcInfo { + char* oid; + char* proname; + char* proowner; + int lang; /* 1 if C, else SQL */ + int nargs; + char* argtypes[8]; /* should be derived from obj/fmgr.h instead of hardwired*/ + char* prorettype; + int retset; /* 1 if the function returns a set, 0 otherwise */ + char* prosrc; + char* probin; + int dumped; /* 1 if already dumped */ +} FuncInfo; + +typedef struct _tableInfo { + char *oid; + char *relname; + char *relarch; + int numatts; /* number of attributes */ + int *inhAttrs; /* an array of flags, one for each attribute + if the value is 1, then this attribute is + an inherited attribute */ + char **attnames; /* the attribute names */ + char **typnames; /* fill out attributes */ + int numParents; /* number of (immediate) parent supertables */ + char **parentRels; /* names of parent relations, NULL + if numParents == 0 */ + char **out_attnames; /* the attribute names, in the order they would + be in, when the table is created in the + target query language. + this is needed because the SQL tables will + not have the same order of attributes as + the POSTQUEL tables */ + +} TableInfo; + +typedef struct _inhInfo { + char *oid; + char *inhrel; + char *inhparent; +} InhInfo; + +typedef struct _indInfo { + char *indexrelname; /* name of the secondary index class */ + char *indrelname; /* name of the indexed heap class */ + char *indamname; /* name of the access method (e.g. btree, rtree, etc.) */ + char *indproc; /* oid of the function to compute the index, 0 if none*/ + char *indkey; /* attribute number of the key attribute */ + char *indclassname; /* name of the opclass of the key */ +} IndInfo; + +typedef struct _aggInfo { + char *oid; + char *aggname; + char *aggtransfn1; + char *aggtransfn2; + char *aggfinalfn; + char *aggtranstype1; + char *aggbasetype; + char *aggtranstype2; + char *agginitval1; + char *agginitval2; +} AggInfo; + +typedef struct _oprInfo { + char *oid; + char *oprname; + char *oprkind; /* "b" = binary, "l" = left unary, "r" = right unary */ + char *oprcode; /* operator function name */ + char *oprleft; /* left operand type */ + char *oprright; /* right operand type */ + char *oprcom; /* oid of the commutator operator */ + char *oprnegate; /* oid of the negator operator */ + char *oprrest; /* name of the function to calculate operator restriction + selectivity */ + char *oprjoin; /* name of the function to calculate operator join + selectivity */ + char *oprcanhash; /* can we use hash join strategy ? */ + char *oprlsortop; /* oid's of the left and right sort operators */ + char *oprrsortop; +} OprInfo; + + +/* global decls */ +extern int g_verbose; /* verbose flag */ +extern int g_last_builtin_oid; /* value of the last builtin oid */ +extern FILE *g_fout; /* the script file */ + +/* placeholders for comment starting and ending delimiters */ +extern char g_comment_start[10]; +extern char g_comment_end[10]; + +extern char g_opaque_type[10]; /* name for the opaque type */ + +/* pg_dump is really two programs in one + one version works with postgres v4r2 + and the other works with postgres95 + the common routines are declared here +*/ +/* + * common utility functions +*/ + +extern TableInfo* dumpSchema(FILE* fout, int *numTablesPtr); + +extern char* findTypeByOid(TypeInfo* tinfo, int numTypes, char* oid); +extern char* findOprByOid(OprInfo *oprinfo, int numOprs, char *oid); +extern int findFuncByName(FuncInfo* finfo, int numFuncs, char* name); +extern char** findParentsByOid(TableInfo* tbinfo, int numTables, + InhInfo* inhinfo, int numInherits, + char *oid, + int *numParents); +extern int findTableByName(TableInfo *tbinfo, int numTables, char *relname); +extern int findTableByOid(TableInfo *tbinfo, int numTables, char *oid); +extern void flagInhAttrs(TableInfo* tbinfo, int numTables, + InhInfo* inhinfo, int numInherits); + +extern void check_conn_and_db(); +extern char* dupstr(char *s); +extern int strInArray(char* pattern, char** arr, int arr_size); +extern void parseArgTypes(char **argtypes, char* str); +extern int isArchiveName(char*); + +/* + * version specific routines + */ +extern TypeInfo* getTypes(int *numTypes); +extern FuncInfo* getFuncs(int *numFuncs); +extern AggInfo* getAggregates(int *numAggregates); +extern OprInfo* getOperators(int *numOperators); +extern TableInfo* getTables(int *numTables); +extern InhInfo* getInherits(int *numInherits); +extern void getTableAttrs(TableInfo* tbinfo, int numTables); +extern IndInfo* getIndices(int *numIndices); +extern void dumpTypes(FILE* fout, FuncInfo* finfo, int numFuncs, + TypeInfo* tinfo, int numTypes); +extern void dumpFuncs(FILE* fout, FuncInfo* finfo, int numFuncs, + TypeInfo *tinfo, int numTypes); +extern void dumpAggs(FILE* fout, AggInfo* agginfo, int numAggregates, + TypeInfo *tinfo, int numTypes); +extern void dumpOprs(FILE* fout, OprInfo* agginfo, int numOperators, + TypeInfo *tinfo, int numTypes); +extern void dumpOneFunc(FILE* fout, FuncInfo* finfo, int i, + TypeInfo *tinfo, int numTypes); +extern void dumpTables(FILE* fout, TableInfo* tbinfo, int numTables, + InhInfo *inhinfo, int numInherits, + TypeInfo *tinfo, int numTypes); +extern void dumpIndices(FILE* fout, IndInfo* indinfo, int numIndices, + TableInfo* tbinfo, int numTables); + +extern void dumpClasses(TableInfo *tbinfo, int numTables, FILE *fout); +extern void dumpTuples(PGresult *res, FILE *fout, int *attrmap); +extern char* checkForQuote(char* s); +extern int findLastBuiltinOid(); + + +/* largest query string size */ +#define MAXQUERYLEN 5000 + +/* these voodoo constants are from the backend */ +#define C_PROLANG_OID 13 diff --git a/src/bin/pg_id/Makefile b/src/bin/pg_id/Makefile new file mode 100644 index 00000000000..db056f89f2c --- /dev/null +++ b/src/bin/pg_id/Makefile @@ -0,0 +1,23 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/pg_id +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/pg_id/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ +# +#------------------------------------------------------------------------- + +PROG= pg_id + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +SRCS= pg_id.c + +include $(MKDIR)/postgres.prog.mk + diff --git a/src/bin/pg_id/pg_id.c b/src/bin/pg_id/pg_id.c new file mode 100644 index 00000000000..675326acbca --- /dev/null +++ b/src/bin/pg_id/pg_id.c @@ -0,0 +1,52 @@ +/*------------------------------------------------------------------------- + * + * pg_id.c-- + * Print the user ID for the login name passed as argument, + * or the real user ID of the caller if no argument. If the + * login name doesn't exist, print "NOUSER" and exit 1. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/bin/pg_id/Attic/pg_id.c,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <sys/types.h> +#include <pwd.h> +#include <stdio.h> + +int +main(int argc, char **argv) +{ + struct passwd *pw; + int ch; + extern int optind; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch (ch) { + case '?': + default: + fprintf(stderr, "usage: pg_id [login]\n"); + exit(1); + } + argc -= optind; + argv += optind; + + if (argc > 0) { + if (argc > 1) { + fprintf(stderr, "usage: pg_id [login]\n"); + exit(1); + } + if ((pw = getpwnam(argv[0])) == NULL) { + printf("NOUSER\n"); + exit(1); + } + printf("%d\n", pw->pw_uid); + } else { + printf("%d\n", getuid()); + } + + exit(0); +} diff --git a/src/bin/pg_version/Makefile b/src/bin/pg_version/Makefile new file mode 100644 index 00000000000..9e46bd27b08 --- /dev/null +++ b/src/bin/pg_version/Makefile @@ -0,0 +1,26 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/pg_version +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/pg_version/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ +# +#------------------------------------------------------------------------- + +PROG= pg_version + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +VPATH:=$(VPATH):$(srcdir)/backend/utils/init +SRCS= pg_version.c magic.c + +CFLAGS+= -I$(srcdir)/backend/port/$(PORTNAME) + +include $(MKDIR)/postgres.prog.mk + diff --git a/src/bin/pg_version/pg_version.c b/src/bin/pg_version/pg_version.c new file mode 100644 index 00000000000..6844692be7e --- /dev/null +++ b/src/bin/pg_version/pg_version.c @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------- + * + * pg_version.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/bin/pg_version/Attic/pg_version.c,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> + +int Noversion = 0; +char *DataDir = (char *) NULL; + +int +main(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, "pg_version: missing argument\n"); + exit(1); + } + SetPgVersion(argv[1]); + exit(0); +} + +elog() {} + +GetDataHome() +{ + return(NULL); +} diff --git a/src/bin/pgtclsh/Makefile b/src/bin/pgtclsh/Makefile new file mode 100644 index 00000000000..a99ab39f082 --- /dev/null +++ b/src/bin/pgtclsh/Makefile @@ -0,0 +1,46 @@ +#------------------------------------------------------------------------- +# +# Makefile +# Makefile for a tclsh workalike with pgtcl commands installed +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/pgtclsh/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $ +# +#------------------------------------------------------------------------- + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +CFLAGS+= -I$(TCL_INCDIR) -I$(TK_INCDIR) + +# try to find libpgtcl.a in either directory +LIBPGTCL= -L$(srcdir)/libpgtcl/$(objdir) -L$(LIBDIR) -lpgtcl + +pgtclsh: $(objdir)/pgtclAppInit.o + $(CC) $(CDEBUG) -o $(objdir)/$(@F) $(objdir)/pgtclAppInit.o\ + $(LIBPGTCL) $(LIBPQ) -L$(TCL_LIBDIR) $(TCL_LIB) -lm $(LD_ADD) + +pgtksh: $(objdir)/pgtkAppInit.o + $(CC) $(CDEBUG) -o $(objdir)/$(@F) $(objdir)/pgtkAppInit.o \ + $(LIBPGTCL) $(LIBPQ) -L$(TCL_LIBDIR) -L$(TK_LIBDIR) \ + $(TK_LIB) $(TCL_LIB) -lX11 -lm $(LD_ADD) + +install:: localobj pgtclsh pgtksh + $(INSTALL) $(INSTL_EXE_OPTS) $(objdir)/pgtclsh $(DESTDIR)$(BINDIR)/pgtclsh + $(INSTALL) $(INSTL_EXE_OPTS) $(objdir)/pgtksh $(DESTDIR)$(BINDIR)/pgtksh + +CLEANFILES = pgtclAppInit.o pgtkAppInit.o pgtclsh pgtksh + +PROG=pgtclsh + +all:: pgtclsh pgtksh + +# don't use the default template for generating executables since we have +# two executable targets +# include $(MKDIR)/postgres.prog.mk + + diff --git a/src/bin/pgtclsh/README b/src/bin/pgtclsh/README new file mode 100644 index 00000000000..bbd89e012f9 --- /dev/null +++ b/src/bin/pgtclsh/README @@ -0,0 +1,21 @@ +pgtclsh is an example of a tclsh extended with the new Tcl +commands provided by the libpgtcl library. By using pgtclsh, one can +write front-end applications to Postgres95 in Tcl without having to +deal with any libpq programming at all. + +The pgtclsh is an enhanced version of tclsh. Similarly, pgtksh is a +wish replacement with postgres95 bindings. The Makefile is also set up +so that you can choose "pgtksh" as a target. + +pgtclsh has been tested with the official releases of + Tcl version 7.4 +and Tk version 4.0 + +and will probably not work with versions older than those (including +earlier beta releases). + +For details of the libpgtcl interface, please see the file +src/doc/libpgtcl.doc. + +If you have any questions or bug reports, please send them to +Jolly Chen at jolly@cs.berkeley.edu. diff --git a/src/bin/pgtclsh/pgtclAppInit.c b/src/bin/pgtclsh/pgtclAppInit.c new file mode 100644 index 00000000000..cc38ca34a70 --- /dev/null +++ b/src/bin/pgtclsh/pgtclAppInit.c @@ -0,0 +1,114 @@ +/* + * pgtclAppInit.c -- + * + * a skeletal Tcl_AppInit that provides pgtcl initialization + * to create a tclsh that can talk to pglite backends + * + * Copyright (c) 1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#ifndef lint +static char sccsid[] = "@(#) tclAppInit.c 1.11 94/12/17 16:14:03"; +#endif /* not lint */ + +#include "tcl.h" + +#include "libpgtcl.h" + +/* + * The following variable is a special hack that is needed in order for + * Sun shared libraries to be used for Tcl. + */ + +#ifdef NEED_MATHERR +extern int matherr(); +int *tclDummyMathPtr = (int *) matherr; +#endif + +/* + *---------------------------------------------------------------------- + * + * main -- + * + * This is the main program for the application. + * + * Results: + * None: Tcl_Main never returns here, so this procedure never + * returns either. + * + * Side effects: + * Whatever the application does. + * + *---------------------------------------------------------------------- + */ + +int +main(argc, argv) + int argc; /* Number of command-line arguments. */ + char **argv; /* Values of command-line arguments. */ +{ + Tcl_Main(argc, argv, Tcl_AppInit); + return 0; /* Needed only to prevent compiler warning. */ +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AppInit -- + * + * This procedure performs application-specific initialization. + * Most applications, especially those that incorporate additional + * packages, will have their own version of this procedure. + * + * Results: + * Returns a standard Tcl completion code, and leaves an error + * message in interp->result if an error occurs. + * + * Side effects: + * Depends on the startup script. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_AppInit(interp) + Tcl_Interp *interp; /* Interpreter for application. */ +{ + if (Tcl_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + + /* + * Call the init procedures for included packages. Each call should + * look like this: + * + * if (Mod_Init(interp) == TCL_ERROR) { + * return TCL_ERROR; + * } + * + * where "Mod" is the name of the module. + */ + + if (Pg_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + + /* + * Call Tcl_CreateCommand for application-specific commands, if + * they weren't already created by the init procedures called above. + */ + + /* + * Specify a user-specific startup file to invoke if the application + * is run interactively. Typically the startup file is "~/.apprc" + * where "app" is the name of the application. If this line is deleted + * then no user-specific startup file will be run under any conditions. + */ + + tcl_RcFileName = "~/.tclshrc"; + return TCL_OK; +} diff --git a/src/bin/pgtclsh/pgtclUtils.tcl b/src/bin/pgtclsh/pgtclUtils.tcl new file mode 100644 index 00000000000..dff87a484f4 --- /dev/null +++ b/src/bin/pgtclsh/pgtclUtils.tcl @@ -0,0 +1,16 @@ +# getDBs : +# get the names of all the databases at a given host and port number +# with the defaults being the localhost and port 5432 +# return them in alphabetical order +proc getDBs { {host "localhost"} {port "5432"} } { + # datnames is the list to be result + set conn [pg_connect template1 -host $host -port $port] + set res [pg_exec $conn "SELECT datname FROM pg_database ORDER BY datname"] + set ntups [pg_result $res -numTuples] + for {set i 0} {$i < $ntups} {incr i} { + lappend datnames [pg_result $res -getTuple $i] + } + pg_disconnect $conn + return $datnames +} + diff --git a/src/bin/pgtclsh/pgtkAppInit.c b/src/bin/pgtclsh/pgtkAppInit.c new file mode 100644 index 00000000000..33763458b51 --- /dev/null +++ b/src/bin/pgtclsh/pgtkAppInit.c @@ -0,0 +1,117 @@ +/* + * pgtkAppInit.c -- + * + * a skeletal Tcl_AppInit that provides pgtcl initialization + * to create a tclsh that can talk to pglite backends + * + * Copyright (c) 1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#ifndef lint +static char sccsid[] = "@(#) tkAppInit.c 1.12 94/12/17 16:30:56"; +#endif /* not lint */ + +#include "tk.h" +#include "libpgtcl.h" + +/* + * The following variable is a special hack that is needed in order for + * Sun shared libraries to be used for Tcl. + */ + +#ifdef NEED_MATHERR +extern int matherr(); +int *tclDummyMathPtr = (int *) matherr; +#endif + +/* + *---------------------------------------------------------------------- + * + * main -- + * + * This is the main program for the application. + * + * Results: + * None: Tk_Main never returns here, so this procedure never + * returns either. + * + * Side effects: + * Whatever the application does. + * + *---------------------------------------------------------------------- + */ + +int +main(argc, argv) + int argc; /* Number of command-line arguments. */ + char **argv; /* Values of command-line arguments. */ +{ + Tk_Main(argc, argv, Tcl_AppInit); + return 0; /* Needed only to prevent compiler warning. */ +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AppInit -- + * + * This procedure performs application-specific initialization. + * Most applications, especially those that incorporate additional + * packages, will have their own version of this procedure. + * + * Results: + * Returns a standard Tcl completion code, and leaves an error + * message in interp->result if an error occurs. + * + * Side effects: + * Depends on the startup script. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_AppInit(interp) + Tcl_Interp *interp; /* Interpreter for application. */ +{ + Tk_Window main; + + if (Tcl_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + if (Tk_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + + /* + * Call the init procedures for included packages. Each call should + * look like this: + * + * if (Mod_Init(interp) == TCL_ERROR) { + * return TCL_ERROR; + * } + * + * where "Mod" is the name of the module. + */ + + if (Pg_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + /* + * Call Tcl_CreateCommand for application-specific commands, if + * they weren't already created by the init procedures called above. + */ + + /* + * Specify a user-specific startup file to invoke if the application + * is run interactively. Typically the startup file is "~/.apprc" + * where "app" is the name of the application. If this line is deleted + * then no user-specific startup file will be run under any conditions. + */ + + tcl_RcFileName = "~/.wishrc"; + return TCL_OK; +} diff --git a/src/bin/pgtclsh/updateStats.tcl b/src/bin/pgtclsh/updateStats.tcl new file mode 100644 index 00000000000..62c9564fea1 --- /dev/null +++ b/src/bin/pgtclsh/updateStats.tcl @@ -0,0 +1,71 @@ +# +# updateStats +# updates the statistic of number of distinct attribute values +# (this should really be done by the vacuum command) +# this is kind of brute force and slow, but it works +# since we use SELECT DISTINCT to calculate the number of distinct values +# and that does a sort, you need to have plenty of disk space for the +# intermediate sort files. +# +# - jolly 6/8/95 + +# +# update_attnvals +# takes in a table and updates the attnvals columns for the attributes +# of that table +# +# conn is the database connection +# rel is the table name +proc update_attnvals {conn rel} { + + # first, get the oid of the rel + set res [pg_exec $conn "SELECT oid FROM pg_class where relname = '$rel'"] + if { [pg_result $res -numTuples] == "0"} { + puts stderr "update_attnvals: Relation named $rel was not found" + return + } + set oid [pg_result $res -getTuple 0] + pg_result $res -clear + + # use this query to find the names of the attributes + set res [pg_exec $conn "SELECT * FROM $rel WHERE 'f'::bool"] + set attrNames [pg_result $res -attributes] + + puts "attrNames = $attrNames" + foreach att $attrNames { + # find how many distinct values there are for this attribute + # this may fail if the user-defined type doesn't have + # comparison operators defined + set res2 [pg_exec $conn "SELECT DISTINCT $att FROM $rel"] + set NVALS($att) [pg_result $res2 -numTuples] + puts "NVALS($att) is $NVALS($att)" + pg_result $res2 -clear + } + pg_result $res -clear + + # now, update the pg_attribute table + foreach att $attrNames { + # first find the oid of the row to change + set res [pg_exec $conn "SELECT oid FROM pg_attribute a WHERE a.attname = '$att' and a.attrelid = '$oid'"] + set attoid [pg_result $res -getTuple 0] + set res2 [pg_exec $conn "UPDATE pg_attribute SET attnvals = $NVALS($att) where pg_attribute.oid = '$attoid'::oid"] + } +} + +# updateStats +# takes in a database name +# and updates the attnval stat for all the user-defined tables +# in the database +proc updateStats { dbName } { + # datnames is the list to be result + set conn [pg_connect $dbName] + set res [pg_exec $conn "SELECT relname FROM pg_class WHERE relkind = 'r' and relname !~ '^pg_' and relname !~ '^Xinv'"] + set ntups [pg_result $res -numTuples] + for {set i 0} {$i < $ntups} {incr i} { + set rel [pg_result $res -getTuple $i] + puts "updating attnvals stats on table $rel" + update_attnvals $conn $rel + } + pg_disconnect $conn +} + diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile new file mode 100644 index 00000000000..8e6ab5bb4f1 --- /dev/null +++ b/src/bin/psql/Makefile @@ -0,0 +1,65 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/psql +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/psql/Makefile,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $ +# +#------------------------------------------------------------------------- + +PROG= psql + +MKDIR= ../../mk +include $(MKDIR)/postgres.mk +include ../Makefile.global + +# +#USE_READLINE is set in Makefile.global +# + +ifeq ($(USE_READLINE), true) + CFLAGS += -I$(READLINE_INCDIR) -I$(HISTORY_INCDIR) + +# if you are using an older readline that uses #include "readline.h" instead +# of #include <readline/readline.h>, +# uncomment this +# CFLAGS += -DOLD_READLINE + + LIBCURSES= -lcurses + LD_ADD += -L$(READLINE_LIBDIR) -L$(HISTORY_LIBDIR) -lreadline -lhistory $(LIBCURSES) +# use the following if your readline has no separate history lib +# LD_ADD += -L$(READLINE_LIBDIR) -lreadline $(LIBCURSES) + + ifeq ($(PORTNAME), ultrix4) + LD_ADD += -ltermcap + else + ifeq ($(PORTNAME), sparc) + LD_ADD += -ltermcap + else + ifeq ($(PORTNAME), linux) + LD_ADD += -ltermcap + endif + ifeq ($(PORTNAME), next) + LD_ADD += -ltermcap + endif + endif + endif +else + CFLAGS += -DNOREADLINE +endif + +SRCS= psql.c stringutils.c + +ifneq ($(USE_READLINE), true) +SRCS+= rlstubs.c +endif + +include $(MKDIR)/postgres.prog.mk + + + + diff --git a/src/bin/psql/psql.c b/src/bin/psql/psql.c new file mode 100644 index 00000000000..d5dbfcea6fd --- /dev/null +++ b/src/bin/psql/psql.c @@ -0,0 +1,1230 @@ +/*------------------------------------------------------------------------- + * + * psql.c-- + * an interactive front-end to postgres95 + * + * Copyright (c) 1996, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "libpq-fe.h" +#include "stringutils.h" + +#include "psqlHelp.h" + +#ifdef NOREADLINE +extern char *readline(char *); /* in rlstubs.c */ +#else +/* from the GNU readline library */ +#ifdef OLD_READLINE +#include "readline.h" +#include "history.h" +#else +#include <readline/readline.h> +#include <history.h> +#endif +#endif + +#define MAX_QUERY_BUFFER 20000 +#define MAX_FIELD_SEP_LENGTH 40 + +#define COPYBUFSIZ 8192 + +#define DEFAULT_FIELD_SEP " " +#define DEFAULT_EDITOR "vi" +#define DEFAULT_SHELL "/bin/sh" + +typedef struct _psqlSettings { + int echoQuery; /* if 1, echo the query before sending it */ + int quiet; /* run quietly, no messages, no promt */ + int singleStep; /* if 1, prompt for each query */ + int singleLineMode; /* if 1, query terminated by newline */ + int useReadline; /* use the readline routines or not */ + int printHeader; /* print output field headers or not */ + int fillAlign; /* fill align the fields */ + FILE *queryFout; /* where to send the query results */ + char fieldSep[MAX_FIELD_SEP_LENGTH]; /* field separator */ +} PsqlSettings; + +/* declarations for functions in this file */ +static void usage(char* progname); +static void slashUsage(); +static void handleCopyOut(PGresult *res, int quiet); +static void handleCopyIn(PGresult *res, int quiet); +static int tableList(PGconn* conn, int deep_tablelist); +static int tableDesc(PGconn* conn, char* table); + +char* gets_noreadline(char* prompt, FILE* source); +char* gets_readline(char* prompt, FILE* source); +char* gets_fromFile(char* prompt, FILE* source); +int listAllDbs(PGconn *db, PsqlSettings *settings); +int SendQuery(PGconn* db, char* query, PsqlSettings *settings); +int HandleSlashCmds(PGconn** db_ptr, + char *line, + char** prompt_ptr, + char *query, + PsqlSettings *settings); +int MainLoop(PGconn** db_ptr, FILE *source, PsqlSettings *settings); +FILE* setFout(char *fname); + + +/* + * usage + * print out usage for command line arguments + */ + +static void +usage(char* progname) +{ + fprintf(stderr,"Usage: %s [options] [dbname]\n",progname); + fprintf(stderr,"\t -a authsvc set authentication service\n"); + fprintf(stderr,"\t -A turn off fill-justification when printing out attributes\n"); + fprintf(stderr,"\t -c query run single query (slash commands too)\n"); + fprintf(stderr,"\t -d dbName specify database name\n"); + fprintf(stderr,"\t -e echo the query sent to the backend\n"); + fprintf(stderr,"\t -f filename use file as a source of queries\n"); + fprintf(stderr,"\t -F sep set the field separator (default is " ")\n"); + fprintf(stderr,"\t -h help information\n"); + fprintf(stderr,"\t -H host set database server host\n"); + fprintf(stderr,"\t -l list available databases\n"); + fprintf(stderr,"\t -n don't use readline library\n"); + fprintf(stderr,"\t -o filename send output to filename\n"); + fprintf(stderr,"\t -p port set port number\n"); + fprintf(stderr,"\t -q run quietly (no messages, no prompts)\n"); + fprintf(stderr,"\t -s single step mode (prompts for each query)\n"); + fprintf(stderr,"\t -S single line mode (i.e. query terminated by newline)\n"); + fprintf(stderr,"\t -T turn off printing of attribute names\n"); + exit(1); +} + +/* + * slashUsage + * print out usage for the backslash commands + */ + +static void +slashUsage() +{ + fprintf(stderr,"\t \\a -- toggle fill-justification of display of attributes\n"); + fprintf(stderr,"\t \\d [<table>] -- list tables in database or columns in <table>\n"); + fprintf(stderr,"\t \\d * -- list tables in database and columns in all tables\n"); + fprintf(stderr,"\t \\e [<fname>] -- edit the current query buffer or <fname>\n"); + fprintf(stderr,"\t \\f <sep> -- change field separator\n"); + fprintf(stderr,"\t \\g -- query to backend\n"); + fprintf(stderr,"\t \\h <command> -- help on syntax of sql commands\n"); + fprintf(stderr,"\t \\h * -- complete description of all sql commands\n"); + fprintf(stderr,"\t \\g -- send query to backend\n"); + fprintf(stderr,"\t \\i <fname> -- read queries from filename\n"); + fprintf(stderr,"\t \\l -- list all databases\n"); + fprintf(stderr,"\t \\o [<fname>] -- send query results file named <fname> or stdout\n"); + fprintf(stderr,"\t \\p -- print the current query buffer\n"); + fprintf(stderr,"\t \\q -- quit\n"); + fprintf(stderr,"\t \\s [<fname>] -- save or print history\n"); + fprintf(stderr,"\t \\t -- toggle output field headers (defaults to on)\n"); + fprintf(stderr,"\t \\! [<cmd>] -- shell escape\n"); + fprintf(stderr,"\t \\? -- help\n"); +} + +/* + * listAllDbs + * + * list all the databases in the system + * returns 0 if all went well + * + * + */ +int +listAllDbs(PGconn *db, PsqlSettings *settings) +{ + PGresult *results; + char* query = "select * from pg_database;"; + + results = PQexec(db, query); + if (results == NULL) { + fprintf(stderr,"%s", PQerrorMessage(db)); + return 1; + } + + if (PQresultStatus(results) != PGRES_TUPLES_OK) + { + fprintf(stderr,"Unexpected error from executing: %s\n", query); + return 2; + } + else + { + PQdisplayTuples(results, + settings->queryFout, + settings->fillAlign, + settings->fieldSep, + settings->printHeader, + settings->quiet); + PQclear(results); + return 0; + } +} + +/* + * tableList (PGconn* conn) + * + * List The Database Tables + * returns 0 if all went well + * + */ +int +tableList (PGconn* conn, int deep_tablelist) +{ + char listbuf[256]; + int nColumns; + int i; + char* ru; + char* rk; + char* rr; + + PGresult* res; + + listbuf[0] = '\0'; + strcat(listbuf,"SELECT usename, relname, relkind, relhasrules"); + strcat(listbuf," FROM pg_class, pg_user "); + strcat(listbuf,"WHERE ( relkind = 'r' OR relkind = 'i') "); + strcat(listbuf," and relname !~ '^pg_'"); + strcat(listbuf," and relname !~ '^Inv'"); +/* the usesysid = relowner won't work on stock 1.0 dbs, need to + add in the int4oideq function */ + strcat(listbuf," and usesysid = relowner"); + strcat(listbuf," ORDER BY relname "); + res = PQexec(conn,listbuf); + if (res == NULL) { + fprintf(stderr,"%s", PQerrorMessage(conn)); + return (-1); + } + + if ((PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res) <= 0)) { + fprintf(stderr,"No tables found in database %s.\n", PQdb(conn)); + PQclear(res); + return (-1); + } + + /* first, print out the attribute names */ + nColumns = PQntuples(res); + if (nColumns > 0) + { + if ( deep_tablelist ) { + /* describe everything here */ + char **table; + table = (char**)malloc(nColumns * sizeof(char*)); + if ( table == NULL ) + perror("malloc"); + + /* load table table*/ + for (i=0; i < nColumns; i++) { + table[i] = (char *) malloc(PQgetlength(res,i,1) * sizeof(char) + 1); + if ( table[i] == NULL ) + perror("malloc"); + strcpy(table[i],PQgetvalue(res,i,1)); + } + + PQclear(res); + for (i=0; i < nColumns; i++) { + tableDesc(conn,table[i]); + } + free(table); + } + else { + /* Display the information */ + + printf ("\nDatabase = %s\n", PQdb(conn)); + printf (" +------------------+----------------------------------+----------+\n"); + printf (" | Owner | Relation | Type |\n"); + printf (" +------------------+----------------------------------+----------+\n"); + + /* next, print out the instances */ + for (i=0; i < PQntuples(res); i++) { + printf (" | %-16.16s", PQgetvalue(res,i,0)); + printf (" | %-32.32s | ", PQgetvalue(res,i,1)); + rk = PQgetvalue(res,i,2); + rr = PQgetvalue(res,i,3); + if (strcmp(rk, "r") == 0) + printf ("%-8.8s |", (rr[0] == 't') ? "view?" : "table" ); + else + printf ("%-8.8s |", "index"); + printf("\n"); + } + printf (" +------------------+----------------------------------+----------+\n"); + PQclear(res); + } + return (0); + + } else { + fprintf (stderr, "Couldn't find any tables!\n"); + return (-1); + } +} + +/* + * Describe a table (PGconn* conn, char* table) + * + * Describe the columns in a database table. + * returns 0 if all went well + * + * + */ +int +tableDesc (PGconn* conn, char* table) +{ + char descbuf[256]; + int nColumns; + char *rtype; + int i; + int rsize; + + PGresult* res; + + /* Build the query */ + + descbuf[0] = '\0'; + strcat(descbuf,"SELECT a.attnum, a.attname, t.typname, a.attlen"); + strcat(descbuf," FROM pg_class c, pg_attribute a, pg_type t "); + strcat(descbuf," WHERE c.relname = '"); + strcat(descbuf,table); + strcat(descbuf,"'"); + strcat(descbuf," and a.attnum > 0 "); + strcat(descbuf," and a.attrelid = c.oid "); + strcat(descbuf," and a.atttypid = t.oid "); + strcat(descbuf," ORDER BY attnum "); + res = PQexec(conn,descbuf); + if (res == NULL) { + fprintf(stderr,"%s", PQerrorMessage(conn)); + return (-1); + } + if ((PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res) <= 0)) { + fprintf(stderr,"Couldn't find table %s!\n", table); + PQclear(res); + return (-1); + } + /* first, print out the attribute names */ + nColumns = PQntuples(res); + if (nColumns > 0) + { + /* + ** Display the information + */ + + printf ("\nTable = %s\n", table); + printf ("+----------------------------------+----------------------------------+-------+\n"); + printf ("| Field | Type | Length|\n"); + printf ("+----------------------------------+----------------------------------+-------+\n"); + + /* next, print out the instances */ + for (i=0; i < PQntuples(res); i++) { + printf ("| %-32.32s | ", PQgetvalue(res,i,1)); + rtype = PQgetvalue(res,i,2); + rsize = atoi(PQgetvalue(res,i,3)); + if (strcmp(rtype, "text") == 0) { + printf ("%-32.32s |", rtype); + printf (" %-6s |", "var" ); + } + else if (strcmp(rtype, "bpchar") == 0) { + printf ("%-32.32s |", "char"); + printf (" %-6i |", rsize > 0 ? rsize - 4 : 0 ); + } + else if (strcmp(rtype, "varchar") == 0) { + printf ("%-32.32s |", rtype); + printf (" %-6i |", rsize > 0 ? rsize - 4 : 0 ); + } + else { + /* array types start with an underscore */ + if (rtype[0] != '_') + printf ("%-32.32s |", rtype); + else { + char *newname; + newname = malloc(strlen(rtype) + 2); + strcpy(newname, rtype+1); + strcat(newname, "[]"); + printf ("%-32.32s |", newname); + free(newname); + } + if (rsize > 0) + printf ("%-6i |", rsize); + else + printf ("%-6s |", "var"); + } + printf("\n"); + } + printf ("+----------------------------------+----------------------------------+-------+\n"); + + PQclear(res); + return (0); + + } else { + fprintf (stderr, "Couldn't find table %s!\n", table); + return (-1); + } +} + +typedef char* (*READ_ROUTINE)(char* prompt, FILE* source); + +/* gets_noreadline prompt source + gets a line of input without calling readline, the source is ignored +*/ +char* +gets_noreadline(char* prompt, FILE* source) +{ + fputs(prompt, stdout); + fflush(stdout); + return(gets_fromFile(prompt,stdin)); +} + +/* + * gets_readline prompt source + * the routine to get input from GNU readline(), the source is ignored + * the prompt argument is used as the prompting string + */ +char* +gets_readline(char* prompt, FILE* source) +{ + return (readline(prompt)); +} + + +/* + * gets_fromFile prompt source + * + * the routine to read from a file, the prompt argument is ignored + * the source argument is a FILE* + */ +char* +gets_fromFile(char* prompt, FILE* source) +{ + char* line; + int len; + + line = malloc(MAX_QUERY_BUFFER+1); + + /* read up to MAX_QUERY_BUFFER characters */ + if (fgets(line, MAX_QUERY_BUFFER, source) == NULL) + return NULL; + + line[MAX_QUERY_BUFFER-1] = '\0'; + len = strlen(line); + if (len == MAX_QUERY_BUFFER) + { + fprintf(stderr, "line read exceeds maximum length. Truncating at %d\n", MAX_QUERY_BUFFER); + } + + return line; +} + +/* + * SendQuery: + SendQuery: send the query string to the backend + * + * return 0 if the query executed successfully + * returns 1 otherwise + */ +int +SendQuery(PGconn* db, char* query, PsqlSettings *settings) +{ + PGresult* results; + PGnotify* notify; + int status = 0; + + if (settings->singleStep) + fprintf(stdout, "\n*******************************************************************************\n"); + + if (settings->echoQuery || settings->singleStep) { + fprintf(stderr,"QUERY: %s\n",query); + fflush(stderr); + } + + if (settings->singleStep) { + fprintf(stdout, "\n*******************************************************************************\n"); + fflush(stdout); + printf("\npress return to continue ..\n"); + gets_fromFile("",stdin); + } + + results = PQexec(db, query); + if (results == NULL) { + fprintf(stderr,"%s",PQerrorMessage(db)); + return 1; + } + + switch (PQresultStatus(results)) { + case PGRES_TUPLES_OK: + PQdisplayTuples(results, + settings->queryFout, + settings->fillAlign, + settings->fieldSep, + settings->printHeader, + settings->quiet); + PQclear(results); + break; + case PGRES_EMPTY_QUERY: + /* do nothing */ + break; + case PGRES_COMMAND_OK: + if (!settings->quiet) + fprintf(stdout,"%s\n",PQcmdStatus(results)); + break; + case PGRES_COPY_OUT: + handleCopyOut(results, settings->quiet); + break; + case PGRES_COPY_IN: + handleCopyIn(results, settings->quiet); + break; + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + case PGRES_BAD_RESPONSE: + status = 1; + fprintf(stderr,"%s",PQerrorMessage(db)); + break; + + } + + /* check for asynchronous returns */ + notify = PQnotifies(db); + if (notify) { + fprintf(stderr,"ASYNC NOTIFY of '%s' from backend pid '%d' received\n", + notify->relname, notify->be_pid); + free(notify); + } + + return status; + +} + +/* + HandleSlashCmds: + + Handles all the different commands that start with \ + db_ptr is a pointer to the TgDb* structure + line is the current input line + prompt_ptr is a pointer to the prompt string, + a pointer is used because the prompt can be used with + a connection to a new database + returns a status: + 0 - send currently constructed query to backend (i.e. we got a \g) + 1 - skip processing of this line, continue building up query + 2 - terminate processing of this query entirely +*/ +int +HandleSlashCmds(PGconn** db_ptr, + char* line, + char** prompt_ptr, + char *query, + PsqlSettings *settings) +{ + int status = 0; + PGconn* db = *db_ptr; + char* dbname = PQdb(db); + char *optarg = NULL; + int len; + + len = strlen(line); + if (len > 2) + optarg = leftTrim(line+2); + switch (line[1]) + { + case 'a': /* toggles to fill fields on output */ + if (settings->fillAlign) + settings->fillAlign = 0; + else + settings->fillAlign = 1; + if (!settings->quiet) + fprintf(stderr,"turning %s fill-justification\n", + (settings->fillAlign) ? "on" : "off" ); + break; + case 'c': /* \c means connect to new database */ + { + if (!optarg) { + fprintf(stderr,"\\c must be followed by a database name\n"); + status = 1; + break; + } + if (strcmp(optarg, dbname) == 0) { + fprintf(stderr,"already connected to %s\n", dbname); + status = 1; + break; + } + else { + PGconn *olddb; + + printf("closing connection to database:%s\n", dbname); + olddb = db; + db = PQsetdb(PQhost(olddb), PQport(olddb), NULL, NULL, optarg); + *db_ptr = db; + printf("connecting to new database: %s\n", optarg); + if (PQstatus(db) == CONNECTION_BAD) { + fprintf(stderr,"%s\n", PQerrorMessage(db)); + printf("reconnecting to %s\n", dbname); + db = PQsetdb(PQhost(olddb), PQport(olddb), + NULL, NULL, dbname); + *db_ptr = db; + if (PQstatus(db) == CONNECTION_BAD) { + fprintf(stderr, + "could not reconnect to %s. exiting\n", dbname); + exit(2); + } + status = 1; + break; + } + PQfinish(olddb); + free(*prompt_ptr); + *prompt_ptr = malloc(strlen(optarg) + 10); + sprintf(*prompt_ptr,"%s=> ", optarg); + status = 1; + break; + } + } + break; + case 'd': /* \d describe tables or columns in a table */ + { + if (!optarg) { + tableList(db,0); + status = 1; + break; + } + if ( strcmp(optarg,"*") == 0 ) { + tableList(db, 0); + tableList(db, 1); + } + else { + tableDesc(db,optarg); + } + status = 1; + break; + } + case 'e': + { + char s[256]; + int fd; + int ql = strlen(query); + int f_arg = 0; + int cc; + if (optarg) + { + f_arg = 1; + strcpy(s, optarg); + } + else + { + sprintf(s, "/tmp/psql.%d.%d", getuid(), getpid()); + unlink(s); + if (ql) + { + if ((fd=open(s, O_EXCL|O_CREAT|O_WRONLY, 0600))==-1) + { + perror(s); + break; + } + if (query[ql-1]!='\n') + strcat(query, "\n"); + if (write(fd, query, ql)!=ql) + { + perror(s); + close(fd); + unlink(s); + break; + } + close(fd); + } + } + { + char sys[256]; + char *editorName; + editorName = getenv("EDITOR"); + if (editorName == NULL) + editorName = DEFAULT_EDITOR; + sprintf(sys, "exec %s %s", editorName, s); + system(sys); + } + if ((fd=open(s, O_RDONLY))==-1) + { + if (!f_arg) + unlink(s); + break; + } + if ((cc=read(fd, query, MAX_QUERY_BUFFER))==-1) + { + perror(s); + close(fd); + if (!f_arg) + unlink(s); + break; + } + query[cc]='\0'; + close(fd); + if (!f_arg) + unlink(s); + rightTrim(query); + if (query[strlen(query)-1]==';') + return 0; + break; + } + case 'f': + if (optarg) + strcpy(settings->fieldSep,optarg); + else + strcpy(settings->fieldSep,DEFAULT_FIELD_SEP); + break; + case 'g': /* \g means send query */ + status = 0; + break; + case 'i': /* \i is include file */ + { + FILE* fd; + + if (!optarg) { + fprintf(stderr,"\\i must be followed by a file name\n"); + status = 1; + break; + } + + if ( (fd = fopen(optarg, "r")) == NULL) + { + fprintf(stderr,"file named %s could not be opened\n",optarg); + status = 1; + break; + } + MainLoop(&db, fd, settings); + fclose(fd); + status = 1; + break; + } + case 'h': + { + char* cmd; + int i, numCmds; + int all_help = 0; + + if (!optarg) { + printf("type \\h <cmd> where <cmd> is one of the following:\n"); + i = 0; + while (QL_HELP[i].cmd != NULL) + { + printf("\t%s\n", QL_HELP[i].cmd); + i++; + } + printf("type \\h * for a complete description of all commands\n"); + } + else + { + cmd = optarg; + + numCmds = 0; + while (QL_HELP[numCmds++].cmd != NULL); + + numCmds = numCmds - 1; + + if ( strcmp(cmd,"*") == 0 ) { + all_help=1; + } + + for (i=0; i<numCmds;i++) { + if (strcmp(QL_HELP[i].cmd, cmd) == 0 || all_help) { + printf("Command: %s\n",QL_HELP[i].cmd); + printf("Description: %s\n", QL_HELP[i].help); + printf("Syntax:\n"); + printf("%s\n", QL_HELP[i].syntax); + if ( all_help ) { + printf("\n"); + } + else { + break; + } + } + } + if (i == numCmds && ! all_help) + printf("command not found, try \\h with no arguments to see available help\n"); + } + status = 1; + break; + } + case 'l': /* \l is list database */ + listAllDbs(db,settings); + status = 1; + break; + case 'o': + settings->queryFout = setFout(optarg); + break; + case 'p': + if (query) { + fputs(query, stdout); + fputc('\n', stdout); + } + break; + case 'q': /* \q is quit */ + status = 2; + break; + case 's': /* \s is save history to a file */ + { + char* fname; + + if (!optarg) { + fprintf(stderr,"\\s must be followed by a file name\n"); + status = 1; + break; + } + + fname = optarg; + if (write_history(fname) != 0) + { + fprintf(stderr,"cannot write history to %s\n",fname); + } + status = 1; + break; + } + case 't': + if ( settings->printHeader ) + settings->printHeader = 0; + else + settings->printHeader = 1; + if (!settings->quiet) + fprintf(stderr,"turning %s printing of field headers\n", + (settings->printHeader) ? "on" : "off" ); + break; + case '!': + if (!optarg) { + char sys[256]; + char *shellName; + shellName = getenv("SHELL"); + if (shellName == NULL) + shellName = DEFAULT_SHELL; + sprintf(sys,"exec %s", shellName); + system(sys); + } + else + system(optarg); + break; + default: + case '?': /* \? is help */ + slashUsage(); + status = 1; + break; + } + return status; +} + +/* + MainLoop: main processing loop for reading lines of input + and sending them to the backend + + this loop is re-entrant. May be called by \i command + which reads input from a file + + *db_ptr must be initialized and set +*/ +int +MainLoop(PGconn** db_ptr, + FILE* source, + PsqlSettings *settings) +{ + char* prompt; /* readline prompt */ + char* line; /* line of input*/ + int len; /* length of the line */ + char query[MAX_QUERY_BUFFER]; /* multi-line query storage */ + PGconn* db = *db_ptr; + char* dbname = PQdb(db); + int exitStatus = 0; + + int slashCmdStatus = 0; + /* slashCmdStatus can be: + 0 - send currently constructed query to backend (i.e. we got a \g) + 1 - skip processing of this line, continue building up query + 2 - terminate processing of this query entirely + */ + + int send_query = 0; + int interactive; + READ_ROUTINE GetNextLine; + + interactive = (source == stdin); + + if (interactive) { + prompt = malloc(strlen(dbname) + 10); + if (settings->quiet) + prompt[0] = '\0'; + else + sprintf(prompt,"%s=> ", dbname); + if (settings->useReadline) { + using_history(); + GetNextLine = gets_readline; + } else + GetNextLine = gets_noreadline; + + } + else + GetNextLine = gets_fromFile; + + query[0] = '\0'; + + /* main loop for getting queries and executing them */ + while ((line = GetNextLine(prompt, source)) != NULL) + { + exitStatus = 0; + line = rightTrim(line); /* remove whitespaces on the right, incl. \n's */ + + if (line[0] == '\0') { + free(line); + continue; + } + + /* filter out comment lines that begin with --, + this could be incorrect if -- is part of a quoted string. + But we won't go through the trouble of detecting that. If you have + -- in your quoted string, be careful and don't start a line with it*/ + if (line[0] == '-' && line[1] == '-') { + if (settings->singleStep) /* in single step mode, show comments */ + fprintf(stdout,"%s\n",line); + free(line); + continue; + } + + len = strlen(line); + + if (interactive && settings->useReadline) + add_history(line); /* save non-empty lines in history */ + + /* do the query immediately if we are doing single line queries + or if the last character is a semicolon */ + send_query = settings->singleLineMode || (line[len-1] == ';') ; + + /* normally, \ commands have to be start the line, + but for backwards compatibility with monitor, + check for \g at the end of line */ + if (len > 2 && !send_query) + { + if (line[len-1]=='g' && line[len-2]=='\\') + { + send_query = 1; + line[len-2]='\0'; + } + } + + /* slash commands have to be on their own line */ + if (line[0] == '\\') { + slashCmdStatus = HandleSlashCmds(db_ptr, + line, + &prompt, + query, + settings); + db = *db_ptr; /* in case \c changed the database */ + if (slashCmdStatus == 1) + continue; + if (slashCmdStatus == 2) + break; + if (slashCmdStatus == 0) + send_query = 1; + } + else + if (strlen(query) + len > MAX_QUERY_BUFFER) + { + fprintf(stderr,"query buffer max length of %d exceeded\n",MAX_QUERY_BUFFER); + fprintf(stderr,"query line ignored\n"); + } + else + if (query[0]!='\0') { + strcat(query,"\n"); + strcat(query,line); + } + else + strcpy(query,line); + + if (send_query && query[0] != '\0') + { + /* echo the line read from the file, + unless we are in single_step mode, because single_step mode + will echo anyway */ + if (!interactive && !settings->singleStep) + fprintf(stderr,"%s\n",query); + + exitStatus = SendQuery(db, query, settings); + query[0] = '\0'; + } + + free(line); /* free storage malloc'd by GetNextLine */ + } /* while */ + return exitStatus; +} + +int +main(int argc, char** argv) +{ + extern char* optarg; + extern int optind, opterr; + + PGconn *db; + char* dbname = NULL; + char* host = NULL; + char* port = NULL; + char* qfilename = NULL; + char errbuf[ERROR_MSG_LENGTH]; + + PsqlSettings settings; + + char* singleQuery = NULL; + + int listDatabases = 0 ; + int exitStatus = 0; + int singleSlashCmd = 0; + int c; + + +#ifdef NOREADLINE + settings.useReadline = 0; +#else + settings.useReadline = 1; +#endif + + settings.quiet = 0; + settings.fillAlign = 1; + settings.printHeader = 1; + settings.echoQuery = 0; + settings.singleStep = 0; + settings.singleLineMode = 0; + settings.queryFout = stdout; + strcpy(settings.fieldSep, DEFAULT_FIELD_SEP); + + while ((c = getopt(argc, argv, "Aa:c:d:ef:F:lhH:nso:p:qST")) != EOF) { + switch (c) { + case 'A': + settings.fillAlign = 0; + break; + case 'a': + fe_setauthsvc(optarg, errbuf); + break; + case 'c': + singleQuery = optarg; + if ( singleQuery[0] == '\\' ) { + singleSlashCmd=1; + } + break; + case 'd': + dbname = optarg; + break; + case 'e': + settings.echoQuery = 1; + break; + case 'f': + qfilename = optarg; + break; + case 'F': + strncpy(settings.fieldSep,optarg,MAX_FIELD_SEP_LENGTH); + break; + case 'l': + listDatabases = 1; + break; + case 'H': + host = optarg; + break; + case 'n': + settings.useReadline = 0; + break; + case 'o': + settings.queryFout = setFout(optarg); + break; + case 'p': + port = optarg; + break; + case 'q': + settings.quiet = 1; + break; + case 's': + settings.singleStep = 1; + break; + case 'S': + settings.singleLineMode = 1; + break; + case 'T': + settings.printHeader = 0; + break; + case 'h': + default: + usage(argv[0]); + break; + } + } + /* if we still have an argument, use it as the database name */ + if (argc - optind == 1) + dbname = argv[optind]; + + if (listDatabases) + dbname = "template1"; + + db = PQsetdb(host, port, NULL, NULL, dbname); + dbname = PQdb(db); + + if (PQstatus(db) == CONNECTION_BAD) { + fprintf(stderr,"Connection to database '%s' failed.\n", dbname); + fprintf(stderr,"%s",PQerrorMessage(db)); + exit(1); + } + if (listDatabases) { + exit(listAllDbs(db,&settings)); + } + + if (!settings.quiet && !singleQuery && !qfilename) { + printf("Welcome to the POSTGRES95 interactive sql monitor:\n"); + printf(" Please read the file COPYRIGHT for copyright terms of POSTGRES95\n\n"); + printf(" type \\? for help on slash commands\n"); + printf(" type \\q to quit\n"); + printf(" type \\g or terminate with semicolon to execute query\n"); + printf(" You are currently connected to the database: %s\n\n", dbname); + } + + if (qfilename || singleSlashCmd) { + /* read in a file full of queries instead of reading in queries + interactively */ + char *line; + char prompt[100]; + + if ( singleSlashCmd ) { + /* Not really a query, but "Do what I mean, not what I say." */ + line = singleQuery; + } + else { + line = malloc(strlen(qfilename) + 5); + sprintf(line,"\\i %s", qfilename); + } + HandleSlashCmds(&db, line, (char**)prompt, "", &settings); + + } else { + if (singleQuery) { + exitStatus = SendQuery(db, singleQuery, &settings); + } + else + exitStatus = MainLoop(&db, stdin, &settings); + } + + PQfinish(db); + + return exitStatus; +} + + +static void +handleCopyOut(PGresult *res, int quiet) +{ + bool copydone = false; + char copybuf[COPYBUFSIZ]; + int ret; + + if (!quiet) + fprintf(stdout, "Copy command returns...\n"); + + while (!copydone) { + ret = PQgetline(res->conn, copybuf, COPYBUFSIZ); + + if (copybuf[0] == '.' && copybuf[1] =='\0') { + copydone = true; /* don't print this... */ + } else { + fputs(copybuf, stdout); + switch (ret) { + case EOF: + copydone = true; + /*FALLTHROUGH*/ + case 0: + fputc('\n', stdout); + break; + case 1: + break; + } + } + } + fflush(stdout); + PQendcopy(res->conn); +} + + +static void +handleCopyIn(PGresult *res, int quiet) +{ + bool copydone = false; + bool firstload; + bool linedone; + char copybuf[COPYBUFSIZ]; + char *s; + int buflen; + int c; + + if (!quiet) { + fputs("Enter info followed by a newline\n", stdout); + fputs("End with a dot on a line by itself.\n", stdout); + } + + /* + * eat extra newline still in input buffer + * + */ + fflush(stdin); + if ((c = getc(stdin)) != '\n' && c != EOF) { + (void) ungetc(c, stdin); + } + + while (!copydone) { /* for each input line ... */ + if (!quiet) { + fputs(">> ", stdout); + fflush(stdout); + } + firstload = true; + linedone = false; + while (!linedone) { /* for each buffer ... */ + s = copybuf; + buflen = COPYBUFSIZ; + for (; buflen > 1 && + !(linedone = (c = getc(stdin)) == '\n' || c == EOF); + --buflen) { + *s++ = c; + } + if (c == EOF) { + /* reading from stdin, but from a file */ + PQputline(res->conn, "."); + copydone = true; + break; + } + *s = '\0'; + PQputline(res->conn, copybuf); + if (firstload) { + if (!strcmp(copybuf, ".")) { + copydone = true; + } + firstload = false; + } + } + PQputline(res->conn, "\n"); + } + PQendcopy(res->conn); +} + + +/* try to open fname and return a FILE*, + if it fails, use stdout, instead */ +FILE* +setFout(char *fname) +{ + FILE *queryFout; + + if (!fname) + queryFout = stdout; + else { + queryFout = fopen(fname, "w"); + if (!queryFout) { + perror(fname); + queryFout = stdout; + } + } + + return queryFout; +} + diff --git a/src/bin/psql/psqlHelp.h b/src/bin/psql/psqlHelp.h new file mode 100644 index 00000000000..e0d5077bc3b --- /dev/null +++ b/src/bin/psql/psqlHelp.h @@ -0,0 +1,168 @@ +/*------------------------------------------------------------------------- + * + * psqlHelp.h-- + * Help for query language syntax + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: psqlHelp.h,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +struct _helpStruct { + char* cmd; /* the command name */ + char* help; /* the help associated with it */ + char* syntax; /* the syntax associated with it */ +} ; + +static struct _helpStruct QL_HELP[] = { + { "abort", + "abort the current transaction", + "abort [transaction];"}, + { "abort transaction", + "abort the current transaction", + "abort [transaction];"}, + { "alter table", + "add/rename attributes, rename tables", + "alter table <relname> [*] add column <attr> <type>;\n\talter table <relname> [*] rename [column] <attr1> to <attr2>;\n\talter table <relname1> rename to <relname2>"}, + { "begin", + "begin a new transaction", + "begin [transaction|work];"}, + { "begin transaction", + "begin a new transaction", + "begin [transaction|work];"}, + { "begin work", + "begin a new transaction", + "begin [transaction|work];"}, + { "cluster", + "create a clustered index (from an existing index)", + "cluster <index_name> on <relation_name>"}, + { "close", + "close an existing cursor (portal)", + "close <portalname>;"}, + { "commit", + "commit a transaction", + "commit [work]"}, + { "commit work", + "commit a transaction", + "commit [work]"}, + { "copy", + "copy data to and from a table", + "copy [binary] [nonulls] <relname>\n\t{to|from} {<filename>|stdin|stdout} [using delimiters <delim>];"}, + { "create", + "Please more be specific:", + "\tcreate aggregate\n\tcreate database\n\tcreate function\n\tcreate index\n\tcreate operator\n\tcreate rule\n\tcreate table\n\tcreate type\n\tcreate view"}, + { "create aggregate", + "define an aggregate function", + "create aggregate <agg_name> [as] (basetype = <data_type>, \n\t[sfunc1 = <sfunc_1>, stype1 = <sfunc1_return_type>]\n\t[sfunc2 = <sfunc_2>, stype2 = <sfunc2_return_type>]\n\t[,finalfunc = <final-function>]\n\t[,initcond1 = <initial-cond1>][,initcond2 = <initial-cond2>]);"}, + { "create database", + "create a database", + "create database <dbname>"}, + { "create function", + "create a user-defined function", + "create function <function_name> ([<type1>,...<typeN>]) returns <return_type>\n\tas '<object_filename>'|'<sql-queries>'\n\tlanguage 'c'|'sql'|'internal';"}, + { "create index", + "construct an index", + "create index <indexname> on <relname> using <access_method> (<attr1>|<funcname>(<attr1>,...) <type_class1>);"}, + { "create operator", + "create a user-defined operator", + "create operator <operator_name> (\n\t[leftarg = <type1>][,rightarg = <type2>]\n\t,procedure = <func_name>,\n\t[,commutator = <com_op>][,negator = <neg_op>]\n\t[,restrict = <res_proc>][,hashes]\n\t[,join = <join_proc>][,sort = <sort_op1>...<sort_opN>]);"}, + { "create rule", + "define a new rule", + "create rule <rule_name> as on\n\t[select|update|delete|insert]\n\tto <object> [where <qual>]\n\tdo [instead] [<action>|nothing| [<actions>]];"}, + { "create table", + "create a new table", + "create table <relname> ( <attr1> <type1>,... <attrN> <typeN>)\n\t[inherits (<relname1>,...<relnameN>\n\tarchive=<archive_mode>\n\tstore=<smgr_name>\n\tarch_store=<smgr_name>];"}, + { "create type", + "create a new user-defined base data type", + "create type <typename> (\n\tinternallength = (<number> | variable),\n\t[externallength = (<number>|variable),]\n\tinput=<input_function>, output = <output_function>\n\t[,element = <typename>][,delimiter=<character>][,default=\'<string>\']\n\t[,send = <send_function>][,receive = <receive_function>][,passedbyvalue]);"}, + { "create view", + "create a view", + "create view <view_name> as select <expr1>[as <attr1>][,... <exprN>[as <attrN>]] [from <from_list>] [where <qual>];"}, + { "declare", + "set up a cursor (portal)", + "declare <portalname> [binary] cursor for\n\tselect [distinct]\n\t<expr1> [as <attr1>],...<exprN> [as <attrN>]\n\t[from <from_list>] [where <qual>]\n\t[order by <attr1> [using <op1>],... <attrN> [using <opN>]];"}, + { "delete", + "delete tuples", + "delete from <relname> [where <qual>];"}, + { "drop", + "Please more be specific:", + "\tdrop aggregate\n\tdrop database\n\tdrop function\n\tdrop index\n\tdrop operator\n\tdrop rule\n\tdrop table\n\tdrop type\n\tdrop view"}, + { "drop aggregate", + "remove an aggregate function", + "drop aggregate <agg_name>;"}, + { "drop database", + "remove a database", + "drop database <dbname>"}, + { "drop function", + "remove a user-defined function", + "drop function <funcname> ([<type1>,....<typeN>]);"}, + { "drop index", + "remove an existing index", + "drop index <indexname>;"}, + { "drop operator", + "remove a user-defined operator", + "drop operator <operator_name> ([<ltype>|none],[<rtype>|none]);"}, + { "drop rule", + "remove a rule", + "drop rule <rulename>;"}, + { "drop table", + "remove a table", + "drop table <relname>[,...<relnameN];"}, + { "drop type", + "remove a user-defined base type", + "drop type <typename>;"}, + { "drop view", + "remove a view", + "drop view <view_name>"}, + { "end", + "end the current transaction", + "end [transaction];"}, + { "end transaction", + "end the current transaction", + "end [transaction];"}, + { "explain", + "explain the query execution plan", + "explain [with {cost|full_plan}] <query>"}, + { "extend index", + "extend a partial index", + "extend index <indexname> [where <qual>];"}, + { "fetch", + "retrieve tuples from a cursor (portal)", + "fetch [forward|backward] [<number>|all] [in <portalname>];"}, + { "grant", + "grant access control to a user or group", + "grant <privilege[,privilege,...]> on <rel1>[,...<reln>] to \n[public | group <group> | <username>]\n\t privilege is {ALL | SELECT | INSERT | UPDATE | DELETE | RULE}"}, + { "insert", + "insert tuples", + "insert into <relname> [(<attr1>...<attrN>)]\n\t[values (<expr1>...<exprN>); |\n\tselect <expr1>,...<exprN> [from <from_clause>] [where <qual>];"}, + { "listen", + "listen for notification on a relation", + "listen <relname>"}, + { "load", + "dynamically load a module", + "load <filename>;"}, + { "notify", + "signal all frontends and backends listening on a relation", + "notify <relname>"}, + { "purge", + "purge historical data", + "purge <relname> [before <abstime>] [after <reltime>];"}, + { "revoke", + "revoke access control from a user or group", + "revoke <privilege[,privilege,...]> on <rel1>[,...<reln>] from \n[public | group <group> | <username>]\n\t privilege is {ALL | SELECT | INSERT | UPDATE | DELETE | RULE}"}, + { "rollback", + "abort a transaction", + "rollback [work]"}, + { "select", + "retrieve tuples", + "select [distinct on <attr>] <expr1> [as <attr1>], ... <exprN> [as <attrN>]\n\t[into table <relname>] [from <from_list>]\n\t[where <qual>]\n\t[order by <attr1>\n\t\t[using <op1>],..<attrN> [[using <opN>] | ASC | DESC]];" }, + { "update", + "update tuples", + "update <relname> set <attr1>=<expr1>,...<attrN>=<exprN> [from <from_clause>] [where <qual>];"}, + { "vacuum", + "vacuum the database, i.e. cleans out deleted records, updates statistics", + "vacuum;"}, + { NULL, NULL, NULL} /* important to keep a NULL terminator here! */ +}; diff --git a/src/bin/psql/rlstubs.c b/src/bin/psql/rlstubs.c new file mode 100644 index 00000000000..2640d3ce094 --- /dev/null +++ b/src/bin/psql/rlstubs.c @@ -0,0 +1,41 @@ +/*------------------------------------------------------------------------- + * + * rlstubs.c-- + * stub routines when compiled without readline and history libraries + * + * Copyright (c) 1994-5, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/bin/psql/Attic/rlstubs.c,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> + +char * +readline(char *prompt) +{ + static char buf[500]; + + printf("%s"); + return fgets(buf, 500, stdin); +} + +int +write_history() +{ + return 0; +} + +int +using_history() +{ + return 0; +} + +int +add_history() +{ + return 0; +} diff --git a/src/bin/psql/stringutils.c b/src/bin/psql/stringutils.c new file mode 100644 index 00000000000..0a9043c6337 --- /dev/null +++ b/src/bin/psql/stringutils.c @@ -0,0 +1,104 @@ +/*------------------------------------------------------------------------- + * + * stringutils.c-- + * simple string manipulation routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/bin/psql/stringutils.c,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include "stringutils.h" + +/* all routines assume null-terminated strings! */ + +/* removes whitespaces from the left, right and both sides of a string */ +/* MODIFIES the string passed in and returns the head of it */ +char* leftTrim(char* s) +{ + char* s2 = s; + int shift=0; + int j=0; + + while (isspace(*s)) + { s++; shift++;} + if (shift > 0) + { + while ( (s2[j] = s2[j+shift]) !='\0') + j++; + } + + return s2; +} + +char* rightTrim(char* s) +{ + char* sEnd; + sEnd = s+strlen(s)-1; + while (isspace(*sEnd)) + sEnd--; + if (sEnd < s) + s[0]='\0'; + else + s[sEnd-s+1]='\0'; + return s; +} + +char* doubleTrim(char* s) +{ + strcpy(s,leftTrim(rightTrim(s))); + return s; +} + +/* dupstr : copies a string, while allocating space for it. + the CALLER is responsible for freeing the space + returns NULL if the argument is NULL*/ +char* dupstr(char *s) +{ + char* result; + + if (s == NULL) + return NULL; + + result = (char*)malloc(strlen(s)+1); + strcpy(result, s); + return result; +} + + +#ifdef STRINGUTILS_TEST +void testStringUtils() +{ + static char* tests[] = {" goodbye \n", /* space on both ends */ + "hello world", /* no spaces to trim */ + "", /* empty string */ + "a", /* string with one char*/ + " ", /* string with one whitespace*/ + NULL_STR}; + + int i=0; + while (tests[i]!=NULL_STR) + { + char* t; + t = dupstr(tests[i]); + printf("leftTrim(%s) = ",t); + printf("%sEND\n", leftTrim(t)); + t = dupstr(tests[i]); + printf("rightTrim(%s) = ",t); + printf("%sEND\n", rightTrim(t)); + t = dupstr(tests[i]); + printf("doubleTrim(%s) = ",t); + printf("%sEND\n", doubleTrim(t)); + i++; + } + +} + +#endif diff --git a/src/bin/psql/stringutils.h b/src/bin/psql/stringutils.h new file mode 100644 index 00000000000..d8564a02d08 --- /dev/null +++ b/src/bin/psql/stringutils.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------- + * + * stringutils.h-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: stringutils.h,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef STRINGUTILS_H +#define STRINGUTILS_H + +/* use this for memory checking of alloc and free using Tcl's memory check + package*/ +#ifdef TCL_MEM_DEBUG +#include <tcl.h> +#define malloc(x) ckalloc(x) +#define free(x) ckfree(x) +#define realloc(x,y) ckrealloc(x,y) +#endif + +/* string fiddling utilties */ + +/* all routines assume null-terminated strings! as arguments */ + +/* removes whitespaces from the left, right and both sides of a string */ +/* MODIFIES the string passed in and returns the head of it */ +extern char* leftTrim(char* s); +extern char* rightTrim(char* s); +extern char* doubleTrim(char* s); + +/* dupstr : copies a string, while making room for it */ +/* the CALLER is responsible for freeing the space */ +/* returns NULL if the argument is NULL */ +extern char* dupstr(char *s); + +#ifdef STRINGUTILS_TEST +extern void testStringUtils(); +#endif + +#ifndef NULL_STR +#define NULL_STR (char*)0 +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#endif /* STRINGUTILS_H */ |