diff options
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/isolation/expected/merge-match-recheck.out | 145 | ||||
-rw-r--r-- | src/test/isolation/specs/merge-match-recheck.spec | 18 | ||||
-rw-r--r-- | src/test/modules/libpq_pipeline/libpq_pipeline.c | 549 | ||||
-rw-r--r-- | src/test/modules/test_dsa/test_dsa.c | 6 | ||||
-rw-r--r-- | src/test/modules/test_dsm_registry/test_dsm_registry.c | 3 | ||||
-rw-r--r-- | src/test/modules/test_radixtree/test_radixtree.c | 9 | ||||
-rw-r--r-- | src/test/modules/test_slru/test_slru.c | 6 | ||||
-rw-r--r-- | src/test/modules/test_tidstore/test_tidstore.c | 3 | ||||
-rw-r--r-- | src/test/regress/expected/copy2.out | 1 | ||||
-rw-r--r-- | src/test/regress/expected/publication.out | 51 | ||||
-rw-r--r-- | src/test/regress/expected/subselect.out | 19 | ||||
-rw-r--r-- | src/test/regress/sql/publication.sql | 58 | ||||
-rw-r--r-- | src/test/regress/sql/subselect.sql | 9 |
13 files changed, 511 insertions, 366 deletions
diff --git a/src/test/isolation/expected/merge-match-recheck.out b/src/test/isolation/expected/merge-match-recheck.out index 90300f1db5a..4250b85af2d 100644 --- a/src/test/isolation/expected/merge-match-recheck.out +++ b/src/test/isolation/expected/merge-match-recheck.out @@ -271,6 +271,151 @@ key|balance|status|val step c1: COMMIT; +starting permutation: update1 update6 merge_bal c2 select1 c1 +step update1: UPDATE target t SET balance = balance + 10, val = t.val || ' updated by update1' WHERE t.key = 1; +step update6: UPDATE target t SET balance = balance - 100, val = t.val || ' updated by update6' WHERE t.key = 1; +step merge_bal: + MERGE INTO target t + USING (SELECT 1 as key) s + ON s.key = t.key + WHEN MATCHED AND balance < 100 THEN + UPDATE SET balance = balance * 2, val = t.val || ' when1' + WHEN MATCHED AND balance < 200 THEN + UPDATE SET balance = balance * 4, val = t.val || ' when2' + WHEN MATCHED AND balance < 300 THEN + UPDATE SET balance = balance * 8, val = t.val || ' when3'; + <waiting ...> +step c2: COMMIT; +step merge_bal: <... completed> +step select1: SELECT * FROM target; +key|balance|status|val +---+-------+------+------------------------------------------------- + 1| 140|s1 |setup updated by update1 updated by update6 when1 +(1 row) + +step c1: COMMIT; + +starting permutation: update1_pa update6_pa merge_bal_pa c2 select1_pa c1 +step update1_pa: UPDATE target_pa t SET balance = balance + 10, val = t.val || ' updated by update1_pa' WHERE t.key = 1; +step update6_pa: UPDATE target_pa t SET balance = balance - 100, val = t.val || ' updated by update6_pa' WHERE t.key = 1; +step merge_bal_pa: + MERGE INTO target_pa t + USING (SELECT 1 as key) s + ON s.key = t.key + WHEN MATCHED AND balance < 100 THEN + UPDATE SET balance = balance * 2, val = t.val || ' when1' + WHEN MATCHED AND balance < 200 THEN + UPDATE SET balance = balance * 4, val = t.val || ' when2' + WHEN MATCHED AND balance < 300 THEN + UPDATE SET balance = balance * 8, val = t.val || ' when3'; + <waiting ...> +step c2: COMMIT; +step merge_bal_pa: <... completed> +step select1_pa: SELECT * FROM target_pa; +key|balance|status|val +---+-------+------+------------------------------------------------------- + 1| 140|s1 |setup updated by update1_pa updated by update6_pa when1 +(1 row) + +step c1: COMMIT; + +starting permutation: update1_tg update6_tg merge_bal_tg c2 select1_tg c1 +s2: NOTICE: Update: (1,160,s1,setup) -> (1,170,s1,"setup updated by update1_tg") +step update1_tg: UPDATE target_tg t SET balance = balance + 10, val = t.val || ' updated by update1_tg' WHERE t.key = 1; +s2: NOTICE: Update: (1,170,s1,"setup updated by update1_tg") -> (1,70,s1,"setup updated by update1_tg updated by update6_tg") +step update6_tg: UPDATE target_tg t SET balance = balance - 100, val = t.val || ' updated by update6_tg' WHERE t.key = 1; +step merge_bal_tg: + WITH t AS ( + MERGE INTO target_tg t + USING (SELECT 1 as key) s + ON s.key = t.key + WHEN MATCHED AND balance < 100 THEN + UPDATE SET balance = balance * 2, val = t.val || ' when1' + WHEN MATCHED AND balance < 200 THEN + UPDATE SET balance = balance * 4, val = t.val || ' when2' + WHEN MATCHED AND balance < 300 THEN + UPDATE SET balance = balance * 8, val = t.val || ' when3' + RETURNING t.* + ) + SELECT * FROM t; + <waiting ...> +step c2: COMMIT; +s1: NOTICE: Update: (1,70,s1,"setup updated by update1_tg updated by update6_tg") -> (1,140,s1,"setup updated by update1_tg updated by update6_tg when1") +step merge_bal_tg: <... completed> +key|balance|status|val +---+-------+------+------------------------------------------------------- + 1| 140|s1 |setup updated by update1_tg updated by update6_tg when1 +(1 row) + +step select1_tg: SELECT * FROM target_tg; +key|balance|status|val +---+-------+------+------------------------------------------------------- + 1| 140|s1 |setup updated by update1_tg updated by update6_tg when1 +(1 row) + +step c1: COMMIT; + +starting permutation: update7 update6 merge_bal c2 select1 c1 +step update7: UPDATE target t SET balance = 350, val = t.val || ' updated by update7' WHERE t.key = 1; +step update6: UPDATE target t SET balance = balance - 100, val = t.val || ' updated by update6' WHERE t.key = 1; +step merge_bal: + MERGE INTO target t + USING (SELECT 1 as key) s + ON s.key = t.key + WHEN MATCHED AND balance < 100 THEN + UPDATE SET balance = balance * 2, val = t.val || ' when1' + WHEN MATCHED AND balance < 200 THEN + UPDATE SET balance = balance * 4, val = t.val || ' when2' + WHEN MATCHED AND balance < 300 THEN + UPDATE SET balance = balance * 8, val = t.val || ' when3'; + <waiting ...> +step c2: COMMIT; +step merge_bal: <... completed> +step select1: SELECT * FROM target; +key|balance|status|val +---+-------+------+------------------------------------------------- + 1| 2000|s1 |setup updated by update7 updated by update6 when3 +(1 row) + +step c1: COMMIT; + +starting permutation: update1_pa_move merge_bal_pa c2 c1 +step update1_pa_move: UPDATE target_pa t SET balance = 210, val = t.val || ' updated by update1_pa_move' WHERE t.key = 1; +step merge_bal_pa: + MERGE INTO target_pa t + USING (SELECT 1 as key) s + ON s.key = t.key + WHEN MATCHED AND balance < 100 THEN + UPDATE SET balance = balance * 2, val = t.val || ' when1' + WHEN MATCHED AND balance < 200 THEN + UPDATE SET balance = balance * 4, val = t.val || ' when2' + WHEN MATCHED AND balance < 300 THEN + UPDATE SET balance = balance * 8, val = t.val || ' when3'; + <waiting ...> +step c2: COMMIT; +step merge_bal_pa: <... completed> +ERROR: tuple to be locked was already moved to another partition due to concurrent update +step c1: COMMIT; + +starting permutation: update1_pa update1_pa_move merge_bal_pa c2 c1 +step update1_pa: UPDATE target_pa t SET balance = balance + 10, val = t.val || ' updated by update1_pa' WHERE t.key = 1; +step update1_pa_move: UPDATE target_pa t SET balance = 210, val = t.val || ' updated by update1_pa_move' WHERE t.key = 1; +step merge_bal_pa: + MERGE INTO target_pa t + USING (SELECT 1 as key) s + ON s.key = t.key + WHEN MATCHED AND balance < 100 THEN + UPDATE SET balance = balance * 2, val = t.val || ' when1' + WHEN MATCHED AND balance < 200 THEN + UPDATE SET balance = balance * 4, val = t.val || ' when2' + WHEN MATCHED AND balance < 300 THEN + UPDATE SET balance = balance * 8, val = t.val || ' when3'; + <waiting ...> +step c2: COMMIT; +step merge_bal_pa: <... completed> +ERROR: tuple to be locked was already moved to another partition due to concurrent update +step c1: COMMIT; + starting permutation: update1 merge_delete c2 select1 c1 step update1: UPDATE target t SET balance = balance + 10, val = t.val || ' updated by update1' WHERE t.key = 1; step merge_delete: diff --git a/src/test/isolation/specs/merge-match-recheck.spec b/src/test/isolation/specs/merge-match-recheck.spec index 15226e40c9e..6e7a776d17e 100644 --- a/src/test/isolation/specs/merge-match-recheck.spec +++ b/src/test/isolation/specs/merge-match-recheck.spec @@ -146,6 +146,8 @@ setup BEGIN ISOLATION LEVEL READ COMMITTED; } step "update1" { UPDATE target t SET balance = balance + 10, val = t.val || ' updated by update1' WHERE t.key = 1; } +step "update1_pa" { UPDATE target_pa t SET balance = balance + 10, val = t.val || ' updated by update1_pa' WHERE t.key = 1; } +step "update1_pa_move" { UPDATE target_pa t SET balance = 210, val = t.val || ' updated by update1_pa_move' WHERE t.key = 1; } step "update1_tg" { UPDATE target_tg t SET balance = balance + 10, val = t.val || ' updated by update1_tg' WHERE t.key = 1; } step "update2" { UPDATE target t SET status = 's2', val = t.val || ' updated by update2' WHERE t.key = 1; } step "update2_tg" { UPDATE target_tg t SET status = 's2', val = t.val || ' updated by update2_tg' WHERE t.key = 1; } @@ -153,6 +155,10 @@ step "update3" { UPDATE target t SET status = 's3', val = t.val || ' updated by step "update3_tg" { UPDATE target_tg t SET status = 's3', val = t.val || ' updated by update3_tg' WHERE t.key = 1; } step "update5" { UPDATE target t SET status = 's5', val = t.val || ' updated by update5' WHERE t.key = 1; } step "update5_tg" { UPDATE target_tg t SET status = 's5', val = t.val || ' updated by update5_tg' WHERE t.key = 1; } +step "update6" { UPDATE target t SET balance = balance - 100, val = t.val || ' updated by update6' WHERE t.key = 1; } +step "update6_pa" { UPDATE target_pa t SET balance = balance - 100, val = t.val || ' updated by update6_pa' WHERE t.key = 1; } +step "update6_tg" { UPDATE target_tg t SET balance = balance - 100, val = t.val || ' updated by update6_tg' WHERE t.key = 1; } +step "update7" { UPDATE target t SET balance = 350, val = t.val || ' updated by update7' WHERE t.key = 1; } step "update_bal1" { UPDATE target t SET balance = 50, val = t.val || ' updated by update_bal1' WHERE t.key = 1; } step "update_bal1_pa" { UPDATE target_pa t SET balance = 50, val = t.val || ' updated by update_bal1_pa' WHERE t.key = 1; } step "update_bal1_tg" { UPDATE target_tg t SET balance = 50, val = t.val || ' updated by update_bal1_tg' WHERE t.key = 1; } @@ -179,6 +185,18 @@ permutation "update_bal1" "merge_bal" "c2" "select1" "c1" permutation "update_bal1_pa" "merge_bal_pa" "c2" "select1_pa" "c1" permutation "update_bal1_tg" "merge_bal_tg" "c2" "select1_tg" "c1" +# merge_bal sees row concurrently updated twice and rechecks WHEN conditions, different check passes, so final balance = 140 +permutation "update1" "update6" "merge_bal" "c2" "select1" "c1" +permutation "update1_pa" "update6_pa" "merge_bal_pa" "c2" "select1_pa" "c1" +permutation "update1_tg" "update6_tg" "merge_bal_tg" "c2" "select1_tg" "c1" + +# merge_bal sees row concurrently updated twice, first update would cause all checks to fail, second update causes different check to pass, so final balance = 2000 +permutation "update7" "update6" "merge_bal" "c2" "select1" "c1" + +# merge_bal sees concurrently updated row moved to new partition, so fails +permutation "update1_pa_move" "merge_bal_pa" "c2" "c1" +permutation "update1_pa" "update1_pa_move" "merge_bal_pa" "c2" "c1" + # merge_delete sees concurrently updated row and rechecks WHEN conditions, but recheck passes and row is deleted permutation "update1" "merge_delete" "c2" "select1" "c1" permutation "update1_tg" "merge_delete_tg" "c2" "select1_tg" "c1" diff --git a/src/test/modules/libpq_pipeline/libpq_pipeline.c b/src/test/modules/libpq_pipeline/libpq_pipeline.c index 9a3c0236325..b3af70fa09b 100644 --- a/src/test/modules/libpq_pipeline/libpq_pipeline.c +++ b/src/test/modules/libpq_pipeline/libpq_pipeline.c @@ -88,20 +88,67 @@ pg_fatal_impl(int line, const char *fmt,...) } /* - * Check that the query on the given connection got canceled. + * Check that libpq next returns a PGresult with the specified status, + * returning the PGresult so that caller can perform additional checks. */ -#define confirm_query_canceled(conn) confirm_query_canceled_impl(__LINE__, conn) -static void -confirm_query_canceled_impl(int line, PGconn *conn) +#define confirm_result_status(conn, status) confirm_result_status_impl(__LINE__, conn, status) +static PGresult * +confirm_result_status_impl(int line, PGconn *conn, ExecStatusType status) { - PGresult *res = NULL; + PGresult *res; res = PQgetResult(conn); if (res == NULL) - pg_fatal_impl(line, "PQgetResult returned null: %s", + pg_fatal_impl(line, "PQgetResult returned null unexpectedly: %s", PQerrorMessage(conn)); - if (PQresultStatus(res) != PGRES_FATAL_ERROR) - pg_fatal_impl(line, "query did not fail when it was expected"); + if (PQresultStatus(res) != status) + pg_fatal_impl(line, "PQgetResult returned status %s, expected %s: %s", + PQresStatus(PQresultStatus(res)), + PQresStatus(status), + PQerrorMessage(conn)); + return res; +} + +/* + * Check that libpq next returns a PGresult with the specified status, + * then free the PGresult. + */ +#define consume_result_status(conn, status) consume_result_status_impl(__LINE__, conn, status) +static void +consume_result_status_impl(int line, PGconn *conn, ExecStatusType status) +{ + PGresult *res; + + res = confirm_result_status_impl(line, conn, status); + PQclear(res); +} + +/* + * Check that libpq next returns a null PGresult. + */ +#define consume_null_result(conn) consume_null_result_impl(__LINE__, conn) +static void +consume_null_result_impl(int line, PGconn *conn) +{ + PGresult *res; + + res = PQgetResult(conn); + if (res != NULL) + pg_fatal_impl(line, "expected NULL PGresult, got %s: %s", + PQresStatus(PQresultStatus(res)), + PQerrorMessage(conn)); +} + +/* + * Check that the query on the given connection got canceled. + */ +#define consume_query_cancel(conn) consume_query_cancel_impl(__LINE__, conn) +static void +consume_query_cancel_impl(int line, PGconn *conn) +{ + PGresult *res; + + res = confirm_result_status_impl(line, conn, PGRES_FATAL_ERROR); if (strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), "57014") != 0) pg_fatal_impl(line, "query failed with a different error than cancellation: %s", PQerrorMessage(conn)); @@ -234,6 +281,10 @@ copy_connection(PGconn *conn) pg_fatal("Connection to database failed: %s", PQerrorMessage(copyConn)); + pfree(keywords); + pfree(vals); + PQconninfoFree(opts); + return copyConn; } @@ -265,13 +316,13 @@ test_cancel(PGconn *conn) cancel = PQgetCancel(conn); if (!PQcancel(cancel, errorbuf, sizeof(errorbuf))) pg_fatal("failed to run PQcancel: %s", errorbuf); - confirm_query_canceled(conn); + consume_query_cancel(conn); /* PGcancel object can be reused for the next query */ send_cancellable_query(conn, monitorConn); if (!PQcancel(cancel, errorbuf, sizeof(errorbuf))) pg_fatal("failed to run PQcancel: %s", errorbuf); - confirm_query_canceled(conn); + consume_query_cancel(conn); PQfreeCancel(cancel); @@ -279,14 +330,14 @@ test_cancel(PGconn *conn) send_cancellable_query(conn, monitorConn); if (!PQrequestCancel(conn)) pg_fatal("failed to run PQrequestCancel: %s", PQerrorMessage(conn)); - confirm_query_canceled(conn); + consume_query_cancel(conn); /* test PQcancelBlocking */ send_cancellable_query(conn, monitorConn); cancelConn = PQcancelCreate(conn); if (!PQcancelBlocking(cancelConn)) pg_fatal("failed to run PQcancelBlocking: %s", PQcancelErrorMessage(cancelConn)); - confirm_query_canceled(conn); + consume_query_cancel(conn); PQcancelFinish(cancelConn); /* test PQcancelCreate and then polling with PQcancelPoll */ @@ -340,7 +391,7 @@ test_cancel(PGconn *conn) } if (PQcancelStatus(cancelConn) != CONNECTION_OK) pg_fatal("unexpected cancel connection status: %s", PQcancelErrorMessage(cancelConn)); - confirm_query_canceled(conn); + consume_query_cancel(conn); /* * test PQcancelReset works on the cancel connection and it can be reused @@ -397,9 +448,10 @@ test_cancel(PGconn *conn) } if (PQcancelStatus(cancelConn) != CONNECTION_OK) pg_fatal("unexpected cancel connection status: %s", PQcancelErrorMessage(cancelConn)); - confirm_query_canceled(conn); + consume_query_cancel(conn); PQcancelFinish(cancelConn); + PQfinish(monitorConn); fprintf(stderr, "ok\n"); } @@ -428,6 +480,7 @@ test_disallowed_in_pipeline(PGconn *conn) "synchronous command execution functions are not allowed in pipeline mode\n") != 0) pg_fatal("did not get expected error message; got: \"%s\"", PQerrorMessage(conn)); + PQclear(res); /* PQsendQuery should fail in pipeline mode */ if (PQsendQuery(conn, "SELECT 1") != 0) @@ -460,6 +513,7 @@ test_disallowed_in_pipeline(PGconn *conn) if (PQresultStatus(res) != PGRES_TUPLES_OK) pg_fatal("PQexec should succeed after exiting pipeline mode but failed with: %s", PQerrorMessage(conn)); + PQclear(res); fprintf(stderr, "ok\n"); } @@ -467,7 +521,6 @@ test_disallowed_in_pipeline(PGconn *conn) static void test_multi_pipelines(PGconn *conn) { - PGresult *res = NULL; const char *dummy_params[1] = {"1"}; Oid dummy_param_oids[1] = {INT4OID}; @@ -508,87 +561,31 @@ test_multi_pipelines(PGconn *conn) /* OK, start processing the results */ /* first pipeline */ + consume_result_status(conn, PGRES_TUPLES_OK); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("PQgetResult returned null when there's a pipeline item: %s", - PQerrorMessage(conn)); - - if (PQresultStatus(res) != PGRES_TUPLES_OK) - pg_fatal("Unexpected result code %s from first pipeline item", - PQresStatus(PQresultStatus(res))); - PQclear(res); - res = NULL; - - if (PQgetResult(conn) != NULL) - pg_fatal("PQgetResult returned something extra after first result"); + consume_null_result(conn); if (PQexitPipelineMode(conn) != 0) pg_fatal("exiting pipeline mode after query but before sync succeeded incorrectly"); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("PQgetResult returned null when sync result expected: %s", - PQerrorMessage(conn)); - - if (PQresultStatus(res) != PGRES_PIPELINE_SYNC) - pg_fatal("Unexpected result code %s instead of sync result, error: %s", - PQresStatus(PQresultStatus(res)), PQerrorMessage(conn)); - PQclear(res); + consume_result_status(conn, PGRES_PIPELINE_SYNC); /* second pipeline */ + consume_result_status(conn, PGRES_TUPLES_OK); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("PQgetResult returned null when there's a pipeline item: %s", - PQerrorMessage(conn)); - - if (PQresultStatus(res) != PGRES_TUPLES_OK) - pg_fatal("Unexpected result code %s from second pipeline item", - PQresStatus(PQresultStatus(res))); - PQclear(res); - res = NULL; - - if (PQgetResult(conn) != NULL) - pg_fatal("PQgetResult returned something extra after first result"); + consume_null_result(conn); if (PQexitPipelineMode(conn) != 0) pg_fatal("exiting pipeline mode after query but before sync succeeded incorrectly"); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("PQgetResult returned null when sync result expected: %s", - PQerrorMessage(conn)); - - if (PQresultStatus(res) != PGRES_PIPELINE_SYNC) - pg_fatal("Unexpected result code %s instead of sync result, error: %s", - PQresStatus(PQresultStatus(res)), PQerrorMessage(conn)); - PQclear(res); + consume_result_status(conn, PGRES_PIPELINE_SYNC); /* third pipeline */ + consume_result_status(conn, PGRES_TUPLES_OK); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("PQgetResult returned null when there's a pipeline item: %s", - PQerrorMessage(conn)); - - if (PQresultStatus(res) != PGRES_TUPLES_OK) - pg_fatal("Unexpected result code %s from third pipeline item", - PQresStatus(PQresultStatus(res))); - - res = PQgetResult(conn); - if (res != NULL) - pg_fatal("Expected null result, got %s", - PQresStatus(PQresultStatus(res))); + consume_null_result(conn); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("PQgetResult returned null when there's a pipeline item: %s", - PQerrorMessage(conn)); - - if (PQresultStatus(res) != PGRES_PIPELINE_SYNC) - pg_fatal("Unexpected result code %s from second pipeline sync", - PQresStatus(PQresultStatus(res))); + consume_result_status(conn, PGRES_PIPELINE_SYNC); /* We're still in pipeline mode ... */ if (PQpipelineStatus(conn) == PQ_PIPELINE_OFF) @@ -657,36 +654,17 @@ test_nosync(PGconn *conn) /* Now read all results */ for (;;) { - PGresult *res; - - res = PQgetResult(conn); - - /* NULL results are only expected after TUPLES_OK */ - if (res == NULL) - pg_fatal("got unexpected NULL result after %d results", results); - /* We expect exactly one TUPLES_OK result for each query we sent */ - if (PQresultStatus(res) == PGRES_TUPLES_OK) - { - PGresult *res2; - - /* and one NULL result should follow each */ - res2 = PQgetResult(conn); - if (res2 != NULL) - pg_fatal("expected NULL, got %s", - PQresStatus(PQresultStatus(res2))); - PQclear(res); - results++; + consume_result_status(conn, PGRES_TUPLES_OK); - /* if we're done, we're done */ - if (results == numqueries) - break; + /* and one NULL result should follow each */ + consume_null_result(conn); - continue; - } + results++; - /* anything else is unexpected */ - pg_fatal("got unexpected %s\n", PQresStatus(PQresultStatus(res))); + /* if we're done, we're done */ + if (results == numqueries) + break; } fprintf(stderr, "ok\n"); @@ -716,10 +694,12 @@ test_pipeline_abort(PGconn *conn) res = PQexec(conn, drop_table_sql); if (PQresultStatus(res) != PGRES_COMMAND_OK) pg_fatal("dispatching DROP TABLE failed: %s", PQerrorMessage(conn)); + PQclear(res); res = PQexec(conn, create_table_sql); if (PQresultStatus(res) != PGRES_COMMAND_OK) pg_fatal("dispatching CREATE TABLE failed: %s", PQerrorMessage(conn)); + PQclear(res); /* * Queue up a couple of small pipelines and process each without returning @@ -763,33 +743,16 @@ test_pipeline_abort(PGconn *conn) * a pipeline aborted message for the second insert, a pipeline-end, then * a command-ok and a pipeline-ok for the second pipeline operation. */ - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("Unexpected NULL result: %s", PQerrorMessage(conn)); - if (PQresultStatus(res) != PGRES_COMMAND_OK) - pg_fatal("Unexpected result status %s: %s", - PQresStatus(PQresultStatus(res)), - PQresultErrorMessage(res)); - PQclear(res); + consume_result_status(conn, PGRES_COMMAND_OK); /* NULL result to signal end-of-results for this command */ - if ((res = PQgetResult(conn)) != NULL) - pg_fatal("Expected null result, got %s", - PQresStatus(PQresultStatus(res))); + consume_null_result(conn); /* Second query caused error, so we expect an error next */ - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("Unexpected NULL result: %s", PQerrorMessage(conn)); - if (PQresultStatus(res) != PGRES_FATAL_ERROR) - pg_fatal("Unexpected result code -- expected PGRES_FATAL_ERROR, got %s", - PQresStatus(PQresultStatus(res))); - PQclear(res); + consume_result_status(conn, PGRES_FATAL_ERROR); /* NULL result to signal end-of-results for this command */ - if ((res = PQgetResult(conn)) != NULL) - pg_fatal("Expected null result, got %s", - PQresStatus(PQresultStatus(res))); + consume_null_result(conn); /* * pipeline should now be aborted. @@ -802,17 +765,10 @@ test_pipeline_abort(PGconn *conn) pg_fatal("pipeline should be flagged as aborted but isn't"); /* third query in pipeline, the second insert */ - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("Unexpected NULL result: %s", PQerrorMessage(conn)); - if (PQresultStatus(res) != PGRES_PIPELINE_ABORTED) - pg_fatal("Unexpected result code -- expected PGRES_PIPELINE_ABORTED, got %s", - PQresStatus(PQresultStatus(res))); - PQclear(res); + consume_result_status(conn, PGRES_PIPELINE_ABORTED); /* NULL result to signal end-of-results for this command */ - if ((res = PQgetResult(conn)) != NULL) - pg_fatal("Expected null result, got %s", PQresStatus(PQresultStatus(res))); + consume_null_result(conn); if (PQpipelineStatus(conn) != PQ_PIPELINE_ABORTED) pg_fatal("pipeline should be flagged as aborted but isn't"); @@ -827,14 +783,7 @@ test_pipeline_abort(PGconn *conn) * (This is so clients know to start processing results normally again and * can tell the difference between skipped commands and the sync.) */ - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("Unexpected NULL result: %s", PQerrorMessage(conn)); - if (PQresultStatus(res) != PGRES_PIPELINE_SYNC) - pg_fatal("Unexpected result code from first pipeline sync\n" - "Expected PGRES_PIPELINE_SYNC, got %s", - PQresStatus(PQresultStatus(res))); - PQclear(res); + consume_result_status(conn, PGRES_PIPELINE_SYNC); if (PQpipelineStatus(conn) == PQ_PIPELINE_ABORTED) pg_fatal("sync should've cleared the aborted flag but didn't"); @@ -844,30 +793,16 @@ test_pipeline_abort(PGconn *conn) pg_fatal("Fell out of pipeline mode somehow"); /* the insert from the second pipeline */ - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("Unexpected NULL result: %s", PQerrorMessage(conn)); - if (PQresultStatus(res) != PGRES_COMMAND_OK) - pg_fatal("Unexpected result code %s from first item in second pipeline", - PQresStatus(PQresultStatus(res))); - PQclear(res); + consume_result_status(conn, PGRES_COMMAND_OK); /* Read the NULL result at the end of the command */ - if ((res = PQgetResult(conn)) != NULL) - pg_fatal("Expected null result, got %s", PQresStatus(PQresultStatus(res))); + consume_null_result(conn); /* the second pipeline sync */ - if ((res = PQgetResult(conn)) == NULL) - pg_fatal("Unexpected NULL result: %s", PQerrorMessage(conn)); - if (PQresultStatus(res) != PGRES_PIPELINE_SYNC) - pg_fatal("Unexpected result code %s from second pipeline sync", - PQresStatus(PQresultStatus(res))); - PQclear(res); + consume_result_status(conn, PGRES_PIPELINE_SYNC); - if ((res = PQgetResult(conn)) != NULL) - pg_fatal("Expected null result, got %s: %s", - PQresStatus(PQresultStatus(res)), - PQerrorMessage(conn)); + /* Read the NULL result at the end of the command */ + consume_null_result(conn); /* Try to send two queries in one command */ if (PQsendQueryParams(conn, "SELECT 1; SELECT 2", 0, NULL, NULL, NULL, NULL, 0) != 1) @@ -890,15 +825,14 @@ test_pipeline_abort(PGconn *conn) pg_fatal("got unexpected status %s", PQresStatus(PQresultStatus(res))); break; } + PQclear(res); } if (!goterror) pg_fatal("did not get cannot-insert-multiple-commands error"); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("got NULL result"); - if (PQresultStatus(res) != PGRES_PIPELINE_SYNC) - pg_fatal("Unexpected result code %s from pipeline sync", - PQresStatus(PQresultStatus(res))); + + /* the second pipeline sync */ + consume_result_status(conn, PGRES_PIPELINE_SYNC); + fprintf(stderr, "ok\n"); /* Test single-row mode with an error partways */ @@ -935,13 +869,9 @@ test_pipeline_abort(PGconn *conn) pg_fatal("did not get division-by-zero error"); if (gotrows != 3) pg_fatal("did not get three rows"); + /* the third pipeline sync */ - if ((res = PQgetResult(conn)) == NULL) - pg_fatal("Unexpected NULL result: %s", PQerrorMessage(conn)); - if (PQresultStatus(res) != PGRES_PIPELINE_SYNC) - pg_fatal("Unexpected result code %s from third pipeline sync", - PQresStatus(PQresultStatus(res))); - PQclear(res); + consume_result_status(conn, PGRES_PIPELINE_SYNC); /* We're still in pipeline mode... */ if (PQpipelineStatus(conn) == PQ_PIPELINE_OFF) @@ -1274,21 +1204,11 @@ test_prepared(PGconn *conn) if (PQpipelineSync(conn) != 1) pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn)); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("PQgetResult returned null"); - if (PQresultStatus(res) != PGRES_COMMAND_OK) - pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res))); - PQclear(res); - res = PQgetResult(conn); - if (res != NULL) - pg_fatal("expected NULL result"); + consume_result_status(conn, PGRES_COMMAND_OK); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("PQgetResult returned NULL"); - if (PQresultStatus(res) != PGRES_COMMAND_OK) - pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res))); + consume_null_result(conn); + + res = confirm_result_status(conn, PGRES_COMMAND_OK); if (PQnfields(res) != lengthof(expected_oids)) pg_fatal("expected %zu columns, got %d", lengthof(expected_oids), PQnfields(res)); @@ -1300,13 +1220,10 @@ test_prepared(PGconn *conn) i, expected_oids[i], typ); } PQclear(res); - res = PQgetResult(conn); - if (res != NULL) - pg_fatal("expected NULL result"); - res = PQgetResult(conn); - if (PQresultStatus(res) != PGRES_PIPELINE_SYNC) - pg_fatal("expected PGRES_PIPELINE_SYNC, got %s", PQresStatus(PQresultStatus(res))); + consume_null_result(conn); + + consume_result_status(conn, PGRES_PIPELINE_SYNC); fprintf(stderr, "closing statement.."); if (PQsendClosePrepared(conn, "select_one") != 1) @@ -1314,18 +1231,11 @@ test_prepared(PGconn *conn) if (PQpipelineSync(conn) != 1) pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn)); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("expected non-NULL result"); - if (PQresultStatus(res) != PGRES_COMMAND_OK) - pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res))); - PQclear(res); - res = PQgetResult(conn); - if (res != NULL) - pg_fatal("expected NULL result"); - res = PQgetResult(conn); - if (PQresultStatus(res) != PGRES_PIPELINE_SYNC) - pg_fatal("expected PGRES_PIPELINE_SYNC, got %s", PQresStatus(PQresultStatus(res))); + consume_result_status(conn, PGRES_COMMAND_OK); + + consume_null_result(conn); + + consume_result_status(conn, PGRES_PIPELINE_SYNC); if (PQexitPipelineMode(conn) != 1) pg_fatal("could not exit pipeline mode: %s", PQerrorMessage(conn)); @@ -1334,6 +1244,7 @@ test_prepared(PGconn *conn) res = PQdescribePrepared(conn, "select_one"); if (PQresultStatus(res) != PGRES_FATAL_ERROR) pg_fatal("expected FATAL_ERROR, got %s", PQresStatus(PQresultStatus(res))); + PQclear(res); /* * Also test the blocking close, this should not fail since closing a @@ -1342,32 +1253,36 @@ test_prepared(PGconn *conn) res = PQclosePrepared(conn, "select_one"); if (PQresultStatus(res) != PGRES_COMMAND_OK) pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res))); + PQclear(res); fprintf(stderr, "creating portal... "); - PQexec(conn, "BEGIN"); - PQexec(conn, "DECLARE cursor_one CURSOR FOR SELECT 1"); + + res = PQexec(conn, "BEGIN"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + pg_fatal("BEGIN failed: %s", PQerrorMessage(conn)); + PQclear(res); + + res = PQexec(conn, "DECLARE cursor_one CURSOR FOR SELECT 1"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + pg_fatal("DECLARE CURSOR failed: %s", PQerrorMessage(conn)); + PQclear(res); + PQenterPipelineMode(conn); if (PQsendDescribePortal(conn, "cursor_one") != 1) pg_fatal("PQsendDescribePortal failed: %s", PQerrorMessage(conn)); if (PQpipelineSync(conn) != 1) pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn)); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("PQgetResult returned null"); - if (PQresultStatus(res) != PGRES_COMMAND_OK) - pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res))); + res = confirm_result_status(conn, PGRES_COMMAND_OK); typ = PQftype(res, 0); if (typ != INT4OID) pg_fatal("portal: expected type %u, got %u", INT4OID, typ); PQclear(res); - res = PQgetResult(conn); - if (res != NULL) - pg_fatal("expected NULL result"); - res = PQgetResult(conn); - if (PQresultStatus(res) != PGRES_PIPELINE_SYNC) - pg_fatal("expected PGRES_PIPELINE_SYNC, got %s", PQresStatus(PQresultStatus(res))); + + consume_null_result(conn); + + consume_result_status(conn, PGRES_PIPELINE_SYNC); fprintf(stderr, "closing portal... "); if (PQsendClosePortal(conn, "cursor_one") != 1) @@ -1375,18 +1290,11 @@ test_prepared(PGconn *conn) if (PQpipelineSync(conn) != 1) pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn)); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("expected non-NULL result"); - if (PQresultStatus(res) != PGRES_COMMAND_OK) - pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res))); - PQclear(res); - res = PQgetResult(conn); - if (res != NULL) - pg_fatal("expected NULL result"); - res = PQgetResult(conn); - if (PQresultStatus(res) != PGRES_PIPELINE_SYNC) - pg_fatal("expected PGRES_PIPELINE_SYNC, got %s", PQresStatus(PQresultStatus(res))); + consume_result_status(conn, PGRES_COMMAND_OK); + + consume_null_result(conn); + + consume_result_status(conn, PGRES_PIPELINE_SYNC); if (PQexitPipelineMode(conn) != 1) pg_fatal("could not exit pipeline mode: %s", PQerrorMessage(conn)); @@ -1395,6 +1303,7 @@ test_prepared(PGconn *conn) res = PQdescribePortal(conn, "cursor_one"); if (PQresultStatus(res) != PGRES_FATAL_ERROR) pg_fatal("expected FATAL_ERROR, got %s", PQresStatus(PQresultStatus(res))); + PQclear(res); /* * Also test the blocking close, this should not fail since closing a @@ -1403,6 +1312,7 @@ test_prepared(PGconn *conn) res = PQclosePortal(conn, "cursor_one"); if (PQresultStatus(res) != PGRES_COMMAND_OK) pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res))); + PQclear(res); fprintf(stderr, "ok\n"); } @@ -1509,6 +1419,10 @@ test_protocol_version(PGconn *conn) pg_fatal("expected 30002, got %d", protocol_version); PQfinish(conn); + + pfree(keywords); + pfree(vals); + PQconninfoFree(opts); } /* Notice processor: print notices, and count how many we got */ @@ -1525,7 +1439,6 @@ notice_processor(void *arg, const char *message) static void test_pipeline_idle(PGconn *conn) { - PGresult *res; int n_notices = 0; fprintf(stderr, "\npipeline idle...\n"); @@ -1538,17 +1451,11 @@ test_pipeline_idle(PGconn *conn) if (PQsendQueryParams(conn, "SELECT 1", 0, NULL, NULL, NULL, NULL, 0) != 1) pg_fatal("failed to send query: %s", PQerrorMessage(conn)); PQsendFlushRequest(conn); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("PQgetResult returned null when there's a pipeline item: %s", - PQerrorMessage(conn)); - if (PQresultStatus(res) != PGRES_TUPLES_OK) - pg_fatal("unexpected result code %s from first pipeline item", - PQresStatus(PQresultStatus(res))); - PQclear(res); - res = PQgetResult(conn); - if (res != NULL) - pg_fatal("did not receive terminating NULL"); + + consume_result_status(conn, PGRES_TUPLES_OK); + + consume_null_result(conn); + if (PQsendQueryParams(conn, "SELECT 2", 0, NULL, NULL, NULL, NULL, 0) != 1) pg_fatal("failed to send query: %s", PQerrorMessage(conn)); if (PQexitPipelineMode(conn) == 1) @@ -1558,14 +1465,11 @@ test_pipeline_idle(PGconn *conn) pg_fatal("did not get expected error; got: %s", PQerrorMessage(conn)); PQsendFlushRequest(conn); - res = PQgetResult(conn); - if (PQresultStatus(res) != PGRES_TUPLES_OK) - pg_fatal("unexpected result code %s from second pipeline item", - PQresStatus(PQresultStatus(res))); - PQclear(res); - res = PQgetResult(conn); - if (res != NULL) - pg_fatal("did not receive terminating NULL"); + + consume_result_status(conn, PGRES_TUPLES_OK); + + consume_null_result(conn); + if (PQexitPipelineMode(conn) != 1) pg_fatal("exiting pipeline failed: %s", PQerrorMessage(conn)); @@ -1579,11 +1483,9 @@ test_pipeline_idle(PGconn *conn) if (PQsendQueryParams(conn, "SELECT pg_catalog.pg_advisory_unlock(1,1)", 0, NULL, NULL, NULL, NULL, 0) != 1) pg_fatal("failed to send query: %s", PQerrorMessage(conn)); PQsendFlushRequest(conn); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("unexpected NULL result received"); - if (PQresultStatus(res) != PGRES_TUPLES_OK) - pg_fatal("unexpected result code %s", PQresStatus(PQresultStatus(res))); + + consume_result_status(conn, PGRES_TUPLES_OK); + if (PQexitPipelineMode(conn) != 1) pg_fatal("failed to exit pipeline mode: %s", PQerrorMessage(conn)); fprintf(stderr, "ok - 2\n"); @@ -1592,7 +1494,6 @@ test_pipeline_idle(PGconn *conn) static void test_simple_pipeline(PGconn *conn) { - PGresult *res = NULL; const char *dummy_params[1] = {"1"}; Oid dummy_param_oids[1] = {INT4OID}; @@ -1623,20 +1524,9 @@ test_simple_pipeline(PGconn *conn) if (PQpipelineSync(conn) != 1) pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn)); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("PQgetResult returned null when there's a pipeline item: %s", - PQerrorMessage(conn)); - - if (PQresultStatus(res) != PGRES_TUPLES_OK) - pg_fatal("Unexpected result code %s from first pipeline item", - PQresStatus(PQresultStatus(res))); - - PQclear(res); - res = NULL; + consume_result_status(conn, PGRES_TUPLES_OK); - if (PQgetResult(conn) != NULL) - pg_fatal("PQgetResult returned something extra after first query result."); + consume_null_result(conn); /* * Even though we've processed the result there's still a sync to come and @@ -1645,21 +1535,9 @@ test_simple_pipeline(PGconn *conn) if (PQexitPipelineMode(conn) != 0) pg_fatal("exiting pipeline mode after query but before sync succeeded incorrectly"); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("PQgetResult returned null when sync result PGRES_PIPELINE_SYNC expected: %s", - PQerrorMessage(conn)); - - if (PQresultStatus(res) != PGRES_PIPELINE_SYNC) - pg_fatal("Unexpected result code %s instead of PGRES_PIPELINE_SYNC, error: %s", - PQresStatus(PQresultStatus(res)), PQerrorMessage(conn)); - - PQclear(res); - res = NULL; + consume_result_status(conn, PGRES_PIPELINE_SYNC); - if (PQgetResult(conn) != NULL) - pg_fatal("PQgetResult returned something extra after pipeline end: %s", - PQresStatus(PQresultStatus(res))); + consume_null_result(conn); /* We're still in pipeline mode... */ if (PQpipelineStatus(conn) == PQ_PIPELINE_OFF) @@ -1792,20 +1670,12 @@ test_singlerowmode(PGconn *conn) pg_fatal("failed to send flush request"); if (PQsetSingleRowMode(conn) != 1) pg_fatal("PQsetSingleRowMode() failed"); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("unexpected NULL"); - if (PQresultStatus(res) != PGRES_SINGLE_TUPLE) - pg_fatal("Expected PGRES_SINGLE_TUPLE, got %s", - PQresStatus(PQresultStatus(res))); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("unexpected NULL"); - if (PQresultStatus(res) != PGRES_TUPLES_OK) - pg_fatal("Expected PGRES_TUPLES_OK, got %s", - PQresStatus(PQresultStatus(res))); - if (PQgetResult(conn) != NULL) - pg_fatal("expected NULL result"); + + consume_result_status(conn, PGRES_SINGLE_TUPLE); + + consume_result_status(conn, PGRES_TUPLES_OK); + + consume_null_result(conn); if (PQsendQueryParams(conn, "SELECT 1", 0, NULL, NULL, NULL, NULL, 0) != 1) @@ -1813,14 +1683,10 @@ test_singlerowmode(PGconn *conn) PQerrorMessage(conn)); if (PQsendFlushRequest(conn) != 1) pg_fatal("failed to send flush request"); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("unexpected NULL"); - if (PQresultStatus(res) != PGRES_TUPLES_OK) - pg_fatal("Expected PGRES_TUPLES_OK, got %s", - PQresStatus(PQresultStatus(res))); - if (PQgetResult(conn) != NULL) - pg_fatal("expected NULL result"); + + consume_result_status(conn, PGRES_TUPLES_OK); + + consume_null_result(conn); /* * Try chunked mode as well; make sure that it correctly delivers a @@ -1834,33 +1700,23 @@ test_singlerowmode(PGconn *conn) pg_fatal("failed to send flush request"); if (PQsetChunkedRowsMode(conn, 3) != 1) pg_fatal("PQsetChunkedRowsMode() failed"); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("unexpected NULL"); - if (PQresultStatus(res) != PGRES_TUPLES_CHUNK) - pg_fatal("Expected PGRES_TUPLES_CHUNK, got %s: %s", - PQresStatus(PQresultStatus(res)), - PQerrorMessage(conn)); + + res = confirm_result_status(conn, PGRES_TUPLES_CHUNK); if (PQntuples(res) != 3) pg_fatal("Expected 3 rows, got %d", PQntuples(res)); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("unexpected NULL"); - if (PQresultStatus(res) != PGRES_TUPLES_CHUNK) - pg_fatal("Expected PGRES_TUPLES_CHUNK, got %s", - PQresStatus(PQresultStatus(res))); + PQclear(res); + + res = confirm_result_status(conn, PGRES_TUPLES_CHUNK); if (PQntuples(res) != 2) pg_fatal("Expected 2 rows, got %d", PQntuples(res)); - res = PQgetResult(conn); - if (res == NULL) - pg_fatal("unexpected NULL"); - if (PQresultStatus(res) != PGRES_TUPLES_OK) - pg_fatal("Expected PGRES_TUPLES_OK, got %s", - PQresStatus(PQresultStatus(res))); + PQclear(res); + + res = confirm_result_status(conn, PGRES_TUPLES_OK); if (PQntuples(res) != 0) pg_fatal("Expected 0 rows, got %d", PQntuples(res)); - if (PQgetResult(conn) != NULL) - pg_fatal("expected NULL result"); + PQclear(res); + + consume_null_result(conn); if (PQexitPipelineMode(conn) != 1) pg_fatal("failed to end pipeline mode: %s", PQerrorMessage(conn)); @@ -1995,9 +1851,8 @@ test_transaction(PGconn *conn) if (num_syncs <= 0) break; } - if (PQgetResult(conn) != NULL) - pg_fatal("returned something extra after all the syncs: %s", - PQresStatus(PQresultStatus(res))); + + consume_null_result(conn); if (PQexitPipelineMode(conn) != 1) pg_fatal("failed to end pipeline mode: %s", PQerrorMessage(conn)); @@ -2053,16 +1908,19 @@ test_uniqviol(PGconn *conn) "create table ppln_uniqviol(id bigint primary key, idata bigint)"); if (PQresultStatus(res) != PGRES_COMMAND_OK) pg_fatal("failed to create table: %s", PQerrorMessage(conn)); + PQclear(res); res = PQexec(conn, "begin"); if (PQresultStatus(res) != PGRES_COMMAND_OK) pg_fatal("failed to begin transaction: %s", PQerrorMessage(conn)); + PQclear(res); res = PQprepare(conn, "insertion", "insert into ppln_uniqviol values ($1, $2) returning id", 2, paramTypes); - if (res == NULL || PQresultStatus(res) != PGRES_COMMAND_OK) + if (PQresultStatus(res) != PGRES_COMMAND_OK) pg_fatal("failed to prepare query: %s", PQerrorMessage(conn)); + PQclear(res); if (PQenterPipelineMode(conn) != 1) pg_fatal("failed to enter pipeline mode"); @@ -2191,7 +2049,6 @@ test_uniqviol(PGconn *conn) static bool process_result(PGconn *conn, PGresult *res, int results, int numsent) { - PGresult *res2; bool got_error = false; if (res == NULL) @@ -2203,29 +2060,19 @@ process_result(PGconn *conn, PGresult *res, int results, int numsent) got_error = true; fprintf(stderr, "result %d/%d (error): %s\n", results, numsent, PQerrorMessage(conn)); PQclear(res); - - res2 = PQgetResult(conn); - if (res2 != NULL) - pg_fatal("expected NULL, got %s", - PQresStatus(PQresultStatus(res2))); + consume_null_result(conn); break; case PGRES_TUPLES_OK: fprintf(stderr, "result %d/%d: %s\n", results, numsent, PQgetvalue(res, 0, 0)); PQclear(res); - - res2 = PQgetResult(conn); - if (res2 != NULL) - pg_fatal("expected NULL, got %s", - PQresStatus(PQresultStatus(res2))); + consume_null_result(conn); break; case PGRES_PIPELINE_ABORTED: fprintf(stderr, "result %d/%d: pipeline aborted\n", results, numsent); - res2 = PQgetResult(conn); - if (res2 != NULL) - pg_fatal("expected NULL, got %s", - PQresStatus(PQresultStatus(res2))); + PQclear(res); + consume_null_result(conn); break; default: @@ -2271,7 +2118,7 @@ main(int argc, char **argv) { const char *conninfo = ""; PGconn *conn; - FILE *trace; + FILE *trace = NULL; char *testname; int numrows = 10000; PGresult *res; @@ -2332,9 +2179,11 @@ main(int argc, char **argv) res = PQexec(conn, "SET lc_messages TO \"C\""); if (PQresultStatus(res) != PGRES_COMMAND_OK) pg_fatal("failed to set \"lc_messages\": %s", PQerrorMessage(conn)); + PQclear(res); res = PQexec(conn, "SET debug_parallel_query = off"); if (PQresultStatus(res) != PGRES_COMMAND_OK) pg_fatal("failed to set \"debug_parallel_query\": %s", PQerrorMessage(conn)); + PQclear(res); /* Set the trace file, if requested */ if (tracefile != NULL) @@ -2388,5 +2237,9 @@ main(int argc, char **argv) /* close the connection to the database and cleanup */ PQfinish(conn); + + if (trace && trace != stdout) + fclose(trace); + return 0; } diff --git a/src/test/modules/test_dsa/test_dsa.c b/src/test/modules/test_dsa/test_dsa.c index cd24d0f4873..01d5c6fa67f 100644 --- a/src/test/modules/test_dsa/test_dsa.c +++ b/src/test/modules/test_dsa/test_dsa.c @@ -29,8 +29,7 @@ test_dsa_basic(PG_FUNCTION_ARGS) dsa_pointer p[100]; /* XXX: this tranche is leaked */ - tranche_id = LWLockNewTrancheId(); - LWLockRegisterTranche(tranche_id, "test_dsa"); + tranche_id = LWLockNewTrancheId("test_dsa"); a = dsa_create(tranche_id); for (int i = 0; i < 100; i++) @@ -70,8 +69,7 @@ test_dsa_resowners(PG_FUNCTION_ARGS) ResourceOwner childowner; /* XXX: this tranche is leaked */ - tranche_id = LWLockNewTrancheId(); - LWLockRegisterTranche(tranche_id, "test_dsa"); + tranche_id = LWLockNewTrancheId("test_dsa"); /* Create DSA in parent resource owner */ a = dsa_create(tranche_id); diff --git a/src/test/modules/test_dsm_registry/test_dsm_registry.c b/src/test/modules/test_dsm_registry/test_dsm_registry.c index 141c8ed1b34..4cc2ccdac3f 100644 --- a/src/test/modules/test_dsm_registry/test_dsm_registry.c +++ b/src/test/modules/test_dsm_registry/test_dsm_registry.c @@ -48,7 +48,7 @@ init_tdr_dsm(void *ptr) { TestDSMRegistryStruct *dsm = (TestDSMRegistryStruct *) ptr; - LWLockInitialize(&dsm->lck, LWLockNewTrancheId()); + LWLockInitialize(&dsm->lck, LWLockNewTrancheId("test_dsm_registry")); dsm->val = 0; } @@ -61,7 +61,6 @@ tdr_attach_shmem(void) sizeof(TestDSMRegistryStruct), init_tdr_dsm, &found); - LWLockRegisterTranche(tdr_dsm->lck.tranche, "test_dsm_registry"); if (tdr_dsa == NULL) tdr_dsa = GetNamedDSA("test_dsm_registry_dsa", &found); diff --git a/src/test/modules/test_radixtree/test_radixtree.c b/src/test/modules/test_radixtree/test_radixtree.c index 80ad0296164..787162c8793 100644 --- a/src/test/modules/test_radixtree/test_radixtree.c +++ b/src/test/modules/test_radixtree/test_radixtree.c @@ -124,10 +124,9 @@ test_empty(void) rt_iter *iter; uint64 key; #ifdef TEST_SHARED_RT - int tranche_id = LWLockNewTrancheId(); + int tranche_id = LWLockNewTrancheId("test_radix_tree"); dsa_area *dsa; - LWLockRegisterTranche(tranche_id, "test_radix_tree"); dsa = dsa_create(tranche_id); radixtree = rt_create(dsa, tranche_id); #else @@ -167,10 +166,9 @@ test_basic(rt_node_class_test_elem *test_info, int shift, bool asc) uint64 *keys; int children = test_info->nkeys; #ifdef TEST_SHARED_RT - int tranche_id = LWLockNewTrancheId(); + int tranche_id = LWLockNewTrancheId("test_radix_tree"); dsa_area *dsa; - LWLockRegisterTranche(tranche_id, "test_radix_tree"); dsa = dsa_create(tranche_id); radixtree = rt_create(dsa, tranche_id); #else @@ -304,10 +302,9 @@ test_random(void) int num_keys = 100000; uint64 *keys; #ifdef TEST_SHARED_RT - int tranche_id = LWLockNewTrancheId(); + int tranche_id = LWLockNewTrancheId("test_radix_tree"); dsa_area *dsa; - LWLockRegisterTranche(tranche_id, "test_radix_tree"); dsa = dsa_create(tranche_id); radixtree = rt_create(dsa, tranche_id); #else diff --git a/src/test/modules/test_slru/test_slru.c b/src/test/modules/test_slru/test_slru.c index 32750930e43..8c0367eeee4 100644 --- a/src/test/modules/test_slru/test_slru.c +++ b/src/test/modules/test_slru/test_slru.c @@ -232,11 +232,9 @@ test_slru_shmem_startup(void) (void) MakePGDirectory(slru_dir_name); /* initialize the SLRU facility */ - test_tranche_id = LWLockNewTrancheId(); - LWLockRegisterTranche(test_tranche_id, "test_slru_tranche"); + test_tranche_id = LWLockNewTrancheId("test_slru_tranche"); - test_buffer_tranche_id = LWLockNewTrancheId(); - LWLockRegisterTranche(test_tranche_id, "test_buffer_tranche"); + test_buffer_tranche_id = LWLockNewTrancheId("test_buffer_tranche"); TestSlruCtl->PagePrecedes = test_slru_page_precedes_logically; SimpleLruInit(TestSlruCtl, "TestSLRU", diff --git a/src/test/modules/test_tidstore/test_tidstore.c b/src/test/modules/test_tidstore/test_tidstore.c index eb16e0fbfa6..0c8f43867e5 100644 --- a/src/test/modules/test_tidstore/test_tidstore.c +++ b/src/test/modules/test_tidstore/test_tidstore.c @@ -103,8 +103,7 @@ test_create(PG_FUNCTION_ARGS) { int tranche_id; - tranche_id = LWLockNewTrancheId(); - LWLockRegisterTranche(tranche_id, "test_tidstore"); + tranche_id = LWLockNewTrancheId("test_tidstore"); tidstore = TidStoreCreateShared(tidstore_max_size, tranche_id); diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out index caa3c44f0d0..f3fdce23459 100644 --- a/src/test/regress/expected/copy2.out +++ b/src/test/regress/expected/copy2.out @@ -163,6 +163,7 @@ COPY x TO stdout WHERE a = 1; ERROR: WHERE clause not allowed with COPY TO LINE 1: COPY x TO stdout WHERE a = 1; ^ +HINT: Try the COPY (SELECT ... WHERE ...) TO variant. COPY x from stdin WHERE a = 50004; COPY x from stdin WHERE a > 60003; COPY x from stdin WHERE f > 60003; diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index 53268059142..895ca87a0df 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -1924,6 +1924,57 @@ DROP PUBLICATION pub1; DROP PUBLICATION pub2; DROP TABLE gencols; RESET client_min_messages; +-- Test that the INSERT ON CONFLICT command correctly checks REPLICA IDENTITY +-- when the target table is published. +CREATE TABLE testpub_insert_onconfl_no_ri (a int unique, b int); +CREATE TABLE testpub_insert_onconfl_parted (a int unique, b int) PARTITION by RANGE (a); +CREATE TABLE testpub_insert_onconfl_part_no_ri PARTITION OF testpub_insert_onconfl_parted FOR VALUES FROM (1) TO (10); +SET client_min_messages = 'ERROR'; +CREATE PUBLICATION pub1 FOR ALL TABLES; +RESET client_min_messages; +-- fail - missing REPLICA IDENTITY +INSERT INTO testpub_insert_onconfl_no_ri VALUES (1, 1) ON CONFLICT (a) DO UPDATE SET b = 2; +ERROR: cannot update table "testpub_insert_onconfl_no_ri" because it does not have a replica identity and publishes updates +HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. +-- ok - no updates +INSERT INTO testpub_insert_onconfl_no_ri VALUES (1, 1) ON CONFLICT DO NOTHING; +-- fail - missing REPLICA IDENTITY in partition testpub_insert_onconfl_no_ri +INSERT INTO testpub_insert_onconfl_parted VALUES (1, 1) ON CONFLICT (a) DO UPDATE SET b = 2; +ERROR: cannot update table "testpub_insert_onconfl_part_no_ri" because it does not have a replica identity and publishes updates +HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. +-- ok - no updates +INSERT INTO testpub_insert_onconfl_parted VALUES (1, 1) ON CONFLICT DO NOTHING; +DROP PUBLICATION pub1; +DROP TABLE testpub_insert_onconfl_no_ri; +DROP TABLE testpub_insert_onconfl_parted; +-- Test that the MERGE command correctly checks REPLICA IDENTITY when the +-- target table is published. +CREATE TABLE testpub_merge_no_ri (a int, b int); +CREATE TABLE testpub_merge_pk (a int primary key, b int); +SET client_min_messages = 'ERROR'; +CREATE PUBLICATION pub1 FOR ALL TABLES; +RESET client_min_messages; +-- fail - missing REPLICA IDENTITY +MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 + WHEN MATCHED THEN UPDATE SET b = s.b; +ERROR: cannot update table "testpub_merge_no_ri" because it does not have a replica identity and publishes updates +HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. +-- fail - missing REPLICA IDENTITY +MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 + WHEN MATCHED THEN DELETE; +ERROR: cannot delete from table "testpub_merge_no_ri" because it does not have a replica identity and publishes deletes +HINT: To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE. +-- ok - insert and do nothing are not restricted +MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 + WHEN MATCHED THEN DO NOTHING + WHEN NOT MATCHED THEN INSERT (a, b) VALUES (0, 0); +-- ok - REPLICA IDENTITY is DEFAULT and table has a PK +MERGE INTO testpub_merge_pk USING testpub_merge_no_ri s ON s.a >= 1 + WHEN MATCHED AND s.a > 0 THEN UPDATE SET b = s.b + WHEN MATCHED THEN DELETE; +DROP PUBLICATION pub1; +DROP TABLE testpub_merge_no_ri; +DROP TABLE testpub_merge_pk; RESET SESSION AUTHORIZATION; DROP ROLE regress_publication_user, regress_publication_user2; DROP ROLE regress_publication_user_dummy; diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out index 0563d0cd5a1..c16dff05bc1 100644 --- a/src/test/regress/expected/subselect.out +++ b/src/test/regress/expected/subselect.out @@ -980,6 +980,25 @@ select (select (a.*)::text) from view_a a; (1 row) -- +-- Test case for bug #19037: no relation entry for relid N +-- +explain (costs off) +select (1 = any(array_agg(f1))) = any (select false) from int4_tbl; + QUERY PLAN +---------------------------- + Aggregate + -> Seq Scan on int4_tbl + SubPlan 1 + -> Result +(4 rows) + +select (1 = any(array_agg(f1))) = any (select false) from int4_tbl; + ?column? +---------- + t +(1 row) + +-- -- Check that whole-row Vars reading the result of a subselect don't include -- any junk columns therein -- diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index deddf0da844..3f423061395 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -1223,6 +1223,64 @@ DROP PUBLICATION pub2; DROP TABLE gencols; RESET client_min_messages; + +-- Test that the INSERT ON CONFLICT command correctly checks REPLICA IDENTITY +-- when the target table is published. +CREATE TABLE testpub_insert_onconfl_no_ri (a int unique, b int); +CREATE TABLE testpub_insert_onconfl_parted (a int unique, b int) PARTITION by RANGE (a); +CREATE TABLE testpub_insert_onconfl_part_no_ri PARTITION OF testpub_insert_onconfl_parted FOR VALUES FROM (1) TO (10); + +SET client_min_messages = 'ERROR'; +CREATE PUBLICATION pub1 FOR ALL TABLES; +RESET client_min_messages; + +-- fail - missing REPLICA IDENTITY +INSERT INTO testpub_insert_onconfl_no_ri VALUES (1, 1) ON CONFLICT (a) DO UPDATE SET b = 2; + +-- ok - no updates +INSERT INTO testpub_insert_onconfl_no_ri VALUES (1, 1) ON CONFLICT DO NOTHING; + +-- fail - missing REPLICA IDENTITY in partition testpub_insert_onconfl_no_ri +INSERT INTO testpub_insert_onconfl_parted VALUES (1, 1) ON CONFLICT (a) DO UPDATE SET b = 2; + +-- ok - no updates +INSERT INTO testpub_insert_onconfl_parted VALUES (1, 1) ON CONFLICT DO NOTHING; + +DROP PUBLICATION pub1; +DROP TABLE testpub_insert_onconfl_no_ri; +DROP TABLE testpub_insert_onconfl_parted; + +-- Test that the MERGE command correctly checks REPLICA IDENTITY when the +-- target table is published. +CREATE TABLE testpub_merge_no_ri (a int, b int); +CREATE TABLE testpub_merge_pk (a int primary key, b int); + +SET client_min_messages = 'ERROR'; +CREATE PUBLICATION pub1 FOR ALL TABLES; +RESET client_min_messages; + +-- fail - missing REPLICA IDENTITY +MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 + WHEN MATCHED THEN UPDATE SET b = s.b; + +-- fail - missing REPLICA IDENTITY +MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 + WHEN MATCHED THEN DELETE; + +-- ok - insert and do nothing are not restricted +MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 + WHEN MATCHED THEN DO NOTHING + WHEN NOT MATCHED THEN INSERT (a, b) VALUES (0, 0); + +-- ok - REPLICA IDENTITY is DEFAULT and table has a PK +MERGE INTO testpub_merge_pk USING testpub_merge_no_ri s ON s.a >= 1 + WHEN MATCHED AND s.a > 0 THEN UPDATE SET b = s.b + WHEN MATCHED THEN DELETE; + +DROP PUBLICATION pub1; +DROP TABLE testpub_merge_no_ri; +DROP TABLE testpub_merge_pk; + RESET SESSION AUTHORIZATION; DROP ROLE regress_publication_user, regress_publication_user2; DROP ROLE regress_publication_user_dummy; diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql index a6d276a115b..8ccebbe51e0 100644 --- a/src/test/regress/sql/subselect.sql +++ b/src/test/regress/sql/subselect.sql @@ -480,6 +480,15 @@ select (select (select view_a)) from view_a; select (select (a.*)::text) from view_a a; -- +-- Test case for bug #19037: no relation entry for relid N +-- + +explain (costs off) +select (1 = any(array_agg(f1))) = any (select false) from int4_tbl; + +select (1 = any(array_agg(f1))) = any (select false) from int4_tbl; + +-- -- Check that whole-row Vars reading the result of a subselect don't include -- any junk columns therein -- |