|
4 | 4 | * A simple benchmark program for PostgreSQL
|
5 | 5 | * Originally written by Tatsuo Ishii and enhanced by many contributors.
|
6 | 6 | *
|
7 |
| - * $PostgreSQL: pgsql/contrib/pgbench/pgbench.c,v 1.92 2009/12/11 21:50:06 tgl Exp $ |
| 7 | + * $PostgreSQL: pgsql/contrib/pgbench/pgbench.c,v 1.93 2009/12/15 07:17:57 itagaki Exp $ |
8 | 8 | * Copyright (c) 2000-2009, PostgreSQL Global Development Group
|
9 | 9 | * ALL RIGHTS RESERVED;
|
10 | 10 | *
|
@@ -159,6 +159,7 @@ typedef struct
|
159 | 159 | } Variable;
|
160 | 160 |
|
161 | 161 | #define MAX_FILES 128 /* max number of SQL script files allowed */
|
| 162 | +#define SHELL_COMMAND_SIZE 256 /* maximum size allowed for shell command */ |
162 | 163 |
|
163 | 164 | /*
|
164 | 165 | * structures used in custom query mode
|
@@ -467,8 +468,8 @@ putVariable(CState *st, char *name, char *value)
|
467 | 468 | var->name = NULL;
|
468 | 469 | var->value = NULL;
|
469 | 470 |
|
470 |
| - if ((var->name = strdup(name)) == NULL |
471 |
| - || (var->value = strdup(value)) == NULL) |
| 471 | + if ((var->name = strdup(name)) == NULL || |
| 472 | + (var->value = strdup(value)) == NULL) |
472 | 473 | {
|
473 | 474 | free(var->name);
|
474 | 475 | free(var->value);
|
@@ -590,6 +591,114 @@ getQueryParams(CState *st, const Command *command, const char **params)
|
590 | 591 | params[i] = getVariable(st, command->argv[i + 1]);
|
591 | 592 | }
|
592 | 593 |
|
| 594 | +/* |
| 595 | + * Run a shell command. The result is assigned to the variable if not NULL. |
| 596 | + * Return true if succeeded, or false on error. |
| 597 | + */ |
| 598 | +static bool |
| 599 | +runShellCommand(CState *st, char *variable, char **argv, int argc) |
| 600 | +{ |
| 601 | + char command[SHELL_COMMAND_SIZE]; |
| 602 | + int i, |
| 603 | + len = 0; |
| 604 | + FILE *fp; |
| 605 | + char res[64]; |
| 606 | + char *endptr; |
| 607 | + int retval; |
| 608 | + |
| 609 | + /* |
| 610 | + * Join arguments with whilespace separaters. Arguments starting with |
| 611 | + * exactly one colon are treated as variables: |
| 612 | + * name - append a string "name" |
| 613 | + * :var - append a variable named 'var'. |
| 614 | + * ::name - append a string ":name" |
| 615 | + */ |
| 616 | + for (i = 0; i < argc; i++) |
| 617 | + { |
| 618 | + char *arg; |
| 619 | + int arglen; |
| 620 | + |
| 621 | + if (argv[i][0] != ':') |
| 622 | + { |
| 623 | + arg = argv[i]; /* a string literal */ |
| 624 | + } |
| 625 | + else if (argv[i][1] == ':') |
| 626 | + { |
| 627 | + arg = argv[i] + 1; /* a string literal starting with colons */ |
| 628 | + } |
| 629 | + else if ((arg = getVariable(st, argv[i] + 1)) == NULL) |
| 630 | + { |
| 631 | + fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[i]); |
| 632 | + return false; |
| 633 | + } |
| 634 | + |
| 635 | + arglen = strlen(arg); |
| 636 | + if (len + arglen + (i > 0 ? 1 : 0) >= SHELL_COMMAND_SIZE - 1) |
| 637 | + { |
| 638 | + fprintf(stderr, "%s: too long shell command\n", argv[0]); |
| 639 | + return false; |
| 640 | + } |
| 641 | + |
| 642 | + if (i > 0) |
| 643 | + command[len++] = ' '; |
| 644 | + memcpy(command + len, arg, arglen); |
| 645 | + len += arglen; |
| 646 | + } |
| 647 | + |
| 648 | + command[len] = '\0'; |
| 649 | + |
| 650 | + /* Fast path for non-assignment case */ |
| 651 | + if (variable == NULL) |
| 652 | + { |
| 653 | + if (system(command)) |
| 654 | + { |
| 655 | + if (!timer_exceeded) |
| 656 | + fprintf(stderr, "%s: cannot launch shell command\n", argv[0]); |
| 657 | + return false; |
| 658 | + } |
| 659 | + return true; |
| 660 | + } |
| 661 | + |
| 662 | + /* Execute the command with pipe and read the standard output. */ |
| 663 | + if ((fp = popen(command, "r")) == NULL) |
| 664 | + { |
| 665 | + fprintf(stderr, "%s: cannot launch shell command\n", argv[0]); |
| 666 | + return false; |
| 667 | + } |
| 668 | + if (fgets(res, sizeof(res), fp) == NULL) |
| 669 | + { |
| 670 | + if (!timer_exceeded) |
| 671 | + fprintf(stderr, "%s: cannot read the result\n", argv[0]); |
| 672 | + return false; |
| 673 | + } |
| 674 | + if (pclose(fp) < 0) |
| 675 | + { |
| 676 | + fprintf(stderr, "%s: cannot close shell command\n", argv[0]); |
| 677 | + return false; |
| 678 | + } |
| 679 | + |
| 680 | + /* Check whether the result is an integer and assign it to the variable */ |
| 681 | + retval = (int) strtol(res, &endptr, 10); |
| 682 | + while (*endptr != '\0' && isspace((unsigned char) *endptr)) |
| 683 | + endptr++; |
| 684 | + if (*res == '\0' || *endptr != '\0') |
| 685 | + { |
| 686 | + fprintf(stderr, "%s: must return an integer ('%s' returned)\n", argv[0], res); |
| 687 | + return false; |
| 688 | + } |
| 689 | + snprintf(res, sizeof(res), "%d", retval); |
| 690 | + if (!putVariable(st, variable, res)) |
| 691 | + { |
| 692 | + fprintf(stderr, "%s: out of memory\n", argv[0]); |
| 693 | + return false; |
| 694 | + } |
| 695 | + |
| 696 | +#ifdef DEBUG |
| 697 | + printf("shell parameter name: %s, value: %s\n", argv[1], res); |
| 698 | +#endif |
| 699 | + return true; |
| 700 | +} |
| 701 | + |
593 | 702 | #define MAX_PREPARE_NAME 32
|
594 | 703 | static void
|
595 | 704 | preparedStatementName(char *buffer, int file, int state)
|
@@ -992,7 +1101,34 @@ doCustom(CState *st, instr_time *conn_time)
|
992 | 1101 |
|
993 | 1102 | st->listen = 1;
|
994 | 1103 | }
|
| 1104 | + else if (pg_strcasecmp(argv[0], "setshell") == 0) |
| 1105 | + { |
| 1106 | + bool ret = runShellCommand(st, argv[1], argv + 2, argc - 2); |
995 | 1107 |
|
| 1108 | + if (timer_exceeded) /* timeout */ |
| 1109 | + return clientDone(st, true); |
| 1110 | + else if (!ret) /* on error */ |
| 1111 | + { |
| 1112 | + st->ecnt++; |
| 1113 | + return true; |
| 1114 | + } |
| 1115 | + else /* succeeded */ |
| 1116 | + st->listen = 1; |
| 1117 | + } |
| 1118 | + else if (pg_strcasecmp(argv[0], "shell") == 0) |
| 1119 | + { |
| 1120 | + bool ret = runShellCommand(st, NULL, argv + 1, argc - 1); |
| 1121 | + |
| 1122 | + if (timer_exceeded) /* timeout */ |
| 1123 | + return clientDone(st, true); |
| 1124 | + else if (!ret) /* on error */ |
| 1125 | + { |
| 1126 | + st->ecnt++; |
| 1127 | + return true; |
| 1128 | + } |
| 1129 | + else /* succeeded */ |
| 1130 | + st->listen = 1; |
| 1131 | + } |
996 | 1132 | goto top;
|
997 | 1133 | }
|
998 | 1134 |
|
@@ -1081,8 +1217,8 @@ init(void)
|
1081 | 1217 |
|
1082 | 1218 | for (i = 0; i < ntellers * scale; i++)
|
1083 | 1219 | {
|
1084 |
| - snprintf(sql, 256, "insert into pgbench_tellers(tid,bid,tbalance) values (%d,%d,0)" |
1085 |
| - ,i + 1, i / ntellers + 1); |
| 1220 | + snprintf(sql, 256, "insert into pgbench_tellers(tid,bid,tbalance) values (%d,%d,0)", |
| 1221 | + i + 1, i / ntellers + 1); |
1086 | 1222 | executeStatement(con, sql);
|
1087 | 1223 | }
|
1088 | 1224 |
|
@@ -1313,6 +1449,22 @@ process_commands(char *buf)
|
1313 | 1449 | fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
|
1314 | 1450 | my_commands->argv[0], my_commands->argv[j]);
|
1315 | 1451 | }
|
| 1452 | + else if (pg_strcasecmp(my_commands->argv[0], "setshell") == 0) |
| 1453 | + { |
| 1454 | + if (my_commands->argc < 3) |
| 1455 | + { |
| 1456 | + fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]); |
| 1457 | + return NULL; |
| 1458 | + } |
| 1459 | + } |
| 1460 | + else if (pg_strcasecmp(my_commands->argv[0], "shell") == 0) |
| 1461 | + { |
| 1462 | + if (my_commands->argc < 1) |
| 1463 | + { |
| 1464 | + fprintf(stderr, "%s: missing command\n", my_commands->argv[0]); |
| 1465 | + return NULL; |
| 1466 | + } |
| 1467 | + } |
1316 | 1468 | else
|
1317 | 1469 | {
|
1318 | 1470 | fprintf(stderr, "Invalid command %s\n", my_commands->argv[0]);
|
|
0 commit comments