Re: Ingres -> Oracle Conversion Kits.

From: Johan Montald <johan_at_dombo>
Date: 20 Sep 1994 07:42:47 GMT
Message-ID: <35m3pn$9v3_at_zebedee.ingres.co.uk>


ctr_at_cybernetics.net wrote:

: Hello.  My company is looking into using Oracle as our primary database
: application. Since we currently use Ingres, I was wondering if anyone has
: found/used any conversion tools.  Let me know what type of experiences,
: good and bad, that you have had.  Thank you for your time.
 

: Kevin Eberwein
: ClinTrials Research, Inc. - RTP Division

Hi,

This was posted some time ago for doing a conversion from Ingres to Oracle, its supposed to work in both ways.

DATABASE UNLOAD UTILITY


This is the sources of two programs to unload an Oracle or Ingres database as an SQL stream.

--------------Cut here-----------------

#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -d ./db_unload`
then
  mkdir ./db_unload
  echo "mkdir ./db_unload"
fi
if `test ! -d ./db_unload/ORACLE`
then
  mkdir ./db_unload/ORACLE
  echo "mkdir ./db_unload/ORACLE"
fi
if `test ! -s ./db_unload/ORACLE/MAKEFILE` then
echo "writing ./db_unload/ORACLE/MAKEFILE" cat > ./db_unload/ORACLE/MAKEFILE << '\Rogue\Monster\'
# When using AT&T C++ the following symbols are defined
#	c_plusplus, __cplusplus, unix, sun

# When using g++ the folllowing symbols are defined
# __GNUG__, __GNUC__, __cplusplus SUFFIXES: .c .sc .pc .C .cc .pcc

# ------------The following for AT&T C++ ---
# This program, although it compiles, will not run if you use the AT&T
# compiler. It will crash as soon as it tries to connect to the DB.
#SUFFIX	= C
#G++	= CC 
#CC	=/usr/5bin/cc
#G++FLAGS= -sys5 -I. -g 
#CFLAGS	= -I. -I/usr/5include -g 
#LDFLAGS	= -Bstatic

#---- AT&T C++ with gcc as the back end. This one works
#SUFFIX	= C
#G++	= CC 
#CC=gcc#/usr/5bin/cc
#G++FLAGS= -sys5 -I. -g 
#CFLAGS	= -I/home/pisces/ingres_conv/raph/lib/gcc-include -I. -I/usr/5include -g 
#LDFLAGS	= -Bstatic
#SQLLIB	= -L/usr/lang/SC1.0 -lC

#------------ The following for GNU g++
SUFFIX	= cc
G++	= g++
CC	= gcc
G++FLAGS= -I. -I/usr/local/include -g -O -w
CFLAGS	= -I. -g -O 
LDFLAGS	= -static
#-------------------------------------
GCC	= gcc
PCC 	= pcc
DBNAME	= rob_dev
USERID	= rob_dev/rob_dev

ORACLE_INCLUDE = $(ORACLE_HOME)/c/lib
PCCFLAGS= include=$(ORACLE_INCLUDE) ireclen=511 oreclen=132 host=C\

        maxopencursors=20
ORA_C_LIBDIR = $(ORACLE_HOME)/c/lib
ORA_RDBMS_LIBDIR = $(ORACLE_HOME)/rdbms/lib  

ORALIBS = $(ORA_RDBMS_LIBDIR)/libsql.a $(ORA_RDBMS_LIBDIR)/osntab.o \ $(ORA_RDBMS_LIBDIR)/libsqlnet.a $(ORA_RDBMS_LIBDIR)/libora.a  

BINDIR=/home/pisces/ingres_conv/raph/bin SRC=\

	makefile\
	TODO\
	data_dict.hpp\
	c_data.pcc\
	column.pcc\
	dequote.c\
	dereserve.c\
	index.pcc\
	table.pcc\
	view.pcc

# Object files for the database conversion program
DOBJ=\

	c_data.o\
	column.o\
	dequote.o\
	dereserve.o\
	index.o\
	table.o\
	view.o


YACC	= bison 
YFLAGS	= -dvy

#.pcc.c:
#	$(PCC) iname=$*.pcc oname=/tmp/$*.x.cc $(PCCFLAGS) 
#	sed -e '/^# *[0-9]/d' -e '/struct  *sqlca/s//struct sql_ca/' \
#	-e '/struct  *SQLDA/s//struct SQL_DA/' \
#	-e '/extern  *sql.*();/d' < /tmp/$*.x.cc > /tmp/$*.${SUFFIX}
#	$(G++) $(G++FLAGS) /tmp/$*.${SUFFIX} >$_at_
pcc.o:
	$(PCC) iname=$*.pcc oname=/tmp/$*.x.cc $(PCCFLAGS) 
	sed -e '/^# *[0-9]/d' -e '/struct  *sqlca/s//struct sql_ca/' \
	-e '/struct  *SQLDA/s//struct SQL_DA/' \
	-e '/extern  *sql.*();/d' < /tmp/$*.x.cc > /tmp/$*.${SUFFIX}
	$(G++) $(G++FLAGS) -c /tmp/$*.${SUFFIX}

cc.o:
	$(G++) $(G++FLAGS) -c $*.${SUFFIX}
#-------------------------------------------------------------------------

# NOTE: ORACLE_HOME must be either:
# . set in the user's environment # . passed in on the command line # . defined in a modified version of this makefile
#

OCILIB = #$(ORACLE_HOME)/c/lib/libocic.a #PCCLIBS = $(ORACLE_HOME)/rdbms/lib/libpcc.a $(SQLLIB) $(OCILIB) PCCLIBS = $(SQLLIB) $(OCILIB)
STLIBS= $(ORACLE_HOME)/rdbms/lib/osntabst.o \

        $(ORACLE_HOME)/rdbms/lib/config.o

all: c_data

c_data:	$(DOBJ)
	$(G++) $(G++FLAGS) $(LDFLAGS) -o $_at_ $(DOBJ)\
		-L/usr/5lib $(PCCLIBS) $(NETLIBS) $(ORALIBS) $(SQLLIB)
pc.c:
	$(PCC) $(PCCFLAGS) iname=$*.pc 

pc.o:
	$(PCC) $(PCCFLAGS) iname=$*.pc oname=/tmp/$*.c
	$(CC) $(CFLAGS) -c /tmp/$*.c

pc:
	-$(PCC) iname=$*.pc oname=/tmp/$*.c $(PCCFLAGS) userid=$(USERID)
	$(CC) $(CFLAGS) -o $* /tmp/$*.c -L/usr/5lib $(SQLLIB) $(NETLIBS) $(ORALIBS) $(OTHERLIBS)
#
#.c.exe:
#	$(CC) $(CFLAGS) -o $* $*.c $(SQLLIB) $(OCILIB) $(NETLIBS) $(ORALIBS) $(OTHERLIBS)
#
#-------------------------------------------------------------------------

$(DOBJ):	data_dict.hpp

print:	$(SRC) 
	pr -f $? |lpr
	touch print
printall:
	pr -f $(SRC) | lpr
	touch print
clean:
	rm -f *.o core *.lis

#-------------------------------------------------------
sc.o:
	esqlc -o.sh -l -f$*.c $<
	$(CC) -c $(CFLAGS) $*.c
sc.c:
	esqlc -o.sh -l -f$*.c $<

test1:	test1.o
	$(CC) -g -o $_at_ test1.o /usr/sun4/ingres/lib/libingres.a -lm

backup:	$(SRC) 
	-mkdir /disk2/tmp/raph/vers6; chmod 777 /disk2/tmp/raph/vers6
	-chmod u+w  /disk2/tmp/raph/vers6/*
	cp $? /disk2/tmp/raph/vers6
	-rm -f *.lis
	touch backup
dereserve.o dequote.o:	$$(_at_:.o=.c)
	$(CC) -c $(CFLAGS) $(_at_:.o=.c)
test:	c_data
	c_data -d $(DBNAME)
install:	c_data
	install -s -c c_data $(BINDIR)

C: $(DOBJ:.o=.c)

\Rogue\Monster\
else
  echo "will not over write ./db_unload/ORACLE/MAKEFILE" fi
if `test ! -s ./db_unload/ORACLE/TODO`
then
echo "writing ./db_unload/ORACLE/TODO"
cat > ./db_unload/ORACLE/TODO << '\Rogue\Monster\' \Rogue\Monster\
else
  echo "will not over write ./db_unload/ORACLE/TODO" fi
if `test ! -s ./db_unload/ORACLE/DEQUOTE.C` then
echo "writing ./db_unload/ORACLE/DEQUOTE.C" cat > ./db_unload/ORACLE/DEQUOTE.C << '\Rogue\Monster\' /* DEQUOTE.C This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk)

*/

#ifdef SYSV
#include <string.h>
#define index   strchr
#else
#include <strings.h>
#endif

/*
	If s.arr contains embedded apostrophes, we have to replace each
	one by a doubled apostrophe in order to keep Ingres happy.
	This we do by counting the total number of apostophes and
	shuffling data to the right by the number of apostrophes to
	the left of each byte.

*/

dequote(s)
struct varchar {

