]> granicus.if.org Git - postgresql/blob - contrib/pgbench/pgbench.c
b5082e0abd62efb9d1a7e68eb7b1d1e149ce5713
[postgresql] / contrib / pgbench / pgbench.c
1 /*
2  * $PostgreSQL: pgsql/contrib/pgbench/pgbench.c,v 1.76 2008/03/10 01:23:04 tgl Exp $
3  *
4  * pgbench: a simple benchmark program for PostgreSQL
5  * written by Tatsuo Ishii
6  *
7  * Copyright (c) 2000-2007      Tatsuo Ishii
8  *
9  * Permission to use, copy, modify, and distribute this software and
10  * its documentation for any purpose and without fee is hereby
11  * granted, provided that the above copyright notice appear in all
12  * copies and that both that copyright notice and this permission
13  * notice appear in supporting documentation, and that the name of the
14  * author not be used in advertising or publicity pertaining to
15  * distribution of the software without specific, written prior
16  * permission. The author makes no representations about the
17  * suitability of this software for any purpose.  It is provided "as
18  * is" without express or implied warranty.
19  */
20 #include "postgres_fe.h"
21
22 #include "libpq-fe.h"
23
24 #include <ctype.h>
25
26 #ifdef WIN32
27 #undef FD_SETSIZE
28 #define FD_SETSIZE 1024
29 #include <win32.h>
30 #else
31 #include <sys/time.h>
32 #include <unistd.h>
33 #endif   /* ! WIN32 */
34
35 #ifdef HAVE_GETOPT_H
36 #include <getopt.h>
37 #endif
38
39 #ifdef HAVE_SYS_SELECT_H
40 #include <sys/select.h>
41 #endif
42
43 #ifdef HAVE_SYS_RESOURCE_H
44 #include <sys/resource.h>               /* for getrlimit */
45 #endif
46
47 extern char *optarg;
48 extern int      optind;
49
50
51 /********************************************************************
52  * some configurable parameters */
53
54 /* max number of clients allowed */
55 #ifdef FD_SETSIZE
56 #define MAXCLIENTS      (FD_SETSIZE - 10)
57 #else
58 #define MAXCLIENTS      1024
59 #endif
60
61 int                     nclients = 1;           /* default number of simulated clients */
62 int                     nxacts = 10;            /* default number of transactions per clients */
63
64 /*
65  * scaling factor. for example, scale = 10 will make 1000000 tuples of
66  * accounts table.
67  */
68 int                     scale = 1;
69
70 /*
71  * fillfactor. for example, fillfactor = 90 will use only 90 percent
72  * space during inserts and leave 10 percent free.
73  */
74 int                     fillfactor = 100;
75
76 /*
77  * end of configurable parameters
78  *********************************************************************/
79
80 #define nbranches       1
81 #define ntellers        10
82 #define naccounts       100000
83
84 FILE       *LOGFILE = NULL;
85
86 bool            use_log;                        /* log transaction latencies to a file */
87
88 int                     remains;                        /* number of remaining clients */
89
90 int                     is_connect;                     /* establish connection  for each transaction */
91
92 char       *pghost = "";
93 char       *pgport = "";
94 char       *pgoptions = NULL;
95 char       *pgtty = NULL;
96 char       *login = NULL;
97 char       *dbName;
98
99 /* variable definitions */
100 typedef struct
101 {
102         char       *name;                       /* variable name */
103         char       *value;                      /* its value */
104 }       Variable;
105
106 /*
107  * structures used in custom query mode
108  */
109
110 typedef struct
111 {
112         PGconn     *con;                        /* connection handle to DB */
113         int                     id;                             /* client No. */
114         int                     state;                  /* state No. */
115         int                     cnt;                    /* xacts count */
116         int                     ecnt;                   /* error count */
117         int                     listen;                 /* 0 indicates that an async query has been
118                                                                  * sent */
119         int                     sleeping;               /* 1 indicates that the client is napping */
120         struct timeval until;           /* napping until */
121         Variable   *variables;          /* array of variable definitions */
122         int                     nvariables;
123         struct timeval txn_begin;       /* used for measuring latencies */
124         int                     use_file;               /* index in sql_files for this client */
125 }       CState;
126
127 /*
128  * queries read from files
129  */
130 #define SQL_COMMAND             1
131 #define META_COMMAND    2
132 #define MAX_ARGS                10
133
134 typedef struct
135 {
136         int                     type;                   /* command type (SQL_COMMAND or META_COMMAND) */
137         int                     argc;                   /* number of commands */
138         char       *argv[MAX_ARGS]; /* command list */
139 }       Command;
140
141 #define MAX_FILES               128             /* max number of SQL script files allowed */
142
143 Command   **sql_files[MAX_FILES];               /* SQL script files */
144 int                     num_files;                      /* its number */
145
146 /* default scenario */
147 static char *tpc_b = {
148         "\\set nbranches :scale\n"
149         "\\set ntellers 10 * :scale\n"
150         "\\set naccounts 100000 * :scale\n"
151         "\\setrandom aid 1 :naccounts\n"
152         "\\setrandom bid 1 :nbranches\n"
153         "\\setrandom tid 1 :ntellers\n"
154         "\\setrandom delta -5000 5000\n"
155         "BEGIN;\n"
156         "UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
157         "SELECT abalance FROM accounts WHERE aid = :aid;\n"
158         "UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
159         "UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
160         "INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
161         "END;\n"
162 };
163
164 /* -N case */
165 static char *simple_update = {
166         "\\set nbranches :scale\n"
167         "\\set ntellers 10 * :scale\n"
168         "\\set naccounts 100000 * :scale\n"
169         "\\setrandom aid 1 :naccounts\n"
170         "\\setrandom bid 1 :nbranches\n"
171         "\\setrandom tid 1 :ntellers\n"
172         "\\setrandom delta -5000 5000\n"
173         "BEGIN;\n"
174         "UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
175         "SELECT abalance FROM accounts WHERE aid = :aid;\n"
176         "INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
177         "END;\n"
178 };
179
180 /* -S case */
181 static char *select_only = {
182         "\\set naccounts 100000 * :scale\n"
183         "\\setrandom aid 1 :naccounts\n"
184         "SELECT abalance FROM accounts WHERE aid = :aid;\n"
185 };
186
187 static void
188 usage(void)
189 {
190         fprintf(stderr, "usage: pgbench [-h hostname][-p port][-c nclients][-t ntransactions][-s scaling_factor][-D varname=value][-n][-C][-v][-S][-N][-f filename][-l][-U login][-d][dbname]\n");
191         fprintf(stderr, "(initialize mode): pgbench -i [-h hostname][-p port][-s scaling_factor] [-F fillfactor] [-U login][-d][dbname]\n");
192 }
193
194 /* random number generator: uniform distribution from min to max inclusive */
195 static int
196 getrand(int min, int max)
197 {
198         /*
199          * Odd coding is so that min and max have approximately the same chance of
200          * being selected as do numbers between them.
201          */
202         return min + (int) (((max - min + 1) * (double) random()) / (MAX_RANDOM_VALUE + 1.0));
203 }
204
205 /* call PQexec() and exit() on failure */
206 static void
207 executeStatement(PGconn *con, const char *sql)
208 {
209         PGresult   *res;
210
211         res = PQexec(con, sql);
212         if (PQresultStatus(res) != PGRES_COMMAND_OK)
213         {
214                 fprintf(stderr, "%s", PQerrorMessage(con));
215                 exit(1);
216         }
217         PQclear(res);
218 }
219
220 /* set up a connection to the backend */
221 static PGconn *
222 doConnect(void)
223 {
224         PGconn     *conn;
225         static char *password = NULL;
226         bool            new_pass;
227
228         /*
229          * Start the connection.  Loop until we have a password if requested by
230          * backend.
231          */
232         do
233         {
234                 new_pass = false;
235
236                 conn = PQsetdbLogin(pghost, pgport, pgoptions, pgtty, dbName,
237                                                         login, password);
238                 if (!conn)
239                 {
240                         fprintf(stderr, "Connection to database \"%s\" failed\n",
241                                         dbName);
242                         return NULL;
243                 }
244
245                 if (PQstatus(conn) == CONNECTION_BAD &&
246                         PQconnectionNeedsPassword(conn) &&
247                         password == NULL &&
248                         !feof(stdin))
249                 {
250                         PQfinish(conn);
251                         password = simple_prompt("Password: ", 100, false);
252                         new_pass = true;
253                 }
254         } while (new_pass);
255
256         /* check to see that the backend connection was successfully made */
257         if (PQstatus(conn) == CONNECTION_BAD)
258         {
259                 fprintf(stderr, "Connection to database \"%s\" failed:\n%s",
260                                 dbName, PQerrorMessage(conn));
261                 PQfinish(conn);
262                 return NULL;
263         }
264
265         executeStatement(conn, "SET search_path = public");
266
267         return conn;
268 }
269
270 /* throw away response from backend */
271 static void
272 discard_response(CState * state)
273 {
274         PGresult   *res;
275
276         do
277         {
278                 res = PQgetResult(state->con);
279                 if (res)
280                         PQclear(res);
281         } while (res);
282 }
283
284 /* check to see if the SQL result was good */
285 static int
286 check(CState * state, PGresult *res, int n)
287 {
288         CState     *st = &state[n];
289
290         switch (PQresultStatus(res))
291         {
292                 case PGRES_COMMAND_OK:
293                 case PGRES_TUPLES_OK:
294                         /* OK */
295                         break;
296                 default:
297                         fprintf(stderr, "Client %d aborted in state %d: %s",
298                                         n, st->state, PQerrorMessage(st->con));
299                         remains--;                      /* I've aborted */
300                         PQfinish(st->con);
301                         st->con = NULL;
302                         return (-1);
303         }
304         return (0);                                     /* OK */
305 }
306
307 static int
308 compareVariables(const void *v1, const void *v2)
309 {
310         return strcmp(((const Variable *) v1)->name,
311                                   ((const Variable *) v2)->name);
312 }
313
314 static char *
315 getVariable(CState * st, char *name)
316 {
317         Variable        key,
318                            *var;
319
320         /* On some versions of Solaris, bsearch of zero items dumps core */
321         if (st->nvariables <= 0)
322                 return NULL;
323
324         key.name = name;
325         var = (Variable *) bsearch((void *) &key,
326                                                            (void *) st->variables,
327                                                            st->nvariables,
328                                                            sizeof(Variable),
329                                                            compareVariables);
330         if (var != NULL)
331                 return var->value;
332         else
333                 return NULL;
334 }
335
336 static int
337 putVariable(CState * st, char *name, char *value)
338 {
339         Variable        key,
340                            *var;
341
342         key.name = name;
343         /* On some versions of Solaris, bsearch of zero items dumps core */
344         if (st->nvariables > 0)
345                 var = (Variable *) bsearch((void *) &key,
346                                                                    (void *) st->variables,
347                                                                    st->nvariables,
348                                                                    sizeof(Variable),
349                                                                    compareVariables);
350         else
351                 var = NULL;
352
353         if (var == NULL)
354         {
355                 Variable   *newvars;
356
357                 if (st->variables)
358                         newvars = (Variable *) realloc(st->variables,
359                                                                         (st->nvariables + 1) * sizeof(Variable));
360                 else
361                         newvars = (Variable *) malloc(sizeof(Variable));
362
363                 if (newvars == NULL)
364                         return false;
365
366                 st->variables = newvars;
367
368                 var = &newvars[st->nvariables];
369
370                 var->name = NULL;
371                 var->value = NULL;
372
373                 if ((var->name = strdup(name)) == NULL
374                         || (var->value = strdup(value)) == NULL)
375                 {
376                         free(var->name);
377                         free(var->value);
378                         return false;
379                 }
380
381                 st->nvariables++;
382
383                 qsort((void *) st->variables, st->nvariables, sizeof(Variable),
384                           compareVariables);
385         }
386         else
387         {
388                 char       *val;
389
390                 if ((val = strdup(value)) == NULL)
391                         return false;
392
393                 free(var->value);
394                 var->value = val;
395         }
396
397         return true;
398 }
399
400 static char *
401 assignVariables(CState * st, char *sql)
402 {
403         int                     i,
404                                 j;
405         char       *p,
406                            *name,
407                            *val;
408         void       *tmp;
409
410         i = 0;
411         while ((p = strchr(&sql[i], ':')) != NULL)
412         {
413                 i = j = p - sql;
414                 do
415                 {
416                         i++;
417                 } while (isalnum((unsigned char) sql[i]) || sql[i] == '_');
418                 if (i == j + 1)
419                         continue;
420
421                 name = malloc(i - j);
422                 if (name == NULL)
423                         return NULL;
424                 memcpy(name, &sql[j + 1], i - (j + 1));
425                 name[i - (j + 1)] = '\0';
426                 val = getVariable(st, name);
427                 free(name);
428                 if (val == NULL)
429                         continue;
430
431                 if (strlen(val) > i - j)
432                 {
433                         tmp = realloc(sql, strlen(sql) - (i - j) + strlen(val) + 1);
434                         if (tmp == NULL)
435                         {
436                                 free(sql);
437                                 return NULL;
438                         }
439                         sql = tmp;
440                 }
441
442                 if (strlen(val) != i - j)
443                         memmove(&sql[j + strlen(val)], &sql[i], strlen(&sql[i]) + 1);
444
445                 strncpy(&sql[j], val, strlen(val));
446
447                 if (strlen(val) < i - j)
448                 {
449                         tmp = realloc(sql, strlen(sql) + 1);
450                         if (tmp == NULL)
451                         {
452                                 free(sql);
453                                 return NULL;
454                         }
455                         sql = tmp;
456                 }
457
458                 i = j + strlen(val);
459         }
460
461         return sql;
462 }
463
464 static void
465 doCustom(CState * state, int n, int debug)
466 {
467         PGresult   *res;
468         CState     *st = &state[n];
469         Command   **commands;
470
471 top:
472         commands = sql_files[st->use_file];
473
474         if (st->sleeping)
475         {                                                       /* are we sleeping? */
476                 int                     usec;
477                 struct timeval now;
478
479                 gettimeofday(&now, NULL);
480                 usec = (st->until.tv_sec - now.tv_sec) * 1000000 +
481                         st->until.tv_usec - now.tv_usec;
482                 if (usec <= 0)
483                         st->sleeping = 0;       /* Done sleeping, go ahead with next command */
484                 else
485                         return;                         /* Still sleeping, nothing to do here */
486         }
487
488         if (st->listen)
489         {                                                       /* are we receiver? */
490                 if (commands[st->state]->type == SQL_COMMAND)
491                 {
492                         if (debug)
493                                 fprintf(stderr, "client %d receiving\n", n);
494                         if (!PQconsumeInput(st->con))
495                         {                                       /* there's something wrong */
496                                 fprintf(stderr, "Client %d aborted in state %d. Probably the backend died while processing.\n", n, st->state);
497                                 remains--;              /* I've aborted */
498                                 PQfinish(st->con);
499                                 st->con = NULL;
500                                 return;
501                         }
502                         if (PQisBusy(st->con))
503                                 return;                 /* don't have the whole result yet */
504                 }
505
506                 /*
507                  * transaction finished: record the time it took in the log
508                  */
509                 if (use_log && commands[st->state + 1] == NULL)
510                 {
511                         double          diff;
512                         struct timeval now;
513
514                         gettimeofday(&now, NULL);
515                         diff = (int) (now.tv_sec - st->txn_begin.tv_sec) * 1000000.0 +
516                                 (int) (now.tv_usec - st->txn_begin.tv_usec);
517
518                         fprintf(LOGFILE, "%d %d %.0f %d %ld %ld\n",
519                                         st->id, st->cnt, diff, st->use_file,
520                                         (long) now.tv_sec, (long) now.tv_usec);
521                 }
522
523                 if (commands[st->state]->type == SQL_COMMAND)
524                 {
525                         res = PQgetResult(st->con);
526                         if (check(state, res, n))
527                         {
528                                 PQclear(res);
529                                 return;
530                         }
531                         PQclear(res);
532                         discard_response(st);
533                 }
534
535                 if (commands[st->state + 1] == NULL)
536                 {
537                         if (is_connect)
538                         {
539                                 PQfinish(st->con);
540                                 st->con = NULL;
541                         }
542
543                         if (++st->cnt >= nxacts)
544                         {
545                                 remains--;              /* I've done */
546                                 if (st->con != NULL)
547                                 {
548                                         PQfinish(st->con);
549                                         st->con = NULL;
550                                 }
551                                 return;
552                         }
553                 }
554
555                 /* increment state counter */
556                 st->state++;
557                 if (commands[st->state] == NULL)
558                 {
559                         st->state = 0;
560                         st->use_file = getrand(0, num_files - 1);
561                         commands = sql_files[st->use_file];
562                 }
563         }
564
565         if (st->con == NULL)
566         {
567                 if ((st->con = doConnect()) == NULL)
568                 {
569                         fprintf(stderr, "Client %d aborted in establishing connection.\n",
570                                         n);
571                         remains--;                      /* I've aborted */
572                         PQfinish(st->con);
573                         st->con = NULL;
574                         return;
575                 }
576         }
577
578         if (use_log && st->state == 0)
579                 gettimeofday(&(st->txn_begin), NULL);
580
581         if (commands[st->state]->type == SQL_COMMAND)
582         {
583                 char       *sql;
584
585                 if ((sql = strdup(commands[st->state]->argv[0])) == NULL
586                         || (sql = assignVariables(st, sql)) == NULL)
587                 {
588                         fprintf(stderr, "out of memory\n");
589                         st->ecnt++;
590                         return;
591                 }
592
593                 if (debug)
594                         fprintf(stderr, "client %d sending %s\n", n, sql);
595                 if (PQsendQuery(st->con, sql) == 0)
596                 {
597                         if (debug)
598                                 fprintf(stderr, "PQsendQuery(%s)failed\n", sql);
599                         st->ecnt++;
600                 }
601                 else
602                 {
603                         st->listen = 1;         /* flags that should be listened */
604                 }
605                 free(sql);
606         }
607         else if (commands[st->state]->type == META_COMMAND)
608         {
609                 int                     argc = commands[st->state]->argc,
610                                         i;
611                 char      **argv = commands[st->state]->argv;
612
613                 if (debug)
614                 {
615                         fprintf(stderr, "client %d executing \\%s", n, argv[0]);
616                         for (i = 1; i < argc; i++)
617                                 fprintf(stderr, " %s", argv[i]);
618                         fprintf(stderr, "\n");
619                 }
620
621                 if (pg_strcasecmp(argv[0], "setrandom") == 0)
622                 {
623                         char       *var;
624                         int                     min,
625                                                 max;
626                         char            res[64];
627
628                         if (*argv[2] == ':')
629                         {
630                                 if ((var = getVariable(st, argv[2] + 1)) == NULL)
631                                 {
632                                         fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[2]);
633                                         st->ecnt++;
634                                         return;
635                                 }
636                                 min = atoi(var);
637                         }
638                         else
639                                 min = atoi(argv[2]);
640
641 #ifdef NOT_USED
642                         if (min < 0)
643                         {
644                                 fprintf(stderr, "%s: invalid minimum number %d\n", argv[0], min);
645                                 st->ecnt++;
646                                 return;
647                         }
648 #endif
649
650                         if (*argv[3] == ':')
651                         {
652                                 if ((var = getVariable(st, argv[3] + 1)) == NULL)
653                                 {
654                                         fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[3]);
655                                         st->ecnt++;
656                                         return;
657                                 }
658                                 max = atoi(var);
659                         }
660                         else
661                                 max = atoi(argv[3]);
662
663                         if (max < min || max > MAX_RANDOM_VALUE)
664                         {
665                                 fprintf(stderr, "%s: invalid maximum number %d\n", argv[0], max);
666                                 st->ecnt++;
667                                 return;
668                         }
669
670 #ifdef DEBUG
671                         printf("min: %d max: %d random: %d\n", min, max, getrand(min, max));
672 #endif
673                         snprintf(res, sizeof(res), "%d", getrand(min, max));
674
675                         if (putVariable(st, argv[1], res) == false)
676                         {
677                                 fprintf(stderr, "%s: out of memory\n", argv[0]);
678                                 st->ecnt++;
679                                 return;
680                         }
681
682                         st->listen = 1;
683                 }
684                 else if (pg_strcasecmp(argv[0], "set") == 0)
685                 {
686                         char       *var;
687                         int                     ope1,
688                                                 ope2;
689                         char            res[64];
690
691                         if (*argv[2] == ':')
692                         {
693                                 if ((var = getVariable(st, argv[2] + 1)) == NULL)
694                                 {
695                                         fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[2]);
696                                         st->ecnt++;
697                                         return;
698                                 }
699                                 ope1 = atoi(var);
700                         }
701                         else
702                                 ope1 = atoi(argv[2]);
703
704                         if (argc < 5)
705                                 snprintf(res, sizeof(res), "%d", ope1);
706                         else
707                         {
708                                 if (*argv[4] == ':')
709                                 {
710                                         if ((var = getVariable(st, argv[4] + 1)) == NULL)
711                                         {
712                                                 fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[4]);
713                                                 st->ecnt++;
714                                                 return;
715                                         }
716                                         ope2 = atoi(var);
717                                 }
718                                 else
719                                         ope2 = atoi(argv[4]);
720
721                                 if (strcmp(argv[3], "+") == 0)
722                                         snprintf(res, sizeof(res), "%d", ope1 + ope2);
723                                 else if (strcmp(argv[3], "-") == 0)
724                                         snprintf(res, sizeof(res), "%d", ope1 - ope2);
725                                 else if (strcmp(argv[3], "*") == 0)
726                                         snprintf(res, sizeof(res), "%d", ope1 * ope2);
727                                 else if (strcmp(argv[3], "/") == 0)
728                                 {
729                                         if (ope2 == 0)
730                                         {
731                                                 fprintf(stderr, "%s: division by zero\n", argv[0]);
732                                                 st->ecnt++;
733                                                 return;
734                                         }
735                                         snprintf(res, sizeof(res), "%d", ope1 / ope2);
736                                 }
737                                 else
738                                 {
739                                         fprintf(stderr, "%s: unsupported operator %s\n", argv[0], argv[3]);
740                                         st->ecnt++;
741                                         return;
742                                 }
743                         }
744
745                         if (putVariable(st, argv[1], res) == false)
746                         {
747                                 fprintf(stderr, "%s: out of memory\n", argv[0]);
748                                 st->ecnt++;
749                                 return;
750                         }
751
752                         st->listen = 1;
753                 }
754                 else if (pg_strcasecmp(argv[0], "sleep") == 0)
755                 {
756                         char       *var;
757                         int                     usec;
758                         struct timeval now;
759
760                         if (*argv[1] == ':')
761                         {
762                                 if ((var = getVariable(st, argv[1] + 1)) == NULL)
763                                 {
764                                         fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[1]);
765                                         st->ecnt++;
766                                         return;
767                                 }
768                                 usec = atoi(var);
769                         }
770                         else
771                                 usec = atoi(argv[1]);
772
773                         if (argc > 2)
774                         {
775                                 if (pg_strcasecmp(argv[2], "ms") == 0)
776                                         usec *= 1000;
777                                 else if (pg_strcasecmp(argv[2], "s") == 0)
778                                         usec *= 1000000;
779                         }
780                         else
781                                 usec *= 1000000;
782
783                         gettimeofday(&now, NULL);
784                         st->until.tv_sec = now.tv_sec + (now.tv_usec + usec) / 1000000;
785                         st->until.tv_usec = (now.tv_usec + usec) % 1000000;
786                         st->sleeping = 1;
787
788                         st->listen = 1;
789                 }
790
791                 goto top;
792         }
793 }
794
795 /* discard connections */
796 static void
797 disconnect_all(CState * state)
798 {
799         int                     i;
800
801         for (i = 0; i < nclients; i++)
802         {
803                 if (state[i].con)
804                         PQfinish(state[i].con);
805         }
806 }
807
808 /* create tables and setup data */
809 static void
810 init(void)
811 {
812         PGconn     *con;
813         PGresult   *res;
814         static char *DDLs[] = {
815                 "drop table if exists branches",
816                 "create table branches(bid int not null,bbalance int,filler char(88)) with (fillfactor=%d)",
817                 "drop table if exists tellers",
818                 "create table tellers(tid int not null,bid int,tbalance int,filler char(84)) with (fillfactor=%d)",
819                 "drop table if exists accounts",
820                 "create table accounts(aid int not null,bid int,abalance int,filler char(84)) with (fillfactor=%d)",
821                 "drop table if exists history",
822         "create table history(tid int,bid int,aid int,delta int,mtime timestamp,filler char(22))"};
823         static char *DDLAFTERs[] = {
824                 "alter table branches add primary key (bid)",
825                 "alter table tellers add primary key (tid)",
826         "alter table accounts add primary key (aid)"};
827
828
829         char            sql[256];
830
831         int                     i;
832
833         if ((con = doConnect()) == NULL)
834                 exit(1);
835
836         for (i = 0; i < lengthof(DDLs); i++)
837         {
838                 /*
839                  * set fillfactor for branches, tellers and accounts tables
840                  */
841                 if ((strstr(DDLs[i], "create table branches") == DDLs[i]) ||
842                         (strstr(DDLs[i], "create table tellers") == DDLs[i]) ||
843                         (strstr(DDLs[i], "create table accounts") == DDLs[i]))
844                 {
845                         char            ddl_stmt[128];
846
847                         snprintf(ddl_stmt, 128, DDLs[i], fillfactor);
848                         executeStatement(con, ddl_stmt);
849                         continue;
850                 }
851                 else
852                         executeStatement(con, DDLs[i]);
853         }
854
855         executeStatement(con, "begin");
856
857         for (i = 0; i < nbranches * scale; i++)
858         {
859                 snprintf(sql, 256, "insert into branches(bid,bbalance) values(%d,0)", i + 1);
860                 executeStatement(con, sql);
861         }
862
863         for (i = 0; i < ntellers * scale; i++)
864         {
865                 snprintf(sql, 256, "insert into tellers(tid,bid,tbalance) values (%d,%d,0)"
866                                  ,i + 1, i / ntellers + 1);
867                 executeStatement(con, sql);
868         }
869
870         executeStatement(con, "commit");
871
872         /*
873          * fill the accounts table with some data
874          */
875         fprintf(stderr, "creating tables...\n");
876
877         executeStatement(con, "begin");
878         executeStatement(con, "truncate accounts");
879
880         res = PQexec(con, "copy accounts from stdin");
881         if (PQresultStatus(res) != PGRES_COPY_IN)
882         {
883                 fprintf(stderr, "%s", PQerrorMessage(con));
884                 exit(1);
885         }
886         PQclear(res);
887
888         for (i = 0; i < naccounts * scale; i++)
889         {
890                 int                     j = i + 1;
891
892                 snprintf(sql, 256, "%d\t%d\t%d\t\n", j, i / naccounts + 1, 0);
893                 if (PQputline(con, sql))
894                 {
895                         fprintf(stderr, "PQputline failed\n");
896                         exit(1);
897                 }
898
899                 if (j % 10000 == 0)
900                         fprintf(stderr, "%d tuples done.\n", j);
901         }
902         if (PQputline(con, "\\.\n"))
903         {
904                 fprintf(stderr, "very last PQputline failed\n");
905                 exit(1);
906         }
907         if (PQendcopy(con))
908         {
909                 fprintf(stderr, "PQendcopy failed\n");
910                 exit(1);
911         }
912         executeStatement(con, "commit");
913
914         /*
915          * create indexes
916          */
917         fprintf(stderr, "set primary key...\n");
918         for (i = 0; i < lengthof(DDLAFTERs); i++)
919                 executeStatement(con, DDLAFTERs[i]);
920
921         /* vacuum */
922         fprintf(stderr, "vacuum...");
923         executeStatement(con, "vacuum analyze");
924
925         fprintf(stderr, "done.\n");
926         PQfinish(con);
927 }
928
929 static Command *
930 process_commands(char *buf)
931 {
932         const char      delim[] = " \f\n\r\t\v";
933
934         Command    *my_commands;
935         int                     j;
936         char       *p,
937                            *tok;
938
939         if ((p = strchr(buf, '\n')) != NULL)
940                 *p = '\0';
941
942         p = buf;
943         while (isspace((unsigned char) *p))
944                 p++;
945
946         if (*p == '\0' || strncmp(p, "--", 2) == 0)
947         {
948                 return NULL;
949         }
950
951         my_commands = (Command *) malloc(sizeof(Command));
952         if (my_commands == NULL)
953         {
954                 return NULL;
955         }
956
957         my_commands->argc = 0;
958
959         if (*p == '\\')
960         {
961                 my_commands->type = META_COMMAND;
962
963                 j = 0;
964                 tok = strtok(++p, delim);
965
966                 while (tok != NULL)
967                 {
968                         if ((my_commands->argv[j] = strdup(tok)) == NULL)
969                                 return NULL;
970
971                         my_commands->argc++;
972
973                         j++;
974                         tok = strtok(NULL, delim);
975                 }
976
977                 if (pg_strcasecmp(my_commands->argv[0], "setrandom") == 0)
978                 {
979                         if (my_commands->argc < 4)
980                         {
981                                 fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
982                                 return NULL;
983                         }
984
985                         for (j = 4; j < my_commands->argc; j++)
986                                 fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
987                                                 my_commands->argv[0], my_commands->argv[j]);
988                 }
989                 else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
990                 {
991                         if (my_commands->argc < 3)
992                         {
993                                 fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
994                                 return NULL;
995                         }
996
997                         for (j = my_commands->argc < 5 ? 3 : 5; j < my_commands->argc; j++)
998                                 fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
999                                                 my_commands->argv[0], my_commands->argv[j]);
1000                 }
1001                 else if (pg_strcasecmp(my_commands->argv[0], "sleep") == 0)
1002                 {
1003                         if (my_commands->argc < 2)
1004                         {
1005                                 fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
1006                                 return NULL;
1007                         }
1008
1009                         if (my_commands->argc >= 3)
1010                         {
1011                                 if (pg_strcasecmp(my_commands->argv[2], "us") != 0 &&
1012                                         pg_strcasecmp(my_commands->argv[2], "ms") != 0 &&
1013                                         pg_strcasecmp(my_commands->argv[2], "s"))
1014                                 {
1015                                         fprintf(stderr, "%s: unknown time unit '%s' - must be us, ms or s\n",
1016                                                         my_commands->argv[0], my_commands->argv[2]);
1017                                         return NULL;
1018                                 }
1019                         }
1020
1021                         for (j = 3; j < my_commands->argc; j++)
1022                                 fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
1023                                                 my_commands->argv[0], my_commands->argv[j]);
1024                 }
1025                 else
1026                 {
1027                         fprintf(stderr, "Invalid command %s\n", my_commands->argv[0]);
1028                         return NULL;
1029                 }
1030         }
1031         else
1032         {
1033                 my_commands->type = SQL_COMMAND;
1034
1035                 if ((my_commands->argv[0] = strdup(p)) == NULL)
1036                         return NULL;
1037
1038                 my_commands->argc++;
1039         }
1040
1041         return my_commands;
1042 }
1043
1044 static int
1045 process_file(char *filename)
1046 {
1047 #define COMMANDS_ALLOC_NUM 128
1048
1049         Command   **my_commands;
1050         FILE       *fd;
1051         int                     lineno;
1052         char            buf[BUFSIZ];
1053         int                     alloc_num;
1054
1055         if (num_files >= MAX_FILES)
1056         {
1057                 fprintf(stderr, "Up to only %d SQL files are allowed\n", MAX_FILES);
1058                 exit(1);
1059         }
1060
1061         alloc_num = COMMANDS_ALLOC_NUM;
1062         my_commands = (Command **) malloc(sizeof(Command *) * alloc_num);
1063         if (my_commands == NULL)
1064                 return false;
1065
1066         if (strcmp(filename, "-") == 0)
1067                 fd = stdin;
1068         else if ((fd = fopen(filename, "r")) == NULL)
1069         {
1070                 fprintf(stderr, "%s: %s\n", filename, strerror(errno));
1071                 return false;
1072         }
1073
1074         lineno = 0;
1075
1076         while (fgets(buf, sizeof(buf), fd) != NULL)
1077         {
1078                 Command    *commands;
1079                 int                     i;
1080
1081                 i = 0;
1082                 while (isspace((unsigned char) buf[i]))
1083                         i++;
1084
1085                 if (buf[i] != '\0' && strncmp(&buf[i], "--", 2) != 0)
1086                 {
1087                         commands = process_commands(&buf[i]);
1088                         if (commands == NULL)
1089                         {
1090                                 fclose(fd);
1091                                 return false;
1092                         }
1093                 }
1094                 else
1095                         continue;
1096
1097                 my_commands[lineno] = commands;
1098                 lineno++;
1099
1100                 if (lineno >= alloc_num)
1101                 {
1102                         alloc_num += COMMANDS_ALLOC_NUM;
1103                         my_commands = realloc(my_commands, sizeof(Command *) * alloc_num);
1104                         if (my_commands == NULL)
1105                         {
1106                                 fclose(fd);
1107                                 return false;
1108                         }
1109                 }
1110         }
1111         fclose(fd);
1112
1113         my_commands[lineno] = NULL;
1114
1115         sql_files[num_files++] = my_commands;
1116
1117         return true;
1118 }
1119
1120 static Command **
1121 process_builtin(char *tb)
1122 {
1123 #define COMMANDS_ALLOC_NUM 128
1124
1125         Command   **my_commands;
1126         int                     lineno;
1127         char            buf[BUFSIZ];
1128         int                     alloc_num;
1129
1130         if (*tb == '\0')
1131                 return NULL;
1132
1133         alloc_num = COMMANDS_ALLOC_NUM;
1134         my_commands = (Command **) malloc(sizeof(Command *) * alloc_num);
1135         if (my_commands == NULL)
1136                 return NULL;
1137
1138         lineno = 0;
1139
1140         for (;;)
1141         {
1142                 char       *p;
1143                 Command    *commands;
1144
1145                 p = buf;
1146                 while (*tb && *tb != '\n')
1147                         *p++ = *tb++;
1148
1149                 if (*tb == '\0')
1150                         break;
1151
1152                 if (*tb == '\n')
1153                         tb++;
1154
1155                 *p = '\0';
1156
1157                 commands = process_commands(buf);
1158                 if (commands == NULL)
1159                 {
1160                         return NULL;
1161                 }
1162
1163                 my_commands[lineno] = commands;
1164                 lineno++;
1165
1166                 if (lineno >= alloc_num)
1167                 {
1168                         alloc_num += COMMANDS_ALLOC_NUM;
1169                         my_commands = realloc(my_commands, sizeof(Command *) * alloc_num);
1170                         if (my_commands == NULL)
1171                         {
1172                                 return NULL;
1173                         }
1174                 }
1175         }
1176
1177         my_commands[lineno] = NULL;
1178
1179         return my_commands;
1180 }
1181
1182 /* print out results */
1183 static void
1184 printResults(
1185                          int ttype, CState * state,
1186                          struct timeval * tv1, struct timeval * tv2,
1187                          struct timeval * tv3)
1188 {
1189         double          t1,
1190                                 t2;
1191         int                     i;
1192         int                     normal_xacts = 0;
1193         char       *s;
1194
1195         for (i = 0; i < nclients; i++)
1196                 normal_xacts += state[i].cnt;
1197
1198         t1 = (tv3->tv_sec - tv1->tv_sec) * 1000000.0 + (tv3->tv_usec - tv1->tv_usec);
1199         t1 = normal_xacts * 1000000.0 / t1;
1200
1201         t2 = (tv3->tv_sec - tv2->tv_sec) * 1000000.0 + (tv3->tv_usec - tv2->tv_usec);
1202         t2 = normal_xacts * 1000000.0 / t2;
1203
1204         if (ttype == 0)
1205                 s = "TPC-B (sort of)";
1206         else if (ttype == 2)
1207                 s = "Update only accounts";
1208         else if (ttype == 1)
1209                 s = "SELECT only";
1210         else
1211                 s = "Custom query";
1212
1213         printf("transaction type: %s\n", s);
1214         printf("scaling factor: %d\n", scale);
1215         printf("number of clients: %d\n", nclients);
1216         printf("number of transactions per client: %d\n", nxacts);
1217         printf("number of transactions actually processed: %d/%d\n", normal_xacts, nxacts * nclients);
1218         printf("tps = %f (including connections establishing)\n", t1);
1219         printf("tps = %f (excluding connections establishing)\n", t2);
1220 }
1221
1222
1223 int
1224 main(int argc, char **argv)
1225 {
1226         int                     c;
1227         int                     is_init_mode = 0;               /* initialize mode? */
1228         int                     is_no_vacuum = 0;               /* no vacuum at all before testing? */
1229         int                     do_vacuum_accounts = 0; /* do vacuum accounts before testing? */
1230         int                     debug = 0;              /* debug flag */
1231         int                     ttype = 0;              /* transaction type. 0: TPC-B, 1: SELECT only,
1232                                                                  * 2: skip update of branches and tellers */
1233         char       *filename = NULL;
1234
1235         CState     *state;                      /* status of clients */
1236
1237         struct timeval tv1;                     /* start up time */
1238         struct timeval tv2;                     /* after establishing all connections to the
1239                                                                  * backend */
1240         struct timeval tv3;                     /* end time */
1241
1242         int                     i;
1243
1244         fd_set          input_mask;
1245         int                     nsocks;                 /* return from select(2) */
1246         int                     maxsock;                /* max socket number to be waited */
1247         struct timeval now;
1248         struct timeval timeout;
1249         int                     min_usec;
1250
1251 #ifdef HAVE_GETRLIMIT
1252         struct rlimit rlim;
1253 #endif
1254
1255         PGconn     *con;
1256         PGresult   *res;
1257         char       *env;
1258
1259         char            val[64];
1260
1261 #ifdef WIN32
1262         /* stderr is buffered on Win32. */
1263         setvbuf(stderr, NULL, _IONBF, 0);
1264 #endif
1265
1266         if ((env = getenv("PGHOST")) != NULL && *env != '\0')
1267                 pghost = env;
1268         if ((env = getenv("PGPORT")) != NULL && *env != '\0')
1269                 pgport = env;
1270         else if ((env = getenv("PGUSER")) != NULL && *env != '\0')
1271                 login = env;
1272
1273         state = (CState *) malloc(sizeof(CState));
1274         if (state == NULL)
1275         {
1276                 fprintf(stderr, "Couldn't allocate memory for state\n");
1277                 exit(1);
1278         }
1279
1280         memset(state, 0, sizeof(*state));
1281
1282         while ((c = getopt(argc, argv, "ih:nvp:dc:t:s:U:CNSlf:D:F:")) != -1)
1283         {
1284                 switch (c)
1285                 {
1286                         case 'i':
1287                                 is_init_mode++;
1288                                 break;
1289                         case 'h':
1290                                 pghost = optarg;
1291                                 break;
1292                         case 'n':
1293                                 is_no_vacuum++;
1294                                 break;
1295                         case 'v':
1296                                 do_vacuum_accounts++;
1297                                 break;
1298                         case 'p':
1299                                 pgport = optarg;
1300                                 break;
1301                         case 'd':
1302                                 debug++;
1303                                 break;
1304                         case 'S':
1305                                 ttype = 1;
1306                                 break;
1307                         case 'N':
1308                                 ttype = 2;
1309                                 break;
1310                         case 'c':
1311                                 nclients = atoi(optarg);
1312                                 if (nclients <= 0 || nclients > MAXCLIENTS)
1313                                 {
1314                                         fprintf(stderr, "invalid number of clients: %d\n", nclients);
1315                                         exit(1);
1316                                 }
1317 #ifdef HAVE_GETRLIMIT
1318 #ifdef RLIMIT_NOFILE                    /* most platforms use RLIMIT_NOFILE */
1319                                 if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
1320 #else                                                   /* but BSD doesn't ... */
1321                                 if (getrlimit(RLIMIT_OFILE, &rlim) == -1)
1322 #endif   /* RLIMIT_NOFILE */
1323                                 {
1324                                         fprintf(stderr, "getrlimit failed: %s\n", strerror(errno));
1325                                         exit(1);
1326                                 }
1327                                 if (rlim.rlim_cur <= (nclients + 2))
1328                                 {
1329                                         fprintf(stderr, "You need at least %d open files but you are only allowed to use %ld.\n", nclients + 2, (long) rlim.rlim_cur);
1330                                         fprintf(stderr, "Use limit/ulimit to increase the limit before using pgbench.\n");
1331                                         exit(1);
1332                                 }
1333 #endif   /* HAVE_GETRLIMIT */
1334                                 break;
1335                         case 'C':
1336                                 is_connect = 1;
1337                                 break;
1338                         case 's':
1339                                 scale = atoi(optarg);
1340                                 if (scale <= 0)
1341                                 {
1342                                         fprintf(stderr, "invalid scaling factor: %d\n", scale);
1343                                         exit(1);
1344                                 }
1345                                 break;
1346                         case 't':
1347                                 nxacts = atoi(optarg);
1348                                 if (nxacts <= 0)
1349                                 {
1350                                         fprintf(stderr, "invalid number of transactions: %d\n", nxacts);
1351                                         exit(1);
1352                                 }
1353                                 break;
1354                         case 'U':
1355                                 login = optarg;
1356                                 break;
1357                         case 'l':
1358                                 use_log = true;
1359                                 break;
1360                         case 'f':
1361                                 ttype = 3;
1362                                 filename = optarg;
1363                                 if (process_file(filename) == false || *sql_files[num_files - 1] == NULL)
1364                                         exit(1);
1365                                 break;
1366                         case 'D':
1367                                 {
1368                                         char       *p;
1369
1370                                         if ((p = strchr(optarg, '=')) == NULL || p == optarg || *(p + 1) == '\0')
1371                                         {
1372                                                 fprintf(stderr, "invalid variable definition: %s\n", optarg);
1373                                                 exit(1);
1374                                         }
1375
1376                                         *p++ = '\0';
1377                                         if (putVariable(&state[0], optarg, p) == false)
1378                                         {
1379                                                 fprintf(stderr, "Couldn't allocate memory for variable\n");
1380                                                 exit(1);
1381                                         }
1382                                 }
1383                                 break;
1384                         case 'F':
1385                                 fillfactor = atoi(optarg);
1386                                 if ((fillfactor < 10) || (fillfactor > 100))
1387                                 {
1388                                         fprintf(stderr, "invalid fillfactor: %d\n", fillfactor);
1389                                         exit(1);
1390                                 }
1391                                 break;
1392                         default:
1393                                 usage();
1394                                 exit(1);
1395                                 break;
1396                 }
1397         }
1398
1399         if (argc > optind)
1400                 dbName = argv[optind];
1401         else
1402         {
1403                 if ((env = getenv("PGDATABASE")) != NULL && *env != '\0')
1404                         dbName = env;
1405                 else if (login != NULL && *login != '\0')
1406                         dbName = login;
1407                 else
1408                         dbName = "";
1409         }
1410
1411         if (is_init_mode)
1412         {
1413                 init();
1414                 exit(0);
1415         }
1416
1417         remains = nclients;
1418
1419         if (getVariable(&state[0], "scale") == NULL)
1420         {
1421                 snprintf(val, sizeof(val), "%d", scale);
1422                 if (putVariable(&state[0], "scale", val) == false)
1423                 {
1424                         fprintf(stderr, "Couldn't allocate memory for variable\n");
1425                         exit(1);
1426                 }
1427         }
1428
1429         if (nclients > 1)
1430         {
1431                 state = (CState *) realloc(state, sizeof(CState) * nclients);
1432                 if (state == NULL)
1433                 {
1434                         fprintf(stderr, "Couldn't allocate memory for state\n");
1435                         exit(1);
1436                 }
1437
1438                 memset(state + 1, 0, sizeof(*state) * (nclients - 1));
1439
1440                 snprintf(val, sizeof(val), "%d", scale);
1441
1442                 for (i = 1; i < nclients; i++)
1443                 {
1444                         int                     j;
1445
1446                         for (j = 0; j < state[0].nvariables; j++)
1447                         {
1448                                 if (putVariable(&state[i], state[0].variables[j].name, state[0].variables[j].value) == false)
1449                                 {
1450                                         fprintf(stderr, "Couldn't allocate memory for variable\n");
1451                                         exit(1);
1452                                 }
1453                         }
1454
1455                         if (putVariable(&state[i], "scale", val) == false)
1456                         {
1457                                 fprintf(stderr, "Couldn't allocate memory for variable\n");
1458                                 exit(1);
1459                         }
1460                 }
1461         }
1462
1463         if (use_log)
1464         {
1465                 char            logpath[64];
1466
1467                 snprintf(logpath, 64, "pgbench_log.%d", (int) getpid());
1468                 LOGFILE = fopen(logpath, "w");
1469
1470                 if (LOGFILE == NULL)
1471                 {
1472                         fprintf(stderr, "Couldn't open logfile \"%s\": %s", logpath, strerror(errno));
1473                         exit(1);
1474                 }
1475         }
1476
1477         if (debug)
1478         {
1479                 printf("pghost: %s pgport: %s nclients: %d nxacts: %d dbName: %s\n",
1480                            pghost, pgport, nclients, nxacts, dbName);
1481         }
1482
1483         /* opening connection... */
1484         con = doConnect();
1485         if (con == NULL)
1486                 exit(1);
1487
1488         if (PQstatus(con) == CONNECTION_BAD)
1489         {
1490                 fprintf(stderr, "Connection to database '%s' failed.\n", dbName);
1491                 fprintf(stderr, "%s", PQerrorMessage(con));
1492                 exit(1);
1493         }
1494
1495         if (ttype != 3)
1496         {
1497                 /*
1498                  * get the scaling factor that should be same as count(*) from
1499                  * branches if this is not a custom query
1500                  */
1501                 res = PQexec(con, "select count(*) from branches");
1502                 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1503                 {
1504                         fprintf(stderr, "%s", PQerrorMessage(con));
1505                         exit(1);
1506                 }
1507                 scale = atoi(PQgetvalue(res, 0, 0));
1508                 if (scale < 0)
1509                 {
1510                         fprintf(stderr, "count(*) from branches invalid (%d)\n", scale);
1511                         exit(1);
1512                 }
1513                 PQclear(res);
1514
1515                 snprintf(val, sizeof(val), "%d", scale);
1516                 if (putVariable(&state[0], "scale", val) == false)
1517                 {
1518                         fprintf(stderr, "Couldn't allocate memory for variable\n");
1519                         exit(1);
1520                 }
1521
1522                 if (nclients > 1)
1523                 {
1524                         for (i = 1; i < nclients; i++)
1525                         {
1526                                 if (putVariable(&state[i], "scale", val) == false)
1527                                 {
1528                                         fprintf(stderr, "Couldn't allocate memory for variable\n");
1529                                         exit(1);
1530                                 }
1531                         }
1532                 }
1533         }
1534
1535         if (!is_no_vacuum)
1536         {
1537                 fprintf(stderr, "starting vacuum...");
1538                 executeStatement(con, "vacuum branches");
1539                 executeStatement(con, "vacuum tellers");
1540                 executeStatement(con, "delete from history");
1541                 executeStatement(con, "vacuum history");
1542                 fprintf(stderr, "end.\n");
1543
1544                 if (do_vacuum_accounts)
1545                 {
1546                         fprintf(stderr, "starting vacuum accounts...");
1547                         executeStatement(con, "vacuum analyze accounts");
1548                         fprintf(stderr, "end.\n");
1549                 }
1550         }
1551         PQfinish(con);
1552
1553         /* set random seed */
1554         gettimeofday(&tv1, NULL);
1555         srandom((unsigned int) tv1.tv_usec);
1556
1557         /* get start up time */
1558         gettimeofday(&tv1, NULL);
1559
1560         if (is_connect == 0)
1561         {
1562                 /* make connections to the database */
1563                 for (i = 0; i < nclients; i++)
1564                 {
1565                         state[i].id = i;
1566                         if ((state[i].con = doConnect()) == NULL)
1567                                 exit(1);
1568                 }
1569         }
1570
1571         /* time after connections set up */
1572         gettimeofday(&tv2, NULL);
1573
1574         /* process bultin SQL scripts */
1575         switch (ttype)
1576         {
1577                 case 0:
1578                         sql_files[0] = process_builtin(tpc_b);
1579                         num_files = 1;
1580                         break;
1581
1582                 case 1:
1583                         sql_files[0] = process_builtin(select_only);
1584                         num_files = 1;
1585                         break;
1586
1587                 case 2:
1588                         sql_files[0] = process_builtin(simple_update);
1589                         num_files = 1;
1590                         break;
1591
1592                 default:
1593                         break;
1594         }
1595
1596         /* send start up queries in async manner */
1597         for (i = 0; i < nclients; i++)
1598         {
1599                 Command   **commands = sql_files[state[i].use_file];
1600                 int                     prev_ecnt = state[i].ecnt;
1601
1602                 state[i].use_file = getrand(0, num_files - 1);
1603                 doCustom(state, i, debug);
1604
1605                 if (state[i].ecnt > prev_ecnt && commands[state[i].state]->type == META_COMMAND)
1606                 {
1607                         fprintf(stderr, "Client %d aborted in state %d. Execution meta-command failed.\n", i, state[i].state);
1608                         remains--;                      /* I've aborted */
1609                         PQfinish(state[i].con);
1610                         state[i].con = NULL;
1611                 }
1612         }
1613
1614         for (;;)
1615         {
1616                 if (remains <= 0)
1617                 {                                               /* all done ? */
1618                         disconnect_all(state);
1619                         /* get end time */
1620                         gettimeofday(&tv3, NULL);
1621                         printResults(ttype, state, &tv1, &tv2, &tv3);
1622                         if (LOGFILE)
1623                                 fclose(LOGFILE);
1624                         exit(0);
1625                 }
1626
1627                 FD_ZERO(&input_mask);
1628
1629                 maxsock = -1;
1630                 min_usec = -1;
1631                 for (i = 0; i < nclients; i++)
1632                 {
1633                         Command   **commands = sql_files[state[i].use_file];
1634
1635                         if (state[i].sleeping)
1636                         {
1637                                 int                     this_usec;
1638                                 int                     sock = PQsocket(state[i].con);
1639
1640                                 if (min_usec < 0)
1641                                 {
1642                                         gettimeofday(&now, NULL);
1643                                         min_usec = 0;
1644                                 }
1645
1646                                 this_usec = (state[i].until.tv_sec - now.tv_sec) * 1000000 +
1647                                         state[i].until.tv_usec - now.tv_usec;
1648
1649                                 if (this_usec > 0 && (min_usec == 0 || this_usec < min_usec))
1650                                         min_usec = this_usec;
1651
1652                                 FD_SET(sock, &input_mask);
1653                                 if (maxsock < sock)
1654                                         maxsock = sock;
1655                         }
1656                         else if (state[i].con && commands[state[i].state]->type != META_COMMAND)
1657                         {
1658                                 int                     sock = PQsocket(state[i].con);
1659
1660                                 if (sock < 0)
1661                                 {
1662                                         disconnect_all(state);
1663                                         exit(1);
1664                                 }
1665                                 FD_SET(sock, &input_mask);
1666                                 if (maxsock < sock)
1667                                         maxsock = sock;
1668                         }
1669                 }
1670
1671                 if (maxsock != -1)
1672                 {
1673                         if (min_usec >= 0)
1674                         {
1675                                 timeout.tv_sec = min_usec / 1000000;
1676                                 timeout.tv_usec = min_usec % 1000000;
1677
1678                                 nsocks = select(maxsock + 1, &input_mask, (fd_set *) NULL,
1679                                                                 (fd_set *) NULL, &timeout);
1680                         }
1681                         else
1682                                 nsocks = select(maxsock + 1, &input_mask, (fd_set *) NULL,
1683                                                                 (fd_set *) NULL, (struct timeval *) NULL);
1684                         if (nsocks < 0)
1685                         {
1686                                 if (errno == EINTR)
1687                                         continue;
1688                                 /* must be something wrong */
1689                                 disconnect_all(state);
1690                                 fprintf(stderr, "select failed: %s\n", strerror(errno));
1691                                 exit(1);
1692                         }
1693 #ifdef NOT_USED
1694                         else if (nsocks == 0)
1695                         {                                       /* timeout */
1696                                 fprintf(stderr, "select timeout\n");
1697                                 for (i = 0; i < nclients; i++)
1698                                 {
1699                                         fprintf(stderr, "client %d:state %d cnt %d ecnt %d listen %d\n",
1700                                                         i, state[i].state, state[i].cnt, state[i].ecnt, state[i].listen);
1701                                 }
1702                                 exit(0);
1703                         }
1704 #endif
1705                 }
1706
1707                 /* ok, backend returns reply */
1708                 for (i = 0; i < nclients; i++)
1709                 {
1710                         Command   **commands = sql_files[state[i].use_file];
1711                         int                     prev_ecnt = state[i].ecnt;
1712
1713                         if (state[i].con && (FD_ISSET(PQsocket(state[i].con), &input_mask)
1714                                                   || commands[state[i].state]->type == META_COMMAND))
1715                         {
1716                                 doCustom(state, i, debug);
1717                         }
1718
1719                         if (state[i].ecnt > prev_ecnt && commands[state[i].state]->type == META_COMMAND)
1720                         {
1721                                 fprintf(stderr, "Client %d aborted in state %d. Execution meta-command failed.\n", i, state[i].state);
1722                                 remains--;              /* I've aborted */
1723                                 PQfinish(state[i].con);
1724                                 state[i].con = NULL;
1725                         }
1726                 }
1727         }
1728 }