]> granicus.if.org Git - postgresql/blob - src/backend/tcop/postgres.c
Bring in a patch from Keith Parks to move the use of European dates
[postgresql] / src / backend / tcop / postgres.c
1 /*-------------------------------------------------------------------------
2  *
3  * postgres.c--
4  *    POSTGRES C Backend Interface
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *    $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.26 1997/01/26 15:30:48 scrappy Exp $
11  *
12  * NOTES
13  *    this is the "main" module of the postgres backend and
14  *    hence the main module of the "traffic cop".
15  *
16  *-------------------------------------------------------------------------
17  */
18
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <signal.h>
23 #include <time.h>
24 #include <setjmp.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <fcntl.h>
28 #include <sys/param.h>          /* for MAXHOSTNAMELEN on most */
29 #ifndef MAXHOSTNAMELEN
30 #include <netdb.h>              /* for MAXHOSTNAMELEN on some */
31 #endif
32 #include <errno.h>
33 #ifdef aix
34 #include <sys/select.h>
35 #endif /* aix */
36
37
38 #include "postgres.h"
39 #include "miscadmin.h"
40 #include "catalog/catname.h"
41 #include "access/xact.h"
42
43 #include "lib/dllist.h"
44
45 #include "parser/catalog_utils.h"
46 #include "parser/parse_query.h"     /* for MakeTimeRange() */
47 #include "commands/async.h"
48 #include "tcop/tcopprot.h"          /* where declarations for this file go */
49 #include "optimizer/planner.h"
50
51 #include "tcop/tcopprot.h"
52 #include "tcop/tcopdebug.h"
53
54 #include "executor/execdebug.h"
55 #include "executor/executor.h"
56 #include "nodes/relation.h"
57
58 #include "optimizer/cost.h"
59 #include "optimizer/planner.h"
60 #if 0
61 #include "optimizer/xfunc.h"
62 #endif
63 #include "optimizer/prep.h"
64 #include "nodes/plannodes.h"
65
66 #include "storage/bufmgr.h"
67 #include "fmgr.h"
68 #include "utils/palloc.h"
69 #include "utils/rel.h"
70
71 #include "nodes/pg_list.h"
72 #include "tcop/dest.h"
73 #include "nodes/memnodes.h"
74 #include "utils/mcxt.h"
75 #include "tcop/pquery.h"
76 #include "tcop/utility.h"
77 #include "tcop/fastpath.h"
78
79 #include "libpq/libpq.h"
80 #include "libpq/pqsignal.h"
81 #include "rewrite/rewriteHandler.h" /* for QueryRewrite() */
82
83 /* ----------------
84  *      global variables
85  * ----------------
86  */
87 static bool     DebugPrintQuery = false;
88 static bool     DebugPrintPlan = false;
89 static bool     DebugPrintParse = false;
90 static bool     DebugPrintRewrittenParsetree = false;
91 /*static bool   EnableRewrite = true; , never changes why have it*/
92 CommandDest whereToSendOutput;
93
94 extern int      lockingOff;
95 extern int      NBuffers;
96
97 int     fsyncOff = 0;
98
99 int     dontExecute = 0;
100 static int      ShowStats;
101 static bool     IsEmptyQuery = false;
102
103 char            relname[80];            /* current relation name */
104
105 #if defined(WIN32) || defined(next)
106 jmp_buf    Warn_restart;
107 #define sigsetjmp(x,y)  setjmp(x)
108 #define siglongjmp longjmp
109 #else
110 sigjmp_buf Warn_restart;
111 #endif /*defined(WIN32) || defined(next) */
112 int InWarn;
113
114 extern int      NBuffers;
115
116 static int      EchoQuery = 0;          /* default don't echo */
117 time_t          tim;
118 char            pg_pathname[256];
119 static int      ShowParserStats;
120 static int      ShowPlannerStats;
121 int             ShowExecutorStats;
122 FILE            *StatFp;
123
124 typedef struct frontend {
125   bool  fn_connected;
126   Port  fn_port;
127   FILE  *fn_Pfin;  /* the input fd */
128   FILE  *fn_Pfout; /* the output fd */
129   bool  fn_done; /* set after the frontend closes its connection */
130 } FrontEnd;
131
132 static Dllist* frontendList;
133
134 /* ----------------
135  *      people who want to use EOF should #define DONTUSENEWLINE in
136  *      tcop/tcopdebug.h
137  * ----------------
138  */
139 #ifndef TCOP_DONTUSENEWLINE
140 int UseNewLine = 1;  /* Use newlines query delimiters (the default) */
141 #else
142 int UseNewLine = 0;  /* Use EOF as query delimiters */
143 #endif /* TCOP_DONTUSENEWLINE */
144
145 /* ----------------
146  *      bushy tree plan flag: if true planner will generate bushy-tree
147  *      plans
148  * ----------------
149  */
150 int BushyPlanFlag = 0; /* default to false -- consider only left-deep trees */
151
152 /*
153 ** Flags for expensive function optimization -- JMH 3/9/92
154 */
155 int XfuncMode = 0;
156
157 /*
158  * ----------------
159  *   Note: _exec_repeat_ defaults to 1 but may be changed
160  *         by a DEBUG command.   If you set this to a large
161  *         number N, run a single query, and then set it
162  *         back to 1 and run N queries, you can get an idea
163  *         of how much time is being spent in the parser and
164  *         planner b/c in the first case this overhead only
165  *         happens once.  -cim 6/9/91
166  * ----------------
167 */
168 int _exec_repeat_ = 1;
169
170 /* ----------------------------------------------------------------
171  *      decls for routines only used in this file
172  * ----------------------------------------------------------------
173  */
174 static char InteractiveBackend(char *inBuf);
175 static char SocketBackend(char *inBuf, bool multiplexedBackend);
176 static char ReadCommand(char *inBuf, bool multiplexedBackend);
177
178
179 /* ----------------------------------------------------------------
180  *      routines to obtain user input
181  * ----------------------------------------------------------------
182  */
183
184 /* ----------------
185  *  InteractiveBackend() is called for user interactive connections
186  *  the string entered by the user is placed in its parameter inBuf.
187  * ----------------
188  */
189
190 static char
191 InteractiveBackend(char *inBuf)
192 {
193     char *stuff = inBuf;                /* current place in input buffer */
194     int c;                              /* character read from getc() */
195     bool end = false;                   /* end-of-input flag */
196     bool backslashSeen = false;         /* have we seen a \ ? */
197     
198     /* ----------------
199      *  display a prompt and obtain input from the user
200      * ----------------
201      */
202     printf("> ");
203     
204     for (;;) {
205         if (UseNewLine) {
206             /* ----------------
207              *  if we are using \n as a delimiter, then read
208              *  characters until the \n.
209              * ----------------
210              */
211             while ( (c = getc(stdin)) != EOF) {
212                 if (c == '\n') {
213                     if (backslashSeen) {
214                         stuff--;
215                         continue;
216                     } else {
217                         /* keep the newline character */
218                         *stuff++ = '\n';
219                         *stuff++ = '\0';
220                         break;
221                     }
222                 } else if (c == '\\')
223                     backslashSeen = true;
224                 else
225                     backslashSeen = false;
226                 
227                 *stuff++ = (char)c;
228             }
229             
230             if (c == EOF)
231                 end = true;
232         } else {
233             /* ----------------
234              *  otherwise read characters until EOF.
235              * ----------------
236              */
237             while ( (c = getc(stdin)) != EOF )
238                 *stuff++ = (char)c;
239             
240             if ( stuff == inBuf )
241                 end = true;
242         }
243         
244         if (end) {
245             if (!Quiet) puts("EOF");
246             IsEmptyQuery = true;
247             exitpg(0);
248         }
249         
250         /* ----------------
251          *  otherwise we have a user query so process it.
252          * ----------------
253          */
254         break;
255     }
256     
257     /* ----------------
258      *  if the query echo flag was given, print the query..
259      * ----------------
260      */
261     if (EchoQuery)
262         printf("query is: %s\n", inBuf);
263     
264     return('Q');
265 }
266
267 /* ----------------
268  *  SocketBackend()     Is called for frontend-backend connections
269  *
270  *  If the input is a query (case 'Q') then the string entered by
271  *  the user is placed in its parameter inBuf.
272  *
273  *  If the input is a fastpath function call (case 'F') then
274  *  the function call is processed in HandleFunctionRequest().
275  *  (now called from PostgresMain())
276  * ----------------
277  */
278
279 static char
280 SocketBackend(char *inBuf, bool multiplexedBackend)
281 {
282     char qtype[2];
283     char result = '\0';
284     
285     /* ----------------
286      *  get input from the frontend
287      * ----------------
288      */
289     (void) strcpy(qtype, "?");
290     if (pq_getnchar(qtype,0,1) == EOF) {
291         /* ------------
292          *  when front-end applications quits/dies
293          * ------------
294          */
295         if (multiplexedBackend) {
296             return 'X';
297         }
298         else
299             exitpg(0);
300     }
301     
302     switch(*qtype) {
303         /* ----------------
304          *  'Q': user entered a query
305          * ----------------
306          */
307     case 'Q':
308         pq_getstr(inBuf, MAX_PARSE_BUFFER);
309         result = 'Q';
310         break;
311         
312         /* ----------------
313          *  'F':  calling user/system functions
314          * ----------------
315          */
316     case 'F':   
317         pq_getstr(inBuf, MAX_PARSE_BUFFER);/* ignore the rest of the line */
318         result = 'F';
319         break;
320         
321         /* ----------------
322          *  'X':  frontend is exiting
323          * ----------------
324          */
325     case 'X':
326         result = 'X';
327         break;
328         
329         /* ----------------
330          *  otherwise we got garbage from the frontend.
331          *
332          *  XXX are we certain that we want to do an elog(FATAL) here?
333          *      -cim 1/24/90
334          * ----------------
335          */
336     default:
337         elog(FATAL, "Socket command type %c unknown\n", *qtype);
338         break;
339     }
340     return result;
341 }
342
343 /* ----------------
344  *      ReadCommand reads a command from either the frontend or
345  *      standard input, places it in inBuf, and returns a char
346  *      representing whether the string is a 'Q'uery or a 'F'astpath
347  *      call.
348  * ----------------
349  */
350 static char
351 ReadCommand(char *inBuf, bool multiplexedBackend)
352 {
353     if (IsUnderPostmaster || multiplexedBackend)
354         return SocketBackend(inBuf, multiplexedBackend);
355     else
356         return InteractiveBackend(inBuf);
357 }
358
359 List *
360 pg_plan(char *query_string,     /* string to execute */
361         Oid *typev,             /* argument types */
362         int nargs,              /* number of arguments */
363         QueryTreeList **queryListP,  /* pointer to the parse trees */
364         CommandDest dest)       /* where results should go */
365 {
366     QueryTreeList *querytree_list;
367     int i;
368     List *plan_list = NIL;
369     Plan *plan;
370     int j;
371     QueryTreeList *new_list; 
372     List *rewritten = NIL;
373     Query* querytree;
374
375     /* ----------------
376      *  (1) parse the request string into a list of parse trees
377      * ----------------
378      */
379     if (ShowParserStats)
380         ResetUsage();
381     
382     querytree_list = parser(query_string, typev, nargs);
383     
384     if (ShowParserStats) {
385         fprintf(stderr, "! Parser Stats:\n");
386         ShowUsage();
387     }
388
389     /* new_list holds the rewritten queries */
390     new_list = (QueryTreeList*)malloc(sizeof(QueryTreeList));
391     new_list->len = querytree_list->len;
392     new_list->qtrees = (Query**)malloc(new_list->len * sizeof(Query*));
393
394     /* ----------------
395      *  (2) rewrite the queries, as necessary     
396      * ----------------
397      */
398     j = 0; /* counter for the new_list, new_list can be longer than
399               old list as a result of rewrites */
400     for (i=0;i<querytree_list->len;i++) {
401         querytree = querytree_list->qtrees[i];
402         
403
404         /* don't rewrite utilites */
405         if (querytree->commandType == CMD_UTILITY) {
406             new_list->qtrees[j++] = querytree;
407             continue;
408         }
409         
410         if ( DebugPrintQuery == true ) {
411             printf("\n---- \tquery is:\n%s\n",query_string);
412             printf("\n");
413             fflush(stdout);
414         }
415         
416         if ( DebugPrintParse == true ) {
417             printf("\n---- \tparser outputs :\n");
418             nodeDisplay(querytree);
419             printf("\n");
420         }
421         
422         /* rewrite queries (retrieve, append, delete, replace) */
423         rewritten = QueryRewrite(querytree);
424         if (rewritten != NULL) {
425           int len, k;
426           len = length(rewritten);
427           if (len == 1)
428             new_list->qtrees[j++] = (Query*)lfirst(rewritten); 
429           else {
430             /* rewritten queries are longer than original query */
431             /* grow the new_list to accommodate */
432             new_list->len += len - 1; /* - 1 because originally we 
433                                          allocated one space for the query */
434             new_list->qtrees = realloc(new_list->qtrees, 
435                                        new_list->len * sizeof(Query*));
436             for (k=0;k<len;k++)
437               new_list->qtrees[j++] = (Query*)nth(k, rewritten);
438           }
439         }
440     }
441     
442     /* we're done with the original lists, free it */
443     free(querytree_list->qtrees);
444     free(querytree_list);
445
446     querytree_list = new_list;
447
448     /* ----------------
449      * Fix time range quals
450      * this _must_ go here, because it must take place after rewrites
451      * ( if they take place ) so that time quals are usable by the executor
452      *
453      * Also, need to frob the range table entries here to plan union
454      * queries for archived relations.
455      * ----------------
456      */
457     for (i=0;i<querytree_list->len;i++) {
458         List *l;
459         List *rt = NULL;
460
461         querytree = querytree_list->qtrees[i];
462
463         /* ----------------
464          *  utilities don't have time ranges
465          * ----------------
466          */
467         if (querytree->commandType == CMD_UTILITY)
468             continue;
469         
470         rt = querytree->rtable;
471         
472         foreach (l, rt) {
473             RangeTblEntry *rte = lfirst(l);
474             TimeRange *timequal = rte->timeRange;
475
476             if (timequal) {
477                 int timecode = (rte->timeRange->endDate == NULL)? 0 : 1;
478
479                 rte->timeQual = makeTimeRange(rte->timeRange->startDate,
480                                               rte->timeRange->endDate,
481                                               timecode);
482             }else {
483                 rte->timeQual = NULL;
484             }
485         }
486         
487         /* check for archived relations */
488         plan_archive(rt);
489     }
490     
491     if (DebugPrintRewrittenParsetree == true) {
492         printf("\n---- \tafter rewriting:\n");
493
494         for (i=0; i<querytree_list->len; i++) {
495             print(querytree_list->qtrees[i]);
496             printf("\n");
497         }
498     }
499     
500     for (i=0; i<querytree_list->len;i++) {
501         querytree = querytree_list->qtrees[i];
502         
503         /*
504          *  For each query that isn't a utility invocation,
505          *  generate a plan.
506          */
507         
508         if (querytree->commandType != CMD_UTILITY) {
509             
510             if (IsAbortedTransactionBlockState()) {
511                 /* ----------------
512                  *   the EndCommand() stuff is to tell the frontend
513                  *   that the command ended. -cim 6/1/90
514                  * ----------------
515                  */
516                 char *tag = "*ABORT STATE*";
517                 EndCommand(tag, dest);
518                 
519                 elog(NOTICE, "(transaction aborted): %s",
520                      "queries ignored until END");
521                 
522                 *queryListP = (QueryTreeList*)NULL;
523                 return (List*)NULL;
524             }
525             
526             if (ShowPlannerStats) ResetUsage();
527             plan = planner(querytree);
528             if (ShowPlannerStats) {
529                 fprintf(stderr, "! Planner Stats:\n");
530                 ShowUsage();
531             }
532             plan_list = lappend(plan_list, plan);
533 #ifdef INDEXSCAN_PATCH
534             /* ----------------
535              *  Print plan if debugging.
536              *  This has been moved here to get debugging output
537              *  also for queries in functions.  DZ - 27-8-1996
538              * ----------------
539              */
540             if ( DebugPrintPlan == true ) {
541                 printf("\n---- \tplan is :\n");
542                 nodeDisplay(plan);
543                 printf("\n");
544             }
545 #endif
546         }
547 #ifdef FUNC_UTIL_PATCH
548         /*
549          * If the command is an utility append a null plan. This is
550          * needed to keep the plan_list aligned with the querytree_list
551          * or the function executor will crash.  DZ - 30-8-1996
552          */
553         else {
554             plan_list = lappend(plan_list, NULL);
555         }
556 #endif
557     }
558     
559     if (queryListP)
560         *queryListP = querytree_list;
561     
562     return (plan_list);
563 }
564
565 /* ----------------------------------------------------------------
566  *      pg_eval()
567  *      
568  *      Takes a querystring, runs the parser/utilities or
569  *      parser/planner/executor over it as necessary
570  *      Begin Transaction Should have been called before this
571  *      and CommitTransaction After this is called
572  *      This is strictly because we do not allow for nested xactions.
573  *
574  *      NON-OBVIOUS-RESTRICTIONS
575  *      this function _MUST_ allocate a new "parsetree" each time, 
576  *      since it may be stored in a named portal and should not 
577  *      change its value.
578  *
579  * ----------------------------------------------------------------
580  */
581
582 void
583 pg_eval(char *query_string, char **argv, Oid *typev, int nargs)
584 {
585     pg_eval_dest(query_string, argv, typev, nargs, whereToSendOutput);
586 }
587
588 void
589 pg_eval_dest(char *query_string, /* string to execute */
590              char **argv,       /* arguments */
591              Oid *typev,        /* argument types */
592              int nargs,         /* number of arguments */
593              CommandDest dest)  /* where results should go */
594 {
595     List *plan_list; 
596     Plan *plan;
597     Query *querytree;
598     int i,j;
599     QueryTreeList *querytree_list;
600     
601     /* plan the queries */
602     plan_list = pg_plan(query_string, typev, nargs, &querytree_list, dest);
603     
604     /* pg_plan could have failed */
605     if (querytree_list == NULL)
606         return;
607
608     for (i=0;i<querytree_list->len;i++) {
609         querytree = querytree_list->qtrees[i];
610         
611 #ifdef FUNC_UTIL_PATCH
612         /*
613          * Advance on the plan_list in every case.  Now the plan_list
614          * has the same length of the querytree_list.  DZ - 30-8-1996
615          */
616         plan = (Plan *) lfirst(plan_list);
617         plan_list = lnext(plan_list);
618 #endif
619         if (querytree->commandType == CMD_UTILITY) {
620             /* ----------------
621              *   process utility functions (create, destroy, etc..)
622              *
623              *   Note: we do not check for the transaction aborted state
624              *   because that is done in ProcessUtility.
625              * ----------------
626              */
627             if (! Quiet) {
628                 time(&tim);
629                 printf("\tProcessUtility() at %s\n", ctime(&tim));
630             }
631             
632             ProcessUtility(querytree->utilityStmt, dest);
633             
634         } else {
635 #ifndef FUNC_UTIL_PATCH
636             /*
637              * Moved before the if.  DZ - 30-8-1996
638              */
639             plan = (Plan *) lfirst(plan_list);
640             plan_list = lnext(plan_list);
641 #endif
642             
643 #ifdef INDEXSCAN_PATCH
644             /*
645              *  Print moved in pg_plan.  DZ - 27-8-1996
646              */
647 #else
648             /* ----------------
649              *  print plan if debugging
650              * ----------------
651              */
652             if ( DebugPrintPlan == true ) {
653                 printf("\n---- plan is :\n");
654                 nodeDisplay(plan);
655                 printf("\n");
656             }
657 #endif
658             
659             /* ----------------
660              *   execute the plan
661              *
662              */
663             if (ShowExecutorStats)
664                 ResetUsage();
665             
666             for (j = 0; j < _exec_repeat_; j++) {
667                 if (! Quiet) {
668                     time(&tim);
669                     printf("\tProcessQuery() at %s\n", ctime(&tim));
670                 }
671                 ProcessQuery(querytree, plan, argv, typev, nargs, dest);
672             }
673             
674             if (ShowExecutorStats) {
675                 fprintf(stderr, "! Executor Stats:\n");
676                 ShowUsage();
677             }
678         }
679         /*
680          *  In a query block, we want to increment the command counter
681          *  between queries so that the effects of early queries are
682          *  visible to subsequent ones.
683          */
684         
685         if (querytree_list)
686             CommandCounterIncrement();
687     }
688
689     free(querytree_list->qtrees);
690     free(querytree_list);
691 }
692
693 /* --------------------------------
694  *      signal handler routines used in PostgresMain()
695  *
696  *      handle_warn() is used to catch kill(getpid(),1) which
697  *      occurs when elog(WARN) is called.
698  *
699  *      quickdie() occurs when signalled by the postmaster, some backend
700  *      has bought the farm we need to stop what we're doing and exit.
701  *
702  *      die() preforms an orderly cleanup via ExitPostgres()
703  * --------------------------------
704  */
705
706 void
707 handle_warn(SIGNAL_ARGS)
708 {
709     siglongjmp(Warn_restart, 1);
710 }
711
712 void
713 quickdie(SIGNAL_ARGS)
714 {
715     elog(NOTICE, "I have been signalled by the postmaster.");
716     elog(NOTICE, "Some backend process has died unexpectedly and possibly");
717     elog(NOTICE, "corrupted shared memory.  The current transaction was");
718     elog(NOTICE, "aborted, and I am going to exit.  Please resend the");
719     elog(NOTICE, "last query. -- The postgres backend");
720     
721     /*
722      *  DO NOT ExitPostgres(0) -- we're here because shared memory may be
723      *  corrupted, so we don't want to flush any shared state to stable
724      *  storage.  Just nail the windows shut and get out of town.
725      */
726     
727     exit (0);
728 }
729
730 void
731 die(SIGNAL_ARGS)
732 {
733     ExitPostgres(0);
734 }
735
736 /* signal handler for floating point exception */
737 static void
738 FloatExceptionHandler(SIGNAL_ARGS)
739 {
740    elog(WARN, "floating point exception! the last floating point operation eit\
741 her exceeded legal ranges or was a divide by zero");
742 }
743
744
745 static void usage(char* progname)
746 {
747     fprintf(stderr, 
748             "Usage: %s [-B nbufs] [-d lvl] ] [-f plantype] \t[-m portno] [\t -o filename]\n",
749             progname);
750     fprintf(stderr,"\t[-P portno] [-t tracetype] [-x opttype] [-bCEiLFNopQSs] [dbname]\n");
751     fprintf(stderr, "    b: consider bushy plan trees during optimization\n");
752     fprintf(stderr, "    B: set number of buffers in buffer pool\n");
753     fprintf(stderr, "    C: supress version info\n");
754     fprintf(stderr, "    d: set debug level\n");
755     fprintf(stderr, "    E: echo query before execution\n");
756     fprintf(stderr, "    F: turn off fsync\n");
757     fprintf(stderr, "    f: forbid plantype generation\n");
758     fprintf(stderr, "    i: don't execute the query, just show the plan tree\n");
759     fprintf(stderr, "    L: turn off locking\n");
760     fprintf(stderr, "    m: set up a listening backend at portno to support multiple front-ends\n");
761     fprintf(stderr, "    M: start as postmaster\n");
762     fprintf(stderr, "    N: don't use newline as query delimiter\n");
763     fprintf(stderr, "    o: send stdout and stderr to given filename \n");
764     fprintf(stderr, "    p: backend started by postmaster\n");
765     fprintf(stderr, "    P: set port file descriptor\n");
766     fprintf(stderr, "    Q: suppress informational messages\n");
767     fprintf(stderr, "    S: assume stable main memory\n");
768     fprintf(stderr, "    s: show stats after each query\n");
769     fprintf(stderr, "    t: trace component execution times\n");
770     fprintf(stderr, "    T: execute all possible plans for each query\n");
771     fprintf(stderr, "    x: control expensive function optimization\n");
772 }
773
774 /* ----------------------------------------------------------------
775  *      PostgresMain
776  *        postgres main loop
777  *      all backends, interactive or otherwise start here
778  * ----------------------------------------------------------------
779  */
780 int
781 PostgresMain(int argc, char *argv[])
782 {
783     int    flagC;
784     int    flagQ;
785     int    flagS;
786     int    flagE;
787     int    flagEu;
788     int    flag;
789     
790     char   *DBName = NULL; 
791     int    errs = 0;
792     
793     char   firstchar;
794     char   parser_input[MAX_PARSE_BUFFER];
795     char *userName;
796     
797     bool   multiplexedBackend;
798     char*  hostName;                /* the host name of the backend server */
799     char   hostbuf[MAXHOSTNAMELEN];
800     int    serverSock;
801     int    serverPortnum = 0;
802     int    nSelected; /* number of descriptors ready from select(); */
803     int    maxFd = 0; /* max file descriptor + 1 */
804     fd_set rmask, basemask;
805     FrontEnd *newFE, *currentFE = NULL;
806     int    numFE = 0; /* keep track of number of active frontends */
807     Port   *newPort;
808     int    newFd;
809     Dlelem *curr;
810     int    status;
811
812 #ifdef WIN32
813     WSADATA WSAData;
814 #endif /* WIN32 */
815
816     extern int    optind;
817     extern char   *optarg;
818     extern short  DebugLvl;
819     
820     /* ----------------
821      *  register signal handlers.
822      * ----------------
823      */
824     pqsignal(SIGINT, die);
825
826 #ifndef WIN32
827     pqsignal(SIGHUP, die);
828     pqsignal(SIGTERM, die);
829     pqsignal(SIGPIPE, die);
830     pqsignal(SIGUSR1, quickdie);
831     pqsignal(SIGUSR2, Async_NotifyHandler);
832     pqsignal(SIGFPE, FloatExceptionHandler);
833 #endif /* WIN32 */
834     
835     /* --------------------
836      *  initialize globals 
837      * -------------------
838      */
839     
840     MasterPid = getpid();
841
842     /* ----------------
843      *  parse command line arguments
844      * ----------------
845      */
846     flagC = flagQ = flagS = flagE = flagEu = ShowStats = 0;
847     ShowParserStats = ShowPlannerStats = ShowExecutorStats = 0;
848     
849     /* get hostname is either the environment variable PGHOST
850        or 'localhost' */
851     if (!(hostName = getenv("PGHOST"))) {
852         if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
853             (void) strcpy(hostbuf, "localhost");
854         hostName = hostbuf;
855     }
856
857     DataDir = getenv("PGDATA");   /* default */
858     multiplexedBackend = false;   /* default */
859
860     while ((flag = getopt(argc, argv, "B:bCD:d:Eef:iLm:MNo:P:pQSst:x:F")) 
861            != EOF)
862         switch (flag) {
863             
864         case 'b':
865             /* ----------------
866              *  set BushyPlanFlag to true.
867              * ----------------
868              */
869             BushyPlanFlag = 1;
870             break;
871         case 'B':
872             /* ----------------
873              *  specify the size of buffer pool
874              * ----------------
875              */
876             NBuffers = atoi(optarg);
877             break;
878             
879         case 'C':
880             /* ----------------
881              *  don't print version string (don't know why this is 'C' --mao)
882              * ----------------
883              */
884             flagC = 1;
885             break;
886             
887         case 'D':   /* PGDATA directory */
888             DataDir = optarg;
889             
890         case 'd':   /* debug level */
891             flagQ = 0;
892             DebugLvl = (short)atoi(optarg);
893             if (DebugLvl > 1)
894                 DebugPrintQuery = true;
895             if (DebugLvl > 2)
896             {
897                 DebugPrintParse = true;
898                 DebugPrintPlan = true;
899                 DebugPrintRewrittenParsetree = true;
900             }
901             break;
902             
903         case 'E':
904             /* ----------------
905              *  E - echo the query the user entered
906              * ----------------
907              */
908             flagE = 1;
909             break;
910             
911         case 'e':
912             /* --------------------------
913              * Use european date formats.
914              * --------------------------
915              */
916             flagEu = 1;
917             break;
918
919         case 'F':
920             /* --------------------
921              *  turn off fsync
922              * --------------------
923              */
924             fsyncOff = 1;
925             break;
926
927         case 'f':
928             /* -----------------
929              *    f - forbid generation of certain plans
930              * -----------------
931              */
932             switch (optarg[0]) {
933             case 's': /* seqscan */
934                 _enable_seqscan_ = false;
935                 break;
936             case 'i': /* indexscan */
937                 _enable_indexscan_ = false;
938                 break;
939             case 'n': /* nestloop */
940                 _enable_nestloop_ = false;
941                 break;
942             case 'm': /* mergejoin */
943                 _enable_mergesort_ = false;
944                 break;
945             case 'h': /* hashjoin */
946                 _enable_hashjoin_ = false;
947                 break;
948             default:
949                 errs++;
950             }
951             break;
952
953         case 'i':
954             dontExecute = 1;
955             break;
956             
957         case 'L':
958             /* --------------------
959              *  turn off locking
960              * --------------------
961              */
962             lockingOff = 1;
963             break;
964             
965         case 'm':
966             /* start up a listening backend that can respond to 
967                multiple front-ends.  (Note:  all the front-end connections
968                are still connected to a single-threaded backend.  Requests
969                are FCFS.  Everything is in one transaction 
970                */
971             multiplexedBackend = true;
972             serverPortnum = atoi(optarg);
973 #ifdef WIN32
974            /* There was no postmaster started so the shared memory
975            ** for the shared memory table hasn't been allocated so
976            ** do it now.
977            */
978            _nt_init();
979 #endif /* WIN32 */
980             break;
981         case 'M':
982             exit(PostmasterMain(argc, argv));
983             break;
984         case 'N':
985             /* ----------------
986              *  N - Don't use newline as a query delimiter
987              * ----------------
988              */
989             UseNewLine = 0;
990             break;
991             
992         case 'o':
993             /* ----------------
994              *  o - send output (stdout and stderr) to the given file
995              * ----------------
996              */
997             (void) strncpy(OutputFileName, optarg, MAXPGPATH);
998             break;
999             
1000         case 'p':       /* started by postmaster */
1001             /* ----------------
1002              *  p - special flag passed if backend was forked
1003              *      by a postmaster.
1004              * ----------------
1005              */
1006             IsUnderPostmaster = true;
1007             break;
1008             
1009         case 'P':
1010             /* ----------------
1011              *  P - Use the passed file descriptor number as the port
1012              *    on which to communicate with the user.  This is ONLY
1013              *    useful for debugging when fired up by the postmaster.
1014              * ----------------
1015              */
1016             Portfd = atoi(optarg);
1017             break;
1018             
1019         case 'Q':
1020             /* ----------------
1021              *  Q - set Quiet mode (reduce debugging output)
1022              * ----------------
1023              */
1024             flagQ = 1;
1025             break;
1026             
1027         case 'S':
1028             /* ----------------
1029              *  S - assume stable main memory
1030              *      (don't flush all pages at end transaction)
1031              * ----------------
1032              */
1033             flagS = 1;
1034             SetTransactionFlushEnabled(false);
1035             break;
1036             
1037         case 's':
1038             /* ----------------
1039              *    s - report usage statistics (timings) after each query
1040              * ----------------
1041              */
1042             ShowStats = 1;
1043             StatFp = stderr;
1044             break;
1045             
1046         case 't':
1047             /* ----------------
1048              *  tell postgres to report usage statistics (timings) for
1049              *  each query
1050              *
1051              *  -tpa[rser] = print stats for parser time of each query
1052              *  -tpl[anner] = print stats for planner time of each query
1053              *  -te[xecutor] = print stats for executor time of each query
1054              *  caution: -s can not be used together with -t.
1055              * ----------------
1056              */
1057             StatFp = stderr;
1058             switch (optarg[0]) {
1059             case 'p':  if (optarg[1] == 'a')
1060                 ShowParserStats = 1;
1061             else if (optarg[1] == 'l')
1062                 ShowPlannerStats = 1;
1063             else
1064                 errs++;
1065                 break;
1066             case 'e':  ShowExecutorStats = 1;   break;
1067             default:   errs++; break;
1068             } 
1069             break;
1070             
1071         case 'x':
1072 #if 0 /* planner/xfunc.h */
1073             /* control joey hellerstein's expensive function optimization */
1074             if (XfuncMode != 0)
1075                 {
1076                     fprintf(stderr, "only one -x flag is allowed\n");
1077                     errs++;
1078                     break;
1079                 }
1080             if (strcmp(optarg, "off") == 0)
1081                 XfuncMode = XFUNC_OFF;
1082             else if (strcmp(optarg, "nor") == 0)
1083                 XfuncMode = XFUNC_NOR;
1084             else if (strcmp(optarg, "nopull") == 0)
1085                 XfuncMode = XFUNC_NOPULL;
1086             else if (strcmp(optarg, "nopm") == 0)
1087                 XfuncMode = XFUNC_NOPM;
1088             else if (strcmp(optarg, "pullall") == 0)
1089                 XfuncMode = XFUNC_PULLALL;
1090             else if (strcmp(optarg, "wait") == 0)
1091                 XfuncMode = XFUNC_WAIT;
1092             else {
1093                 fprintf(stderr, "use -x {off,nor,nopull,nopm,pullall,wait}\n");
1094                 errs++;
1095             }
1096 #endif
1097             break;
1098             
1099         default:
1100             /* ----------------
1101              *  default: bad command line option
1102              * ----------------
1103              */
1104             errs++;
1105         }
1106     
1107     /* ----------------
1108      *  get user name and pathname and check command line validity
1109      * ----------------
1110      */
1111     SetPgUserName();
1112     userName = GetPgUserName();
1113     
1114     if (FindBackend(pg_pathname, argv[0]) < 0)
1115         elog(FATAL, "%s: could not locate executable, bailing out...",
1116              argv[0]);
1117     
1118     if (errs || argc - optind > 1) {
1119         usage (argv[0]);
1120         exitpg(1);
1121     } else if (argc - optind == 1) {
1122         DBName = argv[optind];
1123     } else if ((DBName = userName) == NULL) {
1124         fprintf(stderr, "%s: USER undefined and no database specified\n",
1125                 argv[0]);
1126         exitpg(1);
1127     }
1128     
1129     if (ShowStats && 
1130         (ShowParserStats || ShowPlannerStats || ShowExecutorStats)) {
1131         fprintf(stderr, "-s can not be used together with -t.\n");
1132         exitpg(1);
1133     }
1134
1135     if (!DataDir) {
1136         fprintf(stderr, "%s does not know where to find the database system "
1137                 "data.  You must specify the directory that contains the "
1138                 "database system either by specifying the -D invocation "
1139                 "option or by setting the PGDATA environment variable.\n\n",
1140                 argv[0]);
1141         exitpg(1);
1142     }
1143     
1144     Noversion = flagC;
1145     Quiet = flagQ;
1146     EchoQuery = flagE;
1147     EuroDates = flagEu;
1148     
1149     /* ----------------
1150      *  print flags
1151      * ----------------
1152      */
1153     if (! Quiet) {
1154         puts("\t---debug info---");
1155         printf("\tQuiet =        %c\n", Quiet     ? 't' : 'f');
1156         printf("\tNoversion =    %c\n", Noversion ? 't' : 'f');
1157         printf("\tstable    =    %c\n", flagS     ? 't' : 'f');
1158         printf("\ttimings   =    %c\n", ShowStats ? 't' : 'f');
1159         printf("\tdates     =    %s\n", EuroDates ? "European" : "Normal");
1160         printf("\tbufsize   =    %d\n", NBuffers);
1161         
1162         printf("\tquery echo =   %c\n", EchoQuery ? 't' : 'f');
1163         printf("\tmultiplexed backend? =  %c\n", multiplexedBackend ? 't' : 'f');
1164         printf("\tDatabaseName = [%s]\n", DBName);
1165         puts("\t----------------\n");
1166     }
1167     
1168     /* ----------------
1169      *  initialize portal file descriptors
1170      * ----------------
1171      */
1172     if (IsUnderPostmaster == true) {
1173         if (Portfd < 0) {
1174             fprintf(stderr,
1175                     "Postmaster flag set: no port number specified, use /dev/null\n");
1176             Portfd = open(NULL_DEV, O_RDWR, 0666);
1177         }
1178         pq_init(Portfd);
1179     }
1180
1181 #ifdef WIN32
1182     if ((status = WSAStartup(MAKEWORD(1,1), &WSAData)) == 0)
1183         (void) printf("%s\nInitializing WinSock: %s\n", WSAData.szDescription, WSAData.szSystemStatus);
1184     else {
1185         fprintf(stderr, "Error initializing WinSock: %d is the err", status);
1186         exit(1);
1187     }
1188 #endif /* WIN32 */
1189     
1190     if (multiplexedBackend) {
1191       if (serverPortnum == 0 ||
1192           StreamServerPort(hostName, serverPortnum, &serverSock) != STATUS_OK)
1193         {
1194           fprintf(stderr, "Postgres: cannot create stream port %d\n", serverPortnum);
1195           exit(1);
1196         }
1197 /*
1198 {
1199     char buf[100];
1200     sprintf(buf, "stream port %d created, socket = %d\n", serverPortnum, serverSock);
1201     puts(buf);
1202 }
1203 */
1204       FD_ZERO(&rmask);
1205       FD_ZERO(&basemask);
1206       FD_SET(serverSock, &basemask);  
1207
1208       frontendList = DLNewList();
1209       /* add the original FrontEnd to the list */
1210       if (IsUnderPostmaster == true) {
1211         FrontEnd *fe = malloc(sizeof(FrontEnd));
1212
1213         FD_SET(Portfd, &basemask);
1214         maxFd = Max(serverSock,Portfd) + 1;
1215
1216         fe->fn_connected = true;
1217         fe->fn_Pfin = Pfin;
1218         fe->fn_Pfout = Pfout;
1219         fe->fn_done = false;
1220         (fe->fn_port).sock = Portfd;
1221         DLAddHead(frontendList, DLNewElem(fe));
1222         numFE++;
1223       } else {
1224           numFE = 1;
1225           maxFd = serverSock + 1;
1226       }
1227     }
1228
1229     if (IsUnderPostmaster || multiplexedBackend)
1230         whereToSendOutput = Remote;
1231     else 
1232         whereToSendOutput = Debug;
1233     
1234     SetProcessingMode(InitProcessing);
1235     
1236     /* initialize */
1237     if (! Quiet) {
1238         puts("\tInitPostgres()..");
1239     }
1240  
1241 #if WIN32
1242      _nt_attach();
1243 #endif /* WIN32 */
1244
1245     InitPostgres(DBName);
1246
1247     /* ----------------
1248      *  if an exception is encountered, processing resumes here
1249      *  so we abort the current transaction and start a new one.
1250      *  This must be done after we initialize the slave backends
1251      *  so that the slaves signal the master to abort the transaction
1252      *  rather than calling AbortCurrentTransaction() themselves.
1253      *
1254      *  Note:  elog(WARN) causes a kill(getpid(),1) to occur sending
1255      *         us back here.
1256      * ----------------
1257      */
1258
1259 #ifndef WIN32    
1260     pqsignal(SIGHUP, handle_warn);
1261
1262     if (sigsetjmp(Warn_restart, 1) != 0) {
1263 #else
1264     if (setjmp(Warn_restart) != 0) {
1265 #endif /* WIN32 */
1266         InWarn = 1;
1267
1268         time(&tim);
1269         
1270         if (! Quiet)
1271             printf("\tAbortCurrentTransaction() at %s\n", ctime(&tim));
1272
1273         memset(parser_input, 0, MAX_PARSE_BUFFER);
1274         
1275         AbortCurrentTransaction();
1276     }
1277     InWarn = 0;
1278     
1279     /* ----------------
1280      *  POSTGRES main processing loop begins here
1281      * ----------------
1282      */
1283     if (IsUnderPostmaster == false) {
1284         puts("\nPOSTGRES backend interactive interface");
1285         puts("$Revision: 1.26 $ $Date: 1997/01/26 15:30:48 $");
1286     }
1287     
1288     /* ----------------
1289      * if stable main memory is assumed (-S flag is set), it is necessary
1290      * to flush all dirty shared buffers before exit
1291      * plai 8/7/90
1292      * ----------------
1293      */
1294     if (!TransactionFlushEnabled())
1295         on_exitpg(FlushBufferPool, (caddr_t) 0);
1296     
1297     for (;;) {
1298       
1299       if (multiplexedBackend) {
1300         if (numFE == 0) 
1301           break;
1302
1303         memmove((char *) &rmask, (char *) &basemask, sizeof(fd_set));
1304         nSelected = select(maxFd, &rmask,0,0,0);
1305
1306         if (nSelected < 0) {
1307
1308           if (errno == EINTR) continue;
1309           fprintf(stderr,"postgres: multiplexed backend select failed\n");
1310           exitpg(1);
1311         }
1312         if (FD_ISSET(serverSock, &rmask)) {
1313         /* new connection pending on our well-known port's socket */
1314           newFE = (FrontEnd*) malloc (sizeof(FrontEnd));
1315           memset(newFE, 0, sizeof(FrontEnd));
1316           newFE->fn_connected = false;
1317           newFE->fn_done = false;
1318           newPort = &(newFE->fn_port);
1319           if (StreamConnection(serverSock,newPort) != STATUS_OK) {
1320             StreamClose(newPort->sock);
1321             newFd = -1;
1322           }
1323           else {
1324             DLAddHead(frontendList, DLNewElem(newFE));
1325             numFE++;
1326             newFd = newPort->sock;
1327             if (newFd >= maxFd) maxFd = newFd + 1;
1328             FD_SET(newFd, &rmask);
1329             FD_SET(newFd, &basemask);
1330             --nSelected;
1331             FD_CLR(serverSock, &rmask);
1332           }
1333           continue;
1334         } /* if FD_ISSET(serverSock) */
1335
1336         /* if we get here, it means that the serverSocket was not the one
1337            selected.  Instead, one of the front ends was selected.
1338            find which one */
1339         curr = DLGetHead(frontendList);
1340         while (curr) {
1341           FrontEnd *fe = (FrontEnd*)DLE_VAL(curr);
1342           Port *port = &(fe->fn_port);
1343
1344           /* this is lifted from postmaster.c */
1345           if (FD_ISSET(port->sock, &rmask)) {
1346             if (fe->fn_connected == false) {
1347                 /* we have a message from a new frontEnd */
1348                 status = PacketReceive(port, &port->buf, NON_BLOCKING);
1349                 if (status == STATUS_OK) {
1350                   fe->fn_connected = true;
1351                   pq_init(port->sock);
1352                   fe->fn_Pfin = Pfin;
1353                   fe->fn_Pfout = Pfout;
1354                 }
1355                 else
1356                   fprintf(stderr,"Multiplexed backend: error in reading packets from %d\n", port->sock);
1357                }
1358             else  /* we have a query from an existing,  active FrontEnd */
1359               {
1360                 Pfin = fe->fn_Pfin;
1361                 Pfout = fe->fn_Pfout;
1362                 currentFE = fe;
1363               }
1364             if (fe->fn_done)
1365                 {
1366                     Dlelem *c = curr;
1367                     curr = DLGetSucc(curr);
1368                     DLRemove(c);
1369                 }
1370              break;
1371               }
1372           else
1373             curr = DLGetSucc(curr);
1374         }
1375     }
1376         /* ----------------
1377          *   (1) read a command. 
1378          * ----------------
1379          */
1380         memset(parser_input, 0, MAX_PARSE_BUFFER);
1381
1382         firstchar = ReadCommand(parser_input, multiplexedBackend);
1383         /* process the command */
1384         switch (firstchar) {
1385             /* ----------------
1386              *  'F' indicates a fastpath call.
1387              *      XXX HandleFunctionRequest
1388              * ----------------
1389              */
1390         case 'F':
1391             IsEmptyQuery = false;
1392             
1393             /* start an xact for this function invocation */
1394             if (! Quiet) {
1395                 time(&tim);
1396                 printf("\tStartTransactionCommand() at %s\n", ctime(&tim));
1397             }
1398             
1399             StartTransactionCommand();
1400             HandleFunctionRequest();
1401             break;
1402             
1403             /* ----------------
1404              *  'Q' indicates a user query
1405              * ----------------
1406              */
1407         case 'Q':
1408             fflush(stdout);
1409             
1410             if ( strspn(parser_input," \t\n") == strlen(parser_input)) {
1411                 /* ----------------
1412                  *  if there is nothing in the input buffer, don't bother
1413                  *  trying to parse and execute anything..
1414                  * ----------------
1415                  */
1416                 IsEmptyQuery = true;
1417             } else {
1418                 /* ----------------
1419                  *  otherwise, process the input string.
1420                  * ----------------
1421                  */
1422                 IsEmptyQuery = false;
1423                 if (ShowStats)
1424                     ResetUsage();
1425                 
1426                 /* start an xact for this query */
1427                 if (! Quiet) {
1428                     time(&tim);
1429                     printf("\tStartTransactionCommand() at %s\n", ctime(&tim));
1430                 }
1431                 StartTransactionCommand();
1432                 
1433                 pg_eval(parser_input, (char **) NULL, (Oid *) NULL, 0);
1434                 
1435                 if (ShowStats)
1436                     ShowUsage();
1437             }
1438             break;
1439             
1440             /* ----------------
1441              *  'X' means that the frontend is closing down the socket
1442              * ----------------
1443              */
1444         case 'X':
1445             IsEmptyQuery = true;
1446             if (multiplexedBackend) {
1447                FD_CLR(currentFE->fn_port.sock, &basemask);
1448                currentFE->fn_done = true;
1449                numFE--;
1450              }
1451             pq_close();
1452             break;
1453             
1454         default:
1455             elog(WARN,"unknown frontend message was recieved");
1456         }
1457         
1458         /* ----------------
1459          *   (3) commit the current transaction
1460          *
1461          *   Note: if we had an empty input buffer, then we didn't
1462          *   call pg_eval, so we don't bother to commit this transaction.
1463          * ----------------
1464          */
1465         if (! IsEmptyQuery) {
1466             if (! Quiet) {
1467                 time(&tim);
1468                 printf("\tCommitTransactionCommand() at %s\n", ctime(&tim));
1469             }
1470             CommitTransactionCommand();
1471             
1472         } else {
1473             if (IsUnderPostmaster || multiplexedBackend)
1474                 NullCommand(Remote);
1475         }
1476         
1477 } /* infinite for-loop */
1478   exitpg(0);
1479   return 1;
1480 }
1481
1482 #ifndef WIN32
1483 #ifdef NEED_RUSAGE
1484 #include "rusagestub.h"
1485 #else /* NEED_RUSAGE */
1486 #include <sys/resource.h>
1487 #endif /* NEED_RUSAGE */
1488
1489 struct rusage Save_r;
1490 struct timeval Save_t;
1491
1492 void
1493 ResetUsage(void)
1494 {
1495     struct timezone tz;
1496     getrusage(RUSAGE_SELF, &Save_r);
1497     gettimeofday(&Save_t, &tz);
1498     ResetBufferUsage();
1499 /*    ResetTupleCount(); */
1500 }
1501
1502 void
1503 ShowUsage(void)
1504 {
1505     struct timeval user, sys;
1506     struct timeval elapse_t;
1507     struct timezone tz;
1508     struct rusage r;
1509     
1510     getrusage(RUSAGE_SELF, &r);
1511     gettimeofday(&elapse_t, &tz);
1512     memmove((char *)&user, (char *)&r.ru_utime, sizeof(user)); 
1513     memmove((char *)&sys, (char *)&r.ru_stime,sizeof(sys)); 
1514     if (elapse_t.tv_usec < Save_t.tv_usec) {
1515         elapse_t.tv_sec--;
1516         elapse_t.tv_usec += 1000000;
1517     }
1518     if (r.ru_utime.tv_usec < Save_r.ru_utime.tv_usec) {
1519         r.ru_utime.tv_sec--;
1520         r.ru_utime.tv_usec += 1000000;
1521     }
1522     if (r.ru_stime.tv_usec < Save_r.ru_stime.tv_usec) {
1523         r.ru_stime.tv_sec--;
1524         r.ru_stime.tv_usec += 1000000;
1525     }
1526     
1527     /*
1528      *  the only stats we don't show here are for memory usage -- i can't
1529      *  figure out how to interpret the relevant fields in the rusage
1530      *  struct, and they change names across o/s platforms, anyway.
1531      *  if you can figure out what the entries mean, you can somehow
1532      *  extract resident set size, shared text size, and unshared data
1533      *  and stack sizes.
1534      */
1535     
1536     fprintf(StatFp, "! system usage stats:\n");
1537     fprintf(StatFp, 
1538             "!\t%ld.%06ld elapsed %ld.%06ld user %ld.%06ld system sec\n",
1539             (long int) elapse_t.tv_sec - Save_t.tv_sec,
1540             (long int) elapse_t.tv_usec - Save_t.tv_usec,
1541             (long int) r.ru_utime.tv_sec - Save_r.ru_utime.tv_sec,
1542             (long int) r.ru_utime.tv_usec - Save_r.ru_utime.tv_usec,
1543             (long int) r.ru_stime.tv_sec - Save_r.ru_stime.tv_sec,
1544             (long int) r.ru_stime.tv_usec - Save_r.ru_stime.tv_usec);
1545     fprintf(StatFp,
1546             "!\t[%ld.%06ld user %ld.%06ld sys total]\n",
1547             (long int) user.tv_sec, 
1548             (long int) user.tv_usec, 
1549             (long int) sys.tv_sec, 
1550             (long int) sys.tv_usec);
1551 #ifndef NEED_RUSAGE
1552     fprintf(StatFp, 
1553             "!\t%ld/%ld [%ld/%ld] filesystem blocks in/out\n",
1554             r.ru_inblock - Save_r.ru_inblock,
1555             /* they only drink coffee at dec */
1556             r.ru_oublock - Save_r.ru_oublock,
1557             r.ru_inblock, r.ru_oublock);
1558     fprintf(StatFp, 
1559             "!\t%ld/%ld [%ld/%ld] page faults/reclaims, %ld [%ld] swaps\n",
1560             r.ru_majflt - Save_r.ru_majflt,
1561             r.ru_minflt - Save_r.ru_minflt,
1562             r.ru_majflt, r.ru_minflt,
1563             r.ru_nswap - Save_r.ru_nswap,
1564             r.ru_nswap);
1565     fprintf(StatFp, 
1566             "!\t%ld [%ld] signals rcvd, %ld/%ld [%ld/%ld] messages rcvd/sent\n",
1567             r.ru_nsignals - Save_r.ru_nsignals,
1568             r.ru_nsignals,
1569             r.ru_msgrcv - Save_r.ru_msgrcv,
1570             r.ru_msgsnd - Save_r.ru_msgsnd,
1571             r.ru_msgrcv, r.ru_msgsnd);
1572     fprintf(StatFp, 
1573             "!\t%ld/%ld [%ld/%ld] voluntary/involuntary context switches\n",
1574             r.ru_nvcsw - Save_r.ru_nvcsw,
1575             r.ru_nivcsw - Save_r.ru_nivcsw,
1576             r.ru_nvcsw, r.ru_nivcsw);
1577 #endif /* NEED_RUSAGE */
1578     fprintf(StatFp, "! postgres usage stats:\n");
1579     PrintBufferUsage(StatFp);
1580 /*     DisplayTupleCount(StatFp); */
1581 }
1582 #else
1583 void
1584 ShowUsage()
1585 {}
1586
1587 void
1588 ResetUsage()
1589 {}
1590 #endif /* WIN32 */