diff --git a/doc/src/sgml/ref/reindexdb.sgml b/doc/src/sgml/ref/reindexdb.sgml index 98c3333228..a877439dc5 100644 --- a/doc/src/sgml/ref/reindexdb.sgml +++ b/doc/src/sgml/ref/reindexdb.sgml @@ -179,7 +179,8 @@ PostgreSQL documentation setting is high enough to accommodate all connections. - Note that this option is incompatible with the option. + Note that this option is incompatible with the + and options. diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c index 9ce628e62a..2ee7679cce 100644 --- a/src/backend/access/transam/slru.c +++ b/src/backend/access/transam/slru.c @@ -1353,7 +1353,7 @@ SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied) } /* Do nothing if slot is unused */ - if (shared->page_status[slotno] == SLRU_PAGE_EMPTY) + if (shared->page_status[slotno] != SLRU_PAGE_VALID) continue; SlruInternalWritePage(ctl, slotno, &fdata); diff --git a/src/backend/access/transam/transam.c b/src/backend/access/transam/transam.c index 9a39451a29..14dc9b116c 100644 --- a/src/backend/access/transam/transam.c +++ b/src/backend/access/transam/transam.c @@ -125,50 +125,26 @@ TransactionLogFetch(TransactionId transactionId) bool /* true if given transaction committed */ TransactionIdDidCommit(TransactionId transactionId) { - XidStatus xidstatus; - - xidstatus = TransactionLogFetch(transactionId); - - /* - * If it's marked committed, it's committed. - */ - if (xidstatus == TRANSACTION_STATUS_COMMITTED) - return true; - - /* - * If it's marked subcommitted, we have to check the parent recursively. - * However, if it's older than TransactionXmin, we can't look at - * pg_subtrans; instead assume that the parent crashed without cleaning up - * its children. - * - * Originally we Assert'ed that the result of SubTransGetParent was not - * zero. However with the introduction of prepared transactions, there can - * be a window just after database startup where we do not have complete - * knowledge in pg_subtrans of the transactions after TransactionXmin. - * StartupSUBTRANS() has ensured that any missing information will be - * zeroed. Since this case should not happen under normal conditions, it - * seems reasonable to emit a WARNING for it. - */ - if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED) - { - TransactionId parentXid; - - if (TransactionIdPrecedes(transactionId, TransactionXmin)) - return false; - parentXid = SubTransGetParent(transactionId); - if (!TransactionIdIsValid(parentXid)) - { - elog(WARNING, "no pg_subtrans entry for subcommitted XID %u", - transactionId); - return false; - } - return TransactionIdDidCommit(parentXid); - } - - /* - * It's not committed. - */ - return false; + XidStatus xidstatus; + + while (true) { + xidstatus = TransactionLogFetch(transactionId); + + if (xidstatus == TRANSACTION_STATUS_COMMITTED) + return true; + + if (xidstatus != TRANSACTION_STATUS_SUB_COMMITTED) + return false; + + if (unlikely(TransactionIdPrecedes(transactionId, TransactionXmin))) + return false; + + transactionId = SubTransGetParent(transactionId); + if (unlikely(!TransactionIdIsValid(transactionId))) { + elog(WARNING, "no pg_subtrans entry for subcommitted XID %u", transactionId); + return false; + } + } } /* @@ -187,43 +163,26 @@ TransactionIdDidCommit(TransactionId transactionId) bool /* true if given transaction aborted */ TransactionIdDidAbort(TransactionId transactionId) { - XidStatus xidstatus; - - xidstatus = TransactionLogFetch(transactionId); - - /* - * If it's marked aborted, it's aborted. - */ - if (xidstatus == TRANSACTION_STATUS_ABORTED) - return true; - - /* - * If it's marked subcommitted, we have to check the parent recursively. - * However, if it's older than TransactionXmin, we can't look at - * pg_subtrans; instead assume that the parent crashed without cleaning up - * its children. - */ - if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED) - { - TransactionId parentXid; - - if (TransactionIdPrecedes(transactionId, TransactionXmin)) - return true; - parentXid = SubTransGetParent(transactionId); - if (!TransactionIdIsValid(parentXid)) - { - /* see notes in TransactionIdDidCommit */ - elog(WARNING, "no pg_subtrans entry for subcommitted XID %u", - transactionId); - return true; - } - return TransactionIdDidAbort(parentXid); - } - - /* - * It's not aborted. - */ - return false; + XidStatus xidstatus; + + while (true) { + xidstatus = TransactionLogFetch(transactionId); + + if (xidstatus == TRANSACTION_STATUS_ABORTED) + return true; + + if (xidstatus != TRANSACTION_STATUS_SUB_COMMITTED) + return false; + + if (unlikely(TransactionIdPrecedes(transactionId, TransactionXmin))) + return true; + + transactionId = SubTransGetParent(transactionId); + if (unlikely(!TransactionIdIsValid(transactionId))) { + elog(WARNING, "no pg_subtrans entry for subcommitted XID %u", transactionId); + return true; + } + } } /* diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 54a08e4102..17f2e0665a 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -949,7 +949,7 @@ copy_table_data(Relation NewHeap, Relation OldHeap, Relation OldIndex, bool verb * tells us it's cheaper. Otherwise, always indexscan if an index is * provided, else plain seqscan. */ - if (OldIndex != NULL && OldIndex->rd_rel->relam == BTREE_AM_OID) + if (OldIndex != NULL) use_sort = plan_cluster_use_sort(RelationGetRelid(OldHeap), RelationGetRelid(OldIndex)); else diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 391b34a2af..5803b404a6 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -738,14 +738,14 @@ ExplainPrintSettings(ExplainState *es) for (int i = 0; i < num; i++) { - char *setting; struct config_generic *conf = gucs[i]; + char *setting; + + setting = GetConfigOptionByName(conf->name, NULL, true); if (i > 0) appendStringInfoString(&str, ", "); - setting = GetConfigOptionByName(conf->name, NULL, true); - if (setting) appendStringInfo(&str, "%s = '%s'", conf->name, setting); else diff --git a/src/backend/lib/dshash.c b/src/backend/lib/dshash.c index b8d031f201..3a70c8d6ee 100644 --- a/src/backend/lib/dshash.c +++ b/src/backend/lib/dshash.c @@ -74,6 +74,7 @@ typedef struct dshash_partition { LWLock lock; /* Protects all buckets in this partition. */ size_t count; /* # of items in this partition's buckets */ + char padding[PG_CACHE_LINE_SIZE - sizeof(LWLock) - sizeof(size_t)]; } dshash_partition; /* @@ -656,86 +657,58 @@ dshash_seq_init(dshash_seq_status *status, dshash_table *hash_table, void * dshash_seq_next(dshash_seq_status *status) { - dsa_pointer next_item_pointer; - - /* - * Not yet holding any partition locks. Need to determine the size of the - * hash table, it could have been resized since we were looking last. - * Since we iterate in partition order, we can start by unconditionally - * lock partition 0. - * - * Once we hold the lock, no resizing can happen until the scan ends. So - * we don't need to repeatedly call ensure_valid_bucket_pointers(). - */ - if (status->curpartition == -1) - { - Assert(status->curbucket == 0); - ASSERT_NO_PARTITION_LOCKS_HELD_BY_ME(status->hash_table); - - status->curpartition = 0; - - LWLockAcquire(PARTITION_LOCK(status->hash_table, - status->curpartition), - status->exclusive ? LW_EXCLUSIVE : LW_SHARED); - - ensure_valid_bucket_pointers(status->hash_table); - - status->nbuckets = - NUM_BUCKETS(status->hash_table->control->size_log2); - next_item_pointer = status->hash_table->buckets[status->curbucket]; - } - else - next_item_pointer = status->pnextitem; - - Assert(LWLockHeldByMeInMode(PARTITION_LOCK(status->hash_table, - status->curpartition), - status->exclusive ? LW_EXCLUSIVE : LW_SHARED)); - - /* Move to the next bucket if we finished the current bucket */ - while (!DsaPointerIsValid(next_item_pointer)) - { - int next_partition; - - if (++status->curbucket >= status->nbuckets) - { - /* all buckets have been scanned. finish. */ - return NULL; - } - - /* Check if move to the next partition */ - next_partition = - PARTITION_FOR_BUCKET_INDEX(status->curbucket, - status->hash_table->size_log2); - - if (status->curpartition != next_partition) - { - /* - * Move to the next partition. Lock the next partition then - * release the current, not in the reverse order to avoid - * concurrent resizing. Avoid dead lock by taking lock in the - * same order with resize(). - */ - LWLockAcquire(PARTITION_LOCK(status->hash_table, - next_partition), - status->exclusive ? LW_EXCLUSIVE : LW_SHARED); - LWLockRelease(PARTITION_LOCK(status->hash_table, - status->curpartition)); - status->curpartition = next_partition; - } - - next_item_pointer = status->hash_table->buckets[status->curbucket]; - } - - status->curitem = - dsa_get_address(status->hash_table->area, next_item_pointer); - - /* - * The caller may delete the item. Store the next item in case of - * deletion. - */ - status->pnextitem = status->curitem->next; - - return ENTRY_FROM_ITEM(status->curitem); + dsa_pointer next_item_pointer; + + if (status->curpartition == -1) + { + /* Initialize the scan. */ + status->curpartition = 0; + LWLockAcquire(PARTITION_LOCK(status->hash_table, + status->curpartition), + status->exclusive ? LW_EXCLUSIVE : LW_SHARED); + ensure_valid_bucket_pointers(status->hash_table); + status->nbuckets = + NUM_BUCKETS(status->hash_table->control->size_log2); + next_item_pointer = status->hash_table->buckets[status->curbucket]; + } + else + next_item_pointer = status->pnextitem; + + while (true) + { + /* Process the current bucket. */ + while (DsaPointerIsValid(next_item_pointer)) + { + status->curitem = + dsa_get_address(status->hash_table->area, next_item_pointer); + status->pnextitem = status->curitem->next; + return ENTRY_FROM_ITEM(status->curitem); + } + + /* Move to the next bucket. */ + if (++status->curbucket >= status->nbuckets) + { + /* All buckets have been scanned. */ + return NULL; + } + + /* Check if we need to switch partitions. */ + int next_partition = + PARTITION_FOR_BUCKET_INDEX(status->curbucket, + status->hash_table->size_log2); + + if (status->curpartition != next_partition) + { + LWLockAcquire(PARTITION_LOCK(status->hash_table, + next_partition), + status->exclusive ? LW_EXCLUSIVE : LW_SHARED); + LWLockRelease(PARTITION_LOCK(status->hash_table, + status->curpartition)); + status->curpartition = next_partition; + } + + next_item_pointer = status->hash_table->buckets[status->curbucket]; + } } /* @@ -857,75 +830,65 @@ delete_item(dshash_table *hash_table, dshash_table_item *item) static void resize(dshash_table *hash_table, size_t new_size_log2) { - dsa_pointer old_buckets; - dsa_pointer new_buckets_shared; - dsa_pointer *new_buckets; - size_t size; - size_t new_size = ((size_t) 1) << new_size_log2; - size_t i; - - /* - * Acquire the locks for all lock partitions. This is expensive, but we - * shouldn't have to do it many times. - */ - for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i) - { - Assert(!LWLockHeldByMe(PARTITION_LOCK(hash_table, i))); - - LWLockAcquire(PARTITION_LOCK(hash_table, i), LW_EXCLUSIVE); - if (i == 0 && hash_table->control->size_log2 >= new_size_log2) - { - /* - * Another backend has already increased the size; we can avoid - * obtaining all the locks and return early. - */ - LWLockRelease(PARTITION_LOCK(hash_table, 0)); - return; - } - } - - Assert(new_size_log2 == hash_table->control->size_log2 + 1); - - /* Allocate the space for the new table. */ - new_buckets_shared = - dsa_allocate_extended(hash_table->area, - sizeof(dsa_pointer) * new_size, - DSA_ALLOC_HUGE | DSA_ALLOC_ZERO); - new_buckets = dsa_get_address(hash_table->area, new_buckets_shared); - - /* - * We've allocated the new bucket array; all that remains to do now is to - * reinsert all items, which amounts to adjusting all the pointers. - */ - size = ((size_t) 1) << hash_table->control->size_log2; - for (i = 0; i < size; ++i) - { - dsa_pointer item_pointer = hash_table->buckets[i]; - - while (DsaPointerIsValid(item_pointer)) - { - dshash_table_item *item; - dsa_pointer next_item_pointer; - - item = dsa_get_address(hash_table->area, item_pointer); - next_item_pointer = item->next; - insert_item_into_bucket(hash_table, item_pointer, item, - &new_buckets[BUCKET_INDEX_FOR_HASH_AND_SIZE(item->hash, - new_size_log2)]); - item_pointer = next_item_pointer; - } - } - - /* Swap the hash table into place and free the old one. */ - old_buckets = hash_table->control->buckets; - hash_table->control->buckets = new_buckets_shared; - hash_table->control->size_log2 = new_size_log2; - hash_table->buckets = new_buckets; - dsa_free(hash_table->area, old_buckets); - - /* Release all the locks. */ - for (i = 0; i < DSHASH_NUM_PARTITIONS; ++i) - LWLockRelease(PARTITION_LOCK(hash_table, i)); + dsa_pointer old_buckets; + dsa_pointer new_buckets_shared; + dsa_pointer *new_buckets; + size_t size; + size_t new_size = ((size_t) 1) << new_size_log2; + size_t i; + + /* Allocate and initialize the new bucket array without locks. */ + new_buckets_shared = + dsa_allocate_extended(hash_table->area, + sizeof(dsa_pointer) * new_size, + DSA_ALLOC_HUGE | DSA_ALLOC_ZERO); + new_buckets = dsa_get_address(hash_table->area, new_buckets_shared); + + /* Acquire locks and update pointers. */ + for (i = 0; i < DSHASH_NUM_PARTITIONS; i++) + LWLockAcquire(PARTITION_LOCK(hash_table, i), LW_EXCLUSIVE); + + /* Check if another backend has already resized the table. */ + if (hash_table->control->size_log2 >= new_size_log2) + { + dsa_free(hash_table->area, new_buckets_shared); + + for (i = 0; i < DSHASH_NUM_PARTITIONS; i++) + LWLockRelease(PARTITION_LOCK(hash_table, i)); + + return; + } + + /* Reinsert all items into the new bucket array. */ + size = ((size_t) 1) << hash_table->control->size_log2; + for (i = 0; i < size; i++) + { + dsa_pointer item_pointer = hash_table->buckets[i]; + + while (DsaPointerIsValid(item_pointer)) + { + dshash_table_item *item; + dsa_pointer next_item_pointer; + + item = dsa_get_address(hash_table->area, item_pointer); + next_item_pointer = item->next; + insert_item_into_bucket(hash_table, item_pointer, item, + &new_buckets[BUCKET_INDEX_FOR_HASH_AND_SIZE(item->hash, + new_size_log2)]); + item_pointer = next_item_pointer; + } + } + + /* Swap the hash table into place and free the old one. */ + old_buckets = hash_table->control->buckets; + hash_table->control->buckets = new_buckets_shared; + hash_table->control->size_log2 = new_size_log2; + hash_table->buckets = new_buckets; + dsa_free(hash_table->area, old_buckets); + + /* Release all the locks. */ + for (i = 0; i < DSHASH_NUM_PARTITIONS; i++) + LWLockRelease(PARTITION_LOCK(hash_table, i)); } /* diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c index 64ff3ce3d6..947636816a 100644 --- a/src/backend/libpq/be-secure-openssl.c +++ b/src/backend/libpq/be-secure-openssl.c @@ -765,56 +765,60 @@ ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor) { ssize_t n; - int err; - unsigned long ecode; errno = 0; ERR_clear_error(); n = SSL_read(port->ssl, ptr, len); - err = SSL_get_error(port->ssl, n); - ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; - switch (err) + if (n <= 0) { - case SSL_ERROR_NONE: - /* a-ok */ - break; - case SSL_ERROR_WANT_READ: - *waitfor = WL_SOCKET_READABLE; - errno = EWOULDBLOCK; - n = -1; - break; - case SSL_ERROR_WANT_WRITE: - *waitfor = WL_SOCKET_WRITEABLE; - errno = EWOULDBLOCK; - n = -1; - break; - case SSL_ERROR_SYSCALL: - /* leave it to caller to ereport the value of errno */ - if (n != -1 || errno == 0) - { + int err; + unsigned long ecode; + + err = SSL_get_error(port->ssl, n); + ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; + switch (err) + { + case SSL_ERROR_NONE: + /* a-ok */ + break; + case SSL_ERROR_WANT_READ: + *waitfor = WL_SOCKET_READABLE; + errno = EWOULDBLOCK; + n = -1; + break; + case SSL_ERROR_WANT_WRITE: + *waitfor = WL_SOCKET_WRITEABLE; + errno = EWOULDBLOCK; + n = -1; + break; + case SSL_ERROR_SYSCALL: + /* leave it to caller to ereport the value of errno */ + if (n != -1 || errno == 0) + { + errno = ECONNRESET; + n = -1; + } + break; + case SSL_ERROR_SSL: + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("SSL error: %s", SSLerrmessage(ecode)))); errno = ECONNRESET; n = -1; - } - break; - case SSL_ERROR_SSL: - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("SSL error: %s", SSLerrmessage(ecode)))); - errno = ECONNRESET; - n = -1; - break; - case SSL_ERROR_ZERO_RETURN: - /* connection was cleanly shut down by peer */ - n = 0; - break; - default: - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("unrecognized SSL error code: %d", - err))); - errno = ECONNRESET; - n = -1; - break; + break; + case SSL_ERROR_ZERO_RETURN: + /* connection was cleanly shut down by peer */ + n = 0; + break; + default: + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("unrecognized SSL error code: %d", + err))); + errno = ECONNRESET; + n = -1; + break; + } } return n; @@ -824,67 +828,71 @@ ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfor) { ssize_t n; - int err; - unsigned long ecode; errno = 0; ERR_clear_error(); n = SSL_write(port->ssl, ptr, len); - err = SSL_get_error(port->ssl, n); - ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; - switch (err) + if (n <= 0) { - case SSL_ERROR_NONE: - /* a-ok */ - break; - case SSL_ERROR_WANT_READ: - *waitfor = WL_SOCKET_READABLE; - errno = EWOULDBLOCK; - n = -1; - break; - case SSL_ERROR_WANT_WRITE: - *waitfor = WL_SOCKET_WRITEABLE; - errno = EWOULDBLOCK; - n = -1; - break; - case SSL_ERROR_SYSCALL: + int err; + unsigned long ecode; - /* - * Leave it to caller to ereport the value of errno. However, if - * errno is still zero then assume it's a read EOF situation, and - * report ECONNRESET. (This seems possible because SSL_write can - * also do reads.) - */ - if (n != -1 || errno == 0) - { + err = SSL_get_error(port->ssl, n); + ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; + switch (err) + { + case SSL_ERROR_NONE: + /* a-ok */ + break; + case SSL_ERROR_WANT_READ: + *waitfor = WL_SOCKET_READABLE; + errno = EWOULDBLOCK; + n = -1; + break; + case SSL_ERROR_WANT_WRITE: + *waitfor = WL_SOCKET_WRITEABLE; + errno = EWOULDBLOCK; + n = -1; + break; + case SSL_ERROR_SYSCALL: + + /* + * Leave it to caller to ereport the value of errno. However, if + * errno is still zero then assume it's a read EOF situation, and + * report ECONNRESET. (This seems possible because SSL_write can + * also do reads.) + */ + if (n != -1 || errno == 0) + { + errno = ECONNRESET; + n = -1; + } + break; + case SSL_ERROR_SSL: + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("SSL error: %s", SSLerrmessage(ecode)))); errno = ECONNRESET; n = -1; - } - break; - case SSL_ERROR_SSL: - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("SSL error: %s", SSLerrmessage(ecode)))); - errno = ECONNRESET; - n = -1; - break; - case SSL_ERROR_ZERO_RETURN: + break; + case SSL_ERROR_ZERO_RETURN: - /* - * the SSL connection was closed, leave it to the caller to - * ereport it - */ - errno = ECONNRESET; - n = -1; - break; - default: - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("unrecognized SSL error code: %d", - err))); - errno = ECONNRESET; - n = -1; - break; + /* + * the SSL connection was closed, leave it to the caller to + * ereport it + */ + errno = ECONNRESET; + n = -1; + break; + default: + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("unrecognized SSL error code: %d", + err))); + errno = ECONNRESET; + n = -1; + break; + } } return n; diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c index bf512cf806..d65c2ea429 100644 --- a/src/backend/nodes/bitmapset.c +++ b/src/backend/nodes/bitmapset.c @@ -831,16 +831,13 @@ bms_add_member(Bitmapset *a, int x) if (wordnum >= a->nwords) { int oldnwords = a->nwords; - int i; a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(wordnum + 1)); - a->nwords = wordnum + 1; + /* zero out the enlarged portion */ - i = oldnwords; - do - { - a->words[i] = 0; - } while (++i < a->nwords); + memset(&a->words[oldnwords], 0, (wordnum + 1 - oldnwords) * sizeof(bitmapword)); + + a->nwords = wordnum + 1; } a->words[wordnum] |= ((bitmapword) 1 << bitnum); @@ -971,8 +968,6 @@ bms_add_members(Bitmapset *a, const Bitmapset *b) Bitmapset * bms_replace_members(Bitmapset *a, const Bitmapset *b) { - int i; - Assert(bms_is_valid_set(a)); Assert(bms_is_valid_set(b)); @@ -987,11 +982,7 @@ bms_replace_members(Bitmapset *a, const Bitmapset *b) if (a->nwords < b->nwords) a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(b->nwords)); - i = 0; - do - { - a->words[i] = b->words[i]; - } while (++i < b->nwords); + memcpy(a->words, b->words, b->nwords * sizeof(bitmapword)); a->nwords = b->nwords; @@ -1049,17 +1040,14 @@ bms_add_range(Bitmapset *a, int lower, int upper) else if (uwordnum >= a->nwords) { int oldnwords = a->nwords; - int i; /* ensure we have enough words to store the upper bit */ a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(uwordnum + 1)); - a->nwords = uwordnum + 1; + /* zero out the enlarged portion */ - i = oldnwords; - do - { - a->words[i] = 0; - } while (++i < a->nwords); + memset(&a->words[oldnwords], 0, (uwordnum + 1 - oldnwords) * sizeof(bitmapword)); + + a->nwords = uwordnum + 1; } wordnum = lwordnum = WORDNUM(lower); diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 5d51f97f21..a4882081ae 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -128,6 +128,9 @@ clauselist_selectivity_ext(PlannerInfo *root, ListCell *l; int listidx; + if (clauses == NIL) + return s1; + /* * If there's exactly one clause, just go directly to * clause_selectivity_ext(). None of what we might do below is relevant. @@ -525,6 +528,9 @@ find_single_rel_for_clauses(PlannerInfo *root, List *clauses) int lastrelid = 0; ListCell *l; + if (clauses == NIL) + return NULL; + foreach(l, clauses) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index a43ca16d68..c983753f84 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -4286,7 +4286,8 @@ relation_has_unique_index_ext(PlannerInfo *root, RelOptInfo *rel, { matched = true; /* column is unique */ - if (bms_membership(rinfo->clause_relids) == BMS_SINGLETON) + if (extra_clauses && + bms_membership(rinfo->clause_relids) == BMS_SINGLETON) { MemoryContext oldMemCtx = MemoryContextSwitchTo(root->planner_cxt); @@ -4298,8 +4299,8 @@ relation_has_unique_index_ext(PlannerInfo *root, RelOptInfo *rel, */ Assert(bms_is_empty(rinfo->left_relids) || bms_is_empty(rinfo->right_relids)); - if (extra_clauses) - exprs = lappend(exprs, rinfo); + + exprs = lappend(exprs, rinfo); MemoryContextSwitchTo(oldMemCtx); } diff --git a/src/backend/optimizer/util/joininfo.c b/src/backend/optimizer/util/joininfo.c index f26e38c655..a3fe316d8a 100644 --- a/src/backend/optimizer/util/joininfo.c +++ b/src/backend/optimizer/util/joininfo.c @@ -106,33 +106,11 @@ add_join_clause_to_rels(PlannerInfo *root, return; /* - * Substitute the origin qual with constant-FALSE if it is provably always + * Substitute the origin qual with constant-FALSE if it is probably always * false. - * - * Note that we need to keep the same rinfo_serial, since it is in - * practice the same condition. We also need to reset the - * last_rinfo_serial counter, which is essential to ensure that the - * RestrictInfos for the "same" qual condition get identical serial - * numbers (see deconstruct_distribute_oj_quals). */ if (restriction_is_always_false(root, restrictinfo)) - { - int save_rinfo_serial = restrictinfo->rinfo_serial; - int save_last_rinfo_serial = root->last_rinfo_serial; - - restrictinfo = make_restrictinfo(root, - (Expr *) makeBoolConst(false, false), - restrictinfo->is_pushed_down, - restrictinfo->has_clone, - restrictinfo->is_clone, - restrictinfo->pseudoconstant, - 0, /* security_level */ - restrictinfo->required_relids, - restrictinfo->incompatible_relids, - restrictinfo->outer_relids); - restrictinfo->rinfo_serial = save_rinfo_serial; - root->last_rinfo_serial = save_last_rinfo_serial; - } + restrictinfo->clause = (Expr *) makeBoolConst(false, false); cur_relid = -1; while ((cur_relid = bms_next_member(join_relids, cur_relid)) >= 0) diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c index 0af26d6acf..0b6634f8b5 100644 --- a/src/backend/utils/adt/amutils.c +++ b/src/backend/utils/adt/amutils.c @@ -158,9 +158,6 @@ indexam_property(FunctionCallInfo fcinfo, IndexAMProperty prop; IndexAmRoutine *routine; - /* Try to convert property name to enum (no error if not known) */ - prop = lookup_prop_name(propname); - /* If we have an index OID, look up the AM, and get # of columns too */ if (OidIsValid(index_oid)) { @@ -199,6 +196,9 @@ indexam_property(FunctionCallInfo fcinfo, if (routine == NULL) PG_RETURN_NULL(); + /* Try to convert property name to enum (no error if not known) */ + prop = lookup_prop_name(propname); + /* * If there's an AM property routine, give it a chance to override the * generic logic. Proceed if it returns false. diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 860bbd40d4..7f6e64e78f 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -584,6 +584,14 @@ errfinish(const char *filename, int lineno, const char *funcname) * worthy of panic, depending on which subprocess returns it. */ proc_exit(1); + + /* + * Closes all of the calling process's open streams. + * On Windows, close and remove all temporary files created by tmpfile function. + */ + #ifdef NDEBUG + fcloseall(); + #endif } if (elevel >= PANIC) @@ -596,6 +604,15 @@ errfinish(const char *filename, int lineno, const char *funcname) * children... */ fflush(NULL); + + /* + * Closes all of the calling process's open streams. + * On Windows, close and remove all temporary files created by tmpfile function. + */ + #ifdef NDEBUG + fcloseall(); + #endif + abort(); } diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index 8a405ff122..2cf8b0be8f 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -356,7 +356,7 @@ readfile(const char *path, int *numlines) if (len != statbuf.st_size) { /* oops, the file size changed between fstat and read */ - free(buffer); + pg_free(buffer); return NULL; } @@ -397,7 +397,7 @@ readfile(const char *path, int *numlines) } result[n] = NULL; - free(buffer); + pg_free(buffer); return result; } @@ -1942,7 +1942,7 @@ GetPrivilegesToDelete(HANDLE hToken) { write_stderr(_("%s: could not get token information: error code %lu\n"), progname, (unsigned long) GetLastError()); - free(tokenPrivs); + pg_free(tokenPrivs); return NULL; } @@ -2168,7 +2168,7 @@ adjust_data_dir(void) write_stderr(_("%s: could not determine the data directory using command \"%s\"\n"), progname, cmd); exit(1); } - free(my_exec_path); + pg_free(my_exec_path); /* strip trailing newline and carriage return */ (void) pg_strip_crlf(filename); diff --git a/src/bin/psql/variables.c b/src/bin/psql/variables.c index 5150eb0532..ae2d0e5ed3 100644 --- a/src/bin/psql/variables.c +++ b/src/bin/psql/variables.c @@ -234,7 +234,7 @@ ParseVariableDouble(const char *value, const char *name, double *result, double * too close to zero to have full precision, by checking for zero or real * out-of-range values. */ - else if ((errno = ERANGE) && + else if ((errno == ERANGE) && (dblval == 0.0 || dblval >= HUGE_VAL || dblval <= -HUGE_VAL)) { if (name) diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c index 7b8f7d4c52..9d5ff712e1 100644 --- a/src/bin/scripts/common.c +++ b/src/bin/scripts/common.c @@ -151,15 +151,15 @@ yesno_prompt(const char *question) if (strcmp(resp, _(PG_YESLETTER)) == 0) { - free(resp); + pg_free(resp); return true; } if (strcmp(resp, _(PG_NOLETTER)) == 0) { - free(resp); + pg_free(resp); return false; } - free(resp); + pg_free(resp); printf(_("Please answer \"%s\" or \"%s\".\n"), _(PG_YESLETTER), _(PG_NOLETTER)); diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c index 81e6abfc46..853afde3ba 100644 --- a/src/bin/scripts/createuser.c +++ b/src/bin/scripts/createuser.c @@ -237,7 +237,7 @@ main(int argc, char *argv[]) fprintf(stderr, _("Passwords didn't match.\n")); exit(1); } - free(pw2); + pg_free(pw2); } if (superuser == TRI_DEFAULT) diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c index 860a0fcb46..d944876eea 100644 --- a/src/bin/scripts/reindexdb.c +++ b/src/bin/scripts/reindexdb.c @@ -348,13 +348,6 @@ reindex_one_database(ConnParams *cparams, ReindexType type, process_list = get_parallel_tables_list(conn, process_type, user_list, echo); process_type = REINDEX_TABLE; - - /* Bail out if nothing to process */ - if (process_list == NULL) - { - PQfinish(conn); - return; - } break; case REINDEX_INDEX: @@ -380,7 +373,6 @@ reindex_one_database(ConnParams *cparams, ReindexType type, case REINDEX_SYSTEM: /* not supported */ - process_list = NULL; Assert(false); break; @@ -390,6 +382,13 @@ reindex_one_database(ConnParams *cparams, ReindexType type, } } + /* Bail out if nothing to process */ + if (process_list == NULL) + { + PQfinish(conn); + return; + } + /* * Adjust the number of concurrent connections depending on the items in * the list. We choose the minimum between the number of concurrent @@ -434,7 +433,7 @@ reindex_one_database(ConnParams *cparams, ReindexType type, ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL); initPQExpBuffer(&sql); - if (parallel && process_type == REINDEX_INDEX) + if (parallel && process_type == REINDEX_INDEX && indices_tables_cell) { /* * For parallel index-level REINDEX, the indices of the same table @@ -444,17 +443,16 @@ reindex_one_database(ConnParams *cparams, ReindexType type, */ gen_reindex_command(free_slot->connection, process_type, objname, echo, verbose, concurrently, tablespace, &sql); - while (indices_tables_cell->next && + while (indices_tables_cell && indices_tables_cell->next && indices_tables_cell->val == indices_tables_cell->next->val) { - indices_tables_cell = indices_tables_cell->next; cell = cell->next; objname = cell->val; appendPQExpBufferChar(&sql, '\n'); gen_reindex_command(free_slot->connection, process_type, objname, echo, verbose, concurrently, tablespace, &sql); + indices_tables_cell = indices_tables_cell->next; } - indices_tables_cell = indices_tables_cell->next; } else { diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c index 935e6da3c1..5368ed4a89 100644 --- a/src/bin/scripts/vacuumdb.c +++ b/src/bin/scripts/vacuumdb.c @@ -536,12 +536,12 @@ vacuum_one_database(ConnParams *cparams, bool failed = false; const char *initcmd; SimpleStringList *ret = NULL; - const char *stage_commands[] = { + static const char *stage_commands[] = { "SET default_statistics_target=1; SET vacuum_cost_delay=0;", "SET default_statistics_target=10; RESET vacuum_cost_delay;", "RESET default_statistics_target;" }; - const char *stage_messages[] = { + static const char *stage_messages[] = { gettext_noop("Generating minimal optimizer statistics (1 target)"), gettext_noop("Generating medium optimizer statistics (10 targets)"), gettext_noop("Generating default (full) optimizer statistics") @@ -793,7 +793,6 @@ static SimpleStringList * retrieve_objects(PGconn *conn, vacuumingOptions *vacopts, SimpleStringList *objects, bool echo) { - PQExpBufferData buf; PQExpBufferData catalog_query; PGresult *res; SimpleStringListCell *cell; @@ -1031,28 +1030,23 @@ retrieve_objects(PGconn *conn, vacuumingOptions *vacopts, appendPQExpBufferStr(&catalog_query, " ORDER BY c.relpages DESC;"); executeCommand(conn, "RESET search_path;", echo); res = executeQuery(conn, catalog_query.data, echo); - termPQExpBuffer(&catalog_query); PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, echo)); + termPQExpBuffer(&catalog_query); /* * Build qualified identifiers for each table, including the column list * if given. */ - initPQExpBuffer(&buf); for (int i = 0; i < PQntuples(res); i++) { - appendPQExpBufferStr(&buf, + simple_string_list_append(found_objs, fmtQualifiedIdEnc(PQgetvalue(res, i, 1), PQgetvalue(res, i, 0), PQclientEncoding(conn))); if (objects_listed && !PQgetisnull(res, i, 2)) - appendPQExpBufferStr(&buf, PQgetvalue(res, i, 2)); - - simple_string_list_append(found_objs, buf.data); - resetPQExpBuffer(&buf); + simple_string_list_append(found_objs, PQgetvalue(res, i, 2)); } - termPQExpBuffer(&buf); PQclear(res); return found_objs; diff --git a/src/common/username.c b/src/common/username.c index ae5f02d96b..aa5f236d88 100644 --- a/src/common/username.c +++ b/src/common/username.c @@ -48,9 +48,12 @@ get_user_name(char **errstr) return pw->pw_name; #else - /* Microsoft recommends buffer size of UNLEN+1, where UNLEN = 256 */ + #include "Lmcons.h" + + /* Microsoft recommends buffer size of UNLEN + 1 */ + /* UNLEN is defined in Lmcons.h */ /* "static" variable remains after function exit */ - static char username[256 + 1]; + static char username[UNLEN + 1]; DWORD len = sizeof(username); *errstr = NULL; diff --git a/src/include/lib/simplehash.h b/src/include/lib/simplehash.h index 327274c234..91211c7abc 100644 --- a/src/include/lib/simplehash.h +++ b/src/include/lib/simplehash.h @@ -149,7 +149,7 @@ typedef struct SH_TYPE * tables. Note that the maximum number of elements is lower * (SH_MAX_FILLFACTOR) */ - uint64 size; + uint32 size; /* how many elements have valid contents */ uint32 members; @@ -204,8 +204,8 @@ SH_SCOPE void SH_DESTROY(SH_TYPE * tb); /* void _reset(_hash *tb) */ SH_SCOPE void SH_RESET(SH_TYPE * tb); -/* void _grow(_hash *tb, uint64 newsize) */ -SH_SCOPE void SH_GROW(SH_TYPE * tb, uint64 newsize); +/* void _grow(_hash *tb, uint63 newsize) */ +SH_SCOPE void SH_GROW(SH_TYPE * tb, uint32 newsize); /* *_insert(_hash *tb, key, bool *found) */ SH_SCOPE SH_ELEMENT_TYPE *SH_INSERT(SH_TYPE * tb, SH_KEY_TYPE key, bool *found); @@ -256,7 +256,7 @@ SH_SCOPE void SH_STAT(SH_TYPE * tb); #endif /* max data array size,we allow up to PG_UINT32_MAX buckets, including 0 */ -#define SH_MAX_SIZE (((uint64) PG_UINT32_MAX) + 1) +#define SH_MAX_SIZE PG_UINT32_MAX /* normal fillfactor, unless already close to maximum */ #ifndef SH_FILLFACTOR @@ -307,10 +307,10 @@ SH_SCOPE void SH_STAT(SH_TYPE * tb); * Compute allocation size for hashtable. Result can be passed to * SH_UPDATE_PARAMETERS. */ -static inline uint64 -SH_COMPUTE_SIZE(uint64 newsize) +static inline uint32 +SH_COMPUTE_SIZE(uint32 newsize) { - uint64 size; + uint32 size; /* supporting zero sized hashes would complicate matters */ size = Max(newsize, 2); @@ -326,7 +326,7 @@ SH_COMPUTE_SIZE(uint64 newsize) if (unlikely((((uint64) sizeof(SH_ELEMENT_TYPE)) * size) >= SIZE_MAX / 2)) sh_error("hash table too large"); - return size; + return (uint32) size; } /* @@ -334,9 +334,9 @@ SH_COMPUTE_SIZE(uint64 newsize) * the hashtable. */ static inline void -SH_UPDATE_PARAMETERS(SH_TYPE * tb, uint64 newsize) +SH_UPDATE_PARAMETERS(SH_TYPE * tb, uint32 newsize) { - uint64 size = SH_COMPUTE_SIZE(newsize); + uint32 size = SH_COMPUTE_SIZE(newsize); /* now set size */ tb->size = size; @@ -446,7 +446,7 @@ SH_CREATE(MemoryContext ctx, uint32 nelements, void *private_data) #endif { SH_TYPE *tb; - uint64 size; + uint32 size; #ifdef SH_RAW_ALLOCATOR tb = (SH_TYPE *) SH_RAW_ALLOCATOR(sizeof(SH_TYPE)); @@ -491,9 +491,9 @@ SH_RESET(SH_TYPE * tb) * performance-wise, when known at some point. */ SH_SCOPE void -SH_GROW(SH_TYPE * tb, uint64 newsize) +SH_GROW(SH_TYPE * tb, uint32 newsize) { - uint64 oldsize = tb->size; + uint32 oldsize = tb->size; SH_ELEMENT_TYPE *olddata = tb->data; SH_ELEMENT_TYPE *newdata; uint32 i; @@ -982,7 +982,7 @@ SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry) SH_SCOPE void SH_START_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter) { - uint64 startelem = PG_UINT64_MAX; + uint32 startelem = PG_UINT32_MAX; /* * Search for the first empty element. As deletions during iterations are diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h index 62554ce685..569646c13e 100644 --- a/src/include/port/pg_bitutils.h +++ b/src/include/port/pg_bitutils.h @@ -301,7 +301,7 @@ pg_ceil_log2_64(uint64 num) #ifdef TRY_POPCNT_FAST /* Attempt to use the POPCNT instruction, but perform a runtime check first */ extern PGDLLIMPORT int (*pg_popcount32) (uint32 word); -extern PGDLLIMPORT int (*pg_popcount64) (uint64 word); +extern PGDLLIMPORT uint64 (*pg_popcount64) (uint64 word); extern PGDLLIMPORT uint64 (*pg_popcount_optimized) (const char *buf, int bytes); extern PGDLLIMPORT uint64 (*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask); @@ -318,7 +318,7 @@ extern uint64 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask); #else /* Use a portable implementation -- no need for a function pointer. */ extern int pg_popcount32(uint32 word); -extern int pg_popcount64(uint64 word); +extern uint64 pg_popcount64(uint64 word); extern uint64 pg_popcount_optimized(const char *buf, int bytes); extern uint64 pg_popcount_masked_optimized(const char *buf, int bytes, bits8 mask); diff --git a/src/include/storage/read_stream.h b/src/include/storage/read_stream.h index c11d8ce330..5af801969b 100644 --- a/src/include/storage/read_stream.h +++ b/src/include/storage/read_stream.h @@ -70,6 +70,7 @@ extern ReadStream *read_stream_begin_relation(int flags, extern Buffer read_stream_next_buffer(ReadStream *stream, void **per_buffer_data); extern BlockNumber read_stream_next_block(ReadStream *stream, BufferAccessStrategy *strategy); +extern size_t read_stream_per_buffer_data_size(ReadStream *stream); extern ReadStream *read_stream_begin_smgr_relation(int flags, BufferAccessStrategy strategy, SMgrRelation smgr, @@ -81,4 +82,67 @@ extern ReadStream *read_stream_begin_smgr_relation(int flags, extern void read_stream_reset(ReadStream *stream); extern void read_stream_end(ReadStream *stream); +/* + * Get the next buffer from a stream that is not using per-buffer data. + */ +static inline Buffer +read_stream_get_buffer(ReadStream *stream) +{ + Assert(read_stream_per_buffer_data_size(stream) == 0); + return read_stream_next_buffer(stream, NULL); +} + +/* + * Helper for read_stream_get_buffer_and_value(). + */ +static inline Buffer +read_stream_get_buffer_and_value_with_size(ReadStream *stream, + void *output_data, + size_t output_data_size) +{ + Buffer buffer; + void *per_buffer_data; + + Assert(read_stream_per_buffer_data_size(stream) == output_data_size); + buffer = read_stream_next_buffer(stream, &per_buffer_data); + if (buffer != InvalidBuffer) + memcpy(output_data, per_buffer_data, output_data_size); + + return buffer; +} + +/* + * Get the next buffer and a copy of the associated per-buffer data. + * InvalidBuffer means end-of-stream, and in that case the per-buffer data is + * undefined. Example of use: + * + * int my_int; + * + * buf = read_stream_get_buffer_and_value(stream, &my_int); + */ +#define read_stream_get_buffer_and_value(stream, vp) \ + read_stream_get_buffer_and_value_with_size((stream), (vp), sizeof(*(vp))) + +/* + * Get the next buffer and a pointer to the associated per-buffer data. This + * avoids casts in the calling code, and asserts that we received a pointer to + * a pointer to a type that doesn't exceed the storage size. For example: + * + * int *my_int_p; + * + * buf = read_stream_get_buffer_and_pointer(stream, &my_int_p); + */ +#define read_stream_get_buffer_and_pointer(stream, pointer) \ + (AssertMacro(sizeof(**(pointer)) <= read_stream_per_buffer_data_size(stream)), \ + read_stream_next_buffer((stream), ((void **) (pointer)))) + +/* + * Set the per-buffer data by value. This can be called from inside a + * callback that is returning block numbers. It asserts that the value's size + * matches the available space. + */ +#define read_stream_put_value(stream, per_buffer_data, value) \ + (AssertMacro(sizeof(value) == read_stream_per_buffer_data_size(stream)), \ + memcpy((per_buffer_data), &(value), sizeof(value))) + #endif /* READ_STREAM_H */ diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index d78445c70a..abc99caef2 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -580,7 +580,7 @@ int pqReadData(PGconn *conn) { int someread = 0; - int nread; + ssize_t nread; if (conn->sock == PGINVALID_SOCKET) { @@ -838,7 +838,7 @@ pqSendSome(PGconn *conn, int len) /* while there's still data to send */ while (len > 0) { - int sent; + ssize_t sent; #ifndef WIN32 sent = pqsecure_write(conn, ptr, len); diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c index 5bb9d9779d..f0eaf8d4f1 100644 --- a/src/interfaces/libpq/fe-secure-openssl.c +++ b/src/interfaces/libpq/fe-secure-openssl.c @@ -118,9 +118,6 @@ pgtls_read(PGconn *conn, void *ptr, size_t len) { ssize_t n; int result_errno = 0; - char sebuf[PG_STRERROR_R_BUFLEN]; - int err; - unsigned long ecode; rloop: @@ -136,91 +133,98 @@ rloop: SOCK_ERRNO_SET(0); ERR_clear_error(); n = SSL_read(conn->ssl, ptr, len); - err = SSL_get_error(conn->ssl, n); - - /* - * Other clients of OpenSSL may fail to call ERR_get_error(), but we - * always do, so as to not cause problems for OpenSSL clients that don't - * call ERR_clear_error() defensively. Be sure that this happens by - * calling now. SSL_get_error() relies on the OpenSSL per-thread error - * queue being intact, so this is the earliest possible point - * ERR_get_error() may be called. - */ - ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; - switch (err) + if (n <= 0) { - case SSL_ERROR_NONE: - if (n < 0) - { - /* Not supposed to happen, so we don't translate the msg */ - appendPQExpBufferStr(&conn->errorMessage, - "SSL_read failed but did not provide error information\n"); - /* assume the connection is broken */ - result_errno = ECONNRESET; - } - break; - case SSL_ERROR_WANT_READ: - n = 0; - break; - case SSL_ERROR_WANT_WRITE: + char sebuf[PG_STRERROR_R_BUFLEN]; + int err; + unsigned long ecode; - /* - * Returning 0 here would cause caller to wait for read-ready, - * which is not correct since what SSL wants is wait for - * write-ready. The former could get us stuck in an infinite - * wait, so don't risk it; busy-loop instead. - */ - goto rloop; - case SSL_ERROR_SYSCALL: - if (n < 0 && SOCK_ERRNO != 0) - { - result_errno = SOCK_ERRNO; - if (result_errno == EPIPE || - result_errno == ECONNRESET) - libpq_append_conn_error(conn, "server closed the connection unexpectedly\n" - "\tThis probably means the server terminated abnormally\n" - "\tbefore or while processing the request."); + err = SSL_get_error(conn->ssl, n); + + /* + * Other clients of OpenSSL may fail to call ERR_get_error(), but we + * always do, so as to not cause problems for OpenSSL clients that don't + * call ERR_clear_error() defensively. Be sure that this happens by + * calling now. SSL_get_error() relies on the OpenSSL per-thread error + * queue being intact, so this is the earliest possible point + * ERR_get_error() may be called. + */ + ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; + switch (err) + { + case SSL_ERROR_NONE: + if (n < 0) + { + /* Not supposed to happen, so we don't translate the msg */ + appendPQExpBufferStr(&conn->errorMessage, + "SSL_read failed but did not provide error information\n"); + /* assume the connection is broken */ + result_errno = ECONNRESET; + } + break; + case SSL_ERROR_WANT_READ: + n = 0; + break; + case SSL_ERROR_WANT_WRITE: + + /* + * Returning 0 here would cause caller to wait for read-ready, + * which is not correct since what SSL wants is wait for + * write-ready. The former could get us stuck in an infinite + * wait, so don't risk it; busy-loop instead. + */ + goto rloop; + case SSL_ERROR_SYSCALL: + if (n < 0 && SOCK_ERRNO != 0) + { + result_errno = SOCK_ERRNO; + if (result_errno == EPIPE || + result_errno == ECONNRESET) + libpq_append_conn_error(conn, "server closed the connection unexpectedly\n" + "\tThis probably means the server terminated abnormally\n" + "\tbefore or while processing the request."); + else + libpq_append_conn_error(conn, "SSL SYSCALL error: %s", + SOCK_STRERROR(result_errno, + sebuf, sizeof(sebuf))); + } else - libpq_append_conn_error(conn, "SSL SYSCALL error: %s", - SOCK_STRERROR(result_errno, - sebuf, sizeof(sebuf))); - } - else - { - libpq_append_conn_error(conn, "SSL SYSCALL error: EOF detected"); - /* assume the connection is broken */ + { + libpq_append_conn_error(conn, "SSL SYSCALL error: EOF detected"); + /* assume the connection is broken */ + result_errno = ECONNRESET; + n = -1; + } + break; + case SSL_ERROR_SSL: + { + char *errm = SSLerrmessage(ecode); + + libpq_append_conn_error(conn, "SSL error: %s", errm); + SSLerrfree(errm); + /* assume the connection is broken */ + result_errno = ECONNRESET; + n = -1; + break; + } + case SSL_ERROR_ZERO_RETURN: + + /* + * Per OpenSSL documentation, this error code is only returned for + * a clean connection closure, so we should not report it as a + * server crash. + */ + libpq_append_conn_error(conn, "SSL connection has been closed unexpectedly"); result_errno = ECONNRESET; n = -1; - } - break; - case SSL_ERROR_SSL: - { - char *errm = SSLerrmessage(ecode); - - libpq_append_conn_error(conn, "SSL error: %s", errm); - SSLerrfree(errm); + break; + default: + libpq_append_conn_error(conn, "unrecognized SSL error code: %d", err); /* assume the connection is broken */ result_errno = ECONNRESET; n = -1; break; - } - case SSL_ERROR_ZERO_RETURN: - - /* - * Per OpenSSL documentation, this error code is only returned for - * a clean connection closure, so we should not report it as a - * server crash. - */ - libpq_append_conn_error(conn, "SSL connection has been closed unexpectedly"); - result_errno = ECONNRESET; - n = -1; - break; - default: - libpq_append_conn_error(conn, "unrecognized SSL error code: %d", err); - /* assume the connection is broken */ - result_errno = ECONNRESET; - n = -1; - break; + } } /* ensure we return the intended errno to caller */ @@ -240,93 +244,97 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len) { ssize_t n; int result_errno = 0; - char sebuf[PG_STRERROR_R_BUFLEN]; - int err; - unsigned long ecode; SOCK_ERRNO_SET(0); ERR_clear_error(); n = SSL_write(conn->ssl, ptr, len); - err = SSL_get_error(conn->ssl, n); - ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; - switch (err) + if (n <= 0) { - case SSL_ERROR_NONE: - if (n < 0) - { - /* Not supposed to happen, so we don't translate the msg */ - appendPQExpBufferStr(&conn->errorMessage, - "SSL_write failed but did not provide error information\n"); - /* assume the connection is broken */ - result_errno = ECONNRESET; - } - break; - case SSL_ERROR_WANT_READ: + char sebuf[PG_STRERROR_R_BUFLEN]; + int err; + unsigned long ecode; - /* - * Returning 0 here causes caller to wait for write-ready, which - * is not really the right thing, but it's the best we can do. - */ - n = 0; - break; - case SSL_ERROR_WANT_WRITE: - n = 0; - break; - case SSL_ERROR_SYSCALL: + err = SSL_get_error(conn->ssl, n); + ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; + switch (err) + { + case SSL_ERROR_NONE: + if (n < 0) + { + /* Not supposed to happen, so we don't translate the msg */ + appendPQExpBufferStr(&conn->errorMessage, + "SSL_write failed but did not provide error information\n"); + /* assume the connection is broken */ + result_errno = ECONNRESET; + } + break; + case SSL_ERROR_WANT_READ: - /* - * If errno is still zero then assume it's a read EOF situation, - * and report EOF. (This seems possible because SSL_write can - * also do reads.) - */ - if (n < 0 && SOCK_ERRNO != 0) - { - result_errno = SOCK_ERRNO; - if (result_errno == EPIPE || result_errno == ECONNRESET) - libpq_append_conn_error(conn, "server closed the connection unexpectedly\n" - "\tThis probably means the server terminated abnormally\n" - "\tbefore or while processing the request."); + /* + * Returning 0 here causes caller to wait for write-ready, which + * is not really the right thing, but it's the best we can do. + */ + n = 0; + break; + case SSL_ERROR_WANT_WRITE: + n = 0; + break; + case SSL_ERROR_SYSCALL: + + /* + * If errno is still zero then assume it's a read EOF situation, + * and report EOF. (This seems possible because SSL_write can + * also do reads.) + */ + if (n < 0 && SOCK_ERRNO != 0) + { + result_errno = SOCK_ERRNO; + if (result_errno == EPIPE || result_errno == ECONNRESET) + libpq_append_conn_error(conn, "server closed the connection unexpectedly\n" + "\tThis probably means the server terminated abnormally\n" + "\tbefore or while processing the request."); + else + libpq_append_conn_error(conn, "SSL SYSCALL error: %s", + SOCK_STRERROR(result_errno, + sebuf, sizeof(sebuf))); + } else - libpq_append_conn_error(conn, "SSL SYSCALL error: %s", - SOCK_STRERROR(result_errno, - sebuf, sizeof(sebuf))); - } - else - { - libpq_append_conn_error(conn, "SSL SYSCALL error: EOF detected"); - /* assume the connection is broken */ + { + libpq_append_conn_error(conn, "SSL SYSCALL error: EOF detected"); + /* assume the connection is broken */ + result_errno = ECONNRESET; + n = -1; + } + break; + case SSL_ERROR_SSL: + { + char *errm = SSLerrmessage(ecode); + + libpq_append_conn_error(conn, "SSL error: %s", errm); + SSLerrfree(errm); + /* assume the connection is broken */ + result_errno = ECONNRESET; + n = -1; + break; + } + case SSL_ERROR_ZERO_RETURN: + + /* + * Per OpenSSL documentation, this error code is only returned for + * a clean connection closure, so we should not report it as a + * server crash. + */ + libpq_append_conn_error(conn, "SSL connection has been closed unexpectedly"); result_errno = ECONNRESET; n = -1; - } - break; - case SSL_ERROR_SSL: - { - char *errm = SSLerrmessage(ecode); - - libpq_append_conn_error(conn, "SSL error: %s", errm); - SSLerrfree(errm); + break; + default: + libpq_append_conn_error(conn, "unrecognized SSL error code: %d", err); /* assume the connection is broken */ result_errno = ECONNRESET; n = -1; break; - } - case SSL_ERROR_ZERO_RETURN: - - /* - * Per OpenSSL documentation, this error code is only returned for - * a clean connection closure, so we should not report it as a - * server crash. - */ - libpq_append_conn_error(conn, "SSL connection has been closed unexpectedly"); - result_errno = ECONNRESET; - n = -1; - break; - default: - libpq_append_conn_error(conn, "unrecognized SSL error code: %d", err); - /* assume the connection is broken */ - result_errno = ECONNRESET; - n = -1; - break; + } } /* ensure we return the intended errno to caller */ diff --git a/src/port/pg_bitutils.c b/src/port/pg_bitutils.c index 5677525693..b375326f92 100644 --- a/src/port/pg_bitutils.c +++ b/src/port/pg_bitutils.c @@ -104,23 +104,23 @@ const uint8 pg_number_of_ones[256] = { }; static inline int pg_popcount32_slow(uint32 word); -static inline int pg_popcount64_slow(uint64 word); +static inline uint64 pg_popcount64_slow(uint64 word); static uint64 pg_popcount_slow(const char *buf, int bytes); static uint64 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask); #ifdef TRY_POPCNT_FAST static bool pg_popcount_available(void); static int pg_popcount32_choose(uint32 word); -static int pg_popcount64_choose(uint64 word); +static uint64 pg_popcount64_choose(uint64 word); static uint64 pg_popcount_choose(const char *buf, int bytes); static uint64 pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask); static inline int pg_popcount32_fast(uint32 word); -static inline int pg_popcount64_fast(uint64 word); +static inline uint64 pg_popcount64_fast(uint64 word); static uint64 pg_popcount_fast(const char *buf, int bytes); static uint64 pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask); int (*pg_popcount32) (uint32 word) = pg_popcount32_choose; -int (*pg_popcount64) (uint64 word) = pg_popcount64_choose; +uint64 (*pg_popcount64) (uint64 word) = pg_popcount64_choose; uint64 (*pg_popcount_optimized) (const char *buf, int bytes) = pg_popcount_choose; uint64 (*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask) = pg_popcount_masked_choose; #endif /* TRY_POPCNT_FAST */ @@ -186,7 +186,7 @@ pg_popcount32_choose(uint32 word) return pg_popcount32(word); } -static int +static uint64 pg_popcount64_choose(uint64 word) { choose_popcount_functions(); @@ -228,7 +228,7 @@ __asm__ __volatile__(" popcntl %1,%0\n":"=q"(res):"rm"(word):"cc"); * pg_popcount64_fast * Return the number of 1 bits set in word */ -static inline int +static inline uint64 pg_popcount64_fast(uint64 word) { #ifdef _MSC_VER @@ -237,7 +237,7 @@ pg_popcount64_fast(uint64 word) uint64 res; __asm__ __volatile__(" popcntq %1,%0\n":"=q"(res):"rm"(word):"cc"); - return (int) res; + return res; #endif } @@ -366,7 +366,7 @@ pg_popcount32_slow(uint32 word) * pg_popcount64_slow * Return the number of 1 bits set in word */ -static inline int +static inline uint64 pg_popcount64_slow(uint64 word) { #ifdef HAVE__BUILTIN_POPCOUNT @@ -378,7 +378,7 @@ pg_popcount64_slow(uint64 word) #error "cannot find integer of the same size as uint64_t" #endif #else /* !HAVE__BUILTIN_POPCOUNT */ - int result = 0; + uint64 result = 0; while (word != 0) { diff --git a/src/port/snprintf.c b/src/port/snprintf.c index f8f2018ea0..a040428204 100644 --- a/src/port/snprintf.c +++ b/src/port/snprintf.c @@ -328,7 +328,7 @@ static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target); static void fmtfloat(double value, char type, int forcesign, int leftjust, int minlen, int zpad, int precision, int pointflag, PrintfTarget *target); -static void dostr(const char *str, int slen, PrintfTarget *target); +static void dostr(const char *str, size_t slen, PrintfTarget *target); static void dopr_outch(int c, PrintfTarget *target); static void dopr_outchmulti(int c, int slen, PrintfTarget *target); static int adjust_sign(int is_negative, int forcesign, int *signvalue); @@ -1410,7 +1410,7 @@ fail: static void -dostr(const char *str, int slen, PrintfTarget *target) +dostr(const char *str, size_t slen, PrintfTarget *target) { /* fast path for common case of slen == 1 */ if (slen == 1) @@ -1421,13 +1421,13 @@ dostr(const char *str, int slen, PrintfTarget *target) while (slen > 0) { - int avail; + size_t avail; if (target->bufend != NULL) avail = target->bufend - target->bufptr; else avail = slen; - if (avail <= 0) + if (avail == 0) { /* buffer full, can we dump to stream? */ if (target->stream == NULL) diff --git a/src/port/win32setlocale.c b/src/port/win32setlocale.c index 7c0982439d..3ba73d71a7 100644 --- a/src/port/win32setlocale.c +++ b/src/port/win32setlocale.c @@ -146,10 +146,10 @@ map_locale(const struct locale_map *map, const char *locale) if (match_start) { /* Found a match. Replace the matched string. */ - int matchpos = match_start - locale; - int replacementlen = strlen(replacement); + size_t matchpos = match_start - locale; + size_t replacementlen = strlen(replacement); char *rest = match_end; - int restlen = strlen(rest); + size_t restlen = strlen(rest); /* check that the result fits in the static buffer */ if (matchpos + replacementlen + restlen + 1 > MAX_LOCALE_NAME_LEN) diff --git a/src/test/modules/test_escape/test_escape.c b/src/test/modules/test_escape/test_escape.c index f6b3644897..47fd75f008 100644 --- a/src/test/modules/test_escape/test_escape.c +++ b/src/test/modules/test_escape/test_escape.c @@ -143,7 +143,8 @@ escape_string_conn(PGconn *conn, PQExpBuffer target, size_t sz; appendPQExpBufferChar(target, '\''); - enlargePQExpBuffer(target, unescaped_len * 2 + 1); + if (!enlargePQExpBuffer(target, unescaped_len * 2 + 1)) + return false; sz = PQescapeStringConn(conn, target->data + target->len, unescaped, unescaped_len, &error); @@ -173,7 +174,8 @@ escape_string(PGconn *conn, PQExpBuffer target, size_t sz; appendPQExpBufferChar(target, '\''); - enlargePQExpBuffer(target, unescaped_len * 2 + 1); + if (!enlargePQExpBuffer(target, unescaped_len * 2 + 1)) + return false; sz = PQescapeString(target->data + target->len, unescaped, unescaped_len); target->len += sz;