]> granicus.if.org Git - postgresql/blob - src/backend/tcop/postgres.c
09ee96fca5f3b594a432e6e4516f89d14868cf69
[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.25 1997/01/14 08:05:26 bryanh 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    flag;
788     
789     char   *DBName = NULL; 
790     int    errs = 0;
791     
792     char   firstchar;
793     char   parser_input[MAX_PARSE_BUFFER];
794     char *userName;
795     
796     bool   multiplexedBackend;
797     char*  hostName;                /* the host name of the backend server */
798     char   hostbuf[MAXHOSTNAMELEN];
799     int    serverSock;
800     int    serverPortnum = 0;
801     int    nSelected; /* number of descriptors ready from select(); */
802     int    maxFd = 0; /* max file descriptor + 1 */
803     fd_set rmask, basemask;
804     FrontEnd *newFE, *currentFE = NULL;
805     int    numFE = 0; /* keep track of number of active frontends */
806     Port   *newPort;
807     int    newFd;
808     Dlelem *curr;
809     int    status;
810
811 #ifdef WIN32
812     WSADATA WSAData;
813 #endif /* WIN32 */
814
815     extern int    optind;
816     extern char   *optarg;
817     extern short  DebugLvl;
818     
819     /* ----------------
820      *  register signal handlers.
821      * ----------------
822      */
823     pqsignal(SIGINT, die);
824
825 #ifndef WIN32
826     pqsignal(SIGHUP, die);
827     pqsignal(SIGTERM, die);
828     pqsignal(SIGPIPE, die);
829     pqsignal(SIGUSR1, quickdie);
830     pqsignal(SIGUSR2, Async_NotifyHandler);
831     pqsignal(SIGFPE, FloatExceptionHandler);
832 #endif /* WIN32 */
833     
834     /* --------------------
835      *  initialize globals 
836      * -------------------
837      */
838     
839     MasterPid = getpid();
840
841     /* ----------------
842      *  parse command line arguments
843      * ----------------
844      */
845     flagC = flagQ = flagS = flagE = ShowStats = 0;
846     ShowParserStats = ShowPlannerStats = ShowExecutorStats = 0;
847     
848     /* get hostname is either the environment variable PGHOST
849        or 'localhost' */
850     if (!(hostName = getenv("PGHOST"))) {
851         if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
852             (void) strcpy(hostbuf, "localhost");
853         hostName = hostbuf;
854     }
855
856     DataDir = getenv("PGDATA");   /* default */
857     multiplexedBackend = false;   /* default */
858
859     while ((flag = getopt(argc, argv, "B:bCD:d:Ef:iLm:MNo:P:pQSst:x:F")) 
860            != EOF)
861         switch (flag) {
862             
863         case 'b':
864             /* ----------------
865              *  set BushyPlanFlag to true.
866              * ----------------
867              */
868             BushyPlanFlag = 1;
869             break;
870         case 'B':
871             /* ----------------
872              *  specify the size of buffer pool
873              * ----------------
874              */
875             NBuffers = atoi(optarg);
876             break;
877             
878         case 'C':
879             /* ----------------
880              *  don't print version string (don't know why this is 'C' --mao)
881              * ----------------
882              */
883             flagC = 1;
884             break;
885             
886         case 'D':   /* PGDATA directory */
887             DataDir = optarg;
888             
889         case 'd':   /* debug level */
890             flagQ = 0;
891             DebugLvl = (short)atoi(optarg);
892             if (DebugLvl > 1)
893                 DebugPrintQuery = true;
894             if (DebugLvl > 2)
895             {
896                 DebugPrintParse = true;
897                 DebugPrintPlan = true;
898                 DebugPrintRewrittenParsetree = true;
899             }
900             break;
901             
902         case 'E':
903             /* ----------------
904              *  E - echo the query the user entered
905              * ----------------
906              */
907             flagE = 1;
908             break;
909             
910         case 'F':
911             /* --------------------
912              *  turn off fsync
913              * --------------------
914              */
915             fsyncOff = 1;
916             break;
917
918         case 'f':
919             /* -----------------
920              *    f - forbid generation of certain plans
921              * -----------------
922              */
923             switch (optarg[0]) {
924             case 's': /* seqscan */
925                 _enable_seqscan_ = false;
926                 break;
927             case 'i': /* indexscan */
928                 _enable_indexscan_ = false;
929                 break;
930             case 'n': /* nestloop */
931                 _enable_nestloop_ = false;
932                 break;
933             case 'm': /* mergejoin */
934                 _enable_mergesort_ = false;
935                 break;
936             case 'h': /* hashjoin */
937                 _enable_hashjoin_ = false;
938                 break;
939             default:
940                 errs++;
941             }
942             break;
943
944         case 'i':
945             dontExecute = 1;
946             break;
947             
948         case 'L':
949             /* --------------------
950              *  turn off locking
951              * --------------------
952              */
953             lockingOff = 1;
954             break;
955             
956         case 'm':
957             /* start up a listening backend that can respond to 
958                multiple front-ends.  (Note:  all the front-end connections
959                are still connected to a single-threaded backend.  Requests
960                are FCFS.  Everything is in one transaction 
961                */
962             multiplexedBackend = true;
963             serverPortnum = atoi(optarg);
964 #ifdef WIN32
965            /* There was no postmaster started so the shared memory
966            ** for the shared memory table hasn't been allocated so
967            ** do it now.
968            */
969            _nt_init();
970 #endif /* WIN32 */
971             break;
972         case 'M':
973             exit(PostmasterMain(argc, argv));
974             break;
975         case 'N':
976             /* ----------------
977              *  N - Don't use newline as a query delimiter
978              * ----------------
979              */
980             UseNewLine = 0;
981             break;
982             
983         case 'o':
984             /* ----------------
985              *  o - send output (stdout and stderr) to the given file
986              * ----------------
987              */
988             (void) strncpy(OutputFileName, optarg, MAXPGPATH);
989             break;
990             
991         case 'p':       /* started by postmaster */
992             /* ----------------
993              *  p - special flag passed if backend was forked
994              *      by a postmaster.
995              * ----------------
996              */
997             IsUnderPostmaster = true;
998             break;
999             
1000         case 'P':
1001             /* ----------------
1002              *  P - Use the passed file descriptor number as the port
1003              *    on which to communicate with the user.  This is ONLY
1004              *    useful for debugging when fired up by the postmaster.
1005              * ----------------
1006              */
1007             Portfd = atoi(optarg);
1008             break;
1009             
1010         case 'Q':
1011             /* ----------------
1012              *  Q - set Quiet mode (reduce debugging output)
1013              * ----------------
1014              */
1015             flagQ = 1;
1016             break;
1017             
1018         case 'S':
1019             /* ----------------
1020              *  S - assume stable main memory
1021              *      (don't flush all pages at end transaction)
1022              * ----------------
1023              */
1024             flagS = 1;
1025             SetTransactionFlushEnabled(false);
1026             break;
1027             
1028         case 's':
1029             /* ----------------
1030              *    s - report usage statistics (timings) after each query
1031              * ----------------
1032              */
1033             ShowStats = 1;
1034             StatFp = stderr;
1035             break;
1036             
1037         case 't':
1038             /* ----------------
1039              *  tell postgres to report usage statistics (timings) for
1040              *  each query
1041              *
1042              *  -tpa[rser] = print stats for parser time of each query
1043              *  -tpl[anner] = print stats for planner time of each query
1044              *  -te[xecutor] = print stats for executor time of each query
1045              *  caution: -s can not be used together with -t.
1046              * ----------------
1047              */
1048             StatFp = stderr;
1049             switch (optarg[0]) {
1050             case 'p':  if (optarg[1] == 'a')
1051                 ShowParserStats = 1;
1052             else if (optarg[1] == 'l')
1053                 ShowPlannerStats = 1;
1054             else
1055                 errs++;
1056                 break;
1057             case 'e':  ShowExecutorStats = 1;   break;
1058             default:   errs++; break;
1059             } 
1060             break;
1061             
1062         case 'x':
1063 #if 0 /* planner/xfunc.h */
1064             /* control joey hellerstein's expensive function optimization */
1065             if (XfuncMode != 0)
1066                 {
1067                     fprintf(stderr, "only one -x flag is allowed\n");
1068                     errs++;
1069                     break;
1070                 }
1071             if (strcmp(optarg, "off") == 0)
1072                 XfuncMode = XFUNC_OFF;
1073             else if (strcmp(optarg, "nor") == 0)
1074                 XfuncMode = XFUNC_NOR;
1075             else if (strcmp(optarg, "nopull") == 0)
1076                 XfuncMode = XFUNC_NOPULL;
1077             else if (strcmp(optarg, "nopm") == 0)
1078                 XfuncMode = XFUNC_NOPM;
1079             else if (strcmp(optarg, "pullall") == 0)
1080                 XfuncMode = XFUNC_PULLALL;
1081             else if (strcmp(optarg, "wait") == 0)
1082                 XfuncMode = XFUNC_WAIT;
1083             else {
1084                 fprintf(stderr, "use -x {off,nor,nopull,nopm,pullall,wait}\n");
1085                 errs++;
1086             }
1087 #endif
1088             break;
1089             
1090         default:
1091             /* ----------------
1092              *  default: bad command line option
1093              * ----------------
1094              */
1095             errs++;
1096         }
1097     
1098     /* ----------------
1099      *  get user name and pathname and check command line validity
1100      * ----------------
1101      */
1102     SetPgUserName();
1103     userName = GetPgUserName();
1104     
1105     if (FindBackend(pg_pathname, argv[0]) < 0)
1106         elog(FATAL, "%s: could not locate executable, bailing out...",
1107              argv[0]);
1108     
1109     if (errs || argc - optind > 1) {
1110         usage (argv[0]);
1111         exitpg(1);
1112     } else if (argc - optind == 1) {
1113         DBName = argv[optind];
1114     } else if ((DBName = userName) == NULL) {
1115         fprintf(stderr, "%s: USER undefined and no database specified\n",
1116                 argv[0]);
1117         exitpg(1);
1118     }
1119     
1120     if (ShowStats && 
1121         (ShowParserStats || ShowPlannerStats || ShowExecutorStats)) {
1122         fprintf(stderr, "-s can not be used together with -t.\n");
1123         exitpg(1);
1124     }
1125
1126     if (!DataDir) {
1127         fprintf(stderr, "%s does not know where to find the database system "
1128                 "data.  You must specify the directory that contains the "
1129                 "database system either by specifying the -D invocation "
1130                 "option or by setting the PGDATA environment variable.\n\n",
1131                 argv[0]);
1132         exitpg(1);
1133     }
1134     
1135     Noversion = flagC;
1136     Quiet = flagQ;
1137     EchoQuery = flagE;
1138     
1139     /* ----------------
1140      *  print flags
1141      * ----------------
1142      */
1143     if (! Quiet) {
1144         puts("\t---debug info---");
1145         printf("\tQuiet =        %c\n", Quiet     ? 't' : 'f');
1146         printf("\tNoversion =    %c\n", Noversion ? 't' : 'f');
1147         printf("\tstable    =    %c\n", flagS     ? 't' : 'f');
1148         printf("\ttimings   =    %c\n", ShowStats ? 't' : 'f');
1149         printf("\tbufsize   =    %d\n", NBuffers);
1150         
1151         printf("\tquery echo =   %c\n", EchoQuery ? 't' : 'f');
1152         printf("\tmultiplexed backend? =  %c\n", multiplexedBackend ? 't' : 'f');
1153         printf("\tDatabaseName = [%s]\n", DBName);
1154         puts("\t----------------\n");
1155     }
1156     
1157     /* ----------------
1158      *  initialize portal file descriptors
1159      * ----------------
1160      */
1161     if (IsUnderPostmaster == true) {
1162         if (Portfd < 0) {
1163             fprintf(stderr,
1164                     "Postmaster flag set: no port number specified, use /dev/null\n");
1165             Portfd = open(NULL_DEV, O_RDWR, 0666);
1166         }
1167         pq_init(Portfd);
1168     }
1169
1170 #ifdef WIN32
1171     if ((status = WSAStartup(MAKEWORD(1,1), &WSAData)) == 0)
1172         (void) printf("%s\nInitializing WinSock: %s\n", WSAData.szDescription, WSAData.szSystemStatus);
1173     else {
1174         fprintf(stderr, "Error initializing WinSock: %d is the err", status);
1175         exit(1);
1176     }
1177 #endif /* WIN32 */
1178     
1179     if (multiplexedBackend) {
1180       if (serverPortnum == 0 ||
1181           StreamServerPort(hostName, serverPortnum, &serverSock) != STATUS_OK)
1182         {
1183           fprintf(stderr, "Postgres: cannot create stream port %d\n", serverPortnum);
1184           exit(1);
1185         }
1186 /*
1187 {
1188     char buf[100];
1189     sprintf(buf, "stream port %d created, socket = %d\n", serverPortnum, serverSock);
1190     puts(buf);
1191 }
1192 */
1193       FD_ZERO(&rmask);
1194       FD_ZERO(&basemask);
1195       FD_SET(serverSock, &basemask);  
1196
1197       frontendList = DLNewList();
1198       /* add the original FrontEnd to the list */
1199       if (IsUnderPostmaster == true) {
1200         FrontEnd *fe = malloc(sizeof(FrontEnd));
1201
1202         FD_SET(Portfd, &basemask);
1203         maxFd = Max(serverSock,Portfd) + 1;
1204
1205         fe->fn_connected = true;
1206         fe->fn_Pfin = Pfin;
1207         fe->fn_Pfout = Pfout;
1208         fe->fn_done = false;
1209         (fe->fn_port).sock = Portfd;
1210         DLAddHead(frontendList, DLNewElem(fe));
1211         numFE++;
1212       } else {
1213           numFE = 1;
1214           maxFd = serverSock + 1;
1215       }
1216     }
1217
1218     if (IsUnderPostmaster || multiplexedBackend)
1219         whereToSendOutput = Remote;
1220     else 
1221         whereToSendOutput = Debug;
1222     
1223     SetProcessingMode(InitProcessing);
1224     
1225     /* initialize */
1226     if (! Quiet) {
1227         puts("\tInitPostgres()..");
1228     }
1229  
1230 #if WIN32
1231      _nt_attach();
1232 #endif /* WIN32 */
1233
1234     InitPostgres(DBName);
1235
1236     /* ----------------
1237      *  if an exception is encountered, processing resumes here
1238      *  so we abort the current transaction and start a new one.
1239      *  This must be done after we initialize the slave backends
1240      *  so that the slaves signal the master to abort the transaction
1241      *  rather than calling AbortCurrentTransaction() themselves.
1242      *
1243      *  Note:  elog(WARN) causes a kill(getpid(),1) to occur sending
1244      *         us back here.
1245      * ----------------
1246      */
1247
1248 #ifndef WIN32    
1249     pqsignal(SIGHUP, handle_warn);
1250
1251     if (sigsetjmp(Warn_restart, 1) != 0) {
1252 #else
1253     if (setjmp(Warn_restart) != 0) {
1254 #endif /* WIN32 */
1255         InWarn = 1;
1256
1257         time(&tim);
1258         
1259         if (! Quiet)
1260             printf("\tAbortCurrentTransaction() at %s\n", ctime(&tim));
1261
1262         memset(parser_input, 0, MAX_PARSE_BUFFER);
1263         
1264         AbortCurrentTransaction();
1265     }
1266     InWarn = 0;
1267     
1268     /* ----------------
1269      *  POSTGRES main processing loop begins here
1270      * ----------------
1271      */
1272     if (IsUnderPostmaster == false) {
1273         puts("\nPOSTGRES backend interactive interface");
1274         puts("$Revision: 1.25 $ $Date: 1997/01/14 08:05:26 $");
1275     }
1276     
1277     /* ----------------
1278      * if stable main memory is assumed (-S flag is set), it is necessary
1279      * to flush all dirty shared buffers before exit
1280      * plai 8/7/90
1281      * ----------------
1282      */
1283     if (!TransactionFlushEnabled())
1284         on_exitpg(FlushBufferPool, (caddr_t) 0);
1285     
1286     for (;;) {
1287       
1288       if (multiplexedBackend) {
1289         if (numFE == 0) 
1290           break;
1291
1292         memmove((char *) &rmask, (char *) &basemask, sizeof(fd_set));
1293         nSelected = select(maxFd, &rmask,0,0,0);
1294
1295         if (nSelected < 0) {
1296
1297           if (errno == EINTR) continue;
1298           fprintf(stderr,"postgres: multiplexed backend select failed\n");
1299           exitpg(1);
1300         }
1301         if (FD_ISSET(serverSock, &rmask)) {
1302         /* new connection pending on our well-known port's socket */
1303           newFE = (FrontEnd*) malloc (sizeof(FrontEnd));
1304           memset(newFE, 0, sizeof(FrontEnd));
1305           newFE->fn_connected = false;
1306           newFE->fn_done = false;
1307           newPort = &(newFE->fn_port);
1308           if (StreamConnection(serverSock,newPort) != STATUS_OK) {
1309             StreamClose(newPort->sock);
1310             newFd = -1;
1311           }
1312           else {
1313             DLAddHead(frontendList, DLNewElem(newFE));
1314             numFE++;
1315             newFd = newPort->sock;
1316             if (newFd >= maxFd) maxFd = newFd + 1;
1317             FD_SET(newFd, &rmask);
1318             FD_SET(newFd, &basemask);
1319             --nSelected;
1320             FD_CLR(serverSock, &rmask);
1321           }
1322           continue;
1323         } /* if FD_ISSET(serverSock) */
1324
1325         /* if we get here, it means that the serverSocket was not the one
1326            selected.  Instead, one of the front ends was selected.
1327            find which one */
1328         curr = DLGetHead(frontendList);
1329         while (curr) {
1330           FrontEnd *fe = (FrontEnd*)DLE_VAL(curr);
1331           Port *port = &(fe->fn_port);
1332
1333           /* this is lifted from postmaster.c */
1334           if (FD_ISSET(port->sock, &rmask)) {
1335             if (fe->fn_connected == false) {
1336                 /* we have a message from a new frontEnd */
1337                 status = PacketReceive(port, &port->buf, NON_BLOCKING);
1338                 if (status == STATUS_OK) {
1339                   fe->fn_connected = true;
1340                   pq_init(port->sock);
1341                   fe->fn_Pfin = Pfin;
1342                   fe->fn_Pfout = Pfout;
1343                 }
1344                 else
1345                   fprintf(stderr,"Multiplexed backend: error in reading packets from %d\n", port->sock);
1346                }
1347             else  /* we have a query from an existing,  active FrontEnd */
1348               {
1349                 Pfin = fe->fn_Pfin;
1350                 Pfout = fe->fn_Pfout;
1351                 currentFE = fe;
1352               }
1353             if (fe->fn_done)
1354                 {
1355                     Dlelem *c = curr;
1356                     curr = DLGetSucc(curr);
1357                     DLRemove(c);
1358                 }
1359              break;
1360               }
1361           else
1362             curr = DLGetSucc(curr);
1363         }
1364     }
1365         /* ----------------
1366          *   (1) read a command. 
1367          * ----------------
1368          */
1369         memset(parser_input, 0, MAX_PARSE_BUFFER);
1370
1371         firstchar = ReadCommand(parser_input, multiplexedBackend);
1372         /* process the command */
1373         switch (firstchar) {
1374             /* ----------------
1375              *  'F' indicates a fastpath call.
1376              *      XXX HandleFunctionRequest
1377              * ----------------
1378              */
1379         case 'F':
1380             IsEmptyQuery = false;
1381             
1382             /* start an xact for this function invocation */
1383             if (! Quiet) {
1384                 time(&tim);
1385                 printf("\tStartTransactionCommand() at %s\n", ctime(&tim));
1386             }
1387             
1388             StartTransactionCommand();
1389             HandleFunctionRequest();
1390             break;
1391             
1392             /* ----------------
1393              *  'Q' indicates a user query
1394              * ----------------
1395              */
1396         case 'Q':
1397             fflush(stdout);
1398             
1399             if ( strspn(parser_input," \t\n") == strlen(parser_input)) {
1400                 /* ----------------
1401                  *  if there is nothing in the input buffer, don't bother
1402                  *  trying to parse and execute anything..
1403                  * ----------------
1404                  */
1405                 IsEmptyQuery = true;
1406             } else {
1407                 /* ----------------
1408                  *  otherwise, process the input string.
1409                  * ----------------
1410                  */
1411                 IsEmptyQuery = false;
1412                 if (ShowStats)
1413                     ResetUsage();
1414                 
1415                 /* start an xact for this query */
1416                 if (! Quiet) {
1417                     time(&tim);
1418                     printf("\tStartTransactionCommand() at %s\n", ctime(&tim));
1419                 }
1420                 StartTransactionCommand();
1421                 
1422                 pg_eval(parser_input, (char **) NULL, (Oid *) NULL, 0);
1423                 
1424                 if (ShowStats)
1425                     ShowUsage();
1426             }
1427             break;
1428             
1429             /* ----------------
1430              *  'X' means that the frontend is closing down the socket
1431              * ----------------
1432              */
1433         case 'X':
1434             IsEmptyQuery = true;
1435             if (multiplexedBackend) {
1436                FD_CLR(currentFE->fn_port.sock, &basemask);
1437                currentFE->fn_done = true;
1438                numFE--;
1439              }
1440             pq_close();
1441             break;
1442             
1443         default:
1444             elog(WARN,"unknown frontend message was recieved");
1445         }
1446         
1447         /* ----------------
1448          *   (3) commit the current transaction
1449          *
1450          *   Note: if we had an empty input buffer, then we didn't
1451          *   call pg_eval, so we don't bother to commit this transaction.
1452          * ----------------
1453          */
1454         if (! IsEmptyQuery) {
1455             if (! Quiet) {
1456                 time(&tim);
1457                 printf("\tCommitTransactionCommand() at %s\n", ctime(&tim));
1458             }
1459             CommitTransactionCommand();
1460             
1461         } else {
1462             if (IsUnderPostmaster || multiplexedBackend)
1463                 NullCommand(Remote);
1464         }
1465         
1466 } /* infinite for-loop */
1467   exitpg(0);
1468   return 1;
1469 }
1470
1471 #ifndef WIN32
1472 #ifdef NEED_RUSAGE
1473 #include "rusagestub.h"
1474 #else /* NEED_RUSAGE */
1475 #include <sys/resource.h>
1476 #endif /* NEED_RUSAGE */
1477
1478 struct rusage Save_r;
1479 struct timeval Save_t;
1480
1481 void
1482 ResetUsage(void)
1483 {
1484     struct timezone tz;
1485     getrusage(RUSAGE_SELF, &Save_r);
1486     gettimeofday(&Save_t, &tz);
1487     ResetBufferUsage();
1488 /*    ResetTupleCount(); */
1489 }
1490
1491 void
1492 ShowUsage(void)
1493 {
1494     struct timeval user, sys;
1495     struct timeval elapse_t;
1496     struct timezone tz;
1497     struct rusage r;
1498     
1499     getrusage(RUSAGE_SELF, &r);
1500     gettimeofday(&elapse_t, &tz);
1501     memmove((char *)&user, (char *)&r.ru_utime, sizeof(user)); 
1502     memmove((char *)&sys, (char *)&r.ru_stime,sizeof(sys)); 
1503     if (elapse_t.tv_usec < Save_t.tv_usec) {
1504         elapse_t.tv_sec--;
1505         elapse_t.tv_usec += 1000000;
1506     }
1507     if (r.ru_utime.tv_usec < Save_r.ru_utime.tv_usec) {
1508         r.ru_utime.tv_sec--;
1509         r.ru_utime.tv_usec += 1000000;
1510     }
1511     if (r.ru_stime.tv_usec < Save_r.ru_stime.tv_usec) {
1512         r.ru_stime.tv_sec--;
1513         r.ru_stime.tv_usec += 1000000;
1514     }
1515     
1516     /*
1517      *  the only stats we don't show here are for memory usage -- i can't
1518      *  figure out how to interpret the relevant fields in the rusage
1519      *  struct, and they change names across o/s platforms, anyway.
1520      *  if you can figure out what the entries mean, you can somehow
1521      *  extract resident set size, shared text size, and unshared data
1522      *  and stack sizes.
1523      */
1524     
1525     fprintf(StatFp, "! system usage stats:\n");
1526     fprintf(StatFp, 
1527             "!\t%ld.%06ld elapsed %ld.%06ld user %ld.%06ld system sec\n",
1528             (long int) elapse_t.tv_sec - Save_t.tv_sec,
1529             (long int) elapse_t.tv_usec - Save_t.tv_usec,
1530             (long int) r.ru_utime.tv_sec - Save_r.ru_utime.tv_sec,
1531             (long int) r.ru_utime.tv_usec - Save_r.ru_utime.tv_usec,
1532             (long int) r.ru_stime.tv_sec - Save_r.ru_stime.tv_sec,
1533             (long int) r.ru_stime.tv_usec - Save_r.ru_stime.tv_usec);
1534     fprintf(StatFp,
1535             "!\t[%ld.%06ld user %ld.%06ld sys total]\n",
1536             (long int) user.tv_sec, 
1537             (long int) user.tv_usec, 
1538             (long int) sys.tv_sec, 
1539             (long int) sys.tv_usec);
1540 #ifndef NEED_RUSAGE
1541     fprintf(StatFp, 
1542             "!\t%ld/%ld [%ld/%ld] filesystem blocks in/out\n",
1543             r.ru_inblock - Save_r.ru_inblock,
1544             /* they only drink coffee at dec */
1545             r.ru_oublock - Save_r.ru_oublock,
1546             r.ru_inblock, r.ru_oublock);
1547     fprintf(StatFp, 
1548             "!\t%ld/%ld [%ld/%ld] page faults/reclaims, %ld [%ld] swaps\n",
1549             r.ru_majflt - Save_r.ru_majflt,
1550             r.ru_minflt - Save_r.ru_minflt,
1551             r.ru_majflt, r.ru_minflt,
1552             r.ru_nswap - Save_r.ru_nswap,
1553             r.ru_nswap);
1554     fprintf(StatFp, 
1555             "!\t%ld [%ld] signals rcvd, %ld/%ld [%ld/%ld] messages rcvd/sent\n",
1556             r.ru_nsignals - Save_r.ru_nsignals,
1557             r.ru_nsignals,
1558             r.ru_msgrcv - Save_r.ru_msgrcv,
1559             r.ru_msgsnd - Save_r.ru_msgsnd,
1560             r.ru_msgrcv, r.ru_msgsnd);
1561     fprintf(StatFp, 
1562             "!\t%ld/%ld [%ld/%ld] voluntary/involuntary context switches\n",
1563             r.ru_nvcsw - Save_r.ru_nvcsw,
1564             r.ru_nivcsw - Save_r.ru_nivcsw,
1565             r.ru_nvcsw, r.ru_nivcsw);
1566 #endif /* NEED_RUSAGE */
1567     fprintf(StatFp, "! postgres usage stats:\n");
1568     PrintBufferUsage(StatFp);
1569 /*     DisplayTupleCount(StatFp); */
1570 }
1571 #else
1572 void
1573 ShowUsage()
1574 {}
1575
1576 void
1577 ResetUsage()
1578 {}
1579 #endif /* WIN32 */