ftp.nice.ch/pub/next/database/postgres-4.2/postgresLibs.NIHS.b.tar.gz#/postgresLibs.pkg/postgresLibs.tar.gz#/contrib/xposthelp/posthelp.c

This is posthelp.c in view mode; [Download] [Up]

/*
 * /usr/local/devel/postgres-v4r2/src/contrib/xposthelp/RCS/posthelp.c,v 1.1 1993/07/24 03:03:50 aoki Exp
 */

/**********************************************************
* Getting POSTGRES Table Info. Similar to the command     *
* describe in SQLPLUS (ORACLE).                           *
**********************************************************/

#include "tmp/libpq.h"
#include "utils/geo-decls.h"

#define NUM_DBS  100    /* Maximum number of databases */
#define NUM_REL  1000   /* Maximum number of tables in each database */

char dbs[NUM_DBS][80],  /* All databases */
     user_name[80];     /* All users     */

int user_num,           /* User oid */
    dbs_num,            /* Number of databases */
    tbs_num;            /* Number of tables */

struct {
  char name[80];        /* Relation name */
  int  kind;            /* 1 if it is a relation 0 if it is an index */
  int  ind;             /* 1 if it is indexed 0 otherwise */
  char oid[20];         /* oid of the relation */
  int  numrec;          /* Number of records */
} tbs[NUM_REL];

char *GetIndex();

