/* For testing, PGBENCH_USE_SELECT can be defined to force use of that code */ #ifdefined(HAVE_PPOLL) && !defined(PGBENCH_USE_SELECT) #define POLL_USING_PPOLL #ifdef HAVE_POLL_H #include <poll.h> #endif #else/* no ppoll(), so use select() */ #define POLL_USING_SELECT #include <sys/select.h> #endif
typedefstruct socket_set
{ int maxfds; /* allocated length of pollfds[] array */ int curfds; /* number currently in use */ struct pollfd pollfds[FLEXIBLE_ARRAY_MEMBER];
} socket_set;
#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 */
staticint nxacts = 0; /* number of transactions per client */ staticint duration = 0; /* duration in seconds */ static int64 end_time = 0; /* when to stop in micro seconds, under -T */
/* *Variabledefinitions. * *Ifavariableonlyhasastringvalue,"svalue"isthatvalue,andvalueis *"notset".Ifthevalueisknown,"value"containsthevalue(inany *variant). * *Inthiscase"svalue"containsthestringequivalentofthevalue,ifwe've *hadoccasiontocomputethat,orNULLifwehaven't.
*/ typedefstruct
{ char *name; /* variable's name */ char *svalue; /* its value in string form, if known */
PgBenchValue value; /* actual variable's value */
} Variable;
/* *Datastructureforclientvariables.
*/ typedefstruct
{
Variable *vars; /* array of variable definitions */ int nvars; /* number of variables */
/* *Themaximumnumberofvariablesthatwecancurrentlystorein'vars' *withouthavingtoreallocatemorespace.Wemustalwayshavemax_vars *>=nvars.
*/ int max_vars;
bool vars_sorted; /* are variables sorted by name? */
} Variables;
#define MAX_SCRIPTS 128/* max number of SQL scripts allowed */ #define SHELL_COMMAND_SIZE 256/* maximum size allowed for shell command */
/* *Simpledatastructuretokeepstatsaboutsomething. * *XXXprobablythefirstvalueshouldbekeptandusedasanoffsetfor *betternumericalstability...
*/ typedefstruct SimpleStats
{
int64 count; /* how many values were encountered */ double min; /* the minimum seen */ double max; /* the maximum seen */ double sum; /* sum of values */ double sum2; /* sum of squared values */
} SimpleStats;
/* *Datastructuretoholdvariousstatistics:per-threadandper-scriptstats *aremaintainedandmergedtogether.
*/ typedefstruct StatsData
{
pg_time_usec_t start_time; /* interval start time, for aggregates */
/*---------- *Transactionsarecounteddependingontheirexecutionandoutcome. *Firstatransactionmayhavestartedornot:skippedtransactionsoccur *under--rateand--latency-limitwhentheclientistoolatetoexecute *them.Secondly,astartedtransactionmayultimatelysucceedorfail, *possiblyaftersomeretrieswhen--max-triesisnotone.Thus * *thenumberofalltransactions= *'skipped'(itwastoolatetoexecutethem)+ *'cnt'(thenumberofsuccessfultransactions)+ *'failed'(thenumberoffailedtransactions). * *Asuccessfultransactioncanhaveseveralunsuccessfultriesbeforea *successfulrun.Thus * *'cnt'(thenumberofsuccessfultransactions)= *successfullyretriedtransactions(theygotaserializationora *deadlockerror(s),butwere *successfullyretriedfromthevery *beginning)+ *directlysuccessfultransactions(theyweresuccessfullycompletedon *thefirsttry). * *Afailedtransactionisdefinedasunsuccessfullyretriedtransactions. *Itcanbeoneoftwotypes: * *failed(thenumberoffailedtransactions)= *'serialization_failures'(theygotaserializationerrorandwerenot *successfullyretried)+ *'deadlock_failures'(theygotadeadlockerrorandwerenot *successfullyretried). * *Ifthetransactionwasretriedafteraserializationoradeadlock *errorthisdoesnotguaranteethatthisretrywassuccessful.Thus * *'retries'(numberofretries)= *numberofretriesinallretriedtransactions= *numberofretriesin(successfullyretriedtransactions+ *failedtransactions); * *'retried'(numberofallretriedtransactions)= *successfullyretriedtransactions+ *failedtransactions. *----------
*/
int64 cnt; /* number of successful transactions, not
* including 'skipped' */
int64 skipped; /* number of transactions skipped under --rate
* and --latency-limit */
int64 retries; /* number of retries after a serialization or
* a deadlock error in all the transactions */
int64 retried; /* number of all transactions that were *retriedafteraserializationoradeadlock *error(perhapsthelasttrywas
* unsuccessful) */
int64 serialization_failures; /* number of transactions that were *notsuccessfullyretriedaftera
* serialization error */
int64 deadlock_failures; /* number of transactions that were not *successfullyretriedafteradeadlock
* error */
SimpleStats latency;
SimpleStats lag;
} StatsData;
int use_file; /* index in sql_script for this client */ int command; /* command number in script */ int num_syncs; /* number of ongoing sync commands */
/* client variables */
Variables variables;
/* various times about current transaction in microseconds */
pg_time_usec_t txn_scheduled; /* scheduled start time of transaction */
pg_time_usec_t sleep_until; /* scheduled start time of next cmd */
pg_time_usec_t txn_begin; /* used for measuring schedule lag times */
pg_time_usec_t stmt_begin; /* used for measuring statement latencies */
/* whether client prepared each command of each script */ bool **prepared;
/* *Forprocessingfailuresandrepeatingtransactionswithserialization *ordeadlockerrors:
*/
EStatus estatus; /* the error status of the current transaction *execution;thisisESTATUS_NO_ERRORif
* there were no errors */
pg_prng_state random_state; /* random state */
uint32 tries; /* how many times have we already tried the
* current transaction? */
/* per client collected stats */
int64 cnt; /* client transaction count, for -t; skipped *andfailedtransactionsarealsocounted
* here */
} CState;
/* *Threadstate
*/ typedefstruct
{ int tid; /* thread id */
THREAD_T thread; /* thread handle */
CState *state; /* array of CState */ int nstate; /* length of state[] */
/* *Separaterandomnessforeachthread.Eachthreadoptionusesitsown *randomstatetomakeallofthemindependentofeachotherand *thereforedeterministicatthethreadlevel.
*/
pg_prng_state ts_choose_rs; /* random state for selecting a script */
pg_prng_state ts_throttle_rs; /* random state for transaction throttling */
pg_prng_state ts_sample_rs; /* random state for log sampling */
int64 throttle_trigger; /* previous/next throttling (us) */
FILE *logfile; /* where to log, or NULL */
/* per thread collected stats in microseconds */
pg_time_usec_t create_time; /* thread creation time */
pg_time_usec_t started_time; /* thread is running */
pg_time_usec_t bench_start; /* thread is benchmarking */
pg_time_usec_t conn_duration; /* cumulated connection and disconnection
* delays */
StatsData stats;
int64 latency_late; /* count executed but late transactions */
} TState;
typedefstruct ParsedScript
{ constchar *desc; /* script descriptor (eg, file name) */ int weight; /* selection weight */
Command **commands; /* NULL-terminated array of Commands */
StatsData stats; /* total time spent in script */
} ParsedScript;
static ParsedScript sql_script[MAX_SCRIPTS]; /* SQL script files */ staticint num_scripts; /* number of scripts in sql_script[] */ static int64 total_weight = 0;
staticbool verbose_errors = false; /* print verbose messages of all errors */
staticbool exit_on_abort = false; /* exit when any client is aborted */
/* Builtin test scripts */ typedefstruct BuiltinScript
{ constchar *name; /* very short name for -b ... */ constchar *desc; /* short description */ constchar *script; /* actual pgbench script */
} BuiltinScript;
staticconst BuiltinScript builtin_script[] =
{
{ "tpcb-like", "<builtin: TPC-B (sort of)>", "\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n" "\\set bid random(1, " CppAsString2(nbranches) " * :scale)\n" "\\set tid random(1, " CppAsString2(ntellers) " * :scale)\n" "\\set delta random(-5000, 5000)\n" "BEGIN;\n" "UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n" "SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n" "UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n" "UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n" "INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n" "END;\n"
},
{ "simple-update", "<builtin: simple update>", "\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n" "\\set bid random(1, " CppAsString2(nbranches) " * :scale)\n" "\\set tid random(1, " CppAsString2(ntellers) " * :scale)\n" "\\set delta random(-5000, 5000)\n" "BEGIN;\n" "UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n" "SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n" "INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n" "END;\n"
},
{ "select-only", "<builtin: select only>", "\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n" "SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
}
};
staticvoid
usage(void)
{
printf("%s is a benchmarking tool for PostgreSQL.\n\n" "Usage:\n" " %s [OPTION]... [DBNAME]\n" "\nInitialization options:\n" " -i, --initialize invokes initialization mode\n" " -I, --init-steps=[" ALL_INIT_STEPS "]+ (default \"" DEFAULT_INIT_STEPS "\")\n" " run selected initialization steps, in the specified order\n" " d: drop any existing pgbench tables\n" " t: create the tables used by the standard pgbench scenario\n" " g: generate data, client-side\n" " G: generate data, server-side\n" " v: invoke VACUUM on the standard tables\n" " p: create primary key indexes on the standard tables\n" " f: create foreign keys between the standard tables\n" " -F, --fillfactor=NUM set fill factor\n" " -n, --no-vacuum do not run VACUUM during initialization\n" " -q, --quiet quiet logging (one message each 5 seconds)\n" " -s, --scale=NUM scaling factor\n" " --foreign-keys create foreign key constraints between tables\n" " --index-tablespace=TABLESPACE\n" " create indexes in the specified tablespace\n" " --partition-method=(range|hash)\n" " partition pgbench_accounts with this method (default: range)\n" " --partitions=NUM partition pgbench_accounts into NUM parts (default: 0)\n" " --tablespace=TABLESPACE create tables in the specified tablespace\n" " --unlogged-tables create tables as unlogged tables\n" "\nOptions to select what to run:\n" " -b, --builtin=NAME[@W] add builtin script NAME weighted at W (default: 1)\n" " (use \"-b list\" to list available scripts)\n" " -f, --file=FILENAME[@W] add script FILENAME weighted at W (default: 1)\n" " -N, --skip-some-updates skip updates of pgbench_tellers and pgbench_branches\n" " (same as \"-b simple-update\")\n" " -S, --select-only perform SELECT-only transactions\n" " (same as \"-b select-only\")\n" "\nBenchmarking options:\n" " -c, --client=NUM number of concurrent database clients (default: 1)\n" " -C, --connect establish new connection for each transaction\n" " -D, --define=VARNAME=VALUE\n" " define variable for use by custom script\n" " -j, --jobs=NUM number of threads (default: 1)\n" " -l, --log write transaction times to log file\n" " -L, --latency-limit=NUM count transactions lasting more than NUM ms as late\n" " -M, --protocol=simple|extended|prepared\n" " protocol for submitting queries (default: simple)\n" " -n, --no-vacuum do not run VACUUM before tests\n" " -P, --progress=NUM show thread progress report every NUM seconds\n" " -r, --report-per-command report latencies, failures, and retries per command\n" " -R, --rate=NUM target rate in transactions per second\n" " -s, --scale=NUM report this scale factor in output\n" " -t, --transactions=NUM number of transactions each client runs (default: 10)\n" " -T, --time=NUM duration of benchmark test in seconds\n" " -v, --vacuum-all vacuum all four standard tables before tests\n" " --aggregate-interval=NUM aggregate data over NUM seconds\n" " --exit-on-abort exit when any client is aborted\n" " --failures-detailed report the failures grouped by basic types\n" " --log-prefix=PREFIX prefix for transaction time log file\n" " (default: \"pgbench_log\")\n" " --max-tries=NUM max number of tries to run transaction (default: 1)\n" " --progress-timestamp use Unix epoch timestamps for progress\n" " --random-seed=SEED set random seed (\"time\", \"rand\", integer)\n" " --sampling-rate=NUM fraction of transactions to log (e.g., 0.01 for 1%%)\n" " --show-script=NAME show builtin script code, then exit\n" " --verbose-errors print messages of all errors\n" "\nCommon options:\n" " --debug print debugging output\n" " -d, --dbname=DBNAME database name to connect to\n" " -h, --host=HOSTNAME database server host or socket directory\n" " -p, --port=PORT database server port number\n" " -U, --username=USERNAME connect as specified database user\n" " -V, --version output version information, then exit\n" " -?, --help show this help, then exit\n" "\n" "Report bugs to <%s>.\n" "%s home page: <%s>\n",
progname, progname, PACKAGE_BUGREPORT, PACKAGE_NAME, PACKAGE_URL);
}
/* abort if wrong parameter, but must really be checked beforehand */
Assert(parameter > 0.0);
cut = exp(-parameter); /* pg_prng_double value in [0, 1), uniform in (0, 1] */
uniform = 1.0 - pg_prng_double(state);
/* *innerexpressionin(cut,1](ifparameter>0),randin[0,1)
*/
Assert((1.0 - cut) != 0.0);
rand = -log(cut + (1.0 - cut) * uniform) / parameter; /* return int64 random number within between min and max */ return min + (int64) ((max - min + 1) * rand);
}
/* random number generator: gaussian distribution from min to max inclusive */ static int64
getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
{ double stdev; double rand;
/* abort if parameter is too low, but must really be checked beforehand */
Assert(parameter >= MIN_GAUSSIAN_PARAM);
/* pg_prng_double value in [0, 1), uniform in (0, 1] */
uniform = 1.0 - pg_prng_double(state);
return (int64) (-log(uniform) * center + 0.5);
}
/* *Computingzipfianusingrejectionmethod,basedon *"Non-UniformRandomVariateGeneration", *LucDevroye,p.550-551,Springer1986. * *Thisworksfors>1.0,butmayperformbadlyforsverycloseto1.0.
*/ static int64
computeIterativeZipfian(pg_prng_state *state, int64 n, double s)
{ double b = pow(2.0, s - 1.0); double x,
t,
u,
v;
/* Ensure n is sane */ if (n <= 1) return1;
while (true)
{ /* random variates */
u = pg_prng_double(state);
v = pg_prng_double(state);
x = floor(pow(u, -1.0 / (s - 1.0)));
t = pow(1.0 + 1.0 / x, s - 1.0); /* reject if too large or out of bound */ if (v * x * (t - 1.0) / (b - 1.0) <= t / b && x <= n) break;
} return (int64) x;
}
/* random number generator: zipfian distribution from min to max inclusive */ static int64
getZipfianRand(pg_prng_state *state, int64 min, int64 max, double s)
{
int64 n = max - min + 1;
/* abort if parameter is invalid */
Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM);
return min - 1 + computeIterativeZipfian(state, n, s);
}
/* Random multiply (by an odd number), XOR and rotate of lower half */
m = (pg_prng_uint64(&state) & mask) | 1;
r = pg_prng_uint64(&state) & mask; if (v <= mask)
{
v = ((v * m) ^ r) & mask;
v = ((v << 1) & mask) | (v >> (masklen - 1));
}
/* Random multiply (by an odd number), XOR and rotate of upper half */
m = (pg_prng_uint64(&state) & mask) | 1;
r = pg_prng_uint64(&state) & mask;
t = size - 1 - v; if (t <= mask)
{
t = ((t * m) ^ r) & mask;
t = ((t << 1) & mask) | (t >> (masklen - 1));
v = size - 1 - t;
}
/* Random offset */
r = pg_prng_uint64_range(&state, 0, size - 1);
v = (v + r) % size;
}
/* *Accumulateoneadditionalitemintothegivenstatsobject.
*/ staticvoid
accumStats(StatsData *stats, bool skipped, double lat, double lag,
EStatus estatus, int64 tries)
{ /* Record the skipped transaction */ if (skipped)
{ /* no latency to record on skipped transactions */
stats->skipped++; return;
}
switch (estatus)
{ /* Record the successful transaction */ case ESTATUS_NO_ERROR:
stats->cnt++;
addToSimpleStats(&stats->latency, lat);
/* and possibly the same for schedule lag */ if (throttle_delay)
addToSimpleStats(&stats->lag, lag); break;
/* Record the failed transaction */ case ESTATUS_SERIALIZATION_ERROR:
stats->serialization_failures++; break; case ESTATUS_DEADLOCK_ERROR:
stats->deadlock_failures++; break; default: /* internal error which should never occur */
pg_fatal("unexpected error status: %d", estatus);
}
}
/* call PQexec() and exit() on failure */ staticvoid
executeStatement(PGconn *con, constchar *sql)
{
PGresult *res;
/* call PQexec() and complain, but without exiting, on failure */ staticvoid
tryExecuteStatement(PGconn *con, constchar *sql)
{
PGresult *res;
res = PQexec(con, sql); if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
pg_log_error("%s", PQerrorMessage(con));
pg_log_error_detail("(ignoring this error and continuing anyway)");
}
PQclear(res);
}
/* set up a connection to the backend */ static PGconn *
doConnect(void)
{
PGconn *conn; bool new_pass; staticchar *password = NULL;
/* *Starttheconnection.Loopuntilwehaveapasswordifrequestedby *backend.
*/ do
{ #define PARAMS_ARRAY_SIZE 7
/* check to see that the backend connection was successfully made */ if (PQstatus(conn) == CONNECTION_BAD)
{
pg_log_error("%s", PQerrorMessage(conn));
PQfinish(conn); return NULL;
}
/* Locate a variable by name; returns NULL if unknown */ static Variable *
lookupVariable(Variables *variables, char *name)
{
Variable key;
/* On some versions of Solaris, bsearch of zero items dumps core */ if (variables->nvars <= 0) return NULL;
/* Sort if we have to */ if (!variables->vars_sorted)
{
qsort(variables->vars, variables->nvars, sizeof(Variable),
compareVariableNames);
variables->vars_sorted = true;
}
/* Now we can search */
key.name = name; return (Variable *) bsearch(&key,
variables->vars,
variables->nvars, sizeof(Variable),
compareVariableNames);
}
/* Get the value of a variable, in string form; returns NULL if unknown */ staticchar *
getVariable(Variables *variables, char *name)
{
Variable *var; char stringform[64];
var = lookupVariable(variables, name); if (var == NULL) return NULL; /* not found */
if (var->svalue) return var->svalue; /* we have it in string form */
/* We need to produce a string equivalent of the value */
Assert(var->value.type != PGBT_NO_VALUE); if (var->value.type == PGBT_NULL)
snprintf(stringform, sizeof(stringform), "NULL"); elseif (var->value.type == PGBT_BOOLEAN)
snprintf(stringform, sizeof(stringform), "%s", var->value.u.bval ? "true" : "false"); elseif (var->value.type == PGBT_INT)
snprintf(stringform, sizeof(stringform),
INT64_FORMAT, var->value.u.ival); elseif (var->value.type == PGBT_DOUBLE)
snprintf(stringform, sizeof(stringform), "%.*g", DBL_DIG, var->value.u.dval); else/* internal error, unexpected type */
Assert(0);
var->svalue = pg_strdup(stringform); return var->svalue;
}
/* Try to convert variable to a value; return false on failure */ staticbool
makeVariableValue(Variable *var)
{
size_t slen;
if (var->value.type != PGBT_NO_VALUE) returntrue; /* no work */
slen = strlen(var->svalue);
if (slen == 0) /* what should it do on ""? */ returnfalse;
if (pg_strcasecmp(var->svalue, "null") == 0)
{
setNullValue(&var->value);
}
/* *acceptprefixessuchasy,ye,n,no...butnotfor"o".0/1are *recognizedlaterasanint,whichisconvertedtoboolifneeded.
*/ elseif (pg_strncasecmp(var->svalue, "true", slen) == 0 ||
pg_strncasecmp(var->svalue, "yes", slen) == 0 ||
pg_strcasecmp(var->svalue, "on") == 0)
{
setBoolValue(&var->value, true);
} elseif (pg_strncasecmp(var->svalue, "false", slen) == 0 ||
pg_strncasecmp(var->svalue, "no", slen) == 0 ||
pg_strcasecmp(var->svalue, "off") == 0 ||
pg_strcasecmp(var->svalue, "of") == 0)
{
setBoolValue(&var->value, false);
} elseif (is_an_int(var->svalue))
{ /* if it looks like an int, it must be an int without overflow */
int64 iv;
if (!strtoint64(var->svalue, false, &iv)) returnfalse;
setIntValue(&var->value, iv);
} else/* type should be double */
{ double dv;
/* Mustn't be zero-length */ if (*ptr == '\0') returnfalse;
/* must not start with [0-9] */ if (IS_HIGHBIT_SET(*ptr) ||
strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ""abcdefghijklmnopqrstuvwxyz" "_", *ptr) != NULL)
ptr++; else returnfalse;
/* remaining characters can include [0-9] */ while (*ptr)
{ if (IS_HIGHBIT_SET(*ptr) ||
strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ""abcdefghijklmnopqrstuvwxyz" "_0123456789", *ptr) != NULL)
ptr++; else returnfalse;
}
returntrue;
}
/* *Makesurethereisenoughspacefor'needed'morevariableinthevariables *array.
*/ staticvoid
enlargeVariables(Variables *variables, int needed)
{ /* total number of variables required now */
needed += variables->nvars;
var = lookupVariable(variables, name); if (var == NULL)
{ /* *Checkforthenameonlywhendeclaringanewvariabletoavoid *overhead.
*/ if (!valid_variable_name(name))
{
pg_log_error("%s: invalid variable name: \"%s\"", context, name); return NULL;
}
/* Create variable at the end of the array */
enlargeVariables(variables, 1);
var = &(variables->vars[variables->nvars]);
var->name = pg_strdup(name);
var->svalue = NULL; /* caller is expected to initialize remaining fields */
variables->nvars++; /* we don't re-sort the array till we have to */
variables->vars_sorted = false;
}
return var;
}
/* Assign a string value to a variable, creating it if need be */ /* Returns false on failure (bad name) */ staticbool
putVariable(Variables *variables, constchar *context, char *name, constchar *value)
{
Variable *var; char *val;
var = lookupCreateVariable(variables, context, name); if (!var) returnfalse;
/* dup then free, in case value is pointing at this variable */
val = pg_strdup(value);
/* Assign a value to a variable, creating it if need be */ /* Returns false on failure (bad name) */ staticbool
putVariableValue(Variables *variables, constchar *context, char *name, const PgBenchValue *value)
{
Variable *var;
var = lookupCreateVariable(variables, context, name); if (!var) returnfalse;
/* Assign an integer value to a variable, creating it if need be */ /* Returns false on failure (bad name) */ staticbool
putVariableInt(Variables *variables, constchar *context, char *name,
int64 value)
{
PgBenchValue val;
for (i = 0; i < command->argc - 1; i++)
params[i] = getVariable(variables, command->argv[i + 1]);
}
staticchar *
valueTypeName(PgBenchValue *pval)
{ if (pval->type == PGBT_NO_VALUE) return"none"; elseif (pval->type == PGBT_NULL) return"null"; elseif (pval->type == PGBT_INT) return"int"; elseif (pval->type == PGBT_DOUBLE) return"double"; elseif (pval->type == PGBT_BOOLEAN) return"boolean"; else
{ /* internal error, should never get there */
Assert(false); return NULL;
}
}
/* get a value as a boolean, or tell if there is a problem */ staticbool
coerceToBool(PgBenchValue *pval, bool *bval)
{ if (pval->type == PGBT_BOOLEAN)
{
*bval = pval->u.bval; returntrue;
} else/* NULL, INT or DOUBLE */
{
pg_log_error("cannot coerce %s to boolean", valueTypeName(pval));
*bval = false; /* suppress uninitialized-variable warnings */ returnfalse;
}
}
/* *Returntrueorfalsefromanexpressionforconditionalpurposes. *Nonzeronumericalvaluesaretrue,zeroandNULLarefalse.
*/ staticbool
valueTruth(PgBenchValue *pval)
{ switch (pval->type)
{ case PGBT_NULL: returnfalse; case PGBT_BOOLEAN: return pval->u.bval; case PGBT_INT: return pval->u.ival != 0; case PGBT_DOUBLE: return pval->u.dval != 0.0; default: /* internal error, unexpected type */
Assert(0); returnfalse;
}
}
/* get a value as an int, tell if there is a problem */ staticbool
coerceToInt(PgBenchValue *pval, int64 *ival)
{ if (pval->type == PGBT_INT)
{
*ival = pval->u.ival; returntrue;
} elseif (pval->type == PGBT_DOUBLE)
{ double dval = rint(pval->u.dval);
if (isnan(dval) || !FLOAT8_FITS_IN_INT64(dval))
{
pg_log_error("double to int overflow for %f", dval); returnfalse;
}
*ival = (int64) dval; returntrue;
} else/* BOOLEAN or NULL */
{
pg_log_error("cannot coerce %s to int", valueTypeName(pval)); returnfalse;
}
}
/* get a value as a double, or tell if there is a problem */ staticbool
coerceToDouble(PgBenchValue *pval, double *dval)
{ if (pval->type == PGBT_DOUBLE)
{
*dval = pval->u.dval; returntrue;
} elseif (pval->type == PGBT_INT)
{
*dval = (double) pval->u.ival; returntrue;
} else/* BOOLEAN or NULL */
{
pg_log_error("cannot coerce %s to double", valueTypeName(pval)); returnfalse;
}
}
/* assign a null value */ staticvoid
setNullValue(PgBenchValue *pv)
{
pv->type = PGBT_NULL;
pv->u.ival = 0;
}
/* assign a boolean value */ staticvoid
setBoolValue(PgBenchValue *pv, bool bval)
{
pv->type = PGBT_BOOLEAN;
pv->u.bval = bval;
}
/* assign an integer value */ staticvoid
setIntValue(PgBenchValue *pv, int64 ival)
{
pv->type = PGBT_INT;
pv->u.ival = ival;
}
/* assign a double value */ staticvoid
setDoubleValue(PgBenchValue *pv, double dval)
{
pv->type = PGBT_DOUBLE;
pv->u.dval = dval;
}
/* then evaluate function */ switch (func)
{ /* overloaded operators */ case PGBENCH_ADD: case PGBENCH_SUB: case PGBENCH_MUL: case PGBENCH_DIV: case PGBENCH_MOD: case PGBENCH_EQ: case PGBENCH_NE: case PGBENCH_LE: case PGBENCH_LT:
{
PgBenchValue *lval = &vargs[0],
*rval = &vargs[1];
Assert(nargs == 2);
/* overloaded type management, double if some double */ if ((lval->type == PGBT_DOUBLE ||
rval->type == PGBT_DOUBLE) && func != PGBENCH_MOD)
{ double ld,
rd;
if (!coerceToDouble(lval, &ld) ||
!coerceToDouble(rval, &rd)) returnfalse;
switch (func)
{ case PGBENCH_ADD:
setDoubleValue(retval, ld + rd); returntrue;
case PGBENCH_SUB:
setDoubleValue(retval, ld - rd); returntrue;
case PGBENCH_MUL:
setDoubleValue(retval, ld * rd); returntrue;
case PGBENCH_DIV:
setDoubleValue(retval, ld / rd); returntrue;
case PGBENCH_EQ:
setBoolValue(retval, ld == rd); returntrue;
case PGBENCH_NE:
setBoolValue(retval, ld != rd); returntrue;
case PGBENCH_LE:
setBoolValue(retval, ld <= rd); returntrue;
case PGBENCH_LT:
setBoolValue(retval, ld < rd); returntrue;
default: /* cannot get here */
Assert(0);
}
} else/* we have integer operands, or % */
{
int64 li,
ri,
res;
if (!coerceToInt(lval, &li) ||
!coerceToInt(rval, &ri)) returnfalse;
switch (func)
{ case PGBENCH_ADD: if (pg_add_s64_overflow(li, ri, &res))
{
pg_log_error("bigint add out of range"); returnfalse;
}
setIntValue(retval, res); returntrue;
case PGBENCH_SUB: if (pg_sub_s64_overflow(li, ri, &res))
{
pg_log_error("bigint sub out of range"); returnfalse;
}
setIntValue(retval, res); returntrue;
case PGBENCH_MUL: if (pg_mul_s64_overflow(li, ri, &res))
{
pg_log_error("bigint mul out of range"); returnfalse;
}
setIntValue(retval, res); returntrue;
case PGBENCH_EQ:
setBoolValue(retval, li == ri); returntrue;
case PGBENCH_NE:
setBoolValue(retval, li != ri); returntrue;
case PGBENCH_LE:
setBoolValue(retval, li <= ri); returntrue;
case PGBENCH_LT:
setBoolValue(retval, li < ri); returntrue;
case PGBENCH_DIV: case PGBENCH_MOD: if (ri == 0)
{
pg_log_error("division by zero"); returnfalse;
} /* special handling of -1 divisor */ if (ri == -1)
{ if (func == PGBENCH_DIV)
{ /* overflow check (needed for INT64_MIN) */ if (li == PG_INT64_MIN)
{
pg_log_error("bigint div out of range"); returnfalse;
} else
setIntValue(retval, -li);
} else
setIntValue(retval, 0); returntrue;
} /* else divisor is not -1 */ if (func == PGBENCH_DIV)
setIntValue(retval, li / ri); else/* func == PGBENCH_MOD */
setIntValue(retval, li % ri);
returntrue;
default: /* cannot get here */
Assert(0);
}
}
Assert(0); returnfalse; /* NOTREACHED */
}
/* integer bitwise operators */ case PGBENCH_BITAND: case PGBENCH_BITOR: case PGBENCH_BITXOR: case PGBENCH_LSHIFT: case PGBENCH_RSHIFT:
{
int64 li,
ri;
if (!coerceToInt(&vargs[0], &li) || !coerceToInt(&vargs[1], &ri)) returnfalse;
if (func == PGBENCH_BITAND)
setIntValue(retval, li & ri); elseif (func == PGBENCH_BITOR)
setIntValue(retval, li | ri); elseif (func == PGBENCH_BITXOR)
setIntValue(retval, li ^ ri); elseif (func == PGBENCH_LSHIFT)
setIntValue(retval, li << ri); elseif (func == PGBENCH_RSHIFT)
setIntValue(retval, li >> ri); else/* cannot get here */
Assert(0);
returntrue;
}
/* logical operators */ case PGBENCH_NOT:
{ bool b;
if (!coerceToBool(&vargs[0], &b)) returnfalse;
setBoolValue(retval, !b); returntrue;
}
/* no arguments */ case PGBENCH_PI:
setDoubleValue(retval, M_PI); returntrue;
/* 1 double argument */ case PGBENCH_DOUBLE: case PGBENCH_SQRT: case PGBENCH_LN: case PGBENCH_EXP:
{ double dval;
Assert(nargs == 1);
if (!coerceToDouble(&vargs[0], &dval)) returnfalse;
if (func == PGBENCH_SQRT)
dval = sqrt(dval); elseif (func == PGBENCH_LN)
dval = log(dval); elseif (func == PGBENCH_EXP)
dval = exp(dval); /* else is cast: do nothing */
setDoubleValue(retval, dval); returntrue;
}
/* 1 int argument */ case PGBENCH_INT:
{
int64 ival;
Assert(nargs == 1);
if (!coerceToInt(&vargs[0], &ival)) returnfalse;
setIntValue(retval, ival); returntrue;
}
/* variable number of arguments */ case PGBENCH_LEAST: case PGBENCH_GREATEST:
{ bool havedouble; int i;
Assert(nargs >= 1);
/* need double result if any input is double */
havedouble = false; for (i = 0; i < nargs; i++)
{ if (vargs[i].type == PGBT_DOUBLE)
{
havedouble = true; break;
}
} if (havedouble)
{ double extremum;
if (!coerceToDouble(&vargs[0], &extremum)) returnfalse; for (i = 1; i < nargs; i++)
{ double dval;
/* random functions */ case PGBENCH_RANDOM: case PGBENCH_RANDOM_EXPONENTIAL: case PGBENCH_RANDOM_GAUSSIAN: case PGBENCH_RANDOM_ZIPFIAN:
{
int64 imin,
imax,
delta;
Assert(nargs >= 2);
if (!coerceToInt(&vargs[0], &imin) ||
!coerceToInt(&vargs[1], &imax)) returnfalse;
/* check random range */ if (unlikely(imin > imax))
{
pg_log_error("empty range given to random"); returnfalse;
} elseif (unlikely(pg_sub_s64_overflow(imax, imin, &delta) ||
pg_add_s64_overflow(delta, 1, &delta)))
{ /* prevent int overflows in random functions */
pg_log_error("random range is too large"); returnfalse;
}
if (!coerceToDouble(&vargs[2], ¶m)) returnfalse;
if (func == PGBENCH_RANDOM_GAUSSIAN)
{ if (param < MIN_GAUSSIAN_PARAM)
{
pg_log_error("gaussian parameter must be at least %f (not %f)",
MIN_GAUSSIAN_PARAM, param); returnfalse;
}
setIntValue(retval,
getGaussianRand(&st->cs_func_rs,
imin, imax, param));
} elseif (func == PGBENCH_RANDOM_ZIPFIAN)
{ if (param < MIN_ZIPFIAN_PARAM || param > MAX_ZIPFIAN_PARAM)
{
pg_log_error("zipfian parameter must be in range [%.3f, %.0f] (not %f)",
MIN_ZIPFIAN_PARAM, MAX_ZIPFIAN_PARAM, param); returnfalse;
}
setIntValue(retval,
getZipfianRand(&st->cs_func_rs, imin, imax, param));
} else/* exponential */
{ if (param <= 0.0)
{
pg_log_error("exponential parameter must be greater than zero (not %f)",
param); returnfalse;
}
arglen = strlen(arg); if (len + arglen + (i > 0 ? 1 : 0) >= SHELL_COMMAND_SIZE - 1)
{
pg_log_error("%s: shell command is too long", argv[0]); returnfalse;
}
if (i > 0)
command[len++] = ' ';
memcpy(command + len, arg, arglen);
len += arglen;
}
command[len] = '\0';
fflush(NULL); /* needed before either system() or popen() */
/* Fast path for non-assignment case */ if (variable == NULL)
{ if (system(command))
{ if (!timer_exceeded)
pg_log_error("%s: could not launch shell command", argv[0]); returnfalse;
} returntrue;
}
/* Execute the command with pipe and read the standard output. */ if ((fp = popen(command, "r")) == NULL)
{
pg_log_error("%s: could not launch shell command", argv[0]); returnfalse;
} if (fgets(res, sizeof(res), fp) == NULL)
{ if (!timer_exceeded)
pg_log_error("%s: could not read result of shell command", argv[0]);
(void) pclose(fp); returnfalse;
} if (pclose(fp) < 0)
{
pg_log_error("%s: could not run shell command: %m", argv[0]); returnfalse;
}
/* Check whether the result is an integer and assign it to the variable */
retval = (int) strtol(res, &endptr, 10); while (*endptr != '\0' && isspace((unsignedchar) *endptr))
endptr++; if (*res == '\0' || *endptr != '\0')
{
pg_log_error("%s: shell command must return an integer (not \"%s\")", argv[0], res); returnfalse;
} if (!putVariableInt(variables, "setshell", variable, retval)) returnfalse;
case PGRES_NONFATAL_ERROR: case PGRES_FATAL_ERROR:
st->estatus = getSQLErrorStatus(PQresultErrorField(res,
PG_DIAG_SQLSTATE)); if (canRetryError(st->estatus))
{ if (verbose_errors)
commandError(st, PQresultErrorMessage(res)); goto error;
} /* fall through */
/* Raise an error if the value of a variable is not a number */ if (usec == 0 && !isdigit((unsignedchar) *var))
{
pg_log_error("%s: invalid sleep time \"%s\" for variable \"%s\"",
argv[0], var, argv[1] + 1); returnfalse;
}
} else
usec = atoi(argv[1]);
if (PQstatus(st->con) == CONNECTION_BAD)
{
pg_log_error("client %d aborted while rolling back the transaction after an error; perhaps the backend died while processing",
st->id);
PQclear(res); return0;
}
/* exit pipeline */ if (PQexitPipelineMode(st->con) != 1)
{
pg_log_error("client %d aborted: failed to exit pipeline mode for rolling back the failed transaction",
st->id); return0;
} return1;
}
tx_status = PQtransactionStatus(con); switch (tx_status)
{ case PQTRANS_IDLE: return TSTATUS_IDLE; case PQTRANS_INTRANS: case PQTRANS_INERROR: return TSTATUS_IN_BLOCK; case PQTRANS_UNKNOWN: /* PQTRANS_UNKNOWN is expected given a broken connection */ if (PQstatus(con) == CONNECTION_BAD) return TSTATUS_CONN_ERROR; /* fall through */ case PQTRANS_ACTIVE: default:
if (buf == NULL)
buf = createPQExpBuffer(); else
resetPQExpBuffer(buf);
printfPQExpBuffer(buf, "client %d ", st->id);
appendPQExpBufferStr(buf, (is_retry ? "repeats the transaction after the error" : "ends the failed transaction"));
appendPQExpBuffer(buf, " (try %u", st->tries);
/* Print max_tries if it is not unlimited. */ if (max_tries)
appendPQExpBuffer(buf, "/%u", max_tries);
/* *Ifthelatencylimitisused,printapercentageofthecurrent *transactionlatencyfromthelatencylimit.
*/ if (latency_limit)
{
pg_time_now_lazy(now);
appendPQExpBuffer(buf, ", %.3f%% of the maximum time of tries was used",
(100.0 * (*now - st->txn_scheduled) / latency_limit));
}
appendPQExpBufferStr(buf, ")\n");
/* *nonexecutedconditionalbranch
*/ case CSTATE_SKIP_COMMAND:
Assert(!conditional_active(st->cstack)); /* quickly skip commands until something to do... */ while (true)
{
command = sql_script[st->use_file].commands[st->command];
/* cannot reach end of script in that state */
Assert(command != NULL);
/* *ifthisisconditionalrelated,updateconditional *state
*/ if (command->type == META_COMMAND &&
(command->meta == META_IF ||
command->meta == META_ELIF ||
command->meta == META_ELSE ||
command->meta == META_ENDIF))
{ switch (conditional_stack_peek(st->cstack))
{ case IFSTATE_FALSE: if (command->meta == META_IF)
{ /* nested if in skipped branch - ignore */
conditional_stack_push(st->cstack,
IFSTATE_IGNORED);
st->command++;
} elseif (command->meta == META_ELIF)
{ /* we must evaluate the condition */
st->state = CSTATE_START_COMMAND;
} elseif (command->meta == META_ELSE)
{ /* we must execute next command */
conditional_stack_poke(st->cstack,
IFSTATE_ELSE_TRUE);
st->state = CSTATE_START_COMMAND;
st->command++;
} elseif (command->meta == META_ENDIF)
{
Assert(!conditional_stack_empty(st->cstack));
conditional_stack_pop(st->cstack); if (conditional_active(st->cstack))
st->state = CSTATE_START_COMMAND; /* else state remains CSTATE_SKIP_COMMAND */
st->command++;
} break;
case IFSTATE_IGNORED: case IFSTATE_ELSE_FALSE: if (command->meta == META_IF)
conditional_stack_push(st->cstack,
IFSTATE_IGNORED); elseif (command->meta == META_ENDIF)
{
Assert(!conditional_stack_empty(st->cstack));
conditional_stack_pop(st->cstack); if (conditional_active(st->cstack))
st->state = CSTATE_START_COMMAND;
} /* could detect "else" & "elif" after "else" */
st->command++; break;
case IFSTATE_NONE: case IFSTATE_TRUE: case IFSTATE_ELSE_TRUE: default:
/* *inconsistentifinactive,unreachabledead *code
*/
Assert(false);
}
} else
{ /* skip and consider next */
st->command++;
}
if (st->state != CSTATE_SKIP_COMMAND) /* out of quick skip command loop */ break;
} break;
/* *WaitforthecurrentSQLcommandtocomplete
*/ case CSTATE_WAIT_RESULT:
pg_log_debug("client %d receiving", st->id);
/* *Onlycheckfornewnetworkdataifweprocessedalldata *fetchedprior.Otherwiseweendupdoingasyscallforeach *individualpipelinedquery,whichhasameasurable *performanceimpact.
*/ if (PQisBusy(st->con) && !PQconsumeInput(st->con))
{ /* there's something wrong */
commandFailed(st, "SQL", "perhaps the backend died while processing");
st->state = CSTATE_ABORTED; break;
} if (PQisBusy(st->con)) return; /* don't have the whole result yet */
/* store or discard the query results */ if (readCommandResponse(st,
sql_script[st->use_file].commands[st->command]->meta,
sql_script[st->use_file].commands[st->command]->varprefix))
{ /* *outsideofpipelinemode:stopreadingresults. *pipelinemode:continuereadingresultsuntilan *end-of-pipelineresponse.
*/ if (PQpipelineStatus(st->con) != PQ_PIPELINE_ON)
st->state = CSTATE_END_COMMAND;
} elseif (canRetryError(st->estatus))
st->state = CSTATE_ERROR; else
st->state = CSTATE_ABORTED; break;
/* *Waituntilsleepisdone.Thisstateisenteredaftera *\sleepmetacommand.Thebehaviorissimilarto *CSTATE_THROTTLE,butproceedstoCSTATE_START_COMMAND *insteadofCSTATE_START_TX.
*/ case CSTATE_SLEEP:
pg_time_now_lazy(&now); if (now < st->sleep_until) return; /* still sleeping, nothing to do here */ /* Else done sleeping. */
st->state = CSTATE_END_COMMAND; break;
/* *Endofcommand:recordstatsandproceedtonextcommand.
*/ case CSTATE_END_COMMAND:
/* *commandcompleted:accumulateper-commandexecutiontimes *inthread-localdatastructure,ifper-commandlatencies *arerequested.
*/ if (report_per_command)
{
pg_time_now_lazy(&now);
command = sql_script[st->use_file].commands[st->command]; /* XXX could use a mutex here, but we choose not to */
addToSimpleStats(&command->stats,
PG_TIME_GET_DOUBLE(now - st->stmt_begin));
}
/* Go ahead with next command, to be executed or skipped */
st->command++;
st->state = conditional_active(st->cstack) ?
CSTATE_START_COMMAND : CSTATE_SKIP_COMMAND; break;
/* *Cleanupafteranerror.
*/ case CSTATE_ERROR:
{
TStatus tstatus;
Assert(st->estatus != ESTATUS_NO_ERROR);
/* Clear the conditional stack */
conditional_stack_reset(st->cstack);
/* Read and discard until a sync point in pipeline mode */ if (PQpipelineStatus(st->con) != PQ_PIPELINE_OFF)
{ if (!discardUntilSync(st))
{
st->state = CSTATE_ABORTED; break;
}
}
/* *Checkifwehavea(failed)transactionblockornot, *androllitbackifany.
*/
tstatus = getTransactionStatus(st->con); if (tstatus == TSTATUS_IN_BLOCK)
{ /* Try to rollback a (failed) transaction block. */ if (!PQsendQuery(st->con, "ROLLBACK"))
{
pg_log_error("client %d aborted: failed to send sql command for rolling back the failed transaction",
st->id);
st->state = CSTATE_ABORTED;
} else
st->state = CSTATE_WAIT_ROLLBACK_RESULT;
} elseif (tstatus == TSTATUS_IDLE)
{ /* *Iftimeisover,we'redone;otherwise,checkifwe *canretrytheerror.
*/
st->state = timer_exceeded ? CSTATE_FINISHED :
doRetry(st, &now) ? CSTATE_RETRY : CSTATE_FAILURE;
} else
{ if (tstatus == TSTATUS_CONN_ERROR)
pg_log_error("perhaps the backend died while processing");
pg_log_error("client %d aborted while receiving the transaction status", st->id);
st->state = CSTATE_ABORTED;
} break;
}
/* *Waitfortherollbackcommandtocomplete
*/ case CSTATE_WAIT_ROLLBACK_RESULT:
{
PGresult *res;
pg_log_debug("client %d receiving", st->id); if (!PQconsumeInput(st->con))
{
pg_log_error("client %d aborted while rolling back the transaction after an error; perhaps the backend died while processing",
st->id);
st->state = CSTATE_ABORTED; break;
} if (PQisBusy(st->con)) return; /* don't have the whole result yet */
/* *Readanddiscardthequeryresult;
*/
res = PQgetResult(st->con); switch (PQresultStatus(res))
{ case PGRES_COMMAND_OK: /* OK */
PQclear(res); /* null must be returned */
res = PQgetResult(st->con);
Assert(res == NULL);
/* *Iftimeisover,we'redone;otherwise,check *ifwecanretrytheerror.
*/
st->state = timer_exceeded ? CSTATE_FINISHED :
doRetry(st, &now) ? CSTATE_RETRY : CSTATE_FAILURE; break; default:
pg_log_error("client %d aborted while rolling back the transaction after an error; %s",
st->id, PQerrorMessage(st->con));
PQclear(res);
st->state = CSTATE_ABORTED; break;
} break;
}
/* *Retrythetransactionafteranerror.
*/ case CSTATE_RETRY:
command = sql_script[st->use_file].commands[st->command];
/* *Informthatthetransactionwillberetriedafterthe *error.
*/ if (verbose_errors)
printVerboseErrorMessages(st, &now, true);
/* Count tries and retries */
st->tries++;
command->retries++;
/* *Wemustcompleteallthetransactionblocksthatwere *startedinthisscript.
*/
tstatus = getTransactionStatus(st->con); if (tstatus == TSTATUS_IN_BLOCK)
{
pg_log_error("client %d aborted: end of script reached without completing the last transaction",
st->id);
st->state = CSTATE_ABORTED; break;
} elseif (tstatus != TSTATUS_IDLE)
{ if (tstatus == TSTATUS_CONN_ERROR)
pg_log_error("perhaps the backend died while processing");
pg_log_error("client %d aborted while receiving the transaction status", st->id);
st->state = CSTATE_ABORTED; break;
}
if (is_connect)
{
pg_time_usec_t start = now;
pg_time_now_lazy(&start);
finishCon(st);
now = pg_time_now();
thread->conn_duration += now - start;
}
/* count transactions over the latency limit, if needed */ if (latency_limit && latency > latency_limit)
thread->latency_late++;
/* client stat is just counting */
st->cnt++;
if (use_log)
doLog(thread, st, agg, skipped, latency, lag);
/* XXX could use a mutex here, but we choose not to */ if (per_script_stats)
accumStats(&sql_script[st->use_file].stats, skipped, latency, lag,
st->estatus, st->tries);
}
/* discard connections */ staticvoid
disconnect_all(CState *state, int length)
{ int i;
for (i = 0; i < length; i++)
finishCon(&state[i]);
}
staticvoid
initPopulateTable(PGconn *con, constchar *table, int64 base,
initRowMethod init_row)
{ int n;
int64 k; int chars = 0; int prev_chars = 0;
PGresult *res;
PQExpBufferData sql; char copy_statement[256]; constchar *copy_statement_fmt = "copy %s from stdin";
int64 total = base * scale;
/* used to track elapsed time and estimate of the remaining time */
pg_time_usec_t start; int log_interval = 1;
/* Stay on the same line if reporting to a terminal */ char eol = isatty(fileno(stderr)) ? '\r' : '\n';
initPQExpBuffer(&sql);
/* Use COPY with FREEZE on v14 and later for all ordinary tables */ if ((PQserverVersion(con) >= 140000) &&
get_table_relkind(con, table) == RELKIND_RELATION)
copy_statement_fmt = "copy %s from stdin with (freeze on)";
n = pg_snprintf(copy_statement, sizeof(copy_statement), copy_statement_fmt, table); if (n >= sizeof(copy_statement))
pg_fatal("invalid buffer size: must be at least %d characters long", n); elseif (n == -1)
pg_fatal("invalid format string");
res = PQexec(con, copy_statement);
if (PQresultStatus(res) != PGRES_COPY_IN)
pg_fatal("unexpected copy in result: %s", PQerrorMessage(con));
PQclear(res);
start = pg_time_now();
for (k = 0; k < total; k++)
{
int64 j = k + 1;
init_row(&sql, k); if (PQputline(con, sql.data))
pg_fatal("PQputline failed");
/* *getthescalingfactorthatshouldbesameascount(*)from *pgbench_branchesifthisisnotacustomquery
*/
res = PQexec(con, "select count(*) from pgbench_branches"); if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ char *sqlState = PQresultErrorField(res, PG_DIAG_SQLSTATE);
pg_log_error("could not count number of branches: %s", PQerrorMessage(con));
if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) == 0)
pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".",
PQdb(con));
exit(1);
}
scale = atoi(PQgetvalue(res, 0, 0)); if (scale < 0)
pg_fatal("invalid count(*) from pgbench_branches: \"%s\"",
PQgetvalue(res, 0, 0));
PQclear(res);
/* warn if we override user-given -s switch */ if (scale_given)
pg_log_warning("scale option ignored, using count from pgbench_branches table (%d)",
scale);
/* *Getthepartitioninformationforthefirst"pgbench_accounts"table *foundinsearch_path. * *Theresultisemptyifno"pgbench_accounts"isfound. * *Otherwise,italwaysreturnsonerowevenifthetableisnot *partitioned(inwhichcasethepartitionstrategyisNULL). * *Thenumberofpartitionscanbe0evenforpartitionedtables,ifno *partitionisattached. * *Weassumenopartitioningonanyfailure,soastoavoidfailingonan *oldversionwithout"pg_partitioned_table".
*/
res = PQexec(con, "select o.n, p.partstrat, pg_catalog.count(i.inhparent) " "from pg_catalog.pg_class as c " "join pg_catalog.pg_namespace as n on (n.oid = c.relnamespace) " "cross join lateral (select pg_catalog.array_position(pg_catalog.current_schemas(true), n.nspname)) as o(n) " "left join pg_catalog.pg_partitioned_table as p on (p.partrelid = c.oid) " "left join pg_catalog.pg_inherits as i on (c.oid = i.inhparent) " "where c.relname = 'pgbench_accounts' and o.n is not null " "group by 1, 2 " "order by 1 asc " "limit 1");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ /* probably an older version, coldly assume no partitioning */
partition_method = PART_NONE;
partitions = 0;
} elseif (PQntuples(res) == 0)
{ /* *Thiscaseisunlikelyaspgbenchalreadyfound"pgbench_branches" *abovetocomputethescale.
*/
pg_log_error("no pgbench_accounts table found in \"search_path\"");
pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".", PQdb(con)); exit(1);
} else/* PQntuples(res) == 1 */
{ /* normal case, extract partition information */ if (PQgetisnull(res, 0, 1))
partition_method = PART_NONE; else
{ char *ps = PQgetvalue(res, 0, 1);
/* column must be there */
Assert(ps != NULL);
if (strcmp(ps, "r") == 0)
partition_method = PART_RANGE; elseif (strcmp(ps, "h") == 0)
partition_method = PART_HASH; else
{ /* possibly a newer version with new partition method */
pg_fatal("unexpected partition method: \"%s\"", ps);
}
}
p = sql = pg_strdup(cmd->lines.data); while ((p = strchr(p, ':')) != NULL)
{ char var[13]; char *name; int eaten;
name = parseVariable(p, &eaten); if (name == NULL)
{ while (*p == ':')
{
p++;
} continue;
}
/* *cmd->argv[0]istheSQLstatementitself,sothemaxnumberof *argumentsisonelessthanMAX_ARGS
*/ if (cmd->argc >= MAX_ARGS)
{
pg_log_error("statement has too many arguments (maximum is %d): %s",
MAX_ARGS - 1, cmd->lines.data);
pg_free(name); returnfalse;
}
sprintf(var, "$%d", cmd->argc);
p = replaceVariable(&sql, p, eaten, var);
/* Skip any leading whitespace, as well as "--" style comments */ for (;;)
{ if (isspace((unsignedchar) *p))
p++; elseif (strncmp(p, "--", 2) == 0)
{
p = strchr(p, '\n'); if (p == NULL) return NULL;
p++;
} else break;
}
/* NULL if there's nothing but whitespace and comments */ if (*p == '\0') return NULL;
/* Allocate and initialize Command structure */
my_command = (Command *) pg_malloc(sizeof(Command));
initPQExpBuffer(&my_command->lines);
appendPQExpBufferStr(&my_command->lines, p);
my_command->first_line = NULL; /* this is set later */
my_command->type = SQL_COMMAND;
my_command->meta = META_NONE;
my_command->argc = 0;
my_command->retries = 0;
my_command->failures = 0;
memset(my_command->argv, 0, sizeof(my_command->argv));
my_command->varprefix = NULL; /* allocated later, if needed */
my_command->expr = NULL;
initSimpleStats(&my_command->stats);
my_command->prepname = NULL; /* set later, if needed */
return my_command;
}
/* Free a Command structure and associated data */ staticvoid
free_command(Command *command)
{
termPQExpBuffer(&command->lines);
pg_free(command->first_line); for (int i = 0; i < command->argc; i++)
pg_free(command->argv[i]);
pg_free(command->varprefix);
/* Save the first line for error display. */
strlcpy(buffer, my_command->lines.data, sizeof(buffer));
buffer[strcspn(buffer, "\n\r")] = '\0';
my_command->first_line = pg_strdup(buffer);
/* Parse query and generate prepared statement name, if necessary */ switch (querymode)
{ case QUERY_SIMPLE:
my_command->argv[0] = my_command->lines.data;
my_command->argc++; break; case QUERY_PREPARED:
my_command->prepname = psprintf("P_%d", prepnum++); /* fall through */ case QUERY_EXTENDED: if (!parseQuery(my_command)) exit(1); break; default: exit(1);
}
}
/* *Parseabackslashcommand;returnaCommandstruct,orNULLifcomment * *Atcall,wehavescannedonlytheinitialbackslash.
*/ static Command *
process_backslash_command(PsqlScanState sstate, constchar *source, int lineno, int start_offset)
{
Command *my_command;
PQExpBufferData word_buf; int word_offset; int offsets[MAX_ARGS]; /* offsets of argument words */ int j;
initPQExpBuffer(&word_buf);
/* Collect first word of command */ if (!expr_lex_one_word(sstate, &word_buf, &word_offset))
{
termPQExpBuffer(&word_buf); return NULL;
}
/* For \set, collect var name */ if (my_command->meta == META_SET)
{ if (!expr_lex_one_word(sstate, &word_buf, &word_offset))
syntax_error(source, lineno, my_command->first_line, my_command->argv[0], "missing argument", NULL, -1);
for (i = 0; ps->commands[i] != NULL; i++)
{
Command *cmd = ps->commands[i];
if (cmd->type == META_COMMAND)
{ switch (cmd->meta)
{ case META_IF:
conditional_stack_push(cs, IFSTATE_FALSE); break; case META_ELIF: if (conditional_stack_empty(cs))
ConditionError(ps->desc, i + 1, "\\elif without matching \\if"); if (conditional_stack_peek(cs) == IFSTATE_ELSE_FALSE)
ConditionError(ps->desc, i + 1, "\\elif after \\else"); break; case META_ELSE: if (conditional_stack_empty(cs))
ConditionError(ps->desc, i + 1, "\\else without matching \\if"); if (conditional_stack_peek(cs) == IFSTATE_ELSE_FALSE)
ConditionError(ps->desc, i + 1, "\\else after \\else");
conditional_stack_poke(cs, IFSTATE_ELSE_FALSE); break; case META_ENDIF: if (!conditional_stack_pop(cs))
ConditionError(ps->desc, i + 1, "\\endif without matching \\if"); break; default: /* ignore anything else... */ break;
}
}
} if (!conditional_stack_empty(cs))
ConditionError(ps->desc, i + 1, "\\if without matching \\endif");
conditional_stack_destroy(cs);
}
/* *Parseascript(eitherthecontentsofafile,orabuilt-inscript) *andaddittothelistofscripts.
*/ staticvoid
ParseScript(constchar *script, constchar *desc, int weight)
{
ParsedScript ps;
PsqlScanState sstate;
PQExpBufferData line_buf; int alloc_num; int index;
nread = fread(buf + used, 1, BUFSIZ, fd);
used += nread; /* If fread() read less than requested, must be EOF or error */ if (nread < BUFSIZ) break; /* Enlarge buf so we can read some more */
buflen += BUFSIZ;
buf = (char *) pg_realloc(buf, buflen);
} /* There is surely room for a terminator */
buf[used] = '\0';
/* Slurp the file contents into "buf" */ if (strcmp(filename, "-") == 0)
fd = stdin; elseif ((fd = fopen(filename, "r")) == NULL)
pg_fatal("could not open file \"%s\": %m", filename);
buf = read_file_contents(fd);
if (ferror(fd))
pg_fatal("could not read file \"%s\": %m", filename);
if (fd != stdin)
fclose(fd);
ParseScript(buf, filename, weight);
free(buf);
}
/* Parse the given builtin script and add it to the list. */ staticvoid
process_builtin(const BuiltinScript *bi, int weight)
{
ParseScript(bi->script, bi->desc, weight);
}
/* show available builtin scripts */ staticvoid
listAvailableScripts(void)
{ int i;
fprintf(stderr, "Available builtin scripts:\n"); for (i = 0; i < lengthof(builtin_script); i++)
fprintf(stderr, " %13s: %s\n", builtin_script[i].name, builtin_script[i].desc);
fprintf(stderr, "\n");
}
/* return builtin script "name" if unambiguous, fails if not found */ staticconst BuiltinScript *
findBuiltin(constchar *name)
{ int i,
found = 0,
len = strlen(name); const BuiltinScript *result = NULL;
for (i = 0; i < lengthof(builtin_script); i++)
{ if (strncmp(builtin_script[i].name, name, len) == 0)
{
result = &builtin_script[i];
found++;
}
}
/* ok, unambiguous result */ if (found == 1) return result;
/* error cases */ if (found == 0)
pg_log_error("no builtin script found for name \"%s\"", name); else/* found > 1 */
pg_log_error("ambiguous builtin name: %d builtin scripts found for prefix \"%s\"", found, name);
if ((sep = strrchr(option, WSEP)))
{ int namelen = sep - option; long wtmp; char *badp;
/* generate the script name */
*script = pg_malloc(namelen + 1);
strncpy(*script, option, namelen);
(*script)[namelen] = '\0';
/* process digits of the weight spec */
errno = 0;
wtmp = strtol(sep + 1, &badp, 10); if (errno != 0 || badp == sep + 1 || *badp != '\0')
pg_fatal("invalid weight specification: %s", sep); if (wtmp > INT_MAX || wtmp < 0)
pg_fatal("weight specification out of range (0 .. %d): %lld",
INT_MAX, (longlong) wtmp);
weight = wtmp;
} else
{
*script = pg_strdup(option);
weight = 1;
}
return weight;
}
/* append a script to the list of scripts to process */ staticvoid
addScript(const ParsedScript *script)
{ if (script->commands == NULL || script->commands[0] == NULL)
pg_fatal("empty command list for script \"%s\"", script->desc);
if (num_scripts >= MAX_SCRIPTS)
pg_fatal("at most %d SQL scripts are allowed", MAX_SCRIPTS);
if (progress_timestamp)
{
snprintf(tbuf, sizeof(tbuf), "%.3f s",
PG_TIME_GET_DOUBLE(now + epoch_shift));
} else
{ /* round seconds are expected, but the thread may be late */
snprintf(tbuf, sizeof(tbuf), "%.1f s", total_run);
}
fprintf(stderr, "progress: %s, %.1f tps, lat %.3f ms stddev %.3f, " INT64_FORMAT " failed",
tbuf, tps, latency, stdev, failures);
if (throttle_delay)
{
fprintf(stderr, ", lag %.3f ms", lag); if (latency_limit)
fprintf(stderr, ", " INT64_FORMAT " skipped",
cur.skipped - last->skipped);
}
/* it can be non-zero only if max_tries is not equal to one */ if (max_tries != 1)
fprintf(stderr, ", " INT64_FORMAT " retried, " INT64_FORMAT " retries",
retried, cur.retries - last->retries);
fprintf(stderr, "\n");
/* print version banner */ staticvoid
printVersion(PGconn *con)
{ int server_ver = PQserverVersion(con); int client_ver = PG_VERSION_NUM;
if (server_ver != client_ver)
{ constchar *server_version; char sverbuf[32];
/* Try to get full text form, might include "devel" etc */
server_version = PQparameterStatus(con, "server_version"); /* Otherwise fall back on server_ver */ if (!server_version)
{
formatPGVersionNumber(server_ver, true,
sverbuf, sizeof(sverbuf));
server_version = sverbuf;
}
printf(_("%s (%s, server %s)\n"), "pgbench", PG_VERSION, server_version);
} /* For version match, only print pgbench version */ else
printf("%s (%s)\n", "pgbench", PG_VERSION);
fflush(stdout);
}
/* it can be non-zero only if max_tries is not equal to one */ if (max_tries != 1)
{
printf("number of transactions retried: " INT64_FORMAT " (%.3f%%)\n",
total->retried, 100.0 * total->retried / total_cnt);
printf("total number of retries: " INT64_FORMAT "\n", total->retries);
}
if (throttle_delay && latency_limit)
printf("number of transactions skipped: " INT64_FORMAT " (%.3f%%)\n",
total->skipped, 100.0 * total->skipped / total_cnt);
if (latency_limit)
printf("number of transactions above the %.1f ms latency limit: " INT64_FORMAT "/" INT64_FORMAT " (%.3f%%)\n",
latency_limit / 1000.0, latency_late, total->cnt,
(total->cnt > 0) ? 100.0 * latency_late / total->cnt : 0.0);
if (throttle_delay || progress || latency_limit)
printSimpleStats("latency", &total->latency); else
{ /* no measurement, show average latency computed from run time */
printf("latency average = %.3f ms%s\n", 0.001 * total_duration * nclients / total_cnt,
failures > 0 ? " (including failures)" : "");
}
if (seed == NULL || strcmp(seed, "time") == 0)
{ /* rely on current time */
iseed = pg_time_now();
} elseif (strcmp(seed, "rand") == 0)
{ /* use some "strong" random source */ if (!pg_strong_random(&iseed, sizeof(iseed)))
{
pg_log_error("could not generate random seed"); returnfalse;
}
} else
{ char garbage;
if (sscanf(seed, "%" SCNu64 "%c", &iseed, &garbage) != 1)
{
pg_log_error("unrecognized random seed option \"%s\"", seed);
pg_log_error_detail("Expecting an unsigned integer, \"time\" or \"rand\"."); returnfalse;
}
}
if (seed != NULL)
pg_log_info("setting random seed to %" PRIu64, iseed);
random_seed = iseed;
/* Initialize base_random_sequence using seed */
pg_prng_seed(&base_random_sequence, iseed);
CState *state; /* status of clients */
TState *threads; /* array of thread */
pg_time_usec_t
start_time, /* start up time */
bench_start = 0, /* first recorded benchmarking time */
conn_total_duration; /* cumulated connection time in
* threads */
int64 latency_late = 0;
StatsData stats; int weight;
/* set random seed early, because it may be used while parsing scripts. */ if (!set_random_seed(getenv("PGBENCH_RANDOM_SEED")))
pg_fatal("error while setting random seed from PGBENCH_RANDOM_SEED environment variable");
if (rlim.rlim_max < nclients + 3)
{
pg_log_error("need at least %d open files, but system limit is %ld",
nclients + 3, (long) rlim.rlim_max);
pg_log_error_hint("Reduce number of clients, or use limit/ulimit to increase the system limit."); exit(1);
}
if (rlim.rlim_cur < nclients + 3)
{
rlim.rlim_cur = nclients + 3; if (setrlimit(RLIMIT_NOFILE, &rlim) == -1)
{
pg_log_error("need at least %d open files, but couldn't raise the limit: %m",
nclients + 3);
pg_log_error_hint("Reduce number of clients, or use limit/ulimit to increase the system limit."); exit(1);
}
} #endif/* HAVE_GETRLIMIT */ break; case'C':
benchmarking_option_set = true;
is_connect = true; break; case'd':
dbName = pg_strdup(optarg); break; case'D':
{ char *p;
if (optind < argc)
{
pg_log_error("too many command-line arguments (first is \"%s\")",
argv[optind]);
pg_log_error_hint("Try \"%s --help\" for more information.", progname); exit(1);
}
if (is_init_mode)
{ if (benchmarking_option_set)
pg_fatal("some of the specified options cannot be used in initialization (-i) mode");
if (partitions == 0 && partition_method != PART_NONE)
pg_fatal("--partition-method requires greater than zero --partitions");
/* set default method */ if (partitions > 0 && partition_method == PART_NONE)
partition_method = PART_RANGE;
if (initialize_steps == NULL)
initialize_steps = pg_strdup(DEFAULT_INIT_STEPS);
if (is_no_vacuum)
{ /* Remove any vacuum step in initialize_steps */ char *p;
if (foreign_keys)
{ /* Add 'f' to end of initialize_steps, if not already there */ if (strchr(initialize_steps, 'f') == NULL)
{
initialize_steps = (char *)
pg_realloc(initialize_steps,
strlen(initialize_steps) + 2);
strcat(initialize_steps, "f");
}
}
runInitSteps(initialize_steps); exit(0);
} else
{ if (initialization_option_set)
pg_fatal("some of the specified options cannot be used in benchmarking mode");
}
if (nxacts > 0 && duration > 0)
pg_fatal("specify either a number of transactions (-t) or a duration (-T), not both");
/* Use DEFAULT_NXACTS if neither nxacts nor duration is specified. */ if (nxacts <= 0 && duration <= 0)
nxacts = DEFAULT_NXACTS;
/* --sampling-rate may be used only with -l */ if (sample_rate > 0.0 && !use_log)
pg_fatal("log sampling (--sampling-rate) is allowed only when logging transactions (-l)");
/* --sampling-rate may not be used with --aggregate-interval */ if (sample_rate > 0.0 && agg_interval > 0)
pg_fatal("log sampling (--sampling-rate) and aggregation (--aggregate-interval) cannot be used at the same time");
if (agg_interval > 0 && !use_log)
pg_fatal("log aggregation is allowed only when actually logging transactions");
if (!use_log && logfile_prefix)
pg_fatal("log file prefix (--log-prefix) is allowed only when logging transactions (-l)");
if (duration > 0 && agg_interval > duration)
pg_fatal("number of seconds for aggregation (%d) must not be higher than test duration (%d)", agg_interval, duration);
if (duration > 0 && agg_interval > 0 && duration % agg_interval != 0)
pg_fatal("duration (%d) must be a multiple of aggregation interval (%d)", duration, agg_interval);
if (progress_timestamp && progress == 0)
pg_fatal("--progress-timestamp is allowed only under --progress");
if (!max_tries)
{ if (!latency_limit && duration <= 0)
pg_fatal("an unlimited number of transaction tries can only be used with --latency-limit or a duration (-T)");
}
if (var->value.type != PGBT_NO_VALUE)
{ if (!putVariableValue(&state[i].variables, "startup",
var->name, &var->value)) exit(1);
} else
{ if (!putVariable(&state[i].variables, "startup",
var->name, var->svalue)) exit(1);
}
}
}
}
/* other CState initializations */ for (i = 0; i < nclients; i++)
{
state[i].cstack = conditional_stack_create();
initRandomState(&state[i].cs_func_rs);
}
/* opening connection... */
con = doConnect(); if (con == NULL)
pg_fatal("could not create connection for setup");
/* report pgbench and server versions */
printVersion(con);
if (internal_script_used)
GetTableInfo(con, scale_given);
/* *:scalevariablesnormallyget-sordatabasescale,butdon'toverride *anexplicit-Dswitch
*/ if (lookupVariable(&state[0].variables, "scale") == NULL)
{ for (i = 0; i < nclients; i++)
{ if (!putVariableInt(&state[i].variables, "startup", "scale", scale)) exit(1);
}
}
/* *Definea:client_idvariablethatisuniqueperconnection.Butdon't *overrideanexplicit-Dswitch.
*/ if (lookupVariable(&state[0].variables, "client_id") == NULL)
{ for (i = 0; i < nclients; i++) if (!putVariableInt(&state[i].variables, "startup", "client_id", i)) exit(1);
}
/* set default seed for hash functions */ if (lookupVariable(&state[0].variables, "default_seed") == NULL)
{
uint64 seed = pg_prng_uint64(&base_random_sequence);
for (i = 0; i < nclients; i++) if (!putVariableInt(&state[i].variables, "startup", "default_seed",
(int64) seed)) exit(1);
}
/* set random seed unless overwritten */ if (lookupVariable(&state[0].variables, "random_seed") == NULL)
{ for (i = 0; i < nclients; i++) if (!putVariableInt(&state[i].variables, "startup", "random_seed",
random_seed)) exit(1);
}
/* STEADY */ if (!is_connect)
{ /* make connections to the database before starting */ for (int i = 0; i < nstate; i++)
{ if ((state[i].con = doConnect()) == NULL)
{ /* coldly abort on initial connection failure */
pg_fatal("could not create connection for client %d",
state[i].id);
}
}
}
/* loop till all clients have terminated */ while (remains > 0)
{ int nsocks; /* number of sockets to be waited for */
pg_time_usec_t min_usec;
pg_time_usec_t now = 0; /* set this only if needed */
/* *identifywhichclientsocketsshouldbecheckedforinput,and *computethenearesttime(ifany)atwhichweneedtowakeup.
*/
clear_socket_set(sockets);
nsocks = 0;
min_usec = PG_INT64_MAX; for (int i = 0; i < nstate; i++)
{
CState *st = &state[i];
if (st->state == CSTATE_SLEEP || st->state == CSTATE_THROTTLE)
{ /* a nap from the script, or under throttling */
pg_time_usec_t this_usec;
/* get current time if needed */
pg_time_now_lazy(&now);
/* min_usec should be the minimum delay across all clients */
this_usec = (st->state == CSTATE_SLEEP ?
st->sleep_until : st->txn_scheduled) - now; if (min_usec > this_usec)
min_usec = this_usec;
} elseif (st->state == CSTATE_WAIT_RESULT ||
st->state == CSTATE_WAIT_ROLLBACK_RESULT)
{ /* *waitingforresultfromserver-nothingtodounlessthe *socketisreadable
*/ int sock = PQsocket(st->con);
/* *Ifnoclientsarereadytoexecuteactions,sleepuntilwereceive *dataonsomeclientsocketorthetimeout(ifany)elapses.
*/ if (min_usec > 0)
{ int rc = 0;
if (min_usec != PG_INT64_MAX)
{ if (nsocks > 0)
{
rc = wait_on_socket_set(sockets, min_usec);
} else/* nothing active, simple sleep */
{
pg_usleep(min_usec);
}
} else/* no explicit delay, wait without timeout */
{
rc = wait_on_socket_set(sockets, 0);
}
if (rc < 0)
{ if (errno == EINTR)
{ /* On EINTR, go back to top of loop */ continue;
} /* must be something wrong */
pg_log_error("%s() failed: %m", SOCKET_WAIT_METHOD); goto done;
}
} else
{ /* min_usec <= 0, i.e. something needs to be executed now */
/* If we didn't wait, don't try to read any data */
clear_socket_set(sockets);
}
/* ok, advance the state machine of each connection */
nsocks = 0; for (int i = 0; i < nstate; i++)
{
CState *st = &state[i];
if (st->state == CSTATE_WAIT_RESULT ||
st->state == CSTATE_WAIT_ROLLBACK_RESULT)
{ /* don't call advanceConnectionState unless data is available */ int sock = PQsocket(st->con);
if (!socket_has_input(sockets, sock, nsocks++)) continue;
} elseif (st->state == CSTATE_FINISHED ||
st->state == CSTATE_ABORTED)
{ /* this client is done, no need to consider it anymore */ continue;
}
/* *Ensurethatthenextreportisinthefuture,incase *pgbench/postgresgotstucksomewhere.
*/ do
{
next_report += (int64) 1000000 * progress;
} while (now2 >= next_report);
}
}
}
done: if (exit_on_abort)
{ /* *Abortifanyclientisnotfinished,meaningsomeerroroccurred.
*/ for (int i = 0; i < nstate; i++)
{ if (state[i].state != CSTATE_FINISHED)
{
pg_log_error("Run was aborted due to an error in thread %d",
thread->tid); exit(2);
}
}
}
disconnect_all(state, nstate);
if (thread->logfile)
{ if (agg_interval > 0)
{ /* log aggregated but not yet reported transactions */
doLog(thread, state, &aggs, false, 0, 0);
}
fclose(thread->logfile);
thread->logfile = NULL;
}
free_socket_set(sockets);
THREAD_FUNC_RETURN;
}
/* This function will be called at most once, so we can cheat a bit. */
queue = CreateTimerQueue(); if (seconds > ((DWORD) -1) / 1000 ||
!CreateTimerQueueTimer(&timer, queue,
win32_timer_callback, NULL, seconds * 1000, 0,
WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE))
pg_fatal("failed to set timer");
}
staticvoid
add_socket_to_set(socket_set *sa, int fd, int idx)
{ /* See connect_slot() for background on this code. */ #ifdef WIN32 if (sa->fds.fd_count + 1 >= FD_SETSIZE)
{
pg_log_error("too many concurrent database clients for this platform: %d",
sa->fds.fd_count + 1); exit(1);
} #else if (fd < 0 || fd >= FD_SETSIZE)
{
pg_log_error("socket file descriptor out of range for select(): %d",
fd);
pg_log_error_hint("Try fewer concurrent database clients."); exit(1);
} #endif
FD_SET(fd, &sa->fds); if (fd > sa->maxfd)
sa->maxfd = fd;
}
staticbool
socket_has_input(socket_set *sa, int fd, int idx)
{ return (FD_ISSET(fd, &sa->fds) != 0);
}
#endif/* POLL_USING_SELECT */
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.644Bemerkung:
(vorverarbeitet am 2026-08-07)
¤