PostgreSQL Source Code: src/test/isolation/isolationtester.c File Reference (original) (raw)

Go to the source code of this file.

Macros
#define PREP_WAITING "isolationtester_waiting"
#define STEP_NONBLOCK 0x1 /* return as soon as cmd waits for a lock */
#define STEP_RETRY 0x2 /* this is a retry of a previously-waiting cmd */
Functions
static void check_testspec (TestSpec *testspec)
static void run_testspec (TestSpec *testspec)
static void run_all_permutations (TestSpec *testspec)
static void run_all_permutations_recurse (TestSpec *testspec, int *piles, int nsteps, PermutationStep **steps)
static void run_named_permutations (TestSpec *testspec)
static void run_permutation (TestSpec *testspec, int nsteps, PermutationStep **steps)
static int try_complete_steps (TestSpec *testspec, PermutationStep **waiting, int nwaiting, int flags)
static bool try_complete_step (TestSpec *testspec, PermutationStep *pstep, int flags)
static int step_qsort_cmp (const void *a, const void *b)
static int step_bsearch_cmp (const void *a, const void *b)
static bool step_has_blocker (PermutationStep *pstep)
static void printResultSet (PGresult *res)
static void isotesterNoticeProcessor (void *arg, const char *message)
static void blackholeNoticeProcessor (void *arg, const char *message)
static void disconnect_atexit (void)
int main (int argc, char **argv)
Variables
static IsoConnInfo * conns = NULL
static int nconns = 0
static bool any_new_notice = false
static int64 max_step_wait = 360 * USECS_PER_SEC

PREP_WAITING

#define PREP_WAITING "isolationtester_waiting"

STEP_NONBLOCK

#define STEP_NONBLOCK 0x1 /* return as soon as cmd waits for a lock */

STEP_RETRY

#define STEP_RETRY 0x2 /* this is a retry of a previously-waiting cmd */

IsoConnInfo

blackholeNoticeProcessor()

static void blackholeNoticeProcessor ( void * arg, const char * message ) static

check_testspec()

static void check_testspec ( TestSpec * testspec) static

Definition at line 252 of file isolationtester.c.