void main(argc, argv)
     int argc;
     char *argv[];
{
  PortalBuffer *portalbuf;
  char         *res, str[200], attr_name[80];
  int          ngroups,tupno, grpno, ntups, nflds, i, fldno, attr_type, tuple_index, j;
  static int   extensive = 0;

  if (argc > 5)
    {
      printf("Wrong number of arguments. Type: \n\n posthelp {-ext} db_user {db_name} {table_name}\n");
      exit(1);
    }
  else
    if (argc == 1)
      {
	FindAllUsers();
	printf("\nNow you can try: \n\tposthelp <user_name> or \n\tposthelp ext <user_name> or \n\tposthelp <user_name> <db_name> or \n\tposthelp ext <user_name> <db_name> or \n\tposthelp <user_name> <db_name> <table_name>\n\n");
	exit(1);
      }
  else
    if (argc > 1)
      {
	/* check if user wants extensive listing */
	if ((strcmp(argv[1], "ext") == 0) || (strcmp(argv[1], "-ext") == 0))
	  {
	    if (argc == 2)
	      {
		printf(" You have to at least provide an user, i.e.\n\t\tposthelp -ext <user-name>\n\n");
		exit(1);
	      }
	    extensive = 1;
	  }
	else
	  extensive = 0;
      }

  /* Get user name */
  strcpy(user_name, argv[1 + extensive]);
  user_num = FindUser(user_name);

  if (argc == 2 + extensive)
    {
      /* Open up database */
      PQsetdb("postgres");
      
      /* Start up processing */
      PQexec("begin");
      
      /* Define query for this table */
      sprintf(str, "retrieve iportal junk (p.datname) from p in pg_database where p.datdba = \"%d\" sort by datname", user_num);

      res = (char *)PQexec(str);
      if (*res == 'E') 
	{
	  printf("%s\nfailed",++res);
	  goto exit_error;
	}
      
      res = (char *)PQexec("fetch all in junk");
      if (*res != 'P') 
	{
	  printf("\nno portal");
	  goto exit_error;
	}
      
      /* Get portal buffer given portal name. */
      portalbuf = PQparray(++res);
      
      /* Get number of groups in portal buffer */
      ngroups = PQngroups(portalbuf);
      
      for (grpno = 0; grpno < ngroups; grpno++) 
	{
	  /* Get number of tuples in relation */
	  ntups = PQntuplesGroup(portalbuf, grpno);
	  
	  dbs_num = ntups;

	  for (tupno = 0; tupno < ntups; tupno++) 
	    strcpy(dbs[tupno], PQgetvalue(portalbuf,tupno,0));

	}

      PQexec("end");
  
      PQfinish();

      /* Write all tables for each dbase */
      printf("*******\n*******\n\tUSER:     %s (%d) \n\n", user_name, user_num);

      if (!extensive)
	printf("\tDATABASES:\n\n");

      for (j = 0; j < dbs_num; j++)
	{
	  if (extensive)
	    {
	      printf("\tDATABASE:   %s \n\n", dbs[j]);

	      FillTables(dbs[j]);
	      
	      for (i=0; i < tbs_num; i++)
		{
		  if (tbs[i].ind)
		    printf("\tTABLE   = %s\n\tKIND    = %s\n\tINDEXED = yes (%s)\n\tOID     = %s\n\tRECORDS = %d\n", 
			   tbs[i].name, 
			   (tbs[i].kind) ? "relation": "index", 
			   GetIndex(tbs[i].oid), 
			   tbs[i].oid, tbs[i].numrec);
		  else
		    printf("\tTABLE   = %s\n\tKIND    = %s\n\tINDEXED = no\n\tOID     = %s\n\tRECORDS = %d\n", 
			   tbs[i].name, 
			   (tbs[i].kind) ? "relation": "index", 
			   tbs[i].oid, tbs[i].numrec);

		  /* Only print table if it is a relation. No indexes */
		  if (tbs[i].kind)
		    {
		      printf("\n");
		      PrintTable(tbs[i].oid);
		    }
		  else
		    PrintIndexInfo(tbs[i].oid);
		  
		  printf("\n\n");
		}
	    }
	  else
	    printf("\t%s\n", dbs[j]);
	}
    }
  else
    {
      /* fill up only one database */
      dbs_num = 1;
      strcpy(dbs[0], argv[2 + extensive]);

      if (!CheckDBase(dbs[0]))
	{
	  printf("Database \"%s\" does not exist for USER \"%s\"\n", dbs[0], user_name);
	  exit(1);
	}
	

      /* Get all tables in this db */
      if (argc == 3 + extensive)
	{
	  printf("*******\n*******\n\tUSER:   %s (%d) \n\n", user_name, user_num);
	  printf("\tDATABASE:   %s \n\n", dbs[0]);

	  /* Open up database */
	  FillTables(dbs[0]);

	  if (extensive)
	    {
	      for (i=0; i < tbs_num; i++)
		{
		  if (tbs[i].ind)
		    printf("\tTABLE   = %s\n\tKIND    = %s\n\tINDEXED = yes (%s)\n\tOID     = %s\n\tRECORDS = %d\n", 
			   tbs[i].name, 
			   (tbs[i].kind) ? "relation": "index", 
			   GetIndex(tbs[i].oid), 
			   tbs[i].oid, tbs[i].numrec);
		  else
		    printf("\tTABLE   = %s\n\tKIND    = %s\n\tINDEXED = no\n\tOID     = %s\n\tRECORDS = %d\n", 
			   tbs[i].name, 
			   (tbs[i].kind) ? "relation": "index", 
			   tbs[i].oid, tbs[i].numrec);

		  
		  /* Only print table if it is a relation. No indexes */
		  if (tbs[i].kind)
		    {
		      printf("\n");
		      PrintTable(tbs[i].oid);
		    }
		  else
		    PrintIndexInfo(tbs[i].oid);
		  
		  printf("\n\n");
		}
	    }
	  else
	    {
	      printf("\tTABLE\t\t\tKIND\t\tINDEXED\tOID\tRECORDS\n\n"); 
	      
	      for (i=0; i < tbs_num; i++)
		{
		  FillName(tbs[i].name);
		  printf("\t%s\t\t%c\t\t%c\t%s\t%d\n", 
			 tbs[i].name, (tbs[i].kind) ? 'r' : 'i', (tbs[i].ind) ? 'y' : 'n', tbs[i].oid, tbs[i].numrec);
		}
	    }
	}
      else
	{
	  tbs_num = 1;
	  strcpy(tbs[0].name, argv[3 + extensive]);
	  
	  printf("*******\n*******\n\tUSER:   %s (%d) \n\n", user_name, user_num);
	  printf("\tDATABASE:   %s \n\n", dbs[0]);

	  FillOneTable(dbs[0]);

	  if (tbs[0].ind)
	    printf("\tTABLE   = %s\n\tKIND    = %s\n\tINDEXED = yes (%s)\n\tOID     = %s\n\tRECORDS = %d\n", 
		   tbs[0].name, 
		   (tbs[0].kind) ? "relation": "index", 
		   GetIndex(tbs[0].oid), 
		   tbs[0].oid, tbs[0].numrec);
	  else
	    printf("\tTABLE   = %s\n\tKIND    = %s\n\tINDEXED = no\n\tOID     = %s\n\tRECORDS = %d\n", 
		   tbs[0].name, 
		   (tbs[0].kind) ? "relation": "index", 
		   tbs[0].oid, tbs[0].numrec);
	  
	  
	  if (tbs[0].kind) 
	    {
	      printf("\n");
	      PrintTable(tbs[0].oid);
	    }
	  else
	    PrintIndexInfo(tbs[0].oid);
	}

    }
  
  exit(0);
  
 exit_error:
  PQexec("end");
  PQfinish();
  exit(1);
  
}



