PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pgbench.c File Reference
#include "postgres_fe.h"
#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#include <signal.h>
#include <time.h>
#include <sys/resource.h>
#include <sys/select.h>
#include "catalog/pg_class_d.h"
#include "common/int.h"
#include "common/logging.h"
#include "common/pg_prng.h"
#include "common/string.h"
#include "common/username.h"
#include "fe_utils/cancel.h"
#include "fe_utils/conditional.h"
#include "fe_utils/option_utils.h"
#include "fe_utils/string_utils.h"
#include "getopt_long.h"
#include "libpq-fe.h"
#include "pgbench.h"
#include "port/pg_bitutils.h"
#include "portability/instr_time.h"
#include "port/pg_pthread.h"
Include dependency graph for pgbench.c:

Go to the source code of this file.

Data Structures

struct  socket_set
 
struct  Variable
 
struct  Variables
 
struct  SimpleStats
 
struct  StatsData
 
struct  CState
 
struct  TState
 
struct  Command
 
struct  ParsedScript
 
struct  BuiltinScript
 

Macros

#define POLL_USING_SELECT
 
#define M_PI   3.14159265358979323846
 
#define ERRCODE_T_R_SERIALIZATION_FAILURE   "40001"
 
#define ERRCODE_T_R_DEADLOCK_DETECTED   "40P01"
 
#define ERRCODE_UNDEFINED_TABLE   "42P01"
 
#define FNV_PRIME   UINT64CONST(0x100000001b3)
 
#define FNV_OFFSET_BASIS   UINT64CONST(0xcbf29ce484222325)
 
#define MM2_MUL   UINT64CONST(0xc6a4a7935bd1e995)
 
#define MM2_MUL_TIMES_8   UINT64CONST(0x35253c9ade8f4ca8)
 
#define MM2_ROT   47
 
#define SOCKET_WAIT_METHOD   "select"
 
#define THREAD_T   pthread_t
 
#define THREAD_FUNC_RETURN_TYPE   void *
 
#define THREAD_FUNC_RETURN   return NULL
 
#define THREAD_FUNC_CC
 
#define THREAD_CREATE(handle, function, arg)    pthread_create((handle), NULL, (function), (arg))
 
#define THREAD_JOIN(handle)    pthread_join((handle), NULL)
 
#define THREAD_BARRIER_T   pthread_barrier_t
 
#define THREAD_BARRIER_INIT(barrier, n)    pthread_barrier_init((barrier), NULL, (n))
 
#define THREAD_BARRIER_WAIT(barrier)   pthread_barrier_wait((barrier))
 
#define THREAD_BARRIER_DESTROY(barrier)   pthread_barrier_destroy((barrier))
 
#define DEFAULT_INIT_STEPS   "dtgvp" /* default -I setting */
 
#define ALL_INIT_STEPS   "dtgGvpf" /* all possible steps */
 
#define LOG_STEP_SECONDS   5 /* seconds between log messages */
 
#define DEFAULT_NXACTS   10 /* default nxacts */
 
#define MIN_GAUSSIAN_PARAM   2.0 /* minimum parameter for gauss */
 
#define MIN_ZIPFIAN_PARAM   1.001 /* minimum parameter for zipfian */
 
#define MAX_ZIPFIAN_PARAM   1000.0 /* maximum parameter for zipfian */
 
#define nbranches
 
#define ntellers   10
 
#define naccounts   100000
 
#define SCALE_32BIT_THRESHOLD   20000
 
#define WSEP   '@' /* weight separator */
 
#define VARIABLES_ALLOC_MARGIN   8
 
#define MAX_SCRIPTS   128 /* max number of SQL scripts allowed */
 
#define SHELL_COMMAND_SIZE   256 /* maximum size allowed for shell command */
 
#define SQL_COMMAND   1
 
#define META_COMMAND   2
 
#define MAX_ARGS   256
 
#define PG_TIME_GET_DOUBLE(t)   (0.000001 * (t))
 
#define PARAMS_ARRAY_SIZE   7
 
#define MAX_FARGS   16
 
#define COMMANDS_ALLOC_NUM   128
 

Typedefs

typedef struct socket_set socket_set
 
typedef struct SimpleStats SimpleStats
 
typedef int64 pg_time_usec_t
 
typedef struct StatsData StatsData
 
typedef enum EStatus EStatus
 
typedef enum TStatus TStatus
 
typedef enum MetaCommand MetaCommand
 
typedef enum QueryMode QueryMode
 
typedef struct Command Command
 
typedef struct ParsedScript ParsedScript
 
typedef struct BuiltinScript BuiltinScript
 
typedef void(* initRowMethod) (PQExpBufferData *sql, int64 curr)
 

Enumerations

enum  partition_method_t { PART_NONE , PART_RANGE , PART_HASH }
 
enum  EStatus {
  ESTATUS_NO_ERROR = 0 , ESTATUS_META_COMMAND_ERROR , ESTATUS_CONN_ERROR , ESTATUS_SERIALIZATION_ERROR ,
  ESTATUS_DEADLOCK_ERROR , ESTATUS_OTHER_SQL_ERROR
}
 
enum  TStatus { TSTATUS_IDLE , TSTATUS_IN_BLOCK , TSTATUS_CONN_ERROR , TSTATUS_OTHER_ERROR }
 
enum  ConnectionStateEnum {
  CSTATE_CHOOSE_SCRIPT , CSTATE_START_TX , CSTATE_PREPARE_THROTTLE , CSTATE_THROTTLE ,
  CSTATE_START_COMMAND , CSTATE_WAIT_RESULT , CSTATE_SLEEP , CSTATE_END_COMMAND ,
  CSTATE_SKIP_COMMAND , CSTATE_ERROR , CSTATE_WAIT_ROLLBACK_RESULT , CSTATE_RETRY ,
  CSTATE_FAILURE , CSTATE_END_TX , CSTATE_ABORTED , CSTATE_FINISHED
}
 
enum  MetaCommand {
  META_NONE , META_SET , META_SETSHELL , META_SHELL ,
  META_SLEEP , META_GSET , META_ASET , META_IF ,
  META_ELIF , META_ELSE , META_ENDIF , META_STARTPIPELINE ,
  META_SYNCPIPELINE , META_ENDPIPELINE
}
 
enum  QueryMode { QUERY_SIMPLE , QUERY_EXTENDED , QUERY_PREPARED , NUM_QUERYMODE }
 

Functions

static void setNullValue (PgBenchValue *pv)
 
static void setBoolValue (PgBenchValue *pv, bool bval)
 
static void setIntValue (PgBenchValue *pv, int64 ival)
 
static void setDoubleValue (PgBenchValue *pv, double dval)
 
static bool evaluateExpr (CState *st, PgBenchExpr *expr, PgBenchValue *retval)
 
static ConnectionStateEnum executeMetaCommand (CState *st, pg_time_usec_t *now)
 
static void doLog (TState *thread, CState *st, StatsData *agg, bool skipped, double latency, double lag)
 
static void processXactStats (TState *thread, CState *st, pg_time_usec_t *now, bool skipped, StatsData *agg)
 
static void addScript (const ParsedScript *script)
 
static THREAD_FUNC_RETURN_TYPE THREAD_FUNC_CC threadRun (void *arg)
 
static void finishCon (CState *st)
 
static void setalarm (int seconds)
 
static socket_setalloc_socket_set (int count)
 
static void free_socket_set (socket_set *sa)
 
static void clear_socket_set (socket_set *sa)
 
static void add_socket_to_set (socket_set *sa, int fd, int idx)
 
static int wait_on_socket_set (socket_set *sa, int64 usecs)
 
static bool socket_has_input (socket_set *sa, int fd, int idx)
 
static char get_table_relkind (PGconn *con, const char *table)
 
static pg_time_usec_t pg_time_now (void)
 
static void pg_time_now_lazy (pg_time_usec_t *now)
 
static void usage (void)
 
static bool is_an_int (const char *str)
 
bool strtoint64 (const char *str, bool errorOK, int64 *result)
 
bool strtodouble (const char *str, bool errorOK, double *dv)
 
static void initRandomState (pg_prng_state *state)
 
static int64 getrand (pg_prng_state *state, int64 min, int64 max)
 
static int64 getExponentialRand (pg_prng_state *state, int64 min, int64 max, double parameter)
 
static int64 getGaussianRand (pg_prng_state *state, int64 min, int64 max, double parameter)
 
static int64 getPoissonRand (pg_prng_state *state, double center)
 
static int64 computeIterativeZipfian (pg_prng_state *state, int64 n, double s)
 
static int64 getZipfianRand (pg_prng_state *state, int64 min, int64 max, double s)
 
static int64 getHashFnv1a (int64 val, uint64 seed)
 
static int64 getHashMurmur2 (int64 val, uint64 seed)
 
static int64 permute (const int64 val, const int64 isize, const int64 seed)
 
static void initSimpleStats (SimpleStats *ss)
 
static void addToSimpleStats (SimpleStats *ss, double val)
 
static void mergeSimpleStats (SimpleStats *acc, SimpleStats *ss)
 
static void initStats (StatsData *sd, pg_time_usec_t start)
 
static void accumStats (StatsData *stats, bool skipped, double lat, double lag, EStatus estatus, int64 tries)
 
static void executeStatement (PGconn *con, const char *sql)
 
static void tryExecuteStatement (PGconn *con, const char *sql)
 
static PGconndoConnect (void)
 
static int compareVariableNames (const void *v1, const void *v2)
 
static VariablelookupVariable (Variables *variables, char *name)
 
static chargetVariable (Variables *variables, char *name)
 
static bool makeVariableValue (Variable *var)
 
static bool valid_variable_name (const char *name)
 
static void enlargeVariables (Variables *variables, int needed)
 
static VariablelookupCreateVariable (Variables *variables, const char *context, char *name)
 
static bool putVariable (Variables *variables, const char *context, char *name, const char *value)
 
static bool putVariableValue (Variables *variables, const char *context, char *name, const PgBenchValue *value)
 
static bool putVariableInt (Variables *variables, const char *context, char *name, int64 value)
 
static charparseVariable (const char *sql, int *eaten)
 
static charreplaceVariable (char **sql, char *param, int len, char *value)
 
static charassignVariables (Variables *variables, char *sql)
 
static void getQueryParams (Variables *variables, const Command *command, const char **params)
 
static charvalueTypeName (PgBenchValue *pval)
 
static bool coerceToBool (PgBenchValue *pval, bool *bval)
 
static bool valueTruth (PgBenchValue *pval)
 
static bool coerceToInt (PgBenchValue *pval, int64 *ival)
 
static bool coerceToDouble (PgBenchValue *pval, double *dval)
 
static bool isLazyFunc (PgBenchFunction func)
 
static bool evalLazyFunc (CState *st, PgBenchFunction func, PgBenchExprLink *args, PgBenchValue *retval)
 
static bool evalStandardFunc (CState *st, PgBenchFunction func, PgBenchExprLink *args, PgBenchValue *retval)
 
static bool evalFunc (CState *st, PgBenchFunction func, PgBenchExprLink *args, PgBenchValue *retval)
 
static MetaCommand getMetaCommand (const char *cmd)
 
static bool runShellCommand (Variables *variables, char *variable, char **argv, int argc)
 
static void commandFailed (CState *st, const char *cmd, const char *message)
 
static void commandError (CState *st, const char *message)
 
static int chooseScript (TState *thread)
 
static void allocCStatePrepared (CState *st)
 
static void prepareCommand (CState *st, int command_num)
 
static void prepareCommandsInPipeline (CState *st)
 
static bool sendCommand (CState *st, Command *command)
 
static void discardAvailableResults (CState *st)
 
static EStatus getSQLErrorStatus (CState *st, const char *sqlState)
 
static bool canRetryError (EStatus estatus)
 
static bool canContinueOnError (EStatus estatus)
 
static bool readCommandResponse (CState *st, MetaCommand meta, char *varprefix)
 
static bool evaluateSleep (Variables *variables, int argc, char **argv, int *usecs)
 
static bool doRetry (CState *st, pg_time_usec_t *now)
 
static int discardUntilSync (CState *st)
 
static TStatus getTransactionStatus (PGconn *con)
 
static void printVerboseErrorMessages (CState *st, pg_time_usec_t *now, bool is_retry)
 
static void advanceConnectionState (TState *thread, CState *st, StatsData *agg)
 
static int64 getFailures (const StatsData *stats)
 
static const chargetResultString (bool skipped, EStatus estatus)
 
static void disconnect_all (CState *state, int length)
 
static void initDropTables (PGconn *con)
 
static void createPartitions (PGconn *con)
 
static void initCreateTables (PGconn *con)
 
static void initTruncateTables (PGconn *con)
 
static void initBranch (PQExpBufferData *sql, int64 curr)
 
static void initTeller (PQExpBufferData *sql, int64 curr)
 
static void initAccount (PQExpBufferData *sql, int64 curr)
 
static void initPopulateTable (PGconn *con, const char *table, int64 base, initRowMethod init_row)
 
static void initGenerateDataClientSide (PGconn *con)
 
static void initGenerateDataServerSide (PGconn *con)
 
static void initVacuum (PGconn *con)
 
static void initCreatePKeys (PGconn *con)
 
static void initCreateFKeys (PGconn *con)
 
static void checkInitSteps (const char *initialize_steps)
 
static void runInitSteps (const char *initialize_steps)
 
static void GetTableInfo (PGconn *con, bool scale_given)
 
static bool parseQuery (Command *cmd)
 
void syntax_error (const char *source, int lineno, const char *line, const char *command, const char *msg, const char *more, int column)
 
static charskip_sql_comments (char *sql_command)
 
static Commandcreate_sql_command (PQExpBuffer buf)
 
static void free_command (Command *command)
 
static void postprocess_sql_command (Command *my_command)
 
static Commandprocess_backslash_command (PsqlScanState sstate, const char *source, int lineno, int start_offset)
 
static void ConditionError (const char *desc, int cmdn, const char *msg)
 
static void CheckConditional (const ParsedScript *ps)
 
static void ParseScript (const char *script, const char *desc, int weight)
 
static charread_file_contents (FILE *fd)
 
static void process_file (const char *filename, int weight)
 
static void process_builtin (const BuiltinScript *bi, int weight)
 
static void listAvailableScripts (void)
 
static const BuiltinScriptfindBuiltin (const char *name)
 
static int parseScriptWeight (const char *option, char **script)
 
static void printProgressReport (TState *threads, int64 test_start, pg_time_usec_t now, StatsData *last, int64 *last_report)
 
static void printSimpleStats (const char *prefix, SimpleStats *ss)
 
static void printVersion (PGconn *con)
 
static void printResults (StatsData *total, pg_time_usec_t total_duration, pg_time_usec_t conn_total_duration, pg_time_usec_t conn_elapsed_duration, int64 latency_late)
 
static bool set_random_seed (const char *seed)
 
int main (int argc, char **argv)
 
static void handle_sig_alarm (SIGNAL_ARGS)
 

Variables

static int nxacts = 0
 
static int duration = 0
 
static int64 end_time = 0
 
static int scale = 1
 
static int fillfactor = 100
 
static bool unlogged_tables = false
 
static double sample_rate = 0.0
 
static double throttle_delay = 0
 
static int64 latency_limit = 0
 
static chartablespace = NULL
 
static charindex_tablespace = NULL
 
static int partitions = 0
 
static partition_method_t partition_method = PART_NONE
 
static const char *const PARTITION_METHOD [] = {"none", "range", "hash"}
 
static int64 random_seed = -1
 
static bool use_log
 
static bool use_quiet
 
static int agg_interval
 
static bool per_script_stats = false
 
static int progress = 0
 
static bool progress_timestamp = false
 
static int nclients = 1
 
static int nthreads = 1
 
static bool is_connect
 
static bool report_per_command = false
 
static int main_pid
 
static uint32 max_tries = 1
 
static bool failures_detailed = false
 
static const charpghost = NULL
 
static const charpgport = NULL
 
static const charusername = NULL
 
static const chardbName = NULL
 
static charlogfile_prefix = NULL
 
static const charprogname
 
static volatile sig_atomic_t timer_exceeded = false
 
static pg_time_usec_t epoch_shift
 
static pg_prng_state base_random_sequence
 
static THREAD_BARRIER_T barrier
 
static QueryMode querymode = QUERY_SIMPLE
 
static const char *const QUERYMODE [] = {"simple", "extended", "prepared"}
 
static ParsedScript sql_script [MAX_SCRIPTS]
 
static int num_scripts
 
static int64 total_weight = 0
 
static bool verbose_errors = false
 
static bool exit_on_abort = false
 
static bool continue_on_error = false
 
static const BuiltinScript builtin_script []
 
static const PsqlScanCallbacks pgbench_callbacks
 

Macro Definition Documentation

◆ ALL_INIT_STEPS

#define ALL_INIT_STEPS   "dtgGvpf" /* all possible steps */

Definition at line 164 of file pgbench.c.

◆ COMMANDS_ALLOC_NUM

#define COMMANDS_ALLOC_NUM   128

◆ DEFAULT_INIT_STEPS

#define DEFAULT_INIT_STEPS   "dtgvp" /* default -I setting */

Definition at line 163 of file pgbench.c.

◆ DEFAULT_NXACTS

#define DEFAULT_NXACTS   10 /* default nxacts */

Definition at line 167 of file pgbench.c.

◆ ERRCODE_T_R_DEADLOCK_DETECTED

#define ERRCODE_T_R_DEADLOCK_DETECTED   "40P01"

Definition at line 78 of file pgbench.c.

◆ ERRCODE_T_R_SERIALIZATION_FAILURE

#define ERRCODE_T_R_SERIALIZATION_FAILURE   "40001"

Definition at line 77 of file pgbench.c.

◆ ERRCODE_UNDEFINED_TABLE

#define ERRCODE_UNDEFINED_TABLE   "42P01"

Definition at line 79 of file pgbench.c.

◆ FNV_OFFSET_BASIS

#define FNV_OFFSET_BASIS   UINT64CONST(0xcbf29ce484222325)

Definition at line 85 of file pgbench.c.

◆ FNV_PRIME

#define FNV_PRIME   UINT64CONST(0x100000001b3)

Definition at line 84 of file pgbench.c.

◆ LOG_STEP_SECONDS

#define LOG_STEP_SECONDS   5 /* seconds between log messages */

Definition at line 166 of file pgbench.c.

◆ M_PI

#define M_PI   3.14159265358979323846

Definition at line 74 of file pgbench.c.

◆ MAX_ARGS

#define MAX_ARGS   256

Definition at line 693 of file pgbench.c.

◆ MAX_FARGS

#define MAX_FARGS   16

Definition at line 2236 of file pgbench.c.

◆ MAX_SCRIPTS

#define MAX_SCRIPTS   128 /* max number of SQL scripts allowed */

Definition at line 348 of file pgbench.c.

◆ MAX_ZIPFIAN_PARAM

#define MAX_ZIPFIAN_PARAM   1000.0 /* maximum parameter for zipfian */

Definition at line 172 of file pgbench.c.

◆ META_COMMAND

#define META_COMMAND   2

Definition at line 687 of file pgbench.c.

◆ MIN_GAUSSIAN_PARAM

#define MIN_GAUSSIAN_PARAM   2.0 /* minimum parameter for gauss */

Definition at line 169 of file pgbench.c.

◆ MIN_ZIPFIAN_PARAM

#define MIN_ZIPFIAN_PARAM   1.001 /* minimum parameter for zipfian */

Definition at line 171 of file pgbench.c.

◆ MM2_MUL

#define MM2_MUL   UINT64CONST(0xc6a4a7935bd1e995)

Definition at line 86 of file pgbench.c.

◆ MM2_MUL_TIMES_8

#define MM2_MUL_TIMES_8   UINT64CONST(0x35253c9ade8f4ca8)

Definition at line 87 of file pgbench.c.

◆ MM2_ROT

#define MM2_ROT   47

Definition at line 88 of file pgbench.c.

◆ naccounts

#define naccounts   100000

Definition at line 246 of file pgbench.c.

◆ nbranches

#define nbranches
Value:
1 /* Makes little sense to change this. Change
* -s instead */

Definition at line 244 of file pgbench.c.

◆ ntellers

#define ntellers   10

Definition at line 245 of file pgbench.c.

◆ PARAMS_ARRAY_SIZE

#define PARAMS_ARRAY_SIZE   7

◆ PG_TIME_GET_DOUBLE

#define PG_TIME_GET_DOUBLE (   t)    (0.000001 * (t))

Definition at line 901 of file pgbench.c.

◆ POLL_USING_SELECT

#define POLL_USING_SELECT

Definition at line 52 of file pgbench.c.

◆ SCALE_32BIT_THRESHOLD

#define SCALE_32BIT_THRESHOLD   20000

Definition at line 255 of file pgbench.c.

◆ SHELL_COMMAND_SIZE

#define SHELL_COMMAND_SIZE   256 /* maximum size allowed for shell command */

Definition at line 349 of file pgbench.c.

◆ SOCKET_WAIT_METHOD

#define SOCKET_WAIT_METHOD   "select"

Definition at line 107 of file pgbench.c.

◆ SQL_COMMAND

#define SQL_COMMAND   1

Definition at line 686 of file pgbench.c.

◆ THREAD_BARRIER_DESTROY

#define THREAD_BARRIER_DESTROY (   barrier)    pthread_barrier_destroy((barrier))

Definition at line 156 of file pgbench.c.

◆ THREAD_BARRIER_INIT

#define THREAD_BARRIER_INIT (   barrier,
 
)     pthread_barrier_init((barrier), NULL, (n))

Definition at line 153 of file pgbench.c.

◆ THREAD_BARRIER_T

#define THREAD_BARRIER_T   pthread_barrier_t

Definition at line 152 of file pgbench.c.

◆ THREAD_BARRIER_WAIT

#define THREAD_BARRIER_WAIT (   barrier)    pthread_barrier_wait((barrier))

Definition at line 155 of file pgbench.c.

◆ THREAD_CREATE

#define THREAD_CREATE (   handle,
  function,
  arg 
)     pthread_create((handle), NULL, (function), (arg))

Definition at line 148 of file pgbench.c.

◆ THREAD_FUNC_CC

#define THREAD_FUNC_CC

Definition at line 147 of file pgbench.c.

◆ THREAD_FUNC_RETURN

#define THREAD_FUNC_RETURN   return NULL

Definition at line 146 of file pgbench.c.

◆ THREAD_FUNC_RETURN_TYPE

#define THREAD_FUNC_RETURN_TYPE   void *

Definition at line 145 of file pgbench.c.

◆ THREAD_JOIN

#define THREAD_JOIN (   handle)     pthread_join((handle), NULL)

Definition at line 150 of file pgbench.c.

◆ THREAD_T

#define THREAD_T   pthread_t

Definition at line 144 of file pgbench.c.

◆ VARIABLES_ALLOC_MARGIN

#define VARIABLES_ALLOC_MARGIN   8

Definition at line 311 of file pgbench.c.

◆ WSEP

#define WSEP   '@' /* weight separator */

Definition at line 302 of file pgbench.c.

Typedef Documentation

◆ BuiltinScript

◆ Command

◆ EStatus

◆ initRowMethod

typedef void(* initRowMethod) (PQExpBufferData *sql, int64 curr)

Definition at line 852 of file pgbench.c.

◆ MetaCommand

◆ ParsedScript

◆ pg_time_usec_t

Definition at line 371 of file pgbench.c.

◆ QueryMode

◆ SimpleStats

◆ socket_set

◆ StatsData

◆ TStatus

Enumeration Type Documentation

◆ ConnectionStateEnum

Enumerator
CSTATE_CHOOSE_SCRIPT 
CSTATE_START_TX 
CSTATE_PREPARE_THROTTLE 
CSTATE_THROTTLE 
CSTATE_START_COMMAND 
CSTATE_WAIT_RESULT 
CSTATE_SLEEP 
CSTATE_END_COMMAND 
CSTATE_SKIP_COMMAND 
CSTATE_ERROR 
CSTATE_WAIT_ROLLBACK_RESULT 
CSTATE_RETRY 
CSTATE_FAILURE 
CSTATE_END_TX 
CSTATE_ABORTED 
CSTATE_FINISHED 

Definition at line 493 of file pgbench.c.