253{

254 int nallsteps;

255 Step **allsteps;

256 int i,

257 j,

258 k;

259

260

261 nallsteps = 0;

264

265 allsteps = pg_malloc(nallsteps * sizeof(Step *));

266

267 k = 0;

269 {

272 }

273

275

276

277 for (i = 1; i < nallsteps; i++)

278 {

279 if (strcmp(allsteps[i - 1]->name,

280 allsteps[i]->name) == 0)

281 {

282 fprintf(stderr, "duplicate step name: %s\n",

283 allsteps[i]->name);

284 exit(1);

285 }

286 }

287

288

290 {

292

293 for (j = 0; j < session->nsteps; j++)

295 }

296

297

298

299

300

302 {

304

306 {

309 allsteps,

310 nallsteps,

311 sizeof(Step *),

313

314 if (this == NULL)

315 {

316 fprintf(stderr, "undefined step \"%s\" specified in permutation\n",

317 pstep->name);

318 exit(1);

319 }

320 pstep->step = *this;

321

322

324 }

325

326

327

328

329

330

331

333 {

335

336 for (k = 0; k < pstep->nblockers; k++)

337 {

339 int n;

340

342 continue;

343

344 blocker->step = NULL;

345 for (n = 0; n < p->nsteps; n++)

346 {

348

349 if (strcmp(otherp->name, blocker->stepname) == 0)

350 {

351 blocker->step = otherp->step;

352 break;

353 }

354 }

355 if (blocker->step == NULL)

356 {

357 fprintf(stderr, "undefined blocking step \"%s\" referenced in permutation step \"%s\"\n",

359 exit(1);

360 }

361

363 {

364 fprintf(stderr, "permutation step \"%s\" cannot block on its own session\n",

365 pstep->name);

366 exit(1);

367 }

368 }

369 }

370 }

371

372

373

374

375

376

378 {

379 for (i = 0; i < nallsteps; i++)

380 {

381 if (!allsteps[i]->used)

382 fprintf(stderr, "unused step name: %s\n", allsteps[i]->name);

383 }

384 }

385

386 free(allsteps);

387}

#define fprintf(file, fmt, msg)

void * pg_malloc(size_t size)

static int step_bsearch_cmp(const void *a, const void *b)

static int step_qsort_cmp(const void *a, const void *b)

#define qsort(a, b, c, d)

PermutationStepBlockerType blocktype

PermutationStepBlocker ** blockers

Permutation ** permutations

References PermutationStep::blockers, PermutationStepBlocker::blocktype, fprintf, free, i, j, name, PermutationStep::name, PermutationStep::nblockers, TestSpec::npermutations, TestSpec::nsessions, Session::nsteps, Permutation::nsteps, TestSpec::permutations, pg_malloc(), PSB_ONCE, qsort, Step::session, TestSpec::sessions, PermutationStepBlocker::step, PermutationStep::step, step_bsearch_cmp(), step_qsort_cmp(), PermutationStepBlocker::stepname, Session::steps, Permutation::steps, and Step::used.

Referenced by main().

disconnect_atexit()

static void disconnect_atexit ( void ) static

isotesterNoticeProcessor()

static void isotesterNoticeProcessor ( void * arg, const char * message ) static

main()

int main ( int argc,
char ** argv
)

Definition at line 87 of file isolationtester.c.

88{

89 const char *conninfo;

90 const char *env_wait;

94 int opt;

95 int i;

96

97 while ((opt = getopt(argc, argv, "V")) != -1)

98 {

99 switch (opt)

100 {

101 case 'V':

102 puts("isolationtester (PostgreSQL) " PG_VERSION);

103 exit(0);

104 default:

105 fprintf(stderr, "Usage: isolationtester [CONNINFO]\n");

107 }

108 }

109

110

111

112

113

114 setbuf(stdout, NULL);

115 setbuf(stderr, NULL);

116

117

118

119

120

121

122

124 conninfo = argv[optind];

125 else

126 conninfo = "dbname = postgres";

127

128

129

130

131

132 env_wait = getenv("PG_TEST_TIMEOUT_DEFAULT");

133 if (env_wait != NULL)

135

136

139

140

142

143 printf("Parsed test spec with %d sessions\n", testspec->nsessions);

144

145

146

147

148

152

154 {

155 const char *sessionname;

156

157 if (i == 0)

158 sessionname = "control connection";

159 else

161

163

166 {

167 fprintf(stderr, "Connection %d failed: %s",

169 exit(1);

170 }

171

172

173

174

175

176

177

178 if (i != 0)

182 else

185 NULL);

186

187

188

189

190

191

192

194 "SELECT set_config('application_name',\n"

195 " current_setting('application_name') || '/' || $1,\n"

196 " false)",

197 1, NULL,

198 &sessionname,

199 NULL, NULL, 0);

201 {

202 fprintf(stderr, "setting of application name failed: %s",

204 exit(1);

205 }

206

207

210 }

211

212

213

214

215

216

217

218

219

222 "SELECT pg_catalog.pg_isolation_test_session_is_blocked($1, '{");

223

228

231 {

232 fprintf(stderr, "prepare of lock wait query failed: %s",

234 exit(1);

235 }

238

239

240

241

242

244

245 return 0;

246}

PGconn * PQconnectdb(const char *conninfo)

ConnStatusType PQstatus(const PGconn *conn)

int PQbackendPID(const PGconn *conn)

PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)

char * PQerrorMessage(const PGconn *conn)

PGresult * PQexecParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)

ExecStatusType PQresultStatus(const PGresult *res)

void PQclear(PGresult *res)

PGresult * PQprepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes)

void * pg_malloc0(size_t size)

static void run_testspec(TestSpec *testspec)

static void blackholeNoticeProcessor(void *arg, const char *message)

static void check_testspec(TestSpec *testspec)

static void isotesterNoticeProcessor(void *arg, const char *message)

static int64 max_step_wait

static void disconnect_atexit(void)

int getopt(int nargc, char *const *nargv, const char *ostr)

void initPQExpBuffer(PQExpBuffer str)

void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)

void appendPQExpBufferStr(PQExpBuffer str, const char *data)

void termPQExpBuffer(PQExpBuffer str)

char * psprintf(const char *fmt,...)

const char * backend_pid_str