/*************************************************
 *                                               *
 *                                               *
 *  Given a table name it prints all             *
 *  the attibutes and their types.               *
 *                                               *
 *                                               *
 *                                               *
*************************************************/

PrintTable(tableoid)
     char *tableoid;
{
  PortalBuffer *portalbuf;
  char         *res, str[200], attr_name[80], type_str[80];
  int          ngroups,tupno, grpno, ntups, nflds, i, fldno, attr_type, tuple_index;
  
  /* Start up processing */
  PQexec("begin");
  
  /* Define query for this table */
  sprintf(str, "retrieve iportal junk (p.attname, p.atttypid, p.attnum) from p in pg_attribute where p.attrelid = \"%s\" and p.attnum > 0 sort by attnum", tableoid);

  res = (char *)PQexec(str);
  if (*res == 'E') 
    {
      printf("%s\nfailed",++res);
      goto exit_error;
    }
  
  res = (char *)PQexec("fetch all in junk");
  if (*res != 'P') 
    {
      printf("\nno portal");
      goto exit_error;
    }
  
  /* Get portal buffer given portal name. */
  portalbuf = PQparray(++res);
  
  /* Get number of tuples in relation */
  ntups = PQntuplesGroup(portalbuf, 0);

  /* Print header 
  printf("\t\tAttribute\t\tType\t\t\tNumber\n\n");*/
  printf("\t\tAttribute\t\tType\n\n");
  
  /* Get the attribute name for this group and field number */
  for (i = 0; i < ntups; i++)
    {
      int *tmp;

      strcpy(attr_name, PQgetvalue(portalbuf,i,0));
      tmp = (int *) PQgetvalue(portalbuf,i,1);
      attr_type = *tmp;

      FillName(attr_name);
      
      switch(attr_type)
	{
	case 16:
	  printf("\t\t%s\t\tboolean \n", attr_name);
/*	  printf("\t\t%s\t\tboolean \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 18:
	  printf("\t\t%s\t\tchar    \n", attr_name);
/*	  printf("\t\t%s\t\tchar    \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 19:
	  printf("\t\t%s\t\tchar16  \n", attr_name);
/*	  printf("\t\t%s\t\tchar16  \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 22:
	  printf("\t\t%s\t\tarray   \n", attr_name);
/*	  printf("\t\t%s\t\tarray   \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 23:
	  printf("\t\t%s\t\tint4    \n", attr_name);
/*	  printf("\t\t%s\t\tint4    \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 25:
	  printf("\t\t%s\t\ttext    \n", attr_name);
/*	  printf("\t\t%s\t\ttext    \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 26:
	  printf("\t\t%s\t\toid     \n", attr_name);
/*	  printf("\t\t%s\t\toid     \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 600:
	  printf("\t\t%s\t\tpoint    \n", attr_name);
/*	  printf("\t\t%s\t\tpoint    \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 601:
	  printf("\t\t%s\t\tlseg    \n", attr_name);
/*	  printf("\t\t%s\t\tlseg    \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 602:
	  printf("\t\t%s\t\tpath    \n", attr_name);
/*	  printf("\t\t%s\t\tpath    \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 603:
	  printf("\t\t%s\t\tbox     \n", attr_name);
/*	  printf("\t\t%s\t\tbox     \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 604:
	  printf("\t\t%s\t\tpolygon \n", attr_name);
/*	  printf("\t\t%s\t\tpolygon \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 700:
	  printf("\t\t%s\t\tfloat4  \n", attr_name);
/*	  printf("\t\t%s\t\tfloat4  \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	case 701:
	  printf("\t\t%s\t\tpoint   \n", attr_name);
/*	  printf("\t\t%s\t\tpoint   \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	default:
	  printf("\t\t%s\t\tother   \n", attr_name);
/*	  printf("\t\t%s\t\tother   \t\t(%d)\n", attr_name, attr_type);*/
	  break;
	  
	}/* end switch */
      
    }/* end for loop on fldno */

  if (ntups == 0)
    printf("\t\tEMPTY RELATION\n");

  PQexec("end");
  return;

 exit_error:
  PQexec("end");
  PQfinish();
  exit(1);

}