495{
496 /*
497 * The client must first choose a script to execute. Once chosen, it can
498 * either be throttled (state CSTATE_PREPARE_THROTTLE under --rate), start
499 * right away (state CSTATE_START_TX) or not start at all if the timer was
500 * exceeded (state CSTATE_FINISHED).
501 */
503
504 /*
505 * CSTATE_START_TX performs start-of-transaction processing. Establishes
506 * a new connection for the transaction in --connect mode, records the
507 * transaction start time, and proceed to the first command.
508 *
509 * Note: once a script is started, it will either error or run till its
510 * end, where it may be interrupted. It is not interrupted while running,
511 * so pgbench --time is to be understood as tx are allowed to start in
512 * that time, and will finish when their work is completed.
513 */
515
516 /*
517 * In CSTATE_PREPARE_THROTTLE state, we calculate when to begin the next
518 * transaction, and advance to CSTATE_THROTTLE. CSTATE_THROTTLE state
519 * sleeps until that moment, then advances to CSTATE_START_TX, or
520 * CSTATE_FINISHED if the next transaction would start beyond the end of
521 * the run.
522 */
525
526 /*
527 * We loop through these states, to process each command in the script:
528 *
529 * CSTATE_START_COMMAND starts the execution of a command. On a SQL
530 * command, the command is sent to the server, and we move to
531 * CSTATE_WAIT_RESULT state unless in pipeline mode. On a \sleep
532 * meta-command, the timer is set, and we enter the CSTATE_SLEEP state to
533 * wait for it to expire. Other meta-commands are executed immediately. If
534 * the command about to start is actually beyond the end of the script,
535 * advance to CSTATE_END_TX.
536 *
537 * CSTATE_WAIT_RESULT waits until we get a result set back from the server
538 * for the current command.
539 *
540 * CSTATE_SLEEP waits until the end of \sleep.
541 *
542 * CSTATE_END_COMMAND records the end-of-command timestamp, increments the
543 * command counter, and loops back to CSTATE_START_COMMAND state.
544 *
545 * CSTATE_SKIP_COMMAND is used by conditional branches which are not
546 * executed. It quickly skip commands that do not need any evaluation.
547 * This state can move forward several commands, till there is something
548 * to do or the end of the script.
549 */
555
556 /*
557 * States for failed commands.
558 *
559 * If the SQL/meta command fails, in CSTATE_ERROR clean up after an error:
560 * (1) clear the conditional stack; (2) if we have an unterminated
561 * (possibly failed) transaction block, send the rollback command to the
562 * server and wait for the result in CSTATE_WAIT_ROLLBACK_RESULT. If
563 * something goes wrong with rolling back, go to CSTATE_ABORTED.
564 *
565 * But if everything is ok we are ready for future transactions: if this
566 * is a serialization or deadlock error and we can re-execute the
567 * transaction from the very beginning, go to CSTATE_RETRY; otherwise go
568 * to CSTATE_FAILURE.
569 *
570 * In CSTATE_RETRY report an error, set the same parameters for the
571 * transaction execution as in the previous tries and process the first
572 * transaction command in CSTATE_START_COMMAND.
573 *
574 * In CSTATE_FAILURE report a failure, set the parameters for the
575 * transaction execution as they were before the first run of this
576 * transaction (except for a random state) and go to CSTATE_END_TX to
577 * complete this transaction.
578 */
583
584 /*
585 * CSTATE_END_TX performs end-of-transaction processing. It calculates
586 * latency, and logs the transaction. In --connect mode, it closes the
587 * current connection.
588 *
589 * Then either starts over in CSTATE_CHOOSE_SCRIPT, or enters
590 * CSTATE_FINISHED if we have no more work to do.
591 */
593
594 /*
595 * Final states. CSTATE_ABORTED means that the script execution was
596 * aborted because a command failed, CSTATE_FINISHED means success.
597 */
@ CSTATE_START_TX
Definition pgbench.c:513
@ CSTATE_END_TX
Definition pgbench.c:591
@ CSTATE_RETRY
Definition pgbench.c:580
@ CSTATE_FINISHED
Definition pgbench.c:598
@ CSTATE_SKIP_COMMAND
Definition pgbench.c:553
@ CSTATE_THROTTLE
Definition pgbench.c:523
@ CSTATE_FAILURE
Definition pgbench.c:581
@ CSTATE_START_COMMAND
Definition pgbench.c:549
@ CSTATE_END_COMMAND
Definition pgbench.c:552
@ CSTATE_WAIT_RESULT
Definition pgbench.c:550
@ CSTATE_CHOOSE_SCRIPT
Definition pgbench.c:501
@ CSTATE_WAIT_ROLLBACK_RESULT
Definition pgbench.c:579
@ CSTATE_ABORTED
Definition pgbench.c:597
@ CSTATE_PREPARE_THROTTLE
Definition pgbench.c:522
@ CSTATE_SLEEP
Definition pgbench.c:551
@ CSTATE_ERROR
Definition pgbench.c:578

◆ EStatus

Enumerator
ESTATUS_NO_ERROR 
ESTATUS_META_COMMAND_ERROR 
ESTATUS_CONN_ERROR 
ESTATUS_SERIALIZATION_ERROR 
ESTATUS_DEADLOCK_ERROR 
ESTATUS_OTHER_SQL_ERROR 

Definition at line 461 of file pgbench.c.

463{
467
468 /* SQL errors */
@ ESTATUS_DEADLOCK_ERROR
Definition pgbench.c:469
@ ESTATUS_META_COMMAND_ERROR
Definition pgbench.c:464
@ ESTATUS_CONN_ERROR
Definition pgbench.c:465
@ ESTATUS_OTHER_SQL_ERROR
Definition pgbench.c:470
@ ESTATUS_NO_ERROR
Definition pgbench.c:463
@ ESTATUS_SERIALIZATION_ERROR
Definition pgbench.c:468

◆ MetaCommand

Enumerator
META_NONE 
META_SET 
META_SETSHELL 
META_SHELL 
META_SLEEP 
META_GSET 
META_ASET 
META_IF 
META_ELIF 
META_ELSE 
META_ENDIF 
META_STARTPIPELINE 
META_SYNCPIPELINE 
META_ENDPIPELINE 

Definition at line 695 of file pgbench.c.

697{
698 META_NONE, /* not a known meta-command */
699 META_SET, /* \set */
700 META_SETSHELL, /* \setshell */
701 META_SHELL, /* \shell */
702 META_SLEEP, /* \sleep */
703 META_GSET, /* \gset */
704 META_ASET, /* \aset */
705 META_IF, /* \if */
706 META_ELIF, /* \elif */
707 META_ELSE, /* \else */
708 META_ENDIF, /* \endif */
709 META_STARTPIPELINE, /* \startpipeline */
710 META_SYNCPIPELINE, /* \syncpipeline */
711 META_ENDPIPELINE, /* \endpipeline */
@ META_ELSE
Definition pgbench.c:706
@ META_SETSHELL
Definition pgbench.c:699
@ META_ENDIF
Definition pgbench.c:707
@ META_SHELL
Definition pgbench.c:700
@ META_STARTPIPELINE
Definition pgbench.c:708
@ META_SET
Definition pgbench.c:698
@ META_ELIF
Definition pgbench.c:705
@ META_SYNCPIPELINE
Definition pgbench.c:709
@ META_SLEEP
Definition pgbench.c:701
@ META_NONE
Definition pgbench.c:697
@ META_IF
Definition pgbench.c:704
@ META_ENDPIPELINE
Definition pgbench.c:710
@ META_ASET
Definition pgbench.c:703
@ META_GSET
Definition pgbench.c:702

◆ partition_method_t

Enumerator
PART_NONE 
PART_RANGE 
PART_HASH 

Definition at line 227 of file pgbench.c.

228{
229 PART_NONE, /* no partitioning */
230 PART_RANGE, /* range partitioning */
231 PART_HASH, /* hash partitioning */
partition_method_t
Definition pgbench.c:228
@ PART_NONE
Definition pgbench.c:229
@ PART_RANGE
Definition pgbench.c:230
@ PART_HASH
Definition pgbench.c:231

◆ QueryMode

Enumerator
QUERY_SIMPLE 
QUERY_EXTENDED 
QUERY_PREPARED 
NUM_QUERYMODE 

Definition at line 713 of file pgbench.c.

715{
716 QUERY_SIMPLE, /* simple query */
717 QUERY_EXTENDED, /* extended query */
718 QUERY_PREPARED, /* extended query with prepared statements */
@ QUERY_PREPARED
Definition pgbench.c:717
@ NUM_QUERYMODE
Definition pgbench.c:718
@ QUERY_SIMPLE
Definition pgbench.c:715
@ QUERY_EXTENDED
Definition pgbench.c:716

◆ TStatus

Enumerator
TSTATUS_IDLE 
TSTATUS_IN_BLOCK 
TSTATUS_CONN_ERROR 
TSTATUS_OTHER_ERROR 

Definition at line 476 of file pgbench.c.

478{
@ TSTATUS_CONN_ERROR
Definition pgbench.c:480
@ TSTATUS_IDLE
Definition pgbench.c:478
@ TSTATUS_IN_BLOCK
Definition pgbench.c:479
@ TSTATUS_OTHER_ERROR
Definition pgbench.c:481

Function Documentation

◆ accumStats()

static void accumStats ( StatsData stats,
bool  skipped,
double  lat,
double  lag,
EStatus  estatus,
int64  tries 
)
static

Definition at line 1442 of file pgbench.c.

1445{
1446 /* Record the skipped transaction */
1447 if (skipped)
1448 {
1449 /* no latency to record on skipped transactions */
1450 stats->skipped++;
1451 return;
1452 }
1453
1454 /*
1455 * Record the number of retries regardless of whether the transaction was
1456 * successful or failed.
1457 */
1458 if (tries > 1)
1459 {
1460 stats->retries += (tries - 1);
1461 stats->retried++;
1462 }
1463
1464 switch (estatus)
1465 {
1466 /* Record the successful transaction */
1467 case ESTATUS_NO_ERROR:
1468 stats->cnt++;
1469
1470 addToSimpleStats(&stats->latency, lat);
1471
1472 /* and possibly the same for schedule lag */
1473 if (throttle_delay)
1474 addToSimpleStats(&stats->lag, lag);
1475 break;
1476
1477 /* Record the failed transaction */
1479 stats->serialization_failures++;
1480 break;
1482 stats->deadlock_failures++;
1483 break;
1485 stats->other_sql_failures++;
1486 break;
1487 default:
1488 /* internal error which should never occur */
1489 pg_fatal("unexpected error status: %d", estatus);
1490 }
#define pg_fatal(...)
static double throttle_delay
Definition pgbench.c:204
static void addToSimpleStats(SimpleStats *ss, double val)
Definition pgbench.c:1393
static int fb(int x)
int64 serialization_failures
Definition pgbench.c:437
int64 cnt
Definition pgbench.c:427
int64 retried
Definition pgbench.c:433
int64 deadlock_failures
Definition pgbench.c:440
int64 skipped
Definition pgbench.c:429
int64 other_sql_failures
Definition pgbench.c:443
SimpleStats lag
Definition pgbench.c:449
int64 retries
Definition pgbench.c:431
SimpleStats latency
Definition pgbench.c:448

References addToSimpleStats(), StatsData::cnt, StatsData::deadlock_failures, ESTATUS_DEADLOCK_ERROR, ESTATUS_NO_ERROR, ESTATUS_OTHER_SQL_ERROR, ESTATUS_SERIALIZATION_ERROR, fb(), StatsData::lag, StatsData::latency, StatsData::other_sql_failures, pg_fatal, StatsData::retried, StatsData::retries, StatsData::serialization_failures, StatsData::skipped, and throttle_delay.

Referenced by doLog(), and processXactStats().

◆ add_socket_to_set()

static void add_socket_to_set ( socket_set sa,
int  fd,
int  idx 
)
static

Definition at line 8014 of file pgbench.c.

8016{
8017 /* See connect_slot() for background on this code. */
8018#ifdef WIN32
8019 if (sa->fds.fd_count + 1 >= FD_SETSIZE)
8020 {
8021 pg_log_error("too many concurrent database clients for this platform: %d",
8022 sa->fds.fd_count + 1);
8023 exit(1);
8024 }
8025#else
8027 {
8028 pg_log_error("socket file descriptor out of range for select(): %d",
8029 fd);
8030 pg_log_error_hint("Try fewer concurrent database clients.");
8031 exit(1);
8032 }
8033#endif
8034 FD_SET(fd, &sa->fds);
8035 if (fd > sa->maxfd)
8036 sa->maxfd = fd;
#define pg_log_error(...)
Definition logging.h:108
#define pg_log_error_hint(...)
Definition logging.h:114
static int fd(const char *x, int i)

References fb(), fd(), pg_log_error, and pg_log_error_hint.

Referenced by threadRun().

◆ addScript()

static void addScript ( const ParsedScript script)
static

Definition at line 6290 of file pgbench.c.

6292{
6293 if (script->commands == NULL || script->commands[0] == NULL)
6294 pg_fatal("empty command list for script \"%s\"", script->desc);
6295
6296 if (num_scripts >= MAX_SCRIPTS)
6297 pg_fatal("at most %d SQL scripts are allowed", MAX_SCRIPTS);
6298
6299 CheckConditional(script);
6300
6301 sql_script[num_scripts] = *script;
6302 num_scripts++;
static void CheckConditional(const ParsedScript *ps)
Definition pgbench.c:5948
#define MAX_SCRIPTS
Definition pgbench.c:348
static ParsedScript sql_script[MAX_SCRIPTS]
Definition pgbench.c:772
static int num_scripts
Definition pgbench.c:773
const char * desc
Definition pgbench.c:766
Command ** commands
Definition pgbench.c:768

References CheckConditional(), ParsedScript::commands, ParsedScript::desc, fb(), MAX_SCRIPTS, num_scripts, pg_fatal, and sql_script.

Referenced by ParseScript().

◆ addToSimpleStats()

static void addToSimpleStats ( SimpleStats ss,
double  val 
)
static

Definition at line 1393 of file pgbench.c.

1395{
1396 if (ss->count == 0 || val < ss->min)
1397 ss->min = val;
1398 if (ss->count == 0 || val > ss->max)
1399 ss->max = val;
1400 ss->count++;
1401 ss->sum += val;
1402 ss->sum2 += val * val;
long val
Definition informix.c:689
int64 count
Definition pgbench.c:359
double sum
Definition pgbench.c:362
double min
Definition pgbench.c:360
double max
Definition pgbench.c:361
double sum2
Definition pgbench.c:363

References SimpleStats::count, fb(), SimpleStats::max, SimpleStats::min, SimpleStats::sum, SimpleStats::sum2, and val.

Referenced by accumStats(), and advanceConnectionState().

◆ advanceConnectionState()

static void advanceConnectionState ( TState thread,
CState st,
StatsData agg 
)
static

Definition at line 3667 of file pgbench.c.

3669{
3670
3671 /*
3672 * gettimeofday() isn't free, so we get the current timestamp lazily the
3673 * first time it's needed, and reuse the same value throughout this
3674 * function after that. This also ensures that e.g. the calculated
3675 * latency reported in the log file and in the totals are the same. Zero
3676 * means "not set yet". Reset "now" when we execute shell commands or
3677 * expressions, which might take a non-negligible amount of time, though.
3678 */
3679 pg_time_usec_t now = 0;
3680
3681 /*
3682 * Loop in the state machine, until we have to wait for a result from the
3683 * server or have to sleep for throttling or \sleep.
3684 *
3685 * Note: In the switch-statement below, 'break' will loop back here,
3686 * meaning "continue in the state machine". Return is used to return to
3687 * the caller, giving the thread the opportunity to advance another
3688 * client.
3689 */
3690 for (;;)
3691 {
3692 Command *command;
3693
3694 switch (st->state)
3695 {
3696 /* Select transaction (script) to run. */
3698 st->use_file = chooseScript(thread);
3700
3701 /* reset transaction variables to default values */
3703 st->tries = 1;
3704
3705 pg_log_debug("client %d executing script \"%s\"",
3706 st->id, sql_script[st->use_file].desc);
3707
3708 /*
3709 * If time is over, we're done; otherwise, get ready to start
3710 * a new transaction, or to get throttled if that's requested.
3711 */
3714 break;
3715
3716 /* Start new transaction (script) */
3717 case CSTATE_START_TX:
3719
3720 /* establish connection if needed, i.e. under --connect */
3721 if (st->con == NULL)
3722 {
3724
3725 if ((st->con = doConnect()) == NULL)
3726 {
3727 /*
3728 * as the bench is already running, we do not abort
3729 * the process
3730 */
3731 pg_log_error("client %d aborted while establishing connection", st->id);
3732 st->state = CSTATE_ABORTED;
3733 break;
3734 }
3735
3736 /* reset now after connection */
3737 now = pg_time_now();
3738
3739 thread->conn_duration += now - start;
3740
3741 /* Reset session-local state */
3742 pg_free(st->prepared);
3743 st->prepared = NULL;
3744 }
3745
3746 /*
3747 * It is the first try to run this transaction. Remember the
3748 * random state: maybe it will get an error and we will need
3749 * to run it again.
3750 */
3751 st->random_state = st->cs_func_rs;
3752
3753 /* record transaction start time */
3754 st->txn_begin = now;
3755
3756 /*
3757 * When not throttling, this is also the transaction's
3758 * scheduled start time.
3759 */
3760 if (!throttle_delay)
3761 st->txn_scheduled = now;
3762
3763 /* Begin with the first command */
3765 st->command = 0;
3766 break;
3767
3768 /*
3769 * Handle throttling once per transaction by sleeping.
3770 */
3772
3773 /*
3774 * Generate a delay such that the series of delays will
3775 * approximate a Poisson distribution centered on the
3776 * throttle_delay time.
3777 *
3778 * If transactions are too slow or a given wait is shorter
3779 * than a transaction, the next transaction will start right
3780 * away.
3781 */
3783
3784 thread->throttle_trigger +=
3786 st->txn_scheduled = thread->throttle_trigger;
3787
3788 /*
3789 * If --latency-limit is used, and this slot is already late
3790 * so that the transaction will miss the latency limit even if
3791 * it completed immediately, skip this time slot and loop to
3792 * reschedule.
3793 */
3794 if (latency_limit)
3795 {
3797
3798 if (thread->throttle_trigger < now - latency_limit)
3799 {
3800 processXactStats(thread, st, &now, true, agg);
3801
3802 /*
3803 * Finish client if -T or -t was exceeded.
3804 *
3805 * Stop counting skipped transactions under -T as soon
3806 * as the timer is exceeded. Because otherwise it can
3807 * take a very long time to count all of them
3808 * especially when quite a lot of them happen with
3809 * unrealistically high rate setting in -R, which
3810 * would prevent pgbench from ending immediately.
3811 * Because of this behavior, note that there is no
3812 * guarantee that all skipped transactions are counted
3813 * under -T though there is under -t. This is OK in
3814 * practice because it's very unlikely to happen with
3815 * realistic setting.
3816 */
3817 if (timer_exceeded || (nxacts > 0 && st->cnt >= nxacts))
3818 st->state = CSTATE_FINISHED;
3819
3820 /* Go back to top of loop with CSTATE_PREPARE_THROTTLE */
3821 break;
3822 }
3823 }
3824
3825 /*
3826 * stop client if next transaction is beyond pgbench end of
3827 * execution; otherwise, throttle it.
3828 */
3829 st->state = end_time > 0 && st->txn_scheduled > end_time ?
3831 break;
3832
3833 /*
3834 * Wait until it's time to start next transaction.
3835 */
3836 case CSTATE_THROTTLE:
3838
3839 if (now < st->txn_scheduled)
3840 return; /* still sleeping, nothing to do here */
3841
3842 /* done sleeping, but don't start transaction if we're done */
3844 break;
3845
3846 /*
3847 * Send a command to server (or execute a meta-command)
3848 */
3850 command = sql_script[st->use_file].commands[st->command];
3851
3852 /*
3853 * Transition to script end processing if done, but close up
3854 * shop if a pipeline is open at this point.
3855 */
3856 if (command == NULL)
3857 {
3859 st->state = CSTATE_END_TX;
3860 else
3861 {
3862 pg_log_error("client %d aborted: end of script reached with pipeline open",
3863 st->id);
3864 st->state = CSTATE_ABORTED;
3865 }
3866
3867 break;
3868 }
3869
3870 /* record begin time of next command, and initiate it */
3872 {
3874 st->stmt_begin = now;
3875 }
3876
3877 /* Execute the command */
3878 if (command->type == SQL_COMMAND)
3879 {
3880 /* disallow \aset and \gset in pipeline mode */
3882 {
3883 if (command->meta == META_GSET)
3884 {
3885 commandFailed(st, "gset", "\\gset is not allowed in pipeline mode");
3886 st->state = CSTATE_ABORTED;
3887 break;
3888 }
3889 else if (command->meta == META_ASET)
3890 {
3891 commandFailed(st, "aset", "\\aset is not allowed in pipeline mode");
3892 st->state = CSTATE_ABORTED;
3893 break;
3894 }
3895 }
3896
3897 if (!sendCommand(st, command))
3898 {
3899 commandFailed(st, "SQL", "SQL command send failed");
3900 st->state = CSTATE_ABORTED;
3901 }
3902 else
3903 {
3904 /* Wait for results, unless in pipeline mode */
3907 else
3909 }
3910 }
3911 else if (command->type == META_COMMAND)
3912 {
3913 /*-----
3914 * Possible state changes when executing meta commands:
3915 * - on errors CSTATE_ABORTED
3916 * - on sleep CSTATE_SLEEP
3917 * - else CSTATE_END_COMMAND
3918 */
3919 st->state = executeMetaCommand(st, &now);
3920 if (st->state == CSTATE_ABORTED)
3922 }
3923
3924 /*
3925 * We're now waiting for an SQL command to complete, or
3926 * finished processing a metacommand, or need to sleep, or
3927 * something bad happened.
3928 */
3930 st->state == CSTATE_END_COMMAND ||
3931 st->state == CSTATE_SLEEP ||
3932 st->state == CSTATE_ABORTED);
3933 break;
3934
3935 /*
3936 * non executed conditional branch
3937 */
3940 /* quickly skip commands until something to do... */
3941 while (true)
3942 {
3943 command = sql_script[st->use_file].commands[st->command];
3944
3945 /* cannot reach end of script in that state */
3946 Assert(command != NULL);
3947
3948 /*
3949 * if this is conditional related, update conditional
3950 * state
3951 */
3952 if (command->type == META_COMMAND &&
3953 (command->meta == META_IF ||
3954 command->meta == META_ELIF ||
3955 command->meta == META_ELSE ||
3956 command->meta == META_ENDIF))
3957 {
3958 switch (conditional_stack_peek(st->cstack))
3959 {
3960 case IFSTATE_FALSE:
3961 if (command->meta == META_IF)
3962 {
3963 /* nested if in skipped branch - ignore */
3966 st->command++;
3967 }
3968 else if (command->meta == META_ELIF)
3969 {
3970 /* we must evaluate the condition */
3972 }
3973 else if (command->meta == META_ELSE)
3974 {
3975 /* we must execute next command */
3979 st->command++;
3980 }
3981 else if (command->meta == META_ENDIF)
3982 {
3985 if (conditional_active(st->cstack))
3987 /* else state remains CSTATE_SKIP_COMMAND */
3988 st->command++;
3989 }
3990 break;
3991
3992 case IFSTATE_IGNORED:
3993 case IFSTATE_ELSE_FALSE:
3994 if (command->meta == META_IF)
3997 else if (command->meta == META_ENDIF)
3998 {
4001 if (conditional_active(st->cstack))
4003 }
4004 /* could detect "else" & "elif" after "else" */
4005 st->command++;
4006 break;
4007
4008 case IFSTATE_NONE:
4009 case IFSTATE_TRUE:
4010 case IFSTATE_ELSE_TRUE:
4011 default:
4012
4013 /*
4014 * inconsistent if inactive, unreachable dead
4015 * code
4016 */
4017 Assert(false);
4018 }
4019 }
4020 else
4021 {
4022 /* skip and consider next */
4023 st->command++;
4024 }
4025
4026 if (st->state != CSTATE_SKIP_COMMAND)
4027 /* out of quick skip command loop */
4028 break;
4029 }
4030 break;
4031
4032 /*
4033 * Wait for the current SQL command to complete
4034 */
4035 case CSTATE_WAIT_RESULT:
4036 pg_log_debug("client %d receiving", st->id);
4037
4038 /*
4039 * Only check for new network data if we processed all data
4040 * fetched prior. Otherwise we end up doing a syscall for each
4041 * individual pipelined query, which has a measurable
4042 * performance impact.
4043 */
4044 if (PQisBusy(st->con) && !PQconsumeInput(st->con))
4045 {
4046 /* there's something wrong */
4047 commandFailed(st, "SQL", "perhaps the backend died while processing");
4048 st->state = CSTATE_ABORTED;
4049 break;
4050 }
4051 if (PQisBusy(st->con))
4052 return; /* don't have the whole result yet */
4053
4054 /* store or discard the query results */
4055 if (readCommandResponse(st,
4058 {
4059 /*
4060 * outside of pipeline mode: stop reading results.
4061 * pipeline mode: continue reading results until an
4062 * end-of-pipeline response.
4063 */
4066 }
4067 else if (canRetryError(st->estatus) || canContinueOnError(st->estatus))
4068 st->state = CSTATE_ERROR;
4069 else
4070 st->state = CSTATE_ABORTED;
4071 break;
4072
4073 /*
4074 * Wait until sleep is done. This state is entered after a
4075 * \sleep metacommand. The behavior is similar to
4076 * CSTATE_THROTTLE, but proceeds to CSTATE_START_COMMAND
4077 * instead of CSTATE_START_TX.
4078 */
4079 case CSTATE_SLEEP:
4081 if (now < st->sleep_until)
4082 return; /* still sleeping, nothing to do here */
4083 /* Else done sleeping. */
4085 break;
4086
4087 /*
4088 * End of command: record stats and proceed to next command.
4089 */
4090 case CSTATE_END_COMMAND:
4091
4092 /*
4093 * command completed: accumulate per-command execution times
4094 * in thread-local data structure, if per-command latencies
4095 * are requested.
4096 */
4098 {
4100
4101 command = sql_script[st->use_file].commands[st->command];
4102 /* XXX could use a mutex here, but we choose not to */
4103 addToSimpleStats(&command->stats,
4105 }
4106
4107 /* Go ahead with next command, to be executed or skipped */
4108 st->command++;
4109 st->state = conditional_active(st->cstack) ?
4111 break;
4112
4113 /*
4114 * Clean up after an error.
4115 */
4116 case CSTATE_ERROR:
4117 {
4119
4121
4122 /* Clear the conditional stack */
4124
4125 /* Read and discard until a sync point in pipeline mode */
4127 {
4128 if (!discardUntilSync(st))
4129 {
4130 st->state = CSTATE_ABORTED;
4131 break;
4132 }
4133 }
4134
4135 /*
4136 * Check if we have a (failed) transaction block or not,
4137 * and roll it back if any.
4138 */
4141 {
4142 /* Try to rollback a (failed) transaction block. */
4143 if (!PQsendQuery(st->con, "ROLLBACK"))
4144 {
4145 pg_log_error("client %d aborted: failed to send sql command for rolling back the failed transaction",
4146 st->id);
4147 st->state = CSTATE_ABORTED;
4148 }
4149 else
4151 }
4152 else if (tstatus == TSTATUS_IDLE)
4153 {
4154 /*
4155 * If time is over, we're done; otherwise, check if we
4156 * can retry the error.
4157 */
4160 }
4161 else
4162 {
4164 pg_log_error("perhaps the backend died while processing");
4165
4166 pg_log_error("client %d aborted while receiving the transaction status", st->id);
4167 st->state = CSTATE_ABORTED;
4168 }
4169 break;
4170 }
4171
4172 /*
4173 * Wait for the rollback command to complete
4174 */
4176 {
4177 PGresult *res;
4178
4179 pg_log_debug("client %d receiving", st->id);
4180 if (!PQconsumeInput(st->con))
4181 {
4182 pg_log_error("client %d aborted while rolling back the transaction after an error; perhaps the backend died while processing",
4183 st->id);
4184 st->state = CSTATE_ABORTED;
4185 break;
4186 }
4187 if (PQisBusy(st->con))
4188 return; /* don't have the whole result yet */
4189
4190 /*
4191 * Read and discard the query result;
4192 */
4193 res = PQgetResult(st->con);
4194 switch (PQresultStatus(res))
4195 {
4196 case PGRES_COMMAND_OK:
4197 /* OK */
4198 PQclear(res);
4199 /* null must be returned */
4200 res = PQgetResult(st->con);
4201 Assert(res == NULL);
4202
4203 /*
4204 * If time is over, we're done; otherwise, check
4205 * if we can retry the error.
4206 */
4209 break;
4210 default:
4211 pg_log_error("client %d aborted while rolling back the transaction after an error; %s",
4212 st->id, PQerrorMessage(st->con));
4213 PQclear(res);
4214 st->state = CSTATE_ABORTED;
4215 break;
4216 }
4217 break;
4218 }
4219
4220 /*
4221 * Retry the transaction after an error.
4222 */
4223 case CSTATE_RETRY:
4224 command = sql_script[st->use_file].commands[st->command];
4225
4226 /*
4227 * Inform that the transaction will be retried after the
4228 * error.
4229 */
4230 if (verbose_errors)
4231 printVerboseErrorMessages(st, &now, true);
4232
4233 /* Count tries and retries */
4234 st->tries++;
4235 command->retries++;
4236
4237 /*
4238 * Reset the random state as they were at the beginning of the
4239 * transaction.
4240 */
4241 st->cs_func_rs = st->random_state;
4242
4243 /* Process the first transaction command. */
4244 st->command = 0;
4247 break;
4248
4249 /*
4250 * Record a failed transaction.
4251 */
4252 case CSTATE_FAILURE:
4253 command = sql_script[st->use_file].commands[st->command];
4254
4255 /* Accumulate the failure. */
4256 command->failures++;
4257
4258 /*
4259 * Inform that the failed transaction will not be retried.
4260 */
4261 if (verbose_errors)
4262 printVerboseErrorMessages(st, &now, false);
4263
4264 /* End the failed transaction. */
4265 st->state = CSTATE_END_TX;
4266 break;
4267
4268 /*
4269 * End of transaction (end of script, really).
4270 */
4271 case CSTATE_END_TX:
4272 {
4274
4275 /* transaction finished: calculate latency and do log */
4276 processXactStats(thread, st, &now, false, agg);
4277
4278 /*
4279 * missing \endif... cannot happen if CheckConditional was
4280 * okay
4281 */
4283
4284 /*
4285 * We must complete all the transaction blocks that were
4286 * started in this script.
4287 */
4290 {
4291 pg_log_error("client %d aborted: end of script reached without completing the last transaction",
4292 st->id);
4293 st->state = CSTATE_ABORTED;
4294 break;
4295 }
4296 else if (tstatus != TSTATUS_IDLE)
4297 {
4299 pg_log_error("perhaps the backend died while processing");
4300
4301 pg_log_error("client %d aborted while receiving the transaction status", st->id);
4302 st->state = CSTATE_ABORTED;
4303 break;
4304 }
4305
4306 if (is_connect)
4307 {
4309
4311 finishCon(st);
4312 now = pg_time_now();
4313 thread->conn_duration += now - start;
4314 }
4315
4316 if ((st->cnt >= nxacts && duration <= 0) || timer_exceeded)
4317 {
4318 /* script completed */
4319 st->state = CSTATE_FINISHED;
4320 break;
4321 }
4322
4323 /* next transaction (script) */
4325
4326 /*
4327 * Ensure that we always return on this point, so as to
4328 * avoid an infinite loop if the script only contains meta
4329 * commands.
4330 */
4331 return;
4332 }
4333
4334 /*
4335 * Final states. Close the connection if it's still open.
4336 */
4337 case CSTATE_ABORTED:
4338 case CSTATE_FINISHED:
4339
4340 /*
4341 * Don't measure the disconnection delays here even if in
4342 * CSTATE_FINISHED and -C/--connect option is specified.
4343 * Because in this case all the connections that this thread
4344 * established are closed at the end of transactions and the
4345 * disconnection delays should have already been measured at
4346 * that moment.
4347 *
4348 * In CSTATE_ABORTED state, the measurement is no longer
4349 * necessary because we cannot report complete results anyways
4350 * in this case.
4351 */
4352 finishCon(st);
4353 return;
4354 }
4355 }
Datum now(PG_FUNCTION_ARGS)
Definition timestamp.c:1613
#define Assert(condition)
Definition c.h:943
ifState conditional_stack_peek(ConditionalStack cstack)
void conditional_stack_push(ConditionalStack cstack, ifState new_state)
Definition conditional.c:53
bool conditional_stack_pop(ConditionalStack cstack)
Definition conditional.c:69
bool conditional_active(ConditionalStack cstack)
void conditional_stack_reset(ConditionalStack cstack)
Definition conditional.c:30
bool conditional_stack_poke(ConditionalStack cstack, ifState new_state)
bool conditional_stack_empty(ConditionalStack cstack)
@ IFSTATE_FALSE
Definition conditional.h:34
@ IFSTATE_ELSE_TRUE
Definition conditional.h:40
@ IFSTATE_IGNORED
Definition conditional.h:37
@ IFSTATE_TRUE
Definition conditional.h:32
@ IFSTATE_NONE
Definition conditional.h:31
@ IFSTATE_ELSE_FALSE
Definition conditional.h:42
PGpipelineStatus PQpipelineStatus(const PGconn *conn)
char * PQerrorMessage(const PGconn *conn)
int PQconsumeInput(PGconn *conn)
Definition fe-exec.c:2001
int PQsendQuery(PGconn *conn, const char *query)
Definition fe-exec.c:1433
int PQisBusy(PGconn *conn)
Definition fe-exec.c:2048
void pg_free(void *ptr)
return str start
#define PQgetResult
#define PQclear
#define PQresultStatus
@ PGRES_COMMAND_OK
Definition libpq-fe.h:131
@ PQ_PIPELINE_OFF
Definition libpq-fe.h:193
@ PQ_PIPELINE_ON
Definition libpq-fe.h:194
#define pg_log_debug(...)
Definition logging.h:135
static pg_time_usec_t pg_time_now(void)
Definition pgbench.c:885
static void printVerboseErrorMessages(CState *st, pg_time_usec_t *now, bool is_retry)
Definition pgbench.c:3630
static int64 latency_limit
Definition pgbench.c:212
static TStatus getTransactionStatus(PGconn *con)
Definition pgbench.c:3593
static int64 end_time
Definition pgbench.c:176
static bool doRetry(CState *st, pg_time_usec_t *now)
Definition pgbench.c:3477
static bool sendCommand(CState *st, Command *command)
Definition pgbench.c:3151
TStatus
Definition pgbench.c:477
static void finishCon(CState *st)
Definition pgbench.c:7830
int64 pg_time_usec_t
Definition pgbench.c:371
static bool is_connect
Definition pgbench.c:266
static int nxacts
Definition pgbench.c:174
static bool report_per_command
Definition pgbench.c:267
#define PG_TIME_GET_DOUBLE(t)
Definition pgbench.c:901
static int discardUntilSync(CState *st)
Definition pgbench.c:3523
static ConnectionStateEnum executeMetaCommand(CState *st, pg_time_usec_t *now)
Definition pgbench.c:4365
static bool canRetryError(EStatus estatus)
Definition pgbench.c:3253
static void commandFailed(CState *st, const char *cmd, const char *message)
Definition pgbench.c:3017
static int chooseScript(TState *thread)
Definition pgbench.c:3043
#define SQL_COMMAND
Definition pgbench.c:686
static volatile sig_atomic_t timer_exceeded
Definition pgbench.c:304
static void processXactStats(TState *thread, CState *st, pg_time_usec_t *now, bool skipped, StatsData *agg)
Definition pgbench.c:4752
static int duration
Definition pgbench.c:175
static PGconn * doConnect(void)
Definition pgbench.c:1525
static void pg_time_now_lazy(pg_time_usec_t *now)
Definition pgbench.c:895
static bool verbose_errors
Definition pgbench.c:776
static bool canContinueOnError(EStatus estatus)
Definition pgbench.c:3264
static int64 getPoissonRand(pg_prng_state *state, double center)
Definition pgbench.c:1169
#define META_COMMAND
Definition pgbench.c:687
static bool readCommandResponse(CState *st, MetaCommand meta, char *varprefix)
Definition pgbench.c:3280
int64 cnt
Definition pgbench.c:645
int id
Definition pgbench.c:607
pg_time_usec_t txn_scheduled
Definition pgbench.c:625
pg_time_usec_t stmt_begin
Definition pgbench.c:628
int command
Definition pgbench.c:618
int use_file
Definition pgbench.c:617
ConditionalStack cstack
Definition pgbench.c:609
pg_prng_state random_state
Definition pgbench.c:640
pg_time_usec_t txn_begin
Definition pgbench.c:627
EStatus estatus
Definition pgbench.c:637
PGconn * con
Definition pgbench.c:606
pg_prng_state cs_func_rs
Definition pgbench.c:615
uint32 tries
Definition pgbench.c:641
bool ** prepared
Definition pgbench.c:631
ConnectionStateEnum state
Definition pgbench.c:608
int64 retries
Definition pgbench.c:760
char * varprefix
Definition pgbench.c:757
int type
Definition pgbench.c:752
MetaCommand meta
Definition pgbench.c:753
SimpleStats stats
Definition pgbench.c:759
int64 failures
Definition pgbench.c:761
int64 throttle_trigger
Definition pgbench.c:669
pg_prng_state ts_throttle_rs
Definition pgbench.c:666
pg_time_usec_t conn_duration
Definition pgbench.c:676

References addToSimpleStats(), Assert, canContinueOnError(), canRetryError(), chooseScript(), CState::cnt, CState::command, commandFailed(), ParsedScript::commands, CState::con, conditional_active(), conditional_stack_empty(), conditional_stack_peek(), conditional_stack_poke(), conditional_stack_pop(), conditional_stack_push(), conditional_stack_reset(), TState::conn_duration, CState::cs_func_rs, CState::cstack, CSTATE_ABORTED, CSTATE_CHOOSE_SCRIPT, CSTATE_END_COMMAND, CSTATE_END_TX, CSTATE_ERROR, CSTATE_FAILURE, CSTATE_FINISHED, CSTATE_PREPARE_THROTTLE, CSTATE_RETRY, CSTATE_SKIP_COMMAND, CSTATE_SLEEP, CSTATE_START_COMMAND, CSTATE_START_TX, CSTATE_THROTTLE, CSTATE_WAIT_RESULT, CSTATE_WAIT_ROLLBACK_RESULT, ParsedScript::desc, discardUntilSync(), doConnect(), doRetry(), duration, end_time, CState::estatus, ESTATUS_META_COMMAND_ERROR, ESTATUS_NO_ERROR, executeMetaCommand(), Command::failures, fb(), finishCon(), getPoissonRand(), getTransactionStatus(), CState::id, IFSTATE_ELSE_FALSE, IFSTATE_ELSE_TRUE, IFSTATE_FALSE, IFSTATE_IGNORED, IFSTATE_NONE, IFSTATE_TRUE, is_connect, latency_limit, Command::meta, META_ASET, META_COMMAND, META_ELIF, META_ELSE, META_ENDIF, META_GSET, META_IF, now(), nxacts, pg_free(), pg_log_debug, pg_log_error, PG_TIME_GET_DOUBLE, pg_time_now(), pg_time_now_lazy(), PGRES_COMMAND_OK, PQ_PIPELINE_OFF, PQ_PIPELINE_ON, PQclear, PQconsumeInput(), PQerrorMessage(), PQgetResult, PQisBusy(), PQpipelineStatus(), PQresultStatus, PQsendQuery(), CState::prepared, printVerboseErrorMessages(), processXactStats(), CState::random_state, readCommandResponse(), report_per_command, Command::retries, sendCommand(), SQL_COMMAND, sql_script, start, CState::state, Command::stats, CState::stmt_begin, throttle_delay, TState::throttle_trigger, timer_exceeded, CState::tries, TState::ts_throttle_rs, TSTATUS_CONN_ERROR, TSTATUS_IDLE, TSTATUS_IN_BLOCK, CState::txn_begin, CState::txn_scheduled, Command::type, CState::use_file, Command::varprefix, and verbose_errors.

Referenced by threadRun().

◆ alloc_socket_set()

static socket_set * alloc_socket_set ( int  count)
static

Definition at line 7995 of file pgbench.c.

7997{
#define pg_malloc0_object(type)
Definition fe_memutils.h:61

References pg_malloc0_object.

Referenced by threadRun().

◆ allocCStatePrepared()

static void allocCStatePrepared ( CState st)
static

Definition at line 3065 of file pgbench.c.

3067{
3068 Assert(st->prepared == NULL);
3069
3070 st->prepared = pg_malloc_array(bool *, num_scripts);
3071 for (int i = 0; i < num_scripts; i++)
3072 {
3073 ParsedScript *script = &sql_script[i];
3074 int numcmds;
3075
3076 for (numcmds = 0; script->commands[numcmds] != NULL; numcmds++)
3077 ;
3078 st->prepared[i] = pg_malloc0_array(bool, numcmds);
3079 }
#define pg_malloc_array(type, count)
Definition fe_memutils.h:66
#define pg_malloc0_array(type, count)
Definition fe_memutils.h:67
int i
Definition isn.c:77

References Assert, ParsedScript::commands, fb(), i, num_scripts, pg_malloc0_array, pg_malloc_array, CState::prepared, and sql_script.

Referenced by prepareCommand(), and prepareCommandsInPipeline().

◆ assignVariables()

static char * assignVariables ( Variables variables,
char sql 
)
static

Definition at line 1930 of file pgbench.c.

1932{
1933 char *p,
1934 *name,
1935 *val;
1936
1937 p = sql;
1938 while ((p = strchr(p, ':')) != NULL)
1939 {
1940 int eaten;
1941
1942 name = parseVariable(p, &eaten);
1943 if (name == NULL)
1944 {
1945 while (*p == ':')
1946 {
1947 p++;
1948 }
1949 continue;
1950 }
1951
1952 val = getVariable(variables, name);
1953 free(name);
1954 if (val == NULL)
1955 {
1956 p++;
1957 continue;
1958 }
1959
1960 p = replaceVariable(&sql, p, eaten, val);
1961 }
1962
1963 return sql;
static char * parseVariable(const char *sql, int *eaten)
Definition pgbench.c:1883
static char * getVariable(Variables *variables, char *name)
Definition pgbench.c:1625
static char * replaceVariable(char **sql, char *param, int len, char *value)
Definition pgbench.c:1910
#define free(a)
const char * name

References fb(), free, getVariable(), name, parseVariable(), replaceVariable(), and val.

Referenced by sendCommand().

◆ canContinueOnError()

static bool canContinueOnError ( EStatus  estatus)
static

Definition at line 3264 of file pgbench.c.

3266{
3267 return (continue_on_error &&
3268 estatus == ESTATUS_OTHER_SQL_ERROR);
static bool continue_on_error
Definition pgbench.c:779

References continue_on_error, and ESTATUS_OTHER_SQL_ERROR.

Referenced by advanceConnectionState(), and readCommandResponse().

◆ canRetryError()

static bool canRetryError ( EStatus  estatus)
static

Definition at line 3253 of file pgbench.c.

3255{
3256 return (estatus == ESTATUS_SERIALIZATION_ERROR ||
3257 estatus == ESTATUS_DEADLOCK_ERROR);

References ESTATUS_DEADLOCK_ERROR, and ESTATUS_SERIALIZATION_ERROR.

Referenced by advanceConnectionState(), doRetry(), and readCommandResponse().

◆ CheckConditional()

static void CheckConditional ( const ParsedScript ps)
static

Definition at line 5948 of file pgbench.c.

5950{
5951 /* statically check conditional structure */
5953 int i;
5954
5955 for (i = 0; ps->commands[i] != NULL; i++)
5956 {
5957 Command *cmd = ps->commands[i];
5958
5959 if (cmd->type == META_COMMAND)
5960 {
5961 switch (cmd->meta)
5962 {
5963 case META_IF:
5965 break;
5966 case META_ELIF:
5968 ConditionError(ps->desc, i + 1, "\\elif without matching \\if");
5970 ConditionError(ps->desc, i + 1, "\\elif after \\else");
5971 break;
5972 case META_ELSE:
5974 ConditionError(ps->desc, i + 1, "\\else without matching \\if");
5976 ConditionError(ps->desc, i + 1, "\\else after \\else");
5978 break;
5979 case META_ENDIF:
5980 if (!conditional_stack_pop(cs))
5981 ConditionError(ps->desc, i + 1, "\\endif without matching \\if");
5982 break;
5983 default:
5984 /* ignore anything else... */
5985 break;
5986 }
5987 }
5988 }
5989 if (!conditional_stack_empty(cs))
5990 ConditionError(ps->desc, i + 1, "\\if without matching \\endif");
ConditionalStack conditional_stack_create(void)
Definition conditional.c:18
void conditional_stack_destroy(ConditionalStack cstack)
Definition conditional.c:43
struct parser_state ps
static void ConditionError(const char *desc, int cmdn, const char *msg)
Definition pgbench.c:5938

References conditional_stack_create(), conditional_stack_destroy(), conditional_stack_empty(), conditional_stack_peek(), conditional_stack_poke(), conditional_stack_pop(), conditional_stack_push(), ConditionError(), fb(), i, IFSTATE_ELSE_FALSE, IFSTATE_FALSE, Command::meta, META_COMMAND, META_ELIF, META_ELSE, META_ENDIF, META_IF, ps, and Command::type.

Referenced by addScript().

◆ checkInitSteps()

static void checkInitSteps ( const char initialize_steps)
static

Definition at line 5305 of file pgbench.c.

5307{
5308 if (initialize_steps[0] == '\0')
5309 pg_fatal("no initialization steps specified");
5310
5311 for (const char *step = initialize_steps; *step != '\0'; step++)
5312 {
5313 if (strchr(ALL_INIT_STEPS " ", *step) == NULL)
5314 {
5315 pg_log_error("unrecognized initialization step \"%c\"", *step);
5316 pg_log_error_detail("Allowed step characters are: \"" ALL_INIT_STEPS "\".");
5317 exit(1);
5318 }
5319 }
#define pg_log_error_detail(...)
Definition logging.h:111
#define ALL_INIT_STEPS
Definition pgbench.c:164

References ALL_INIT_STEPS, fb(), pg_fatal, pg_log_error, and pg_log_error_detail.

Referenced by main().

◆ chooseScript()

static int chooseScript ( TState thread)
static

Definition at line 3043 of file pgbench.c.

3045{
3046 int i = 0;
3047 int64 w;
3048
3049 if (num_scripts == 1)
3050 return 0;
3051
3052 w = getrand(&thread->ts_choose_rs, 0, total_weight - 1);
3053 do
3054 {
3055 w -= sql_script[i++].weight;
3056 } while (w >= 0);
3057
3058 return i - 1;
int64_t int64
Definition c.h:621
static int64 total_weight
Definition pgbench.c:774
static int64 getrand(pg_prng_state *state, int64 min, int64 max)
Definition pgbench.c:1092
pg_prng_state ts_choose_rs
Definition pgbench.c:665

References getrand(), i, num_scripts, sql_script, total_weight, TState::ts_choose_rs, and ParsedScript::weight.

Referenced by advanceConnectionState().

◆ clear_socket_set()

static void clear_socket_set ( socket_set sa)
static

Definition at line 8007 of file pgbench.c.

8009{
8010 FD_ZERO(&sa->fds);
8011 sa->maxfd = -1;

References fb().

Referenced by threadRun().

◆ coerceToBool()

static bool coerceToBool ( PgBenchValue pval,
bool bval 
)
static

Definition at line 1998 of file pgbench.c.

2000{
2001 if (pval->type == PGBT_BOOLEAN)
2002 {
2003 *bval = pval->u.bval;
2004 return true;
2005 }
2006 else /* NULL, INT or DOUBLE */
2007 {
2008 pg_log_error("cannot coerce %s to boolean", valueTypeName(pval));
2009 *bval = false; /* suppress uninitialized-variable warnings */
2010 return false;
2011 }
static char * valueTypeName(PgBenchValue *pval)
Definition pgbench.c:1976
@ PGBT_BOOLEAN
Definition pgbench.h:40

References fb(), pg_log_error, PGBT_BOOLEAN, and valueTypeName().

Referenced by evalLazyFunc(), and evalStandardFunc().

◆ coerceToDouble()

static bool coerceToDouble ( PgBenchValue pval,
double dval 
)
static

Definition at line 2067 of file pgbench.c.

2069{
2070 if (pval->type == PGBT_DOUBLE)
2071 {
2072 *dval = pval->u.dval;
2073 return true;
2074 }
2075 else if (pval->type == PGBT_INT)
2076 {
2077 *dval = (double) pval->u.ival;
2078 return true;
2079 }
2080 else /* BOOLEAN or NULL */
2081 {
2082 pg_log_error("cannot coerce %s to double", valueTypeName(pval));
2083 return false;
2084 }
@ PGBT_INT
Definition pgbench.h:38
@ PGBT_DOUBLE
Definition pgbench.h:39

References fb(), pg_log_error, PGBT_DOUBLE, PGBT_INT, and valueTypeName().

Referenced by evalStandardFunc().

◆ coerceToInt()

static bool coerceToInt ( PgBenchValue pval,
int64 ival 
)
static

Definition at line 2039 of file pgbench.c.

2041{
2042 if (pval->type == PGBT_INT)
2043 {
2044 *ival = pval->u.ival;
2045 return true;
2046 }
2047 else if (pval->type == PGBT_DOUBLE)
2048 {
2049 double dval = rint(pval->u.dval);
2050
2051 if (isnan(dval) || !FLOAT8_FITS_IN_INT64(dval))
2052 {
2053 pg_log_error("double to int overflow for %f", dval);
2054 return false;
2055 }
2056 *ival = (int64) dval;
2057 return true;
2058 }
2059 else /* BOOLEAN or NULL */
2060 {
2061 pg_log_error("cannot coerce %s to int", valueTypeName(pval));
2062 return false;
2063 }
#define FLOAT8_FITS_IN_INT64(num)
Definition c.h:1179

References fb(), FLOAT8_FITS_IN_INT64, pg_log_error, PGBT_DOUBLE, PGBT_INT, and valueTypeName().

Referenced by evalStandardFunc().

◆ commandError()

static void commandError ( CState st,
const char message 
)
static

Definition at line 3027 of file pgbench.c.

3029{
3030 /*
3031 * Errors should only be detected during an SQL command or the
3032 * \endpipeline meta command. Any other case triggers an assertion
3033 * failure.
3034 */
3037
3038 pg_log_info("client %d got an error in command %d (SQL) of script %d; %s",
3039 st->id, st->command, st->use_file, message);
#define pg_log_info(...)
Definition logging.h:126

References Assert, CState::command, ParsedScript::commands, CState::id, Command::meta, META_ENDPIPELINE, pg_log_info, SQL_COMMAND, sql_script, Command::type, and CState::use_file.

Referenced by readCommandResponse().

◆ commandFailed()

static void commandFailed ( CState st,
const char cmd,
const char message 
)
static

Definition at line 3017 of file pgbench.c.

3019{
3020 pg_log_error("client %d aborted in command %d (%s) of script %d; %s",
3021 st->id, st->command, cmd, st->use_file, message);

References CState::command, CState::id, pg_log_error, and CState::use_file.

Referenced by advanceConnectionState(), and executeMetaCommand().

◆ compareVariableNames()

static int compareVariableNames ( const void v1,
const void v2 
)
static

Definition at line 1590 of file pgbench.c.

1592{
1593 return strcmp(((const Variable *) v1)->name,
1594 ((const Variable *) v2)->name);

References fb(), and name.

Referenced by lookupVariable().

◆ computeIterativeZipfian()

static int64 computeIterativeZipfian ( pg_prng_state state,
int64  n,
double  s 
)
static

Definition at line 1191 of file pgbench.c.

1193{
1194 double b = pow(2.0, s - 1.0);
1195 double x,
1196 t,
1197 u,
1198 v;
1199
1200 /* Ensure n is sane */
1201 if (n <= 1)
1202 return 1;
1203
1204 while (true)
1205 {
1206 /* random variates */
1207 u = pg_prng_double(state);
1208 v = pg_prng_double(state);
1209
1210 x = floor(pow(u, -1.0 / (s - 1.0)));
1211
1212 t = pow(1.0 + 1.0 / x, s - 1.0);
1213 /* reject if too large or out of bound */
1214 if (v * x * (t - 1.0) / (b - 1.0) <= t / b && x <= n)
1215 break;
1216 }
1217 return (int64) x;
int b
Definition isn.c:74
int x
Definition isn.c:75
double pg_prng_double(pg_prng_state *state)
Definition pg_prng.c:268

References b, fb(), pg_prng_double(), and x.

Referenced by getZipfianRand().

◆ ConditionError()

static void ConditionError ( const char desc,
int  cmdn,
const char msg 
)
static

Definition at line 5938 of file pgbench.c.

5940{
5941 pg_fatal("condition error in script \"%s\" command %d: %s",
5942 desc, cmdn, msg);

References fb(), and pg_fatal.

Referenced by CheckConditional().

◆ create_sql_command()

static Command * create_sql_command ( PQExpBuffer  buf)
static

Definition at line 5651 of file pgbench.c.

5653{
5655 char *p = skip_sql_comments(buf->data);
5656
5657 if (p == NULL)
5658 return NULL;
5659
5660 /* Allocate and initialize Command structure */
5662 initPQExpBuffer(&my_command->lines);
5663 appendPQExpBufferStr(&my_command->lines, p);
5664 my_command->first_line = NULL; /* this is set later */
5665 my_command->type = SQL_COMMAND;
5666 my_command->meta = META_NONE;
5667 my_command->argc = 0;
5668 my_command->retries = 0;
5669 my_command->failures = 0;
5670 memset(my_command->argv, 0, sizeof(my_command->argv));
5671 my_command->varprefix = NULL; /* allocated later, if needed */
5672 my_command->expr = NULL;
5673 initSimpleStats(&my_command->stats);
5674 my_command->prepname = NULL; /* set later, if needed */
5675
5676 return my_command;
static char buf[DEFAULT_XLOG_SEG_SIZE]
static char * skip_sql_comments(char *sql_command)
Definition pgbench.c:5616
static void initSimpleStats(SimpleStats *ss)
Definition pgbench.c:1384
void initPQExpBuffer(PQExpBuffer str)
Definition pqexpbuffer.c:90
void appendPQExpBufferStr(PQExpBuffer str, const char *data)

References appendPQExpBufferStr(), buf, fb(), initPQExpBuffer(), initSimpleStats(), META_NONE, pg_malloc0_object, skip_sql_comments(), and SQL_COMMAND.

Referenced by ParseScript().

◆ createPartitions()

static void createPartitions ( PGconn con)
static

Definition at line 4825 of file pgbench.c.

4827{
4828 PQExpBufferData query;
4829
4830 /* we must have to create some partitions */
4831 Assert(partitions > 0);
4832
4833 fprintf(stderr, "creating %d partitions...\n", partitions);
4834
4835 initPQExpBuffer(&query);
4836
4837 for (int p = 1; p <= partitions; p++)
4838 {
4840 {
4842
4843 printfPQExpBuffer(&query,
4844 "create%s table pgbench_accounts_%d\n"
4845 " partition of pgbench_accounts\n"
4846 " for values from (",
4847 unlogged_tables ? " unlogged" : "", p);
4848
4849 /*
4850 * For RANGE, we use open-ended partitions at the beginning and
4851 * end to allow any valid value for the primary key. Although the
4852 * actual minimum and maximum values can be derived from the
4853 * scale, it is more generic and the performance is better.
4854 */
4855 if (p == 1)
4856 appendPQExpBufferStr(&query, "minvalue");
4857 else
4858 appendPQExpBuffer(&query, INT64_FORMAT, (p - 1) * part_size + 1);
4859
4860 appendPQExpBufferStr(&query, ") to (");
4861
4862 if (p < partitions)
4863 appendPQExpBuffer(&query, INT64_FORMAT, p * part_size + 1);
4864 else
4865 appendPQExpBufferStr(&query, "maxvalue");
4866
4867 appendPQExpBufferChar(&query, ')');
4868 }
4869 else if (partition_method == PART_HASH)
4870 printfPQExpBuffer(&query,
4871 "create%s table pgbench_accounts_%d\n"
4872 " partition of pgbench_accounts\n"
4873 " for values with (modulus %d, remainder %d)",
4874 unlogged_tables ? " unlogged" : "", p,
4875 partitions, p - 1);
4876 else /* cannot get there */
4877 Assert(0);
4878
4879 /*
4880 * Per ddlinfo in initCreateTables, fillfactor is needed on table
4881 * pgbench_accounts.
4882 */
4883 appendPQExpBuffer(&query, " with (fillfactor=%d)", fillfactor);
4884
4885 executeStatement(con, query.data);
4886 }
4887
4888 termPQExpBuffer(&query);
#define INT64_FORMAT
Definition c.h:634
#define fprintf(file, fmt, msg)
Definition cubescan.l:21
static int scale
Definition pgbench.c:182
static int partitions
Definition pgbench.c:224
static int fillfactor
Definition pgbench.c:188
static partition_method_t partition_method
Definition pgbench.c:234
static bool unlogged_tables
Definition pgbench.c:193
#define naccounts
Definition pgbench.c:246
static void executeStatement(PGconn *con, const char *sql)
Definition pgbench.c:1494
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void appendPQExpBufferChar(PQExpBuffer str, char ch)
void termPQExpBuffer(PQExpBuffer str)

References appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), Assert, PQExpBufferData::data, executeStatement(), fb(), fillfactor, fprintf, initPQExpBuffer(), INT64_FORMAT, naccounts, PART_HASH, PART_RANGE, partition_method, partitions, printfPQExpBuffer(), scale, termPQExpBuffer(), and unlogged_tables.

Referenced by initCreateTables().

◆ discardAvailableResults()

static void discardAvailableResults ( CState st)
static

Definition at line 3204 of file pgbench.c.

3206{
3207 PGresult *res = NULL;
3208
3209 for (;;)
3210 {
3211 res = PQgetResult(st->con);
3212
3213 /*
3214 * Read and discard results until PQgetResult() returns NULL (no more
3215 * results) or a connection failure is detected. If the pipeline
3216 * status is PQ_PIPELINE_ABORTED, more results may still be available
3217 * even after PQgetResult() returns NULL, so continue reading in that
3218 * case.
3219 */
3220 if ((res == NULL && PQpipelineStatus(st->con) != PQ_PIPELINE_ABORTED) ||
3221 PQstatus(st->con) == CONNECTION_BAD)
3222 break;
3223
3224 PQclear(res);
3225 }
3226 PQclear(res);
ConnStatusType PQstatus(const PGconn *conn)
@ CONNECTION_BAD
Definition libpq-fe.h:91
@ PQ_PIPELINE_ABORTED
Definition libpq-fe.h:195

References CState::con, CONNECTION_BAD, fb(), PQ_PIPELINE_ABORTED, PQclear, PQgetResult, PQpipelineStatus(), and PQstatus().

Referenced by getSQLErrorStatus(), and readCommandResponse().

◆ discardUntilSync()

static int discardUntilSync ( CState st)
static

Definition at line 3523 of file pgbench.c.

3525{
3526 bool received_sync = false;
3527
3528 /*
3529 * Send a Sync message to ensure at least one PGRES_PIPELINE_SYNC is
3530 * received and to avoid an infinite loop, since all earlier ones may have
3531 * already been received.
3532 */
3533 if (!PQpipelineSync(st->con))
3534 {
3535 pg_log_error("client %d aborted: failed to send a pipeline sync",
3536 st->id);
3537 return 0;
3538 }
3539
3540 /*
3541 * Continue reading results until the last sync point, i.e., until
3542 * reaching null just after PGRES_PIPELINE_SYNC.
3543 */
3544 for (;;)
3545 {
3546 PGresult *res = PQgetResult(st->con);
3547
3548 if (PQstatus(st->con) == CONNECTION_BAD)
3549 {
3550 pg_log_error("client %d aborted while rolling back the transaction after an error; perhaps the backend died while processing",
3551 st->id);
3552 PQclear(res);
3553 return 0;
3554 }
3555
3557 received_sync = true;
3558 else if (received_sync && res == NULL)
3559 {
3560 /*
3561 * Reset ongoing sync count to 0 since all PGRES_PIPELINE_SYNC
3562 * results have been discarded.
3563 */
3564 st->num_syncs = 0;
3565 break;
3566 }
3567 else
3568 {
3569 /*
3570 * If a PGRES_PIPELINE_SYNC is followed by something other than
3571 * PGRES_PIPELINE_SYNC or NULL, another PGRES_PIPELINE_SYNC will
3572 * appear later. Reset received_sync to false to wait for it.
3573 */
3574 received_sync = false;
3575 }
3576 PQclear(res);
3577 }
3578
3579 /* exit pipeline */
3580 if (PQexitPipelineMode(st->con) != 1)
3581 {
3582 pg_log_error("client %d aborted: failed to exit pipeline mode for rolling back the failed transaction",
3583 st->id);
3584 return 0;
3585 }
3586 return 1;
int PQexitPipelineMode(PGconn *conn)
Definition fe-exec.c:3104
int PQpipelineSync(PGconn *conn)
Definition fe-exec.c:3303
@ PGRES_PIPELINE_SYNC
Definition libpq-fe.h:145
int num_syncs
Definition pgbench.c:619

References CState::con, CONNECTION_BAD, fb(), CState::id, CState::num_syncs, pg_log_error, PGRES_PIPELINE_SYNC, PQclear, PQexitPipelineMode(), PQgetResult, PQpipelineSync(), PQresultStatus, and PQstatus().

Referenced by advanceConnectionState().

◆ disconnect_all()

static void disconnect_all ( CState state,
int  length 
)
static

Definition at line 4791 of file pgbench.c.

4793{
4794 int i;
4795
4796 for (i = 0; i < length; i++)
4797 finishCon(&state[i]);

References finishCon(), and i.

Referenced by main(), and threadRun().

◆ doConnect()

static PGconn * doConnect ( void  )
static

Definition at line 1525 of file pgbench.c.

1527{
1528 PGconn *conn;
1529 bool new_pass;
1530 static char *password = NULL;
1531
1532 /*
1533 * Start the connection. Loop until we have a password if requested by
1534 * backend.
1535 */
1536 do
1537 {
1538#define PARAMS_ARRAY_SIZE 7
1539
1540 const char *keywords[PARAMS_ARRAY_SIZE];
1541 const char *values[PARAMS_ARRAY_SIZE];
1542
1543 keywords[0] = "host";
1544 values[0] = pghost;
1545 keywords[1] = "port";
1546 values[1] = pgport;
1547 keywords[2] = "user";
1548 values[2] = username;
1549 keywords[3] = "password";
1550 values[3] = password;
1551 keywords[4] = "dbname";
1552 values[4] = dbName;
1553 keywords[5] = "fallback_application_name";
1554 values[5] = progname;
1555 keywords[6] = NULL;
1556 values[6] = NULL;
1557
1558 new_pass = false;
1559
1561
1562 if (!conn)
1563 {
1564 pg_log_error("connection to database \"%s\" failed", dbName);
1565 return NULL;
1566 }
1567
1568 if (PQstatus(conn) == CONNECTION_BAD &&
1570 !password)
1571 {
1572 PQfinish(conn);
1573 password = simple_prompt("Password: ", false);
1574 new_pass = true;
1575 }
1576 } while (new_pass);
1577
1578 /* check to see that the backend connection was successfully made */
1580 {
1582 PQfinish(conn);
1583 return NULL;
1584 }
1585
1586 return conn;
static Datum values[MAXATTR]
Definition bootstrap.c:190
int PQconnectionNeedsPassword(const PGconn *conn)
void PQfinish(PGconn *conn)
PGconn * PQconnectdbParams(const char *const *keywords, const char *const *values, int expand_dbname)
Definition fe-connect.c:775
static const JsonPathKeyword keywords[]
#define PARAMS_ARRAY_SIZE
static const char * pghost
Definition pgbench.c:295
static const char * username
Definition pgbench.c:297
static const char * progname
Definition pgbench.c:300
static const char * pgport
Definition pgbench.c:296
static const char * dbName
Definition pgbench.c:298
char * simple_prompt(const char *prompt, bool echo)
Definition sprompt.c:38
static char * password
Definition streamutil.c:51
PGconn * conn
Definition streamutil.c:52

References conn, CONNECTION_BAD, dbName, fb(), keywords, PARAMS_ARRAY_SIZE, password, pg_log_error, pghost, pgport, PQconnectdbParams(), PQconnectionNeedsPassword(), PQerrorMessage(), PQfinish(), PQstatus(), progname, simple_prompt(), username, and values.

Referenced by advanceConnectionState(), main(), runInitSteps(), and threadRun().

◆ doLog()

static void doLog ( TState thread,
CState st,
StatsData agg,
bool  skipped,
double  latency,
double  lag 
)
static

Definition at line 4629 of file pgbench.c.

4632{
4633 FILE *logfile = thread->logfile;
4635
4636 Assert(use_log);
4637
4638 /*
4639 * Skip the log entry if sampling is enabled and this row doesn't belong
4640 * to the random sample.
4641 */
4642 if (sample_rate != 0.0 &&
4644 return;
4645
4646 /* should we aggregate the results or not? */
4647 if (agg_interval > 0)
4648 {
4650
4651 /*
4652 * Loop until we reach the interval of the current moment, and print
4653 * any empty intervals in between (this may happen with very low tps,
4654 * e.g. --rate=0.1).
4655 */
4656
4657 while ((next = agg->start_time + agg_interval * INT64CONST(1000000)) <= now)
4658 {
4659 double lag_sum = 0.0;
4660 double lag_sum2 = 0.0;
4661 double lag_min = 0.0;
4662 double lag_max = 0.0;
4663 int64 skipped = 0;
4664 int64 serialization_failures = 0;
4665 int64 deadlock_failures = 0;
4666 int64 other_sql_failures = 0;
4667 int64 retried = 0;
4668 int64 retries = 0;
4669
4670 /* print aggregated report to logfile */
4671 fprintf(logfile, INT64_FORMAT " " INT64_FORMAT " %.0f %.0f %.0f %.0f",
4672 agg->start_time / 1000000, /* seconds since Unix epoch */
4673 agg->cnt,
4674 agg->latency.sum,
4675 agg->latency.sum2,
4676 agg->latency.min,
4677 agg->latency.max);
4678
4679 if (throttle_delay)
4680 {
4681 lag_sum = agg->lag.sum;
4682 lag_sum2 = agg->lag.sum2;
4683 lag_min = agg->lag.min;
4684 lag_max = agg->lag.max;
4685 }
4686 fprintf(logfile, " %.0f %.0f %.0f %.0f",
4687 lag_sum,
4688 lag_sum2,
4689 lag_min,
4690 lag_max);
4691
4692 if (latency_limit)
4693 skipped = agg->skipped;
4694 fprintf(logfile, " " INT64_FORMAT, skipped);
4695
4696 if (max_tries != 1)
4697 {
4698 retried = agg->retried;
4699 retries = agg->retries;
4700 }
4701 fprintf(logfile, " " INT64_FORMAT " " INT64_FORMAT, retried, retries);
4702
4704 {
4705 serialization_failures = agg->serialization_failures;
4706 deadlock_failures = agg->deadlock_failures;
4707 other_sql_failures = agg->other_sql_failures;
4708 }
4710 serialization_failures,
4711 deadlock_failures,
4712 other_sql_failures);
4713
4714 fputc('\n', logfile);
4715
4716 /* reset data and move to next interval */
4717 initStats(agg, next);
4718 }
4719
4720 /* accumulate the current transaction */
4721 accumStats(agg, skipped, latency, lag, st->estatus, st->tries);
4722 }
4723 else
4724 {
4725 /* no, print raw transactions */
4726 if (!skipped && st->estatus == ESTATUS_NO_ERROR)
4727 fprintf(logfile, "%d " INT64_FORMAT " %.0f %d " INT64_FORMAT " "
4729 st->id, st->cnt, latency, st->use_file,
4730 now / 1000000, now % 1000000);
4731 else
4732 fprintf(logfile, "%d " INT64_FORMAT " %s %d " INT64_FORMAT " "
4734 st->id, st->cnt, getResultString(skipped, st->estatus),
4735 st->use_file, now / 1000000, now % 1000000);
4736
4737 if (throttle_delay)
4738 fprintf(logfile, " %.0f", lag);
4739 if (max_tries != 1)
4740 fprintf(logfile, " %u", st->tries - 1);
4741 fputc('\n', logfile);
4742 }
static int32 next
Definition blutils.c:225
#define INT64CONST(x)
Definition c.h:630
static FILE * logfile
Definition pg_regress.c:128
static void accumStats(StatsData *stats, bool skipped, double lat, double lag, EStatus estatus, int64 tries)
Definition pgbench.c:1442
static uint32 max_tries
Definition pgbench.c:290
static bool use_log
Definition pgbench.c:257
static int agg_interval
Definition pgbench.c:259
static void initStats(StatsData *sd, pg_time_usec_t start)
Definition pgbench.c:1424
static double sample_rate
Definition pgbench.c:198
static bool failures_detailed
Definition pgbench.c:292
static pg_time_usec_t epoch_shift
Definition pgbench.c:456
static const char * getResultString(bool skipped, EStatus estatus)
Definition pgbench.c:4596
FILE * logfile
Definition pgbench.c:670
pg_prng_state ts_sample_rs
Definition pgbench.c:667

References accumStats(), agg_interval, Assert, CState::cnt, epoch_shift, CState::estatus, ESTATUS_NO_ERROR, failures_detailed, fb(), fprintf, getResultString(), CState::id, initStats(), INT64_FORMAT, INT64CONST, latency_limit, TState::logfile, logfile, max_tries, next, now(), pg_prng_double(), pg_time_now(), sample_rate, throttle_delay, CState::tries, TState::ts_sample_rs, CState::use_file, and use_log.

Referenced by processXactStats(), and threadRun().

◆ doRetry()

static bool doRetry ( CState st,
pg_time_usec_t now 
)
static

Definition at line 3477 of file pgbench.c.

3479{
3481
3482 /* We can only retry serialization or deadlock errors. */
3483 if (!canRetryError(st->estatus))
3484 return false;
3485
3486 /*
3487 * We must have at least one option to limit the retrying of transactions
3488 * that got an error.
3489 */
3491
3492 /*
3493 * We cannot retry the error if we have reached the maximum number of
3494 * tries.
3495 */
3496 if (max_tries && st->tries >= max_tries)
3497 return false;
3498
3499 /*
3500 * We cannot retry the error if we spent too much time on this
3501 * transaction.
3502 */
3503 if (latency_limit)
3504 {
3506 if (*now - st->txn_scheduled > latency_limit)
3507 return false;
3508 }
3509
3510 /*
3511 * We cannot retry the error if the benchmark duration is over.
3512 */
3513 if (timer_exceeded)
3514 return false;
3515
3516 /* OK */
3517 return true;

References Assert, canRetryError(), duration, CState::estatus, ESTATUS_NO_ERROR, latency_limit, max_tries, now(), pg_time_now_lazy(), timer_exceeded, CState::tries, and CState::txn_scheduled.

Referenced by advanceConnectionState().

◆ enlargeVariables()

static void enlargeVariables ( Variables variables,
int  needed 
)
static

Definition at line 1767 of file pgbench.c.

1769{
1770 /* total number of variables required now */
1771 needed += variables->nvars;
1772
1773 if (variables->max_vars < needed)
1774 {
1775 variables->max_vars = needed + VARIABLES_ALLOC_MARGIN;
1776 variables->vars = (Variable *)
1777 pg_realloc_array(variables->vars, Variable, variables->max_vars);
1778 }
#define pg_realloc_array(pointer, type, count)
Definition fe_memutils.h:74
#define VARIABLES_ALLOC_MARGIN
Definition pgbench.c:311
Variable * vars
Definition pgbench.c:335
int nvars
Definition pgbench.c:336
int max_vars
Definition pgbench.c:343

References fb(), Variables::max_vars, Variables::nvars, pg_realloc_array, VARIABLES_ALLOC_MARGIN, and Variables::vars.

Referenced by lookupCreateVariable().

◆ evalFunc()

static bool evalFunc ( CState st,
PgBenchFunction  func,
PgBenchExprLink args,
PgBenchValue retval 
)
static

Definition at line 2810 of file pgbench.c.

2813{
2814 if (isLazyFunc(func))
2815 return evalLazyFunc(st, func, args, retval);
2816 else
2817 return evalStandardFunc(st, func, args, retval);
static bool isLazyFunc(PgBenchFunction func)
Definition pgbench.c:2119
static bool evalLazyFunc(CState *st, PgBenchFunction func, PgBenchExprLink *args, PgBenchValue *retval)
Definition pgbench.c:2126
static bool evalStandardFunc(CState *st, PgBenchFunction func, PgBenchExprLink *args, PgBenchValue *retval)
Definition pgbench.c:2243

References evalLazyFunc(), evalStandardFunc(), and isLazyFunc().

Referenced by evaluateExpr().

◆ evalLazyFunc()

static bool evalLazyFunc ( CState st,
PgBenchFunction  func,
PgBenchExprLink args,
PgBenchValue retval 
)
static

Definition at line 2126 of file pgbench.c.

2129{
2131 a2;
2132 bool ba1,
2133 ba2;
2134
2135 Assert(isLazyFunc(func) && args != NULL && args->next != NULL);
2136
2137 /* args points to first condition */
2138 if (!evaluateExpr(st, args->expr, &a1))
2139 return false;
2140
2141 /* second condition for AND/OR and corresponding branch for CASE */
2142 args = args->next;
2143
2144 switch (func)
2145 {
2146 case PGBENCH_AND:
2147 if (a1.type == PGBT_NULL)
2148 {
2149 setNullValue(retval);
2150 return true;
2151 }
2152
2153 if (!coerceToBool(&a1, &ba1))
2154 return false;
2155
2156 if (!ba1)
2157 {
2158 setBoolValue(retval, false);
2159 return true;
2160 }
2161
2162 if (!evaluateExpr(st, args->expr, &a2))
2163 return false;
2164
2165 if (a2.type == PGBT_NULL)
2166 {
2167 setNullValue(retval);
2168 return true;
2169 }
2170 else if (!coerceToBool(&a2, &ba2))
2171 return false;
2172 else
2173 {
2174 setBoolValue(retval, ba2);
2175 return true;
2176 }
2177
2178 return true;
2179
2180 case PGBENCH_OR:
2181
2182 if (a1.type == PGBT_NULL)
2183 {
2184 setNullValue(retval);
2185 return true;
2186 }
2187
2188 if (!coerceToBool(&a1, &ba1))
2189 return false;
2190
2191 if (ba1)
2192 {
2193 setBoolValue(retval, true);
2194 return true;
2195 }
2196
2197 if (!evaluateExpr(st, args->expr, &a2))
2198 return false;
2199
2200 if (a2.type == PGBT_NULL)
2201 {
2202 setNullValue(retval);
2203 return true;
2204 }
2205 else if (!coerceToBool(&a2, &ba2))
2206 return false;
2207 else
2208 {
2209 setBoolValue(retval, ba2);
2210 return true;
2211 }
2212
2213 case PGBENCH_CASE:
2214 /* when true, execute branch */
2215 if (valueTruth(&a1))
2216 return evaluateExpr(st, args->expr, retval);
2217
2218 /* now args contains next condition or final else expression */
2219 args = args->next;
2220
2221 /* final else case? */
2222 if (args->next == NULL)
2223 return evaluateExpr(st, args->expr, retval);
2224
2225 /* no, another when, proceed */
2226 return evalLazyFunc(st, PGBENCH_CASE, args, retval);
2227
2228 default:
2229 /* internal error, cannot get here */
2230 Assert(0);
2231 break;
2232 }
2233 return false;
static const FormData_pg_attribute a1
Definition heap.c:144
static const FormData_pg_attribute a2
Definition heap.c:157
static void setNullValue(PgBenchValue *pv)
Definition pgbench.c:2088
static bool evaluateExpr(CState *st, PgBenchExpr *expr, PgBenchValue *retval)
Definition pgbench.c:2826
static void setBoolValue(PgBenchValue *pv, bool bval)
Definition pgbench.c:2096
static bool valueTruth(PgBenchValue *pval)
Definition pgbench.c:2018
static bool coerceToBool(PgBenchValue *pval, bool *bval)
Definition pgbench.c:1998
@ PGBT_NULL
Definition pgbench.h:37
@ PGBENCH_AND
Definition pgbench.h:87
@ PGBENCH_OR
Definition pgbench.h:88
@ PGBENCH_CASE
Definition pgbench.h:100

References a1, a2, Assert, coerceToBool(), evalLazyFunc(), evaluateExpr(), fb(), isLazyFunc(), PGBENCH_AND, PGBENCH_CASE, PGBENCH_OR, PGBT_NULL, setBoolValue(), setNullValue(), and valueTruth().

Referenced by evalFunc(), and evalLazyFunc().

◆ evalStandardFunc()

static bool evalStandardFunc ( CState st,
PgBenchFunction  func,
PgBenchExprLink args,
PgBenchValue retval 
)
static

Definition at line 2243 of file pgbench.c.

2247{
2248 /* evaluate all function arguments */
2249 int nargs = 0;
2251 PgBenchExprLink *l = args;
2252 bool has_null = false;
2253
2254 for (nargs = 0; nargs < MAX_FARGS && l != NULL; nargs++, l = l->next)
2255 {
2256 if (!evaluateExpr(st, l->expr, &vargs[nargs]))
2257 return false;
2258 has_null |= vargs[nargs].type == PGBT_NULL;
2259 }
2260
2261 if (l != NULL)
2262 {
2263 pg_log_error("too many function arguments, maximum is %d", MAX_FARGS);
2264 return false;
2265 }
2266
2267 /* NULL arguments */
2268 if (has_null && func != PGBENCH_IS && func != PGBENCH_DEBUG)
2269 {
2270 setNullValue(retval);
2271 return true;
2272 }
2273
2274 /* then evaluate function */
2275 switch (func)
2276 {
2277 /* overloaded operators */
2278 case PGBENCH_ADD:
2279 case PGBENCH_SUB:
2280 case PGBENCH_MUL:
2281 case PGBENCH_DIV:
2282 case PGBENCH_MOD:
2283 case PGBENCH_EQ:
2284 case PGBENCH_NE:
2285 case PGBENCH_LE:
2286 case PGBENCH_LT:
2287 {
2288 PgBenchValue *lval = &vargs[0],
2289 *rval = &vargs[1];
2290
2291 Assert(nargs == 2);
2292
2293 /* overloaded type management, double if some double */
2294 if ((lval->type == PGBT_DOUBLE ||
2295 rval->type == PGBT_DOUBLE) && func != PGBENCH_MOD)
2296 {
2297 double ld,
2298 rd;
2299
2300 if (!coerceToDouble(lval, &ld) ||
2302 return false;
2303
2304 switch (func)
2305 {
2306 case PGBENCH_ADD:
2307 setDoubleValue(retval, ld + rd);
2308 return true;
2309
2310 case PGBENCH_SUB:
2311 setDoubleValue(retval, ld - rd);
2312 return true;
2313
2314 case PGBENCH_MUL:
2315 setDoubleValue(retval, ld * rd);
2316 return true;
2317
2318 case PGBENCH_DIV:
2319 setDoubleValue(retval, ld / rd);
2320 return true;
2321
2322 case PGBENCH_EQ:
2323 setBoolValue(retval, ld == rd);
2324 return true;
2325
2326 case PGBENCH_NE:
2327 setBoolValue(retval, ld != rd);
2328 return true;
2329
2330 case PGBENCH_LE:
2331 setBoolValue(retval, ld <= rd);
2332 return true;
2333
2334 case PGBENCH_LT:
2335 setBoolValue(retval, ld < rd);
2336 return true;
2337
2338 default:
2339 /* cannot get here */
2340 Assert(0);
2341 }
2342 }
2343 else /* we have integer operands, or % */
2344 {
2345 int64 li,
2346 ri,
2347 res;
2348
2349 if (!coerceToInt(lval, &li) ||
2350 !coerceToInt(rval, &ri))
2351 return false;
2352
2353 switch (func)
2354 {
2355 case PGBENCH_ADD:
2356 if (pg_add_s64_overflow(li, ri, &res))
2357 {
2358 pg_log_error("bigint add out of range");
2359 return false;
2360 }
2361 setIntValue(retval, res);
2362 return true;
2363
2364 case PGBENCH_SUB:
2365 if (pg_sub_s64_overflow(li, ri, &res))
2366 {
2367 pg_log_error("bigint sub out of range");
2368 return false;
2369 }
2370 setIntValue(retval, res);
2371 return true;
2372
2373 case PGBENCH_MUL:
2374 if (pg_mul_s64_overflow(li, ri, &res))
2375 {
2376 pg_log_error("bigint mul out of range");
2377 return false;
2378 }
2379 setIntValue(retval, res);
2380 return true;
2381
2382 case PGBENCH_EQ:
2383 setBoolValue(retval, li == ri);
2384 return true;
2385
2386 case PGBENCH_NE:
2387 setBoolValue(retval, li != ri);
2388 return true;
2389
2390 case PGBENCH_LE:
2391 setBoolValue(retval, li <= ri);
2392 return true;
2393
2394 case PGBENCH_LT:
2395 setBoolValue(retval, li < ri);
2396 return true;
2397
2398 case PGBENCH_DIV:
2399 case PGBENCH_MOD:
2400 if (ri == 0)
2401 {
2402 pg_log_error("division by zero");
2403 return false;
2404 }
2405 /* special handling of -1 divisor */
2406 if (ri == -1)
2407 {
2408 if (func == PGBENCH_DIV)
2409 {
2410 /* overflow check (needed for INT64_MIN) */
2411 if (li == PG_INT64_MIN)
2412 {
2413 pg_log_error("bigint div out of range");
2414 return false;
2415 }
2416 else
2417 setIntValue(retval, -li);
2418 }
2419 else
2420 setIntValue(retval, 0);
2421 return true;
2422 }
2423 /* else divisor is not -1 */
2424 if (func == PGBENCH_DIV)
2425 setIntValue(retval, li / ri);
2426 else /* func == PGBENCH_MOD */
2427 setIntValue(retval, li % ri);
2428
2429 return true;
2430
2431 default:
2432 /* cannot get here */
2433 Assert(0);
2434 }
2435 }
2436
2437 Assert(0);
2438 return false; /* NOTREACHED */
2439 }
2440
2441 /* integer bitwise operators */
2442 case PGBENCH_BITAND:
2443 case PGBENCH_BITOR:
2444 case PGBENCH_BITXOR:
2445 case PGBENCH_LSHIFT:
2446 case PGBENCH_RSHIFT:
2447 {
2448 int64 li,
2449 ri;
2450
2451 if (!coerceToInt(&vargs[0], &li) || !coerceToInt(&vargs[1], &ri))
2452 return false;
2453
2454 if (func == PGBENCH_BITAND)
2455 setIntValue(retval, li & ri);
2456 else if (func == PGBENCH_BITOR)
2457 setIntValue(retval, li | ri);
2458 else if (func == PGBENCH_BITXOR)
2459 setIntValue(retval, li ^ ri);
2460 else if (func == PGBENCH_LSHIFT)
2461 setIntValue(retval, li << ri);
2462 else if (func == PGBENCH_RSHIFT)
2463 setIntValue(retval, li >> ri);
2464 else /* cannot get here */
2465 Assert(0);
2466
2467 return true;
2468 }
2469
2470 /* logical operators */
2471 case PGBENCH_NOT:
2472 {
2473 bool b;
2474
2475 if (!coerceToBool(&vargs[0], &b))
2476 return false;
2477
2478 setBoolValue(retval, !b);
2479 return true;
2480 }
2481
2482 /* no arguments */
2483 case PGBENCH_PI:
2484 setDoubleValue(retval, M_PI);
2485 return true;
2486
2487 /* 1 overloaded argument */
2488 case PGBENCH_ABS:
2489 {
2490 PgBenchValue *varg = &vargs[0];
2491
2492 Assert(nargs == 1);
2493
2494 if (varg->type == PGBT_INT)
2495 {
2496 int64 i = varg->u.ival;
2497
2498 setIntValue(retval, i < 0 ? -i : i);
2499 }
2500 else
2501 {
2502 double d = varg->u.dval;
2503
2504 Assert(varg->type == PGBT_DOUBLE);
2505 setDoubleValue(retval, d < 0.0 ? -d : d);
2506 }
2507
2508 return true;
2509 }
2510
2511 case PGBENCH_DEBUG:
2512 {
2513 PgBenchValue *varg = &vargs[0];
2514
2515 Assert(nargs == 1);
2516
2517 fprintf(stderr, "debug(script=%d,command=%d): ",
2518 st->use_file, st->command + 1);
2519
2520 if (varg->type == PGBT_NULL)
2521 fprintf(stderr, "null\n");
2522 else if (varg->type == PGBT_BOOLEAN)
2523 fprintf(stderr, "boolean %s\n", varg->u.bval ? "true" : "false");
2524 else if (varg->type == PGBT_INT)
2525 fprintf(stderr, "int " INT64_FORMAT "\n", varg->u.ival);
2526 else if (varg->type == PGBT_DOUBLE)
2527 fprintf(stderr, "double %.*g\n", DBL_DIG, varg->u.dval);
2528 else /* internal error, unexpected type */
2529 Assert(0);
2530
2531 *retval = *varg;
2532
2533 return true;
2534 }
2535
2536 /* 1 double argument */
2537 case PGBENCH_DOUBLE:
2538 case PGBENCH_SQRT:
2539 case PGBENCH_LN:
2540 case PGBENCH_EXP:
2541 {
2542 double dval;
2543
2544 Assert(nargs == 1);
2545
2546 if (!coerceToDouble(&vargs[0], &dval))
2547 return false;
2548
2549 if (func == PGBENCH_SQRT)
2550 dval = sqrt(dval);
2551 else if (func == PGBENCH_LN)
2552 dval = log(dval);
2553 else if (func == PGBENCH_EXP)
2554 dval = exp(dval);
2555 /* else is cast: do nothing */
2556
2557 setDoubleValue(retval, dval);
2558 return true;
2559 }
2560
2561 /* 1 int argument */
2562 case PGBENCH_INT:
2563 {
2564 int64 ival;
2565
2566 Assert(nargs == 1);
2567
2568 if (!coerceToInt(&vargs[0], &ival))
2569 return false;
2570
2571 setIntValue(retval, ival);
2572 return true;
2573 }
2574
2575 /* variable number of arguments */
2576 case PGBENCH_LEAST:
2577 case PGBENCH_GREATEST:
2578 {
2579 bool havedouble;
2580 int i;
2581
2582 Assert(nargs >= 1);
2583
2584 /* need double result if any input is double */
2585 havedouble = false;
2586 for (i = 0; i < nargs; i++)
2587 {
2588 if (vargs[i].type == PGBT_DOUBLE)
2589 {
2590 havedouble = true;
2591 break;
2592 }
2593 }
2594 if (havedouble)
2595 {
2596 double extremum;
2597
2598 if (!coerceToDouble(&vargs[0], &extremum))
2599 return false;
2600 for (i = 1; i < nargs; i++)
2601 {
2602 double dval;
2603
2604 if (!coerceToDouble(&vargs[i], &dval))
2605 return false;
2606 if (func == PGBENCH_LEAST)
2607 extremum = Min(extremum, dval);
2608 else
2609 extremum = Max(extremum, dval);
2610 }
2611 setDoubleValue(retval, extremum);
2612 }
2613 else
2614 {
2616
2617 if (!coerceToInt(&vargs[0], &extremum))
2618 return false;
2619 for (i = 1; i < nargs; i++)
2620 {
2621 int64 ival;
2622
2623 if (!coerceToInt(&vargs[i], &ival))
2624 return false;
2625 if (func == PGBENCH_LEAST)
2626 extremum = Min(extremum, ival);
2627 else
2628 extremum = Max(extremum, ival);
2629 }
2630 setIntValue(retval, extremum);
2631 }
2632 return true;
2633 }
2634
2635 /* random functions */
2636 case PGBENCH_RANDOM:
2640 {
2641 int64 imin,
2642 imax,
2643 delta;
2644
2645 Assert(nargs >= 2);
2646
2647 if (!coerceToInt(&vargs[0], &imin) ||
2648 !coerceToInt(&vargs[1], &imax))
2649 return false;
2650
2651 /* check random range */
2652 if (unlikely(imin > imax))
2653 {
2654 pg_log_error("empty range given to random");
2655 return false;
2656 }
2657 else if (unlikely(pg_sub_s64_overflow(imax, imin, &delta) ||
2658 pg_add_s64_overflow(delta, 1, &delta)))
2659 {
2660 /* prevent int overflows in random functions */
2661 pg_log_error("random range is too large");
2662 return false;
2663 }
2664
2665 if (func == PGBENCH_RANDOM)
2666 {
2667 Assert(nargs == 2);
2668 setIntValue(retval, getrand(&st->cs_func_rs, imin, imax));
2669 }
2670 else /* gaussian & exponential */
2671 {
2672 double param;
2673
2674 Assert(nargs == 3);
2675
2676 if (!coerceToDouble(&vargs[2], &param))
2677 return false;
2678
2679 if (func == PGBENCH_RANDOM_GAUSSIAN)
2680 {
2681 if (param < MIN_GAUSSIAN_PARAM)
2682 {
2683 pg_log_error("gaussian parameter must be at least %f (not %f)",
2684 MIN_GAUSSIAN_PARAM, param);
2685 return false;
2686 }
2687
2688 setIntValue(retval,
2690 imin, imax, param));
2691 }
2692 else if (func == PGBENCH_RANDOM_ZIPFIAN)
2693 {
2695 {
2696 pg_log_error("zipfian parameter must be in range [%.3f, %.0f] (not %f)",
2698 return false;
2699 }
2700
2701 setIntValue(retval,
2702 getZipfianRand(&st->cs_func_rs, imin, imax, param));
2703 }
2704 else /* exponential */
2705 {
2706 if (param <= 0.0)
2707 {
2708 pg_log_error("exponential parameter must be greater than zero (not %f)",
2709 param);
2710 return false;
2711 }
2712
2713 setIntValue(retval,
2715 imin, imax, param));
2716 }
2717 }
2718
2719 return true;
2720 }
2721
2722 case PGBENCH_POW:
2723 {
2724 PgBenchValue *lval = &vargs[0];
2725 PgBenchValue *rval = &vargs[1];
2726 double ld,
2727 rd;
2728
2729 Assert(nargs == 2);
2730
2731 if (!coerceToDouble(lval, &ld) ||
2733 return false;
2734
2735 setDoubleValue(retval, pow(ld, rd));
2736
2737 return true;
2738 }
2739
2740 case PGBENCH_IS:
2741 {
2742 Assert(nargs == 2);
2743
2744 /*
2745 * note: this simple implementation is more permissive than
2746 * SQL
2747 */
2748 setBoolValue(retval,
2749 vargs[0].type == vargs[1].type &&
2750 vargs[0].u.bval == vargs[1].u.bval);
2751 return true;
2752 }
2753
2754 /* hashing */
2755 case PGBENCH_HASH_FNV1A:
2757 {
2758 int64 val,
2759 seed;
2760
2761 Assert(nargs == 2);
2762
2763 if (!coerceToInt(&vargs[0], &val) ||
2764 !coerceToInt(&vargs[1], &seed))
2765 return false;
2766
2767 if (func == PGBENCH_HASH_MURMUR2)
2768 setIntValue(retval, getHashMurmur2(val, seed));
2769 else if (func == PGBENCH_HASH_FNV1A)
2770 setIntValue(retval, getHashFnv1a(val, seed));
2771 else
2772 /* cannot get here */
2773 Assert(0);
2774
2775 return true;
2776 }
2777
2778 case PGBENCH_PERMUTE:
2779 {
2780 int64 val,
2781 size,
2782 seed;
2783
2784 Assert(nargs == 3);
2785
2786 if (!coerceToInt(&vargs[0], &val) ||
2787 !coerceToInt(&vargs[1], &size) ||
2788 !coerceToInt(&vargs[2], &seed))
2789 return false;
2790
2791 if (size <= 0)
2792 {
2793 pg_log_error("permute size parameter must be greater than zero");
2794 return false;
2795 }
2796
2797 setIntValue(retval, permute(val, size, seed));
2798 return true;
2799 }
2800
2801 default:
2802 /* cannot get here */
2803 Assert(0);
2804 /* dead code to avoid a compiler warning */
2805 return false;
2806 }
#define Min(x, y)
Definition c.h:1091
#define Max(x, y)
Definition c.h:1085
#define PG_INT64_MIN
Definition c.h:675
#define unlikely(x)
Definition c.h:438
static bool pg_mul_s64_overflow(int64 a, int64 b, int64 *result)
Definition int.h:293
static bool pg_sub_s64_overflow(int64 a, int64 b, int64 *result)
Definition int.h:262
static bool pg_add_s64_overflow(int64 a, int64 b, int64 *result)
Definition int.h:235
static int64 getZipfianRand(pg_prng_state *state, int64 min, int64 max, double s)
Definition pgbench.c:1221
static void setIntValue(PgBenchValue *pv, int64 ival)
Definition pgbench.c:2104
static bool coerceToInt(PgBenchValue *pval, int64 *ival)
Definition pgbench.c:2039
static int64 getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
Definition pgbench.c:1103
static void setDoubleValue(PgBenchValue *pv, double dval)
Definition pgbench.c:2112
#define MIN_ZIPFIAN_PARAM
Definition pgbench.c:171
static int64 permute(const int64 val, const int64 isize, const int64 seed)
Definition pgbench.c:1293
#define MAX_FARGS
Definition pgbench.c:2236
static int64 getHashMurmur2(int64 val, uint64 seed)
Definition pgbench.c:1260
static int64 getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
Definition pgbench.c:1127
#define MIN_GAUSSIAN_PARAM
Definition pgbench.c:169
#define M_PI
Definition pgbench.c:74
static int64 getHashFnv1a(int64 val, uint64 seed)
Definition pgbench.c:1235
static bool coerceToDouble(PgBenchValue *pval, double *dval)
Definition pgbench.c:2067
#define MAX_ZIPFIAN_PARAM
Definition pgbench.c:172
@ PGBENCH_DIV
Definition pgbench.h:70
@ PGBENCH_DOUBLE
Definition pgbench.h:77
@ PGBENCH_LT
Definition pgbench.h:98
@ PGBENCH_LN
Definition pgbench.h:80
@ PGBENCH_RANDOM_EXPONENTIAL
Definition pgbench.h:84
@ PGBENCH_RSHIFT
Definition pgbench.h:94
@ PGBENCH_DEBUG
Definition pgbench.h:72
@ PGBENCH_MOD
Definition pgbench.h:71
@ PGBENCH_GREATEST
Definition pgbench.h:75
@ PGBENCH_BITXOR
Definition pgbench.h:92
@ PGBENCH_RANDOM_ZIPFIAN
Definition pgbench.h:85
@ PGBENCH_INT
Definition pgbench.h:76
@ PGBENCH_NE
Definition pgbench.h:96
@ PGBENCH_LE
Definition pgbench.h:97
@ PGBENCH_EXP
Definition pgbench.h:81
@ PGBENCH_PI
Definition pgbench.h:78
@ PGBENCH_ADD
Definition pgbench.h:67
@ PGBENCH_EQ
Definition pgbench.h:95
@ PGBENCH_LSHIFT
Definition pgbench.h:93
@ PGBENCH_RANDOM
Definition pgbench.h:82
@ PGBENCH_POW
Definition pgbench.h:86
@ PGBENCH_IS
Definition pgbench.h:99
@ PGBENCH_SUB
Definition pgbench.h:68
@ PGBENCH_HASH_MURMUR2
Definition pgbench.h:102
@ PGBENCH_ABS
Definition pgbench.h:73
@ PGBENCH_BITOR
Definition pgbench.h:91
@ PGBENCH_SQRT
Definition pgbench.h:79
@ PGBENCH_LEAST
Definition pgbench.h:74
@ PGBENCH_PERMUTE
Definition pgbench.h:103
@ PGBENCH_HASH_FNV1A
Definition pgbench.h:101
@ PGBENCH_NOT
Definition pgbench.h:89
@ PGBENCH_BITAND
Definition pgbench.h:90
@ PGBENCH_RANDOM_GAUSSIAN
Definition pgbench.h:83
@ PGBENCH_MUL
Definition pgbench.h:69
PgBenchValueType type
Definition pgbench.h:46
const char * type

References Assert, b, PgBenchValue::bval, coerceToBool(), coerceToDouble(), coerceToInt(), CState::command, CState::cs_func_rs, evaluateExpr(), PgBenchExprLink::expr, fb(), fprintf, getExponentialRand(), getGaussianRand(), getHashFnv1a(), getHashMurmur2(), getrand(), getZipfianRand(), i, INT64_FORMAT, M_PI, Max, MAX_FARGS, MAX_ZIPFIAN_PARAM, Min, MIN_GAUSSIAN_PARAM, MIN_ZIPFIAN_PARAM, PgBenchExprLink::next, permute(), pg_add_s64_overflow(), PG_INT64_MIN, pg_log_error, pg_mul_s64_overflow(), pg_sub_s64_overflow(), PGBENCH_ABS, PGBENCH_ADD, PGBENCH_BITAND, PGBENCH_BITOR, PGBENCH_BITXOR, PGBENCH_DEBUG, PGBENCH_DIV, PGBENCH_DOUBLE, PGBENCH_EQ, PGBENCH_EXP, PGBENCH_GREATEST, PGBENCH_HASH_FNV1A, PGBENCH_HASH_MURMUR2, PGBENCH_INT, PGBENCH_IS, PGBENCH_LE, PGBENCH_LEAST, PGBENCH_LN, PGBENCH_LSHIFT, PGBENCH_LT, PGBENCH_MOD, PGBENCH_MUL, PGBENCH_NE, PGBENCH_NOT, PGBENCH_PERMUTE, PGBENCH_PI, PGBENCH_POW, PGBENCH_RANDOM, PGBENCH_RANDOM_EXPONENTIAL, PGBENCH_RANDOM_GAUSSIAN, PGBENCH_RANDOM_ZIPFIAN, PGBENCH_RSHIFT, PGBENCH_SQRT, PGBENCH_SUB, PGBT_BOOLEAN, PGBT_DOUBLE, PGBT_INT, PGBT_NULL, setBoolValue(), setDoubleValue(), setIntValue(), setNullValue(), type, PgBenchValue::type, unlikely, CState::use_file, and val.

Referenced by evalFunc().

◆ evaluateExpr()

static bool evaluateExpr ( CState st,
PgBenchExpr expr,
PgBenchValue retval 
)
static

Definition at line 2826 of file pgbench.c.

2828{
2829 switch (expr->etype)
2830 {
2831 case ENODE_CONSTANT:
2832 {
2833 *retval = expr->u.constant;
2834 return true;
2835 }
2836
2837 case ENODE_VARIABLE:
2838 {
2839 Variable *var;
2840
2841 if ((var = lookupVariable(&st->variables, expr->u.variable.varname)) == NULL)
2842 {
2843 pg_log_error("undefined variable \"%s\"", expr->u.variable.varname);
2844 return false;
2845 }
2846
2847 if (!makeVariableValue(var))
2848 return false;
2849
2850 *retval = var->value;
2851 return true;
2852 }
2853
2854 case ENODE_FUNCTION:
2855 return evalFunc(st,
2856 expr->u.function.function,
2857 expr->u.function.args,
2858 retval);
2859
2860 default:
2861 /* internal error which should never occur */
2862 pg_fatal("unexpected enode type in evaluation: %d", expr->etype);
2863 }
static bool evalFunc(CState *st, PgBenchFunction func, PgBenchExprLink *args, PgBenchValue *retval)
Definition pgbench.c:2810
static bool makeVariableValue(Variable *var)
Definition pgbench.c:1658
static Variable * lookupVariable(Variables *variables, char *name)
Definition pgbench.c:1598
@ ENODE_VARIABLE
Definition pgbench.h:60
@ ENODE_CONSTANT
Definition pgbench.h:59
@ ENODE_FUNCTION
Definition pgbench.h:61
Variables variables
Definition pgbench.c:622
PgBenchValue constant
Definition pgbench.h:115
union PgBenchExpr::@43 u
char * varname
Definition pgbench.h:118
PgBenchFunction function
Definition pgbench.h:122
PgBenchExprType etype
Definition pgbench.h:112
struct PgBenchExpr::@43::@44 variable
PgBenchValue value
Definition pgbench.c:327

References PgBenchExpr::constant, ENODE_CONSTANT, ENODE_FUNCTION, ENODE_VARIABLE, PgBenchExpr::etype, evalFunc(), fb(), PgBenchExpr::function, lookupVariable(), makeVariableValue(), pg_fatal, pg_log_error, PgBenchExpr::u, Variable::value, PgBenchExpr::variable, CState::variables, and PgBenchExpr::varname.

Referenced by evalLazyFunc(), evalStandardFunc(), and executeMetaCommand().

◆ evaluateSleep()

static bool evaluateSleep ( Variables variables,
int  argc,
char **  argv,
int usecs 
)
static

Definition at line 3432 of file pgbench.c.

3434{
3435 char *var;
3436 int usec;
3437
3438 if (*argv[1] == ':')
3439 {
3440 if ((var = getVariable(variables, argv[1] + 1)) == NULL)
3441 {
3442 pg_log_error("%s: undefined variable \"%s\"", argv[0], argv[1] + 1);
3443 return false;
3444 }
3445
3446 usec = atoi(var);
3447
3448 /* Raise an error if the value of a variable is not a number */
3449 if (usec == 0 && !isdigit((unsigned char) *var))
3450 {
3451 pg_log_error("%s: invalid sleep time \"%s\" for variable \"%s\"",
3452 argv[0], var, argv[1] + 1);
3453 return false;
3454 }
3455 }
3456 else
3457 usec = atoi(argv[1]);
3458
3459 if (argc > 2)
3460 {
3461 if (pg_strcasecmp(argv[2], "ms") == 0)
3462 usec *= 1000;
3463 else if (pg_strcasecmp(argv[2], "s") == 0)
3464 usec *= 1000000;
3465 }
3466 else
3467 usec *= 1000000;
3468
3469 *usecs = usec;
3470 return true;
int pg_strcasecmp(const char *s1, const char *s2)

References fb(), getVariable(), pg_log_error, and pg_strcasecmp().

Referenced by executeMetaCommand().

◆ executeMetaCommand()

static ConnectionStateEnum executeMetaCommand ( CState st,
pg_time_usec_t now 
)
static

Definition at line 4365 of file pgbench.c.

4367{
4368 Command *command = sql_script[st->use_file].commands[st->command];
4369 int argc;
4370 char **argv;
4371
4372 Assert(command != NULL && command->type == META_COMMAND);
4373
4374 argc = command->argc;
4375 argv = command->argv;
4376
4378 {
4380
4382
4383 printfPQExpBuffer(&buf, "client %d executing \\%s", st->id, argv[0]);
4384 for (int i = 1; i < argc; i++)
4385 appendPQExpBuffer(&buf, " %s", argv[i]);
4386
4387 pg_log_debug("%s", buf.data);
4388
4390 }
4391
4392 if (command->meta == META_SLEEP)
4393 {
4394 int usec;
4395
4396 /*
4397 * A \sleep doesn't execute anything, we just get the delay from the
4398 * argument, and enter the CSTATE_SLEEP state. (The per-command
4399 * latency will be recorded in CSTATE_SLEEP state, not here, after the
4400 * delay has elapsed.)
4401 */
4402 if (!evaluateSleep(&st->variables, argc, argv, &usec))
4403 {
4404 commandFailed(st, "sleep", "execution of meta-command failed");
4405 return CSTATE_ABORTED;
4406 }
4407
4409 st->sleep_until = (*now) + usec;
4410 return CSTATE_SLEEP;
4411 }
4412 else if (command->meta == META_SET)
4413 {
4414 PgBenchExpr *expr = command->expr;
4416
4417 if (!evaluateExpr(st, expr, &result))
4418 {
4419 commandFailed(st, argv[0], "evaluation of meta-command failed");
4420 return CSTATE_ABORTED;
4421 }
4422
4423 if (!putVariableValue(&st->variables, argv[0], argv[1], &result))
4424 {
4425 commandFailed(st, "set", "assignment of meta-command failed");
4426 return CSTATE_ABORTED;
4427 }
4428 }
4429 else if (command->meta == META_IF)
4430 {
4431 /* backslash commands with an expression to evaluate */
4432 PgBenchExpr *expr = command->expr;
4434 bool cond;
4435
4436 if (!evaluateExpr(st, expr, &result))
4437 {
4438 commandFailed(st, argv[0], "evaluation of meta-command failed");
4439 return CSTATE_ABORTED;
4440 }
4441
4442 cond = valueTruth(&result);
4444 }
4445 else if (command->meta == META_ELIF)
4446 {
4447 /* backslash commands with an expression to evaluate */
4448 PgBenchExpr *expr = command->expr;
4450 bool cond;
4451
4453 {
4454 /* elif after executed block, skip eval and wait for endif. */
4456 return CSTATE_END_COMMAND;
4457 }
4458
4459 if (!evaluateExpr(st, expr, &result))
4460 {
4461 commandFailed(st, argv[0], "evaluation of meta-command failed");
4462 return CSTATE_ABORTED;
4463 }
4464
4465 cond = valueTruth(&result);
4468 }
4469 else if (command->meta == META_ELSE)
4470 {
4471 switch (conditional_stack_peek(st->cstack))
4472 {
4473 case IFSTATE_TRUE:
4475 break;
4476 case IFSTATE_FALSE: /* inconsistent if active */
4477 case IFSTATE_IGNORED: /* inconsistent if active */
4478 case IFSTATE_NONE: /* else without if */
4479 case IFSTATE_ELSE_TRUE: /* else after else */
4480 case IFSTATE_ELSE_FALSE: /* else after else */
4481 default:
4482 /* dead code if conditional check is ok */
4483 Assert(false);
4484 }
4485 }
4486 else if (command->meta == META_ENDIF)
4487 {
4490 }
4491 else if (command->meta == META_SETSHELL)
4492 {
4493 if (!runShellCommand(&st->variables, argv[1], argv + 2, argc - 2))
4494 {
4495 commandFailed(st, "setshell", "execution of meta-command failed");
4496 return CSTATE_ABORTED;
4497 }
4498 }
4499 else if (command->meta == META_SHELL)
4500 {
4501 if (!runShellCommand(&st->variables, NULL, argv + 1, argc - 1))
4502 {
4503 commandFailed(st, "shell", "execution of meta-command failed");
4504 return CSTATE_ABORTED;
4505 }
4506 }
4507 else if (command->meta == META_STARTPIPELINE)
4508 {
4509 /*
4510 * In pipeline mode, we use a workflow based on libpq pipeline
4511 * functions.
4512 */
4513 if (querymode == QUERY_SIMPLE)
4514 {
4515 commandFailed(st, "startpipeline", "cannot use pipeline mode with the simple query protocol");
4516 return CSTATE_ABORTED;
4517 }
4518
4519 /*
4520 * If we're in prepared-query mode, we need to prepare all the
4521 * commands that are inside the pipeline before we actually start the
4522 * pipeline itself. This solves the problem that running BEGIN
4523 * ISOLATION LEVEL SERIALIZABLE in a pipeline would fail due to a
4524 * snapshot having been acquired by the prepare within the pipeline.
4525 */
4528
4530 {
4531 commandFailed(st, "startpipeline", "already in pipeline mode");
4532 return CSTATE_ABORTED;
4533 }
4534 if (PQenterPipelineMode(st->con) == 0)
4535 {
4536 commandFailed(st, "startpipeline", "failed to enter pipeline mode");
4537 return CSTATE_ABORTED;
4538 }
4539 }
4540 else if (command->meta == META_SYNCPIPELINE)
4541 {
4543 {
4544 commandFailed(st, "syncpipeline", "not in pipeline mode");
4545 return CSTATE_ABORTED;
4546 }
4547 if (PQsendPipelineSync(st->con) == 0)
4548 {
4549 commandFailed(st, "syncpipeline", "failed to send a pipeline sync");
4550 return CSTATE_ABORTED;
4551 }
4552 st->num_syncs++;
4553 }
4554 else if (command->meta == META_ENDPIPELINE)
4555 {
4557 {
4558 commandFailed(st, "endpipeline", "not in pipeline mode");
4559 return CSTATE_ABORTED;
4560 }
4561 if (!PQpipelineSync(st->con))
4562 {
4563 commandFailed(st, "endpipeline", "failed to send a pipeline sync");
4564 return CSTATE_ABORTED;
4565 }
4566 st->num_syncs++;
4567 /* Now wait for the PGRES_PIPELINE_SYNC and exit pipeline mode there */
4568 /* collect pending results before getting out of pipeline mode */
4569 return CSTATE_WAIT_RESULT;
4570 }
4571
4572 /*
4573 * executing the expression or shell command might have taken a
4574 * non-negligible amount of time, so reset 'now'
4575 */
4576 *now = 0;
4577
4578 return CSTATE_END_COMMAND;
uint32 result
int PQenterPipelineMode(PGconn *conn)
Definition fe-exec.c:3073
int PQsendPipelineSync(PGconn *conn)
Definition fe-exec.c:3313
enum pg_log_level __pg_log_level
Definition logging.c:21
@ PG_LOG_DEBUG
Definition logging.h:26
static QueryMode querymode
Definition pgbench.c:721
static void prepareCommandsInPipeline(CState *st)
Definition pgbench.c:3118
static bool runShellCommand(Variables *variables, char *variable, char **argv, int argc)
Definition pgbench.c:2911
static bool evaluateSleep(Variables *variables, int argc, char **argv, int *usecs)
Definition pgbench.c:3432
static bool putVariableValue(Variables *variables, const char *context, char *name, const PgBenchValue *value)
Definition pgbench.c:1846
pg_time_usec_t sleep_until
Definition pgbench.c:626
PgBenchExpr * expr
Definition pgbench.c:758
char * argv[MAX_ARGS]
Definition pgbench.c:755
int argc
Definition pgbench.c:754

References __pg_log_level, appendPQExpBuffer(), Command::argc, Command::argv, Assert, buf, CState::command, commandFailed(), ParsedScript::commands, CState::con, conditional_stack_empty(), conditional_stack_peek(), conditional_stack_poke(), conditional_stack_pop(), conditional_stack_push(), CState::cstack, CSTATE_ABORTED, CSTATE_END_COMMAND, CSTATE_SLEEP, CSTATE_WAIT_RESULT, evaluateExpr(), evaluateSleep(), Command::expr, fb(), i, CState::id, IFSTATE_ELSE_FALSE, IFSTATE_ELSE_TRUE, IFSTATE_FALSE, IFSTATE_IGNORED, IFSTATE_NONE, IFSTATE_TRUE, initPQExpBuffer(), Command::meta, META_COMMAND, META_ELIF, META_ELSE, META_ENDIF, META_ENDPIPELINE, META_IF, META_SET, META_SETSHELL, META_SHELL, META_SLEEP, META_STARTPIPELINE, META_SYNCPIPELINE, now(), CState::num_syncs, PG_LOG_DEBUG, pg_log_debug, pg_time_now_lazy(), PQ_PIPELINE_OFF, PQ_PIPELINE_ON, PQenterPipelineMode(), PQpipelineStatus(), PQpipelineSync(), PQsendPipelineSync(), prepareCommandsInPipeline(), printfPQExpBuffer(), putVariableValue(), QUERY_PREPARED, QUERY_SIMPLE, querymode, result, runShellCommand(), CState::sleep_until, sql_script, termPQExpBuffer(), Command::type, unlikely, CState::use_file, valueTruth(), and CState::variables.

Referenced by advanceConnectionState().

◆ executeStatement()

static void executeStatement ( PGconn con,
const char sql 
)
static

Definition at line 1494 of file pgbench.c.

1496{
1497 PGresult *res;
1498
1499 res = PQexec(con, sql);
1500 if (PQresultStatus(res) != PGRES_COMMAND_OK)
1501 {
1502 pg_log_error("query failed: %s", PQerrorMessage(con));
1503 pg_log_error_detail("Query was: %s", sql);
1504 exit(1);
1505 }
1506 PQclear(res);
PGresult * PQexec(PGconn *conn, const char *query)
Definition fe-exec.c:2279

References fb(), pg_log_error, pg_log_error_detail, PGRES_COMMAND_OK, PQclear, PQerrorMessage(), PQexec(), and PQresultStatus.

Referenced by createPartitions(), initCreateFKeys(), initCreatePKeys(), initCreateTables(), initDropTables(), initGenerateDataClientSide(), initGenerateDataServerSide(), initTruncateTables(), and initVacuum().

◆ findBuiltin()

static const BuiltinScript * findBuiltin ( const char name)
static

Definition at line 6217 of file pgbench.c.

6219{
6220 int i,
6221 found = 0,
6222 len = strlen(name);
6223 const BuiltinScript *result = NULL;
6224
6225 for (i = 0; i < lengthof(builtin_script); i++)
6226 {
6227 if (strncmp(builtin_script[i].name, name, len) == 0)
6228 {
6230 found++;
6231 }
6232 }
6233
6234 /* ok, unambiguous result */
6235 if (found == 1)
6236 return result;
6237
6238 /* error cases */
6239 if (found == 0)
6240 pg_log_error("no builtin script found for name \"%s\"", name);
6241 else /* found > 1 */
6242 pg_log_error("ambiguous builtin name: %d builtin scripts found for prefix \"%s\"", found, name);
6243
6245 exit(1);
#define lengthof(array)
Definition c.h:873
const void size_t len
static void listAvailableScripts(void)
Definition pgbench.c:6205
static const BuiltinScript builtin_script[]
Definition pgbench.c:789

References builtin_script, fb(), i, len, lengthof, listAvailableScripts(), name, pg_log_error, and result.

Referenced by main().

◆ finishCon()

static void finishCon ( CState st)
static

Definition at line 7830 of file pgbench.c.

7832{
7833 if (st->con != NULL)
7834 {
7835 PQfinish(st->con);
7836 st->con = NULL;
7837 }

References CState::con, fb(), and PQfinish().

Referenced by advanceConnectionState(), and disconnect_all().

◆ free_command()

static void free_command ( Command command)
static

Definition at line 5680 of file pgbench.c.

5682{
5683 termPQExpBuffer(&command->lines);
5684 pg_free(command->first_line);
5685 for (int i = 0; i < command->argc; i++)
5686 pg_free(command->argv[i]);
5687 pg_free(command->varprefix);
5688
5689 /*
5690 * It should also free expr recursively, but this is currently not needed
5691 * as only gset commands (which do not have an expression) are freed.
5692 */
5693 pg_free(command);
PQExpBufferData lines
Definition pgbench.c:750
char * first_line
Definition pgbench.c:751

References Command::argc, Command::argv, Command::first_line, i, Command::lines, pg_free(), termPQExpBuffer(), and Command::varprefix.

Referenced by ParseScript().

◆ free_socket_set()

static void free_socket_set ( socket_set sa)
static

Definition at line 8001 of file pgbench.c.

8003{
8004 pg_free(sa);

References pg_free().

Referenced by threadRun().

◆ get_table_relkind()

static char get_table_relkind ( PGconn con,
const char table 
)
static

Definition at line 860 of file pgbench.c.

862{
863 PGresult *res;
864 char *val;
865 char relkind;
866 const char *params[1] = {table};
867 const char *sql =
868 "SELECT relkind FROM pg_catalog.pg_class WHERE oid=$1::pg_catalog.regclass";
869
870 res = PQexecParams(con, sql, 1, NULL, params, NULL, NULL, 0);
872 {
873 pg_log_error("query failed: %s", PQerrorMessage(con));
874 pg_log_error_detail("Query was: %s", sql);
875 exit(1);
876 }
877 val = PQgetvalue(res, 0, 0);
878 Assert(strlen(val) == 1);
879 relkind = val[0];
880 PQclear(res);
881
882 return relkind;
PGresult * PQexecParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
Definition fe-exec.c:2293
#define PQgetvalue
@ PGRES_TUPLES_OK
Definition libpq-fe.h:134
static const struct lconv_member_info table[]

References Assert, fb(), pg_log_error, pg_log_error_detail, PGRES_TUPLES_OK, PQclear, PQerrorMessage(), PQexecParams(), PQgetvalue, PQresultStatus, table, and val.

Referenced by initPopulateTable().

◆ getExponentialRand()

static int64 getExponentialRand ( pg_prng_state state,
int64  min,
int64  max,
double  parameter 
)
static

Definition at line 1103 of file pgbench.c.

1106{
1107 double cut,
1108 uniform,
1109 rand;
1110
1111 /* abort if wrong parameter, but must really be checked beforehand */
1112 Assert(parameter > 0.0);
1113 cut = exp(-parameter);
1114 /* pg_prng_double value in [0, 1), uniform in (0, 1] */
1115 uniform = 1.0 - pg_prng_double(state);
1116
1117 /*
1118 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
1119 */
1120 Assert((1.0 - cut) != 0.0);
1121 rand = -log(cut + (1.0 - cut) * uniform) / parameter;
1122 /* return int64 random number within between min and max */
1123 return min + (int64) ((max - min + 1) * rand);

References Assert, fb(), and pg_prng_double().

Referenced by evalStandardFunc().

◆ getFailures()

static int64 getFailures ( const StatsData stats)
static

Definition at line 4584 of file pgbench.c.

4586{
4587 return (stats->serialization_failures +
4588 stats->deadlock_failures +
4589 stats->other_sql_failures);

References StatsData::deadlock_failures, StatsData::other_sql_failures, and StatsData::serialization_failures.

Referenced by printProgressReport(), and printResults().

◆ getGaussianRand()

static int64 getGaussianRand ( pg_prng_state state,
int64  min,
int64  max,
double  parameter 
)
static

Definition at line 1127 of file pgbench.c.

1130{
1131 double stdev;
1132 double rand;
1133
1134 /* abort if parameter is too low, but must really be checked beforehand */
1136
1137 /*
1138 * Get normally-distributed random number in the range -parameter <= stdev
1139 * < parameter.
1140 *
1141 * This loop is executed until the number is in the expected range.
1142 *
1143 * As the minimum parameter is 2.0, the probability of looping is low:
1144 * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the
1145 * average sinus multiplier as 2/pi, we have a 8.6% looping probability in
1146 * the worst case. For a parameter value of 5.0, the looping probability
1147 * is about e^{-5} * 2 / pi ~ 0.43%.
1148 */
1149 do
1150 {
1152 }
1154
1155 /* stdev is in [-parameter, parameter), normalization to [0,1) */
1156 rand = (stdev + parameter) / (parameter * 2.0);
1157
1158 /* return int64 random number within between min and max */
1159 return min + (int64) ((max - min + 1) * rand);
double pg_prng_double_normal(pg_prng_state *state)
Definition pg_prng.c:290

References Assert, fb(), MIN_GAUSSIAN_PARAM, and pg_prng_double_normal().

Referenced by evalStandardFunc().

◆ getHashFnv1a()

static int64 getHashFnv1a ( int64  val,
uint64  seed 
)
static

Definition at line 1235 of file pgbench.c.

1237{
1238 int64 result;
1239 int i;
1240
1241 result = FNV_OFFSET_BASIS ^ seed;
1242 for (i = 0; i < 8; ++i)
1243 {
1244 int32 octet = val & 0xff;
1245
1246 val = val >> 8;
1247 result = result ^ octet;
1249 }
1250
1251 return result;
int32_t int32
Definition c.h:620
#define FNV_OFFSET_BASIS
Definition pgbench.c:85
#define FNV_PRIME
Definition pgbench.c:84

References fb(), FNV_OFFSET_BASIS, FNV_PRIME, i, result, and val.

Referenced by evalStandardFunc().

◆ getHashMurmur2()

static int64 getHashMurmur2 ( int64  val,
uint64  seed 
)
static

Definition at line 1260 of file pgbench.c.

1262{
1263 uint64 result = seed ^ MM2_MUL_TIMES_8; /* sizeof(int64) */
1264 uint64 k = (uint64) val;
1265
1266 k *= MM2_MUL;
1267 k ^= k >> MM2_ROT;
1268 k *= MM2_MUL;
1269
1270 result ^= k;
1271 result *= MM2_MUL;
1272
1273 result ^= result >> MM2_ROT;
1274 result *= MM2_MUL;
1275 result ^= result >> MM2_ROT;
1276
1277 return (int64) result;
uint64_t uint64
Definition c.h:625
#define MM2_MUL_TIMES_8
Definition pgbench.c:87
#define MM2_ROT
Definition pgbench.c:88
#define MM2_MUL
Definition pgbench.c:86

References MM2_MUL, MM2_MUL_TIMES_8, MM2_ROT, result, and val.

Referenced by evalStandardFunc().

◆ getMetaCommand()

static MetaCommand getMetaCommand ( const char cmd)
static

Definition at line 2869 of file pgbench.c.

2871{
2873
2874 if (cmd == NULL)
2875 mc = META_NONE;
2876 else if (pg_strcasecmp(cmd, "set") == 0)
2877 mc = META_SET;
2878 else if (pg_strcasecmp(cmd, "setshell") == 0)
2879 mc = META_SETSHELL;
2880 else if (pg_strcasecmp(cmd, "shell") == 0)
2881 mc = META_SHELL;
2882 else if (pg_strcasecmp(cmd, "sleep") == 0)
2883 mc = META_SLEEP;
2884 else if (pg_strcasecmp(cmd, "if") == 0)
2885 mc = META_IF;
2886 else if (pg_strcasecmp(cmd, "elif") == 0)
2887 mc = META_ELIF;
2888 else if (pg_strcasecmp(cmd, "else") == 0)
2889 mc = META_ELSE;
2890 else if (pg_strcasecmp(cmd, "endif") == 0)
2891 mc = META_ENDIF;
2892 else if (pg_strcasecmp(cmd, "gset") == 0)
2893 mc = META_GSET;
2894 else if (pg_strcasecmp(cmd, "aset") == 0)
2895 mc = META_ASET;
2896 else if (pg_strcasecmp(cmd, "startpipeline") == 0)
2898 else if (pg_strcasecmp(cmd, "syncpipeline") == 0)
2900 else if (pg_strcasecmp(cmd, "endpipeline") == 0)
2902 else
2903 mc = META_NONE;
2904 return mc;
MetaCommand
Definition pgbench.c:696

References fb(), META_ASET, META_ELIF, META_ELSE, META_ENDIF, META_ENDPIPELINE, META_GSET, META_IF, META_NONE, META_SET, META_SETSHELL, META_SHELL, META_SLEEP, META_STARTPIPELINE, META_SYNCPIPELINE, and pg_strcasecmp().

Referenced by process_backslash_command().

◆ getPoissonRand()

static int64 getPoissonRand ( pg_prng_state state,
double  center 
)
static

Definition at line 1169 of file pgbench.c.

1171{
1172 /*
1173 * Use inverse transform sampling to generate a value > 0, such that the
1174 * expected (i.e. average) value is the given argument.
1175 */
1176 double uniform;
1177
1178 /* pg_prng_double value in [0, 1), uniform in (0, 1] */
1179 uniform = 1.0 - pg_prng_double(state);
1180
1181 return (int64) (-log(uniform) * center + 0.5);

References fb(), and pg_prng_double().

Referenced by advanceConnectionState().

◆ getQueryParams()

static void getQueryParams ( Variables variables,
const Command command,
const char **  params 
)
static

Definition at line 1966 of file pgbench.c.

1969{
1970 int i;
1971
1972 for (i = 0; i < command->argc - 1; i++)
1973 params[i] = getVariable(variables, command->argv[i + 1]);

References Command::argc, Command::argv, getVariable(), and i.

Referenced by sendCommand().

◆ getrand()

static int64 getrand ( pg_prng_state state,
int64  min,
int64  max 
)
static

Definition at line 1092 of file pgbench.c.

1094{
1095 return min + (int64) pg_prng_uint64_range(state, 0, max - min);
uint64 pg_prng_uint64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
Definition pg_prng.c:144

References pg_prng_uint64_range().

Referenced by chooseScript(), and evalStandardFunc().

◆ getResultString()

static const char * getResultString ( bool  skipped,
EStatus  estatus 
)
static

Definition at line 4596 of file pgbench.c.

4598{
4599 if (skipped)
4600 return "skipped";
4601 else if (failures_detailed)
4602 {
4603 switch (estatus)
4604 {
4606 return "serialization";
4608 return "deadlock";
4610 return "other";
4611 default:
4612 /* internal error which should never occur */
4613 pg_fatal("unexpected error status: %d", estatus);
4614 }
4615 }
4616 else
4617 return "failed";

References ESTATUS_DEADLOCK_ERROR, ESTATUS_OTHER_SQL_ERROR, ESTATUS_SERIALIZATION_ERROR, failures_detailed, and pg_fatal.

Referenced by doLog().

◆ getSQLErrorStatus()

static EStatus getSQLErrorStatus ( CState st,
const char sqlState 
)
static

Definition at line 3232 of file pgbench.c.

3234{
3236 if (PQstatus(st->con) == CONNECTION_BAD)
3237 return ESTATUS_CONN_ERROR;
3238
3239 if (sqlState != NULL)
3240 {
3245 }
3246
static void discardAvailableResults(CState *st)
Definition pgbench.c:3204
#define ERRCODE_T_R_DEADLOCK_DETECTED
Definition pgbench.c:78
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition pgbench.c:77

References CState::con, CONNECTION_BAD, discardAvailableResults(), ERRCODE_T_R_DEADLOCK_DETECTED, ERRCODE_T_R_SERIALIZATION_FAILURE, ESTATUS_CONN_ERROR, ESTATUS_DEADLOCK_ERROR, ESTATUS_OTHER_SQL_ERROR, ESTATUS_SERIALIZATION_ERROR, fb(), and PQstatus().

Referenced by readCommandResponse().

◆ GetTableInfo()

static void GetTableInfo ( PGconn con,
bool  scale_given 
)
static

Definition at line 5410 of file pgbench.c.

5412{
5413 PGresult *res;
5414
5415 /*
5416 * get the scaling factor that should be same as count(*) from
5417 * pgbench_branches if this is not a custom query
5418 */
5419 res = PQexec(con, "select count(*) from pgbench_branches");
5420 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5421 {
5423
5424 pg_log_error("could not count number of branches: %s", PQerrorMessage(con));
5425
5427 pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".",
5428 PQdb(con));
5429
5430 exit(1);
5431 }
5432 scale = atoi(PQgetvalue(res, 0, 0));
5433 if (scale < 0)
5434 pg_fatal("invalid count(*) from pgbench_branches: \"%s\"",
5435 PQgetvalue(res, 0, 0));
5436 PQclear(res);
5437
5438 /* warn if we override user-given -s switch */
5439 if (scale_given)
5440 pg_log_warning("scale option ignored, using count from pgbench_branches table (%d)",
5441 scale);
5442
5443 /*
5444 * Get the partition information for the first "pgbench_accounts" table
5445 * found in search_path.
5446 *
5447 * The result is empty if no "pgbench_accounts" is found.
5448 *
5449 * Otherwise, it always returns one row even if the table is not
5450 * partitioned (in which case the partition strategy is NULL).
5451 *
5452 * The number of partitions can be 0 even for partitioned tables, if no
5453 * partition is attached.
5454 *
5455 * We assume no partitioning on any failure, so as to avoid failing on an
5456 * old version without "pg_partitioned_table".
5457 */
5458 res = PQexec(con,
5459 "select o.n, p.partstrat, pg_catalog.count(i.inhparent) "
5460 "from pg_catalog.pg_class as c "
5461 "join pg_catalog.pg_namespace as n on (n.oid = c.relnamespace) "
5462 "cross join lateral (select pg_catalog.array_position(pg_catalog.current_schemas(true), n.nspname)) as o(n) "
5463 "left join pg_catalog.pg_partitioned_table as p on (p.partrelid = c.oid) "
5464 "left join pg_catalog.pg_inherits as i on (c.oid = i.inhparent) "
5465 "where c.relname = 'pgbench_accounts' and o.n is not null "
5466 "group by 1, 2 "
5467 "order by 1 asc "
5468 "limit 1");
5469
5470 if (PQresultStatus(res) != PGRES_TUPLES_OK)
5471 {
5472 /* probably an older version, coldly assume no partitioning */
5474 partitions = 0;
5475 }
5476 else if (PQntuples(res) == 0)
5477 {
5478 /*
5479 * This case is unlikely as pgbench already found "pgbench_branches"
5480 * above to compute the scale.
5481 */
5482 pg_log_error("no pgbench_accounts table found in \"search_path\"");
5483 pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".", PQdb(con));
5484 exit(1);
5485 }
5486 else /* PQntuples(res) == 1 */
5487 {
5488 /* normal case, extract partition information */
5489 if (PQgetisnull(res, 0, 1))
5491 else
5492 {
5493 char *ps = PQgetvalue(res, 0, 1);
5494
5495 /* column must be there */
5496 Assert(ps != NULL);
5497
5498 if (strcmp(ps, "r") == 0)
5500 else if (strcmp(ps, "h") == 0)
5502 else
5503 {
5504 /* possibly a newer version with new partition method */
5505 pg_fatal("unexpected partition method: \"%s\"", ps);
5506 }
5507 }
5508
5509 partitions = atoi(PQgetvalue(res, 0, 2));
5510 }
5511
5512 PQclear(res);
char * PQdb(const PGconn *conn)
#define PQresultErrorField
#define PQgetisnull
#define PQntuples
#define ERRCODE_UNDEFINED_TABLE
Definition pgbench.c:79
#define pg_log_warning(...)
Definition pgfnames.c:24
#define PG_DIAG_SQLSTATE

References Assert, ERRCODE_UNDEFINED_TABLE, fb(), PART_HASH, PART_NONE, PART_RANGE, partition_method, partitions, PG_DIAG_SQLSTATE, pg_fatal, pg_log_error, pg_log_error_hint, pg_log_warning, PGRES_TUPLES_OK, PQclear, PQdb(), PQerrorMessage(), PQexec(), PQgetisnull, PQgetvalue, PQntuples, PQresultErrorField, PQresultStatus, ps, and scale.

Referenced by main().

◆ getTransactionStatus()

static TStatus getTransactionStatus ( PGconn con)
static

Definition at line 3593 of file pgbench.c.

3595{
3597
3599 switch (tx_status)
3600 {
3601 case PQTRANS_IDLE:
3602 return TSTATUS_IDLE;
3603 case PQTRANS_INTRANS:
3604 case PQTRANS_INERROR:
3605 return TSTATUS_IN_BLOCK;
3606 case PQTRANS_UNKNOWN:
3607 /* PQTRANS_UNKNOWN is expected given a broken connection */
3608 if (PQstatus(con) == CONNECTION_BAD)
3609 return TSTATUS_CONN_ERROR;
3611 case PQTRANS_ACTIVE:
3612 default:
3613
3614 /*
3615 * We cannot find out whether we are in a transaction block or
3616 * not. Internal error which should never occur.
3617 */
3618 pg_log_error("unexpected transaction status %d", tx_status);
3619 return TSTATUS_OTHER_ERROR;
3620 }
3621
3622 /* not reached */
3623 Assert(false);
3624 return TSTATUS_OTHER_ERROR;
#define pg_fallthrough
Definition c.h:161
PGTransactionStatusType PQtransactionStatus(const PGconn *conn)
PGTransactionStatusType
Definition libpq-fe.h:152
@ PQTRANS_INTRANS
Definition libpq-fe.h:155
@ PQTRANS_IDLE
Definition libpq-fe.h:153
@ PQTRANS_ACTIVE
Definition libpq-fe.h:154
@ PQTRANS_UNKNOWN
Definition libpq-fe.h:157
@ PQTRANS_INERROR
Definition libpq-fe.h:156

References Assert, CONNECTION_BAD, fb(), pg_fallthrough, pg_log_error, PQstatus(), PQTRANS_ACTIVE, PQTRANS_IDLE, PQTRANS_INERROR, PQTRANS_INTRANS, PQTRANS_UNKNOWN, PQtransactionStatus(), TSTATUS_CONN_ERROR, TSTATUS_IDLE, TSTATUS_IN_BLOCK, and TSTATUS_OTHER_ERROR.

Referenced by advanceConnectionState().

◆ getVariable()

static char * getVariable ( Variables variables,
char name 
)
static

Definition at line 1625 of file pgbench.c.

1627{
1628 Variable *var;
1629 char stringform[64];
1630
1631 var = lookupVariable(variables, name);
1632 if (var == NULL)
1633 return NULL; /* not found */
1634
1635 if (var->svalue)
1636 return var->svalue; /* we have it in string form */
1637
1638 /* We need to produce a string equivalent of the value */
1639 Assert(var->value.type != PGBT_NO_VALUE);
1640 if (var->value.type == PGBT_NULL)
1641 snprintf(stringform, sizeof(stringform), "NULL");
1642 else if (var->value.type == PGBT_BOOLEAN)
1644 "%s", var->value.u.bval ? "true" : "false");
1645 else if (var->value.type == PGBT_INT)
1647 INT64_FORMAT, var->value.u.ival);
1648 else if (var->value.type == PGBT_DOUBLE)
1650 "%.*g", DBL_DIG, var->value.u.dval);
1651 else /* internal error, unexpected type */
1652 Assert(0);
1653 var->svalue = pg_strdup(stringform);
1654 return var->svalue;
char * pg_strdup(const char *in)
Definition fe_memutils.c:91
@ PGBT_NO_VALUE
Definition pgbench.h:36
#define snprintf
Definition port.h:261
union PgBenchValue::@42 u
bool bval
Definition pgbench.h:51
int64 ival
Definition pgbench.h:49
double dval
Definition pgbench.h:50
char * svalue
Definition pgbench.c:326

References Assert, PgBenchValue::bval, PgBenchValue::dval, fb(), INT64_FORMAT, PgBenchValue::ival, lookupVariable(), name, pg_strdup(), PGBT_BOOLEAN, PGBT_DOUBLE, PGBT_INT, PGBT_NO_VALUE, PGBT_NULL, snprintf, Variable::svalue, PgBenchValue::type, PgBenchValue::u, and Variable::value.

Referenced by assignVariables(), evaluateSleep(), getQueryParams(), and runShellCommand().

◆ getZipfianRand()

static int64 getZipfianRand ( pg_prng_state state,
int64  min,
int64  max,
double  s 
)
static

Definition at line 1221 of file pgbench.c.

1223{
1224 int64 n = max - min + 1;
1225
1226 /* abort if parameter is invalid */
1228
1229 return min - 1 + computeIterativeZipfian(state, n, s);
static int64 computeIterativeZipfian(pg_prng_state *state, int64 n, double s)
Definition pgbench.c:1191

References Assert, computeIterativeZipfian(), MAX_ZIPFIAN_PARAM, and MIN_ZIPFIAN_PARAM.

Referenced by evalStandardFunc().

◆ handle_sig_alarm()

static void handle_sig_alarm ( SIGNAL_ARGS  )
static

Definition at line 7846 of file pgbench.c.

7848{
7849 timer_exceeded = true;

References timer_exceeded.

Referenced by setalarm().

◆ initAccount()

static void initAccount ( PQExpBufferData sql,
int64  curr 
)
static

Definition at line 5017 of file pgbench.c.

5019{
5020 /* "filler" column defaults to blank padded empty string */
5022 INT64_FORMAT "\t" INT64_FORMAT "\t0\t\n",
5023 curr + 1, curr / naccounts + 1);

References fb(), INT64_FORMAT, naccounts, and printfPQExpBuffer().

Referenced by initGenerateDataClientSide().

◆ initBranch()

static void initBranch ( PQExpBufferData sql,
int64  curr 
)
static

Definition at line 4999 of file pgbench.c.

5001{
5002 /* "filler" column uses NULL */
5004 INT64_FORMAT "\t0\t\\N\n",
5005 curr + 1);

References fb(), INT64_FORMAT, and printfPQExpBuffer().

Referenced by initGenerateDataClientSide().

◆ initCreateFKeys()

static void initCreateFKeys ( PGconn con)
static

Definition at line 5279 of file pgbench.c.

5281{
5282 static const char *const DDLKEYs[] = {
5283 "alter table pgbench_tellers add constraint pgbench_tellers_bid_fkey foreign key (bid) references pgbench_branches",
5284 "alter table pgbench_accounts add constraint pgbench_accounts_bid_fkey foreign key (bid) references pgbench_branches",
5285 "alter table pgbench_history add constraint pgbench_history_bid_fkey foreign key (bid) references pgbench_branches",
5286 "alter table pgbench_history add constraint pgbench_history_tid_fkey foreign key (tid) references pgbench_tellers",
5287 "alter table pgbench_history add constraint pgbench_history_aid_fkey foreign key (aid) references pgbench_accounts"
5288 };
5289 int i;
5290
5291 fprintf(stderr, "creating foreign keys...\n");
5292 for (i = 0; i < lengthof(DDLKEYs); i++)
5293 {
5294 executeStatement(con, DDLKEYs[i]);
5295 }

References executeStatement(), fb(), fprintf, i, and lengthof.

Referenced by runInitSteps().

◆ initCreatePKeys()

static void initCreatePKeys ( PGconn con)
static

Definition at line 5241 of file pgbench.c.

5243{
5244 static const char *const DDLINDEXes[] = {
5245 "alter table pgbench_branches add primary key (bid)",
5246 "alter table pgbench_tellers add primary key (tid)",
5247 "alter table pgbench_accounts add primary key (aid)"
5248 };
5249 int i;
5250 PQExpBufferData query;
5251
5252 fprintf(stderr, "creating primary keys...\n");
5253 initPQExpBuffer(&query);
5254
5255 for (i = 0; i < lengthof(DDLINDEXes); i++)
5256 {
5257 resetPQExpBuffer(&query);
5259
5260 if (index_tablespace != NULL)
5261 {
5262 char *escape_tablespace;
5263
5266 appendPQExpBuffer(&query, " using index tablespace %s", escape_tablespace);
5268 }
5269
5270 executeStatement(con, query.data);
5271 }
5272
5273 termPQExpBuffer(&query);
void PQfreemem(void *ptr)
Definition fe-exec.c:4068
char * PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
Definition fe-exec.c:4424
static char * index_tablespace
Definition pgbench.c:218
void resetPQExpBuffer(PQExpBuffer str)

References appendPQExpBuffer(), appendPQExpBufferStr(), PQExpBufferData::data, executeStatement(), fb(), fprintf, i, index_tablespace, initPQExpBuffer(), lengthof, PQescapeIdentifier(), PQfreemem(), resetPQExpBuffer(), and termPQExpBuffer().

Referenced by runInitSteps().

◆ initCreateTables()

static void initCreateTables ( PGconn con)
static

Definition at line 4894 of file pgbench.c.

4896{
4897 /*
4898 * Note: TPC-B requires at least 100 bytes per row, and the "filler"
4899 * fields in these table declarations were intended to comply with that.
4900 * The pgbench_accounts table complies with that because the "filler"
4901 * column is set to blank-padded empty string. But for all other tables
4902 * the columns default to NULL and so don't actually take any space. We
4903 * could fix that by giving them non-null default values. However, that
4904 * would completely break comparability of pgbench results with prior
4905 * versions. Since pgbench has never pretended to be fully TPC-B compliant
4906 * anyway, we stick with the historical behavior.
4907 */
4908 struct ddlinfo
4909 {
4910 const char *table; /* table name */
4911 const char *smcols; /* column decls if accountIDs are 32 bits */
4912 const char *bigcols; /* column decls if accountIDs are 64 bits */
4914 };
4915 static const struct ddlinfo DDLs[] = {
4916 {
4917 "pgbench_history",
4918 "tid int,bid int,aid int,delta int,mtime timestamp,filler char(22)",
4919 "tid int,bid int,aid bigint,delta int,mtime timestamp,filler char(22)",
4920 0
4921 },
4922 {
4923 "pgbench_tellers",
4924 "tid int not null,bid int,tbalance int,filler char(84)",
4925 "tid int not null,bid int,tbalance int,filler char(84)",
4926 1
4927 },
4928 {
4929 "pgbench_accounts",
4930 "aid int not null,bid int,abalance int,filler char(84)",
4931 "aid bigint not null,bid int,abalance int,filler char(84)",
4932 1
4933 },
4934 {
4935 "pgbench_branches",
4936 "bid int not null,bbalance int,filler char(88)",
4937 "bid int not null,bbalance int,filler char(88)",
4938 1
4939 }
4940 };
4941 int i;
4942 PQExpBufferData query;
4943
4944 fprintf(stderr, "creating tables...\n");
4945
4946 initPQExpBuffer(&query);
4947
4948 for (i = 0; i < lengthof(DDLs); i++)
4949 {
4950 const struct ddlinfo *ddl = &DDLs[i];
4951
4952 /* Construct new create table statement. */
4953 printfPQExpBuffer(&query, "create%s table %s(%s)",
4954 (unlogged_tables && partition_method == PART_NONE) ? " unlogged" : "",
4955 ddl->table,
4957
4958 /* Partition pgbench_accounts table */
4959 if (partition_method != PART_NONE && strcmp(ddl->table, "pgbench_accounts") == 0)
4960 appendPQExpBuffer(&query,
4961 " partition by %s (aid)", PARTITION_METHOD[partition_method]);
4962 else if (ddl->declare_fillfactor)
4963 {
4964 /* fillfactor is only expected on actual tables */
4965 appendPQExpBuffer(&query, " with (fillfactor=%d)", fillfactor);
4966 }
4967
4968 if (tablespace != NULL)
4969 {
4970 char *escape_tablespace;
4971
4973 appendPQExpBuffer(&query, " tablespace %s", escape_tablespace);
4975 }
4976
4977 executeStatement(con, query.data);
4978 }
4979
4980 termPQExpBuffer(&query);
4981
4983 createPartitions(con);
static void createPartitions(PGconn *con)
Definition pgbench.c:4825
static const char *const PARTITION_METHOD[]
Definition pgbench.c:235
#define SCALE_32BIT_THRESHOLD
Definition pgbench.c:255
static char * tablespace
Definition pgbench.c:217

References appendPQExpBuffer(), createPartitions(), PQExpBufferData::data, executeStatement(), fb(), fillfactor, fprintf, i, initPQExpBuffer(), lengthof, PART_NONE, partition_method, PARTITION_METHOD, PQescapeIdentifier(), PQfreemem(), printfPQExpBuffer(), scale, SCALE_32BIT_THRESHOLD, table, tablespace, termPQExpBuffer(), and unlogged_tables.

Referenced by runInitSteps().

◆ initDropTables()

static void initDropTables ( PGconn con)
static

Definition at line 4803 of file pgbench.c.

4805{
4806 fprintf(stderr, "dropping old tables...\n");
4807
4808 /*
4809 * We drop all the tables in one command, so that whether there are
4810 * foreign key dependencies or not doesn't matter.
4811 */
4812 executeStatement(con, "drop table if exists "
4813 "pgbench_accounts, "
4814 "pgbench_branches, "
4815 "pgbench_history, "
4816 "pgbench_tellers");

References executeStatement(), fb(), and fprintf.

Referenced by runInitSteps().

◆ initGenerateDataClientSide()

static void initGenerateDataClientSide ( PGconn con)
static

Definition at line 5151 of file pgbench.c.

5153{
5154 fprintf(stderr, "generating data (client-side)...\n");
5155
5156 /*
5157 * we do all of this in one transaction to enable the backend's
5158 * data-loading optimizations
5159 */
5160 executeStatement(con, "begin");
5161
5162 /* truncate away any old data */
5163 initTruncateTables(con);
5164
5165 /*
5166 * fill branches, tellers, accounts in that order in case foreign keys
5167 * already exist
5168 */
5169 initPopulateTable(con, "pgbench_branches", nbranches, initBranch);
5170 initPopulateTable(con, "pgbench_tellers", ntellers, initTeller);
5171 initPopulateTable(con, "pgbench_accounts", naccounts, initAccount);
5172
5173 executeStatement(con, "commit");
static void initTeller(PQExpBufferData *sql, int64 curr)
Definition pgbench.c:5008
static void initBranch(PQExpBufferData *sql, int64 curr)
Definition pgbench.c:4999
#define ntellers
Definition pgbench.c:245
static void initTruncateTables(PGconn *con)
Definition pgbench.c:4989
static void initAccount(PQExpBufferData *sql, int64 curr)
Definition pgbench.c:5017
#define nbranches
Definition pgbench.c:244
static void initPopulateTable(PGconn *con, const char *table, int64 base, initRowMethod init_row)
Definition pgbench.c:5026

References executeStatement(), fb(), fprintf, initAccount(), initBranch(), initPopulateTable(), initTeller(), initTruncateTables(), naccounts, nbranches, and ntellers.

Referenced by runInitSteps().

◆ initGenerateDataServerSide()

static void initGenerateDataServerSide ( PGconn con)
static

Definition at line 5183 of file pgbench.c.

5185{
5186 PQExpBufferData sql;
5187
5188 fprintf(stderr, "generating data (server-side)...\n");
5189
5190 /*
5191 * we do all of this in one transaction to enable the backend's
5192 * data-loading optimizations
5193 */
5194 executeStatement(con, "begin");
5195
5196 /* truncate away any old data */
5197 initTruncateTables(con);
5198
5199 initPQExpBuffer(&sql);
5200
5201 printfPQExpBuffer(&sql,
5202 "insert into pgbench_branches(bid,bbalance) "
5203 "select bid, 0 "
5204 "from generate_series(1, %d) as bid", nbranches * scale);
5205 executeStatement(con, sql.data);
5206
5207 printfPQExpBuffer(&sql,
5208 "insert into pgbench_tellers(tid,bid,tbalance) "
5209 "select tid, (tid - 1) / %d + 1, 0 "
5210 "from generate_series(1, %d) as tid", ntellers, ntellers * scale);
5211 executeStatement(con, sql.data);
5212
5213 printfPQExpBuffer(&sql,
5214 "insert into pgbench_accounts(aid,bid,abalance,filler) "
5215 "select aid, (aid - 1) / %d + 1, 0, '' "
5216 "from generate_series(1, " INT64_FORMAT ") as aid",
5218 executeStatement(con, sql.data);
5219
5220 termPQExpBuffer(&sql);
5221
5222 executeStatement(con, "commit");

References PQExpBufferData::data, executeStatement(), fb(), fprintf, initPQExpBuffer(), initTruncateTables(), INT64_FORMAT, naccounts, nbranches, ntellers, printfPQExpBuffer(), scale, and termPQExpBuffer().

Referenced by runInitSteps().

◆ initPopulateTable()

static void initPopulateTable ( PGconn con,
const char table,
int64  base,
initRowMethod  init_row 
)
static

Definition at line 5026 of file pgbench.c.

5029{
5030 int n;
5031 int64 k;
5032 int chars = 0;
5033 int prev_chars = 0;
5034 PGresult *res;
5035 PQExpBufferData sql;
5036 char copy_statement[256];
5037 const char *copy_statement_fmt = "copy %s from stdin";
5038 int64 total = base * scale;
5039
5040 /* used to track elapsed time and estimate of the remaining time */
5042 int log_interval = 1;
5043
5044 /* Stay on the same line if reporting to a terminal */
5045 char eol = isatty(fileno(stderr)) ? '\r' : '\n';
5046
5047 initPQExpBuffer(&sql);
5048
5049 /* Use COPY with FREEZE on v14 and later for all ordinary tables */
5050 if ((PQserverVersion(con) >= 140000) &&
5052 copy_statement_fmt = "copy %s from stdin with (freeze on)";
5053
5054
5056 if (n >= sizeof(copy_statement))
5057 pg_fatal("invalid buffer size: must be at least %d characters long", n);
5058 else if (n == -1)
5059 pg_fatal("invalid format string");
5060
5061 res = PQexec(con, copy_statement);
5062
5063 if (PQresultStatus(res) != PGRES_COPY_IN)
5064 pg_fatal("unexpected copy in result: %s", PQerrorMessage(con));
5065 PQclear(res);
5066
5067 start = pg_time_now();
5068
5069 for (k = 0; k < total; k++)
5070 {
5071 int64 j = k + 1;
5072
5073 init_row(&sql, k);
5074 if (PQputline(con, sql.data))
5075 pg_fatal("PQputline failed");
5076
5077 if (CancelRequested)
5078 break;
5079
5080 /*
5081 * If we want to stick with the original logging, print a message each
5082 * 100k inserted rows.
5083 */
5084 if ((!use_quiet) && (j % 100000 == 0))
5085 {
5087 double remaining_sec = ((double) total - j) * elapsed_sec / j;
5088
5089 chars = fprintf(stderr, INT64_FORMAT " of " INT64_FORMAT " tuples (%d%%) of %s done (elapsed %.2f s, remaining %.2f s)",
5090 j, total,
5091 (int) ((j * 100) / total),
5093
5094 /*
5095 * If the previous progress message is longer than the current
5096 * one, add spaces to the current line to fully overwrite any
5097 * remaining characters from the previous message.
5098 */
5099 if (prev_chars > chars)
5100 fprintf(stderr, "%*c", prev_chars - chars, ' ');
5101 fputc(eol, stderr);
5102 prev_chars = chars;
5103 }
5104 /* let's not call the timing for each row, but only each 100 rows */
5105 else if (use_quiet && (j % 100 == 0))
5106 {
5108 double remaining_sec = ((double) total - j) * elapsed_sec / j;
5109
5110 /* have we reached the next interval (or end)? */
5111 if ((j == total) || (elapsed_sec >= log_interval * LOG_STEP_SECONDS))
5112 {
5113 chars = fprintf(stderr, INT64_FORMAT " of " INT64_FORMAT " tuples (%d%%) of %s done (elapsed %.2f s, remaining %.2f s)",
5114 j, total,
5115 (int) ((j * 100) / total),
5117
5118 /*
5119 * If the previous progress message is longer than the current
5120 * one, add spaces to the current line to fully overwrite any
5121 * remaining characters from the previous message.
5122 */
5123 if (prev_chars > chars)
5124 fprintf(stderr, "%*c", prev_chars - chars, ' ');
5125 fputc(eol, stderr);
5126 prev_chars = chars;
5127
5128 /* skip to the next interval */
5130 }
5131 }
5132 }
5133
5134 if (chars != 0 && eol != '\n')
5135 fprintf(stderr, "%*c\r", chars, ' '); /* Clear the current line */
5136
5137 if (PQputline(con, "\\.\n"))
5138 pg_fatal("very last PQputline failed");
5139 if (PQendcopy(con))
5140 pg_fatal("PQendcopy failed");
5141
5142 termPQExpBuffer(&sql);
volatile sig_atomic_t CancelRequested
Definition cancel.c:59
int PQserverVersion(const PGconn *conn)
int PQendcopy(PGconn *conn)
Definition fe-exec.c:2966
int PQputline(PGconn *conn, const char *string)
Definition fe-exec.c:2935
int j
Definition isn.c:78
@ PGRES_COPY_IN
Definition libpq-fe.h:138
static char get_table_relkind(PGconn *con, const char *table)
Definition pgbench.c:860
#define LOG_STEP_SECONDS
Definition pgbench.c:166
static bool use_quiet
Definition pgbench.c:258
int int pg_snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
static char chars[TZ_MAX_CHARS]
Definition zic.c:408

References CancelRequested, chars, PQExpBufferData::data, fb(), fprintf, get_table_relkind(), initPQExpBuffer(), INT64_FORMAT, j, LOG_STEP_SECONDS, pg_fatal, pg_snprintf(), PG_TIME_GET_DOUBLE, pg_time_now(), PGRES_COPY_IN, PQclear, PQendcopy(), PQerrorMessage(), PQexec(), PQputline(), PQresultStatus, PQserverVersion(), scale, start, table, termPQExpBuffer(), and use_quiet.

Referenced by initGenerateDataClientSide().

◆ initRandomState()

static void initRandomState ( pg_prng_state state)
static

Definition at line 1078 of file pgbench.c.

1080{
uint64 pg_prng_uint64(pg_prng_state *state)
Definition pg_prng.c:134
void pg_prng_seed(pg_prng_state *state, uint64 seed)
Definition pg_prng.c:89
static pg_prng_state base_random_sequence
Definition pgbench.c:485

References base_random_sequence, pg_prng_seed(), and pg_prng_uint64().

Referenced by main().

◆ initSimpleStats()

static void initSimpleStats ( SimpleStats ss)
static

Definition at line 1384 of file pgbench.c.

1386{
1387 memset(ss, 0, sizeof(SimpleStats));

References fb().

Referenced by create_sql_command(), initStats(), and process_backslash_command().

◆ initStats()

static void initStats ( StatsData sd,
pg_time_usec_t  start 
)
static

Definition at line 1424 of file pgbench.c.

1426{
1427 sd->start_time = start;
1428 sd->cnt = 0;
1429 sd->skipped = 0;
1430 sd->retries = 0;
1431 sd->retried = 0;
1432 sd->serialization_failures = 0;
1433 sd->deadlock_failures = 0;
1434 sd->other_sql_failures = 0;
1435 initSimpleStats(&sd->latency);
1436 initSimpleStats(&sd->lag);

References fb(), initSimpleStats(), and start.

Referenced by doLog(), main(), ParseScript(), printProgressReport(), and threadRun().

◆ initTeller()

static void initTeller ( PQExpBufferData sql,
int64  curr 
)
static

Definition at line 5008 of file pgbench.c.

5010{
5011 /* "filler" column uses NULL */
5013 INT64_FORMAT "\t" INT64_FORMAT "\t0\t\\N\n",
5014 curr + 1, curr / ntellers + 1);

References fb(), INT64_FORMAT, ntellers, and printfPQExpBuffer().

Referenced by initGenerateDataClientSide().

◆ initTruncateTables()

static void initTruncateTables ( PGconn con)
static

Definition at line 4989 of file pgbench.c.

4991{
4992 executeStatement(con, "truncate table "
4993 "pgbench_accounts, "
4994 "pgbench_branches, "
4995 "pgbench_history, "
4996 "pgbench_tellers");

References executeStatement().

Referenced by initGenerateDataClientSide(), and initGenerateDataServerSide().

◆ initVacuum()

static void initVacuum ( PGconn con)
static

Definition at line 5228 of file pgbench.c.

5230{
5231 fprintf(stderr, "vacuuming...\n");
5232 executeStatement(con, "vacuum analyze pgbench_branches");
5233 executeStatement(con, "vacuum analyze pgbench_tellers");
5234 executeStatement(con, "vacuum analyze pgbench_accounts");
5235 executeStatement(con, "vacuum analyze pgbench_history");

References executeStatement(), fb(), and fprintf.

Referenced by runInitSteps().

◆ is_an_int()

static bool is_an_int ( const char str)
static

Definition at line 990 of file pgbench.c.

992{
993 const char *ptr = str;
994
995 /* skip leading spaces */
996 while (*ptr && isspace((unsigned char) *ptr))
997 ptr++;
998
999 /* skip sign */
1000 if (*ptr == '+' || *ptr == '-')
1001 ptr++;
1002
1003 /* at least one digit */
1004 if (*ptr && !isdigit((unsigned char) *ptr))
1005 return false;
1006
1007 /* eat all digits */
1008 while (*ptr && isdigit((unsigned char) *ptr))
1009 ptr++;
1010
1011 /* must have reached end of string */
1012 return *ptr == '\0';
const char * str

References fb(), and str.

Referenced by makeVariableValue().

◆ isLazyFunc()

static bool isLazyFunc ( PgBenchFunction  func)
static

Definition at line 2119 of file pgbench.c.

2121{
2122 return func == PGBENCH_AND || func == PGBENCH_OR || func == PGBENCH_CASE;

References PGBENCH_AND, PGBENCH_CASE, and PGBENCH_OR.

Referenced by evalFunc(), and evalLazyFunc().

◆ listAvailableScripts()

static void listAvailableScripts ( void  )
static

Definition at line 6205 of file pgbench.c.

6207{
6208 int i;
6209
6210 fprintf(stderr, "Available builtin scripts:\n");
6211 for (i = 0; i < lengthof(builtin_script); i++)
6212 fprintf(stderr, " %13s: %s\n", builtin_script[i].name, builtin_script[i].desc);
6213 fprintf(stderr, "\n");

References builtin_script, fb(), fprintf, i, lengthof, and name.

Referenced by findBuiltin(), and main().

◆ lookupCreateVariable()

static Variable * lookupCreateVariable ( Variables variables,
const char context,
char name 
)
static

Definition at line 1786 of file pgbench.c.

1788{
1789 Variable *var;
1790
1791 var = lookupVariable(variables, name);
1792 if (var == NULL)
1793 {
1794 /*
1795 * Check for the name only when declaring a new variable to avoid
1796 * overhead.
1797 */
1799 {
1800 pg_log_error("%s: invalid variable name: \"%s\"", context, name);
1801 return NULL;
1802 }
1803
1804 /* Create variable at the end of the array */
1805 enlargeVariables(variables, 1);
1806
1807 var = &(variables->vars[variables->nvars]);
1808
1809 var->name = pg_strdup(name);
1810 var->svalue = NULL;
1811 /* caller is expected to initialize remaining fields */
1812
1813 variables->nvars++;
1814 /* we don't re-sort the array till we have to */
1815 variables->vars_sorted = false;
1816 }
1817
1818 return var;
static bool valid_variable_name(const char *name)
Definition pgbench.c:1732
static void enlargeVariables(Variables *variables, int needed)
Definition pgbench.c:1767
char * name
Definition pgbench.c:325
bool vars_sorted
Definition pgbench.c:345

References enlargeVariables(), fb(), lookupVariable(), name, Variable::name, Variables::nvars, pg_log_error, pg_strdup(), Variable::svalue, valid_variable_name(), Variables::vars, and Variables::vars_sorted.

Referenced by putVariable(), and putVariableValue().

◆ lookupVariable()

static Variable * lookupVariable ( Variables variables,
char name 
)
static

Definition at line 1598 of file pgbench.c.

1600{
1601 Variable key;
1602
1603 /* On some versions of Solaris, bsearch of zero items dumps core */
1604 if (variables->nvars <= 0)
1605 return NULL;
1606
1607 /* Sort if we have to */
1608 if (!variables->vars_sorted)
1609 {
1610 qsort(variables->vars, variables->nvars, sizeof(Variable),
1612 variables->vars_sorted = true;
1613 }
1614
1615 /* Now we can search */
1616 key.name = name;
1617 return (Variable *) bsearch(&key,
1618 variables->vars,
1619 variables->nvars,
1620 sizeof(Variable),
static int compareVariableNames(const void *v1, const void *v2)
Definition pgbench.c:1590
#define qsort(a, b, c, d)
Definition port.h:496

References compareVariableNames(), fb(), name, Variables::nvars, qsort, Variables::vars, and Variables::vars_sorted.

Referenced by evaluateExpr(), getVariable(), lookupCreateVariable(), and main().

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 6730 of file pgbench.c.

6732{
6733 static struct option long_options[] = {
6734 /* systematic long/short named options */
6735 {"builtin", required_argument, NULL, 'b'},
6736 {"client", required_argument, NULL, 'c'},
6737 {"connect", no_argument, NULL, 'C'},
6738 {"dbname", required_argument, NULL, 'd'},
6739 {"define", required_argument, NULL, 'D'},
6740 {"file", required_argument, NULL, 'f'},
6741 {"fillfactor", required_argument, NULL, 'F'},
6742 {"host", required_argument, NULL, 'h'},
6743 {"initialize", no_argument, NULL, 'i'},
6744 {"init-steps", required_argument, NULL, 'I'},
6745 {"jobs", required_argument, NULL, 'j'},
6746 {"log", no_argument, NULL, 'l'},
6747 {"latency-limit", required_argument, NULL, 'L'},
6748 {"no-vacuum", no_argument, NULL, 'n'},
6749 {"port", required_argument, NULL, 'p'},
6750 {"progress", required_argument, NULL, 'P'},
6751 {"protocol", required_argument, NULL, 'M'},
6752 {"quiet", no_argument, NULL, 'q'},
6753 {"report-per-command", no_argument, NULL, 'r'},
6754 {"rate", required_argument, NULL, 'R'},
6755 {"scale", required_argument, NULL, 's'},
6756 {"select-only", no_argument, NULL, 'S'},
6757 {"skip-some-updates", no_argument, NULL, 'N'},
6758 {"time", required_argument, NULL, 'T'},
6759 {"transactions", required_argument, NULL, 't'},
6760 {"username", required_argument, NULL, 'U'},
6761 {"vacuum-all", no_argument, NULL, 'v'},
6762 /* long-named only options */
6763 {"unlogged-tables", no_argument, NULL, 1},
6764 {"tablespace", required_argument, NULL, 2},
6765 {"index-tablespace", required_argument, NULL, 3},
6766 {"sampling-rate", required_argument, NULL, 4},
6767 {"aggregate-interval", required_argument, NULL, 5},
6768 {"progress-timestamp", no_argument, NULL, 6},
6769 {"log-prefix", required_argument, NULL, 7},
6770 {"foreign-keys", no_argument, NULL, 8},
6771 {"random-seed", required_argument, NULL, 9},
6772 {"show-script", required_argument, NULL, 10},
6773 {"partitions", required_argument, NULL, 11},
6774 {"partition-method", required_argument, NULL, 12},
6775 {"failures-detailed", no_argument, NULL, 13},
6776 {"max-tries", required_argument, NULL, 14},
6777 {"verbose-errors", no_argument, NULL, 15},
6778 {"exit-on-abort", no_argument, NULL, 16},
6779 {"debug", no_argument, NULL, 17},
6780 {"continue-on-error", no_argument, NULL, 18},
6781 {NULL, 0, NULL, 0}
6782 };
6783
6784 int c;
6785 bool is_init_mode = false; /* initialize mode? */
6786 char *initialize_steps = NULL;
6787 bool foreign_keys = false;
6788 bool is_no_vacuum = false;
6789 bool do_vacuum_accounts = false; /* vacuum accounts table? */
6790 int optindex;
6791 bool scale_given = false;
6792
6793 bool benchmarking_option_set = false;
6794 bool initialization_option_set = false;
6795 bool internal_script_used = false;
6796
6797 CState *state; /* status of clients */
6798 TState *threads; /* array of thread */
6799
6801 start_time, /* start up time */
6802 bench_start = 0, /* first recorded benchmarking time */
6803 conn_total_duration; /* cumulated connection time in
6804 * threads */
6805 int64 latency_late = 0;
6806 StatsData stats;
6807 int weight;
6808
6809 int i;
6810 int nclients_dealt;
6811
6812#ifdef HAVE_GETRLIMIT
6813 struct rlimit rlim;
6814#endif
6815
6816 PGconn *con;
6817 char *env;
6818
6819 int exit_code = 0;
6820 struct timeval tv;
6821
6822 /* initialize timing infrastructure (required for INSTR_* calls) */
6824
6825 /*
6826 * Record difference between Unix time and instr_time time. We'll use
6827 * this for logging and aggregation.
6828 */
6829 gettimeofday(&tv, NULL);
6830 epoch_shift = tv.tv_sec * INT64CONST(1000000) + tv.tv_usec - pg_time_now();
6831
6832 pg_logging_init(argv[0]);
6833 progname = get_progname(argv[0]);
6834
6835 if (argc > 1)
6836 {
6837 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
6838 {
6839 usage();
6840 exit(0);
6841 }
6842 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
6843 {
6844 puts("pgbench (PostgreSQL) " PG_VERSION);
6845 exit(0);
6846 }
6847 }
6848
6850
6851 /* set random seed early, because it may be used while parsing scripts. */
6852 if (!set_random_seed(getenv("PGBENCH_RANDOM_SEED")))
6853 pg_fatal("error while setting random seed from PGBENCH_RANDOM_SEED environment variable");
6854
6855 while ((c = getopt_long(argc, argv, "b:c:Cd:D:f:F:h:iI:j:lL:M:nNp:P:qrR:s:St:T:U:v", long_options, &optindex)) != -1)
6856 {
6857 char *script;
6858
6859 switch (c)
6860 {
6861 case 'b':
6862 if (strcmp(optarg, "list") == 0)
6863 {
6865 exit(0);
6866 }
6867 weight = parseScriptWeight(optarg, &script);
6868 process_builtin(findBuiltin(script), weight);
6870 internal_script_used = true;
6871 break;
6872 case 'c':
6874 if (!option_parse_int(optarg, "-c/--clients", 1, INT_MAX,
6875 &nclients))
6876 {
6877 exit(1);
6878 }
6879#ifdef HAVE_GETRLIMIT
6880 if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
6881 pg_fatal("getrlimit failed: %m");
6882
6883 if (rlim.rlim_max < nclients + 3)
6884 {
6885 pg_log_error("need at least %d open files, but system limit is %ld",
6886 nclients + 3, (long) rlim.rlim_max);
6887 pg_log_error_hint("Reduce number of clients, or use limit/ulimit to increase the system limit.");
6888 exit(1);
6889 }
6890
6891 if (rlim.rlim_cur < nclients + 3)
6892 {
6893 rlim.rlim_cur = nclients + 3;
6894 if (setrlimit(RLIMIT_NOFILE, &rlim) == -1)
6895 {
6896 pg_log_error("need at least %d open files, but couldn't raise the limit: %m",
6897 nclients + 3);
6898 pg_log_error_hint("Reduce number of clients, or use limit/ulimit to increase the system limit.");
6899 exit(1);
6900 }
6901 }
6902#endif /* HAVE_GETRLIMIT */
6903 break;
6904 case 'C':
6906 is_connect = true;
6907 break;
6908 case 'd':
6910 break;
6911 case 'D':
6912 {
6913 char *p;
6914
6916
6917 if ((p = strchr(optarg, '=')) == NULL || p == optarg || *(p + 1) == '\0')
6918 pg_fatal("invalid variable definition: \"%s\"", optarg);
6919
6920 *p++ = '\0';
6921 if (!putVariable(&state[0].variables, "option", optarg, p))
6922 exit(1);
6923 }
6924 break;
6925 case 'f':
6926 weight = parseScriptWeight(optarg, &script);
6927 process_file(script, weight);
6929 break;
6930 case 'F':
6932 if (!option_parse_int(optarg, "-F/--fillfactor", 10, 100,
6933 &fillfactor))
6934 exit(1);
6935 break;
6936 case 'h':
6938 break;
6939 case 'i':
6940 is_init_mode = true;
6941 break;
6942 case 'I':
6947 break;
6948 case 'j': /* jobs */
6950 if (!option_parse_int(optarg, "-j/--jobs", 1, INT_MAX,
6951 &nthreads))
6952 {
6953 exit(1);
6954 }
6955 break;
6956 case 'l':
6958 use_log = true;
6959 break;
6960 case 'L':
6961 {
6962 double limit_ms = atof(optarg);
6963
6964 if (limit_ms <= 0.0)
6965 pg_fatal("invalid latency limit: \"%s\"", optarg);
6967 latency_limit = (int64) (limit_ms * 1000);
6968 }
6969 break;
6970 case 'M':
6973 if (strcmp(optarg, QUERYMODE[querymode]) == 0)
6974 break;
6975 if (querymode >= NUM_QUERYMODE)
6976 pg_fatal("invalid query mode (-M): \"%s\"", optarg);
6977 break;
6978 case 'n':
6979 is_no_vacuum = true;
6980 break;
6981 case 'N':
6982 process_builtin(findBuiltin("simple-update"), 1);
6984 internal_script_used = true;
6985 break;
6986 case 'p':
6988 break;
6989 case 'P':
6991 if (!option_parse_int(optarg, "-P/--progress", 1, INT_MAX,
6992 &progress))
6993 exit(1);
6994 break;
6995 case 'q':
6997 use_quiet = true;
6998 break;
6999 case 'r':
7001 report_per_command = true;
7002 break;
7003 case 'R':
7004 {
7005 /* get a double from the beginning of option value */
7006 double throttle_value = atof(optarg);
7007
7009
7010 if (throttle_value <= 0.0)
7011 pg_fatal("invalid rate limit: \"%s\"", optarg);
7012 /* Invert rate limit into per-transaction delay in usec */
7013 throttle_delay = 1000000.0 / throttle_value;
7014 }
7015 break;
7016 case 's':
7017 scale_given = true;
7018 if (!option_parse_int(optarg, "-s/--scale", 1, INT_MAX,
7019 &scale))
7020 exit(1);
7021 break;
7022 case 'S':
7023 process_builtin(findBuiltin("select-only"), 1);
7025 internal_script_used = true;
7026 break;
7027 case 't':
7029 if (!option_parse_int(optarg, "-t/--transactions", 1, INT_MAX,
7030 &nxacts))
7031 exit(1);
7032 break;
7033 case 'T':
7035 if (!option_parse_int(optarg, "-T/--time", 1, INT_MAX,
7036 &duration))
7037 exit(1);
7038 break;
7039 case 'U':
7041 break;
7042 case 'v':
7044 do_vacuum_accounts = true;
7045 break;
7046 case 1: /* unlogged-tables */
7048 unlogged_tables = true;
7049 break;
7050 case 2: /* tablespace */
7053 break;
7054 case 3: /* index-tablespace */
7057 break;
7058 case 4: /* sampling-rate */
7062 pg_fatal("invalid sampling rate: \"%s\"", optarg);
7063 break;
7064 case 5: /* aggregate-interval */
7066 if (!option_parse_int(optarg, "--aggregate-interval", 1, INT_MAX,
7067 &agg_interval))
7068 exit(1);
7069 break;
7070 case 6: /* progress-timestamp */
7071 progress_timestamp = true;
7073 break;
7074 case 7: /* log-prefix */
7077 break;
7078 case 8: /* foreign-keys */
7080 foreign_keys = true;
7081 break;
7082 case 9: /* random-seed */
7084 if (!set_random_seed(optarg))
7085 pg_fatal("error while setting random seed from --random-seed option");
7086 break;
7087 case 10: /* list */
7088 {
7089 const BuiltinScript *s = findBuiltin(optarg);
7090
7091 fprintf(stderr, "-- %s: %s\n%s\n", s->name, s->desc, s->script);
7092 exit(0);
7093 }
7094 break;
7095 case 11: /* partitions */
7097 if (!option_parse_int(optarg, "--partitions", 0, INT_MAX,
7098 &partitions))
7099 exit(1);
7100 break;
7101 case 12: /* partition-method */
7103 if (pg_strcasecmp(optarg, "range") == 0)
7105 else if (pg_strcasecmp(optarg, "hash") == 0)
7107 else
7108 pg_fatal("invalid partition method, expecting \"range\" or \"hash\", got: \"%s\"",
7109 optarg);
7110 break;
7111 case 13: /* failures-detailed */
7113 failures_detailed = true;
7114 break;
7115 case 14: /* max-tries */
7116 {
7118
7119 if (max_tries_arg < 0)
7120 pg_fatal("invalid number of maximum tries: \"%s\"", optarg);
7121
7124 }
7125 break;
7126 case 15: /* verbose-errors */
7128 verbose_errors = true;
7129 break;
7130 case 16: /* exit-on-abort */
7132 exit_on_abort = true;
7133 break;
7134 case 17: /* debug */
7136 break;
7137 case 18: /* continue-on-error */
7139 continue_on_error = true;
7140 break;
7141 default:
7142 /* getopt_long already emitted a complaint */
7143 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
7144 exit(1);
7145 }
7146 }
7147
7148 /* set default script if none */
7149 if (num_scripts == 0 && !is_init_mode)
7150 {
7151 process_builtin(findBuiltin("tpcb-like"), 1);
7153 internal_script_used = true;
7154 }
7155
7156 /* complete SQL command initialization and compute total weight */
7157 for (i = 0; i < num_scripts; i++)
7158 {
7159 Command **commands = sql_script[i].commands;
7160
7161 for (int j = 0; commands[j] != NULL; j++)
7162 if (commands[j]->type == SQL_COMMAND)
7163 postprocess_sql_command(commands[j]);
7164
7165 /* cannot overflow: weight is 32b, total_weight 64b */
7167 }
7168
7169 if (total_weight == 0 && !is_init_mode)
7170 pg_fatal("total script weight must not be zero");
7171
7172 /* show per script stats if several scripts are used */
7173 if (num_scripts > 1)
7174 per_script_stats = true;
7175
7176 /*
7177 * Don't need more threads than there are clients. (This is not merely an
7178 * optimization; throttle_delay is calculated incorrectly below if some
7179 * threads have no clients assigned to them.)
7180 */
7181 if (nthreads > nclients)
7183
7184 /*
7185 * Convert throttle_delay to a per-thread delay time. Note that this
7186 * might be a fractional number of usec, but that's OK, since it's just
7187 * the center of a Poisson distribution of delays.
7188 */
7190
7191 if (dbName == NULL)
7192 {
7193 if (argc > optind)
7194 dbName = argv[optind++];
7195 else
7196 {
7197 if ((env = getenv("PGDATABASE")) != NULL && *env != '\0')
7198 dbName = env;
7199 else if ((env = getenv("PGUSER")) != NULL && *env != '\0')
7200 dbName = env;
7201 else
7203 }
7204 }
7205
7206 if (optind < argc)
7207 {
7208 pg_log_error("too many command-line arguments (first is \"%s\")",
7209 argv[optind]);
7210 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
7211 exit(1);
7212 }
7213
7214 if (is_init_mode)
7215 {
7217 pg_fatal("some of the specified options cannot be used in initialization (-i) mode");
7218
7219 if (partitions == 0 && partition_method != PART_NONE)
7220 pg_fatal("--partition-method requires greater than zero --partitions");
7221
7222 /* set default method */
7225
7226 if (initialize_steps == NULL)
7228
7229 if (is_no_vacuum)
7230 {
7231 /* Remove any vacuum step in initialize_steps */
7232 char *p;
7233
7234 while ((p = strchr(initialize_steps, 'v')) != NULL)
7235 *p = ' ';
7236 }
7237
7238 if (foreign_keys)
7239 {
7240 /* Add 'f' to end of initialize_steps, if not already there */
7241 if (strchr(initialize_steps, 'f') == NULL)
7242 {
7243 initialize_steps = (char *)
7247 }
7248 }
7249
7251 exit(0);
7252 }
7253 else
7254 {
7256 pg_fatal("some of the specified options cannot be used in benchmarking mode");
7257 }
7258
7259 if (nxacts > 0 && duration > 0)
7260 pg_fatal("specify either a number of transactions (-t) or a duration (-T), not both");
7261
7262 /* Use DEFAULT_NXACTS if neither nxacts nor duration is specified. */
7263 if (nxacts <= 0 && duration <= 0)
7265
7266 /* --sampling-rate may be used only with -l */
7267 if (sample_rate > 0.0 && !use_log)
7268 pg_fatal("log sampling (--sampling-rate) is allowed only when logging transactions (-l)");
7269
7270 /* --sampling-rate may not be used with --aggregate-interval */
7271 if (sample_rate > 0.0 && agg_interval > 0)
7272 pg_fatal("log sampling (--sampling-rate) and aggregation (--aggregate-interval) cannot be used at the same time");
7273
7274 if (agg_interval > 0 && !use_log)
7275 pg_fatal("log aggregation is allowed only when actually logging transactions");
7276
7277 if (!use_log && logfile_prefix)
7278 pg_fatal("log file prefix (--log-prefix) is allowed only when logging transactions (-l)");
7279
7280 if (duration > 0 && agg_interval > duration)
7281 pg_fatal("number of seconds for aggregation (%d) must not be higher than test duration (%d)", agg_interval, duration);
7282
7283 if (duration > 0 && agg_interval > 0 && duration % agg_interval != 0)
7284 pg_fatal("duration (%d) must be a multiple of aggregation interval (%d)", duration, agg_interval);
7285
7286 if (progress_timestamp && progress == 0)
7287 pg_fatal("--progress-timestamp is allowed only under --progress");
7288
7289 if (!max_tries)
7290 {
7291 if (!latency_limit && duration <= 0)
7292 pg_fatal("an unlimited number of transaction tries can only be used with --latency-limit or a duration (-T)");
7293 }
7294
7295 /*
7296 * save main process id in the global variable because process id will be
7297 * changed after fork.
7298 */
7299 main_pid = (int) getpid();
7300
7301 if (nclients > 1)
7302 {
7304 memset(state + 1, 0, sizeof(CState) * (nclients - 1));
7305
7306 /* copy any -D switch values to all clients */
7307 for (i = 1; i < nclients; i++)
7308 {
7309 int j;
7310
7311 state[i].id = i;
7312 for (j = 0; j < state[0].variables.nvars; j++)
7313 {
7314 Variable *var = &state[0].variables.vars[j];
7315
7316 if (var->value.type != PGBT_NO_VALUE)
7317 {
7318 if (!putVariableValue(&state[i].variables, "startup",
7319 var->name, &var->value))
7320 exit(1);
7321 }
7322 else
7323 {
7324 if (!putVariable(&state[i].variables, "startup",
7325 var->name, var->svalue))
7326 exit(1);
7327 }
7328 }
7329 }
7330 }
7331
7332 /* other CState initializations */
7333 for (i = 0; i < nclients; i++)
7334 {
7335 state[i].cstack = conditional_stack_create();
7336 initRandomState(&state[i].cs_func_rs);
7337 }
7338
7339 /* opening connection... */
7340 con = doConnect();
7341 if (con == NULL)
7342 pg_fatal("could not create connection for setup");
7343
7344 /* report pgbench and server versions */
7345 printVersion(con);
7346
7347 pg_log_debug("pghost: %s pgport: %s nclients: %d %s: %d dbName: %s",
7348 PQhost(con), PQport(con), nclients,
7349 duration <= 0 ? "nxacts" : "duration",
7350 duration <= 0 ? nxacts : duration, PQdb(con));
7351
7354
7355 /*
7356 * :scale variables normally get -s or database scale, but don't override
7357 * an explicit -D switch
7358 */
7359 if (lookupVariable(&state[0].variables, "scale") == NULL)
7360 {
7361 for (i = 0; i < nclients; i++)
7362 {
7363 if (!putVariableInt(&state[i].variables, "startup", "scale", scale))
7364 exit(1);
7365 }
7366 }
7367
7368 /*
7369 * Define a :client_id variable that is unique per connection. But don't
7370 * override an explicit -D switch.
7371 */
7372 if (lookupVariable(&state[0].variables, "client_id") == NULL)
7373 {
7374 for (i = 0; i < nclients; i++)
7375 if (!putVariableInt(&state[i].variables, "startup", "client_id", i))
7376 exit(1);
7377 }
7378
7379 /* set default seed for hash functions */
7380 if (lookupVariable(&state[0].variables, "default_seed") == NULL)
7381 {
7383
7384 for (i = 0; i < nclients; i++)
7385 if (!putVariableInt(&state[i].variables, "startup", "default_seed",
7386 (int64) seed))
7387 exit(1);
7388 }
7389
7390 /* set random seed unless overwritten */
7391 if (lookupVariable(&state[0].variables, "random_seed") == NULL)
7392 {
7393 for (i = 0; i < nclients; i++)
7394 if (!putVariableInt(&state[i].variables, "startup", "random_seed",
7395 random_seed))
7396 exit(1);
7397 }
7398
7399 if (!is_no_vacuum)
7400 {
7401 fprintf(stderr, "starting vacuum...");
7402 tryExecuteStatement(con, "vacuum pgbench_branches");
7403 tryExecuteStatement(con, "vacuum pgbench_tellers");
7404 tryExecuteStatement(con, "truncate pgbench_history");
7405 fprintf(stderr, "end.\n");
7406
7408 {
7409 fprintf(stderr, "starting vacuum pgbench_accounts...");
7410 tryExecuteStatement(con, "vacuum analyze pgbench_accounts");
7411 fprintf(stderr, "end.\n");
7412 }
7413 }
7414 PQfinish(con);
7415
7416 /* set up thread data structures */
7418 nclients_dealt = 0;
7419
7420 for (i = 0; i < nthreads; i++)
7421 {
7422 TState *thread = &threads[i];
7423
7424 thread->tid = i;
7425 thread->state = &state[nclients_dealt];
7426 thread->nstate =
7427 (nclients - nclients_dealt + nthreads - i - 1) / (nthreads - i);
7428 initRandomState(&thread->ts_choose_rs);
7430 initRandomState(&thread->ts_sample_rs);
7431 thread->logfile = NULL; /* filled in later */
7432 thread->latency_late = 0;
7433 initStats(&thread->stats, 0);
7434
7435 nclients_dealt += thread->nstate;
7436 }
7437
7438 /* all clients must be assigned to a thread */
7440
7441 /* get start up time for the whole computation */
7443
7444 /* set alarm if duration is specified. */
7445 if (duration > 0)
7447
7449 if (errno != 0)
7450 pg_fatal("could not initialize barrier: %m");
7451
7452 /* start all threads but thread 0 which is executed directly later */
7453 for (i = 1; i < nthreads; i++)
7454 {
7455 TState *thread = &threads[i];
7456
7457 thread->create_time = pg_time_now();
7458 errno = THREAD_CREATE(&thread->thread, threadRun, thread);
7459
7460 if (errno != 0)
7461 pg_fatal("could not create thread: %m");
7462 }
7463
7464 /* compute when to stop */
7465 threads[0].create_time = pg_time_now();
7466 if (duration > 0)
7467 end_time = threads[0].create_time + (int64) 1000000 * duration;
7468
7469 /* run thread 0 directly */
7470 (void) threadRun(&threads[0]);
7471
7472 /* wait for other threads and accumulate results */
7473 initStats(&stats, 0);
7475
7476 for (i = 0; i < nthreads; i++)
7477 {
7478 TState *thread = &threads[i];
7479
7480 if (i > 0)
7481 THREAD_JOIN(thread->thread);
7482
7483 for (int j = 0; j < thread->nstate; j++)
7484 if (thread->state[j].state != CSTATE_FINISHED)
7485 exit_code = 2;
7486
7487 /* aggregate thread level stats */
7488 mergeSimpleStats(&stats.latency, &thread->stats.latency);
7489 mergeSimpleStats(&stats.lag, &thread->stats.lag);
7490 stats.cnt += thread->stats.cnt;
7491 stats.skipped += thread->stats.skipped;
7492 stats.retries += thread->stats.retries;
7493 stats.retried += thread->stats.retried;
7495 stats.deadlock_failures += thread->stats.deadlock_failures;
7497 latency_late += thread->latency_late;
7499
7500 /* first recorded benchmarking start time */
7501 if (bench_start == 0 || thread->bench_start < bench_start)
7502 bench_start = thread->bench_start;
7503 }
7504
7505 /*
7506 * All connections should be already closed in threadRun(), so this
7507 * disconnect_all() will be a no-op, but clean up the connections just to
7508 * be sure. We don't need to measure the disconnection delays here.
7509 */
7511
7512 /*
7513 * Beware that performance of short benchmarks with many threads and
7514 * possibly long transactions can be deceptive because threads do not
7515 * start and finish at the exact same time. The total duration computed
7516 * here encompasses all transactions so that tps shown is somehow slightly
7517 * underestimated.
7518 */
7519 printResults(&stats, pg_time_now() - bench_start, conn_total_duration,
7520 bench_start - start_time, latency_late);
7521
7523
7524 if (exit_code != 0)
7525 pg_log_error("Run was aborted; the above results are incomplete.");
7526
7527 return exit_code;
uint32_t uint32
Definition c.h:624
char * PQport(const PGconn *conn)
char * PQhost(const PGconn *conn)
void * pg_realloc(void *ptr, size_t size)
Definition fe_memutils.c:71
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition getopt_long.c:60
#define no_argument
Definition getopt_long.h:25
#define required_argument
Definition getopt_long.h:26
void pg_initialize_timing(void)
Definition instr_time.c:84
void pg_logging_increase_verbosity(void)
Definition logging.c:187
void pg_logging_init(const char *argv0)
Definition logging.c:85
bool option_parse_int(const char *optarg, const char *optname, int min_range, int max_range, int *result)
static time_t start_time
Definition pg_ctl.c:96
PGDLLIMPORT int optind
Definition getopt.c:47
PGDLLIMPORT char * optarg
Definition getopt.c:49
static void printResults(StatsData *total, pg_time_usec_t total_duration, pg_time_usec_t conn_total_duration, pg_time_usec_t conn_elapsed_duration, int64 latency_late)
Definition pgbench.c:6451
static bool putVariableInt(Variables *variables, const char *context, char *name, int64 value)
Definition pgbench.c:1865
static void GetTableInfo(PGconn *con, bool scale_given)
Definition pgbench.c:5410
static void initRandomState(pg_prng_state *state)
Definition pgbench.c:1078
static bool per_script_stats
Definition pgbench.c:261
static THREAD_FUNC_RETURN_TYPE THREAD_FUNC_CC threadRun(void *arg)
Definition pgbench.c:7530
static void setalarm(int seconds)
Definition pgbench.c:7852
static int nthreads
Definition pgbench.c:265
static bool exit_on_abort
Definition pgbench.c:778
static bool putVariable(Variables *variables, const char *context, char *name, const char *value)
Definition pgbench.c:1823
static int nclients
Definition pgbench.c:264
static void printVersion(PGconn *con)
Definition pgbench.c:6420
#define DEFAULT_NXACTS
Definition pgbench.c:167
static void checkInitSteps(const char *initialize_steps)
Definition pgbench.c:5305
static int progress
Definition pgbench.c:262
static void postprocess_sql_command(Command *my_command)
Definition pgbench.c:5700
static bool progress_timestamp
Definition pgbench.c:263
static const char *const QUERYMODE[]
Definition pgbench.c:722
static const BuiltinScript * findBuiltin(const char *name)
Definition pgbench.c:6217
static void mergeSimpleStats(SimpleStats *acc, SimpleStats *ss)
Definition pgbench.c:1408
#define THREAD_JOIN(handle)
Definition pgbench.c:150
static char * logfile_prefix
Definition pgbench.c:299
static bool set_random_seed(const char *seed)
Definition pgbench.c:6688
static void runInitSteps(const char *initialize_steps)
Definition pgbench.c:5325
static void process_builtin(const BuiltinScript *bi, int weight)
Definition pgbench.c:6198
static int parseScriptWeight(const char *option, char **script)
Definition pgbench.c:6253
static void tryExecuteStatement(PGconn *con, const char *sql)
Definition pgbench.c:1510
static THREAD_BARRIER_T barrier
Definition pgbench.c:488
static void process_file(const char *filename, int weight)
Definition pgbench.c:6172
static int main_pid
Definition pgbench.c:270
#define THREAD_BARRIER_INIT(barrier, n)
Definition pgbench.c:153
static void usage(void)
Definition pgbench.c:904
static void disconnect_all(CState *state, int length)
Definition pgbench.c:4791
#define DEFAULT_INIT_STEPS
Definition pgbench.c:163
static int64 random_seed
Definition pgbench.c:238
#define THREAD_CREATE(handle, function, arg)
Definition pgbench.c:148
#define THREAD_BARRIER_DESTROY(barrier)
Definition pgbench.c:156
const char * get_progname(const char *argv0)
Definition path.c:669
char * c
const char * desc
Definition pgbench.c:785
const char * name
Definition pgbench.c:784
const char * script
Definition pgbench.c:786
pg_time_usec_t create_time
Definition pgbench.c:673
CState * state
Definition pgbench.c:657
int tid
Definition pgbench.c:655
int nstate
Definition pgbench.c:658
StatsData stats
Definition pgbench.c:679
THREAD_T thread
Definition pgbench.c:656
pg_time_usec_t bench_start
Definition pgbench.c:675
int64 latency_late
Definition pgbench.c:680
const char * get_user_name_or_exit(const char *progname)
Definition username.c:74
int gettimeofday(struct timeval *tp, void *tzp)

References agg_interval, Assert, barrier, base_random_sequence, TState::bench_start, checkInitSteps(), StatsData::cnt, ParsedScript::commands, conditional_stack_create(), TState::conn_duration, continue_on_error, TState::create_time, CSTATE_FINISHED, dbName, StatsData::deadlock_failures, DEFAULT_INIT_STEPS, DEFAULT_NXACTS, BuiltinScript::desc, disconnect_all(), doConnect(), duration, end_time, epoch_shift, exit_on_abort, failures_detailed, fb(), fillfactor, findBuiltin(), fprintf, get_progname(), get_user_name_or_exit(), getopt_long(), GetTableInfo(), gettimeofday(), i, index_tablespace, initRandomState(), initStats(), INT64CONST, is_connect, j, StatsData::lag, StatsData::latency, TState::latency_late, latency_limit, listAvailableScripts(), TState::logfile, logfile_prefix, lookupVariable(), main_pid, max_tries, mergeSimpleStats(), Variable::name, BuiltinScript::name, nclients, no_argument, TState::nstate, nthreads, NUM_QUERYMODE, num_scripts, nxacts, optarg, optind, option_parse_int(), StatsData::other_sql_failures, parseScriptWeight(), PART_HASH, PART_NONE, PART_RANGE, partition_method, partitions, per_script_stats, pg_fatal, pg_free(), pg_initialize_timing(), pg_log_debug, pg_log_error, pg_log_error_hint, pg_logging_increase_verbosity(), pg_logging_init(), pg_malloc0_object, pg_malloc_array, pg_prng_uint64(), pg_realloc(), pg_realloc_array, pg_strcasecmp(), pg_strdup(), pg_time_now(), PGBT_NO_VALUE, pghost, pgport, postprocess_sql_command(), PQdb(), PQfinish(), PQhost(), PQport(), printResults(), printVersion(), process_builtin(), process_file(), progname, progress, progress_timestamp, putVariable(), putVariableInt(), putVariableValue(), querymode, QUERYMODE, random_seed, report_per_command, required_argument, StatsData::retried, StatsData::retries, runInitSteps(), sample_rate, scale, BuiltinScript::script, StatsData::serialization_failures, set_random_seed(), setalarm(), StatsData::skipped, SQL_COMMAND, sql_script, start_time, CState::state, TState::state, TState::stats, Variable::svalue, tablespace, TState::thread, THREAD_BARRIER_DESTROY, THREAD_BARRIER_INIT, THREAD_CREATE, THREAD_JOIN, threadRun(), throttle_delay, TState::tid, total_weight, tryExecuteStatement(), TState::ts_choose_rs, TState::ts_sample_rs, TState::ts_throttle_rs, type, PgBenchValue::type, unlogged_tables, usage(), use_log, use_quiet, username, Variable::value, verbose_errors, and ParsedScript::weight.

◆ makeVariableValue()

static bool makeVariableValue ( Variable var)
static

Definition at line 1658 of file pgbench.c.

1660{
1661 size_t slen;
1662
1663 if (var->value.type != PGBT_NO_VALUE)
1664 return true; /* no work */
1665
1666 slen = strlen(var->svalue);
1667
1668 if (slen == 0)
1669 /* what should it do on ""? */
1670 return false;
1671
1672 if (pg_strcasecmp(var->svalue, "null") == 0)
1673 {
1674 setNullValue(&var->value);
1675 }
1676
1677 /*
1678 * accept prefixes such as y, ye, n, no... but not for "o". 0/1 are
1679 * recognized later as an int, which is converted to bool if needed.
1680 */
1681 else if (pg_strncasecmp(var->svalue, "true", slen) == 0 ||
1682 pg_strncasecmp(var->svalue, "yes", slen) == 0 ||
1683 pg_strcasecmp(var->svalue, "on") == 0)
1684 {
1685 setBoolValue(&var->value, true);
1686 }
1687 else if (pg_strncasecmp(var->svalue, "false", slen) == 0 ||
1688 pg_strncasecmp(var->svalue, "no", slen) == 0 ||
1689 pg_strcasecmp(var->svalue, "off") == 0 ||
1690 pg_strcasecmp(var->svalue, "of") == 0)
1691 {
1692 setBoolValue(&var->value, false);
1693 }
1694 else if (is_an_int(var->svalue))
1695 {
1696 /* if it looks like an int, it must be an int without overflow */
1697 int64 iv;
1698
1699 if (!strtoint64(var->svalue, false, &iv))
1700 return false;
1701
1702 setIntValue(&var->value, iv);
1703 }
1704 else /* type should be double */
1705 {
1706 double dv;
1707
1708 if (!strtodouble(var->svalue, true, &dv))
1709 {
1710 pg_log_error("malformed variable \"%s\" value: \"%s\"",
1711 var->name, var->svalue);
1712 return false;
1713 }
1714 setDoubleValue(&var->value, dv);
1715 }
1716 return true;
bool strtodouble(const char *str, bool errorOK, double *dv)
Definition pgbench.c:1049
bool strtoint64(const char *str, bool errorOK, int64 *result)
Definition pgbench.c:1024
static bool is_an_int(const char *str)
Definition pgbench.c:990
int pg_strncasecmp(const char *s1, const char *s2, size_t n)

References fb(), is_an_int(), Variable::name, pg_log_error, pg_strcasecmp(), pg_strncasecmp(), PGBT_NO_VALUE, setBoolValue(), setDoubleValue(), setIntValue(), setNullValue(), strtodouble(), strtoint64(), Variable::svalue, PgBenchValue::type, and Variable::value.

Referenced by evaluateExpr().

◆ mergeSimpleStats()

static void mergeSimpleStats ( SimpleStats acc,
SimpleStats ss 
)
static

Definition at line 1408 of file pgbench.c.

1410{
1411 if (acc->count == 0 || ss->min < acc->min)
1412 acc->min = ss->min;
1413 if (acc->count == 0 || ss->max > acc->max)
1414 acc->max = ss->max;
1415 acc->count += ss->count;
1416 acc->sum += ss->sum;
1417 acc->sum2 += ss->sum2;

References SimpleStats::count, fb(), SimpleStats::max, SimpleStats::min, SimpleStats::sum, and SimpleStats::sum2.

Referenced by main(), and printProgressReport().

◆ parseQuery()

static bool parseQuery ( Command cmd)
static

Definition at line 5519 of file pgbench.c.

5521{
5522 char *sql,
5523 *p;
5524
5525 cmd->argc = 1;
5526
5527 p = sql = pg_strdup(cmd->lines.data);
5528 while ((p = strchr(p, ':')) != NULL)
5529 {
5530 char var[13];
5531 char *name;
5532 int eaten;
5533
5534 name = parseVariable(p, &eaten);
5535 if (name == NULL)
5536 {
5537 while (*p == ':')
5538 {
5539 p++;
5540 }
5541 continue;
5542 }
5543
5544 /*
5545 * cmd->argv[0] is the SQL statement itself, so the max number of
5546 * arguments is one less than MAX_ARGS
5547 */
5548 if (cmd->argc >= MAX_ARGS)
5549 {
5550 pg_log_error("statement has too many arguments (maximum is %d): %s",
5551 MAX_ARGS - 1, cmd->lines.data);
5552 pg_free(name);
5553 return false;
5554 }
5555
5556 sprintf(var, "$%d", cmd->argc);
5557 p = replaceVariable(&sql, p, eaten, var);
5558
5559 cmd->argv[cmd->argc] = name;
5560 cmd->argc++;
5561 }
5562
5563 Assert(cmd->argv[0] == NULL);
5564 cmd->argv[0] = sql;
5565 return true;
#define MAX_ARGS
Definition pgbench.c:693
#define sprintf
Definition port.h:263

References Command::argc, Command::argv, Assert, PQExpBufferData::data, fb(), Command::lines, MAX_ARGS, name, parseVariable(), pg_free(), pg_log_error, pg_strdup(), replaceVariable(), and sprintf.

Referenced by postprocess_sql_command().

◆ ParseScript()

static void ParseScript ( const char script,
const char desc,
int  weight 
)
static

Definition at line 5998 of file pgbench.c.

6000{
6002 PsqlScanState sstate;
6003 PQExpBufferData line_buf;
6004 int alloc_num;
6005 int index;
6006
6007#define COMMANDS_ALLOC_NUM 128
6009
6010 /* Initialize all fields of ps */
6011 ps.desc = desc;
6012 ps.weight = weight;
6013 ps.commands = pg_malloc_array(Command *, alloc_num);
6014 initStats(&ps.stats, 0);
6015
6016 /* Prepare to parse script */
6018
6019 /*
6020 * Ideally, we'd scan scripts using the encoding and stdstrings settings
6021 * we get from a DB connection. However, without major rearrangement of
6022 * pgbench's argument parsing, we can't have a DB connection at the time
6023 * we parse scripts. Using SQL_ASCII (encoding 0) should work well enough
6024 * with any backend-safe encoding, though conceivably we could be fooled
6025 * if a script file uses a client-only encoding. We also assume that
6026 * stdstrings should be true, which is a bit riskier.
6027 */
6028 psql_scan_setup(sstate, script, strlen(script), 0, true);
6029
6030 initPQExpBuffer(&line_buf);
6031
6032 index = 0;
6033
6034 for (;;)
6035 {
6038 Command *command = NULL;
6039
6040 resetPQExpBuffer(&line_buf);
6041
6042 sr = psql_scan(sstate, &line_buf, &prompt);
6043
6044 /* If we collected a new SQL command, process that */
6045 command = create_sql_command(&line_buf);
6046
6047 /* store new command */
6048 if (command)
6049 ps.commands[index++] = command;
6050
6051 /* If we reached a backslash, process that */
6052 if (sr == PSCAN_BACKSLASH)
6053 {
6054 int lineno;
6055 int start_offset;
6056
6057 /* Capture location of the backslash */
6058 psql_scan_get_location(sstate, &lineno, &start_offset);
6059 start_offset--;
6060
6061 command = process_backslash_command(sstate, desc,
6062 lineno, start_offset);
6063
6064 if (command)
6065 {
6066 /*
6067 * If this is gset or aset, merge into the preceding command.
6068 * (We don't use a command slot in this case).
6069 */
6070 if (command->meta == META_GSET || command->meta == META_ASET)
6071 {
6072 Command *cmd;
6073
6074 if (index == 0)
6075 syntax_error(desc, lineno, NULL, NULL,
6076 "\\gset must follow an SQL command",
6077 NULL, -1);
6078
6079 cmd = ps.commands[index - 1];
6080
6081 if (cmd->type != SQL_COMMAND ||
6082 cmd->varprefix != NULL)
6083 syntax_error(desc, lineno, NULL, NULL,
6084 "\\gset must follow an SQL command",
6085 cmd->first_line, -1);
6086
6087 /* get variable prefix */
6088 if (command->argc <= 1 || command->argv[1][0] == '\0')
6089 cmd->varprefix = pg_strdup("");
6090 else
6091 cmd->varprefix = pg_strdup(command->argv[1]);
6092
6093 /* update the sql command meta */
6094 cmd->meta = command->meta;
6095
6096 /* cleanup unused command */
6097 free_command(command);
6098
6099 continue;
6100 }
6101
6102 /* Attach any other backslash command as a new command */
6103 ps.commands[index++] = command;
6104 }
6105 }
6106
6107 /*
6108 * Since we used a command slot, allocate more if needed. Note we
6109 * always allocate one more in order to accommodate the NULL
6110 * terminator below.
6111 */
6112 if (index >= alloc_num)
6113 {
6115 ps.commands = (Command **)
6116 pg_realloc_array(ps.commands, Command *, alloc_num);
6117 }
6118
6119 /* Done if we reached EOF */
6120 if (sr == PSCAN_INCOMPLETE || sr == PSCAN_EOL)
6121 break;
6122 }
6123
6124 ps.commands[index] = NULL;
6125
6126 addScript(&ps);
6127
6128 termPQExpBuffer(&line_buf);
6129 psql_scan_finish(sstate);
6130 psql_scan_destroy(sstate);
void syntax_error(const char *source, int lineno, const char *line, const char *command, const char *msg, const char *more, int column)
Definition pgbench.c:5580
#define COMMANDS_ALLOC_NUM
static void free_command(Command *command)
Definition pgbench.c:5680
static Command * create_sql_command(PQExpBuffer buf)
Definition pgbench.c:5651
static Command * process_backslash_command(PsqlScanState sstate, const char *source, int lineno, int start_offset)
Definition pgbench.c:5737
static void addScript(const ParsedScript *script)
Definition pgbench.c:6290
static const PsqlScanCallbacks pgbench_callbacks
Definition pgbench.c:855
PsqlScanResult
Definition psqlscan.h:31
@ PSCAN_BACKSLASH
Definition psqlscan.h:33
@ PSCAN_EOL
Definition psqlscan.h:35
@ PSCAN_INCOMPLETE
Definition psqlscan.h:34
enum _promptStatus promptStatus_t
void psql_scan_get_location(PsqlScanState state, int *lineno, int *offset)
Definition psqlscan.l:1347
void psql_scan_destroy(PsqlScanState state)
Definition psqlscan.l:1034
PsqlScanResult psql_scan(PsqlScanState state, PQExpBuffer query_buf, promptStatus_t *prompt)
Definition psqlscan.l:1133
PsqlScanState psql_scan_create(const PsqlScanCallbacks *callbacks)
Definition psqlscan.l:1013
void psql_scan_setup(PsqlScanState state, const char *line, int line_len, int encoding, bool std_strings)
Definition psqlscan.l:1071
void psql_scan_finish(PsqlScanState state)
Definition psqlscan.l:1260
Definition type.h:97

References addScript(), Command::argc, Command::argv, COMMANDS_ALLOC_NUM, create_sql_command(), fb(), Command::first_line, free_command(), initPQExpBuffer(), initStats(), Command::meta, META_ASET, META_GSET, pg_malloc_array, pg_realloc_array, pg_strdup(), pgbench_callbacks, process_backslash_command(), ps, PSCAN_BACKSLASH, PSCAN_EOL, PSCAN_INCOMPLETE, psql_scan(), psql_scan_create(), psql_scan_destroy(), psql_scan_finish(), psql_scan_get_location(), psql_scan_setup(), resetPQExpBuffer(), SQL_COMMAND, syntax_error(), termPQExpBuffer(), Command::type, and Command::varprefix.

Referenced by process_builtin(), and process_file().

◆ parseScriptWeight()

static int parseScriptWeight ( const char option,
char **  script 
)
static

Definition at line 6253 of file pgbench.c.

6255{
6256 const char *sep;
6257 int weight;
6258
6259 if ((sep = strrchr(option, WSEP)))
6260 {
6261 int namelen = sep - option;
6262 long wtmp;
6263 char *badp;
6264
6265 /* generate the script name */
6266 *script = pg_malloc(namelen + 1);
6267 strncpy(*script, option, namelen);
6268 (*script)[namelen] = '\0';
6269
6270 /* process digits of the weight spec */
6271 errno = 0;
6272 wtmp = strtol(sep + 1, &badp, 10);
6273 if (errno != 0 || badp == sep + 1 || *badp != '\0')
6274 pg_fatal("invalid weight specification: %s", sep);
6275 if (wtmp > INT_MAX || wtmp < 0)
6276 pg_fatal("weight specification out of range (0 .. %d): %ld",
6277 INT_MAX, wtmp);
6278 weight = wtmp;
6279 }
6280 else
6281 {
6282 *script = pg_strdup(option);
6283 weight = 1;
6284 }
6285
6286 return weight;
void * pg_malloc(size_t size)
Definition fe_memutils.c:53
#define WSEP
Definition pgbench.c:302

References fb(), pg_fatal, pg_malloc(), pg_strdup(), and WSEP.

Referenced by main().

◆ parseVariable()

static char * parseVariable ( const char sql,
int eaten 
)
static

Definition at line 1883 of file pgbench.c.

1885{
1886 int i = 1; /* starting at 1 skips the colon */
1887 char *name;
1888
1889 /* keep this logic in sync with valid_variable_name() */
1890 if (IS_HIGHBIT_SET(sql[i]) ||
1891 strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
1892 "_", sql[i]) != NULL)
1893 i++;
1894 else
1895 return NULL;
1896
1897 while (IS_HIGHBIT_SET(sql[i]) ||
1898 strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
1899 "_0123456789", sql[i]) != NULL)
1900 i++;
1901
1902 name = pg_malloc(i);
1903 memcpy(name, &sql[1], i - 1);
1904 name[i - 1] = '\0';
1905
1906 *eaten = i;
1907 return name;
#define IS_HIGHBIT_SET(ch)
Definition c.h:1244
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))

References fb(), i, IS_HIGHBIT_SET, memcpy(), name, and pg_malloc().

Referenced by assignVariables(), and parseQuery().

◆ permute()

static int64 permute ( const int64  val,
const int64  isize,
const int64  seed 
)
static

Definition at line 1293 of file pgbench.c.

1295{
1296 /* using a high-end PRNG is probably overkill */
1298 uint64 size;
1299 uint64 v;
1300 int masklen;
1301 uint64 mask;
1302 int i;
1303
1304 if (isize < 2)
1305 return 0; /* nothing to permute */
1306
1307 /* Initialize prng state using the seed */
1308 pg_prng_seed(&state, (uint64) seed);
1309
1310 /* Computations are performed on unsigned values */
1311 size = (uint64) isize;
1312 v = (uint64) val % size;
1313
1314 /* Mask to work modulo largest power of 2 less than or equal to size */
1315 masklen = pg_leftmost_one_pos64(size);
1316 mask = (((uint64) 1) << masklen) - 1;
1317
1318 /*
1319 * Permute the input value by applying several rounds of pseudorandom
1320 * bijective transformations. The intention here is to distribute each
1321 * input uniformly randomly across the range, and separate adjacent inputs
1322 * approximately uniformly randomly from each other, leading to a fairly
1323 * random overall choice of permutation.
1324 *
1325 * To separate adjacent inputs, we multiply by a random number modulo
1326 * (mask + 1), which is a power of 2. For this to be a bijection, the
1327 * multiplier must be odd. Since this is known to lead to less randomness
1328 * in the lower bits, we also apply a rotation that shifts the topmost bit
1329 * into the least significant bit. In the special cases where size <= 3,
1330 * mask = 1 and each of these operations is actually a no-op, so we also
1331 * XOR the value with a different random number to inject additional
1332 * randomness. Since the size is generally not a power of 2, we apply
1333 * this bijection on overlapping upper and lower halves of the input.
1334 *
1335 * To distribute the inputs uniformly across the range, we then also apply
1336 * a random offset modulo the full range.
1337 *
1338 * Taken together, these operations resemble a modified linear
1339 * congruential generator, as is commonly used in pseudorandom number
1340 * generators. The number of rounds is fairly arbitrary, but six has been
1341 * found empirically to give a fairly good tradeoff between performance
1342 * and uniform randomness. For small sizes it selects each of the (size!)
1343 * possible permutations with roughly equal probability. For larger
1344 * sizes, not all permutations can be generated, but the intended random
1345 * spread is still produced.
1346 */
1347 for (i = 0; i < 6; i++)
1348 {
1349 uint64 m,
1350 r,
1351 t;
1352
1353 /* Random multiply (by an odd number), XOR and rotate of lower half */
1354 m = (pg_prng_uint64(&state) & mask) | 1;
1355 r = pg_prng_uint64(&state) & mask;
1356 if (v <= mask)
1357 {
1358 v = ((v * m) ^ r) & mask;
1359 v = ((v << 1) & mask) | (v >> (masklen - 1));
1360 }
1361
1362 /* Random multiply (by an odd number), XOR and rotate of upper half */
1363 m = (pg_prng_uint64(&state) & mask) | 1;
1364 r = pg_prng_uint64(&state) & mask;
1365 t = size - 1 - v;
1366 if (t <= mask)
1367 {
1368 t = ((t * m) ^ r) & mask;
1369 t = ((t << 1) & mask) | (t >> (masklen - 1));
1370 v = size - 1 - t;
1371 }
1372
1373 /* Random offset */
1374 r = pg_prng_uint64_range(&state, 0, size - 1);
1375 v = (v + r) % size;
1376 }
1377
1378 return (int64) v;
static int pg_leftmost_one_pos64(uint64 word)
Definition pg_bitutils.h:72

References fb(), i, pg_leftmost_one_pos64(), pg_prng_seed(), pg_prng_uint64(), pg_prng_uint64_range(), and val.

Referenced by evalStandardFunc().

◆ pg_time_now()

static pg_time_usec_t pg_time_now ( void  )
inlinestatic

◆ pg_time_now_lazy()

static void pg_time_now_lazy ( pg_time_usec_t now)
inlinestatic

Definition at line 895 of file pgbench.c.

897{
898 if ((*now) == 0)
899 (*now) = pg_time_now();

References now(), and pg_time_now().

Referenced by advanceConnectionState(), doRetry(), executeMetaCommand(), printVerboseErrorMessages(), processXactStats(), and threadRun().

◆ postprocess_sql_command()

static void postprocess_sql_command ( Command my_command)
static

Definition at line 5700 of file pgbench.c.

5702{
5703 char buffer[128];
5704 static int prepnum = 0;
5705
5706 Assert(my_command->type == SQL_COMMAND);
5707
5708 /* Save the first line for error display. */
5709 strlcpy(buffer, my_command->lines.data, sizeof(buffer));
5710 buffer[strcspn(buffer, "\n\r")] = '\0';
5711 my_command->first_line = pg_strdup(buffer);
5712
5713 /* Parse query and generate prepared statement name, if necessary */
5714 switch (querymode)
5715 {
5716 case QUERY_SIMPLE:
5717 my_command->argv[0] = my_command->lines.data;
5718 my_command->argc++;
5719 break;
5720 case QUERY_PREPARED:
5721 my_command->prepname = psprintf("P_%d", prepnum++);
5723 case QUERY_EXTENDED:
5724 if (!parseQuery(my_command))
5725 exit(1);
5726 break;
5727 default:
5728 exit(1);
5729 }
static bool parseQuery(Command *cmd)
Definition pgbench.c:5519
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition strlcpy.c:45
char * psprintf(const char *fmt,...)
Definition psprintf.c:43

References Assert, fb(), parseQuery(), pg_fallthrough, pg_strdup(), psprintf(), QUERY_EXTENDED, QUERY_PREPARED, QUERY_SIMPLE, querymode, SQL_COMMAND, and strlcpy().

Referenced by main().

◆ prepareCommand()

static void prepareCommand ( CState st,
int  command_num 
)
static

Definition at line 3085 of file pgbench.c.

3087{
3089
3090 /* No prepare for non-SQL commands */
3091 if (command->type != SQL_COMMAND)
3092 return;
3093
3094 if (!st->prepared)
3096
3097 if (!st->prepared[st->use_file][command_num])
3098 {
3099 PGresult *res;
3100
3101 pg_log_debug("client %d preparing %s", st->id, command->prepname);
3102 res = PQprepare(st->con, command->prepname,
3103 command->argv[0], command->argc - 1, NULL);
3104 if (PQresultStatus(res) != PGRES_COMMAND_OK)
3105 pg_log_error("%s", PQerrorMessage(st->con));
3106 PQclear(res);
3107 st->prepared[st->use_file][command_num] = true;
3108 }
PGresult * PQprepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes)
Definition fe-exec.c:2323
static void allocCStatePrepared(CState *st)
Definition pgbench.c:3065
char * prepname
Definition pgbench.c:756

References allocCStatePrepared(), Command::argc, Command::argv, ParsedScript::commands, CState::con, fb(), CState::id, pg_log_debug, pg_log_error, PGRES_COMMAND_OK, PQclear, PQerrorMessage(), PQprepare(), PQresultStatus, CState::prepared, Command::prepname, SQL_COMMAND, sql_script, Command::type, and CState::use_file.

Referenced by prepareCommandsInPipeline(), and sendCommand().

◆ prepareCommandsInPipeline()

static void prepareCommandsInPipeline ( CState st)
static

Definition at line 3118 of file pgbench.c.

3120{
3121 int j;
3122 Command **commands = sql_script[st->use_file].commands;
3123
3124 Assert(commands[st->command]->type == META_COMMAND &&
3125 commands[st->command]->meta == META_STARTPIPELINE);
3126
3127 if (!st->prepared)
3129
3130 /*
3131 * We set the 'prepared' flag on the \startpipeline itself to flag that we
3132 * don't need to do this next time without calling prepareCommand(), even
3133 * though we don't actually prepare this command.
3134 */
3135 if (st->prepared[st->use_file][st->command])
3136 return;
3137
3138 for (j = st->command + 1; commands[j] != NULL; j++)
3139 {
3140 if (commands[j]->type == META_COMMAND &&
3141 commands[j]->meta == META_ENDPIPELINE)
3142 break;
3143
3144 prepareCommand(st, j);
3145 }
3146
3147 st->prepared[st->use_file][st->command] = true;
static void prepareCommand(CState *st, int command_num)
Definition pgbench.c:3085

References allocCStatePrepared(), Assert, CState::command, ParsedScript::commands, fb(), j, Command::meta, META_COMMAND, META_ENDPIPELINE, META_STARTPIPELINE, prepareCommand(), CState::prepared, sql_script, type, Command::type, and CState::use_file.

Referenced by executeMetaCommand().

◆ printProgressReport()

static void printProgressReport ( TState threads,
int64  test_start,
pg_time_usec_t  now,
StatsData last,
int64 last_report 
)
static

Definition at line 6311 of file pgbench.c.

6314{
6315 /* generate and show report */
6317 int64 cnt,
6318 failures,
6319 retried;
6320 double tps,
6321 total_run,
6322 latency,
6323 sqlat,
6324 lag,
6325 stdev;
6326 char tbuf[315];
6327 StatsData cur;
6328
6329 /*
6330 * Add up the statistics of all threads.
6331 *
6332 * XXX: No locking. There is no guarantee that we get an atomic snapshot
6333 * of the transaction count and latencies, so these figures can well be
6334 * off by a small amount. The progress report's purpose is to give a
6335 * quick overview of how the test is going, so that shouldn't matter too
6336 * much. (If a read from a 64-bit integer is not atomic, you might get a
6337 * "torn" read and completely bogus latencies though!)
6338 */
6339 initStats(&cur, 0);
6340 for (int i = 0; i < nthreads; i++)
6341 {
6342 mergeSimpleStats(&cur.latency, &threads[i].stats.latency);
6343 mergeSimpleStats(&cur.lag, &threads[i].stats.lag);
6344 cur.cnt += threads[i].stats.cnt;
6345 cur.skipped += threads[i].stats.skipped;
6346 cur.retries += threads[i].stats.retries;
6347 cur.retried += threads[i].stats.retried;
6348 cur.serialization_failures +=
6349 threads[i].stats.serialization_failures;
6350 cur.deadlock_failures += threads[i].stats.deadlock_failures;
6351 cur.other_sql_failures += threads[i].stats.other_sql_failures;
6352 }
6353
6354 /* we count only actually executed transactions */
6355 cnt = cur.cnt - last->cnt;
6356 total_run = (now - test_start) / 1000000.0;
6357 tps = 1000000.0 * cnt / run;
6358 if (cnt > 0)
6359 {
6360 latency = 0.001 * (cur.latency.sum - last->latency.sum) / cnt;
6361 sqlat = 1.0 * (cur.latency.sum2 - last->latency.sum2) / cnt;
6362 stdev = 0.001 * sqrt(sqlat - 1000000.0 * latency * latency);
6363 lag = 0.001 * (cur.lag.sum - last->lag.sum) / cnt;
6364 }
6365 else
6366 {
6367 latency = sqlat = stdev = lag = 0;
6368 }
6369 failures = getFailures(&cur) - getFailures(last);
6370 retried = cur.retried - last->retried;
6371
6373 {
6374 snprintf(tbuf, sizeof(tbuf), "%.3f s",
6376 }
6377 else
6378 {
6379 /* round seconds are expected, but the thread may be late */
6380 snprintf(tbuf, sizeof(tbuf), "%.1f s", total_run);
6381 }
6382
6384 "progress: %s, %.1f tps, lat %.3f ms stddev %.3f, " INT64_FORMAT " failed",
6385 tbuf, tps, latency, stdev, failures);
6386
6387 if (throttle_delay)
6388 {
6389 fprintf(stderr, ", lag %.3f ms", lag);
6390 if (latency_limit)
6391 fprintf(stderr, ", " INT64_FORMAT " skipped",
6392 cur.skipped - last->skipped);
6393 }
6394
6395 /* it can be non-zero only if max_tries is not equal to one */
6396 if (max_tries != 1)
6398 ", " INT64_FORMAT " retried, " INT64_FORMAT " retries",
6399 retried, cur.retries - last->retries);
6400 fprintf(stderr, "\n");
6401
6402 *last = cur;
6403 *last_report = now;
struct cursor * cur
Definition ecpg.c:29
static int64 getFailures(const StatsData *stats)
Definition pgbench.c:4584

References StatsData::cnt, cur, epoch_shift, fb(), fprintf, getFailures(), i, initStats(), INT64_FORMAT, StatsData::lag, StatsData::latency, latency_limit, max_tries, mergeSimpleStats(), now(), nthreads, PG_TIME_GET_DOUBLE, progress_timestamp, StatsData::retried, StatsData::retries, StatsData::skipped, snprintf, SimpleStats::sum, SimpleStats::sum2, and throttle_delay.

Referenced by threadRun().

◆ printResults()

static void printResults ( StatsData total,
pg_time_usec_t  total_duration,
pg_time_usec_t  conn_total_duration,
pg_time_usec_t  conn_elapsed_duration,
int64  latency_late 
)
static

Definition at line 6451 of file pgbench.c.

6457{
6458 /* tps is about actually executed transactions during benchmarking */
6459 int64 failures = getFailures(total);
6460 int64 total_cnt = total->cnt + total->skipped + failures;
6462 double tps = total->cnt / bench_duration;
6463
6464 /* Report test parameters. */
6465 printf("transaction type: %s\n",
6466 num_scripts == 1 ? sql_script[0].desc : "multiple scripts");
6467 printf("scaling factor: %d\n", scale);
6468 /* only print partitioning information if some partitioning was detected */
6470 printf("partition method: %s\npartitions: %d\n",
6472 printf("query mode: %s\n", QUERYMODE[querymode]);
6473 printf("number of clients: %d\n", nclients);
6474 printf("number of threads: %d\n", nthreads);
6475
6476 if (max_tries)
6477 printf("maximum number of tries: %u\n", max_tries);
6478
6479 if (duration <= 0)
6480 {
6481 printf("number of transactions per client: %d\n", nxacts);
6482 printf("number of transactions actually processed: " INT64_FORMAT "/%d\n",
6483 total->cnt, nxacts * nclients);
6484 }
6485 else
6486 {
6487 printf("duration: %d s\n", duration);
6488 printf("number of transactions actually processed: " INT64_FORMAT "\n",
6489 total->cnt);
6490 }
6491
6492 /*
6493 * Remaining stats are nonsensical if we failed to execute any xacts due
6494 * to other than serialization or deadlock errors and --continue-on-error
6495 * is not set.
6496 */
6497 if (total_cnt <= 0)
6498 return;
6499
6500 printf("number of failed transactions: " INT64_FORMAT " (%.3f%%)\n",
6501 failures, 100.0 * failures / total_cnt);
6502
6504 {
6505 printf("number of serialization failures: " INT64_FORMAT " (%.3f%%)\n",
6507 100.0 * total->serialization_failures / total_cnt);
6508 printf("number of deadlock failures: " INT64_FORMAT " (%.3f%%)\n",
6509 total->deadlock_failures,
6510 100.0 * total->deadlock_failures / total_cnt);
6511 printf("number of other failures: " INT64_FORMAT " (%.3f%%)\n",
6512 total->other_sql_failures,
6513 100.0 * total->other_sql_failures / total_cnt);
6514 }
6515
6516 /* it can be non-zero only if max_tries is not equal to one */
6517 if (max_tries != 1)
6518 {
6519 printf("number of transactions retried: " INT64_FORMAT " (%.3f%%)\n",
6520 total->retried, 100.0 * total->retried / total_cnt);
6521 printf("total number of retries: " INT64_FORMAT "\n", total->retries);
6522 }
6523
6525 printf("number of transactions skipped: " INT64_FORMAT " (%.3f%%)\n",
6526 total->skipped, 100.0 * total->skipped / total_cnt);
6527
6528 if (latency_limit)
6529 printf("number of transactions above the %.1f ms latency limit: " INT64_FORMAT "/" INT64_FORMAT " (%.3f%%)\n",
6530 latency_limit / 1000.0, latency_late, total->cnt,
6531 (total->cnt > 0) ? 100.0 * latency_late / total->cnt : 0.0);
6532
6534 printSimpleStats("latency", &total->latency);
6535 else
6536 {
6537 /* no measurement, show average latency computed from run time */
6538 printf("latency average = %.3f ms%s\n",
6539 0.001 * total_duration * nclients / total_cnt,
6540 failures > 0 ? " (including failures)" : "");
6541 }
6542
6543 if (throttle_delay)
6544 {
6545 /*
6546 * Report average transaction lag under rate limit throttling. This
6547 * is the delay between scheduled and actual start times for the
6548 * transaction. The measured lag may be caused by thread/client load,
6549 * the database load, or the Poisson throttling process.
6550 */
6551 printf("rate limit schedule lag: avg %.3f (max %.3f) ms\n",
6552 0.001 * total->lag.sum / total->cnt, 0.001 * total->lag.max);
6553 }
6554
6555 /*
6556 * Under -C/--connect, each transaction incurs a significant connection
6557 * cost, it would not make much sense to ignore it in tps, and it would
6558 * not be tps anyway.
6559 *
6560 * Otherwise connections are made just once at the beginning of the run
6561 * and should not impact performance but for very short run, so they are
6562 * (right)fully ignored in tps.
6563 */
6564 if (is_connect)
6565 {
6566 printf("average connection time = %.3f ms\n", 0.001 * conn_total_duration / (total->cnt + failures));
6567 printf("tps = %f (including reconnection times)\n", tps);
6568 }
6569 else
6570 {
6571 printf("initial connection time = %.3f ms\n", 0.001 * conn_elapsed_duration);
6572 printf("tps = %f (without initial connection time)\n", tps);
6573 }
6574
6575 /* Report per-script/command statistics */
6577 {
6578 int i;
6579
6580 for (i = 0; i < num_scripts; i++)
6581 {
6582 if (per_script_stats)
6583 {
6587 sstats->cnt + sstats->skipped + script_failures;
6588
6589 printf("SQL script %d: %s\n"
6590 " - weight: %d (targets %.1f%% of total)\n"
6591 " - " INT64_FORMAT " transactions (%.1f%% of total)\n",
6592 i + 1, sql_script[i].desc,
6593 sql_script[i].weight,
6594 100.0 * sql_script[i].weight / total_weight,
6596 100.0 * script_total_cnt / total_cnt);
6597
6598 if (script_total_cnt > 0)
6599 {
6600 printf(" - number of transactions actually processed: " INT64_FORMAT " (tps = %f)\n",
6601 sstats->cnt, sstats->cnt / bench_duration);
6602
6603 printf(" - number of failed transactions: " INT64_FORMAT " (%.3f%%)\n",
6606
6608 {
6609 printf(" - number of serialization failures: " INT64_FORMAT " (%.3f%%)\n",
6610 sstats->serialization_failures,
6611 (100.0 * sstats->serialization_failures /
6613 printf(" - number of deadlock failures: " INT64_FORMAT " (%.3f%%)\n",
6614 sstats->deadlock_failures,
6615 (100.0 * sstats->deadlock_failures /
6617 printf(" - number of other failures: " INT64_FORMAT " (%.3f%%)\n",
6618 sstats->other_sql_failures,
6619 (100.0 * sstats->other_sql_failures /
6621 }
6622
6623 /*
6624 * it can be non-zero only if max_tries is not equal to
6625 * one
6626 */
6627 if (max_tries != 1)
6628 {
6629 printf(" - number of transactions retried: " INT64_FORMAT " (%.3f%%)\n",
6630 sstats->retried,
6631 100.0 * sstats->retried / script_total_cnt);
6632 printf(" - total number of retries: " INT64_FORMAT "\n",
6633 sstats->retries);
6634 }
6635
6637 printf(" - number of transactions skipped: " INT64_FORMAT " (%.3f%%)\n",
6638 sstats->skipped,
6639 100.0 * sstats->skipped / script_total_cnt);
6640
6641 }
6642 printSimpleStats(" - latency", &sstats->latency);
6643 }
6644
6645 /*
6646 * Report per-command statistics: latencies, retries after errors,
6647 * failures (errors without retrying).
6648 */
6650 {
6651 Command **commands;
6652
6653 printf("%sstatement latencies in milliseconds%s:\n",
6654 per_script_stats ? " - " : "",
6655 (max_tries == 1 ?
6656 " and failures" :
6657 ", failures and retries"));
6658
6659 for (commands = sql_script[i].commands;
6660 *commands != NULL;
6661 commands++)
6662 {
6663 SimpleStats *cstats = &(*commands)->stats;
6664
6665 if (max_tries == 1)
6666 printf(" %11.3f %10" PRId64 " %s\n",
6667 (cstats->count > 0) ?
6668 1000.0 * cstats->sum / cstats->count : 0.0,
6669 (*commands)->failures,
6670 (*commands)->first_line);
6671 else
6672 printf(" %11.3f %10" PRId64 " %10" PRId64 " %s\n",
6673 (cstats->count > 0) ?
6674 1000.0 * cstats->sum / cstats->count : 0.0,
6675 (*commands)->failures,
6676 (*commands)->retries,
6677 (*commands)->first_line);
6678 }
6679 }
6680 }
6681 }
static void printSimpleStats(const char *prefix, SimpleStats *ss)
Definition pgbench.c:6406
#define printf(...)
Definition port.h:267
StatsData stats
Definition pgbench.c:769

References StatsData::cnt, StatsData::deadlock_failures, duration, failures_detailed, fb(), getFailures(), i, INT64_FORMAT, is_connect, StatsData::lag, StatsData::latency, latency_limit, SimpleStats::max, max_tries, nclients, nthreads, num_scripts, nxacts, StatsData::other_sql_failures, PART_NONE, partition_method, PARTITION_METHOD, partitions, per_script_stats, PG_TIME_GET_DOUBLE, printf, printSimpleStats(), progress, querymode, QUERYMODE, report_per_command, StatsData::retried, StatsData::retries, scale, StatsData::serialization_failures, StatsData::skipped, sql_script, ParsedScript::stats, SimpleStats::sum, throttle_delay, and total_weight.

Referenced by main().

◆ printSimpleStats()

static void printSimpleStats ( const char prefix,
SimpleStats ss 
)
static

Definition at line 6406 of file pgbench.c.

6408{
6409 if (ss->count > 0)
6410 {
6411 double latency = ss->sum / ss->count;
6412 double stddev = sqrt(ss->sum2 / ss->count - latency * latency);
6413
6414 printf("%s average = %.3f ms\n", prefix, 0.001 * latency);
6415 printf("%s stddev = %.3f ms\n", prefix, 0.001 * stddev);
6416 }

References SimpleStats::count, fb(), printf, SimpleStats::sum, and SimpleStats::sum2.

Referenced by printResults().

◆ printVerboseErrorMessages()

static void printVerboseErrorMessages ( CState st,
pg_time_usec_t now,
bool  is_retry 
)
static

Definition at line 3630 of file pgbench.c.

3632{
3634
3636
3637 printfPQExpBuffer(&buf, "client %d ", st->id);
3639 "repeats the transaction after the error" :
3640 "ends the failed transaction"));
3641 appendPQExpBuffer(&buf, " (try %u", st->tries);
3642
3643 /* Print max_tries if it is not unlimited. */
3644 if (max_tries)
3646
3647 /*
3648 * If the latency limit is used, print a percentage of the current
3649 * transaction latency from the latency limit.
3650 */
3651 if (latency_limit)
3652 {
3654 appendPQExpBuffer(&buf, ", %.3f%% of the maximum time of tries was used",
3655 (100.0 * (*now - st->txn_scheduled) / latency_limit));
3656 }
3657 appendPQExpBufferStr(&buf, ")\n");
3658
3659 pg_log_info("%s", buf.data);
3660

References appendPQExpBuffer(), appendPQExpBufferStr(), buf, fb(), CState::id, initPQExpBuffer(), latency_limit, max_tries, now(), pg_log_info, pg_time_now_lazy(), printfPQExpBuffer(), termPQExpBuffer(), CState::tries, and CState::txn_scheduled.

Referenced by advanceConnectionState().

◆ printVersion()

static void printVersion ( PGconn con)
static

Definition at line 6420 of file pgbench.c.

6422{
6423 int server_ver = PQserverVersion(con);
6425
6426 if (server_ver != client_ver)
6427 {
6428 const char *server_version;
6429 char sverbuf[32];
6430
6431 /* Try to get full text form, might include "devel" etc */
6432 server_version = PQparameterStatus(con, "server_version");
6433 /* Otherwise fall back on server_ver */
6434 if (!server_version)
6435 {
6437 sverbuf, sizeof(sverbuf));
6439 }
6440
6441 printf(_("%s (%s, server %s)\n"),
6442 "pgbench", PG_VERSION, server_version);
6443 }
6444 /* For version match, only print pgbench version */
6445 else
6446 printf("%s (%s)\n", "pgbench", PG_VERSION);
6447 fflush(stdout);
#define _(x)
Definition elog.c:96
const char * PQparameterStatus(const PGconn *conn, const char *paramName)
static int server_version
Definition pg_dumpall.c:122
char * formatPGVersionNumber(int version_number, bool include_minor, char *buf, size_t buflen)

References _, fb(), formatPGVersionNumber(), PQparameterStatus(), PQserverVersion(), printf, and server_version.

Referenced by main().

◆ process_backslash_command()

static Command * process_backslash_command ( PsqlScanState  sstate,
const char source,
int  lineno,
int  start_offset 
)
static

Definition at line 5737 of file pgbench.c.

5740{
5743 int word_offset;
5744 int offsets[MAX_ARGS]; /* offsets of argument words */
5745 int j;
5746
5748
5749 /* Collect first word of command */
5750 if (!expr_lex_one_word(sstate, &word_buf, &word_offset))
5751 {
5753 return NULL;
5754 }
5755
5756 /* Allocate and initialize Command structure */
5758 my_command->type = META_COMMAND;
5759 my_command->argc = 0;
5760 initSimpleStats(&my_command->stats);
5761
5762 /* Save first word (command name) */
5763 j = 0;
5764 offsets[j] = word_offset;
5765 my_command->argv[j++] = pg_strdup(word_buf.data);
5766 my_command->argc++;
5767
5768 /* ... and convert it to enum form */
5769 my_command->meta = getMetaCommand(my_command->argv[0]);
5770
5771 if (my_command->meta == META_SET ||
5772 my_command->meta == META_IF ||
5773 my_command->meta == META_ELIF)
5774 {
5775 yyscan_t yyscanner;
5776
5777 /* For \set, collect var name */
5778 if (my_command->meta == META_SET)
5779 {
5780 if (!expr_lex_one_word(sstate, &word_buf, &word_offset))
5781 syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
5782 "missing argument", NULL, -1);
5783
5784 offsets[j] = word_offset;
5785 my_command->argv[j++] = pg_strdup(word_buf.data);
5786 my_command->argc++;
5787 }
5788
5789 /* then for all parse the expression */
5790 yyscanner = expr_scanner_init(sstate, source, lineno, start_offset,
5791 my_command->argv[0]);
5792
5793 if (expr_yyparse(&my_command->expr, yyscanner) != 0)
5794 {
5795 /* dead code: exit done from syntax_error called by yyerror */
5796 exit(1);
5797 }
5798
5799 /* Save line, trimming any trailing newline */
5800 my_command->first_line =
5803 true);
5804
5805 expr_scanner_finish(yyscanner);
5806
5808
5809 return my_command;
5810 }
5811
5812 /* For all other commands, collect remaining words. */
5813 while (expr_lex_one_word(sstate, &word_buf, &word_offset))
5814 {
5815 /*
5816 * my_command->argv[0] is the command itself, so the max number of
5817 * arguments is one less than MAX_ARGS
5818 */
5819 if (j >= MAX_ARGS)
5820 syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
5821 "too many arguments", NULL, -1);
5822
5823 offsets[j] = word_offset;
5824 my_command->argv[j++] = pg_strdup(word_buf.data);
5825 my_command->argc++;
5826 }
5827
5828 /* Save line, trimming any trailing newline */
5829 my_command->first_line =
5832 true);
5833
5834 if (my_command->meta == META_SLEEP)
5835 {
5836 if (my_command->argc < 2)
5837 syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
5838 "missing argument", NULL, -1);
5839
5840 if (my_command->argc > 3)
5841 syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
5842 "too many arguments", NULL,
5843 offsets[3] - start_offset);
5844
5845 /*
5846 * Split argument into number and unit to allow "sleep 1ms" etc. We
5847 * don't have to terminate the number argument with null because it
5848 * will be parsed with atoi, which ignores trailing non-digit
5849 * characters.
5850 */
5851 if (my_command->argv[1][0] != ':')
5852 {
5853 char *c = my_command->argv[1];
5854 bool have_digit = false;
5855
5856 /* Skip sign */
5857 if (*c == '+' || *c == '-')
5858 c++;
5859
5860 /* Require at least one digit */
5861 if (*c && isdigit((unsigned char) *c))
5862 have_digit = true;
5863
5864 /* Eat all digits */
5865 while (*c && isdigit((unsigned char) *c))
5866 c++;
5867
5868 if (*c)
5869 {
5870 if (my_command->argc == 2 && have_digit)
5871 {
5872 my_command->argv[2] = c;
5873 offsets[2] = offsets[1] + (c - my_command->argv[1]);
5874 my_command->argc = 3;
5875 }
5876 else
5877 {
5878 /*
5879 * Raise an error if argument starts with non-digit
5880 * character (after sign).
5881 */
5882 syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
5883 "invalid sleep time, must be an integer",
5884 my_command->argv[1], offsets[1] - start_offset);
5885 }
5886 }
5887 }
5888
5889 if (my_command->argc == 3)
5890 {
5891 if (pg_strcasecmp(my_command->argv[2], "us") != 0 &&
5892 pg_strcasecmp(my_command->argv[2], "ms") != 0 &&
5893 pg_strcasecmp(my_command->argv[2], "s") != 0)
5894 syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
5895 "unrecognized time unit, must be us, ms or s",
5896 my_command->argv[2], offsets[2] - start_offset);
5897 }
5898 }
5899 else if (my_command->meta == META_SETSHELL)
5900 {
5901 if (my_command->argc < 3)
5902 syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
5903 "missing argument", NULL, -1);
5904 }
5905 else if (my_command->meta == META_SHELL)
5906 {
5907 if (my_command->argc < 2)
5908 syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
5909 "missing command", NULL, -1);
5910 }
5911 else if (my_command->meta == META_ELSE || my_command->meta == META_ENDIF ||
5912 my_command->meta == META_STARTPIPELINE ||
5913 my_command->meta == META_ENDPIPELINE ||
5915 {
5916 if (my_command->argc != 1)
5917 syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
5918 "unexpected argument", NULL, -1);
5919 }
5920 else if (my_command->meta == META_GSET || my_command->meta == META_ASET)
5921 {
5922 if (my_command->argc > 2)
5923 syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
5924 "too many arguments", NULL, -1);
5925 }
5926 else
5927 {
5928 /* my_command->meta == META_NONE */
5929 syntax_error(source, lineno, my_command->first_line, my_command->argv[0],
5930 "invalid command", NULL, -1);
5931 }
5932
5934
5935 return my_command;
void * yyscan_t
Definition cubedata.h:65
bool expr_lex_one_word(PsqlScanState state, PQExpBuffer word_buf, int *offset)
Definition exprscan.l:318
char * expr_scanner_get_substring(PsqlScanState state, int start_offset, bool chomp)
Definition exprscan.l:425
void expr_scanner_finish(yyscan_t yyscanner)
Definition exprscan.l:402
yyscan_t expr_scanner_init(PsqlScanState state, const char *source, int lineno, int start_offset, const char *command)
Definition exprscan.l:370
static rewind_source * source
Definition pg_rewind.c:89
static MetaCommand getMetaCommand(const char *cmd)
Definition pgbench.c:2869
int expr_yyparse(PgBenchExpr **expr_parse_result_p, yyscan_t yyscanner)

References expr_lex_one_word(), expr_scanner_finish(), expr_scanner_get_substring(), expr_scanner_init(), expr_yyparse(), fb(), getMetaCommand(), initPQExpBuffer(), initSimpleStats(), j, MAX_ARGS, META_ASET, META_COMMAND, META_ELIF, META_ELSE, META_ENDIF, META_ENDPIPELINE, META_GSET, META_IF, META_SET, META_SETSHELL, META_SHELL, META_SLEEP, META_STARTPIPELINE, META_SYNCPIPELINE, pg_malloc0_object, pg_strcasecmp(), pg_strdup(), source, syntax_error(), and termPQExpBuffer().

Referenced by ParseScript().

◆ process_builtin()

static void process_builtin ( const BuiltinScript bi,
int  weight 
)
static

Definition at line 6198 of file pgbench.c.

6200{
6201 ParseScript(bi->script, bi->desc, weight);
static void ParseScript(const char *script, const char *desc, int weight)
Definition pgbench.c:5998

References fb(), and ParseScript().

Referenced by main().

◆ process_file()

static void process_file ( const char filename,
int  weight 
)
static

Definition at line 6172 of file pgbench.c.

6174{
6175 FILE *fd;
6176 char *buf;
6177
6178 /* Slurp the file contents into "buf" */
6179 if (strcmp(filename, "-") == 0)
6180 fd = stdin;
6181 else if ((fd = fopen(filename, "r")) == NULL)
6182 pg_fatal("could not open file \"%s\": %m", filename);
6183
6185
6186 if (ferror(fd))
6187 pg_fatal("could not read file \"%s\": %m", filename);
6188
6189 if (fd != stdin)
6190 fclose(fd);
6191
6192 ParseScript(buf, filename, weight);
6193
6194 free(buf);
static char * filename
Definition pg_dumpall.c:133
static char * read_file_contents(FILE *fd)
Definition pgbench.c:6139

References buf, fb(), fd(), filename, free, ParseScript(), pg_fatal, and read_file_contents().

Referenced by main(), main(), and process_psqlrc_file().

◆ processXactStats()

static void processXactStats ( TState thread,
CState st,
pg_time_usec_t now,
bool  skipped,
StatsData agg 
)
static

Definition at line 4752 of file pgbench.c.

4755{
4756 double latency = 0.0,
4757 lag = 0.0;
4760
4761 if (detailed && !skipped && st->estatus == ESTATUS_NO_ERROR)
4762 {
4764
4765 /* compute latency & lag */
4766 latency = (*now) - st->txn_scheduled;
4767 lag = st->txn_begin - st->txn_scheduled;
4768 }
4769
4770 /* keep detailed thread stats */
4771 accumStats(&thread->stats, skipped, latency, lag, st->estatus, st->tries);
4772
4773 /* count transactions over the latency limit, if needed */
4774 if (latency_limit && latency > latency_limit)
4775 thread->latency_late++;
4776
4777 /* client stat is just counting */
4778 st->cnt++;
4779
4780 if (use_log)
4781 doLog(thread, st, agg, skipped, latency, lag);
4782
4783 /* XXX could use a mutex here, but we choose not to */
4784 if (per_script_stats)
4785 accumStats(&sql_script[st->use_file].stats, skipped, latency, lag,
4786 st->estatus, st->tries);
static void doLog(TState *thread, CState *st, StatsData *agg, bool skipped, double latency, double lag)
Definition pgbench.c:4629

References accumStats(), CState::cnt, doLog(), CState::estatus, ESTATUS_NO_ERROR, fb(), TState::latency_late, latency_limit, now(), per_script_stats, pg_time_now_lazy(), progress, sql_script, TState::stats, ParsedScript::stats, throttle_delay, CState::tries, CState::txn_begin, CState::txn_scheduled, CState::use_file, and use_log.

Referenced by advanceConnectionState().

◆ putVariable()

static bool putVariable ( Variables variables,
const char context,
char name,
const char value 
)
static

Definition at line 1823 of file pgbench.c.

1826{
1827 Variable *var;
1828 char *val;
1829
1830 var = lookupCreateVariable(variables, context, name);
1831 if (!var)
1832 return false;
1833
1834 /* dup then free, in case value is pointing at this variable */
1835 val = pg_strdup(value);
1836
1837 free(var->svalue);
1838 var->svalue = val;
1839 var->value.type = PGBT_NO_VALUE;
1840
1841 return true;
static struct @177 value
static Variable * lookupCreateVariable(Variables *variables, const char *context, char *name)
Definition pgbench.c:1786

References free, lookupCreateVariable(), name, pg_strdup(), PGBT_NO_VALUE, Variable::svalue, PgBenchValue::type, val, Variable::value, and value.

Referenced by main(), and readCommandResponse().

◆ putVariableInt()

static bool putVariableInt ( Variables variables,
const char context,
char name,
int64  value 
)
static

Definition at line 1865 of file pgbench.c.

1868{
1870
1872 return putVariableValue(variables, context, name, &val);

References name, putVariableValue(), setIntValue(), val, and value.

Referenced by main(), and runShellCommand().

◆ putVariableValue()

static bool putVariableValue ( Variables variables,
const char context,
char name,
const PgBenchValue value 
)
static

Definition at line 1846 of file pgbench.c.

1849{
1850 Variable *var;
1851
1852 var = lookupCreateVariable(variables, context, name);
1853 if (!var)
1854 return false;
1855
1856 free(var->svalue);
1857 var->svalue = NULL;
1858 var->value = *value;
1859
1860 return true;

References fb(), free, lookupCreateVariable(), name, Variable::svalue, Variable::value, and value.

Referenced by executeMetaCommand(), main(), and putVariableInt().

◆ read_file_contents()

static char * read_file_contents ( FILE fd)
static

Definition at line 6139 of file pgbench.c.

6141{
6142 char *buf;
6143 size_t buflen = BUFSIZ;
6144 size_t used = 0;
6145
6146 buf = (char *) pg_malloc(buflen);
6147
6148 for (;;)
6149 {
6150 size_t nread;
6151
6152 nread = fread(buf + used, 1, BUFSIZ, fd);
6153 used += nread;
6154 /* If fread() read less than requested, must be EOF or error */
6155 if (nread < BUFSIZ)
6156 break;
6157 /* Enlarge buf so we can read some more */
6158 buflen += BUFSIZ;
6159 buf = (char *) pg_realloc(buf, buflen);
6160 }
6161 /* There is surely room for a terminator */
6162 buf[used] = '\0';
6163
6164 return buf;

References buf, fb(), fd(), pg_malloc(), and pg_realloc().

Referenced by process_file().

◆ readCommandResponse()

static bool readCommandResponse ( CState st,
MetaCommand  meta,
char varprefix 
)
static

Definition at line 3280 of file pgbench.c.

3282{
3283 PGresult *res;
3285 int qrynum = 0;
3286
3287 /*
3288 * varprefix should be set only with \gset or \aset, and \endpipeline and
3289 * SQL commands do not need it.
3290 */
3291 Assert((meta == META_NONE && varprefix == NULL) ||
3292 ((meta == META_ENDPIPELINE) && varprefix == NULL) ||
3293 ((meta == META_GSET || meta == META_ASET) && varprefix != NULL));
3294
3295 res = PQgetResult(st->con);
3296
3297 while (res != NULL)
3298 {
3299 bool is_last;
3300
3301 /* peek at the next result to know whether the current is last */
3302 next_res = PQgetResult(st->con);
3303 is_last = (next_res == NULL);
3304
3305 switch (PQresultStatus(res))
3306 {
3307 case PGRES_COMMAND_OK: /* non-SELECT commands */
3308 case PGRES_EMPTY_QUERY: /* may be used for testing no-op overhead */
3309 if (is_last && meta == META_GSET)
3310 {
3311 pg_log_error("client %d script %d command %d query %d: expected one row, got %d",
3312 st->id, st->use_file, st->command, qrynum, 0);
3314 goto error;
3315 }
3316 break;
3317
3318 case PGRES_TUPLES_OK:
3319 if ((is_last && meta == META_GSET) || meta == META_ASET)
3320 {
3321 int ntuples = PQntuples(res);
3322
3323 if (meta == META_GSET && ntuples != 1)
3324 {
3325 /* under \gset, report the error */
3326 pg_log_error("client %d script %d command %d query %d: expected one row, got %d",
3327 st->id, st->use_file, st->command, qrynum, PQntuples(res));
3329 goto error;
3330 }
3331 else if (meta == META_ASET && ntuples <= 0)
3332 {
3333 /* coldly skip empty result under \aset */
3334 break;
3335 }
3336
3337 /* store results into variables */
3338 for (int fld = 0; fld < PQnfields(res); fld++)
3339 {
3340 char *varname = PQfname(res, fld);
3341
3342 /* allocate varname only if necessary, freed below */
3343 if (*varprefix != '\0')
3344 varname = psprintf("%s%s", varprefix, varname);
3345
3346 /* store last row result as a string */
3347 if (!putVariable(&st->variables, meta == META_ASET ? "aset" : "gset", varname,
3348 PQgetvalue(res, ntuples - 1, fld)))
3349 {
3350 /* internal error */
3351 pg_log_error("client %d script %d command %d query %d: error storing into variable %s",
3352 st->id, st->use_file, st->command, qrynum, varname);
3354 goto error;
3355 }
3356
3357 if (*varprefix != '\0')
3358 pg_free(varname);
3359 }
3360 }
3361 /* otherwise the result is simply thrown away by PQclear below */
3362 break;
3363
3365 pg_log_debug("client %d pipeline ending, ongoing syncs: %d",
3366 st->id, st->num_syncs);
3367 st->num_syncs--;
3368 if (st->num_syncs == 0 && PQexitPipelineMode(st->con) != 1)
3369 pg_log_error("client %d failed to exit pipeline mode: %s", st->id,
3371 break;
3372
3373 case PGRES_COPY_IN:
3374 case PGRES_COPY_OUT:
3375 case PGRES_COPY_BOTH:
3376 pg_log_error("COPY is not supported in pgbench, aborting");
3377
3378 /*
3379 * We need to exit the copy state. Otherwise, PQgetResult()
3380 * will always return an empty PGresult as an effect of
3381 * getCopyResult(), leading to an infinite loop in the error
3382 * cleanup done below.
3383 */
3384 PQendcopy(st->con);
3385 goto error;
3386
3388 case PGRES_FATAL_ERROR:
3392 {
3393 if (verbose_errors)
3395 goto error;
3396 }
3398
3399 default:
3400 /* anything else is unexpected */
3401 pg_log_error("client %d script %d aborted in command %d query %d: %s",
3402 st->id, st->use_file, st->command, qrynum,
3404 goto error;
3405 }
3406
3407 PQclear(res);
3408 qrynum++;
3409 res = next_res;
3410 }
3411
3412 if (qrynum == 0)
3413 {
3414 pg_log_error("client %d command %d: no results", st->id, st->command);
3415 return false;
3416 }
3417
3418 return true;
3419
3420error:
3421 PQclear(res);
3424
3425 return false;
#define PQresultErrorMessage
#define PQnfields
#define PQfname
@ PGRES_COPY_BOTH
Definition libpq-fe.h:143
@ PGRES_FATAL_ERROR
Definition libpq-fe.h:142
@ PGRES_COPY_OUT
Definition libpq-fe.h:137
@ PGRES_EMPTY_QUERY
Definition libpq-fe.h:130
@ PGRES_NONFATAL_ERROR
Definition libpq-fe.h:141
static void commandError(CState *st, const char *message)
Definition pgbench.c:3027
static EStatus getSQLErrorStatus(CState *st, const char *sqlState)
Definition pgbench.c:3232
static void error(void)

References Assert, canContinueOnError(), canRetryError(), CState::command, commandError(), CState::con, discardAvailableResults(), error(), CState::estatus, ESTATUS_META_COMMAND_ERROR, fb(), getSQLErrorStatus(), CState::id, META_ASET, META_ENDPIPELINE, META_GSET, META_NONE, CState::num_syncs, PG_DIAG_SQLSTATE, pg_fallthrough, pg_free(), pg_log_debug, pg_log_error, PGRES_COMMAND_OK, PGRES_COPY_BOTH, PGRES_COPY_IN, PGRES_COPY_OUT, PGRES_EMPTY_QUERY, PGRES_FATAL_ERROR, PGRES_NONFATAL_ERROR, PGRES_PIPELINE_SYNC, PGRES_TUPLES_OK, PQclear, PQendcopy(), PQexitPipelineMode(), PQfname, PQgetResult, PQgetvalue, PQnfields, PQntuples, PQresultErrorField, PQresultErrorMessage, PQresultStatus, psprintf(), putVariable(), CState::use_file, CState::variables, and verbose_errors.

Referenced by advanceConnectionState().

◆ replaceVariable()

static char * replaceVariable ( char **  sql,
char param,
int  len,
char value 
)
static

Definition at line 1910 of file pgbench.c.

1912{
1913 int valueln = strlen(value);
1914
1915 if (valueln > len)
1916 {
1917 size_t offset = param - *sql;
1918
1919 *sql = pg_realloc(*sql, strlen(*sql) - len + valueln + 1);
1920 param = *sql + offset;
1921 }
1922
1923 if (valueln != len)
1924 memmove(param + valueln, param + len, strlen(param + len) + 1);
1925 memcpy(param, value, valueln);
1926
1927 return param + valueln;

References fb(), len, memcpy(), pg_realloc(), and value.

Referenced by assignVariables(), and parseQuery().

◆ runInitSteps()

static void runInitSteps ( const char initialize_steps)
static

Definition at line 5325 of file pgbench.c.

5327{
5328 PQExpBufferData stats;
5329 PGconn *con;
5330 const char *step;
5331 double run_time = 0.0;
5332 bool first = true;
5333
5334 initPQExpBuffer(&stats);
5335
5336 if ((con = doConnect()) == NULL)
5337 pg_fatal("could not create connection for initialization");
5338
5340 SetCancelConn(con);
5341
5342 for (step = initialize_steps; *step != '\0'; step++)
5343 {
5344 char *op = NULL;
5346
5347 switch (*step)
5348 {
5349 case 'd':
5350 op = "drop tables";
5351 initDropTables(con);
5352 break;
5353 case 't':
5354 op = "create tables";
5355 initCreateTables(con);
5356 break;
5357 case 'g':
5358 op = "client-side generate";
5360 break;
5361 case 'G':
5362 op = "server-side generate";
5364 break;
5365 case 'v':
5366 op = "vacuum";
5367 initVacuum(con);
5368 break;
5369 case 'p':
5370 op = "primary keys";
5371 initCreatePKeys(con);
5372 break;
5373 case 'f':
5374 op = "foreign keys";
5375 initCreateFKeys(con);
5376 break;
5377 case ' ':
5378 break; /* ignore */
5379 default:
5380 pg_log_error("unrecognized initialization step \"%c\"", *step);
5381 PQfinish(con);
5382 exit(1);
5383 }
5384
5385 if (op != NULL)
5386 {
5388
5389 if (!first)
5390 appendPQExpBufferStr(&stats, ", ");
5391 else
5392 first = false;
5393
5394 appendPQExpBuffer(&stats, "%s %.2f s", op, elapsed_sec);
5395
5397 }
5398 }
5399
5400 fprintf(stderr, "done in %.2f s (%s).\n", run_time, stats.data);
5402 PQfinish(con);
5403 termPQExpBuffer(&stats);
void ResetCancelConn(void)
Definition cancel.c:107
void SetCancelConn(PGconn *conn)
Definition cancel.c:77
void setup_cancel_handler(void(*query_cancel_callback)(void))
Definition cancel.c:183
static void initCreatePKeys(PGconn *con)
Definition pgbench.c:5241
static void initDropTables(PGconn *con)
Definition pgbench.c:4803
static void initGenerateDataServerSide(PGconn *con)
Definition pgbench.c:5183
static void initCreateFKeys(PGconn *con)
Definition pgbench.c:5279
static void initVacuum(PGconn *con)
Definition pgbench.c:5228
static void initCreateTables(PGconn *con)
Definition pgbench.c:4894
static void initGenerateDataClientSide(PGconn *con)
Definition pgbench.c:5151

References appendPQExpBuffer(), appendPQExpBufferStr(), PQExpBufferData::data, doConnect(), fb(), fprintf, initCreateFKeys(), initCreatePKeys(), initCreateTables(), initDropTables(), initGenerateDataClientSide(), initGenerateDataServerSide(), initPQExpBuffer(), initVacuum(), pg_fatal, pg_log_error, PG_TIME_GET_DOUBLE, pg_time_now(), PQfinish(), ResetCancelConn(), SetCancelConn(), setup_cancel_handler(), start, and termPQExpBuffer().

Referenced by main().

◆ runShellCommand()

static bool runShellCommand ( Variables variables,
char variable,
char **  argv,
int  argc 
)
static

Definition at line 2911 of file pgbench.c.

2913{
2914 char command[SHELL_COMMAND_SIZE];
2915 int i,
2916 len = 0;
2917 FILE *fp;
2918 char res[64];
2919 char *endptr;
2920 int retval;
2921
2922 /*----------
2923 * Join arguments with whitespace separators. Arguments starting with
2924 * exactly one colon are treated as variables:
2925 * name - append a string "name"
2926 * :var - append a variable named 'var'
2927 * ::name - append a string ":name"
2928 *----------
2929 */
2930 for (i = 0; i < argc; i++)
2931 {
2932 char *arg;
2933 int arglen;
2934
2935 if (argv[i][0] != ':')
2936 {
2937 arg = argv[i]; /* a string literal */
2938 }
2939 else if (argv[i][1] == ':')
2940 {
2941 arg = argv[i] + 1; /* a string literal starting with colons */
2942 }
2943 else if ((arg = getVariable(variables, argv[i] + 1)) == NULL)
2944 {
2945 pg_log_error("%s: undefined variable \"%s\"", argv[0], argv[i]);
2946 return false;
2947 }
2948
2949 arglen = strlen(arg);
2950 if (len + arglen + (i > 0 ? 1 : 0) >= SHELL_COMMAND_SIZE - 1)
2951 {
2952 pg_log_error("%s: shell command is too long", argv[0]);
2953 return false;
2954 }
2955
2956 if (i > 0)
2957 command[len++] = ' ';
2958 memcpy(command + len, arg, arglen);
2959 len += arglen;
2960 }
2961
2962 command[len] = '\0';
2963
2964 fflush(NULL); /* needed before either system() or popen() */
2965
2966 /* Fast path for non-assignment case */
2967 if (variable == NULL)
2968 {
2969 if (system(command))
2970 {
2971 if (!timer_exceeded)
2972 pg_log_error("%s: could not launch shell command", argv[0]);
2973 return false;
2974 }
2975 return true;
2976 }
2977
2978 /* Execute the command with pipe and read the standard output. */
2979 if ((fp = popen(command, "r")) == NULL)
2980 {
2981 pg_log_error("%s: could not launch shell command", argv[0]);
2982 return false;
2983 }
2984 if (fgets(res, sizeof(res), fp) == NULL)
2985 {
2986 if (!timer_exceeded)
2987 pg_log_error("%s: could not read result of shell command", argv[0]);
2988 (void) pclose(fp);
2989 return false;
2990 }
2991 if (pclose(fp) < 0)
2992 {
2993 pg_log_error("%s: could not run shell command: %m", argv[0]);
2994 return false;
2995 }
2996
2997 /* Check whether the result is an integer and assign it to the variable */
2998 retval = (int) strtol(res, &endptr, 10);
2999 while (*endptr != '\0' && isspace((unsigned char) *endptr))
3000 endptr++;
3001 if (*res == '\0' || *endptr != '\0')
3002 {
3003 pg_log_error("%s: shell command must return an integer (not \"%s\")", argv[0], res);
3004 return false;
3005 }
3006 if (!putVariableInt(variables, "setshell", variable, retval))
3007 return false;
3008
3009 pg_log_debug("%s: shell parameter name: \"%s\", value: \"%s\"", argv[0], argv[1], res);
3010
3011 return true;
Datum arg
Definition elog.c:1323
#define SHELL_COMMAND_SIZE
Definition pgbench.c:349

References arg, fb(), getVariable(), i, len, memcpy(), pg_log_debug, pg_log_error, putVariableInt(), SHELL_COMMAND_SIZE, and timer_exceeded.

Referenced by executeMetaCommand().

◆ sendCommand()

static bool sendCommand ( CState st,
Command command 
)
static

Definition at line 3151 of file pgbench.c.

3153{
3154 int r;
3155
3156 if (querymode == QUERY_SIMPLE)
3157 {
3158 char *sql;
3159
3160 sql = pg_strdup(command->argv[0]);
3161 sql = assignVariables(&st->variables, sql);
3162
3163 pg_log_debug("client %d sending %s", st->id, sql);
3164 r = PQsendQuery(st->con, sql);
3165 free(sql);
3166 }
3167 else if (querymode == QUERY_EXTENDED)
3168 {
3169 const char *sql = command->argv[0];
3170 const char *params[MAX_ARGS];
3171
3172 getQueryParams(&st->variables, command, params);
3173
3174 pg_log_debug("client %d sending %s", st->id, sql);
3175 r = PQsendQueryParams(st->con, sql, command->argc - 1,
3176 NULL, params, NULL, NULL, 0);
3177 }
3178 else if (querymode == QUERY_PREPARED)
3179 {
3180 const char *params[MAX_ARGS];
3181
3182 prepareCommand(st, st->command);
3183 getQueryParams(&st->variables, command, params);
3184
3185 pg_log_debug("client %d sending %s", st->id, command->prepname);
3186 r = PQsendQueryPrepared(st->con, command->prepname, command->argc - 1,
3187 params, NULL, NULL, 0);
3188 }
3189 else /* unknown sql mode */
3190 r = 0;
3191
3192 if (r == 0)
3193 {
3194 pg_log_debug("client %d could not send %s", st->id, command->argv[0]);
3195 return false;
3196 }
3197 else
3198 return true;
int PQsendQueryParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
Definition fe-exec.c:1509
int PQsendQueryPrepared(PGconn *conn, const char *stmtName, int nParams, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
Definition fe-exec.c:1650
static void getQueryParams(Variables *variables, const Command *command, const char **params)
Definition pgbench.c:1966
static char * assignVariables(Variables *variables, char *sql)
Definition pgbench.c:1930

References Command::argc, Command::argv, assignVariables(), CState::command, CState::con, fb(), free, getQueryParams(), CState::id, MAX_ARGS, pg_log_debug, pg_strdup(), PQsendQuery(), PQsendQueryParams(), PQsendQueryPrepared(), prepareCommand(), Command::prepname, QUERY_EXTENDED, QUERY_PREPARED, QUERY_SIMPLE, querymode, and CState::variables.

Referenced by advanceConnectionState().

◆ set_random_seed()

static bool set_random_seed ( const char seed)
static

Definition at line 6688 of file pgbench.c.

6690{
6691 uint64 iseed;
6692
6693 if (seed == NULL || strcmp(seed, "time") == 0)
6694 {
6695 /* rely on current time */
6696 iseed = pg_time_now();
6697 }
6698 else if (strcmp(seed, "rand") == 0)
6699 {
6700 /* use some "strong" random source */
6701 if (!pg_strong_random(&iseed, sizeof(iseed)))
6702 {
6703 pg_log_error("could not generate random seed");
6704 return false;
6705 }
6706 }
6707 else
6708 {
6709 char garbage;
6710
6711 if (sscanf(seed, "%" SCNu64 "%c", &iseed, &garbage) != 1)
6712 {
6713 pg_log_error("unrecognized random seed option \"%s\"", seed);
6714 pg_log_error_detail("Expecting an unsigned integer, \"time\" or \"rand\".");
6715 return false;
6716 }
6717 }
6718
6719 if (seed != NULL)
6720 pg_log_info("setting random seed to %" PRIu64, iseed);
6721
6723
6724 /* Initialize base_random_sequence using seed */
6726
6727 return true;
bool pg_strong_random(void *buf, size_t len)

References base_random_sequence, fb(), pg_log_error, pg_log_error_detail, pg_log_info, pg_prng_seed(), pg_strong_random(), pg_time_now(), and random_seed.

Referenced by main().

◆ setalarm()

static void setalarm ( int  seconds)
static

Definition at line 7852 of file pgbench.c.

7854{
7856 alarm(seconds);
static void handle_sig_alarm(SIGNAL_ARGS)
Definition pgbench.c:7846
#define pqsignal
Definition port.h:548
#define SIGALRM
Definition win32_port.h:164

References fb(), handle_sig_alarm(), pqsignal, and SIGALRM.

Referenced by main().

◆ setBoolValue()

static void setBoolValue ( PgBenchValue pv,
bool  bval 
)
static

Definition at line 2096 of file pgbench.c.

2098{
2099 pv->type = PGBT_BOOLEAN;
2100 pv->u.bval = bval;

References fb(), and PGBT_BOOLEAN.

Referenced by evalLazyFunc(), evalStandardFunc(), and makeVariableValue().

◆ setDoubleValue()

static void setDoubleValue ( PgBenchValue pv,
double  dval 
)
static

Definition at line 2112 of file pgbench.c.

2114{
2115 pv->type = PGBT_DOUBLE;
2116 pv->u.dval = dval;

References fb(), and PGBT_DOUBLE.

Referenced by evalStandardFunc(), and makeVariableValue().

◆ setIntValue()

static void setIntValue ( PgBenchValue pv,
int64  ival 
)
static

Definition at line 2104 of file pgbench.c.

2106{
2107 pv->type = PGBT_INT;
2108 pv->u.ival = ival;

References fb(), and PGBT_INT.

Referenced by evalStandardFunc(), makeVariableValue(), and putVariableInt().

◆ setNullValue()

static void setNullValue ( PgBenchValue pv)
static

Definition at line 2088 of file pgbench.c.

2090{
2091 pv->type = PGBT_NULL;
2092 pv->u.ival = 0;

References fb(), and PGBT_NULL.

Referenced by evalLazyFunc(), evalStandardFunc(), and makeVariableValue().

◆ skip_sql_comments()

static char * skip_sql_comments ( char sql_command)
static

Definition at line 5616 of file pgbench.c.

5618{
5619 char *p = sql_command;
5620
5621 /* Skip any leading whitespace, as well as "--" style comments */
5622 for (;;)
5623 {
5624 if (isspace((unsigned char) *p))
5625 p++;
5626 else if (strncmp(p, "--", 2) == 0)
5627 {
5628 p = strchr(p, '\n');
5629 if (p == NULL)
5630 return NULL;
5631 p++;
5632 }
5633 else
5634 break;
5635 }
5636
5637 /* NULL if there's nothing but whitespace and comments */
5638 if (*p == '\0')
5639 return NULL;
5640
5641 return p;

References fb().

Referenced by create_sql_command().

◆ socket_has_input()

static bool socket_has_input ( socket_set sa,
int  fd,
int  idx 
)
static

Definition at line 8056 of file pgbench.c.

8058{
8059 return (FD_ISSET(fd, &sa->fds) != 0);

References fb(), and fd().

Referenced by threadRun().

◆ strtodouble()

bool strtodouble ( const char str,
bool  errorOK,
double dv 
)

Definition at line 1049 of file pgbench.c.

1051{
1052 char *end;
1053
1054 errno = 0;
1055 *dv = strtod(str, &end);
1056
1057 if (unlikely(errno == ERANGE))
1058 {
1059 if (!errorOK)
1060 pg_log_error("value \"%s\" is out of range for type double", str);
1061 return false;
1062 }
1063
1064 if (unlikely(errno != 0 || end == str || *end != '\0'))
1065 {
1066 if (!errorOK)
1067 pg_log_error("invalid input syntax for type double: \"%s\"", str);
1068 return false;
1069 }
1070 return true;

References fb(), pg_log_error, str, and unlikely.

Referenced by makeVariableValue().

◆ strtoint64()

bool strtoint64 ( const char str,
bool  errorOK,
int64 result 
)

Definition at line 1024 of file pgbench.c.

1026{
1027 char *end;
1028
1029 errno = 0;
1030 *result = strtoi64(str, &end, 10);
1031
1032 if (unlikely(errno == ERANGE))
1033 {
1034 if (!errorOK)
1035 pg_log_error("value \"%s\" is out of range for type bigint", str);
1036 return false;
1037 }
1038
1039 if (unlikely(errno != 0 || end == str || *end != '\0'))
1040 {
1041 if (!errorOK)
1042 pg_log_error("invalid input syntax for type bigint: \"%s\"", str);
1043 return false;
1044 }
1045 return true;

References fb(), pg_log_error, result, str, and unlikely.

Referenced by makeVariableValue().

◆ syntax_error()

void syntax_error ( const char source,
int  lineno,
const char line,
const char command,
const char msg,
const char more,
int  column 
)

Definition at line 5580 of file pgbench.c.

5584{
5586
5588
5589 printfPQExpBuffer(&buf, "%s:%d: %s", source, lineno, msg);
5590 if (more != NULL)
5591 appendPQExpBuffer(&buf, " (%s)", more);
5592 if (column >= 0 && line == NULL)
5593 appendPQExpBuffer(&buf, " at column %d", column + 1);
5594 if (command != NULL)
5595 appendPQExpBuffer(&buf, " in command \"%s\"", command);
5596
5597 pg_log_error("%s", buf.data);
5598
5600
5601 if (line != NULL)
5602 {
5603 fprintf(stderr, "%s\n", line);
5604 if (column >= 0)
5605 fprintf(stderr, "%*c error found here\n", column + 1, '^');
5606 }
5607
5608 exit(1);

References appendPQExpBuffer(), buf, fb(), fprintf, initPQExpBuffer(), pg_log_error, printfPQExpBuffer(), source, and termPQExpBuffer().

Referenced by expr_yyerror_more(), ParseScript(), pg_lsn_in_safe(), process_backslash_command(), and string_to_uuid().

◆ threadRun()

static THREAD_FUNC_RETURN_TYPE THREAD_FUNC_CC threadRun ( void arg)
static

Definition at line 7530 of file pgbench.c.

7532{
7533 TState *thread = (TState *) arg;
7534 CState *state = thread->state;
7536 int nstate = thread->nstate;
7537 int remains = nstate; /* number of remaining clients */
7542 StatsData last,
7543 aggs;
7544
7545 /* open log file if requested */
7546 if (use_log)
7547 {
7548 char logpath[MAXPGPATH];
7549 char *prefix = logfile_prefix ? logfile_prefix : "pgbench_log";
7550
7551 if (thread->tid == 0)
7552 snprintf(logpath, sizeof(logpath), "%s.%d", prefix, main_pid);
7553 else
7554 snprintf(logpath, sizeof(logpath), "%s.%d.%d", prefix, main_pid, thread->tid);
7555
7556 thread->logfile = fopen(logpath, "w");
7557
7558 if (thread->logfile == NULL)
7559 pg_fatal("could not open logfile \"%s\": %m", logpath);
7560 }
7561
7562 /* explicitly initialize the state machines */
7563 for (int i = 0; i < nstate; i++)
7565
7566 /* READY */
7568
7570 thread->started_time = thread_start;
7571 thread->conn_duration = 0;
7573 next_report = last_report + (int64) 1000000 * progress;
7574
7575 /* STEADY */
7576 if (!is_connect)
7577 {
7578 /* make connections to the database before starting */
7579 for (int i = 0; i < nstate; i++)
7580 {
7581 if ((state[i].con = doConnect()) == NULL)
7582 {
7583 /* coldly abort on initial connection failure */
7584 pg_fatal("could not create connection for client %d",
7585 state[i].id);
7586 }
7587 }
7588 }
7589
7590 /* GO */
7592
7593 start = pg_time_now();
7594 thread->bench_start = start;
7595 thread->throttle_trigger = start;
7596
7597 /*
7598 * The log format currently has Unix epoch timestamps with whole numbers
7599 * of seconds. Round the first aggregate's start time down to the nearest
7600 * Unix epoch second (the very first aggregate might really have started a
7601 * fraction of a second later, but later aggregates are measured from the
7602 * whole number time that is actually logged).
7603 */
7604 initStats(&aggs, (start + epoch_shift) / 1000000 * 1000000);
7605 last = aggs;
7606
7607 /* loop till all clients have terminated */
7608 while (remains > 0)
7609 {
7610 int nsocks; /* number of sockets to be waited for */
7612 pg_time_usec_t now = 0; /* set this only if needed */
7613
7614 /*
7615 * identify which client sockets should be checked for input, and
7616 * compute the nearest time (if any) at which we need to wake up.
7617 */
7619 nsocks = 0;
7621 for (int i = 0; i < nstate; i++)
7622 {
7623 CState *st = &state[i];
7624
7625 if (st->state == CSTATE_SLEEP || st->state == CSTATE_THROTTLE)
7626 {
7627 /* a nap from the script, or under throttling */
7629
7630 /* get current time if needed */
7632
7633 /* min_usec should be the minimum delay across all clients */
7634 this_usec = (st->state == CSTATE_SLEEP ?
7635 st->sleep_until : st->txn_scheduled) - now;
7636 if (min_usec > this_usec)
7638 }
7639 else if (st->state == CSTATE_WAIT_RESULT ||
7641 {
7642 /*
7643 * waiting for result from server - nothing to do unless the
7644 * socket is readable
7645 */
7646 int sock = PQsocket(st->con);
7647
7648 if (sock < 0)
7649 {
7650 pg_log_error("invalid socket: %s", PQerrorMessage(st->con));
7651 goto done;
7652 }
7653
7655 }
7656 else if (st->state != CSTATE_ABORTED &&
7657 st->state != CSTATE_FINISHED)
7658 {
7659 /*
7660 * This client thread is ready to do something, so we don't
7661 * want to wait. No need to examine additional clients.
7662 */
7663 min_usec = 0;
7664 break;
7665 }
7666 }
7667
7668 /* also wake up to print the next progress report on time */
7669 if (progress && min_usec > 0 && thread->tid == 0)
7670 {
7672
7673 if (now >= next_report)
7674 min_usec = 0;
7675 else if ((next_report - now) < min_usec)
7677 }
7678
7679 /*
7680 * If no clients are ready to execute actions, sleep until we receive
7681 * data on some client socket or the timeout (if any) elapses.
7682 */
7683 if (min_usec > 0)
7684 {
7685 int rc = 0;
7686
7687 if (min_usec != PG_INT64_MAX)
7688 {
7689 if (nsocks > 0)
7690 {
7692 }
7693 else /* nothing active, simple sleep */
7694 {
7696 }
7697 }
7698 else /* no explicit delay, wait without timeout */
7699 {
7700 rc = wait_on_socket_set(sockets, 0);
7701 }
7702
7703 if (rc < 0)
7704 {
7705 if (errno == EINTR)
7706 {
7707 /* On EINTR, go back to top of loop */
7708 continue;
7709 }
7710 /* must be something wrong */
7711 pg_log_error("%s() failed: %m", SOCKET_WAIT_METHOD);
7712 goto done;
7713 }
7714 }
7715 else
7716 {
7717 /* min_usec <= 0, i.e. something needs to be executed now */
7718
7719 /* If we didn't wait, don't try to read any data */
7721 }
7722
7723 /* ok, advance the state machine of each connection */
7724 nsocks = 0;
7725 for (int i = 0; i < nstate; i++)
7726 {
7727 CState *st = &state[i];
7728
7729 if (st->state == CSTATE_WAIT_RESULT ||
7731 {
7732 /* don't call advanceConnectionState unless data is available */
7733 int sock = PQsocket(st->con);
7734
7735 if (sock < 0)
7736 {
7737 pg_log_error("invalid socket: %s", PQerrorMessage(st->con));
7738 goto done;
7739 }
7740
7741 if (!socket_has_input(sockets, sock, nsocks++))
7742 continue;
7743 }
7744 else if (st->state == CSTATE_FINISHED ||
7745 st->state == CSTATE_ABORTED)
7746 {
7747 /* this client is done, no need to consider it anymore */
7748 continue;
7749 }
7750
7751 advanceConnectionState(thread, st, &aggs);
7752
7753 /*
7754 * If --exit-on-abort is used, the program is going to exit when
7755 * any client is aborted.
7756 */
7757 if (exit_on_abort && st->state == CSTATE_ABORTED)
7758 goto done;
7759
7760 /*
7761 * If advanceConnectionState changed client to finished state,
7762 * that's one fewer client that remains.
7763 */
7764 else if (st->state == CSTATE_FINISHED ||
7765 st->state == CSTATE_ABORTED)
7766 remains--;
7767 }
7768
7769 /* progress report is made by thread 0 for all threads */
7770 if (progress && thread->tid == 0)
7771 {
7773
7774 if (now2 >= next_report)
7775 {
7776 /*
7777 * Horrible hack: this relies on the thread pointer we are
7778 * passed to be equivalent to threads[0], that is the first
7779 * entry of the threads array. That is why this MUST be done
7780 * by thread 0 and not any other.
7781 */
7783 &last, &last_report);
7784
7785 /*
7786 * Ensure that the next report is in the future, in case
7787 * pgbench/postgres got stuck somewhere.
7788 */
7789 do
7790 {
7791 next_report += (int64) 1000000 * progress;
7792 } while (now2 >= next_report);
7793 }
7794 }
7795 }
7796
7797done:
7798 if (exit_on_abort)
7799 {
7800 /*
7801 * Abort if any client is not finished, meaning some error occurred.
7802 */
7803 for (int i = 0; i < nstate; i++)
7804 {
7805 if (state[i].state != CSTATE_FINISHED)
7806 {
7807 pg_log_error("Run was aborted due to an error in thread %d",
7808 thread->tid);
7809 exit(2);
7810 }
7811 }
7812 }
7813
7814 disconnect_all(state, nstate);
7815
7816 if (thread->logfile)
7817 {
7818 if (agg_interval > 0)
7819 {
7820 /* log aggregated but not yet reported transactions */
7821 doLog(thread, state, &aggs, false, 0, 0);
7822 }
7823 fclose(thread->logfile);
7824 thread->logfile = NULL;
7825 }
#define PG_INT64_MAX
Definition c.h:676
int PQsocket(const PGconn *conn)
#define MAXPGPATH
#define THREAD_BARRIER_WAIT(barrier)
Definition pgbench.c:155
#define SOCKET_WAIT_METHOD
Definition pgbench.c:107
static void free_socket_set(socket_set *sa)
Definition pgbench.c:8001
static void clear_socket_set(socket_set *sa)
Definition pgbench.c:8007
static void add_socket_to_set(socket_set *sa, int fd, int idx)
Definition pgbench.c:8014
#define THREAD_FUNC_RETURN
Definition pgbench.c:146
static void advanceConnectionState(TState *thread, CState *st, StatsData *agg)
Definition pgbench.c:3667
static void printProgressReport(TState *threads, int64 test_start, pg_time_usec_t now, StatsData *last, int64 *last_report)
Definition pgbench.c:6311
static bool socket_has_input(socket_set *sa, int fd, int idx)
Definition pgbench.c:8056
static int wait_on_socket_set(socket_set *sa, int64 usecs)
Definition pgbench.c:8039
static socket_set * alloc_socket_set(int count)
Definition pgbench.c:7995
void pg_usleep(long microsec)
Definition signal.c:53
pg_time_usec_t started_time
Definition pgbench.c:674
#define EINTR
Definition win32_port.h:361

References add_socket_to_set(), advanceConnectionState(), agg_interval, alloc_socket_set(), arg, barrier, TState::bench_start, clear_socket_set(), CState::con, TState::conn_duration, CSTATE_ABORTED, CSTATE_CHOOSE_SCRIPT, CSTATE_FINISHED, CSTATE_SLEEP, CSTATE_THROTTLE, CSTATE_WAIT_RESULT, CSTATE_WAIT_ROLLBACK_RESULT, disconnect_all(), doConnect(), doLog(), EINTR, epoch_shift, exit_on_abort, fb(), free_socket_set(), i, initStats(), is_connect, TState::logfile, logfile_prefix, main_pid, MAXPGPATH, now(), TState::nstate, pg_fatal, PG_INT64_MAX, pg_log_error, pg_time_now(), pg_time_now_lazy(), pg_usleep(), PQerrorMessage(), PQsocket(), printProgressReport(), progress, CState::sleep_until, snprintf, socket_has_input(), SOCKET_WAIT_METHOD, start, TState::started_time, CState::state, TState::state, THREAD_BARRIER_WAIT, THREAD_FUNC_RETURN, TState::throttle_trigger, TState::tid, CState::txn_scheduled, use_log, and wait_on_socket_set().

Referenced by main().

◆ tryExecuteStatement()

static void tryExecuteStatement ( PGconn con,
const char sql 
)
static

Definition at line 1510 of file pgbench.c.

1512{
1513 PGresult *res;
1514
1515 res = PQexec(con, sql);
1516 if (PQresultStatus(res) != PGRES_COMMAND_OK)
1517 {
1518 pg_log_error("%s", PQerrorMessage(con));
1519 pg_log_error_detail("(ignoring this error and continuing anyway)");
1520 }
1521 PQclear(res);

References pg_log_error, pg_log_error_detail, PGRES_COMMAND_OK, PQclear, PQerrorMessage(), PQexec(), and PQresultStatus.

Referenced by main().

◆ usage()

static void usage ( void  )
static

Definition at line 904 of file pgbench.c.

906{
907 printf("%s is a benchmarking tool for PostgreSQL.\n\n"
908 "Usage:\n"
909 " %s [OPTION]... [DBNAME]\n"
910 "\nInitialization options:\n"
911 " -i, --initialize invokes initialization mode\n"
912 " -I, --init-steps=[" ALL_INIT_STEPS "]+ (default \"" DEFAULT_INIT_STEPS "\")\n"
913 " run selected initialization steps, in the specified order\n"
914 " d: drop any existing pgbench tables\n"
915 " t: create the tables used by the standard pgbench scenario\n"
916 " g: generate data, client-side\n"
917 " G: generate data, server-side\n"
918 " v: invoke VACUUM on the standard tables\n"
919 " p: create primary key indexes on the standard tables\n"
920 " f: create foreign keys between the standard tables\n"
921 " -F, --fillfactor=NUM set fill factor\n"
922 " -n, --no-vacuum do not run VACUUM during initialization\n"
923 " -q, --quiet quiet logging (one message each 5 seconds)\n"
924 " -s, --scale=NUM scaling factor\n"
925 " --foreign-keys create foreign key constraints between tables\n"
926 " --index-tablespace=TABLESPACE\n"
927 " create indexes in the specified tablespace\n"
928 " --partition-method=(range|hash)\n"
929 " partition pgbench_accounts with this method (default: range)\n"
930 " --partitions=NUM partition pgbench_accounts into NUM parts (default: 0)\n"
931 " --tablespace=TABLESPACE create tables in the specified tablespace\n"
932 " --unlogged-tables create tables as unlogged tables\n"
933 "\nOptions to select what to run:\n"
934 " -b, --builtin=NAME[@W] add builtin script NAME weighted at W (default: 1)\n"
935 " (use \"-b list\" to list available scripts)\n"
936 " -f, --file=FILENAME[@W] add script FILENAME weighted at W (default: 1)\n"
937 " -N, --skip-some-updates skip updates of pgbench_tellers and pgbench_branches\n"
938 " (same as \"-b simple-update\")\n"
939 " -S, --select-only perform SELECT-only transactions\n"
940 " (same as \"-b select-only\")\n"
941 "\nBenchmarking options:\n"
942 " -c, --client=NUM number of concurrent database clients (default: 1)\n"
943 " -C, --connect establish new connection for each transaction\n"
944 " -D, --define=VARNAME=VALUE\n"
945 " define variable for use by custom script\n"
946 " -j, --jobs=NUM number of threads (default: 1)\n"
947 " -l, --log write transaction times to log file\n"
948 " -L, --latency-limit=NUM count transactions lasting more than NUM ms as late\n"
949 " -M, --protocol=simple|extended|prepared\n"
950 " protocol for submitting queries (default: simple)\n"
951 " -n, --no-vacuum do not run VACUUM before tests\n"
952 " -P, --progress=NUM show thread progress report every NUM seconds\n"
953 " -r, --report-per-command report latencies, failures, and retries per command\n"
954 " -R, --rate=NUM target rate in transactions per second\n"
955 " -s, --scale=NUM report this scale factor in output\n"
956 " -t, --transactions=NUM number of transactions each client runs (default: 10)\n"
957 " -T, --time=NUM duration of benchmark test in seconds\n"
958 " -v, --vacuum-all vacuum all four standard tables before tests\n"
959 " --aggregate-interval=NUM aggregate data over NUM seconds\n"
960 " --continue-on-error continue running after an SQL error\n"
961 " --exit-on-abort exit when any client is aborted\n"
962 " --failures-detailed report the failures grouped by basic types\n"
963 " --log-prefix=PREFIX prefix for transaction time log file\n"
964 " (default: \"pgbench_log\")\n"
965 " --max-tries=NUM max number of tries to run transaction (default: 1)\n"
966 " --progress-timestamp use Unix epoch timestamps for progress\n"
967 " --random-seed=SEED set random seed (\"time\", \"rand\", integer)\n"
968 " --sampling-rate=NUM fraction of transactions to log (e.g., 0.01 for 1%%)\n"
969 " --show-script=NAME show builtin script code, then exit\n"
970 " --verbose-errors print messages of all errors\n"
971 "\nCommon options:\n"
972 " --debug print debugging output\n"
973 " -d, --dbname=DBNAME database name to connect to\n"
974 " -h, --host=HOSTNAME database server host or socket directory\n"
975 " -p, --port=PORT database server port number\n"
976 " -U, --username=USERNAME connect as specified database user\n"
977 " -V, --version output version information, then exit\n"
978 " -?, --help show this help, then exit\n"
979 "\n"
980 "Report bugs to <%s>.\n"
981 "%s home page: <%s>\n",

References ALL_INIT_STEPS, DEFAULT_INIT_STEPS, fb(), printf, and progname.

Referenced by main().

◆ valid_variable_name()

static bool valid_variable_name ( const char name)
static

Definition at line 1732 of file pgbench.c.

1734{
1735 const unsigned char *ptr = (const unsigned char *) name;
1736
1737 /* Mustn't be zero-length */
1738 if (*ptr == '\0')
1739 return false;
1740
1741 /* must not start with [0-9] */
1742 if (IS_HIGHBIT_SET(*ptr) ||
1743 strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
1744 "_", *ptr) != NULL)
1745 ptr++;
1746 else
1747 return false;
1748
1749 /* remaining characters can include [0-9] */
1750 while (*ptr)
1751 {
1752 if (IS_HIGHBIT_SET(*ptr) ||
1753 strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
1754 "_0123456789", *ptr) != NULL)
1755 ptr++;
1756 else
1757 return false;
1758 }
1759
1760 return true;

References fb(), IS_HIGHBIT_SET, and name.

Referenced by lookupCreateVariable().

◆ valueTruth()

static bool valueTruth ( PgBenchValue pval)
static

Definition at line 2018 of file pgbench.c.

2020{
2021 switch (pval->type)
2022 {
2023 case PGBT_NULL:
2024 return false;
2025 case PGBT_BOOLEAN:
2026 return pval->u.bval;
2027 case PGBT_INT:
2028 return pval->u.ival != 0;
2029 case PGBT_DOUBLE:
2030 return pval->u.dval != 0.0;
2031 default:
2032 /* internal error, unexpected type */
2033 Assert(0);
2034 return false;
2035 }

References Assert, fb(), PGBT_BOOLEAN, PGBT_DOUBLE, PGBT_INT, and PGBT_NULL.

Referenced by evalLazyFunc(), and executeMetaCommand().

◆ valueTypeName()

static char * valueTypeName ( PgBenchValue pval)
static

Definition at line 1976 of file pgbench.c.

1978{
1979 if (pval->type == PGBT_NO_VALUE)
1980 return "none";
1981 else if (pval->type == PGBT_NULL)
1982 return "null";
1983 else if (pval->type == PGBT_INT)
1984 return "int";
1985 else if (pval->type == PGBT_DOUBLE)
1986 return "double";
1987 else if (pval->type == PGBT_BOOLEAN)
1988 return "boolean";
1989 else
1990 {
1991 /* internal error, should never get there */
1992 Assert(false);
1993 return NULL;
1994 }

References Assert, fb(), PGBT_BOOLEAN, PGBT_DOUBLE, PGBT_INT, PGBT_NO_VALUE, and PGBT_NULL.

Referenced by coerceToBool(), coerceToDouble(), and coerceToInt().

◆ wait_on_socket_set()

static int wait_on_socket_set ( socket_set sa,
int64  usecs 
)
static

Definition at line 8039 of file pgbench.c.

8041{
8042 if (usecs > 0)
8043 {
8044 struct timeval timeout;
8045
8046 timeout.tv_sec = usecs / 1000000;
8047 timeout.tv_usec = usecs % 1000000;
8048 return select(sa->maxfd + 1, &sa->fds, NULL, NULL, &timeout);
8049 }
8050 else
8051 {
8052 return select(sa->maxfd + 1, &sa->fds, NULL, NULL, NULL);
8053 }
#define select(n, r, w, e, timeout)
Definition win32_port.h:500

References fb(), and select.

Referenced by threadRun().

Variable Documentation

◆ agg_interval

int agg_interval
static

Definition at line 259 of file pgbench.c.

Referenced by doLog(), main(), and threadRun().

◆ barrier

◆ base_random_sequence

pg_prng_state base_random_sequence
static

Definition at line 485 of file pgbench.c.

Referenced by initRandomState(), main(), and set_random_seed().

◆ builtin_script

const BuiltinScript builtin_script[]
static

Definition at line 789 of file pgbench.c.

791{
792 {
793 "tpcb-like",
794 "<builtin: TPC-B (sort of)>",
795 "\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
796 "\\set bid random(1, " CppAsString2(nbranches) " * :scale)\n"
797 "\\set tid random(1, " CppAsString2(ntellers) " * :scale)\n"
798 "\\set delta random(-5000, 5000)\n"
799 "BEGIN;\n"
800 "UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
801 "SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
802 "UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
803 "UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
804 "INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
805 "END;\n"
806 },
807 {
808 "simple-update",
809 "<builtin: simple update>",
810 "\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
811 "\\set bid random(1, " CppAsString2(nbranches) " * :scale)\n"
812 "\\set tid random(1, " CppAsString2(ntellers) " * :scale)\n"
813 "\\set delta random(-5000, 5000)\n"
814 "BEGIN;\n"
815 "UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
816 "SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
817 "INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
818 "END;\n"
819 },
820 {
821 "select-only",
822 "<builtin: select only>",
823 "\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
824 "SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
825 }
#define CppAsString2(x)
Definition c.h:506

Referenced by findBuiltin(), and listAvailableScripts().

◆ continue_on_error

bool continue_on_error = false
static

Definition at line 779 of file pgbench.c.

Referenced by canContinueOnError(), and main().

◆ dbName

const char* dbName = NULL
static

Definition at line 298 of file pgbench.c.

Referenced by doConnect(), main(), pg_database_size_name(), and PQsetdbLogin().

◆ duration

int duration = 0
static

◆ end_time

◆ epoch_shift

pg_time_usec_t epoch_shift
static

Definition at line 456 of file pgbench.c.

Referenced by doLog(), main(), printProgressReport(), and threadRun().

◆ exit_on_abort

bool exit_on_abort = false
static

Definition at line 778 of file pgbench.c.

Referenced by main(), and threadRun().

◆ failures_detailed

bool failures_detailed = false
static

Definition at line 292 of file pgbench.c.

Referenced by doLog(), getResultString(), main(), and printResults().

◆ fillfactor

◆ index_tablespace

char* index_tablespace = NULL
static

Definition at line 218 of file pgbench.c.

Referenced by initCreatePKeys(), and main().

◆ is_connect

bool is_connect
static

Definition at line 266 of file pgbench.c.

Referenced by advanceConnectionState(), main(), printResults(), and threadRun().

◆ latency_limit

◆ logfile_prefix

char* logfile_prefix = NULL
static

Definition at line 299 of file pgbench.c.

Referenced by main(), and threadRun().

◆ main_pid

int main_pid
static

Definition at line 270 of file pgbench.c.

Referenced by main(), and threadRun().

◆ max_tries

uint32 max_tries = 1
static

◆ nclients

int nclients = 1
static

Definition at line 264 of file pgbench.c.

Referenced by main(), and printResults().

◆ nthreads

int nthreads = 1
static

Definition at line 265 of file pgbench.c.

Referenced by main(), printProgressReport(), and printResults().

◆ num_scripts

int num_scripts
static

Definition at line 773 of file pgbench.c.

Referenced by addScript(), allocCStatePrepared(), chooseScript(), main(), and printResults().

◆ nxacts

int nxacts = 0
static

Definition at line 174 of file pgbench.c.

Referenced by advanceConnectionState(), main(), and printResults().

◆ partition_method

partition_method_t partition_method = PART_NONE
static

Definition at line 234 of file pgbench.c.

Referenced by createPartitions(), GetTableInfo(), initCreateTables(), main(), and printResults().

◆ PARTITION_METHOD

const char* const PARTITION_METHOD[] = {"none", "range", "hash"}
static

Definition at line 235 of file pgbench.c.

235{"none", "range", "hash"};

Referenced by initCreateTables(), and printResults().

◆ partitions

◆ per_script_stats

bool per_script_stats = false
static

Definition at line 261 of file pgbench.c.

Referenced by main(), printResults(), and processXactStats().

◆ pgbench_callbacks

const PsqlScanCallbacks pgbench_callbacks
static
Initial value:
= {
NULL,
}

Definition at line 855 of file pgbench.c.

856 {
857 NULL, /* don't need get_variable functionality */

Referenced by ParseScript().

◆ pghost

const char* pghost = NULL
static

Definition at line 295 of file pgbench.c.

Referenced by ConnectDatabase(), doConnect(), initialize_environment(), main(), main(), and PQsetdbLogin().

◆ pgport

const char* pgport = NULL
static

Definition at line 296 of file pgbench.c.

Referenced by ConnectDatabase(), doConnect(), initialize_environment(), main(), main(), and PQsetdbLogin().

◆ progname

const char* progname
static

Definition at line 300 of file pgbench.c.

Referenced by doConnect(), main(), and usage().

◆ progress

◆ progress_timestamp

bool progress_timestamp = false
static

Definition at line 263 of file pgbench.c.

Referenced by main(), and printProgressReport().

◆ querymode

QueryMode querymode = QUERY_SIMPLE
static

◆ QUERYMODE

const char* const QUERYMODE[] = {"simple", "extended", "prepared"}
static

Definition at line 722 of file pgbench.c.

Referenced by main(), and printResults().

◆ random_seed

int64 random_seed = -1
static

Definition at line 238 of file pgbench.c.

Referenced by main(), and set_random_seed().

◆ report_per_command

bool report_per_command = false
static

Definition at line 267 of file pgbench.c.

Referenced by advanceConnectionState(), main(), and printResults().

◆ sample_rate

double sample_rate = 0.0
static

Definition at line 198 of file pgbench.c.

Referenced by doLog(), and main().

◆ scale

◆ sql_script

◆ tablespace

◆ throttle_delay

double throttle_delay = 0
static

◆ timer_exceeded

volatile sig_atomic_t timer_exceeded = false
static

Definition at line 304 of file pgbench.c.

Referenced by advanceConnectionState(), doRetry(), handle_sig_alarm(), and runShellCommand().

◆ total_weight

int64 total_weight = 0
static

Definition at line 774 of file pgbench.c.

Referenced by chooseScript(), main(), and printResults().

◆ unlogged_tables

bool unlogged_tables = false
static

Definition at line 193 of file pgbench.c.

Referenced by createPartitions(), initCreateTables(), and main().

◆ use_log

bool use_log
static

Definition at line 257 of file pgbench.c.

Referenced by doLog(), main(), processXactStats(), and threadRun().

◆ use_quiet

bool use_quiet
static

Definition at line 258 of file pgbench.c.

Referenced by initPopulateTable(), and main().

◆ username

const char* username = NULL
static

Definition at line 297 of file pgbench.c.

Referenced by doConnect(), and main().

◆ verbose_errors

bool verbose_errors = false
static

Definition at line 776 of file pgbench.c.

Referenced by advanceConnectionState(), main(), and readCommandResponse().