References appendPQExpBuffer(), appendPQExpBufferStr(), IsoConnInfo::backend_pid, IsoConnInfo::backend_pid_str, blackholeNoticeProcessor(), check_testspec(), conn, IsoConnInfo::conn, CONNECTION_OK, conns, PQExpBufferData::data, disconnect_atexit(), EXIT_FAILURE, fprintf, getopt(), i, initPQExpBuffer(), isotesterNoticeProcessor(), max_step_wait, Session::name, nconns, TestSpec::nsessions, optind, parseresult, pg_malloc0(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQbackendPID(), PQclear(), PQconnectdb(), PQerrorMessage(), PQexecParams(), PQprepare(), PQresultStatus(), PQsetNoticeProcessor(), PQstatus(), PREP_WAITING, printf, psprintf(), run_testspec(), IsoConnInfo::sessionname, TestSpec::sessions, spec_yyparse(), generate_unaccent_rules::stdout, termPQExpBuffer(), and USECS_PER_SEC.

printResultSet()

static void printResultSet ( PGresult * res) static

run_all_permutations()

static void run_all_permutations ( TestSpec * testspec) static

Definition at line 406 of file isolationtester.c.

407{

408 int nsteps;

409 int i;

412 int *piles;

413

414

415 nsteps = 0;

418

419

422 for (i = 0; i < nsteps; i++)

423 stepptrs[i] = steps + i;

424

425

426

427

428

429

430

431

432

433

436 piles[i] = 0;

437

439

441 free(stepptrs);

443}

static void run_all_permutations_recurse(TestSpec *testspec, int *piles, int nsteps, PermutationStep **steps)

References free, i, TestSpec::nsessions, Session::nsteps, pg_malloc(), pg_malloc0(), run_all_permutations_recurse(), and TestSpec::sessions.

Referenced by run_testspec().

run_all_permutations_recurse()

static void run_all_permutations_recurse ( TestSpec * testspec, int * piles, int nsteps, PermutationStep ** steps ) static

Definition at line 446 of file isolationtester.c.

448{

449 int i;

450 bool found = false;

451

453 {

454

456 {

458

459

460

461

462

463

464 steps[nsteps]->name = newstep->name;

465 steps[nsteps]->step = newstep;

466

467 piles[i]++;

468

470

471 piles[i]--;

472

473 found = true;

474 }

475 }

476

477

478 if (!found)

480}

static void run_permutation(TestSpec *testspec, int nsteps, PermutationStep **steps)

References i, Step::name, PermutationStep::name, TestSpec::nsessions, Session::nsteps, run_all_permutations_recurse(), run_permutation(), TestSpec::sessions, PermutationStep::step, and Session::steps.

Referenced by run_all_permutations(), and run_all_permutations_recurse().

run_named_permutations()

static void run_named_permutations ( TestSpec * testspec) static

run_permutation()

Definition at line 520 of file isolationtester.c.

521{

523 int i;

524 int nwaiting = 0;

526

528

529 printf("\nstarting permutation:");

530 for (i = 0; i < nsteps; i++)

533

534

536 {

539 {

541 }

543 {

545 exit(1);

546 }

548 }

549

550

552 {

554 {

557 {

559 }

561 {

562 fprintf(stderr, "setup of session %s failed: %s",

563 conns[i + 1].sessionname,

565 exit(1);

566 }

568 }

569 }

570

571

572 for (i = 0; i < nsteps; i++)

573 {

578 bool mustwait;

579 int j;

580

581

582

583

584

586 {

588

590

592 {

594

595

596

597

598

599

601 {

602

603 int w;

604

605 for (w = 0; w < nwaiting; w++)

606 {

607 if (oldstep == waiting[w])

608 break;

609 }

610 if (w >= nwaiting)

611 abort();

612 if (w + 1 < nwaiting)

615 nwaiting--;

616 }

617

618

619

620

621

622

623

624

627

628

629

630

631

632

633

634

635

637 {

638 struct timeval current_time;

640

646 {

647 fprintf(stderr, "step %s timed out after %d seconds\n",

650 fprintf(stderr, "active steps are:");

652 {

654

658 }

660 exit(1);

661 }

662 }

663 }

664 }

665

666

668 {

669 fprintf(stdout, "failed to send query for step %s: %s\n",

671 exit(1);

672 }

673

674

676

677

679 {

681

685 }

686

687

689

690

693

694

695 if (mustwait)

696 waiting[nwaiting++] = pstep;

697 }

698

699

701 if (nwaiting != 0)

702 {

703 fprintf(stderr, "failed to complete permutation due to mutually-blocking steps\n");

704 exit(1);

705 }

706

707

709 {

711 {

714 {

716 }

718 {

719 fprintf(stderr, "teardown of session %s failed: %s",

720 conns[i + 1].sessionname,

722

723 }

725 }

726 }

727

728

730 {

733 {

735 }

737 {

738 fprintf(stderr, "teardown failed: %s",

740

741 }

743 }

744

746}

int PQsendQuery(PGconn *conn, const char *query)

PGresult * PQexec(PGconn *conn, const char *query)

static void printResultSet(PGresult *res)

static int try_complete_steps(TestSpec *testspec, PermutationStep **waiting, int nwaiting, int flags)

static bool try_complete_step(TestSpec *testspec, PermutationStep *pstep, int flags)

PermutationStep * active_step

static volatile sig_atomic_t waiting

int gettimeofday(struct timeval *tp, void *tzp)

References IsoConnInfo::active_step, PermutationStep::blockers, PermutationStepBlocker::blocktype, conn, IsoConnInfo::conn, conns, fprintf, free, gettimeofday(), i, j, max_step_wait, name, Step::name, PermutationStep::name, PermutationStep::nblockers, nconns, TestSpec::nsessions, TestSpec::nsetupsqls, PermutationStepBlocker::num_notices, pg_malloc(), PGRES_COMMAND_OK, PGRES_TUPLES_OK, PQclear(), PQerrorMessage(), PQexec(), PQresultStatus(), PQsendQuery(), printf, printResultSet(), PSB_NUM_NOTICES, Step::session, TestSpec::sessions, Session::setupsql, TestSpec::setupsqls, Step::sql, start_time, generate_unaccent_rules::stdout, PermutationStepBlocker::step, PermutationStep::step, STEP_NONBLOCK, STEP_RETRY, PermutationStepBlocker::target_notices, Session::teardownsql, TestSpec::teardownsql, IsoConnInfo::total_notices, try_complete_step(), try_complete_steps(), USECS_PER_SEC, and waiting.

Referenced by run_all_permutations_recurse(), and run_named_permutations().

run_testspec()

static void run_testspec ( TestSpec * testspec) static

step_bsearch_cmp()

static int step_bsearch_cmp ( const void * a, const void * b ) static

step_has_blocker()

Definition at line 1080 of file isolationtester.c.

1081{

1082 int i;

1083

1085 {

1088

1090 {

1092

1093 break;

1095

1099 return true;

1100 break;

1102

1105 return true;

1106 break;

1107 }

1108 }

1109 return false;

1110}

References IsoConnInfo::active_step, PermutationStep::blockers, PermutationStepBlocker::blocktype, conns, i, PermutationStep::nblockers, PSB_NUM_NOTICES, PSB_ONCE, PSB_OTHER_STEP, Step::session, PermutationStepBlocker::step, PermutationStep::step, PermutationStepBlocker::target_notices, and IsoConnInfo::total_notices.

Referenced by try_complete_step().

step_qsort_cmp()

static int step_qsort_cmp ( const void * a, const void * b ) static

try_complete_step()

Definition at line 818 of file isolationtester.c.

819{

823 fd_set read_set;

825 struct timeval timeout;

827 int ret;

830 bool canceled = false;

831

832

833

834

835

836

838 {

839 int i;

840

842 {

844

846 {

847 printf("step %s: %s <waiting ...>\n",

849 return true;

850 }

851 }

852 }

853

854 if (sock < 0)

855 {

857 exit(1);

858 }

859

861 FD_ZERO(&read_set);

862

864 {

865 FD_SET(sock, &read_set);

866 timeout.tv_sec = 0;

867 timeout.tv_usec = 10000;

868

869 ret = select(sock + 1, &read_set, NULL, NULL, &timeout);

870 if (ret < 0)

871 {

872 if (errno == EINTR)

873 continue;

874 fprintf(stderr, "select failed: %m\n");

875 exit(1);

876 }

877 else if (ret == 0)

878 {

879 struct timeval current_time;

881

882

884 {

886

889 NULL, NULL, 0);

892 {

893 fprintf(stderr, "lock wait query failed: %s",

895 exit(1);

896 }

899

900 if (waiting)

901 {

902

903

904

905

906

907

908

909

910

912 {

913 fprintf(stderr, "PQconsumeInput failed: %s\n",

915 exit(1);

916 }

918 break;

919

920

921

922

923

925 printf("step %s: %s <waiting ...>\n",

927 return true;

928 }

929

930 }

931

932

937

938

939

940

941

942

943

944

945

946

948 {

950

952 {

953

954

955

956

957 printf("isolationtester: canceling step %s after %d seconds\n",

959 canceled = true;

960 }

961 else

964 }

965

966

967

968

969

970

971

972

974 {

975 fprintf(stderr, "step %s timed out after %d seconds\n",

977 exit(1);

978 }

979 }

981 {

982 fprintf(stderr, "PQconsumeInput failed: %s\n",

984 exit(1);

985 }

986 }

987

988

989

990

991

993 {

995 printf("step %s: %s <waiting ...>\n",

997 return true;

998 }

999

1000

1002 printf("step %s: <... completed>\n", step->name);

1003 else

1005

1007 {

1009 {

1012 break;

1015 break;

1017

1018

1019

1020

1021

1022

1023 {

1028

1029 if (sev && msg)

1030 printf("%s: %s\n", sev, msg);

1031 else

1033 }

1034 break;

1035 default:

1036 printf("unexpected result status: %s\n",

1038 }

1040 }

1041

1042

1045 {

1046

1047 const char *sendername = NULL;

1048 char pidstring[32];

1049 int i;

1050

1052 {

1054 {

1056 break;

1057 }

1058 }

1059 if (sendername == NULL)

1060 {

1061

1062 snprintf(pidstring, sizeof(pidstring), "PID %d", notify->be_pid);

1063 sendername = pidstring;

1064 }

1065 printf("%s: NOTIFY \"%s\" with payload \"%s\" from %s\n",

1067 notify->relname, notify->extra, sendername);

1070 }

1071

1072

1074

1075 return false;

1076}

PGcancelConn * PQcancelCreate(PGconn *conn)

int PQcancelBlocking(PGcancelConn *cancelConn)

void PQcancelFinish(PGcancelConn *cancelConn)

char * PQcancelErrorMessage(const PGcancelConn *cancelConn)

int PQsocket(const PGconn *conn)

void PQfreemem(void *ptr)

PGresult * PQexecPrepared(PGconn *conn, const char *stmtName, int nParams, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)

char * PQgetvalue(const PGresult *res, int tup_num, int field_num)

PGresult * PQgetResult(PGconn *conn)

int PQntuples(const PGresult *res)

char * PQresultErrorMessage(const PGresult *res)

int PQconsumeInput(PGconn *conn)

char * PQresultErrorField(const PGresult *res, int fieldcode)

char * PQresStatus(ExecStatusType status)

int PQisBusy(PGconn *conn)

PGnotify * PQnotifies(PGconn *conn)

static bool step_has_blocker(PermutationStep *pstep)

#define PG_DIAG_MESSAGE_PRIMARY

#define select(n, r, w, e, timeout)

References IsoConnInfo::active_step, IsoConnInfo::backend_pid, IsoConnInfo::backend_pid_str, pgNotify::be_pid, PermutationStep::blockers, PermutationStepBlocker::blocktype, conn, IsoConnInfo::conn, conns, EINTR, pgNotify::extra, fprintf, gettimeofday(), i, max_step_wait, Session::name, Step::name, PermutationStep::nblockers, TestSpec::nsessions, PG_DIAG_MESSAGE_PRIMARY, PG_DIAG_SEVERITY, PGRES_COMMAND_OK, PGRES_EMPTY_QUERY, PGRES_FATAL_ERROR, PGRES_TUPLES_OK, PQcancelBlocking(), PQcancelCreate(), PQcancelErrorMessage(), PQcancelFinish(), PQclear(), PQconsumeInput(), PQerrorMessage(), PQexecPrepared(), PQfreemem(), PQgetResult(), PQgetvalue(), PQisBusy(), PQnotifies(), PQntuples(), PQresStatus(), PQresultErrorField(), PQresultErrorMessage(), PQresultStatus(), PQsocket(), PREP_WAITING, printf, printResultSet(), PSB_ONCE, pgNotify::relname, select, Step::session, IsoConnInfo::sessionname, TestSpec::sessions, snprintf, Step::sql, start_time, PermutationStep::step, step_has_blocker(), STEP_NONBLOCK, STEP_RETRY, USECS_PER_SEC, and waiting.

Referenced by run_permutation(), and try_complete_steps().

try_complete_steps()

Definition at line 755 of file isolationtester.c.

757{

758 int old_nwaiting;

759 bool have_blocker;

760

761 do

762 {

763 int w = 0;

764

765

767

768

769 old_nwaiting = nwaiting;

770 have_blocker = false;

771

772

773 while (w < nwaiting)

774 {

776 {

777

778 if (waiting[w]->nblockers > 0)

779 have_blocker = true;

780 w++;

781 }

782 else

783 {

784

785 if (w + 1 < nwaiting)

788 nwaiting--;

789 }

790 }

791

792

793

794

795

796

797

798

799

800 } while (have_blocker && (nwaiting < old_nwaiting || any_new_notice));

801 return nwaiting;

802}

References any_new_notice, try_complete_step(), and waiting.

Referenced by run_permutation().

any_new_notice

bool any_new_notice = false static

conns

max_step_wait

nconns