/*

    GEO, Postgres GIS (Geografic Information System) frontend

    Copyright (C) 1991  TNO Institute for Perception, The Netherlands
			Written by: Tom Vijlbrief

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


Note that parts of this distribution are taken from the ET++
distribution. See the et2.2/ subdirectories for details.

===============================================================================
Tom Vijlbrief
TNO Institute for Perception		Phone: +31 34 63 562 11
P.O. Box 23				Fax:   +31 34 63 539 77
3769 ZG  Soesterberg			E-mail: tom@izf.tno.nl
The Netherlands				or: uunet!hp4nl.nluug.nl!tnosoes!tom
===============================================================================

*/

static char *sccs_id= (sccs_id, "@(#)Quel.c	3.11 7/29/92");

#include	"Quel.h"
#include	"Alert.h"
#include	"stdio.h"

#include	"String.h"

#define FChunk	500
#define FChunkS	"500"

#define DEBUG

#ifdef ET23
inline void AllWait(bool) {}
#else
#endif

char *itoa(int i)
{
  static char s[20];

  sprintf(s, "%d", i);
  return s;
}


// code taken from the PostGres monitor:
char * QuelExec(char *s)
{
  char	*r;
  int	nqueries= 0;

  AllWait(TRUE);
  for (;;) {
#ifdef DEBUG
    extern char *curdb;
    if (s[0] != ' ')
      fprintf(stderr, "PostQuel (%s): %s\n", curdb, s);
#endif
    r= PQexec(s);
#ifdef DEBUG
    if (r[0] != 'I')
      fprintf(stderr, "Result: %s\n", r);
#endif
    s= " ";
    if (r[0] == 'A' || r[0] == 'P') {
      nqueries++;
      continue;
    } else if (r[0] == 'C') {
      nqueries++;
      continue;
    } else if (r[0] == 'I') {
      PQFlushI(nqueries - 1);
      break;
    } else {
      break;
    }
  }
  AllWait(FALSE);
  return r;
}


bool	QueryIter::busy_iter[MYMAXPORTALS];

void QueryIter::SetFields(bool add_table, char **fp)
{
  bool first= TRUE;

  fields[0]= '\0';
  for (; *fp; fp++) {
	char col[100];
	if (add_table)
	  sprintf(col, "%s%s.%s", first ? "": ",", table, *fp);
	else
	  sprintf(col, "%s%s", first ? "": ",", *fp);
	first= FALSE;
	strcat(fields, col);
  }
}


void QueryIter::SetVFields(bool add_table, char *f, va_list ap)
{
  char		*field_list[100];
  char		**attr= field_list;

  *attr++= f;
  while (*attr= va_arg(ap, char*)) {
	attr++;
  }
  SetFields(add_table, field_list);
}


void QueryIter::SetFields(bool add_table, char *va_(field), ...)
{
  va_list	ap;

  va_start(ap, va_(field));
  SetVFields(add_table, va_(field), ap);
  va_end(ap);
}

void QueryIter::SetBin()
{
  is_bin= TRUE;
}

void QueryIter::SetNoTrans()
{
  notrans= TRUE;
}

void QueryIter::SortBy(char *s)
{
  SafeDelete(sortby);
  sortby= strsave(s);
}


void QueryIter::Init(char *t, int nf, char *w)
{
  is_bin= notrans= manwhere= QuelError= fetched= FALSE;
  nfields= ti= nt= 0;
  time= 0;
  sortby= 0;
  first_fetch= TRUE;

  for (iterind= 0; iterind < MYMAXPORTALS; iterind++)
	if (!busy_iter[iterind])
		break;
  if (iterind == MYMAXPORTALS) {
	fprintf(stderr, "Too many QueryIters\n");
	abort();
  }
  busy_iter[iterind]= TRUE;

  nfetch= nf;
  table= strsave(t);
  where= w ? strsave(w): 0;
}


static int Pid;