/*************************************************
 *                                               *
 *                                               *
 *                                               *
 *                                               *
 *                                               *
 *                                               *
 *                                               *
*************************************************/

FillTables(dbname)
     char *dbname;
{
  PortalBuffer *portalbuf;
  char         *res, str[200], attr_name[80];
  int          ngroups,tupno, grpno, ntups, nflds, i, fldno, attr_type, tuple_index, j;
  
  /* Open up database */
  PQsetdb(dbname);
  
  /* Start up processing */
  PQexec("begin");
  
  /* Define query for this table */
  sprintf(str, 
	  "retrieve portal junk (p.relname,p.relkind,p.relhasindex, p.oid, p.reltuples) from p in pg_class where p.relowner = \"%d\" sort by relname", user_num);

  res = (char *)PQexec(str);
  if (*res == 'E') 
    {
      printf("%s\nfailed",++res);
      goto exit_error;
    }
  
  res = (char *)PQexec("fetch all in junk");
  if (*res != 'P') 
    {
      printf("\nno portal");
      goto exit_error;
    }
  
  /* Get portal buffer given portal name. */
  portalbuf = PQparray(++res);
  
  /* Get number of groups in portal buffer */
  ngroups = PQngroups(portalbuf);
  
  for (grpno = 0; grpno < ngroups; grpno++) 
    {
      /* Get number of tuples in relation */
      ntups = PQntuplesGroup(portalbuf, grpno);
      
      tbs_num = ntups;

      for (tupno = 0; tupno < ntups; tupno++) 
	{
	  char *c;
	  int  *tmp;
	  
	  strcpy(tbs[tupno].name, PQgetvalue(portalbuf,tupno,0));

	  c = PQgetvalue(portalbuf,tupno,1);
	  
	  tbs[tupno].kind = (c[0] == 'r') ? 1 : 0;
	  
	  c = (char *)PQgetvalue(portalbuf,tupno,2);
	  
	  tbs[tupno].ind = (c[0] == 't') ? 1 : 0;

	  strcpy(tbs[tupno].oid, PQgetvalue(portalbuf,tupno,3));

	  c = (char *) PQgetvalue(portalbuf,tupno,4);

	  
	  tbs[tupno].numrec = atoi(c);
	  
	}
    }
  
  PQexec("end");
  
  PQfinish();

  return;

 exit_error:
  PQexec("end");
  PQfinish();
  exit(1);

}



/*************************************************
 *                                               *
 *                                               *
 *                                               *
 *                                               *
 *                                               *
 *                                               *
 *                                               *
*************************************************/