	short	len;
	char	arr[1];
}	*s;
{	int	i, j, len, count, count2;
	char	*p;

	if (!(p=index(s->arr, '\'')))
		return ;
	count = 1;
	while (p= index(p+1, '\''))
		count++;
	count2	= count;
	for (i= s->len-1; count > 0; i--) {
		if (s->arr[i] == '\'') {
			s->arr[i+count] = s->arr[i];
			count--;
		}
 s->arr[i+count] = s->arr[i];
	}
	s->len		+= count2;
	s->arr[s->len]	= 0;

	return ;

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/ORACLE/DEQUOTE.C" fi
if `test ! -s ./db_unload/ORACLE/DERESERV.C` then
echo "writing ./db_unload/ORACLE/DERESERV.C" cat > ./db_unload/ORACLE/DERESERV.C << '\Rogue\Monster\' /* DERESERVE.C This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk)

CHANGES:
7/4/92 Change returned type from void to char*

*/

#include <stdio.h>
#ifndef __GNUG__	/* For AT&T compiler	*/
#define stricmp strcasecmp
#include <strings.h>
#else
#include <string.h>
#endif

static char *reserved[] = {
			"command",
			"count",
			"default",
			"file",
			"index",
			0
		};

const char *dereserve(const char name[])
{	int	i;
	static char	buf[100];

	for (i=0; reserved[i]; i++) {
	    if (!stricmp(name, reserved[i])) {
		strcpy(buf, name);
		strcat(buf, "_x");
		return buf;
	    }
	}

	return name;

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/ORACLE/DERESERV.C" fi
if `test ! -s ./db_unload/ORACLE/BOOL.H` then
echo "writing ./db_unload/ORACLE/BOOL.H" cat > ./db_unload/ORACLE/BOOL.H << '\Rogue\Monster\' #ifndef _BOOL_
#define _BOOL_
enum bool {FALSE=0, TRUE=1};
#endif
\Rogue\Monster\
else
  echo "will not over write ./db_unload/ORACLE/BOOL.H" fi
if `test ! -s ./db_unload/ORACLE/DATA_DIC.HPP` then
echo "writing ./db_unload/ORACLE/DATA_DIC.HPP" cat > ./db_unload/ORACLE/DATA_DIC.HPP << '\Rogue\Monster\' /* DATA_DICT.HPP         Oracle Data Dictionary structures

This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk)

*/

#ifndef DATA_DICT_
#include <stream.h>
#include <bool.h>

#ifndef __GNUG__		/* For AT&T compiler	*/
extern "C" void exit(int);
#define stricmp strcasecmp
extern "C" char *strcpy(...);
extern "C" int strcasecmp(...);
extern "C" int strncmp(...);
extern "C" char *strchr(...);
extern "C" int strlen(...);
#include <bstring.h>		/* for bzero()	*/

const char *dereserve(const char *);
#else
extern "C" const char *dereserve(const char *); #endif

#define DATA_DICT_
#define NAME_LENGTH 33

class Table;
class Index;
class View;
enum col_type { NUM, CHAR, DATE, FLOAT, MONEY};

class DatabaseObject {

	char	object_name[NAME_LENGTH];
	char	owner_name[NAME_LENGTH];
public:
	DatabaseObject(const char name[], const char owner[]);
	const char	*name()		{ return object_name;	}
	const char	*owner()	{ return owner_name;	}
};
class	Column {
	char	cname[NAME_LENGTH];
	bool	nulls;			/* 'NULL' or 'NOT NULL' */
	bool	WithDefault;
	col_type 	coltype;	// CHAR, NUMBER or DATE
	int	colwidth;
	Table	*parent;
	Column	*next;
	bool	IndexColumn;
public:
	const char *name();
	Column *link();
	int	width();
	int	type();
	Column(long  number, Table *up, Column *prev);
	Column(long  number, View *up, Column *prev);
	~Column();
	friend ostream& operator<<(ostream & s, Column & c);
	void	set_index_col();
	bool	is_index_column();
	bool	non_null() {	return !nulls;	};

};

class Table :public DatabaseObject {

	Column	*cols;		// The column chain
	Index	**index_list;	// The indexes on this table
	int	IndexCount;	// sizeof(index_list)
	bool	NoDuplicates;	// WITH NODUPLICATES clause
public:
	Table(const char name[], const char *owner="");
	~Table();
	friend ostream& operator<<(ostream& s, Table & t);
	void	extract(ostream &s);
	Column	*column(const char cname[]);
	void	SetNoDuplicates();
	void	grant(ostream&);

};

class View : public DatabaseObject {

	char	*vtext;		// The view text
	Column	*cols;		// The column chain
public:
	View();
	View(const char [], const char *owner="");
	~View();
	friend ostream& operator<<(ostream &s, View& v);
};

enum index_type { UNIQUE, NON_UNIQUE};
enum index_order {ASC, DESC};

class Index : public DatabaseObject {

	Table		*parent;
	index_type	type;		// UNIQUE or non-unique
	int		index_count;
	struct index_field {
	    index_order	order;
	    char	*name;
	}			*column_list;
public:
	Index();
	Index(Table *t, char unique);
	Index(Table *t, const char iname[]);
	~Index();
	Index *link();
	void modify(ostream& s);
	friend ostream& operator<<(ostream &s, Index *ind);
	bool	OK();

};

inline DatabaseObject::DatabaseObject(const char name[], const char user[])

{	strcpy(object_name, name);
	char *s = strchr(object_name, ' ');
	if (s) *s = 0;
	strcpy(owner_name, user);
	s = strchr(owner_name, ' ');
	if (s) *s = 0;

}
inline Column::~Column() 		{ if(next) delete next; }
inline const char *Column::name()	{ return cname;		}
inline Column *Column::link()		{ return next;		}
inline Column::type()			{ return coltype;	}
inline Column::width()			{ return colwidth;	}
//inline void Column::set_non_null()	{ nulls	= FALSE;	}
inline void Column::set_index_col()	{ nulls	= FALSE; IndexColumn = TRUE;}
inline bool Column::is_index_column()	{ return IndexColumn;	}


inline View::View() : DatabaseObject("", "")
{	vtext	= 0;
	cols	= 0;

}
inline View::~View()
{
	delete vtext;
	if (cols) delete cols;

}

inline Index::Index() : DatabaseObject ("", "") {

	parent	= 0;
	column_list=0;

}

inline void Table::SetNoDuplicates() { NoDuplicates = TRUE; }

extern "C"	sqlab2(...);
extern "C"	sqlad2(...);
extern "C"	sqlbs2(...);
extern "C"	sqlcls(...);
extern "C"	sqlexe(...);
extern "C"	sqlfcc(...);
extern "C"	sqlfch(...);
extern "C"	sqliem(...);
extern "C"	sqllo2(unsigned long *, unsigned char *[], unsigned long [], unsigned short[], unsigned long *, int *, int *, unsigned long *);
extern "C"	sqlopn(...);
extern "C"	sqlosq(...);
extern "C"	sqlsca(...);
extern "C"	sqlscc(...);
extern "C"	sqlsch(...);
extern "C"	sqltfl(...);
extern "C"	sqltoc(...);
extern "C"	sqlos2(...);
extern "C"	sqlclu(...);
extern "C"	sqlgd2(...);
extern "C"	void *sqlald(int, int, int);
extern	"C" void dequote(void *);
#ifdef INGRES
#define	NOT_FOUND	100
#else
#define	NOT_FOUND	1403

#endif
#ifndef EXTERN
#define EXTERN extern
#endif
#define SQLCA_STORAGE_CLASS static

EXTERN char	*GoString;
EXTERN bool	OracleOut;
EXTERN bool	Default;
EXTERN bool	NotNull;

#endif
\Rogue\Monster\
else
  echo "will not over write ./db_unload/ORACLE/DATA_DIC.HPP" fi
if `test ! -s ./db_unload/ORACLE/COLUMN.PCC` then
echo "writing ./db_unload/ORACLE/COLUMN.PCC" cat > ./db_unload/ORACLE/COLUMN.PCC << '\Rogue\Monster\' /* COLUMN.PCC  Process a column definition, while converting a database table  from Oracle to Ingres.

This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk)

CHANGES:
7/4/92 Deresrve all names.

*/
#include <data_dict.hpp>

EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
static VARCHAR sql_tname[31];
static char

	sql_cname[31],
	sql_coltype[7],
	sql_nulls[2],
	sql_defaultval[1000];
static long
	sql_width,	/* Used for char types		*/
	sql_precision,	/* Used for numeric types	*/
	sql_scale,
	sql_colno;

EXEC SQL END DECLARE SECTION; Column::Column(long num, Table *up, Column *succ) {
	next		= succ;
	IndexColumn	= FALSE;
	parent		= up;
	sql_colno	= num;
	strcpy(sql_tname.arr, up->name());
	sql_tname.len	= strlen(sql_tname.arr);
	EXEC SQL SELECT 
			column_name,
			data_type,
			data_length,
			data_precision,
			data_scale,
			nullable,
			data_default
		 INTO	:sql_cname,
			:sql_coltype,
			:sql_width,
			:sql_precision,
			:sql_scale,
			:sql_nulls,
			:sql_defaultval
		 FROM accessible_columns
		 WHERE table_name = :sql_tname AND
		       column_id = :sql_colno;
	if (sqlca.sqlcode) {
		cerr << "Oracle select error " << sqlca.sqlcode << " in Column::Column(long, Table*, Column*)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		cerr << form("Table name: %s, column id %d\n",
				sql_tname.arr, sql_colno);
		exit(1);
	}
	sql_cname[sizeof(sql_cname)-1]			= 0;
	char	*s = strchr(sql_cname, ' ');
	if (s)	*s = 0;
	if (strlen(sql_cname) > 24) {
	   cerr << form("Column name '%s' exceeds 24 characters\n",
			sql_cname);
	}
	sql_defaultval[sizeof(sql_defaultval)-1]	= 0;
	sql_coltype[sizeof(sql_coltype)-1]		= 0;
	sql_nulls[sizeof(sql_nulls)-1]			= 0;
	strcpy(cname, sql_cname);
	if (sql_nulls[0] == 'Y' && !NotNull)
	    nulls	= TRUE;
	else
	    nulls	= FALSE;
	if (!strncmp(sql_coltype, "NUM", 3))	coltype= NUM;
	else if (!strncmp(sql_coltype, "DATE", 4)) coltype= DATE;
	else					coltype	= CHAR;
	if (!strncmp(sql_coltype, "LONG", 4))
		sql_width	= 1000;
	if (coltype==NUM)
	    colwidth	= sql_precision;
	else
	    colwidth	= sql_width;

}  