void QueryIter::DoFetch()
{
  if (fetched || ti < nt || QuelError)
	return;

  char	q[8192];

  ti= 0;
  if (first_fetch) {
    char from[8192];

    if (Pid == 0)
      Pid= getpid();

    if (time)
      sprintf(from, "from %s in %s%s", table, table, time);
    else
      from[0]= '\0';

    // Unique portal names may prevent multi user problems, Postgres 3.1
    sprintf(q, "retrieve %sportal qi_%d_%d (%s) %s %s %s %s",
	is_bin ? "i": "", Pid, iterind,
	fields, from,
	where && where[0] && !manwhere ? "where": "",
	where ? where: "",
	sortby ? sortby: "");

    char *rets;
    if (!notrans)
      QuelExec("begin");
    if ((rets= QuelExec(q))[0] == 'R') {
	nt= nfields= 0;
	QuelError= TRUE;
#if 0
	ShowAlert(eAlertNote, "%s:\n@B%s@P",
	  q, rets);
#else
	extern char PQerrormsg[];
	ShowAlert(eAlertNote, "%s", PQerrormsg);
	// fprintf(stderr, "%s\n", PQerrormsg);
#endif
	return;
    }
  }
  sprintf(q, "fetch %s in qi_%d_%d",
    nfetch== 0 ? FChunkS: itoa(nfetch), Pid, iterind);
  QuelExec(q);

  sprintf(q, "qi_%d_%d", Pid, iterind);
  p= PQparray(q);

  int ntgroups= PQngroups(p);
  if (ntgroups > 1) {
    fprintf(stderr, "QueryIter::DoFetch: Ingnoring %d tuple groups!\n",
      ntgroups-1);
  } else if (ntgroups == 1) {
    nt= PQntuplesGroup(p, 0);
    if (nt < FChunk || nfetch)
	fetched= TRUE;
    if (first_fetch)
	nfields= PQnfieldsGroup(p, 0);
  } else {
    nt= nfields= 0;
  }
  first_fetch= FALSE;
}


QueryIter::QueryIter(char *t, int nf, char *w)
{
  Init(t, nf, w);
}


QueryIter::QueryIter(char *t)
{
  static char *all_list[]= {
    "all", 0
  };

  Init(t, 0, 0);
  SetFields(TRUE, all_list);
}


QueryIter::QueryIter(char *t, int nf, char *w,
	char *va_(field), ...)
{
  Init(t, nf, w);

  va_list	ap;

  va_start(ap, va_(field));
  SetVFields(TRUE, va_(field), ap);
  va_end(ap);
}


QueryIter::~QueryIter()
{
  SafeDelete(table);
  SafeDelete(where);
  SafeDelete(time);
  SafeDelete(sortby);

  busy_iter[iterind]= FALSE;

  if (first_fetch) {	// Nothing fetched, no portal has been opened
    if (!notrans)
      QuelExec("end");
    return;
  }

  if (QuelError)
	return;

  char pn[1000];
  char q[1000];

  sprintf(pn, "qi_%d_%d", Pid, iterind);
  sprintf(q, "close %s", pn);
  QuelExec(q);
  if (!notrans)
    QuelExec("end");
  PQclear(pn);

#if 0
  int nasy= PQnportals(1);
  fprintf(stderr, "Nr portals: %d (ASY: %d)\n",
    PQnportals(0), nasy);
  if (nasy) {
    static char asyspace[10][20];
    static char *asynames[10]= {
	asyspace[0], asyspace[1], asyspace[2], asyspace[4],
	asyspace[5], asyspace[6], asyspace[7], asyspace[8],
	asyspace[9]};

    PQpnames(asynames, 1);
    for (int i= 0; i < nasy; i++)
	fprintf(stderr, "Asy %d: %s\n", asynames[i], i);
  }
#endif
}


void QueryIter::SetWhere(char *w)
{
  manwhere= TRUE;
  SafeDelete(where);
  where= strsave(w);
}


void QueryIter::SetTime(char *t)
{
  SafeDelete(time);
  time= strsave(t);
}


char **QueryIter::Next()
{
  static char	*qa[100];

  DoFetch();

  if (ti == nt)
	return 0;

  for (int i= 0; i < nfields; i++)
	qa[i]= PQgetvalue(p, ti, i);
  qa[i]= 0;

  ++ti;
  return qa;
}


char **QueryIter::Attributes()
{
  static char	*qa[100];

  DoFetch();

  for (int i= 0; i < nfields; i++)
	qa[i]= PQfnameGroup(p, 0, i);
  qa[i]= 0;

  return qa;
}