FillOneTable(dbname)
     char *dbname;
{
  PortalBuffer *portalbuf;
  char         *res, str[200], attr_name[80], *c;
  int          ngroups,tupno, grpno, ntups, nflds, i, fldno, attr_type, tuple_index, j;
  
  /* Open up database */
  PQsetdb(dbname);
  
  /* Start up processing */
  PQexec("begin");
  
  /* Define query for this table */
  sprintf(str, 
	  "retrieve portal junk (p.relname, p.relkind,p.relhasindex, p.oid, p.reltuples) from p in pg_class where p.relname = \"%s\"", tbs[0].name);

  res = (char *)PQexec(str);
  if (*res == 'E') 
    {
      printf("%s\nfailed",++res);
      goto exit_error;
    }
  
  res = (char *)PQexec("fetch all in junk");
  if (*res != 'P') 
    {
      printf("\nno portal");
      goto exit_error;
    }
  
  /* Get portal buffer given portal name. */
  portalbuf = PQparray(++res);
  
  /* Get number of groups in portal buffer */
  ngroups = PQngroups(portalbuf);
  
  for (grpno = 0; grpno < ngroups; grpno++) 
    {
      /* Get number of tuples in relation */
      ntups = PQntuplesGroup(portalbuf, grpno);
       
      if (ntups)
	{
	  char *c;
	  int  *tmp;
	  
	  strcpy(tbs[0].name, PQgetvalue(portalbuf,0,0));

	  c = PQgetvalue(portalbuf,0,1);
	  
	  tbs[0].kind = (c[0] == 'r') ? 1 : 0;
	  
	  c = (char *)PQgetvalue(portalbuf,0,2);
	  
	  tbs[0].ind = (c[0] == 't') ? 1 : 0;

	  strcpy(tbs[0].oid, PQgetvalue(portalbuf,0,3));

	  c = (char *) PQgetvalue(portalbuf,0,4);
	  
	  tbs[tupno].numrec = atoi(c);
	  
	}
      else
	{
	  printf(" Error: table %s does not exist in database %s.\n", tbs[0].name, dbname);
	  goto exit_error;
	}
    }

  

  PQexec("end");
  
  PQfinish();

  return;

 exit_error:
  PQexec("end");
  PQfinish();
  exit(1);

}



FillName(attr_name)
     char *attr_name;
{
  int length, i;

  /* Fill up attr_name if less than 8 characters */
  length = strlen(attr_name);

  if (length <9)
    {
      for (i = length; i < 9; i++)
	attr_name[i] = ' ';

      attr_name[9] = '\0';

    }

}






/*************************************************
 *                                               *
 *                                               *
 *  Given an indexed table name it prints all    *
 *  the index information.                       *
 *                                               *
 *  In pg_index the attributes:                  *
 *                                               *
 *  indexrelid stands for the oid of the index   *
 *             relation.                         *
 *                                               *
 *  indrelid is the oid of the relation indexed. *
 *                                               *
*************************************************/

PrintIndexInfo(indexoid)
     char *indexoid; /* oid of the index relation. More than one key on this index */
{
  PortalBuffer *portalbuf;
  char         *res, str[200], relname[80];
  int          ntups, i, relid, numkeys;
  struct {
    int key;
    char attname[20];
  } keys[10];

  /* Start up processing */
  PQexec("begin");
  
  /****************************************
  * First let's find out the relation oid *
  * that was indexed by indexoid and the  *
  * correpondent index keys               *
  ****************************************/

  /* Define query for this table */
  sprintf(str,"retrieve portal junk (p.indrelid, p.indkey[1],p.indkey[2],p.indkey[3],p.indkey[4],p.indkey[5],p.indkey[6],p.indkey[7],p.indkey[8]) from p in pg_index where p.indexrelid = \"%s\"", indexoid);

  res = (char *)PQexec(str);
  if (*res == 'E') 
    {
      printf("%s\nfailed",++res);
      goto exit_error;
    }
  
  res = (char *)PQexec("fetch all in junk");
  if (*res != 'P') 
    {
      printf("\nno portal");
      goto exit_error;
    }
  
  /* Get portal buffer given portal name. */
  portalbuf = PQparray(++res);
  
  /* Get number of tuples in relation */
  ntups = PQntuplesGroup(portalbuf, 0);

  if (ntups)
    {
      char *tmp;

      tmp = (char *) PQgetvalue(portalbuf,0,0);

      /* relation oid on which the index is defined */
      relid = atoi(tmp);

      for (i = 1; i < 9; i++)
	{
	  /* Get each key attribute number */
	  tmp = (char *) PQgetvalue(portalbuf,0,i);

	  if (atoi(tmp) == 0)
	    break;

	  keys[i-1].key = atoi(tmp);

	}

      numkeys = i-1;

      /******************************************************
      *  Now for each key number and for the relation oid   *
      *  get the relation name and the attribute name       *
      ******************************************************/
      for (i = 0; i < numkeys; i++)
	{
	  PQexec("end");
	  PQfinish();
	  
	  /* Start up processing */
	  PQexec("begin");
  
	  /* Define query for this table */
	  sprintf(str,"retrieve portal junk (p.relname, pg_attribute.attname) from p in pg_class where p.oid = \"%d\" and pg_attribute.attnum = \"%d\" and pg_attribute.attrelid = \"%d\"", relid, keys[i].key, relid);

	  res = (char *)PQexec(str);
	  if (*res == 'E') 
	    {
	      printf("%s\nfailed",++res);
	      goto exit_error;
	    }
	  
	  res = (char *)PQexec("fetch all in junk");
	  if (*res != 'P') 
	    {
	      printf("\nno portal");
	      goto exit_error;
	    }
	  
	  /* Get portal buffer given portal name. */
	  portalbuf = PQparray(++res);
	  
	  /* Get number of tuples in relation */
	  ntups = PQntuplesGroup(portalbuf, 0);
	  
	  if (ntups)
	    {
	      strcpy(keys[i].attname, PQgetvalue(portalbuf,0,1));
	      strcpy(relname, PQgetvalue(portalbuf,0,0));
	    }
	}
      
      /* Put out info on relation name and key attribute names */
      printf("\tINDEX ON= %s \n", relname);

      printf("\tKEYS    = "); 
      for (i=0; i< numkeys; i++)
	printf("%s  ", keys[i].attname);

      printf("\n");
	  

    }

  PQexec("end");
  PQfinish();
  return;

 exit_error:
  PQexec("end");
  PQfinish();
  exit(1);




}