Column::Column(long num, View *up, Column *succ) {

	next		= succ;
	IndexColumn	= FALSE;
	parent		= (Table*)up;
	sql_colno	= num;
	strcpy(sql_tname.arr, up->name());
	sql_tname.len	= strlen(sql_tname.arr);
	EXEC SQL SELECT 
			column_name
		 INTO	:sql_cname
		 FROM accessible_columns
		 WHERE table_name = :sql_tname AND
		       column_id = :sql_colno;
	if (sqlca.sqlcode) {
		cerr << "Oracle select error " << sqlca.sqlcode << " in Column::Column(long, View*, Column*)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	sql_cname[sizeof(sql_cname)-1]			= 0;
	char	*s = strchr(sql_cname, ' ');
	if (s)	*s = 0;
	if (strlen(sql_cname) > 24) {
	   cerr << form("Column name '%s' exceeds 24 characters\n",
			sql_cname);
	}

 strcpy(cname, sql_cname);
}  

ostream& operator<<(ostream& s, Column& c) {

	s << form("\t%-.24s\t", dereserve(c.cname));
	switch (c.coltype) {
	case CHAR:
		s << form("%s(%d)",
			OracleOut?"VARCHAR":"VARCHAR",
			c.colwidth);
		break;
	case NUM:
		s << form(c.colwidth<3? "SMALLINT":
				c.colwidth< 5? "INTEGER2":
					"INTEGER");
		break;
	case DATE:
		s << form("DATE");
		break;
	}
	if (!c.nulls) {
		s << " NOT NULL";
		if (Default)
		    if (!c.IndexColumn)
			s << " WITH DEFAULT";
	}

 return s;
}

// Locate a column by name
Column *Table::column(const char cname[]) {

	for (Column *c = cols; c; c= c->link())
	    if (!stricmp(cname, c->name()))
		return c;
	return 0;

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/ORACLE/COLUMN.PCC" fi
if `test ! -s ./db_unload/ORACLE/C_DATA.PCC` then
echo "writing ./db_unload/ORACLE/C_DATA.PCC" cat > ./db_unload/ORACLE/C_DATA.PCC << '\Rogue\Monster\' /* C_DATA           Convert an Oracle database to an Ingres database.

    This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk)

Options and arguments

	-d	convert the data as well as the structure
	-g	generate GRANT commands on all tables and views
	-n	generate NOT NULL on all columns
	-v	generate WITH DEFAULT on all NOT NULL columns
	-o	generate Oracle compatible output rather than
		Ingres. i.e.
			user ';' instead of '\p\g'
			'create index' instead of 'modify'
		The Ingres manuals always say 'varchar' but
		the programs will not accept it.
	db_name name of the DB to be processed. We assume that the 
	-V	Views only
		password is the same as the db-name.

*/

#define EXTERN
EXEC SQL INCLUDE SQLCA;

#include "data_dict.hpp"
#include  <stream.h>
#include <ctype.h>

void affix(const char *filename);

EXEC SQL BEGIN DECLARE SECTION;

VARCHAR db_name[21],		/* Database name		*/
	owner[31],	/* Pattern for matching owner	*/
	tabtype[8];		/* VIEW or TABLE		*/
char	tname[31],		/* Table or view name		*/
	uname[31];		/* Actual owner			*/

EXEC SQL END DECLARE SECTION; main(int argc, char *argv[])
{	bool	ConvertData	= FALSE,
		Grant		= FALSE;
	int	NumberOfTables	= 0;
	bool	ViewsOnly	= FALSE;
	int	stop;

	GoString	= "\\p\\g";
	strcpy(owner.arr, "%");
	owner.len	= strlen(owner.arr);
	while (argc >1 && argv[1][0] == '-') {
	    for (int j=1; argv[1][j]; j++) {
		switch(argv[1][j]) {
		case 'd':
		    ConvertData = TRUE;
		    break;
		case 'g':
		    Grant	= TRUE;
		    break;
		case 'o':		// Oracle compatible output
		    OracleOut = TRUE;
		    GoString  = ";";
		    break;
		case 'n':		// Force NOT NULL on all columns
		    NotNull	= TRUE;
		    break;
		case 'u':			// Specify form owner
		    strcpy(owner.arr, argv[2]);
		    owner.len	= strlen(owner.arr);
		    argc--;
		    argv++;
		    break;
		case 'v':		// Enable WITH DEFAULT
		    Default	= TRUE;
		    break;
		case 'V':
		    ViewsOnly	= TRUE;
		    break;
		default:
		    break;
		}
	    }
	    argc--;
	    argv++;
	}

	if (argc < 2) {
		db_name.len = 4;
		strcpy(db_name.arr, "pcms");
	} else {
		strcpy(db_name.arr, argv[1]);
		db_name.len = strlen(argv[1]);
	}
	EXEC SQL CONNECT :db_name IDENTIFIED BY :db_name;
	if (sqlca.sqlcode ) {
		cerr << "Oracle connect error " << sqlca.sqlcode <<"\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}

	affix("prefix.sql");
	if ( argc > 2) {	/* If user specified a list */
	    for (int i = 2; i < argc; i++) {
		Table *t = new Table(argv[i]);
		cout << *t;
		if (ConvertData) {
		    t->extract(cout);
		}
		if (Grant)
		    t->grant(cout);
		delete t;
	    }
	    affix("postfix.sql");
	    cout << form("commit%s\n", GoString);
	    exit(0);
	} 
	

/*

    Get all tables before all views
*/

	if (!ViewsOnly) {
	EXEC SQL DECLARE tb CURSOR FOR
		 SELECT DISTINCT 
			table_name
		 FROM	user_tables
		 /*WHERE owner LIKE :owner*/
		 ORDER BY table_name;
	EXEC SQL OPEN tb;
	if (sqlca.sqlcode ) {
		cerr << "Oracle 'tb' cursor open error " << sqlca.sqlcode << " in ::main()()\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	for (stop=0; !stop; ) {
		EXEC SQL FETCH tb
			 INTO :tname;
		switch (sqlca.sqlcode) {
		case 0:
			tname[sizeof(tname)-1]	= 0;
			Table *t = new Table(tname);
			cout << *t;
			if (ConvertData) {
			    t->extract(cout);
			}
			if (Grant)
			    t->grant(cout);
			delete t;
			break;
		default:
			cout << form("rollback%s\n", GoString);
			cerr << "Oracle fetch error on 'tb' " << sqlca.sqlcode << " in ::main()\n";
			cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		case NOT_FOUND:
			stop = 1;
			break;
		}
		if (NumberOfTables++ > 20) {
			cout << form("commit%s\n", GoString);
			NumberOfTables	= 0;
		}
	}
 EXEC SQL CLOSE tb;
	}		// End !ViewsOnly

	EXEC SQL DECLARE vw CURSOR FOR
		 SELECT DISTINCT 
			view_name
		 FROM	user_views
		 ORDER BY view_name;
	EXEC SQL OPEN vw;
	if (sqlca.sqlcode ) {
		cerr << "Oracle 'vw' cursor open error " << sqlca.sqlcode << " in ::main()()\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	for (stop=0; !stop; ) {
		EXEC SQL FETCH vw
			 INTO :tname;
		switch (sqlca.sqlcode) {
		case 0:
			tname[sizeof(tname)-1]	= 0;
			View *v = new View(tname);
			cout << *v;
			delete v;
			break;
		default:
			cout << form("rollback%s\n", GoString);
			cerr << "Oracle fetch error on 'vw' " << sqlca.sqlcode << " in ::main()\n";
			cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		case NOT_FOUND:
			stop = 1;
			break;
		}
		if (NumberOfTables++ > 20) {
			cout << form("commit%s\n", GoString);
			NumberOfTables	= 0;
		}
	}
	EXEC SQL CLOSE vw;

	cout << form("commit%s\n", GoString);

	affix("postfix.sql");
	exit(0);

}

void affix(const char *filename)
{

#ifdef __GNUG__
	one_arg_error_handler_t old	=
		    set_File_error_handler(quiet_File_error_handler);
	set_File_error_handler(old);
	istream priv(filename, io_readonly, a_useonly);
#else	
	filebuf fb;
	fb.open(filename, 1);
	istream priv(&fb);
#endif
	char	privbuf[51];

	while (priv.rdstate() == _good) {
		priv.getline(privbuf, sizeof privbuf);
		cout << privbuf;
	}

 cout << form("\ncommit%s\n", GoString); }
\Rogue\Monster\
else
  echo "will not over write ./db_unload/ORACLE/C_DATA.PCC" fi
if `test ! -s ./db_unload/ORACLE/INDEX.PCC` then
echo "writing ./db_unload/ORACLE/INDEX.PCC" cat > ./db_unload/ORACLE/INDEX.PCC << '\Rogue\Monster\' /* INDEX.PCC This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk)
	At version 6.0 all Oracle indexes are in ascending order
	regardless of what the user requests.
CHANGES:
7/4/92	Dereserve all names.

*/
#include "data_dict.hpp"
EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
static VARCHAR	sql_iname[31],
		sql_tname[31],
		sql_colname[31];
static char	sql_itype[11],	/* UNIQUE or NON UNIQUE	*/
		sql_iorder[5];
static int	sql_seq;

EXEC SQL END DECLARE SECTION; Index::Index(Table *up, const char iname[]) :

                        DatabaseObject(iname, "")
{

	parent		= up;
	column_list	= 0;
	if (strlen(name()) > 24) 
	   cerr << form("Index name '%s' exceeds 24 characters\n",
			name());
	strcpy(sql_iname.arr, name());
	sql_iname.len	= strlen(name());
	strcpy(sql_tname.arr, up->name());
	sql_tname.len 	= strlen(sql_tname.arr);

	EXEC SQL SELECT count(*)
		 INTO	:sql_seq
		 FROM	user_ind_columns
		 WHERE	index_name = :sql_iname AND
			table_name = :sql_tname;
	if (sqlca.sqlcode ) {
		cerr << "Oracle select (count(*)) error " << sqlca.sqlcode << " in Index::Index(Table*, char[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	EXEC SQL SELECT DISTINCT
			LOWER(uniqueness)/* UNIQUE or NONUNIQUE */
		 INTO	:sql_itype
		 FROM	user_indexes
		 WHERE	index_name = :sql_iname AND
			 table_name = :sql_tname;
	if (sqlca.sqlcode ) {
		cerr << "Oracle select (index uniqueness) error " << sqlca.sqlcode << " in Index::Index(Table*, char[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	index_count	= sql_seq;
	column_list	= new struct index_field[index_count];
	if (!strncmp(sql_itype, "unique", 6))
	    type = UNIQUE;
	else
	    type = NON_UNIQUE;

	EXEC SQL DECLARE col CURSOR FOR
		SELECT 
			column_name,
			column_position
		FROM user_ind_columns
		WHERE index_name = :sql_iname AND
			table_name = :sql_tname
		ORDER BY column_position;
	EXEC SQL OPEN col;
	if (sqlca.sqlcode ) {
		cerr << "Oracle cursor open (col) error " << sqlca.sqlcode << " in Index::Index(Table*, char[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}

	for (int stop = 0; stop == 0; ) {
	    EXEC SQL FETCH col INTO
			:sql_colname,
			:sql_seq;
	    switch (sqlca.sqlcode) {
	    case 0:
		column_list[sql_seq-1].name = new char [sql_colname.len+1];
		sql_colname.arr[sql_colname.len]	= 0;
		strcpy(column_list[sql_seq-1].name, sql_colname.arr);
		column_list[sql_seq-1].order		= ASC;
		break;
	    default:
		cerr << "Oracle fetch (col) error " << sqlca.sqlcode << " in Index::Index(Table*, char[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
	    case NOT_FOUND:
		stop = 1;
		break;
	    }
	}
	EXEC SQL CLOSE col;

}

Index::~Index()
{

	if (!column_list) return;
	for (int i = 0; i < index_count; i++)
		delete column_list[i].name;
	delete column_list;

}

void Index::modify(ostream& s)
{

	s << form("modify %.24s to btree %s on ", 
		dereserve(parent->name()),
		type == UNIQUE ? "unique" : "");
	char *sep	= "\n\t";
	for (int i = 0; i < index_count; i++){
	    s << form("%s%.24s%s",
		sep,
		dereserve(column_list[i].name),
		column_list[i].order == DESC ? " desc" : "");
	    sep	= ",\n\t";
	}
	s << form("%s\n", GoString);

}

ostream& operator<<(ostream& s, Index *ind) {

    s << form("create %sindex %.24s on %.24s (",

	    OracleOut && ind->type == UNIQUE ? "unique " : "",
	    dereserve(ind->name()),
	    dereserve(ind->parent->name()));
    char *sep	= "\n\t";
    for (int i = 0; i < ind->index_count; i++){
	s << form("%s%.24s%s", 
	    sep, 
	    dereserve(ind->column_list[i].name),
	    OracleOut && ind->column_list[i].order == DESC ? " desc" : ""
	    );
	sep	= ",\n\t";

    }
    s << form(")%s\n", GoString);
    return s;
}

bool Index::OK()
{ bool result = TRUE;

// Force all columns that are used in indexing to be NON NULL

	for (int i = 0; i < index_count; i++) {
	    Column *c = parent -> column(column_list[i].name);
	    if (!c) {
		cerr << form("Table %s index %s refers to non-existent column %s\n",
			parent->name(),
			name(),
			column_list[i].name);
		result = FALSE;
	    } else {
		c->set_index_col();
	    }
	}

// If the index is UNIQUE for the parent table to have WITH NODUPLICATES

	if (type == UNIQUE)
		parent->SetNoDuplicates();

	return result;

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/ORACLE/INDEX.PCC" fi
if `test ! -s ./db_unload/ORACLE/TABLE.PCC` then
echo "writing ./db_unload/ORACLE/TABLE.PCC" cat > ./db_unload/ORACLE/TABLE.PCC << '\Rogue\Monster\' /* TABLE.PCC      Build the representation of an Oracle table and all its indexes in    memory. We can then output the appropriate SQL to reconstruct it in    another database.

This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk)

CHANGES:

2/4/91	Restrict Index and table names to 24 characters.
17/4/91	'With NoDuplicates' is not valid for Oracle output.
7/4/92	Never generate 'With NoDuplicates', it is inefficient and busy
	nothing.
	Dereserve all names.

*/
#include <data_dict.hpp>

EXEC SQL INCLUDE SQLCA;
EXEC SQL INCLUDE SQLDA;
static SQLDA *fetchda;
EXEC SQL BEGIN DECLARE SECTION;

static VARCHAR	sql_tname[31],
		sql_iname[31];
static VARCHAR	sql_data[100][240];/**/
static short	sql_ind[100],/*	*/
		sql_indcount;
static long	sql_colno;
EXEC SQL END DECLARE SECTION;
typedef struct {short len; char arr[1];}	VC;

Table::Table(const char tname[], const char *user) :
				DatabaseObject(tname, user)
{
	cols	 	= 0;
	NoDuplicates	= FALSE;
	if (strlen(name()) > 24)
	   cerr << form("Table name '%s' exceeds 24 characters\n",
			name());
	strcpy(sql_tname.arr, name());
	sql_tname.len	= strlen(name());
	EXEC SQL DECLARE col CURSOR FOR
		 SELECT DISTINCT column_id
		 FROM accessible_columns
		 WHERE table_name = UPPER(:sql_tname)
		 ORDER BY column_id DESC;
	EXEC SQL OPEN col;
	if (sqlca.sqlcode ) {
		cerr << "Oracle cursor open error " << sqlca.sqlcode << " in Table::Table(char name[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	for (int stop = 0; !stop;) {
		EXEC SQL FETCH col
			 INTO :sql_colno;
		switch (sqlca.sqlcode) {
		case 0:
			cols = new Column(sql_colno, this, cols);
			break;
		default:
			cerr << "Oracle fetch error " << sqlca.sqlcode << " in Table::Table(char name[])\n";
			cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		case NOT_FOUND:
			stop = 1;
		}
	}
	EXEC SQL CLOSE col;
 
	EXEC SQL SELECT count(*)
		 INTO   :sql_indcount
		 FROM	user_indexes
		 WHERE	table_name = :sql_tname;
	if (sqlca.sqlcode ) {
		cerr << "Oracle select (count(*)) error " << sqlca.sqlcode << " in Table::Table(char name[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	IndexCount	= 0;
	index_list	= new Index *[sql_indcount];

	EXEC SQL DECLARE ind CURSOR FOR
		SELECT DISTINCT index_name
		       FROM user_indexes
		       WHERE table_name = :sql_tname AND
				index_name != :sql_tname
		       ORDER by index_name ;
	EXEC SQL OPEN ind;
	if (sqlca.sqlcode ) {
		cerr << "Oracle cursor open (index) error " << sqlca.sqlcode << " in Table::Table()()\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	for (stop=0; !stop; ) {
		EXEC SQL FETCH ind
			 INTO :sql_iname;
		switch (sqlca.sqlcode) {
		case 0:
			sql_iname.arr[sql_iname.len]	= 0;
			Index *ind			=
			index_list[IndexCount++]	=
				new Index(this, (char *)sql_iname.arr);
			ind->OK();
			break;
		default:
			cout << form("rollback%s\n", GoString);
			cerr << "Oracle fetch (index) error " << sqlca.sqlcode << " in Table::Table()\n";
			cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		case NOT_FOUND:
			stop = 1;
		}
	}
	EXEC SQL CLOSE ind;


}

Table::~Table()
{

	if (cols) delete cols;
	for (int i = 0; i < IndexCount; i++) {
	    delete index_list[i];
	    index_list[i]	= 0;
	}
	if (index_list)
	    delete index_list;

}  

ostream& operator<<(ostream& s, Table & t) {

    s << form("drop table %-.24s%s\n", dereserve(t.name()), GoString);     s << form("create table %-.24s(\n", dereserve(t.name()));     for (Column *c = t.cols; c; c = c->link()) {

	    s << *c;
	    if (c->link()) 
		    s << ",\n";
	    else
		    s << "\n";

    }
    s << form(")%s%s\n",
	    (0 && t.NoDuplicates && !OracleOut) ? " with noduplicates" : "",
	    GoString);
    if (!OracleOut && t.IndexCount) {
	t.index_list[0]->modify(s);

    }
    for (int i=(OracleOut?0:1); i<t.IndexCount; i++) {

        s << t.index_list[i];
    }
    s.flush();
    return s;
}  

void	Table::extract(ostream &s)
{	int	i;
	EXEC SQL BEGIN DECLARE SECTION;
	VARCHAR	selectbuf[5000];	/* Oracle SELECT statement	*/
	EXEC SQL END DECLARE SECTION;
	char	insertbuf[5000];	/* Ingres INSERT statement	*/
	bzero(selectbuf.arr, sizeof(selectbuf.arr));
	bzero(insertbuf, sizeof(insertbuf));
	ostream b(sizeof(selectbuf.arr), (char *)selectbuf.arr);
	ostream ins(sizeof(insertbuf), insertbuf);

    /*
	In selectbuf[] build a SELECT statement to use as a cursor to
	fetch the Oracle data rows from the table. Simultaneously, in
	insertbuf[] build the stem of an INSERT statement to insert
	the data into the output table. The actual data values will be
	added to the stem as each data row is fetched.
    */
	int field_count = 0;
	b << "SELECT ";
	ins << form("insert into %-.24s (\n", name());
	for (Column *c = cols; c; c= c->link()) {
		switch (c->type()) {
		case NUM:
			b << form("TO_CHAR(%s)%s ", 
					c->name(), 
					c->link() ? "," : "");
			break;
		case DATE:
		default:
			b << form("%s%s", c->name(), c->link() ? ", " : " ");
			break;
		}
		ins << form("\t%-.24s%s\n", c->name(), c->link() ? "," : "");
		field_count++;
	}
	ins << ") values (\n";
	b << form("FROM %s", name());
	b.flush();
	selectbuf.len	= strlen(selectbuf.arr);
	ins.flush();

    /*
	We have now built the text of the various SQL statements that
	we need. The next step is to tell Oracle about it and the
	target variables that we are going to use.
    */
	EXEC SQL PREPARE s1 FROM :selectbuf;
	if (sqlca.sqlcode ) {
		cerr << "Oracle PREPARE error " << sqlca.sqlcode << " for S1 in Table::extract(ostream &)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		cerr << form("Text is: '%s'\n", selectbuf.arr);
		s << form("rollback%s\n", GoString);
		exit(1);
	}
	EXEC SQL DECLARE cc CURSOR FOR s1;

    /*
	Allocate a sqlda for 'field_count' columns with 30-char names.
	This will be used to hold pointers to our work variables into
	which Oracle will store the data rows fetched from the table.

    */
	fetchda = (SQLDA*)sqlald(field_count, 30, 30);
	if (!fetchda) {
		cerr << form("Oracle error allocating sqlda\n");
		s << form("rollback%s\n", GoString);
		exit(1);
	}
    /*
	sql_data[][], declared above, can hold 100 data items each of
	up to 240 bytes. sql_ind[] are the corresponding 100 indicators.
    */
	i = 0;
	for (c = cols; c; c= c->link()) {
		fetchda->T[i] = 9;		
		fetchda->V[i] = (char *)&sql_data[i];
		fetchda->I[i] = &sql_ind[i];
		i++;
	}

	EXEC SQL OPEN cc;
	if (sqlca.sqlcode ) {
		cerr << "Oracle cursor open error " << sqlca.sqlcode << " in Table::extract(ostream &)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}

	EXEC SQL DESCRIBE SELECT LIST FOR s1 INTO fetchda;
	if (sqlca.sqlcode ) {
		cerr << "Oracle describe-select error " << sqlca.sqlcode << " in Table::extract(ostream &)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}

	i = 0;
	for (c = cols; c; c= c->link()) {
		fetchda->T[i] = 9;		/* VARCHAR	*/
		switch (c->type()) {
		case DATE:
			fetchda->L[i] = 11;
			break;
		case CHAR:
			fetchda->L[i] = c->width()+2;
			break;
		case NUM:
		default:
			break;
		}
//		fetchda->V[i] = new char[fetchda->L[i]];
//		fetchda->I[i] = new short;
		i++;
	}
	if (fetchda->F < 0) {
		cerr << form("Oracle DESCRIBE error - %d fields allocated; %d fields found\n",
		     field_count, fetchda->F);
		exit(1);
	}
	for (int stop = 0; !stop;) {
	    EXEC SQL FETCH cc
		     USING DESCRIPTOR fetchda;
	    switch (sqlca.sqlcode) {
	    case 0:
		s << insertbuf;
		int	i = 0;
		for (c=cols; c; c = c->link()) {
		    if (*fetchda->I[i] < 0)  {	// If NULL value
			if (NotNull || c->non_null()) {	// Force NOT NULL
	    cerr << form("Forcing non-null value for %s.%s\n", 
				name(), c->name());
			    switch(c->type()) {
			    case NUM:
				s << form("\t0%s\n",
				    c->link() ? "," : "");
				break;
			    case CHAR:
			    case DATE:
			    default:
				s << form("\t''%s\n",
					c->link() ? "," : "");
				break;
			    }
			} else {		// Honour NULLs
			    s << form("\tNULL%s\n",
				c->link() ? "," : "");
			}
		    } else if (*fetchda->I[i] > 0) {
			cerr << form("Table %s, field %s truncated from %d ch\n",
			   name(), c->name(), *fetchda->I[i]);
		    }

		    if (*fetchda->I[i] >= 0) {
			switch(c->type()) {
			case NUM:
			    if (!((VC *)fetchda->V[i])->len) 
				s << form("\t0%s\n",
				    c->link() ? "," : "");
			    else
				s << form("\t%.*s%s\n",
				    ((VC *)fetchda->V[i])->len,
				    ((VC *)fetchda->V[i])->arr,
				    c->link() ? "," : "");
			    break;
			case CHAR:
			    dequote((VC*)fetchda->V[i]);
			case DATE:
			default:
			    s << form("\t'%*.*s'%s\n",
				    ((VC *)fetchda->V[i])->len,
				    ((VC *)fetchda->V[i])->len,
				    ((VC *)fetchda->V[i])->arr,
				    c->link() ? "," : "");
			    break;
			}
		    }
		    i++;
		}
		s << form(")%s\n", GoString);
		break;
	    default:
		    cerr << "Oracle fetch error " << sqlca.sqlcode << " in Table::extract(ostream&)\n";
		    cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		    cerr << form("Table name %s\n", name());
		    s << form("rollback%s", GoString);
	    case NOT_FOUND:
		    stop = 1;
	    }
	}
	EXEC SQL CLOSE cc;
#if 0
	for (i=0; i < fetchda->N; i++) {
	    delete fetchda->V[i];
	    delete fetchda->I[i];
	}
#endif
	sqlclu(fetchda);	// Release the SQLDA
	s.flush();

}
void	Table::grant(ostream &s)
{	for (Column *c = cols; c; c = c->link()) {
	    if (!c->is_index_column())
		s << form("grant update(%.24s) on %.24s to public%s\n",
		    dereserve(c->name()), dereserve(name()), GoString);
	}
	s << form("grant select on %.24s to public%s\n",
	    dereserve(name()), GoString);
	s << form("grant delete on %.24s to public%s\n",
	    dereserve(name()), GoString);
	s << form("grant insert on %.24s to public%s\n",
	    dereserve(name()), GoString);

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/ORACLE/TABLE.PCC" fi
if `test ! -s ./db_unload/ORACLE/VIEW.PCC` then
echo "writing ./db_unload/ORACLE/VIEW.PCC" cat > ./db_unload/ORACLE/VIEW.PCC << '\Rogue\Monster\' /* VIEW.PCC This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk)

CHANGES:
7/4/92 Dereserve all names.

*/

#include "data_dict.hpp"
EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;

static VARCHAR	sql_name[31],
		sql_text[2000];
static long	sql_colno;

EXEC SQL END DECLARE SECTION; View::View(const char vname[], const char *user) :

                DatabaseObject(vname, "")
{

	cols	= 0;
	strcpy(sql_name.arr, name());
	sql_name.len	= strlen(name());
	EXEC SQL SELECT text
		 INTO :sql_text
		 FROM user_views
		 WHERE view_name = :sql_name;
	sql_text.arr[sql_text.len]	= 0;
	vtext				= new char[sql_text.len+1];
	strcpy(vtext, sql_text.arr);
	EXEC SQL DECLARE col CURSOR FOR
		 SELECT DISTINCT column_id
		 FROM accessible_columns
		 WHERE table_name = UPPER(:sql_name)
		 ORDER BY column_id DESC;
	EXEC SQL OPEN col;
	if (sqlca.sqlcode ) {
		cerr << "Oracle cursor open error " << sqlca.sqlcode << " in Table::Table(char name[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	for (int stop = 0; !stop;) {
		EXEC SQL FETCH col
			 INTO :sql_colno;
		switch (sqlca.sqlcode) {
		case 0:
			cols	= new Column(sql_colno, this, cols);
			break;
		default:
			cerr << "Oracle fetch error " << sqlca.sqlcode << " in View::View(char name[])\n";
			cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		case NOT_FOUND:
			stop	= 1;
		}
	}
	EXEC SQL CLOSE col;

}

ostream& operator<<(ostream& s,View& v)
{

	s << form("drop view %s%s\n", dereserve(v.name()), GoString);
	s << form("create view %-.24s (\n", dereserve(v.name())); 
	for (Column *c = v.cols; c; c = c->link()) {
		s << form("\t\t%-.24s", dereserve(c->name()));
		if (c->link()) 
			s << ",\n";
		else
			s << "\n";
	}
	s << form("\t) as\n%s%s\n", v.vtext, GoString);
	s.flush();
	return s;

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/ORACLE/VIEW.PCC" fi
if `test ! -s ./db_unload/ORACLE/PREFIX.SQL` then
echo "writing ./db_unload/ORACLE/PREFIX.SQL" cat > ./db_unload/ORACLE/PREFIX.SQL << '\Rogue\Monster\' set autocommit on;
\Rogue\Monster\
else
  echo "will not over write ./db_unload/ORACLE/PREFIX.SQL" fi
if `test ! -d ./db_unload/INGRES`
then
  mkdir ./db_unload/INGRES
  echo "mkdir ./db_unload/INGRES"
fi
if `test ! -s ./db_unload/INGRES/C_DATA.SCC` then
echo "writing ./db_unload/INGRES/C_DATA.SCC" cat > ./db_unload/INGRES/C_DATA.SCC << '\Rogue\Monster\' /* C_DATA           Convert an Ingres database to an SQL database.

This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk)

Options and arguments

	-d	convert the data as well as the structure
	-g	generate GRANT commands on all tables and views
	-n	generate NOT NULL on all columns
	-v	generate WITH DEFAULT on all NOT NULL columns
	-o	generate Oracle compatible output rather than
		Ingres. i.e.
			user ';' instead of '\p\g'
			'create index' instead of 'modify'
			'vchar' is spellt 'varchar'. 
		The Ingres manuals always say 'varchar' but
		the programs will not accept it.
	-u name User who owns the tables etc to be converted
	db_name name of the DB to be processed. We assume that the 
	-V	Views only
		password is the same as the db-name.

*/

#define EXTERN
EXEC SQL INCLUDE SQLCA;

#include "data_dict.hpp"
#include  <stream.h>
#include <ctype.h>

EXEC SQL BEGIN DECLARE SECTION;
static char	db_name[NAME_LENGTH],	/* Database name	*/
		user[NAME_LENGTH],
		tabtype[8];		/* VIEW or TABLE	*/
static char	tname[NAME_LENGTH];	/* Table or view name	*/
EXEC SQL END DECLARE SECTION; main(int argc, char *argv[])
{	bool	ConvertData	= FALSE,
		Grant		= FALSE;
	int	NumberOfTables	= 0;
	bool	ViewsOnly	= FALSE;
	int	stop;

	GoString	= "\\p\\g";
	strcpy(user, "%");
	while (argc >1 && argv[1][0] == '-') {
	    for (int j=1; argv[1][j]; j++) {
		switch(argv[1][j]) {
		case 'd':
		    ConvertData = TRUE;
		    break;
		case 'g':
		    Grant	= TRUE;
		    break;
		case 'o':		// Oracle compatible output
		    OracleOut = TRUE;
		    GoString  = ";";
		    break;
		case 'n':		// Force NOT NULL on all columns
		    NotNull	= TRUE;
		    break;
		case 'v':		// Enable WITH DEFAULT
		    Default	= TRUE;
		    break;
		case 'V':
		    ViewsOnly	= TRUE;
		    break;
		case 'u':		// User name
		    strcpy(user, argv[2]);
		    strcat(user, "%");
		    argc--;
		    argv++;
		    break;
		default:
		    break;
		}
	    }
	    argc--;
	    argv++;
	}

	if (argc < 2) {
		strcpy(db_name, "raphdb");
	} else {
		strcpy(db_name, argv[1]);
	}
	EXEC SQL CONNECT :db_name ;
	if (sqlca.sqlcode ) {
		cerr << "Ingres connect error " << sqlca.sqlcode <<"\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}

/*

    Get all tables before all views
*/

	if (!ViewsOnly) {
	EXEC SQL DECLARE tb CURSOR FOR
		 SELECT DISTINCT 
			table_name
		 FROM	iitables
		 WHERE table_owner LIKE :user and
			table_type LIKE 'T%' and
			system_use LIKE 'U%'
		 ORDER BY table_name;
	EXEC SQL OPEN tb;
	if (sqlca.sqlcode ) {
		cerr << "Ingres 'tb' cursor open error " << sqlca.sqlcode << " in ::main()()\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	for (stop=0; !stop; ) {
		EXEC SQL FETCH tb
			 INTO :tname;
		switch (sqlca.sqlcode) {
		case 0:
			Table *t = new Table(tname, user);
			cout << *t;
			if (ConvertData) {
			    t->extract(cout);
			}
			if (Grant)
			    t->grant(cout);
			delete t;
			break;
		default:
			cout << form("rollback%s\n", GoString);
			cerr << "Ingres fetch error on 'tb' " << sqlca.sqlcode << " in ::main()\n";
			cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		case NOT_FOUND:
			stop = 1;
			break;
		}
		if (NumberOfTables++ > 20) {
			cout << form("commit%s\n", GoString);
			NumberOfTables	= 0;
		}
	}
	EXEC SQL CLOSE tb;
	}		// End !ViewsOnly

	EXEC SQL DECLARE vw CURSOR FOR
		 SELECT DISTINCT 
			table_name
		 FROM	iitables
		 WHERE table_owner LIKE :user and
			table_type LIKE 'V%' and
			system_use LIKE 'U%'
		 ORDER BY table_name;
	EXEC SQL OPEN vw;
	if (sqlca.sqlcode ) {
		cerr << "Ingres 'vw' cursor open error " << sqlca.sqlcode << " in ::main()()\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	for (stop=0; !stop; ) {
		EXEC SQL FETCH vw
			 INTO :tname;
		switch (sqlca.sqlcode) {
		case 0:
			View *v = new View(tname, user);
			cout << *v;
			delete v;
			break;
		default:
			cout << form("rollback%s\n", GoString);
			cerr << "Ingres fetch error on 'vw' " << sqlca.sqlcode << " in ::main()\n";
			cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		case NOT_FOUND:
			stop = 1;
			break;
		}
		if (NumberOfTables++ > 20) {
			cout << form("commit%s\n", GoString);
			NumberOfTables	= 0;
		}
	}
	EXEC SQL CLOSE vw;

	cout << form("commit%s\n", GoString);

	exit(0);

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/INGRES/C_DATA.SCC" fi
if `test ! -s ./db_unload/INGRES/VIEW.SCC` then
echo "writing ./db_unload/INGRES/VIEW.SCC" cat > ./db_unload/INGRES/VIEW.SCC << '\Rogue\Monster\' #include "data_dict.hpp"
EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
static char	sql_name[NAME_LENGTH],
		sql_text[257];
static long	sql_colno,
		sql_sequence;
static char	user[NAME_LENGTH];

EXEC SQL END DECLARE SECTION; View::View(const char vname[], const char *vowner) :

                        DatabaseObject(vname, vowner)
{

	cols	= 0;
	strcpy(sql_name, name());
	strcpy(user, owner());
	EXEC SQL DECLARE vv CURSOR FOR
		 SELECT DISTINCT
			text_segment,
			text_sequence
		 FROM iiviews
		 WHERE table_name = :sql_name
		 ORDER BY text_sequence;
	EXEC SQL OPEN vv;
	if (sqlca.sqlcode ) {
		cerr << "Ingres cursor 'vv' open error " << sqlca.sqlcode << " in View::View(char name[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	char *s = new char[1];
	*s = 0;
	while (1) {
	    EXEC SQL FETCH vv
		 INTO :sql_text, :sql_sequence;
	    if (sqlca.sqlcode == NOT_FOUND)
		break;
	    s = realloc(s, strlen(s)+strlen(sql_text));
	    strcat(s, sql_text);
	}
	EXEC SQL CLOSE vv;
	vtext	=  s;

	EXEC SQL DECLARE col CURSOR FOR
		 SELECT DISTINCT column_sequence
		 FROM iicolumns
		 WHERE table_name = :sql_name AND
		       table_owner LIKE :user 
		 ORDER BY column_sequence DESC;
	EXEC SQL OPEN col;
	if (sqlca.sqlcode ) {
		cerr << "Ingres cursor 'col' open error " << sqlca.sqlcode << " in View::View(char name[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	for (int stop = 0; !stop;) {
		EXEC SQL FETCH col
			 INTO :sql_colno;
		switch (sqlca.sqlcode) {
		case 0:
			cols	= new Column(sql_colno, this, cols);
			break;
		default:
			cerr << "Ingres fetch error " << sqlca.sqlcode << " in View::View(char name[])\n";
			cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		case NOT_FOUND:
			stop	= 1;
		}
	}
	EXEC SQL CLOSE col;

}

ostream& operator<<(ostream& s,View& v)
{

        s << form("drop view %s%s\n", v.name(), GoString); #if 0

	s << form("create view %s (\n", v.name()); 
	for (Column *c = v.cols; c; c = c->link()) {
		s << form("\t\t%s", c->name());
		if (c->link()) 
			s << ",\n";
		else
			s << "\n";
	}
	s << form("\t) as\n%s%s\n", v.vtext, GoString);
#else
	s << form("%s%s\n", v.vtext, GoString);
#endif
	s.flush();
	return s;

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/INGRES/VIEW.SCC" fi
if `test ! -s ./db_unload/INGRES/DATA_DIC.HPP` then
echo "writing ./db_unload/INGRES/DATA_DIC.HPP" cat > ./db_unload/INGRES/DATA_DIC.HPP << '\Rogue\Monster\' // DATA_DICT.HPP // Oracle Data Dictionary structures
#ifndef DATA_DICT_
#include <stream.h>
#include <bool.h>
#define DATA_DICT_
#define	NAME_LENGTH	33

class Table;
class Index;
class View;
enum col_type { NUM, CHAR, DATE, FLOAT, MONEY};

class DatabaseObject {

	char	object_name[NAME_LENGTH];
	char	owner_name[NAME_LENGTH];
public:
	DatabaseObject(const char name[], const char owner[]);
	const char	*name()		{ return object_name;	}
	const char	*owner()	{ return owner_name;	}
};
class	Column {
	char	cname[NAME_LENGTH];
	bool	nulls;			/* 'NULL' or 'NOT NULL' */
	bool	WithDefault;
	col_type 	coltype;	// CHAR, NUMBER or DATE
	int	colwidth;
	Table	*parent;
	Column	*next;
public:
	const char *name();
	bool	IndexColumn;
	Column *link();
	int	width();
	int	type();
	Column(long  number, Table *up, Column *prev);
	Column(long  number, View *up, Column *prev);
	~Column();
	friend ostream& operator<<(ostream & s, Column & c);
	void	set_index_col();
	void	set_non_null();

};

class Table :public DatabaseObject {

	Column	*cols;		// The column chain
	Index	**index_list;	// The indexes on this table
	int	IndexCount;	// sizeof(index_list)
	bool	NoDuplicates;	// WITH NODUPLICATES clause
public:
	Table(const char name[], const char *owner="");
	~Table();
	friend ostream& operator<<(ostream& s, Table & t);
	void	extract(ostream &s);
	Column	*column(const char cname[]);
	void	SetNoDuplicates();
	void	grant(ostream&);

};

class View : public DatabaseObject {

	char	*vtext;		// The view text
	Column	*cols;		// The column chain
public:
	View();
	View(const char [], const char *owner="");
	~View();
	friend ostream& operator<<(ostream &s, View& v);
};

enum index_type { UNIQUE, NON_UNIQUE};
enum index_order {ASC, DESC};

class Index : public DatabaseObject {

	Table		*parent;
	index_type	type;		// UNIQUE or non-unique
	int		index_count;
	struct index_field {
	    index_order	order;
	    char	*name;
	}			*column_list;
public:
	Index();
	Index(Table *t, char unique);
	Index(Table *t, const char iname[]);
	~Index();
	Index *link();
	void modify(ostream& s);
	friend ostream& operator<<(ostream &s, Index *ind);
	bool	OK();

};

inline DatabaseObject::DatabaseObject(const char name[], const char user[])

{	strcpy(object_name, name);
	char *s = strchr(object_name, ' ');
	if (s) *s = 0;
	strcpy(owner_name, user);
	s = strchr(owner_name, ' ');
	if (s) *s = 0;

}
inline Column::~Column() 		{ if(next) delete next; }
inline const char *Column::name()	{ return cname;		}
inline Column *Column::link()		{ return next;		}
inline Column::type()			{ return coltype;	}
inline Column::width()			{ return colwidth;	}
inline void Column::set_non_null()	{ nulls	= FALSE;	}
inline void Column::set_index_col()	{ nulls	= FALSE; IndexColumn = TRUE;}


inline View::View() : DatabaseObject("", "")
{	vtext	= 0;
	cols	= 0;

}
inline View::~View()
{
	delete vtext;
	if (cols) delete cols;

}

inline Index::Index() : DatabaseObject ("", "") {

	parent	= 0;
	column_list=0;

}

inline void Table::SetNoDuplicates() { NoDuplicates = TRUE; }

extern "C"	sqlab2(...);
extern "C"	sqlad2(...);
extern "C"	sqlbs2(...);
extern "C"	sqlcls(...);
extern "C"	sqlexe(...);
extern "C"	sqlfcc(...);
extern "C"	sqlfch(...);
extern "C"	sqliem(...);
extern "C"	sqllo2(...);
extern "C"	sqlopn(...);
extern "C"	sqlosq(...);
extern "C"	sqlsca(...);
extern "C"	sqlscc(...);
extern "C"	sqlsch(...);
extern "C"	sqltfl(...);
extern "C"	sqltoc(...);
extern "C"	sqlos2(...);
extern "C"	sqlclu(...);
extern "C"	sqlgd2(...);
extern "C"	void *sqlald(int, int, int);
extern	"C" void dequote(void *);
#ifdef INGRES
#define	NOT_FOUND	100
#else
#define	NOT_FOUND	1403

#endif
#ifndef EXTERN
#define EXTERN extern
#endif

EXTERN char	*GoString;
EXTERN bool	OracleOut;
EXTERN bool	Default;
EXTERN bool	NotNull;

#endif
\Rogue\Monster\
else
  echo "will not over write ./db_unload/INGRES/DATA_DIC.HPP" fi
if `test ! -s ./db_unload/INGRES/MAKEFILE` then
echo "writing ./db_unload/INGRES/MAKEFILE" cat > ./db_unload/INGRES/MAKEFILE << '\Rogue\Monster\'
SUFFIXES:	.c .sc .pc .cc .scc .pcc 
G++	= g++
CC	= gcc
G++FLAGS= -I.. -I/usr/local/include -g -O -w -DINGRES
CFLAGS	= -I.. -g -O 
LDFLAGS	= -static
GCC	= gcc
PCC 	= pcc
DBNAME	= test
USERID	= comp/comp
PCCFLAGS= include=$(ORACLE_INCLUDE) ireclen=511 oreclen=132 host=C\
	maxopencursors=20 # sqlcheck=limited userid=pcms/pcms 

SRC=\
	makefile\
	../data_dict.hpp\
	c_data.scc\
	column.scc\
	index.scc\
	table.scc\
	view.scc

# Object files for the database conversion program
DOBJ=\

	c_data.o\
	column.o\
	dequote.o\
	index.o\
	table.o\
	view.o


YACC	= bison 
YFLAGS	= -dvy

pcc.o:
	$(PCC) iname=$*.pcc oname=/tmp/$*.x.cc $(PCCFLAGS) 
	sed -e '/^# *[0-9]/d' -e '/struct  *sqlca/s//struct sql_ca/' \
	-e '/struct  *SQLDA/s//struct SQL_DA/' \
	-e '/extern  *sql.*();/d' < /tmp/$*.x.cc > /tmp/$*.cc
	$(G++) $(G++FLAGS) -c /tmp/$*.cc

cc.o:
	$(G++) $(G++FLAGS) -c $*.cc
#-------------------------------------------------------------------------

# NOTE: ORACLE_HOME must be either:
# . set in the user's environment # . passed in on the command line # . defined in a modified version of this makefile
#

#CC=/usr/5bin/cc

OCILIB = #$(ORACLE_HOME)/c/lib/libocic.a #PCCLIBS = $(ORACLE_HOME)/rdbms/lib/libpcc.a $(SQLLIB) $(OCILIB) PCCLIBS = $(SQLLIB) $(OCILIB)
STLIBS= $(ORACLE_HOME)/rdbms/lib/osntabst.o \

        $(ORACLE_HOME)/rdbms/lib/config.o

all: c_data

c_data:	$(DOBJ)
	$(G++) $(G++FLAGS) $(LDFLAGS) -o $_at_ $(DOBJ)\
		-L/usr/5lib $(II_SYSTEM)/ingres/lib/libingres.a -lm
pc.c:
	$(PCC) $(PCCFLAGS) iname=$*.pc 

pc.o:
	$(PCC) $(PCCFLAGS) iname=$*.pc oname=/tmp/$*.c
	$(CC) $(CFLAGS) -c /tmp/$*.c

pc:
	-$(PCC) iname=$*.pc oname=/tmp/$*.c $(PCCFLAGS) userid=$(USERID)
	$(CC) $(CFLAGS) -o $* /tmp/$*.c -L/usr/5lib $(SQLLIB) $(NETLIBS) $(ORALIBS) $(OTHERLIBS)
#
#.c.exe:
#	$(CC) $(CFLAGS) -o $* $*.c $(SQLLIB) $(OCILIB) $(NETLIBS) $(ORALIBS) $(OTHERLIBS)
#
#-------------------------------------------------------------------------

$(DOBJ):	../data_dict.hpp

print:	$(SRC) 
	pr -f $? |lpr
	touch print
printall:
	pr -f $(SRC) | lpr
	touch print
safe:
	-chmod a-w $(SRC)
clean:
	rm -f *.o core *.lis

#-------------------------------------------------------
scc.o:
	cp $< /tmp/$*.cc
	$(G++) $(G++FLAGS) -E /tmp/$*.cc > /tmp/$*.sc
	esqlc -o.sh -f/tmp/$*.cc /tmp/$*.sc
	$(G++) -c $(G++FLAGS) /tmp/$*.cc
scc.cc:
	cp $< /tmp/$*.cc
	$(G++) $(G++FLAGS) -E /tmp/$*.cc > /tmp/$*.sc
	esqlc -o.sh -f$*.cc /tmp/$*.sc
sc.o:
	esqlc -o.sh -f/tmp/$*.c $<
	$(CC) -c $(CFLAGS) /tmp/$*.c
sc.c:
	esqlc -o.sh -f$*.c $<

test1:	test1.o
	gcc -g -o $_at_ test1.o /usr/sun4/ingres/lib/libingres.a -lm

backup:	$(SRC) 
	-mkdir /common/tmp/raph/ingdata; chmod 777 /common/tmp/raph/ingdata
	cp $? /common/tmp/raph/ingdata
	-rm -f *.lis
	touch backup
	-$(MAKE) safe
dequote.o:	../dequote.c
	$(CC) -c $(CFLAGS) ../dequote.c
test:	c_data
	c_data -d $(DBNAME)

\Rogue\Monster\
else
  echo "will not over write ./db_unload/INGRES/MAKEFILE" fi
if `test ! -s ./db_unload/INGRES/INDEX.SCC` then
echo "writing ./db_unload/INGRES/INDEX.SCC" cat > ./db_unload/INGRES/INDEX.SCC << '\Rogue\Monster\' /* INDEX.SCC This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk) */
#include "data_dict.hpp"
#include <assert.h>
EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
static char	sql_iname[NAME_LENGTH],
		sql_tname[NAME_LENGTH],
		sql_colname[NAME_LENGTH];
static char	sql_itype[9],	/* UNIQUE or NON UNIQUE	*/
		sql_iorder[9];
static int	sql_seq;
static char	user[NAME_LENGTH];

EXEC SQL END DECLARE SECTION; /*

    Create a secondary index.
*/

Index::Index(Table *up, const char iname[]) :

                                DatabaseObject(iname, up->owner())
{

	parent		= up;
	column_list	= 0;
	index_count	= 0;
	strcpy(sql_iname, name());
	strcpy(sql_tname, up->name());
	strcpy(user, up->owner());

	EXEC SQL SELECT count(*)
		 INTO	:sql_seq
		 FROM	iiindex_columns
		 WHERE	index_name = :sql_iname AND
			index_owner LIKE :user;
	if (sqlca.sqlcode ) {
		cerr << "Ingres select (count(*)) error " << sqlca.sqlcode << " in Index::Index(Table*, char[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	if (sql_seq <= 0) {
	    cerr << form("Table %s index %s has no columns\n",
				sql_tname, sql_iname);
	    return;
	}
	EXEC SQL SELECT DISTINCT
			LOWERCASE(unique_rule)/* UNIQUE or NONUNIQUE */
		 INTO	:sql_itype
		 FROM	iiindexes
		 WHERE	index_name = :sql_iname AND
			 index_owner LIKE :user AND
			 base_name = :sql_tname;
	if (sqlca.sqlcode ) {
		cerr << "Ingres select (index uniqueness) error " << sqlca.sqlcode << " in Index::Index(Table*, char[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	index_count	= sql_seq;
	column_list	= new struct index_field[index_count];
	if (sql_itype[0] == 'U')
	    type = UNIQUE;
	else
	    type = NON_UNIQUE;

	EXEC SQL DECLARE col CURSOR FOR
		SELECT 
			column_name,
			LOWERCASE(sort_direction),
			key_sequence
		FROM iiindex_columns
		WHERE index_name = :sql_iname AND
			index_owner LIKE :user
		ORDER BY key_sequence;
	EXEC SQL OPEN col;
	if (sqlca.sqlcode ) {
		cerr << "Ingres cursor open (col) error " << sqlca.sqlcode << " in Index::Index(Table*, char[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}

	for (int stop = 0; stop == 0; ) {
	    EXEC SQL FETCH col INTO
			:sql_colname,
			:sql_iorder,
			:sql_seq;
	    assert(sql_seq > 0);
	    switch (sqlca.sqlcode) {
	    case 0:
		char	*s = strchr(sql_colname, ' ');
		if (s)	*s = 0;
		column_list[sql_seq-1].name
				= new char [strlen(sql_colname)+1];
		strcpy(column_list[sql_seq-1].name, sql_colname);
		column_list[sql_seq-1].order	
				= (sql_iorder[0] == 'a') ? ASC : DESC;
		break;
	    default:
		cerr << "Ingres fetch (col) error " << sqlca.sqlcode << " in Index::Index(Table*, char[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
	    case NOT_FOUND:
		stop = 1;
		break;
	    }
	}
	EXEC SQL CLOSE col;

}

 /*

    Create the primary index that arises from a MODIFY statement. It     is quite possible to have secondary indexes but no primary index. */

Index::Index(Table *up, char unique) :

                                DatabaseObject(up->name(), up->owner()) {

	parent		= up;
	column_list	= 0;
	strcpy(sql_tname, up->name());
	strcpy(user, up->owner());

	EXEC SQL SELECT count(*)
		 INTO	:sql_seq
		 FROM	iicolumns
		 WHERE	table_name = :sql_tname AND
			table_owner LIKE :user AND
			key_sequence != 0;
	if (sqlca.sqlcode ) {
		cerr << "Ingres select (count(*)) error " << sqlca.sqlcode << " in Index::Index(Table*, char)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	index_count	= sql_seq;
	column_list	= new struct index_field[index_count];
	if (unique == 'U')
	    type = UNIQUE;
	else
	    type = NON_UNIQUE;

	EXEC SQL DECLARE col2 CURSOR FOR
		SELECT 
			column_name,
			LOWERCASE(sort_direction),
			key_sequence
		FROM iicolumns
		WHERE table_name = :sql_tname AND
			table_owner LIKE :user AND
			key_sequence != 0
		ORDER BY key_sequence;
	EXEC SQL OPEN col2;
	if (sqlca.sqlcode ) {
		cerr << "Ingres cursor open (col) error " << sqlca.sqlcode << " in Index::Index(Table*, char)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}

	for (int stop = 0; stop == 0; ) {
	    EXEC SQL FETCH col2 INTO
			:sql_colname,
			:sql_iorder,
			:sql_seq;
	    assert(sql_seq > 0);
	    switch (sqlca.sqlcode) {
	    case 0:
		char	*s = strchr(sql_colname, ' ');
		if (s)	*s = 0;
		column_list[sql_seq-1].name
				= new char [strlen(sql_colname)+1];
		strcpy(column_list[sql_seq-1].name, sql_colname);
		column_list[sql_seq-1].order	
				= (sql_iorder[0] == 'a') ? ASC : DESC;
		break;
	    default:
		cerr << "Ingres fetch (col) error " << sqlca.sqlcode << " in Index::Index(Table*, char)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
	    case NOT_FOUND:
		stop = 1;
		break;
	    }
	}
	EXEC SQL CLOSE col2;

}

Index::~Index()
{

	if (!column_list) return;
	for (int i = 0; i < index_count; i++)
		delete column_list[i].name;
	delete column_list;

}

void Index::modify(ostream& s)
{

	s << form("modify %s to btree %s on ", 
		parent->name(),
		type == UNIQUE ? "unique" : "");
	char *sep	= "\n\t";
	for (int i = 0; i < index_count; i++){
	    s << form("%s%s%s",
		sep,
		column_list[i].name,
		column_list[i].order == DESC ? " desc" : "");
	    sep	= ",\n\t";
	}
	s << form("%s\n", GoString);

}

ostream& operator<<(ostream& s, Index *ind) {

    s << form("create %sindex %s on %s (",

	    OracleOut && ind->type == UNIQUE ? "unique " : "",
	    ind->name(),
	    ind->parent->name());
    char *sep	= "\n\t";
    for (int i = 0; i < ind->index_count; i++){
	s << form("%s%s%s", 
	    sep, 
	    ind->column_list[i].name,
	    OracleOut && ind->column_list[i].order == DESC ? " desc" : ""
	    );
	sep	= ",\n\t";

    }
    s << form(")%s\n", GoString);
    return s;
}

bool Index::OK()
{ bool result = TRUE;

// Force all columns that are used in indexing to be NON NULL

	for (int i = 0; i < index_count; i++) {
	    Column *c = parent -> column(column_list[i].name);
	    if (!c) {
		cerr << form("Table %s index %s refers to non-existent column %s\n",
			parent->name(),
			name,
			column_list[i].name);
		result = FALSE;
	    } else {
		c->set_index_col();
	    }
	}

// If the index is UNIQUE for the parent table to have WITH NODUPLICATES

	if (type == UNIQUE)
		parent->SetNoDuplicates();

	return result;

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/INGRES/INDEX.SCC" fi
if `test ! -s ./db_unload/INGRES/TABLE.SCC` then
echo "writing ./db_unload/INGRES/TABLE.SCC" cat > ./db_unload/INGRES/TABLE.SCC << '\Rogue\Monster\' /* TABLE.SCC      Build the representation of an Ingres table and all its indexes in    memory. We can then output the appropriate SQL to reconstruct it in    another database.

This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk) */
#include <data_dict.hpp>
#include <assert.h>

EXEC SQL INCLUDE SQLCA;
EXEC SQL INCLUDE SQLDA;
static IISQLDA *fetchda;
EXEC SQL BEGIN DECLARE SECTION;

static char	sql_tname[NAME_LENGTH],
		sql_iname[NAME_LENGTH],
		sql_duplicates[9],	/* WITH NO DUPLICATES	*/
		sql_unique[9];		/* MODIFY to UNIQUE	*/
static struct {
	short	len;
	char	arr[240];
    } sql_data[IISQ_MAX_COLS];
static short	sql_ind[IISQ_MAX_COLS],
		sql_indcount;
static long	sql_colno;
static char	user[NAME_LENGTH];
EXEC SQL END DECLARE SECTION;
typedef struct {short len; char arr[1];}	VC;

Table::Table(const char tname[], const char *towner) :
			DatabaseObject(tname, towner)
{
	cols	 	= 0;
	NoDuplicates	= FALSE;
	strcpy(sql_tname, name());
	strcpy(user, owner());
	EXEC SQL SELECT	
		duplicate_rows,
		unique_rule
	    INTO :sql_duplicates,	/* WITH NO DUPLICATES	*/
		 :sql_unique		/* MODIFY to xxx UNIQUE	*/
	    FROM iitables
	    WHERE table_name = :sql_tname and
		  table_owner LIKE :user;
	if (sqlca.sqlcode ) {
		cerr << "Ingres SELECT duplicates error " << sqlca.sqlcode << " in Table::Table(char name[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	NoDuplicates	= (sql_duplicates[0] == 'U') ||
			  (sql_unique[0] == 'U');
	EXEC SQL DECLARE col CURSOR FOR
		 SELECT DISTINCT column_sequence
		 FROM iicolumns
		 WHERE table_name = :sql_tname and
		       table_owner LIKE :user
		 ORDER BY column_sequence DESC;
	EXEC SQL OPEN col;
	if (sqlca.sqlcode ) {
		cerr << "Ingres cursor open error " << sqlca.sqlcode << " in Table::Table(char name[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	for (int stop = 0; !stop;) {
		EXEC SQL FETCH col
			 INTO :sql_colno;
		switch (sqlca.sqlcode) {
		case 0:
			cols = new Column(sql_colno, this, cols);
			break;
		default:
			cerr << "Ingres fetch error " << sqlca.sqlcode << " in Table::Table(char name[])\n";
			cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		case NOT_FOUND:
			stop = 1;
		}
	}
	EXEC SQL CLOSE col;
 
    /*
	Count how many indexes there are and allocate space for them
	plus one for the primary index, if any.
    */
	EXEC SQL SELECT count(*)+1
		 INTO   :sql_indcount
		 FROM	iiindexes
		 WHERE	base_name = :sql_tname AND
			index_owner LIKE :user;
	if (sqlca.sqlcode ) {
		cerr << "Ingres select (count(*)+1) error " << sqlca.sqlcode << " in Table::Table(char name[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	IndexCount	= 0;
	index_list	= new Index *[sql_indcount];

    /*
	Is there a MODIFY statement for this table ?
    */
	EXEC SQL SELECT COUNT(*)
		 INTO :sql_colno
		 FROM iicolumns
		 WHERE table_name = :sql_tname AND
		       table_owner LIKE :user AND
		       key_sequence != 0;
	if (sqlca.sqlcode ) {
		cerr << "Ingres select (count(*)) error " << sqlca.sqlcode << " in Table::Table(char name[])\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
    /*
	If there is a MODIFY create an index for it.
    */
	if (sql_colno > 0) {
	    Index	*ind	=
	    index_list[IndexCount++]	
			= new Index(this, sql_unique[0]);
	    if (!ind->OK()) {
		cerr << "Error on primary index \n";
	    }
	}

    /*
	Now deal with the secondary indexes.
    */
	EXEC SQL DECLARE ind CURSOR FOR
		SELECT DISTINCT index_name
		       FROM iiindexes
		       WHERE base_name = :sql_tname AND
				index_owner LIKE :user
		       ORDER by index_name ;
	EXEC SQL OPEN ind;
	if (sqlca.sqlcode ) {
		cerr << "Ingres cursor open (index) error " << sqlca.sqlcode << " in Table::Table()()\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	for (stop=0; !stop; ) {
		EXEC SQL FETCH ind
			 INTO :sql_iname;
		switch (sqlca.sqlcode) {
		case 0:
			Index *ind			=
			index_list[IndexCount++]	=
				new Index(this, sql_iname);
			if (!ind->OK()) {
			    cerr << form("Error on index %s\n", sql_iname);
			}
			break;
		default:
			cout << form("rollback%s\n", GoString);
			cerr << "Ingres fetch (index) error " << sqlca.sqlcode << " in Table::Table()\n";
			cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		case NOT_FOUND:
			stop = 1;
		}
	}
	EXEC SQL CLOSE ind;


}

Table::~Table()
{

	if (cols) delete cols;
	for (int i = 0; i < IndexCount; i++) {
	    delete index_list[i];
	    index_list[i]	= 0;
	}
	if (index_list)
	    delete index_list;

}  

ostream& operator<<(ostream& s, Table & t) {

	s << form("drop table %s%s\n", t.name(), GoString);
	s << form("create table %s(\n", t.name());
	for (Column *c = t.cols; c; c = c->link()) {
		s << *c;
		if (c->link()) 
			s << ",\n";
		else
			s << "\n";
	}
	s << form(")%s%s\n", 
		t.NoDuplicates ? " with noduplicates" : "",
		GoString);
	if (!OracleOut && t.IndexCount) {
	    t.index_list[0]->modify(s);
	}
	for (int i=(OracleOut?0:1); i<t.IndexCount; i++) {
	    s << t.index_list[i];
	}
	s.flush();
	return s;

}  
void	Table::extract(ostream &s)
{	int	i;
	EXEC SQL BEGIN DECLARE SECTION;
	char	selectbuf[5000];	/* Ingres SELECT statement	*/
	EXEC SQL END DECLARE SECTION;
	char	insertbuf[5000];	/* Ingres INSERT statement	*/
	bzero(selectbuf, sizeof(selectbuf));
	bzero(insertbuf, sizeof(insertbuf));
	ostream b(sizeof(selectbuf), selectbuf);
	ostream ins(sizeof(insertbuf), insertbuf);

    /*
	In selectbuf[] build a SELECT statement to use as a cursor to
	fetch the Ingres data rows from the table. Simultaneously, in
	insertbuf[] build the stem of an INSERT statement to insert
	the data into the output table. The actual data values will be
	added to the stem as each data row is fetched.
    */
	int field_count = 0;
	b << "SELECT ";
	ins << form("insert into %s (\n", name());
	for (Column *c = cols; c; c= c->link()) {
		switch (c->type()) {
		case NUM:
		    b << form("VARCHAR(%s)%s ", 
				    c->name(), 
				    c->link() ? "," : "");
		    break;
		case MONEY:
		    b << form("VARCHAR(FLOAT8(%s))%s ",
				    c->name(), 
				    c->link() ? "," : "");
		    break;
		case DATE:
		default:
		    b << form("VARCHAR(%s)%s", c->name(), c->link() ? ", " : " ");
		    break;
		}
		ins << form("\t%s%s\n", c->name(), c->link() ? "," : "");
		field_count++;
	}
	ins << ") values (\n";
	b << form("FROM %s", name());
	b.close();
	ins.close();

    /*
	We have now built the text of the various SQL statements that
	we need. The next step is to tell Ingres about it and the
	target variables that we are going to use.
    */
	EXEC SQL PREPARE s1 FROM :selectbuf;
	if (sqlca.sqlcode ) {
		cerr << "Ingres PREPARE error " << sqlca.sqlcode << " for S1 in Table::extract(ostream &)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		cerr << form("Text is: '%s'\n", selectbuf);
		s << form("rollback%s\n", GoString);
		exit(1);
	}
	EXEC SQL DECLARE cc CURSOR FOR s1;

    /*
	Allocate a sqlda for 'field_count' columns with 30-char names.
	This will be used to hold pointers to our work variables into
	which Ingres will store the data rows fetched from the table.

    */
	fetchda		= new IISQLDA;
	if (!fetchda) {
		cerr << form("Ingres error allocating sqlda\n");
		s << form("rollback%s\n", GoString);
		exit(1);
	}
	fetchda->sqln	= field_count;
	fetchda->sqld	= field_count;
    /*
	sql_data[][], declared above, can hold 100 data items each of
	up to 240 bytes. sql_ind[] are the corresponding 100 indicators.
    */
	i = 0;
	for (c = cols; c; c= c->link()) {
		fetchda->sqlvar[i].sqltype	= IISQ_VCH_TYPE;		
		fetchda->sqlvar[i].sqldata	= (char *)&sql_data[i];
		fetchda->sqlvar[i].sqlind	= &sql_ind[i];
		fetchda->sqlvar[i].sqllen	= 240;
		i++;
	}

	EXEC SQL OPEN cc;
	if (sqlca.sqlcode ) {
		cerr << "Ingres cursor open error " << sqlca.sqlcode << " in Table::extract(ostream &)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}

	EXEC SQL DESCRIBE s1 INTO fetchda;
	if (sqlca.sqlcode ) {
		cerr << "Ingres describe-select error " << sqlca.sqlcode << " in Table::extract(ostream &)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}

	i = 0;
	for (c = cols; c; c= c->link()) {
		switch (c->type()) {
		case DATE:
		    fetchda->sqlvar[i].sqllen = IISQ_DTE_LEN+2;
		    break;
		case CHAR:
		    fetchda->sqlvar[i].sqllen = c->width()+2;
		    break;
		case MONEY:
		case FLOAT:
		case NUM:
		    break;
		default:
		    assert(0);
		}
		i++;
	}
	if (fetchda->sqln < 0) {
		cerr << form("Ingres DESCRIBE error - %d fields allocated; %d fields found\n",
		     field_count, fetchda->sqln);
		exit(1);
	}
	for (int stop = 0; !stop;) {
	    EXEC SQL FETCH cc
		     USING DESCRIPTOR fetchda;
	    switch (sqlca.sqlcode) {
	    case 0:
		s << insertbuf;
		int	i = 0;
		for (c=cols; c; c = c->link()) {
		    if (*fetchda->sqlvar[i].sqlind < 0)  {	// If NULL value
			if (NotNull) {		// Force NOT NULL
			    switch(c->type()) {
			    case FLOAT:
			    case MONEY:
			    case NUM:
				s << form("\t0%s\n",
				    c->link() ? "," : "");
				break;
			    case CHAR:
			    case DATE:
			    default:
				s << form("\t''%s\n",
					c->link() ? "," : "");
				break;
			    }
			} else {		// Honour NULLs
			    s << form("\tNULL%s\n",
				c->link() ? "," : "");
			}
		    } else if (*fetchda->sqlvar[i].sqlind > 0) {
			cerr << form("Table %s, field %s truncated from %d ch\n",
			   name(), c->name(), *fetchda->sqlvar[i].sqlind);
		    }

		    if (*fetchda->sqlvar[i].sqlind >= 0) {
			switch(c->type()) {
			case MONEY:
			case FLOAT:
			case NUM:
			    if (!((VC *)fetchda->sqlvar[i].sqldata)->len) 
				s << form("\t0%s\n",
				    c->link() ? "," : "");
			    else
				s << form("\t%.*s%s\n",
				    sql_data[i].len,
				    sql_data[i].arr,
				    c->link() ? "," : "");
			    break;
			case CHAR:
			    dequote(sql_data[i].arr);
			case DATE:
			default:
			    s << form("\t'%.*s'%s\n",
				    sql_data[i].len,
				    sql_data[i].arr,
				    c->link() ? "," : "");
			    break;
			}
		    }
		    i++;
		}
		s << form(")%s\n", GoString);
		break;
	    default:
		    cerr << "Ingres fetch error " << sqlca.sqlcode << " in Table::extract(ostream&)\n";
		    cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		    cerr << form("Table name %s\n", name());
		    s << form("rollback%s", GoString);
	    case NOT_FOUND:
		    stop = 1;
	    }
	    s.flush();
	}			// End of FETCH loop
	EXEC SQL CLOSE cc;
	delete fetchda;

}
void	Table::grant(ostream &s)
{	for (Column *c = cols; c; c = c->link()) {
	    if (!c->IndexColumn)
		s << form("grant update(%s) on %s to public%s\n",
		    c->name(), name(), GoString);
	}
	s << form("grant select on %s to public%s\n",
	    name(), GoString);
	s << form("grant delete on %s to public%s\n",
	    name(), GoString);
	s << form("grant insert on %s to public%s\n",
	    name(), GoString);

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/INGRES/TABLE.SCC" fi
if `test ! -s ./db_unload/INGRES/COLUMN.SCC` then
echo "writing ./db_unload/INGRES/COLUMN.SCC" cat > ./db_unload/INGRES/COLUMN.SCC << '\Rogue\Monster\' /* COLUMN.SCC  Process a column definition, while converting a database table  from Ingres to SQL.

This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk) */
#include <data_dict.hpp>
#include <assert.h>

EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
static char sql_tname[NAME_LENGTH];
static char

	sql_cname[NAME_LENGTH],
	sql_coltype[NAME_LENGTH],
	sql_nulls[9],
	sql_defaultval[9];
static long
	sql_int_datatype,
	sql_width,
	sql_scale,
	sql_colno;
static char	user[NAME_LENGTH];

EXEC SQL END DECLARE SECTION; Column::Column(long num, Table *up, Column *succ) {
	next		= succ;
	IndexColumn	= FALSE;
	parent		= up;
	nulls		= FALSE;
	WithDefault	= FALSE;
	sql_colno	= num;
	strcpy(sql_tname, up->name());
	strcpy(user, up->owner());
	EXEC SQL SELECT 
			column_name,
			column_datatype,
			column_length,
			column_scale,
			column_nulls,
			column_defaults,
			column_ingdatatype
		 INTO	:sql_cname,
			:sql_coltype,
			:sql_width,
			:sql_scale,
			:sql_nulls,
			:sql_defaultval,
			:sql_int_datatype
		 FROM iicolumns
		 WHERE table_name = :sql_tname AND
		       table_owner LIKE :user AND
		       column_sequence = :sql_colno;
	if (sqlca.sqlcode) {
		cerr << "Ingres select error " << sqlca.sqlcode << " in Column::Column(long, Table*, Column*)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	char	*s = strchr(sql_cname, ' ');
	if (s)	*s = 0;
	strcpy(cname, sql_cname);
	if (sql_int_datatype < 0 && !NotNull)
	    nulls	= TRUE;
	else
	    nulls	= FALSE;
	switch(sql_int_datatype) {
	case 30:	case -30:
	    coltype	= NUM;
	    break;
	case 31:	case -31:
	    coltype	= FLOAT;
	    break;
	case 37:	case -37:
	case 20:	case -20:
	case 21:	case -21:
	    coltype	= CHAR;
	    break;
	case 3:		case -3:
	    coltype	= DATE;
	    break;
	case 5:		case -5:
	    coltype	= MONEY;
	    break;
	}
	colwidth	= sql_width;

}  

Column::Column(long num, View *up, Column *succ) {

	next		= succ;
	IndexColumn	= FALSE;
	parent		= (Table*)up;
	sql_colno	= num;
	strcpy(sql_tname, up->name());
	EXEC SQL SELECT 
			column_name
		 INTO	:sql_cname
		 FROM iicolumns
		 WHERE table_name = :sql_tname AND
		       table_owner LIKE :user AND
		       column_sequence = :sql_colno;
	if (sqlca.sqlcode) {
		cerr << "Ingres select error " << sqlca.sqlcode << " in Column::Column(long, View*, Column*)\n";
		cerr << sqlca.sqlerrm.sqlerrmc << "\n";
		exit(1);
	}
	char	*s = strchr(sql_cname, ' ');
	if (s)	*s = 0;
	strcpy(cname, sql_cname);

}  

ostream& operator<<(ostream& s, Column& c) {

	s << form("\t%-.24s\t", c.cname);
	switch (c.coltype) {
	case CHAR:
		s << form("%s(%d)",
			OracleOut?"VARCHAR":"VCHAR",
			c.colwidth);
		break;
	case NUM:
		s << form("INTEGER");
		break;
	case DATE:
		s << form("DATE");
		break;
	case FLOAT:
		s << form("FLOAT");
		break;
	case MONEY:
		s << form("MONEY");
		break;
	default:
		assert(0);
	}
	if (!c.nulls) {
		s << " NOT NULL";
		if (Default || c.WithDefault)
		    if (!c.IndexColumn)
			s << " WITH DEFAULT";
	}
	return s;

}

// Locate a column by name
Column *Table::column(const char cname[]) {

	for (Column *c = cols; c; c= c->link())
	    if (!stricmp(cname, c->name()))
		return c;
	return 0;

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/INGRES/COLUMN.SCC" fi
if `test ! -s ./db_unload/INGRES/DEQUOTE.C` then
echo "writing ./db_unload/INGRES/DEQUOTE.C" cat > ./db_unload/INGRES/DEQUOTE.C << '\Rogue\Monster\' /* DEQUOTE.C This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk)

*/

#ifdef SYSV
#include <string.h>
#define index   strchr
#else
#include <strings.h>
#endif

/*
	If s.arr contains embedded apostrophes, we have to replace each
	one by a doubled apostrophe in order to keep Ingres happy.
	This we do by counting the total number of apostophes and
	shuffling data to the right by the number of apostrophes to
	the left of each byte.

*/

dequote(s)
struct varchar {

	short	len;
	char	arr[1];
}	*s;
{	int	i, j, len, count, count2;
	char	*p;

	if (!(p=index(s->arr, '\'')))
		return ;
	count = 1;
	while (p= index(p+1, '\''))
		count++;
	count2	= count;
	for (i= s->len-1; count > 0; i--) {
		if (s->arr[i] == '\'') {
			s->arr[i+count] = s->arr[i];
			count--;
		}
		s->arr[i+count] = s->arr[i];
	}
	s->len		+= count2;
	s->arr[s->len]	= 0;

	return ;

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/INGRES/DEQUOTE.C" fi
if `test ! -s ./db_unload/INGRES/README` then
echo "writing ./db_unload/INGRES/README" cat > ./db_unload/INGRES/README << '\Rogue\Monster\' This program unloads an Ingres database as an SQL stream. I wrote it for converting Ingres DBs to Oracle. There is also an equivalent, and very similar, program for converting Oracle to Ingres.

Not all data types are catered for: they did not occur in my DB. No attempt is made to deal with constraints, user groups or other esoterica.

The supplied ``man'' page is actually from the Oracle to Ingres version. It is _almost_ correct, but take it with a pinch of salt.

Since I am embedding SQL in C++, we have to be a little careful in how we write the code. In particular: 1. esql/C does not recognise // comments. 2. Function arguments and members of classes cannot be used as SQL variables

   (they can't be in a DECLARE SECTION). 3. All sorts of functions have to be declared by hand as extern "C" to satisfy

   C++'s calling convention.
4. We have to massage the output of esql/c with sed to deal with the conflict

   between variable names and structure tags.

I offer no warranties as to the correctness of this code. You are welcome to use it, but at your own risk.

Raphael Mankin (raph_at_panache.demon.co.uk). 7 Jan 1993
\Rogue\Monster\
else
  echo "will not over write ./db_unload/INGRES/README" fi
if `test ! -s ./db_unload/INGRES/DERESERV.C` then
echo "writing ./db_unload/INGRES/DERESERV.C" cat > ./db_unload/INGRES/DERESERV.C << '\Rogue\Monster\' /* DERESERVE.C This program was written by Raphael Mankin. (raph_at_panache.demon.co.uk)

CHANGES:
7/4/92 Change returned type from void to char*

*/

#include <stdio.h>
#ifndef __GNUG__	/* For AT&T compiler	*/
#define stricmp strcasecmp
#include <strings.h>
#else
#include <string.h>
#endif

static char *reserved[] = {
			"command",
			"count",
			"default",
			"file",
			"index",
			0
		};

const char *dereserve(const char name[])
{	int	i;
	static char	buf[100];

	for (i=0; reserved[i]; i++) {
	    if (!stricmp(name, reserved[i])) {
		strcpy(buf, name);
		strcat(buf, "_x");
		return buf;
	    }
	}

	return name;

}
\Rogue\Monster\
else
  echo "will not over write ./db_unload/INGRES/DERESERV.C" fi
if `test ! -s ./db_unload/README`
then
echo "writing ./db_unload/README"
cat > ./db_unload/README << '\Rogue\Monster\' These programs unload an Ingres or Oracle database as an SQL stream. I wrote them for converting databases back and forth when porting an application from Oracle to Ingres and the data also had to be moved.

Not all data types are catered for: they did not occur in my DB. No attempt is made to deal with constraints, user groups or other esoterica.

The supplied ``man'' page is actually from the Oracle to Ingres version. It is _almost_ correct, but take it with a pinch of salt.

Since I am embedding SQL in C++, we have to be a little careful in how we write the code. In particular: 1. esql/C does not recognise // comments. 2. Function arguments and members of classes cannot be used as SQL variables

   (they can't be in a DECLARE SECTION). 3. All sorts of functions have to be declared by hand as extern "C" to satisfy

   C++'s calling convention.
4. We have to massage the output of esql/c with sed to deal with the conflict

   between variable names and structure tags.

I offer no warranties as to the correctness of this code. You are welcome to use it, but at your own risk.

Raphael Mankin (raph_at_panache.demon.co.uk). 7 Jan 1993
\Rogue\Monster\
else
  echo "will not over write ./db_unload/README" fi
echo "Finished archive 1 of 1"
exit

--------------Cut here-----------------
______________________________________________________________
Johan Montald                                                /
Customer Representative                                     ///
  Computer Associates International Belgium                /// /
  Woluwelaan 34 B13                                       /// / /
  1200 Brussels                                          ///// / /
  Belgium                                               ///// / / /
email: johan_at_ingres.com                                /////// / / /
phone: +32-2/773.28.11  ext 865                       ///////// / / /
fax  : +32-2/762.73.59                               ///////////// / /
____________________________________________________///////////////////.

God is real, unless declared integer Received on Tue Sep 20 1994 - 09:42:47 CEST

Original text of this message