/*************************************************
 *                                               *
 *  Find all users in postgres system.           *
 *                                               *
*************************************************/

int FindAllUsers()
{
  PortalBuffer *portalbuf;
  char         *res, str[200];
  int          ntups, result, i;
  
  /* Open up database */
  PQsetdb("postgres");
  
  /* Start up processing */
  PQexec("begin");
  
  /* Define query for this table */
  strcpy(str,"retrieve portal junk (p.usename) from p in pg_user ");

  res = (char *)PQexec(str);
  if (*res == 'E') 
    {
      printf("%s\nfailed",++res);
      goto exit_error;
    }
  
  res = (char *)PQexec("fetch all in junk");
  if (*res != 'P') 
    {
      printf("\nno portal");
      goto exit_error;
    }
  
  /* Get portal buffer given portal name. */
  portalbuf = PQparray(++res);
  
  /* Get number of tuples in relation */
  ntups = PQntuplesGroup(portalbuf, 0);
      
  printf("\tUSERS: \n");

  for (i = 0; i < ntups; i++)
    printf("\t\t%s\n", PQgetvalue(portalbuf,i,0));

  PQexec("end");
  PQfinish();
  return;

 exit_error:
  PQexec("end");
  PQfinish();
  exit(1);

}




/*************************************************
 *                                               *
 *  Check if the given dbase exists in the       * 
 *  postgres system.                             *
 *  Returns TRUE if it exists FALSE otherwise.   *
 *                                               *
*************************************************/

int CheckDBase(dbname)
     char *dbname;
{
  PortalBuffer *portalbuf;
  char         *res, str[200];
  int          ntups, result, i;
  
  /* Open up database */
  PQsetdb("postgres");
  
  /* Start up processing */
  PQexec("begin");
  
  /* Define query for this table */
  sprintf(str,"retrieve portal junk (p.datname) from p in pg_database where p.datdba = \"%d\" and p.datname = \"%s\" ", user_num, dbname);

  res = (char *)PQexec(str);
  if (*res == 'E') 
    {
      printf("%s\nfailed",++res);
      goto exit_error;
    }
  
  res = (char *)PQexec("fetch all in junk");
  if (*res != 'P') 
    {
      printf("\nno portal");
      goto exit_error;
    }
  
  /* Get portal buffer given portal name. */
  portalbuf = PQparray(++res);
  
  /* Get number of tuples in relation */
  ntups = PQntuplesGroup(portalbuf, 0);
      
  PQexec("end");
  PQfinish();
  return(ntups);

 exit_error:
  PQexec("end");
  PQfinish();
  exit(1);

}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.