mirror of
https://github.com/postgres/postgres.git
synced 2025-04-20 00:42:27 +03:00
pgindent run for release 9.3
This is the first run of the Perl-based pgindent script. Also update pgindent instructions.
This commit is contained in:
parent
07ab261ef3
commit
9af4159fce
@ -97,7 +97,6 @@ gbt_bit_xfrm(bytea *leaf)
|
|||||||
static GBT_VARKEY *
|
static GBT_VARKEY *
|
||||||
gbt_bit_l2n(GBT_VARKEY *leaf)
|
gbt_bit_l2n(GBT_VARKEY *leaf)
|
||||||
{
|
{
|
||||||
|
|
||||||
GBT_VARKEY *out = leaf;
|
GBT_VARKEY *out = leaf;
|
||||||
GBT_VARKEY_R r = gbt_var_key_readable(leaf);
|
GBT_VARKEY_R r = gbt_var_key_readable(leaf);
|
||||||
bytea *o;
|
bytea *o;
|
||||||
|
@ -121,7 +121,6 @@ gbt_text_compress(PG_FUNCTION_ARGS)
|
|||||||
Datum
|
Datum
|
||||||
gbt_bpchar_compress(PG_FUNCTION_ARGS)
|
gbt_bpchar_compress(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
|
||||||
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
||||||
GISTENTRY *retval;
|
GISTENTRY *retval;
|
||||||
|
|
||||||
|
@ -382,7 +382,6 @@ gbt_ts_union(PG_FUNCTION_ARGS)
|
|||||||
Datum
|
Datum
|
||||||
gbt_ts_penalty(PG_FUNCTION_ARGS)
|
gbt_ts_penalty(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
|
||||||
tsKEY *origentry = (tsKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
|
tsKEY *origentry = (tsKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
|
||||||
tsKEY *newentry = (tsKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
|
tsKEY *newentry = (tsKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
|
||||||
float *result = (float *) PG_GETARG_POINTER(2);
|
float *result = (float *) PG_GETARG_POINTER(2);
|
||||||
|
@ -137,7 +137,6 @@ gbt_num_union(GBT_NUMKEY *out, const GistEntryVector *entryvec, const gbtree_nin
|
|||||||
bool
|
bool
|
||||||
gbt_num_same(const GBT_NUMKEY *a, const GBT_NUMKEY *b, const gbtree_ninfo *tinfo)
|
gbt_num_same(const GBT_NUMKEY *a, const GBT_NUMKEY *b, const gbtree_ninfo *tinfo)
|
||||||
{
|
{
|
||||||
|
|
||||||
GBT_NUMKEY_R b1,
|
GBT_NUMKEY_R b1,
|
||||||
b2;
|
b2;
|
||||||
|
|
||||||
@ -159,7 +158,6 @@ gbt_num_same(const GBT_NUMKEY *a, const GBT_NUMKEY *b, const gbtree_ninfo *tinfo
|
|||||||
void
|
void
|
||||||
gbt_num_bin_union(Datum *u, GBT_NUMKEY *e, const gbtree_ninfo *tinfo)
|
gbt_num_bin_union(Datum *u, GBT_NUMKEY *e, const gbtree_ninfo *tinfo)
|
||||||
{
|
{
|
||||||
|
|
||||||
GBT_NUMKEY_R rd;
|
GBT_NUMKEY_R rd;
|
||||||
|
|
||||||
rd.lower = &e[0];
|
rd.lower = &e[0];
|
||||||
|
@ -56,7 +56,6 @@ gbt_var_decompress(PG_FUNCTION_ARGS)
|
|||||||
GBT_VARKEY_R
|
GBT_VARKEY_R
|
||||||
gbt_var_key_readable(const GBT_VARKEY *k)
|
gbt_var_key_readable(const GBT_VARKEY *k)
|
||||||
{
|
{
|
||||||
|
|
||||||
GBT_VARKEY_R r;
|
GBT_VARKEY_R r;
|
||||||
|
|
||||||
r.lower = (bytea *) &(((char *) k)[VARHDRSZ]);
|
r.lower = (bytea *) &(((char *) k)[VARHDRSZ]);
|
||||||
@ -270,7 +269,6 @@ gbt_var_bin_union(Datum *u, GBT_VARKEY *e, Oid collation,
|
|||||||
GISTENTRY *
|
GISTENTRY *
|
||||||
gbt_var_compress(GISTENTRY *entry, const gbtree_vinfo *tinfo)
|
gbt_var_compress(GISTENTRY *entry, const gbtree_vinfo *tinfo)
|
||||||
{
|
{
|
||||||
|
|
||||||
GISTENTRY *retval;
|
GISTENTRY *retval;
|
||||||
|
|
||||||
if (entry->leafkey)
|
if (entry->leafkey)
|
||||||
@ -299,7 +297,6 @@ GBT_VARKEY *
|
|||||||
gbt_var_union(const GistEntryVector *entryvec, int32 *size, Oid collation,
|
gbt_var_union(const GistEntryVector *entryvec, int32 *size, Oid collation,
|
||||||
const gbtree_vinfo *tinfo)
|
const gbtree_vinfo *tinfo)
|
||||||
{
|
{
|
||||||
|
|
||||||
int i = 0,
|
int i = 0,
|
||||||
numranges = entryvec->n;
|
numranges = entryvec->n;
|
||||||
GBT_VARKEY *cur;
|
GBT_VARKEY *cur;
|
||||||
|
@ -1973,7 +1973,7 @@ dblink_fdw_validator(PG_FUNCTION_ARGS)
|
|||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FDW_OUT_OF_MEMORY),
|
(errcode(ERRCODE_FDW_OUT_OF_MEMORY),
|
||||||
errmsg("out of memory"),
|
errmsg("out of memory"),
|
||||||
errdetail("could not get libpq's default connection options")));
|
errdetail("could not get libpq's default connection options")));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate each supplied option. */
|
/* Validate each supplied option. */
|
||||||
@ -1984,9 +1984,9 @@ dblink_fdw_validator(PG_FUNCTION_ARGS)
|
|||||||
if (!is_valid_dblink_option(options, def->defname, context))
|
if (!is_valid_dblink_option(options, def->defname, context))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Unknown option, or invalid option for the context specified,
|
* Unknown option, or invalid option for the context specified, so
|
||||||
* so complain about it. Provide a hint with list of valid
|
* complain about it. Provide a hint with list of valid options
|
||||||
* options for the context.
|
* for the context.
|
||||||
*/
|
*/
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
const PQconninfoOption *opt;
|
const PQconninfoOption *opt;
|
||||||
|
@ -140,8 +140,8 @@ static void fileGetOptions(Oid foreigntableid,
|
|||||||
char **filename, List **other_options);
|
char **filename, List **other_options);
|
||||||
static List *get_file_fdw_attribute_options(Oid relid);
|
static List *get_file_fdw_attribute_options(Oid relid);
|
||||||
static bool check_selective_binary_conversion(RelOptInfo *baserel,
|
static bool check_selective_binary_conversion(RelOptInfo *baserel,
|
||||||
Oid foreigntableid,
|
Oid foreigntableid,
|
||||||
List **columns);
|
List **columns);
|
||||||
static void estimate_size(PlannerInfo *root, RelOptInfo *baserel,
|
static void estimate_size(PlannerInfo *root, RelOptInfo *baserel,
|
||||||
FileFdwPlanState *fdw_private);
|
FileFdwPlanState *fdw_private);
|
||||||
static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
|
static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
|
||||||
@ -478,7 +478,7 @@ fileGetForeignPaths(PlannerInfo *root,
|
|||||||
&startup_cost, &total_cost);
|
&startup_cost, &total_cost);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a ForeignPath node and add it as only possible path. We use the
|
* Create a ForeignPath node and add it as only possible path. We use the
|
||||||
* fdw_private list of the path to carry the convert_selectively option;
|
* fdw_private list of the path to carry the convert_selectively option;
|
||||||
* it will be propagated into the fdw_private list of the Plan node.
|
* it will be propagated into the fdw_private list of the Plan node.
|
||||||
*/
|
*/
|
||||||
@ -770,7 +770,7 @@ check_selective_binary_conversion(RelOptInfo *baserel,
|
|||||||
/* Add all the attributes used by restriction clauses. */
|
/* Add all the attributes used by restriction clauses. */
|
||||||
foreach(lc, baserel->baserestrictinfo)
|
foreach(lc, baserel->baserestrictinfo)
|
||||||
{
|
{
|
||||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
|
||||||
|
|
||||||
pull_varattnos((Node *) rinfo->clause, baserel->relid,
|
pull_varattnos((Node *) rinfo->clause, baserel->relid,
|
||||||
&attrs_used);
|
&attrs_used);
|
||||||
|
@ -1300,7 +1300,7 @@ hstore_to_json_loose(PG_FUNCTION_ARGS)
|
|||||||
* digit as numeric - could be a zip code or similar
|
* digit as numeric - could be a zip code or similar
|
||||||
*/
|
*/
|
||||||
if (src->len > 0 &&
|
if (src->len > 0 &&
|
||||||
!(src->data[0] == '0' && isdigit((unsigned char) src->data[1])) &&
|
!(src->data[0] == '0' && isdigit((unsigned char) src->data[1])) &&
|
||||||
strspn(src->data, "+-0123456789Ee.") == src->len)
|
strspn(src->data, "+-0123456789Ee.") == src->len)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -1308,9 +1308,9 @@ hstore_to_json_loose(PG_FUNCTION_ARGS)
|
|||||||
* value. Ignore any actual parsed value.
|
* value. Ignore any actual parsed value.
|
||||||
*/
|
*/
|
||||||
char *endptr = "junk";
|
char *endptr = "junk";
|
||||||
long lval;
|
long lval;
|
||||||
|
|
||||||
lval = strtol(src->data, &endptr, 10);
|
lval = strtol(src->data, &endptr, 10);
|
||||||
(void) lval;
|
(void) lval;
|
||||||
if (*endptr == '\0')
|
if (*endptr == '\0')
|
||||||
{
|
{
|
||||||
@ -1323,7 +1323,7 @@ hstore_to_json_loose(PG_FUNCTION_ARGS)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* not an int - try a double */
|
/* not an int - try a double */
|
||||||
double dval;
|
double dval;
|
||||||
|
|
||||||
dval = strtod(src->data, &endptr);
|
dval = strtod(src->data, &endptr);
|
||||||
(void) dval;
|
(void) dval;
|
||||||
|
@ -215,7 +215,7 @@ add_one_elt(char *eltname, eary *eary)
|
|||||||
{
|
{
|
||||||
eary ->alloc *= 2;
|
eary ->alloc *= 2;
|
||||||
eary ->array = (char **) pg_realloc(eary->array,
|
eary ->array = (char **) pg_realloc(eary->array,
|
||||||
eary->alloc * sizeof(char *));
|
eary->alloc * sizeof(char *));
|
||||||
}
|
}
|
||||||
|
|
||||||
eary ->array[eary->num] = pg_strdup(eltname);
|
eary ->array[eary->num] = pg_strdup(eltname);
|
||||||
|
@ -299,8 +299,8 @@ main(int argc, char **argv)
|
|||||||
dryrun = true;
|
dryrun = true;
|
||||||
break;
|
break;
|
||||||
case 'x':
|
case 'x':
|
||||||
additional_ext = strdup(optarg); /* Extension to remove from
|
additional_ext = strdup(optarg); /* Extension to remove
|
||||||
* xlogfile names */
|
* from xlogfile names */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
|
fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
|
||||||
|
@ -593,7 +593,7 @@ main(int argc, char **argv)
|
|||||||
* There's no way to trigger failover via signal on Windows.
|
* There's no way to trigger failover via signal on Windows.
|
||||||
*/
|
*/
|
||||||
(void) pqsignal(SIGUSR1, sighandler);
|
(void) pqsignal(SIGUSR1, sighandler);
|
||||||
(void) pqsignal(SIGINT, sighandler); /* deprecated, use SIGUSR1 */
|
(void) pqsignal(SIGINT, sighandler); /* deprecated, use SIGUSR1 */
|
||||||
(void) pqsignal(SIGQUIT, sigquit_handler);
|
(void) pqsignal(SIGQUIT, sigquit_handler);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ static uint64 test_timing(int32);
|
|||||||
static void output(uint64 loop_count);
|
static void output(uint64 loop_count);
|
||||||
|
|
||||||
/* record duration in powers of 2 microseconds */
|
/* record duration in powers of 2 microseconds */
|
||||||
int64 histogram[32];
|
int64 histogram[32];
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
@ -110,8 +110,11 @@ test_timing(int32 duration)
|
|||||||
uint64 total_time;
|
uint64 total_time;
|
||||||
int64 time_elapsed = 0;
|
int64 time_elapsed = 0;
|
||||||
uint64 loop_count = 0;
|
uint64 loop_count = 0;
|
||||||
uint64 prev, cur;
|
uint64 prev,
|
||||||
instr_time start_time, end_time, temp;
|
cur;
|
||||||
|
instr_time start_time,
|
||||||
|
end_time,
|
||||||
|
temp;
|
||||||
|
|
||||||
total_time = duration > 0 ? duration * 1000000 : 0;
|
total_time = duration > 0 ? duration * 1000000 : 0;
|
||||||
|
|
||||||
@ -120,7 +123,8 @@ test_timing(int32 duration)
|
|||||||
|
|
||||||
while (time_elapsed < total_time)
|
while (time_elapsed < total_time)
|
||||||
{
|
{
|
||||||
int32 diff, bits = 0;
|
int32 diff,
|
||||||
|
bits = 0;
|
||||||
|
|
||||||
prev = cur;
|
prev = cur;
|
||||||
INSTR_TIME_SET_CURRENT(temp);
|
INSTR_TIME_SET_CURRENT(temp);
|
||||||
@ -163,7 +167,8 @@ test_timing(int32 duration)
|
|||||||
static void
|
static void
|
||||||
output(uint64 loop_count)
|
output(uint64 loop_count)
|
||||||
{
|
{
|
||||||
int64 max_bit = 31, i;
|
int64 max_bit = 31,
|
||||||
|
i;
|
||||||
|
|
||||||
/* find highest bit value */
|
/* find highest bit value */
|
||||||
while (max_bit > 0 && histogram[max_bit] == 0)
|
while (max_bit > 0 && histogram[max_bit] == 0)
|
||||||
@ -179,6 +184,6 @@ output(uint64 loop_count)
|
|||||||
/* lame hack to work around INT64_FORMAT deficiencies */
|
/* lame hack to work around INT64_FORMAT deficiencies */
|
||||||
snprintf(buf, sizeof(buf), INT64_FORMAT, histogram[i]);
|
snprintf(buf, sizeof(buf), INT64_FORMAT, histogram[i]);
|
||||||
printf("%6ld %9.5f %10s\n", 1l << i,
|
printf("%6ld %9.5f %10s\n", 1l << i,
|
||||||
(double) histogram[i] * 100 / loop_count, buf);
|
(double) histogram[i] * 100 / loop_count, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,8 +347,8 @@ get_wildcard_part(const char *str, int lenstr,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Back up endword to the escape character when stopping at
|
* Back up endword to the escape character when stopping at an
|
||||||
* an escaped char, so that subsequent get_wildcard_part will
|
* escaped char, so that subsequent get_wildcard_part will
|
||||||
* restart from the escape character. We assume here that
|
* restart from the escape character. We assume here that
|
||||||
* escape chars are single-byte.
|
* escape chars are single-byte.
|
||||||
*/
|
*/
|
||||||
|
@ -36,8 +36,8 @@ fix_path_separator(char *path)
|
|||||||
{
|
{
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
|
||||||
char *result;
|
char *result;
|
||||||
char *c;
|
char *c;
|
||||||
|
|
||||||
result = pg_strdup(path);
|
result = pg_strdup(path);
|
||||||
|
|
||||||
@ -46,11 +46,9 @@ fix_path_separator(char *path)
|
|||||||
*c = '\\';
|
*c = '\\';
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,17 +154,17 @@ check_new_cluster(void)
|
|||||||
check_is_super_user(&new_cluster);
|
check_is_super_user(&new_cluster);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't restore our own user, so both clusters must match have
|
* We don't restore our own user, so both clusters must match have
|
||||||
* matching install-user oids.
|
* matching install-user oids.
|
||||||
*/
|
*/
|
||||||
if (old_cluster.install_role_oid != new_cluster.install_role_oid)
|
if (old_cluster.install_role_oid != new_cluster.install_role_oid)
|
||||||
pg_log(PG_FATAL,
|
pg_log(PG_FATAL,
|
||||||
"Old and new cluster install users have different values for pg_authid.oid.\n");
|
"Old and new cluster install users have different values for pg_authid.oid.\n");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We only allow the install user in the new cluster because other
|
* We only allow the install user in the new cluster because other defined
|
||||||
* defined users might match users defined in the old cluster and
|
* users might match users defined in the old cluster and generate an
|
||||||
* generate an error during pg_dump restore.
|
* error during pg_dump restore.
|
||||||
*/
|
*/
|
||||||
if (new_cluster.role_count != 1)
|
if (new_cluster.role_count != 1)
|
||||||
pg_log(PG_FATAL, "Only the install user can be defined in the new cluster.\n");
|
pg_log(PG_FATAL, "Only the install user can be defined in the new cluster.\n");
|
||||||
@ -247,14 +245,14 @@ output_completion_banner(char *analyze_script_file_name,
|
|||||||
|
|
||||||
if (deletion_script_file_name)
|
if (deletion_script_file_name)
|
||||||
pg_log(PG_REPORT,
|
pg_log(PG_REPORT,
|
||||||
"Running this script will delete the old cluster's data files:\n"
|
"Running this script will delete the old cluster's data files:\n"
|
||||||
" %s\n",
|
" %s\n",
|
||||||
deletion_script_file_name);
|
deletion_script_file_name);
|
||||||
else
|
else
|
||||||
pg_log(PG_REPORT,
|
pg_log(PG_REPORT,
|
||||||
"Could not create a script to delete the old cluster's data\n"
|
"Could not create a script to delete the old cluster's data\n"
|
||||||
"files because user-defined tablespaces exist in the old cluster\n"
|
"files because user-defined tablespaces exist in the old cluster\n"
|
||||||
"directory. The old cluster's contents must be deleted manually.\n");
|
"directory. The old cluster's contents must be deleted manually.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -323,8 +321,8 @@ check_cluster_compatibility(bool live_check)
|
|||||||
/* We read the real port number for PG >= 9.1 */
|
/* We read the real port number for PG >= 9.1 */
|
||||||
if (live_check && GET_MAJOR_VERSION(old_cluster.major_version) < 901 &&
|
if (live_check && GET_MAJOR_VERSION(old_cluster.major_version) < 901 &&
|
||||||
old_cluster.port == DEF_PGUPORT)
|
old_cluster.port == DEF_PGUPORT)
|
||||||
pg_log(PG_FATAL, "When checking a pre-PG 9.1 live old server, "
|
pg_log(PG_FATAL, "When checking a pre-PG 9.1 live old server, "
|
||||||
"you must specify the old server's port number.\n");
|
"you must specify the old server's port number.\n");
|
||||||
|
|
||||||
if (live_check && old_cluster.port == new_cluster.port)
|
if (live_check && old_cluster.port == new_cluster.port)
|
||||||
pg_log(PG_FATAL, "When checking a live server, "
|
pg_log(PG_FATAL, "When checking a live server, "
|
||||||
@ -366,18 +364,18 @@ set_locale_and_encoding(ClusterInfo *cluster)
|
|||||||
if (GET_MAJOR_VERSION(cluster->major_version) < 902)
|
if (GET_MAJOR_VERSION(cluster->major_version) < 902)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Pre-9.2 did not canonicalize the supplied locale names
|
* Pre-9.2 did not canonicalize the supplied locale names to match
|
||||||
* to match what the system returns, while 9.2+ does, so
|
* what the system returns, while 9.2+ does, so convert pre-9.2 to
|
||||||
* convert pre-9.2 to match.
|
* match.
|
||||||
*/
|
*/
|
||||||
ctrl->lc_collate = get_canonical_locale_name(LC_COLLATE,
|
ctrl->lc_collate = get_canonical_locale_name(LC_COLLATE,
|
||||||
pg_strdup(PQgetvalue(res, 0, i_datcollate)));
|
pg_strdup(PQgetvalue(res, 0, i_datcollate)));
|
||||||
ctrl->lc_ctype = get_canonical_locale_name(LC_CTYPE,
|
ctrl->lc_ctype = get_canonical_locale_name(LC_CTYPE,
|
||||||
pg_strdup(PQgetvalue(res, 0, i_datctype)));
|
pg_strdup(PQgetvalue(res, 0, i_datctype)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ctrl->lc_collate = pg_strdup(PQgetvalue(res, 0, i_datcollate));
|
ctrl->lc_collate = pg_strdup(PQgetvalue(res, 0, i_datcollate));
|
||||||
ctrl->lc_ctype = pg_strdup(PQgetvalue(res, 0, i_datctype));
|
ctrl->lc_ctype = pg_strdup(PQgetvalue(res, 0, i_datctype));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,21 +408,21 @@ check_locale_and_encoding(ControlData *oldctrl,
|
|||||||
ControlData *newctrl)
|
ControlData *newctrl)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* These are often defined with inconsistent case, so use pg_strcasecmp().
|
* These are often defined with inconsistent case, so use pg_strcasecmp().
|
||||||
* They also often use inconsistent hyphenation, which we cannot fix, e.g.
|
* They also often use inconsistent hyphenation, which we cannot fix, e.g.
|
||||||
* UTF-8 vs. UTF8, so at least we display the mismatching values.
|
* UTF-8 vs. UTF8, so at least we display the mismatching values.
|
||||||
*/
|
*/
|
||||||
if (pg_strcasecmp(oldctrl->lc_collate, newctrl->lc_collate) != 0)
|
if (pg_strcasecmp(oldctrl->lc_collate, newctrl->lc_collate) != 0)
|
||||||
pg_log(PG_FATAL,
|
pg_log(PG_FATAL,
|
||||||
"lc_collate cluster values do not match: old \"%s\", new \"%s\"\n",
|
"lc_collate cluster values do not match: old \"%s\", new \"%s\"\n",
|
||||||
oldctrl->lc_collate, newctrl->lc_collate);
|
oldctrl->lc_collate, newctrl->lc_collate);
|
||||||
if (pg_strcasecmp(oldctrl->lc_ctype, newctrl->lc_ctype) != 0)
|
if (pg_strcasecmp(oldctrl->lc_ctype, newctrl->lc_ctype) != 0)
|
||||||
pg_log(PG_FATAL,
|
pg_log(PG_FATAL,
|
||||||
"lc_ctype cluster values do not match: old \"%s\", new \"%s\"\n",
|
"lc_ctype cluster values do not match: old \"%s\", new \"%s\"\n",
|
||||||
oldctrl->lc_ctype, newctrl->lc_ctype);
|
oldctrl->lc_ctype, newctrl->lc_ctype);
|
||||||
if (pg_strcasecmp(oldctrl->encoding, newctrl->encoding) != 0)
|
if (pg_strcasecmp(oldctrl->encoding, newctrl->encoding) != 0)
|
||||||
pg_log(PG_FATAL,
|
pg_log(PG_FATAL,
|
||||||
"encoding cluster values do not match: old \"%s\", new \"%s\"\n",
|
"encoding cluster values do not match: old \"%s\", new \"%s\"\n",
|
||||||
oldctrl->encoding, newctrl->encoding);
|
oldctrl->encoding, newctrl->encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -597,9 +595,9 @@ create_script_for_old_cluster_deletion(char **deletion_script_file_name)
|
|||||||
SCRIPT_EXT);
|
SCRIPT_EXT);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some users (oddly) create tablespaces inside the cluster data
|
* Some users (oddly) create tablespaces inside the cluster data
|
||||||
* directory. We can't create a proper old cluster delete script
|
* directory. We can't create a proper old cluster delete script in that
|
||||||
* in that case.
|
* case.
|
||||||
*/
|
*/
|
||||||
strlcpy(old_cluster_pgdata, old_cluster.pgdata, MAXPGPATH);
|
strlcpy(old_cluster_pgdata, old_cluster.pgdata, MAXPGPATH);
|
||||||
canonicalize_path(old_cluster_pgdata);
|
canonicalize_path(old_cluster_pgdata);
|
||||||
@ -997,7 +995,7 @@ get_canonical_locale_name(int category, const char *locale)
|
|||||||
|
|
||||||
save = setlocale(category, NULL);
|
save = setlocale(category, NULL);
|
||||||
if (!save)
|
if (!save)
|
||||||
pg_log(PG_FATAL, "failed to get the current locale\n");
|
pg_log(PG_FATAL, "failed to get the current locale\n");
|
||||||
|
|
||||||
/* 'save' may be pointing at a modifiable scratch variable, so copy it. */
|
/* 'save' may be pointing at a modifiable scratch variable, so copy it. */
|
||||||
save = pg_strdup(save);
|
save = pg_strdup(save);
|
||||||
@ -1006,13 +1004,13 @@ get_canonical_locale_name(int category, const char *locale)
|
|||||||
res = setlocale(category, locale);
|
res = setlocale(category, locale);
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
pg_log(PG_FATAL, "failed to get system local name for \"%s\"\n", res);
|
pg_log(PG_FATAL, "failed to get system local name for \"%s\"\n", res);
|
||||||
|
|
||||||
res = pg_strdup(res);
|
res = pg_strdup(res);
|
||||||
|
|
||||||
/* restore old value. */
|
/* restore old value. */
|
||||||
if (!setlocale(category, save))
|
if (!setlocale(category, save))
|
||||||
pg_log(PG_FATAL, "failed to restore old locale \"%s\"\n", save);
|
pg_log(PG_FATAL, "failed to restore old locale \"%s\"\n", save);
|
||||||
|
|
||||||
pg_free(save);
|
pg_free(save);
|
||||||
|
|
||||||
|
@ -472,10 +472,10 @@ get_control_data(ClusterInfo *cluster, bool live_check)
|
|||||||
pg_free(lc_messages);
|
pg_free(lc_messages);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Before 9.3, pg_resetxlog reported the xlogid and segno of the first
|
* Before 9.3, pg_resetxlog reported the xlogid and segno of the first log
|
||||||
* log file after reset as separate lines. Starting with 9.3, it reports
|
* file after reset as separate lines. Starting with 9.3, it reports the
|
||||||
* the WAL file name. If the old cluster is older than 9.3, we construct
|
* WAL file name. If the old cluster is older than 9.3, we construct the
|
||||||
* the WAL file name from the xlogid and segno.
|
* WAL file name from the xlogid and segno.
|
||||||
*/
|
*/
|
||||||
if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
|
if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
|
||||||
{
|
{
|
||||||
@ -499,8 +499,8 @@ get_control_data(ClusterInfo *cluster, bool live_check)
|
|||||||
!got_date_is_int || !got_float8_pass_by_value || !got_data_checksum_version)
|
!got_date_is_int || !got_float8_pass_by_value || !got_data_checksum_version)
|
||||||
{
|
{
|
||||||
pg_log(PG_REPORT,
|
pg_log(PG_REPORT,
|
||||||
"The %s cluster lacks some required control information:\n",
|
"The %s cluster lacks some required control information:\n",
|
||||||
CLUSTER_NAME(cluster));
|
CLUSTER_NAME(cluster));
|
||||||
|
|
||||||
if (!got_xid)
|
if (!got_xid)
|
||||||
pg_log(PG_REPORT, " checkpoint next XID\n");
|
pg_log(PG_REPORT, " checkpoint next XID\n");
|
||||||
@ -576,7 +576,7 @@ check_control_data(ControlData *oldctrl,
|
|||||||
{
|
{
|
||||||
if (oldctrl->align == 0 || oldctrl->align != newctrl->align)
|
if (oldctrl->align == 0 || oldctrl->align != newctrl->align)
|
||||||
pg_log(PG_FATAL,
|
pg_log(PG_FATAL,
|
||||||
"old and new pg_controldata alignments are invalid or do not match\n"
|
"old and new pg_controldata alignments are invalid or do not match\n"
|
||||||
"Likely one cluster is a 32-bit install, the other 64-bit\n");
|
"Likely one cluster is a 32-bit install, the other 64-bit\n");
|
||||||
|
|
||||||
if (oldctrl->blocksz == 0 || oldctrl->blocksz != newctrl->blocksz)
|
if (oldctrl->blocksz == 0 || oldctrl->blocksz != newctrl->blocksz)
|
||||||
@ -621,7 +621,10 @@ check_control_data(ControlData *oldctrl,
|
|||||||
"options.\n");
|
"options.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We might eventually allow upgrades from checksum to no-checksum clusters. */
|
/*
|
||||||
|
* We might eventually allow upgrades from checksum to no-checksum
|
||||||
|
* clusters.
|
||||||
|
*/
|
||||||
if (oldctrl->data_checksum_version != newctrl->data_checksum_version)
|
if (oldctrl->data_checksum_version != newctrl->data_checksum_version)
|
||||||
{
|
{
|
||||||
pg_log(PG_FATAL,
|
pg_log(PG_FATAL,
|
||||||
|
@ -44,6 +44,7 @@ exec_prog(const char *log_file, const char *opt_log_file,
|
|||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
int written;
|
int written;
|
||||||
|
|
||||||
#define MAXCMDLEN (2 * MAXPGPATH)
|
#define MAXCMDLEN (2 * MAXPGPATH)
|
||||||
char cmd[MAXCMDLEN];
|
char cmd[MAXCMDLEN];
|
||||||
mode_t old_umask = 0;
|
mode_t old_umask = 0;
|
||||||
@ -68,14 +69,14 @@ exec_prog(const char *log_file, const char *opt_log_file,
|
|||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* "pg_ctl -w stop" might have reported that the server has
|
* "pg_ctl -w stop" might have reported that the server has stopped
|
||||||
* stopped because the postmaster.pid file has been removed,
|
* because the postmaster.pid file has been removed, but "pg_ctl -w
|
||||||
* but "pg_ctl -w start" might still be in the process of
|
* start" might still be in the process of closing and might still be
|
||||||
* closing and might still be holding its stdout and -l log
|
* holding its stdout and -l log file descriptors open. Therefore,
|
||||||
* file descriptors open. Therefore, try to open the log
|
* try to open the log file a few more times.
|
||||||
* file a few more times.
|
|
||||||
*/
|
*/
|
||||||
int iter;
|
int iter;
|
||||||
|
|
||||||
for (iter = 0; iter < 4 && log == NULL; iter++)
|
for (iter = 0; iter < 4 && log == NULL; iter++)
|
||||||
{
|
{
|
||||||
sleep(1);
|
sleep(1);
|
||||||
@ -122,12 +123,13 @@ exec_prog(const char *log_file, const char *opt_log_file,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can't do this on Windows because it will keep the "pg_ctl start"
|
* We can't do this on Windows because it will keep the "pg_ctl start"
|
||||||
* output filename open until the server stops, so we do the \n\n above
|
* output filename open until the server stops, so we do the \n\n above on
|
||||||
* on that platform. We use a unique filename for "pg_ctl start" that is
|
* that platform. We use a unique filename for "pg_ctl start" that is
|
||||||
* never reused while the server is running, so it works fine. We could
|
* never reused while the server is running, so it works fine. We could
|
||||||
* log these commands to a third file, but that just adds complexity.
|
* log these commands to a third file, but that just adds complexity.
|
||||||
*/
|
*/
|
||||||
if ((log = fopen_priv(log_file, "a")) == NULL)
|
if ((log = fopen_priv(log_file, "a")) == NULL)
|
||||||
pg_log(PG_FATAL, "cannot write to log file %s\n", log_file);
|
pg_log(PG_FATAL, "cannot write to log file %s\n", log_file);
|
||||||
@ -178,7 +180,6 @@ pid_lock_file_exists(const char *datadir)
|
|||||||
void
|
void
|
||||||
verify_directories(void)
|
verify_directories(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
if (access(".", R_OK | W_OK | X_OK) != 0)
|
if (access(".", R_OK | W_OK | X_OK) != 0)
|
||||||
#else
|
#else
|
||||||
|
@ -127,14 +127,13 @@ linkAndUpdateFile(pageCnvCtx *pageConverter,
|
|||||||
static int
|
static int
|
||||||
copy_file(const char *srcfile, const char *dstfile, bool force)
|
copy_file(const char *srcfile, const char *dstfile, bool force)
|
||||||
{
|
{
|
||||||
|
|
||||||
#define COPY_BUF_SIZE (50 * BLCKSZ)
|
#define COPY_BUF_SIZE (50 * BLCKSZ)
|
||||||
|
|
||||||
int src_fd;
|
int src_fd;
|
||||||
int dest_fd;
|
int dest_fd;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int save_errno = 0;
|
int save_errno = 0;
|
||||||
|
|
||||||
if ((srcfile == NULL) || (dstfile == NULL))
|
if ((srcfile == NULL) || (dstfile == NULL))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -60,10 +60,9 @@ gen_db_file_maps(DbInfo *old_db, DbInfo *new_db,
|
|||||||
* table names change during ALTER TABLE ALTER COLUMN SET TYPE. In >=
|
* table names change during ALTER TABLE ALTER COLUMN SET TYPE. In >=
|
||||||
* 9.0, TOAST relation names always use heap table oids, hence we
|
* 9.0, TOAST relation names always use heap table oids, hence we
|
||||||
* cannot check relation names when upgrading from pre-9.0. Clusters
|
* cannot check relation names when upgrading from pre-9.0. Clusters
|
||||||
* upgraded to 9.0 will get matching TOAST names.
|
* upgraded to 9.0 will get matching TOAST names. If index names don't
|
||||||
* If index names don't match primary key constraint names, this will
|
* match primary key constraint names, this will fail because pg_dump
|
||||||
* fail because pg_dump dumps constraint names and pg_upgrade checks
|
* dumps constraint names and pg_upgrade checks index names.
|
||||||
* index names.
|
|
||||||
*/
|
*/
|
||||||
if (strcmp(old_rel->nspname, new_rel->nspname) != 0 ||
|
if (strcmp(old_rel->nspname, new_rel->nspname) != 0 ||
|
||||||
((GET_MAJOR_VERSION(old_cluster.major_version) >= 900 ||
|
((GET_MAJOR_VERSION(old_cluster.major_version) >= 900 ||
|
||||||
@ -79,7 +78,10 @@ gen_db_file_maps(DbInfo *old_db, DbInfo *new_db,
|
|||||||
num_maps++;
|
num_maps++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do this check after the loop so hopefully we will produce a clearer error above */
|
/*
|
||||||
|
* Do this check after the loop so hopefully we will produce a clearer
|
||||||
|
* error above
|
||||||
|
*/
|
||||||
if (old_db->rel_arr.nrels != new_db->rel_arr.nrels)
|
if (old_db->rel_arr.nrels != new_db->rel_arr.nrels)
|
||||||
pg_log(PG_FATAL, "old and new databases \"%s\" have a different number of relations\n",
|
pg_log(PG_FATAL, "old and new databases \"%s\" have a different number of relations\n",
|
||||||
old_db->db_name);
|
old_db->db_name);
|
||||||
@ -285,8 +287,11 @@ get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo)
|
|||||||
"LEFT OUTER JOIN pg_catalog.pg_index i "
|
"LEFT OUTER JOIN pg_catalog.pg_index i "
|
||||||
" ON c.oid = i.indexrelid "
|
" ON c.oid = i.indexrelid "
|
||||||
"WHERE relkind IN ('r', 'm', 'i'%s) AND "
|
"WHERE relkind IN ('r', 'm', 'i'%s) AND "
|
||||||
/* pg_dump only dumps valid indexes; testing indisready is
|
|
||||||
* necessary in 9.2, and harmless in earlier/later versions. */
|
/*
|
||||||
|
* pg_dump only dumps valid indexes; testing indisready is necessary in
|
||||||
|
* 9.2, and harmless in earlier/later versions.
|
||||||
|
*/
|
||||||
" i.indisvalid IS DISTINCT FROM false AND "
|
" i.indisvalid IS DISTINCT FROM false AND "
|
||||||
" i.indisready IS DISTINCT FROM false AND "
|
" i.indisready IS DISTINCT FROM false AND "
|
||||||
/* exclude possible orphaned temp tables */
|
/* exclude possible orphaned temp tables */
|
||||||
@ -309,8 +314,8 @@ get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo)
|
|||||||
PQclear(executeQueryOrDie(conn, "%s", query));
|
PQclear(executeQueryOrDie(conn, "%s", query));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get TOAST tables and indexes; we have to gather the TOAST tables in
|
* Get TOAST tables and indexes; we have to gather the TOAST tables in
|
||||||
* later steps because we can't schema-qualify TOAST tables.
|
* later steps because we can't schema-qualify TOAST tables.
|
||||||
*/
|
*/
|
||||||
PQclear(executeQueryOrDie(conn,
|
PQclear(executeQueryOrDie(conn,
|
||||||
"INSERT INTO info_rels "
|
"INSERT INTO info_rels "
|
||||||
@ -335,8 +340,8 @@ get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo)
|
|||||||
/* we preserve pg_class.oid so we sort by it to match old/new */
|
/* we preserve pg_class.oid so we sort by it to match old/new */
|
||||||
"ORDER BY 1;",
|
"ORDER BY 1;",
|
||||||
/* 9.2 removed the spclocation column */
|
/* 9.2 removed the spclocation column */
|
||||||
(GET_MAJOR_VERSION(cluster->major_version) <= 901) ?
|
(GET_MAJOR_VERSION(cluster->major_version) <= 901) ?
|
||||||
"t.spclocation" : "pg_catalog.pg_tablespace_location(t.oid) AS spclocation");
|
"t.spclocation" : "pg_catalog.pg_tablespace_location(t.oid) AS spclocation");
|
||||||
|
|
||||||
res = executeQueryOrDie(conn, "%s", query);
|
res = executeQueryOrDie(conn, "%s", query);
|
||||||
|
|
||||||
@ -437,5 +442,5 @@ print_rel_infos(RelInfoArr *rel_arr)
|
|||||||
for (relnum = 0; relnum < rel_arr->nrels; relnum++)
|
for (relnum = 0; relnum < rel_arr->nrels; relnum++)
|
||||||
pg_log(PG_VERBOSE, "relname: %s.%s: reloid: %u reltblspace: %s\n",
|
pg_log(PG_VERBOSE, "relname: %s.%s: reloid: %u reltblspace: %s\n",
|
||||||
rel_arr->rels[relnum].nspname, rel_arr->rels[relnum].relname,
|
rel_arr->rels[relnum].nspname, rel_arr->rels[relnum].relname,
|
||||||
rel_arr->rels[relnum].reloid, rel_arr->rels[relnum].tablespace);
|
rel_arr->rels[relnum].reloid, rel_arr->rels[relnum].tablespace);
|
||||||
}
|
}
|
||||||
|
@ -314,8 +314,8 @@ check_required_directory(char **dirpath, char **configpath,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Trim off any trailing path separators because we construct paths
|
* Trim off any trailing path separators because we construct paths by
|
||||||
* by appending to this path.
|
* appending to this path.
|
||||||
*/
|
*/
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
if ((*dirpath)[strlen(*dirpath) - 1] == '/')
|
if ((*dirpath)[strlen(*dirpath) - 1] == '/')
|
||||||
@ -398,10 +398,10 @@ void
|
|||||||
get_sock_dir(ClusterInfo *cluster, bool live_check)
|
get_sock_dir(ClusterInfo *cluster, bool live_check)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_UNIX_SOCKETS
|
#ifdef HAVE_UNIX_SOCKETS
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sockdir and port were added to postmaster.pid in PG 9.1.
|
* sockdir and port were added to postmaster.pid in PG 9.1. Pre-9.1 cannot
|
||||||
* Pre-9.1 cannot process pg_ctl -w for sockets in non-default
|
* process pg_ctl -w for sockets in non-default locations.
|
||||||
* locations.
|
|
||||||
*/
|
*/
|
||||||
if (GET_MAJOR_VERSION(cluster->major_version) >= 901)
|
if (GET_MAJOR_VERSION(cluster->major_version) >= 901)
|
||||||
{
|
{
|
||||||
@ -415,12 +415,14 @@ get_sock_dir(ClusterInfo *cluster, bool live_check)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If we are doing a live check, we will use the old cluster's Unix
|
* If we are doing a live check, we will use the old cluster's
|
||||||
* domain socket directory so we can connect to the live server.
|
* Unix domain socket directory so we can connect to the live
|
||||||
|
* server.
|
||||||
*/
|
*/
|
||||||
unsigned short orig_port = cluster->port;
|
unsigned short orig_port = cluster->port;
|
||||||
char filename[MAXPGPATH], line[MAXPGPATH];
|
char filename[MAXPGPATH],
|
||||||
FILE *fp;
|
line[MAXPGPATH];
|
||||||
|
FILE *fp;
|
||||||
int lineno;
|
int lineno;
|
||||||
|
|
||||||
snprintf(filename, sizeof(filename), "%s/postmaster.pid",
|
snprintf(filename, sizeof(filename), "%s/postmaster.pid",
|
||||||
@ -429,7 +431,7 @@ get_sock_dir(ClusterInfo *cluster, bool live_check)
|
|||||||
pg_log(PG_FATAL, "Cannot open file %s: %m\n", filename);
|
pg_log(PG_FATAL, "Cannot open file %s: %m\n", filename);
|
||||||
|
|
||||||
for (lineno = 1;
|
for (lineno = 1;
|
||||||
lineno <= Max(LOCK_FILE_LINE_PORT, LOCK_FILE_LINE_SOCKET_DIR);
|
lineno <= Max(LOCK_FILE_LINE_PORT, LOCK_FILE_LINE_SOCKET_DIR);
|
||||||
lineno++)
|
lineno++)
|
||||||
{
|
{
|
||||||
if (fgets(line, sizeof(line), fp) == NULL)
|
if (fgets(line, sizeof(line), fp) == NULL)
|
||||||
@ -450,14 +452,17 @@ get_sock_dir(ClusterInfo *cluster, bool live_check)
|
|||||||
/* warn of port number correction */
|
/* warn of port number correction */
|
||||||
if (orig_port != DEF_PGUPORT && old_cluster.port != orig_port)
|
if (orig_port != DEF_PGUPORT && old_cluster.port != orig_port)
|
||||||
pg_log(PG_WARNING, "User-supplied old port number %hu corrected to %hu\n",
|
pg_log(PG_WARNING, "User-supplied old port number %hu corrected to %hu\n",
|
||||||
orig_port, cluster->port);
|
orig_port, cluster->port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
/* Can't get sockdir and pg_ctl -w can't use a non-default, use default */
|
|
||||||
cluster->sockdir = NULL;
|
|
||||||
|
|
||||||
#else /* !HAVE_UNIX_SOCKETS */
|
/*
|
||||||
|
* Can't get sockdir and pg_ctl -w can't use a non-default, use
|
||||||
|
* default
|
||||||
|
*/
|
||||||
|
cluster->sockdir = NULL;
|
||||||
|
#else /* !HAVE_UNIX_SOCKETS */
|
||||||
cluster->sockdir = NULL;
|
cluster->sockdir = NULL;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -59,9 +59,9 @@ setupPageConverter(void)
|
|||||||
if (newPageVersion != oldPageVersion)
|
if (newPageVersion != oldPageVersion)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The clusters use differing page layouts, see if we can find a plugin
|
* The clusters use differing page layouts, see if we can find a
|
||||||
* that knows how to convert from the old page layout to the new page
|
* plugin that knows how to convert from the old page layout to the
|
||||||
* layout.
|
* new page layout.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if ((converter = loadConverterPlugin(newPageVersion, oldPageVersion)) == NULL)
|
if ((converter = loadConverterPlugin(newPageVersion, oldPageVersion)) == NULL)
|
||||||
@ -161,6 +161,4 @@ loadConverterPlugin(uint16 newPageVersion, uint16 oldPageVersion)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#include <io.h>
|
#include <io.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int parallel_jobs;
|
static int parallel_jobs;
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
/*
|
/*
|
||||||
@ -28,31 +28,32 @@ static int parallel_jobs;
|
|||||||
* it can be passed to WaitForMultipleObjects(). We use two arrays
|
* it can be passed to WaitForMultipleObjects(). We use two arrays
|
||||||
* so the thread_handles array can be passed to WaitForMultipleObjects().
|
* so the thread_handles array can be passed to WaitForMultipleObjects().
|
||||||
*/
|
*/
|
||||||
HANDLE *thread_handles;
|
HANDLE *thread_handles;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
char log_file[MAXPGPATH];
|
{
|
||||||
char opt_log_file[MAXPGPATH];
|
char log_file[MAXPGPATH];
|
||||||
char cmd[MAX_STRING];
|
char opt_log_file[MAXPGPATH];
|
||||||
|
char cmd[MAX_STRING];
|
||||||
} exec_thread_arg;
|
} exec_thread_arg;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
DbInfoArr *old_db_arr;
|
{
|
||||||
DbInfoArr *new_db_arr;
|
DbInfoArr *old_db_arr;
|
||||||
char old_pgdata[MAXPGPATH];
|
DbInfoArr *new_db_arr;
|
||||||
char new_pgdata[MAXPGPATH];
|
char old_pgdata[MAXPGPATH];
|
||||||
char old_tablespace[MAXPGPATH];
|
char new_pgdata[MAXPGPATH];
|
||||||
|
char old_tablespace[MAXPGPATH];
|
||||||
} transfer_thread_arg;
|
} transfer_thread_arg;
|
||||||
|
|
||||||
exec_thread_arg **exec_thread_args;
|
exec_thread_arg **exec_thread_args;
|
||||||
transfer_thread_arg **transfer_thread_args;
|
transfer_thread_arg **transfer_thread_args;
|
||||||
|
|
||||||
/* track current thread_args struct so reap_child() can be used for all cases */
|
/* track current thread_args struct so reap_child() can be used for all cases */
|
||||||
void **cur_thread_args;
|
void **cur_thread_args;
|
||||||
|
|
||||||
DWORD win32_exec_prog(exec_thread_arg *args);
|
|
||||||
DWORD win32_transfer_all_new_dbs(transfer_thread_arg *args);
|
|
||||||
|
|
||||||
|
DWORD win32_exec_prog(exec_thread_arg *args);
|
||||||
|
DWORD win32_transfer_all_new_dbs(transfer_thread_arg *args);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -67,11 +68,12 @@ parallel_exec_prog(const char *log_file, const char *opt_log_file,
|
|||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
char cmd[MAX_STRING];
|
char cmd[MAX_STRING];
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
pid_t child;
|
pid_t child;
|
||||||
#else
|
#else
|
||||||
HANDLE child;
|
HANDLE child;
|
||||||
exec_thread_arg *new_arg;
|
exec_thread_arg *new_arg;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
@ -85,7 +87,7 @@ parallel_exec_prog(const char *log_file, const char *opt_log_file,
|
|||||||
{
|
{
|
||||||
/* parallel */
|
/* parallel */
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
cur_thread_args = (void **)exec_thread_args;
|
cur_thread_args = (void **) exec_thread_args;
|
||||||
#endif
|
#endif
|
||||||
/* harvest any dead children */
|
/* harvest any dead children */
|
||||||
while (reap_child(false) == true)
|
while (reap_child(false) == true)
|
||||||
@ -112,22 +114,22 @@ parallel_exec_prog(const char *log_file, const char *opt_log_file,
|
|||||||
#else
|
#else
|
||||||
if (thread_handles == NULL)
|
if (thread_handles == NULL)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
thread_handles = pg_malloc(user_opts.jobs * sizeof(HANDLE));
|
thread_handles = pg_malloc(user_opts.jobs * sizeof(HANDLE));
|
||||||
exec_thread_args = pg_malloc(user_opts.jobs * sizeof(exec_thread_arg *));
|
exec_thread_args = pg_malloc(user_opts.jobs * sizeof(exec_thread_arg *));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For safety and performance, we keep the args allocated during
|
* For safety and performance, we keep the args allocated during
|
||||||
* the entire life of the process, and we don't free the args
|
* the entire life of the process, and we don't free the args in a
|
||||||
* in a thread different from the one that allocated it.
|
* thread different from the one that allocated it.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < user_opts.jobs; i++)
|
for (i = 0; i < user_opts.jobs; i++)
|
||||||
exec_thread_args[i] = pg_malloc(sizeof(exec_thread_arg));
|
exec_thread_args[i] = pg_malloc(sizeof(exec_thread_arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* use first empty array element */
|
/* use first empty array element */
|
||||||
new_arg = exec_thread_args[parallel_jobs-1];
|
new_arg = exec_thread_args[parallel_jobs - 1];
|
||||||
|
|
||||||
/* Can only pass one pointer into the function, so use a struct */
|
/* Can only pass one pointer into the function, so use a struct */
|
||||||
strcpy(new_arg->log_file, log_file);
|
strcpy(new_arg->log_file, log_file);
|
||||||
@ -135,11 +137,11 @@ parallel_exec_prog(const char *log_file, const char *opt_log_file,
|
|||||||
strcpy(new_arg->cmd, cmd);
|
strcpy(new_arg->cmd, cmd);
|
||||||
|
|
||||||
child = (HANDLE) _beginthreadex(NULL, 0, (void *) win32_exec_prog,
|
child = (HANDLE) _beginthreadex(NULL, 0, (void *) win32_exec_prog,
|
||||||
new_arg, 0, NULL);
|
new_arg, 0, NULL);
|
||||||
if (child == 0)
|
if (child == 0)
|
||||||
pg_log(PG_FATAL, "could not create worker thread: %s\n", strerror(errno));
|
pg_log(PG_FATAL, "could not create worker thread: %s\n", strerror(errno));
|
||||||
|
|
||||||
thread_handles[parallel_jobs-1] = child;
|
thread_handles[parallel_jobs - 1] = child;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +153,7 @@ parallel_exec_prog(const char *log_file, const char *opt_log_file,
|
|||||||
DWORD
|
DWORD
|
||||||
win32_exec_prog(exec_thread_arg *args)
|
win32_exec_prog(exec_thread_arg *args)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = !exec_prog(args->log_file, args->opt_log_file, true, "%s", args->cmd);
|
ret = !exec_prog(args->log_file, args->opt_log_file, true, "%s", args->cmd);
|
||||||
|
|
||||||
@ -167,15 +169,16 @@ win32_exec_prog(exec_thread_arg *args)
|
|||||||
* This has the same API as transfer_all_new_dbs, except it does parallel execution
|
* This has the same API as transfer_all_new_dbs, except it does parallel execution
|
||||||
* by transfering multiple tablespaces in parallel
|
* by transfering multiple tablespaces in parallel
|
||||||
*/
|
*/
|
||||||
void parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
|
void
|
||||||
char *old_pgdata, char *new_pgdata,
|
parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
|
||||||
char *old_tablespace)
|
char *old_pgdata, char *new_pgdata,
|
||||||
|
char *old_tablespace)
|
||||||
{
|
{
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
pid_t child;
|
pid_t child;
|
||||||
#else
|
#else
|
||||||
HANDLE child;
|
HANDLE child;
|
||||||
transfer_thread_arg *new_arg;
|
transfer_thread_arg *new_arg;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (user_opts.jobs <= 1)
|
if (user_opts.jobs <= 1)
|
||||||
@ -185,7 +188,7 @@ void parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
|
|||||||
{
|
{
|
||||||
/* parallel */
|
/* parallel */
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
cur_thread_args = (void **)transfer_thread_args;
|
cur_thread_args = (void **) transfer_thread_args;
|
||||||
#endif
|
#endif
|
||||||
/* harvest any dead children */
|
/* harvest any dead children */
|
||||||
while (reap_child(false) == true)
|
while (reap_child(false) == true)
|
||||||
@ -217,22 +220,22 @@ void parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
|
|||||||
#else
|
#else
|
||||||
if (thread_handles == NULL)
|
if (thread_handles == NULL)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
thread_handles = pg_malloc(user_opts.jobs * sizeof(HANDLE));
|
thread_handles = pg_malloc(user_opts.jobs * sizeof(HANDLE));
|
||||||
transfer_thread_args = pg_malloc(user_opts.jobs * sizeof(transfer_thread_arg *));
|
transfer_thread_args = pg_malloc(user_opts.jobs * sizeof(transfer_thread_arg *));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For safety and performance, we keep the args allocated during
|
* For safety and performance, we keep the args allocated during
|
||||||
* the entire life of the process, and we don't free the args
|
* the entire life of the process, and we don't free the args in a
|
||||||
* in a thread different from the one that allocated it.
|
* thread different from the one that allocated it.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < user_opts.jobs; i++)
|
for (i = 0; i < user_opts.jobs; i++)
|
||||||
transfer_thread_args[i] = pg_malloc(sizeof(transfer_thread_arg));
|
transfer_thread_args[i] = pg_malloc(sizeof(transfer_thread_arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* use first empty array element */
|
/* use first empty array element */
|
||||||
new_arg = transfer_thread_args[parallel_jobs-1];
|
new_arg = transfer_thread_args[parallel_jobs - 1];
|
||||||
|
|
||||||
/* Can only pass one pointer into the function, so use a struct */
|
/* Can only pass one pointer into the function, so use a struct */
|
||||||
new_arg->old_db_arr = old_db_arr;
|
new_arg->old_db_arr = old_db_arr;
|
||||||
@ -242,11 +245,11 @@ void parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
|
|||||||
strcpy(new_arg->old_tablespace, old_tablespace);
|
strcpy(new_arg->old_tablespace, old_tablespace);
|
||||||
|
|
||||||
child = (HANDLE) _beginthreadex(NULL, 0, (void *) win32_exec_prog,
|
child = (HANDLE) _beginthreadex(NULL, 0, (void *) win32_exec_prog,
|
||||||
new_arg, 0, NULL);
|
new_arg, 0, NULL);
|
||||||
if (child == 0)
|
if (child == 0)
|
||||||
pg_log(PG_FATAL, "could not create worker thread: %s\n", strerror(errno));
|
pg_log(PG_FATAL, "could not create worker thread: %s\n", strerror(errno));
|
||||||
|
|
||||||
thread_handles[parallel_jobs-1] = child;
|
thread_handles[parallel_jobs - 1] = child;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,11 +277,11 @@ bool
|
|||||||
reap_child(bool wait_for_child)
|
reap_child(bool wait_for_child)
|
||||||
{
|
{
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
int work_status;
|
int work_status;
|
||||||
int ret;
|
int ret;
|
||||||
#else
|
#else
|
||||||
int thread_num;
|
int thread_num;
|
||||||
DWORD res;
|
DWORD res;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (user_opts.jobs <= 1 || parallel_jobs == 0)
|
if (user_opts.jobs <= 1 || parallel_jobs == 0)
|
||||||
@ -293,11 +296,10 @@ reap_child(bool wait_for_child)
|
|||||||
|
|
||||||
if (WEXITSTATUS(work_status) != 0)
|
if (WEXITSTATUS(work_status) != 0)
|
||||||
pg_log(PG_FATAL, "child worker exited abnormally: %s\n", strerror(errno));
|
pg_log(PG_FATAL, "child worker exited abnormally: %s\n", strerror(errno));
|
||||||
|
|
||||||
#else
|
#else
|
||||||
/* wait for one to finish */
|
/* wait for one to finish */
|
||||||
thread_num = WaitForMultipleObjects(parallel_jobs, thread_handles,
|
thread_num = WaitForMultipleObjects(parallel_jobs, thread_handles,
|
||||||
false, wait_for_child ? INFINITE : 0);
|
false, wait_for_child ? INFINITE : 0);
|
||||||
|
|
||||||
if (thread_num == WAIT_TIMEOUT || thread_num == WAIT_FAILED)
|
if (thread_num == WAIT_TIMEOUT || thread_num == WAIT_FAILED)
|
||||||
return false;
|
return false;
|
||||||
@ -313,18 +315,18 @@ reap_child(bool wait_for_child)
|
|||||||
/* dispose of handle to stop leaks */
|
/* dispose of handle to stop leaks */
|
||||||
CloseHandle(thread_handles[thread_num]);
|
CloseHandle(thread_handles[thread_num]);
|
||||||
|
|
||||||
/* Move last slot into dead child's position */
|
/* Move last slot into dead child's position */
|
||||||
if (thread_num != parallel_jobs - 1)
|
if (thread_num != parallel_jobs - 1)
|
||||||
{
|
{
|
||||||
void *tmp_args;
|
void *tmp_args;
|
||||||
|
|
||||||
thread_handles[thread_num] = thread_handles[parallel_jobs - 1];
|
thread_handles[thread_num] = thread_handles[parallel_jobs - 1];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We must swap the arg struct pointers because the thread we
|
* We must swap the arg struct pointers because the thread we just
|
||||||
* just moved is active, and we must make sure it is not
|
* moved is active, and we must make sure it is not reused by the next
|
||||||
* reused by the next created thread. Instead, the new thread
|
* created thread. Instead, the new thread will use the arg struct of
|
||||||
* will use the arg struct of the thread that just died.
|
* the thread that just died.
|
||||||
*/
|
*/
|
||||||
tmp_args = cur_thread_args[thread_num];
|
tmp_args = cur_thread_args[thread_num];
|
||||||
cur_thread_args[thread_num] = cur_thread_args[parallel_jobs - 1];
|
cur_thread_args[thread_num] = cur_thread_args[parallel_jobs - 1];
|
||||||
|
@ -134,7 +134,7 @@ main(int argc, char **argv)
|
|||||||
disable_old_cluster();
|
disable_old_cluster();
|
||||||
|
|
||||||
transfer_all_new_tablespaces(&old_cluster.dbarr, &new_cluster.dbarr,
|
transfer_all_new_tablespaces(&old_cluster.dbarr, &new_cluster.dbarr,
|
||||||
old_cluster.pgdata, new_cluster.pgdata);
|
old_cluster.pgdata, new_cluster.pgdata);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assuming OIDs are only used in system tables, there is no need to
|
* Assuming OIDs are only used in system tables, there is no need to
|
||||||
@ -193,13 +193,12 @@ setup(char *argv0, bool *live_check)
|
|||||||
if (pid_lock_file_exists(old_cluster.pgdata))
|
if (pid_lock_file_exists(old_cluster.pgdata))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If we have a postmaster.pid file, try to start the server. If
|
* If we have a postmaster.pid file, try to start the server. If it
|
||||||
* it starts, the pid file was stale, so stop the server. If it
|
* starts, the pid file was stale, so stop the server. If it doesn't
|
||||||
* doesn't start, assume the server is running. If the pid file
|
* start, assume the server is running. If the pid file is left over
|
||||||
* is left over from a server crash, this also allows any committed
|
* from a server crash, this also allows any committed transactions
|
||||||
* transactions stored in the WAL to be replayed so they are not
|
* stored in the WAL to be replayed so they are not lost, because WAL
|
||||||
* lost, because WAL files are not transfered from old to new
|
* files are not transfered from old to new servers.
|
||||||
* servers.
|
|
||||||
*/
|
*/
|
||||||
if (start_postmaster(&old_cluster, false))
|
if (start_postmaster(&old_cluster, false))
|
||||||
stop_postmaster(false);
|
stop_postmaster(false);
|
||||||
@ -220,7 +219,7 @@ setup(char *argv0, bool *live_check)
|
|||||||
stop_postmaster(false);
|
stop_postmaster(false);
|
||||||
else
|
else
|
||||||
pg_log(PG_FATAL, "There seems to be a postmaster servicing the new cluster.\n"
|
pg_log(PG_FATAL, "There seems to be a postmaster servicing the new cluster.\n"
|
||||||
"Please shutdown that postmaster and try again.\n");
|
"Please shutdown that postmaster and try again.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get path to pg_upgrade executable */
|
/* get path to pg_upgrade executable */
|
||||||
@ -312,9 +311,9 @@ create_new_objects(void)
|
|||||||
prep_status("Adding support functions to new cluster");
|
prep_status("Adding support functions to new cluster");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Technically, we only need to install these support functions in new
|
* Technically, we only need to install these support functions in new
|
||||||
* databases that also exist in the old cluster, but for completeness
|
* databases that also exist in the old cluster, but for completeness we
|
||||||
* we process all new databases.
|
* process all new databases.
|
||||||
*/
|
*/
|
||||||
for (dbnum = 0; dbnum < new_cluster.dbarr.ndbs; dbnum++)
|
for (dbnum = 0; dbnum < new_cluster.dbarr.ndbs; dbnum++)
|
||||||
{
|
{
|
||||||
@ -330,21 +329,22 @@ create_new_objects(void)
|
|||||||
|
|
||||||
for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
|
for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
|
||||||
{
|
{
|
||||||
char sql_file_name[MAXPGPATH], log_file_name[MAXPGPATH];
|
char sql_file_name[MAXPGPATH],
|
||||||
DbInfo *old_db = &old_cluster.dbarr.dbs[dbnum];
|
log_file_name[MAXPGPATH];
|
||||||
|
DbInfo *old_db = &old_cluster.dbarr.dbs[dbnum];
|
||||||
|
|
||||||
pg_log(PG_STATUS, "%s", old_db->db_name);
|
pg_log(PG_STATUS, "%s", old_db->db_name);
|
||||||
snprintf(sql_file_name, sizeof(sql_file_name), DB_DUMP_FILE_MASK, old_db->db_oid);
|
snprintf(sql_file_name, sizeof(sql_file_name), DB_DUMP_FILE_MASK, old_db->db_oid);
|
||||||
snprintf(log_file_name, sizeof(log_file_name), DB_DUMP_LOG_FILE_MASK, old_db->db_oid);
|
snprintf(log_file_name, sizeof(log_file_name), DB_DUMP_LOG_FILE_MASK, old_db->db_oid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pg_dump only produces its output at the end, so there is little
|
* pg_dump only produces its output at the end, so there is little
|
||||||
* parallelism if using the pipe.
|
* parallelism if using the pipe.
|
||||||
*/
|
*/
|
||||||
parallel_exec_prog(log_file_name, NULL,
|
parallel_exec_prog(log_file_name, NULL,
|
||||||
"\"%s/pg_restore\" %s --exit-on-error --verbose --dbname \"%s\" \"%s\"",
|
"\"%s/pg_restore\" %s --exit-on-error --verbose --dbname \"%s\" \"%s\"",
|
||||||
new_cluster.bindir, cluster_conn_opts(&new_cluster),
|
new_cluster.bindir, cluster_conn_opts(&new_cluster),
|
||||||
old_db->db_name, sql_file_name);
|
old_db->db_name, sql_file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reap all children */
|
/* reap all children */
|
||||||
@ -418,6 +418,7 @@ copy_clog_xlog_xid(void)
|
|||||||
copy_subdir_files("pg_multixact/offsets");
|
copy_subdir_files("pg_multixact/offsets");
|
||||||
copy_subdir_files("pg_multixact/members");
|
copy_subdir_files("pg_multixact/members");
|
||||||
prep_status("Setting next multixact ID and offset for new cluster");
|
prep_status("Setting next multixact ID and offset for new cluster");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* we preserve all files and contents, so we must preserve both "next"
|
* we preserve all files and contents, so we must preserve both "next"
|
||||||
* counters here and the oldest multi present on system.
|
* counters here and the oldest multi present on system.
|
||||||
@ -434,6 +435,7 @@ copy_clog_xlog_xid(void)
|
|||||||
else if (new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
|
else if (new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
|
||||||
{
|
{
|
||||||
prep_status("Setting oldest multixact ID on new cluster");
|
prep_status("Setting oldest multixact ID on new cluster");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't preserve files in this case, but it's important that the
|
* We don't preserve files in this case, but it's important that the
|
||||||
* oldest multi is set to the latest value used by the old system, so
|
* oldest multi is set to the latest value used by the old system, so
|
||||||
@ -549,7 +551,6 @@ set_frozenxids(void)
|
|||||||
static void
|
static void
|
||||||
cleanup(void)
|
cleanup(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
fclose(log_opts.internal);
|
fclose(log_opts.internal);
|
||||||
|
|
||||||
/* Remove dump and log files? */
|
/* Remove dump and log files? */
|
||||||
@ -567,8 +568,9 @@ cleanup(void)
|
|||||||
if (old_cluster.dbarr.dbs)
|
if (old_cluster.dbarr.dbs)
|
||||||
for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
|
for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
|
||||||
{
|
{
|
||||||
char sql_file_name[MAXPGPATH], log_file_name[MAXPGPATH];
|
char sql_file_name[MAXPGPATH],
|
||||||
DbInfo *old_db = &old_cluster.dbarr.dbs[dbnum];
|
log_file_name[MAXPGPATH];
|
||||||
|
DbInfo *old_db = &old_cluster.dbarr.dbs[dbnum];
|
||||||
|
|
||||||
snprintf(sql_file_name, sizeof(sql_file_name), DB_DUMP_FILE_MASK, old_db->db_oid);
|
snprintf(sql_file_name, sizeof(sql_file_name), DB_DUMP_FILE_MASK, old_db->db_oid);
|
||||||
unlink(sql_file_name);
|
unlink(sql_file_name);
|
||||||
|
@ -73,24 +73,24 @@ extern char *output_files[];
|
|||||||
#define pg_copy_file copy_file
|
#define pg_copy_file copy_file
|
||||||
#define pg_mv_file rename
|
#define pg_mv_file rename
|
||||||
#define pg_link_file link
|
#define pg_link_file link
|
||||||
#define PATH_SEPARATOR '/'
|
#define PATH_SEPARATOR '/'
|
||||||
#define RM_CMD "rm -f"
|
#define RM_CMD "rm -f"
|
||||||
#define RMDIR_CMD "rm -rf"
|
#define RMDIR_CMD "rm -rf"
|
||||||
#define SCRIPT_EXT "sh"
|
#define SCRIPT_EXT "sh"
|
||||||
#define ECHO_QUOTE "'"
|
#define ECHO_QUOTE "'"
|
||||||
#define ECHO_BLANK ""
|
#define ECHO_BLANK ""
|
||||||
#else
|
#else
|
||||||
#define pg_copy_file CopyFile
|
#define pg_copy_file CopyFile
|
||||||
#define pg_mv_file pgrename
|
#define pg_mv_file pgrename
|
||||||
#define pg_link_file win32_pghardlink
|
#define pg_link_file win32_pghardlink
|
||||||
#define sleep(x) Sleep(x * 1000)
|
#define sleep(x) Sleep(x * 1000)
|
||||||
#define PATH_SEPARATOR '\\'
|
#define PATH_SEPARATOR '\\'
|
||||||
#define RM_CMD "DEL /q"
|
#define RM_CMD "DEL /q"
|
||||||
#define RMDIR_CMD "RMDIR /s/q"
|
#define RMDIR_CMD "RMDIR /s/q"
|
||||||
#define SCRIPT_EXT "bat"
|
#define SCRIPT_EXT "bat"
|
||||||
#define EXE_EXT ".exe"
|
#define EXE_EXT ".exe"
|
||||||
#define ECHO_QUOTE ""
|
#define ECHO_QUOTE ""
|
||||||
#define ECHO_BLANK "."
|
#define ECHO_BLANK "."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define CLUSTER_NAME(cluster) ((cluster) == &old_cluster ? "old" : \
|
#define CLUSTER_NAME(cluster) ((cluster) == &old_cluster ? "old" : \
|
||||||
@ -122,8 +122,8 @@ extern char *output_files[];
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
/* Can't use NAMEDATALEN; not guaranteed to fit on client */
|
/* Can't use NAMEDATALEN; not guaranteed to fit on client */
|
||||||
char *nspname; /* namespace name */
|
char *nspname; /* namespace name */
|
||||||
char *relname; /* relation name */
|
char *relname; /* relation name */
|
||||||
Oid reloid; /* relation oid */
|
Oid reloid; /* relation oid */
|
||||||
Oid relfilenode; /* relation relfile node */
|
Oid relfilenode; /* relation relfile node */
|
||||||
/* relation tablespace path, or "" for the cluster default */
|
/* relation tablespace path, or "" for the cluster default */
|
||||||
@ -155,8 +155,8 @@ typedef struct
|
|||||||
Oid old_relfilenode;
|
Oid old_relfilenode;
|
||||||
Oid new_relfilenode;
|
Oid new_relfilenode;
|
||||||
/* the rest are used only for logging and error reporting */
|
/* the rest are used only for logging and error reporting */
|
||||||
char *nspname; /* namespaces */
|
char *nspname; /* namespaces */
|
||||||
char *relname;
|
char *relname;
|
||||||
} FileNameMap;
|
} FileNameMap;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -165,7 +165,7 @@ typedef struct
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
Oid db_oid; /* oid of the database */
|
Oid db_oid; /* oid of the database */
|
||||||
char *db_name; /* database name */
|
char *db_name; /* database name */
|
||||||
char db_tblspace[MAXPGPATH]; /* database default tablespace path */
|
char db_tblspace[MAXPGPATH]; /* database default tablespace path */
|
||||||
RelInfoArr rel_arr; /* array of all user relinfos */
|
RelInfoArr rel_arr; /* array of all user relinfos */
|
||||||
} DbInfo;
|
} DbInfo;
|
||||||
@ -254,8 +254,8 @@ typedef struct
|
|||||||
char major_version_str[64]; /* string PG_VERSION of cluster */
|
char major_version_str[64]; /* string PG_VERSION of cluster */
|
||||||
uint32 bin_version; /* version returned from pg_ctl */
|
uint32 bin_version; /* version returned from pg_ctl */
|
||||||
Oid pg_database_oid; /* OID of pg_database relation */
|
Oid pg_database_oid; /* OID of pg_database relation */
|
||||||
Oid install_role_oid; /* OID of connected role */
|
Oid install_role_oid; /* OID of connected role */
|
||||||
Oid role_count; /* number of roles defined in the cluster */
|
Oid role_count; /* number of roles defined in the cluster */
|
||||||
char *tablespace_suffix; /* directory specification */
|
char *tablespace_suffix; /* directory specification */
|
||||||
} ClusterInfo;
|
} ClusterInfo;
|
||||||
|
|
||||||
@ -312,12 +312,12 @@ extern OSInfo os_info;
|
|||||||
/* check.c */
|
/* check.c */
|
||||||
|
|
||||||
void output_check_banner(bool live_check);
|
void output_check_banner(bool live_check);
|
||||||
void check_and_dump_old_cluster(bool live_check,
|
void check_and_dump_old_cluster(bool live_check,
|
||||||
char **sequence_script_file_name);
|
char **sequence_script_file_name);
|
||||||
void check_new_cluster(void);
|
void check_new_cluster(void);
|
||||||
void report_clusters_compatible(void);
|
void report_clusters_compatible(void);
|
||||||
void issue_warnings(char *sequence_script_file_name);
|
void issue_warnings(char *sequence_script_file_name);
|
||||||
void output_completion_banner(char *analyze_script_file_name,
|
void output_completion_banner(char *analyze_script_file_name,
|
||||||
char *deletion_script_file_name);
|
char *deletion_script_file_name);
|
||||||
void check_cluster_versions(void);
|
void check_cluster_versions(void);
|
||||||
void check_cluster_compatibility(bool live_check);
|
void check_cluster_compatibility(bool live_check);
|
||||||
@ -413,11 +413,11 @@ void get_sock_dir(ClusterInfo *cluster, bool live_check);
|
|||||||
/* relfilenode.c */
|
/* relfilenode.c */
|
||||||
|
|
||||||
void get_pg_database_relfilenode(ClusterInfo *cluster);
|
void get_pg_database_relfilenode(ClusterInfo *cluster);
|
||||||
void transfer_all_new_tablespaces(DbInfoArr *old_db_arr,
|
void transfer_all_new_tablespaces(DbInfoArr *old_db_arr,
|
||||||
DbInfoArr *new_db_arr, char *old_pgdata, char *new_pgdata);
|
DbInfoArr *new_db_arr, char *old_pgdata, char *new_pgdata);
|
||||||
void transfer_all_new_dbs(DbInfoArr *old_db_arr,
|
void transfer_all_new_dbs(DbInfoArr *old_db_arr,
|
||||||
DbInfoArr *new_db_arr, char *old_pgdata, char *new_pgdata,
|
DbInfoArr *new_db_arr, char *old_pgdata, char *new_pgdata,
|
||||||
char *old_tablespace);
|
char *old_tablespace);
|
||||||
|
|
||||||
/* tablespace.c */
|
/* tablespace.c */
|
||||||
|
|
||||||
@ -477,11 +477,11 @@ void old_8_3_invalidate_bpchar_pattern_ops_indexes(ClusterInfo *cluster,
|
|||||||
char *old_8_3_create_sequence_script(ClusterInfo *cluster);
|
char *old_8_3_create_sequence_script(ClusterInfo *cluster);
|
||||||
|
|
||||||
/* parallel.c */
|
/* parallel.c */
|
||||||
void parallel_exec_prog(const char *log_file, const char *opt_log_file,
|
void
|
||||||
const char *fmt,...)
|
parallel_exec_prog(const char *log_file, const char *opt_log_file,
|
||||||
|
const char *fmt,...)
|
||||||
__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));
|
__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));
|
||||||
void parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
|
void parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
|
||||||
char *old_pgdata, char *new_pgdata,
|
char *old_pgdata, char *new_pgdata,
|
||||||
char *old_tablespace);
|
char *old_tablespace);
|
||||||
bool reap_child(bool wait_for_child);
|
bool reap_child(bool wait_for_child);
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
static void transfer_single_new_db(pageCnvCtx *pageConverter,
|
static void transfer_single_new_db(pageCnvCtx *pageConverter,
|
||||||
FileNameMap *maps, int size, char *old_tablespace);
|
FileNameMap *maps, int size, char *old_tablespace);
|
||||||
static void transfer_relfile(pageCnvCtx *pageConverter, FileNameMap *map,
|
static void transfer_relfile(pageCnvCtx *pageConverter, FileNameMap *map,
|
||||||
const char *suffix);
|
const char *suffix);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -29,32 +29,32 @@ static void transfer_relfile(pageCnvCtx *pageConverter, FileNameMap *map,
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
transfer_all_new_tablespaces(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
|
transfer_all_new_tablespaces(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
|
||||||
char *old_pgdata, char *new_pgdata)
|
char *old_pgdata, char *new_pgdata)
|
||||||
{
|
{
|
||||||
pg_log(PG_REPORT, "%s user relation files\n",
|
pg_log(PG_REPORT, "%s user relation files\n",
|
||||||
user_opts.transfer_mode == TRANSFER_MODE_LINK ? "Linking" : "Copying");
|
user_opts.transfer_mode == TRANSFER_MODE_LINK ? "Linking" : "Copying");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Transfering files by tablespace is tricky because a single database
|
* Transfering files by tablespace is tricky because a single database can
|
||||||
* can use multiple tablespaces. For non-parallel mode, we just pass a
|
* use multiple tablespaces. For non-parallel mode, we just pass a NULL
|
||||||
* NULL tablespace path, which matches all tablespaces. In parallel mode,
|
* tablespace path, which matches all tablespaces. In parallel mode, we
|
||||||
* we pass the default tablespace and all user-created tablespaces
|
* pass the default tablespace and all user-created tablespaces and let
|
||||||
* and let those operations happen in parallel.
|
* those operations happen in parallel.
|
||||||
*/
|
*/
|
||||||
if (user_opts.jobs <= 1)
|
if (user_opts.jobs <= 1)
|
||||||
parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata,
|
parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata,
|
||||||
new_pgdata, NULL);
|
new_pgdata, NULL);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int tblnum;
|
int tblnum;
|
||||||
|
|
||||||
/* transfer default tablespace */
|
/* transfer default tablespace */
|
||||||
parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata,
|
parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata,
|
||||||
new_pgdata, old_pgdata);
|
new_pgdata, old_pgdata);
|
||||||
|
|
||||||
for (tblnum = 0; tblnum < os_info.num_old_tablespaces; tblnum++)
|
for (tblnum = 0; tblnum < os_info.num_old_tablespaces; tblnum++)
|
||||||
parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata,
|
parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata,
|
||||||
new_pgdata, os_info.old_tablespaces[tblnum]);
|
new_pgdata, os_info.old_tablespaces[tblnum]);
|
||||||
/* reap all children */
|
/* reap all children */
|
||||||
while (reap_child(true) == true)
|
while (reap_child(true) == true)
|
||||||
;
|
;
|
||||||
@ -75,7 +75,7 @@ transfer_all_new_tablespaces(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
|
transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
|
||||||
char *old_pgdata, char *new_pgdata, char *old_tablespace)
|
char *old_pgdata, char *new_pgdata, char *old_tablespace)
|
||||||
{
|
{
|
||||||
int old_dbnum,
|
int old_dbnum,
|
||||||
new_dbnum;
|
new_dbnum;
|
||||||
@ -173,8 +173,8 @@ transfer_single_new_db(pageCnvCtx *pageConverter,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Do the old and new cluster disagree on the crash-safetiness of the vm
|
* Do the old and new cluster disagree on the crash-safetiness of the vm
|
||||||
* files? If so, do not copy them.
|
* files? If so, do not copy them.
|
||||||
*/
|
*/
|
||||||
if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_CRASHSAFE_CAT_VER &&
|
if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_CRASHSAFE_CAT_VER &&
|
||||||
new_cluster.controldata.cat_ver >= VISIBILITY_MAP_CRASHSAFE_CAT_VER)
|
new_cluster.controldata.cat_ver >= VISIBILITY_MAP_CRASHSAFE_CAT_VER)
|
||||||
vm_crashsafe_match = false;
|
vm_crashsafe_match = false;
|
||||||
@ -219,11 +219,9 @@ transfer_relfile(pageCnvCtx *pageConverter, FileNameMap *map,
|
|||||||
char extent_suffix[65];
|
char extent_suffix[65];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now copy/link any related segments as well. Remember, PG breaks
|
* Now copy/link any related segments as well. Remember, PG breaks large
|
||||||
* large files into 1GB segments, the first segment has no extension,
|
* files into 1GB segments, the first segment has no extension, subsequent
|
||||||
* subsequent segments are named relfilenode.1, relfilenode.2,
|
* segments are named relfilenode.1, relfilenode.2, relfilenode.3. copied.
|
||||||
* relfilenode.3.
|
|
||||||
* copied.
|
|
||||||
*/
|
*/
|
||||||
for (segno = 0;; segno++)
|
for (segno = 0;; segno++)
|
||||||
{
|
{
|
||||||
@ -233,10 +231,10 @@ transfer_relfile(pageCnvCtx *pageConverter, FileNameMap *map,
|
|||||||
snprintf(extent_suffix, sizeof(extent_suffix), ".%d", segno);
|
snprintf(extent_suffix, sizeof(extent_suffix), ".%d", segno);
|
||||||
|
|
||||||
snprintf(old_file, sizeof(old_file), "%s%s/%u/%u%s%s", map->old_tablespace,
|
snprintf(old_file, sizeof(old_file), "%s%s/%u/%u%s%s", map->old_tablespace,
|
||||||
map->old_tablespace_suffix, map->old_db_oid, map->old_relfilenode,
|
map->old_tablespace_suffix, map->old_db_oid, map->old_relfilenode,
|
||||||
type_suffix, extent_suffix);
|
type_suffix, extent_suffix);
|
||||||
snprintf(new_file, sizeof(new_file), "%s%s/%u/%u%s%s", map->new_tablespace,
|
snprintf(new_file, sizeof(new_file), "%s%s/%u/%u%s%s", map->new_tablespace,
|
||||||
map->new_tablespace_suffix, map->new_db_oid, map->new_relfilenode,
|
map->new_tablespace_suffix, map->new_db_oid, map->new_relfilenode,
|
||||||
type_suffix, extent_suffix);
|
type_suffix, extent_suffix);
|
||||||
|
|
||||||
/* Is it an extent, fsm, or vm file? */
|
/* Is it an extent, fsm, or vm file? */
|
||||||
@ -282,8 +280,7 @@ transfer_relfile(pageCnvCtx *pageConverter, FileNameMap *map,
|
|||||||
"error while creating link for relation \"%s.%s\" (\"%s\" to \"%s\"): %s\n",
|
"error while creating link for relation \"%s.%s\" (\"%s\" to \"%s\"): %s\n",
|
||||||
map->nspname, map->relname, old_file, new_file, msg);
|
map->nspname, map->relname, old_file, new_file, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ get_db_conn(ClusterInfo *cluster, const char *db_name)
|
|||||||
char *
|
char *
|
||||||
cluster_conn_opts(ClusterInfo *cluster)
|
cluster_conn_opts(ClusterInfo *cluster)
|
||||||
{
|
{
|
||||||
static char conn_opts[MAXPGPATH + NAMEDATALEN + 100];
|
static char conn_opts[MAXPGPATH + NAMEDATALEN + 100];
|
||||||
|
|
||||||
if (cluster->sockdir)
|
if (cluster->sockdir)
|
||||||
snprintf(conn_opts, sizeof(conn_opts),
|
snprintf(conn_opts, sizeof(conn_opts),
|
||||||
@ -192,7 +192,7 @@ start_postmaster(ClusterInfo *cluster, bool throw_error)
|
|||||||
strcat(socket_string,
|
strcat(socket_string,
|
||||||
" -c listen_addresses='' -c unix_socket_permissions=0700");
|
" -c listen_addresses='' -c unix_socket_permissions=0700");
|
||||||
|
|
||||||
/* Have a sockdir? Tell the postmaster. */
|
/* Have a sockdir? Tell the postmaster. */
|
||||||
if (cluster->sockdir)
|
if (cluster->sockdir)
|
||||||
snprintf(socket_string + strlen(socket_string),
|
snprintf(socket_string + strlen(socket_string),
|
||||||
sizeof(socket_string) - strlen(socket_string),
|
sizeof(socket_string) - strlen(socket_string),
|
||||||
@ -215,13 +215,13 @@ start_postmaster(ClusterInfo *cluster, bool throw_error)
|
|||||||
* win on ext4.
|
* win on ext4.
|
||||||
*/
|
*/
|
||||||
snprintf(cmd, sizeof(cmd),
|
snprintf(cmd, sizeof(cmd),
|
||||||
"\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start",
|
"\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start",
|
||||||
cluster->bindir, SERVER_LOG_FILE, cluster->pgconfig, cluster->port,
|
cluster->bindir, SERVER_LOG_FILE, cluster->pgconfig, cluster->port,
|
||||||
(cluster->controldata.cat_ver >=
|
(cluster->controldata.cat_ver >=
|
||||||
BINARY_UPGRADE_SERVER_FLAG_CAT_VER) ? " -b" :
|
BINARY_UPGRADE_SERVER_FLAG_CAT_VER) ? " -b" :
|
||||||
" -c autovacuum=off -c autovacuum_freeze_max_age=2000000000",
|
" -c autovacuum=off -c autovacuum_freeze_max_age=2000000000",
|
||||||
(cluster == &new_cluster) ?
|
(cluster == &new_cluster) ?
|
||||||
" -c synchronous_commit=off -c fsync=off -c full_page_writes=off" : "",
|
" -c synchronous_commit=off -c fsync=off -c full_page_writes=off" : "",
|
||||||
cluster->pgopts ? cluster->pgopts : "", socket_string);
|
cluster->pgopts ? cluster->pgopts : "", socket_string);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -229,7 +229,7 @@ start_postmaster(ClusterInfo *cluster, bool throw_error)
|
|||||||
* it might supply a reason for the failure.
|
* it might supply a reason for the failure.
|
||||||
*/
|
*/
|
||||||
pg_ctl_return = exec_prog(SERVER_START_LOG_FILE,
|
pg_ctl_return = exec_prog(SERVER_START_LOG_FILE,
|
||||||
/* pass both file names if they differ */
|
/* pass both file names if they differ */
|
||||||
(strcmp(SERVER_LOG_FILE,
|
(strcmp(SERVER_LOG_FILE,
|
||||||
SERVER_START_LOG_FILE) != 0) ?
|
SERVER_START_LOG_FILE) != 0) ?
|
||||||
SERVER_LOG_FILE : NULL,
|
SERVER_LOG_FILE : NULL,
|
||||||
|
@ -59,7 +59,7 @@ get_tablespace_paths(void)
|
|||||||
|
|
||||||
if ((os_info.num_old_tablespaces = PQntuples(res)) != 0)
|
if ((os_info.num_old_tablespaces = PQntuples(res)) != 0)
|
||||||
os_info.old_tablespaces = (char **) pg_malloc(
|
os_info.old_tablespaces = (char **) pg_malloc(
|
||||||
os_info.num_old_tablespaces * sizeof(char *));
|
os_info.num_old_tablespaces * sizeof(char *));
|
||||||
else
|
else
|
||||||
os_info.old_tablespaces = NULL;
|
os_info.old_tablespaces = NULL;
|
||||||
|
|
||||||
|
@ -40,8 +40,8 @@ void
|
|||||||
end_progress_output(void)
|
end_progress_output(void)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* In case nothing printed; pass a space so gcc doesn't complain about
|
* In case nothing printed; pass a space so gcc doesn't complain about
|
||||||
* empty format string.
|
* empty format string.
|
||||||
*/
|
*/
|
||||||
prep_status(" ");
|
prep_status(" ");
|
||||||
}
|
}
|
||||||
@ -115,12 +115,12 @@ pg_log(eLogType type, char *fmt,...)
|
|||||||
if (isatty(fileno(stdout)))
|
if (isatty(fileno(stdout)))
|
||||||
/* -2 because we use a 2-space indent */
|
/* -2 because we use a 2-space indent */
|
||||||
printf(" %s%-*.*s\r",
|
printf(" %s%-*.*s\r",
|
||||||
/* prefix with "..." if we do leading truncation */
|
/* prefix with "..." if we do leading truncation */
|
||||||
strlen(message) <= MESSAGE_WIDTH - 2 ? "" : "...",
|
strlen(message) <= MESSAGE_WIDTH - 2 ? "" : "...",
|
||||||
MESSAGE_WIDTH - 2, MESSAGE_WIDTH - 2,
|
MESSAGE_WIDTH - 2, MESSAGE_WIDTH - 2,
|
||||||
/* optional leading truncation */
|
/* optional leading truncation */
|
||||||
strlen(message) <= MESSAGE_WIDTH - 2 ? message :
|
strlen(message) <= MESSAGE_WIDTH - 2 ? message :
|
||||||
message + strlen(message) - MESSAGE_WIDTH + 3 + 2);
|
message + strlen(message) - MESSAGE_WIDTH + 3 + 2);
|
||||||
else
|
else
|
||||||
printf(" %s\n", _(message));
|
printf(" %s\n", _(message));
|
||||||
break;
|
break;
|
||||||
|
@ -41,7 +41,7 @@ timestamptz_to_time_t(TimestampTz t)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Stopgap implementation of timestamptz_to_str that doesn't depend on backend
|
* Stopgap implementation of timestamptz_to_str that doesn't depend on backend
|
||||||
* infrastructure. This will work for timestamps that are within the range
|
* infrastructure. This will work for timestamps that are within the range
|
||||||
* of the platform time_t type. (pg_time_t is compatible except for possibly
|
* of the platform time_t type. (pg_time_t is compatible except for possibly
|
||||||
* being wider.)
|
* being wider.)
|
||||||
*
|
*
|
||||||
@ -77,7 +77,7 @@ timestamptz_to_str(TimestampTz dt)
|
|||||||
* be linked/called.
|
* be linked/called.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
appendStringInfo(StringInfo str, const char *fmt, ...)
|
appendStringInfo(StringInfo str, const char *fmt,...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ fatal_error(const char *fmt,...)
|
|||||||
static void
|
static void
|
||||||
print_rmgr_list(void)
|
print_rmgr_list(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < RM_MAX_ID + 1; i++)
|
for (i = 0; i < RM_MAX_ID + 1; i++)
|
||||||
{
|
{
|
||||||
@ -88,7 +88,8 @@ print_rmgr_list(void)
|
|||||||
static bool
|
static bool
|
||||||
verify_directory(const char *directory)
|
verify_directory(const char *directory)
|
||||||
{
|
{
|
||||||
DIR *dir = opendir(directory);
|
DIR *dir = opendir(directory);
|
||||||
|
|
||||||
if (dir == NULL)
|
if (dir == NULL)
|
||||||
return false;
|
return false;
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
@ -113,7 +114,7 @@ split_path(const char *path, char **dir, char **fname)
|
|||||||
if (sep != NULL)
|
if (sep != NULL)
|
||||||
{
|
{
|
||||||
*dir = pg_strdup(path);
|
*dir = pg_strdup(path);
|
||||||
(*dir)[(sep - path) + 1] = '\0'; /* no strndup */
|
(*dir)[(sep - path) + 1] = '\0'; /* no strndup */
|
||||||
*fname = pg_strdup(sep + 1);
|
*fname = pg_strdup(sep + 1);
|
||||||
}
|
}
|
||||||
/* local directory */
|
/* local directory */
|
||||||
@ -596,7 +597,7 @@ main(int argc, char **argv)
|
|||||||
else if (!XLByteInSeg(private.startptr, segno))
|
else if (!XLByteInSeg(private.startptr, segno))
|
||||||
{
|
{
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%s: start log position %X/%X is not inside file \"%s\"\n",
|
"%s: start log position %X/%X is not inside file \"%s\"\n",
|
||||||
progname,
|
progname,
|
||||||
(uint32) (private.startptr >> 32),
|
(uint32) (private.startptr >> 32),
|
||||||
(uint32) private.startptr,
|
(uint32) private.startptr,
|
||||||
@ -672,9 +673,9 @@ main(int argc, char **argv)
|
|||||||
(uint32) private.startptr);
|
(uint32) private.startptr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Display a message that we're skipping data if `from` wasn't a pointer to
|
* Display a message that we're skipping data if `from` wasn't a pointer
|
||||||
* the start of a record and also wasn't a pointer to the beginning of a
|
* to the start of a record and also wasn't a pointer to the beginning of
|
||||||
* segment (e.g. we were used in file mode).
|
* a segment (e.g. we were used in file mode).
|
||||||
*/
|
*/
|
||||||
if (first_record != private.startptr && (private.startptr % XLogSegSize) != 0)
|
if (first_record != private.startptr && (private.startptr % XLogSegSize) != 0)
|
||||||
printf("first record is after %X/%X, at %X/%X, skipping over %u bytes\n",
|
printf("first record is after %X/%X, at %X/%X, skipping over %u bytes\n",
|
||||||
|
@ -33,4 +33,3 @@
|
|||||||
const RmgrDescData RmgrDescTable[RM_MAX_ID + 1] = {
|
const RmgrDescData RmgrDescTable[RM_MAX_ID + 1] = {
|
||||||
#include "access/rmgrlist.h"
|
#include "access/rmgrlist.h"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,9 +13,9 @@
|
|||||||
typedef struct RmgrDescData
|
typedef struct RmgrDescData
|
||||||
{
|
{
|
||||||
const char *rm_name;
|
const char *rm_name;
|
||||||
void (*rm_desc) (StringInfo buf, uint8 xl_info, char *rec);
|
void (*rm_desc) (StringInfo buf, uint8 xl_info, char *rec);
|
||||||
} RmgrDescData;
|
} RmgrDescData;
|
||||||
|
|
||||||
extern const RmgrDescData RmgrDescTable[];
|
extern const RmgrDescData RmgrDescTable[];
|
||||||
|
|
||||||
#endif /* RMGRDESC_H */
|
#endif /* RMGRDESC_H */
|
||||||
|
@ -162,7 +162,8 @@ char *index_tablespace = NULL;
|
|||||||
|
|
||||||
bool use_log; /* log transaction latencies to a file */
|
bool use_log; /* log transaction latencies to a file */
|
||||||
bool use_quiet; /* quiet logging onto stderr */
|
bool use_quiet; /* quiet logging onto stderr */
|
||||||
int agg_interval; /* log aggregates instead of individual transactions */
|
int agg_interval; /* log aggregates instead of individual
|
||||||
|
* transactions */
|
||||||
bool is_connect; /* establish connection for each transaction */
|
bool is_connect; /* establish connection for each transaction */
|
||||||
bool is_latencies; /* report per-command latencies */
|
bool is_latencies; /* report per-command latencies */
|
||||||
int main_pid; /* main process id used in log filename */
|
int main_pid; /* main process id used in log filename */
|
||||||
@ -261,12 +262,13 @@ typedef struct
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
|
||||||
long start_time; /* when does the interval start */
|
long start_time; /* when does the interval start */
|
||||||
int cnt; /* number of transactions */
|
int cnt; /* number of transactions */
|
||||||
double min_duration; /* min/max durations */
|
double min_duration; /* min/max durations */
|
||||||
double max_duration;
|
double max_duration;
|
||||||
double sum; /* sum(duration), sum(duration^2) - for estimates */
|
double sum; /* sum(duration), sum(duration^2) - for
|
||||||
double sum2;
|
* estimates */
|
||||||
|
double sum2;
|
||||||
|
|
||||||
} AggVals;
|
} AggVals;
|
||||||
|
|
||||||
@ -874,12 +876,13 @@ clientDone(CState *st, bool ok)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
void agg_vals_init(AggVals * aggs, instr_time start)
|
void
|
||||||
|
agg_vals_init(AggVals *aggs, instr_time start)
|
||||||
{
|
{
|
||||||
/* basic counters */
|
/* basic counters */
|
||||||
aggs->cnt = 0; /* number of transactions */
|
aggs->cnt = 0; /* number of transactions */
|
||||||
aggs->sum = 0; /* SUM(duration) */
|
aggs->sum = 0; /* SUM(duration) */
|
||||||
aggs->sum2 = 0; /* SUM(duration*duration) */
|
aggs->sum2 = 0; /* SUM(duration*duration) */
|
||||||
|
|
||||||
/* min and max transaction duration */
|
/* min and max transaction duration */
|
||||||
aggs->min_duration = 0;
|
aggs->min_duration = 0;
|
||||||
@ -891,7 +894,7 @@ void agg_vals_init(AggVals * aggs, instr_time start)
|
|||||||
|
|
||||||
/* return false iff client should be disconnected */
|
/* return false iff client should be disconnected */
|
||||||
static bool
|
static bool
|
||||||
doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile, AggVals * agg)
|
doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile, AggVals *agg)
|
||||||
{
|
{
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
Command **commands;
|
Command **commands;
|
||||||
@ -964,31 +967,39 @@ top:
|
|||||||
/* should we aggregate the results or not? */
|
/* should we aggregate the results or not? */
|
||||||
if (agg_interval > 0)
|
if (agg_interval > 0)
|
||||||
{
|
{
|
||||||
/* are we still in the same interval? if yes, accumulate the
|
/*
|
||||||
* values (print them otherwise) */
|
* are we still in the same interval? if yes, accumulate
|
||||||
|
* the values (print them otherwise)
|
||||||
|
*/
|
||||||
if (agg->start_time + agg_interval >= INSTR_TIME_GET_DOUBLE(now))
|
if (agg->start_time + agg_interval >= INSTR_TIME_GET_DOUBLE(now))
|
||||||
{
|
{
|
||||||
agg->cnt += 1;
|
agg->cnt += 1;
|
||||||
agg->sum += usec;
|
agg->sum += usec;
|
||||||
agg->sum2 += usec * usec;
|
agg->sum2 += usec * usec;
|
||||||
|
|
||||||
/* first in this aggregation interval */
|
/* first in this aggregation interval */
|
||||||
if ((agg->cnt == 1) || (usec < agg->min_duration))
|
if ((agg->cnt == 1) || (usec < agg->min_duration))
|
||||||
agg->min_duration = usec;
|
agg->min_duration = usec;
|
||||||
|
|
||||||
if ((agg->cnt == 1) || (usec > agg->max_duration))
|
if ((agg->cnt == 1) || (usec > agg->max_duration))
|
||||||
agg->max_duration = usec;
|
agg->max_duration = usec;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Loop until we reach the interval of the current transaction (and
|
/*
|
||||||
* print all the empty intervals in between). */
|
* Loop until we reach the interval of the current
|
||||||
|
* transaction (and print all the empty intervals in
|
||||||
|
* between).
|
||||||
|
*/
|
||||||
while (agg->start_time + agg_interval < INSTR_TIME_GET_DOUBLE(now))
|
while (agg->start_time + agg_interval < INSTR_TIME_GET_DOUBLE(now))
|
||||||
{
|
{
|
||||||
/* This is a non-Windows branch (thanks to the ifdef in usage), so
|
/*
|
||||||
* we don't need to handle this in a special way (see below). */
|
* This is a non-Windows branch (thanks to the
|
||||||
|
* ifdef in usage), so we don't need to handle
|
||||||
|
* this in a special way (see below).
|
||||||
|
*/
|
||||||
fprintf(logfile, "%ld %d %.0f %.0f %.0f %.0f\n",
|
fprintf(logfile, "%ld %d %.0f %.0f %.0f %.0f\n",
|
||||||
agg->start_time, agg->cnt, agg->sum, agg->sum2,
|
agg->start_time, agg->cnt, agg->sum, agg->sum2,
|
||||||
agg->min_duration, agg->max_duration);
|
agg->min_duration, agg->max_duration);
|
||||||
|
|
||||||
/* move to the next inteval */
|
/* move to the next inteval */
|
||||||
@ -1002,7 +1013,10 @@ top:
|
|||||||
agg->sum2 = 0;
|
agg->sum2 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* and now update the reset values (include the current) */
|
/*
|
||||||
|
* and now update the reset values (include the
|
||||||
|
* current)
|
||||||
|
*/
|
||||||
agg->cnt = 1;
|
agg->cnt = 1;
|
||||||
agg->min_duration = usec;
|
agg->min_duration = usec;
|
||||||
agg->max_duration = usec;
|
agg->max_duration = usec;
|
||||||
@ -1014,12 +1028,20 @@ top:
|
|||||||
{
|
{
|
||||||
/* no, print raw transactions */
|
/* no, print raw transactions */
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
/* This is more than we really ought to know about instr_time */
|
|
||||||
|
/*
|
||||||
|
* This is more than we really ought to know about
|
||||||
|
* instr_time
|
||||||
|
*/
|
||||||
fprintf(logfile, "%d %d %.0f %d %ld %ld\n",
|
fprintf(logfile, "%d %d %.0f %d %ld %ld\n",
|
||||||
st->id, st->cnt, usec, st->use_file,
|
st->id, st->cnt, usec, st->use_file,
|
||||||
(long) now.tv_sec, (long) now.tv_usec);
|
(long) now.tv_sec, (long) now.tv_usec);
|
||||||
#else
|
#else
|
||||||
/* On Windows, instr_time doesn't provide a timestamp anyway */
|
|
||||||
|
/*
|
||||||
|
* On Windows, instr_time doesn't provide a timestamp
|
||||||
|
* anyway
|
||||||
|
*/
|
||||||
fprintf(logfile, "%d %d %.0f %d 0 0\n",
|
fprintf(logfile, "%d %d %.0f %d 0 0\n",
|
||||||
st->id, st->cnt, usec, st->use_file);
|
st->id, st->cnt, usec, st->use_file);
|
||||||
#endif
|
#endif
|
||||||
@ -1234,11 +1256,11 @@ top:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* getrand() needs to be able to subtract max from min and add
|
* getrand() needs to be able to subtract max from min and add one
|
||||||
* one to the result without overflowing. Since we know max > min,
|
* to the result without overflowing. Since we know max > min, we
|
||||||
* we can detect overflow just by checking for a negative result.
|
* can detect overflow just by checking for a negative result. But
|
||||||
* But we must check both that the subtraction doesn't overflow,
|
* we must check both that the subtraction doesn't overflow, and
|
||||||
* and that adding one to the result doesn't overflow either.
|
* that adding one to the result doesn't overflow either.
|
||||||
*/
|
*/
|
||||||
if (max - min < 0 || (max - min) + 1 < 0)
|
if (max - min < 0 || (max - min) + 1 < 0)
|
||||||
{
|
{
|
||||||
@ -1418,7 +1440,6 @@ disconnect_all(CState *state, int length)
|
|||||||
static void
|
static void
|
||||||
init(bool is_no_vacuum)
|
init(bool is_no_vacuum)
|
||||||
{
|
{
|
||||||
|
|
||||||
/* The scale factor at/beyond which 32bit integers are incapable of storing
|
/* The scale factor at/beyond which 32bit integers are incapable of storing
|
||||||
* 64bit values.
|
* 64bit values.
|
||||||
*
|
*
|
||||||
@ -1446,8 +1467,8 @@ init(bool is_no_vacuum)
|
|||||||
{
|
{
|
||||||
"pgbench_history",
|
"pgbench_history",
|
||||||
scale >= SCALE_32BIT_THRESHOLD
|
scale >= SCALE_32BIT_THRESHOLD
|
||||||
? "tid int,bid int,aid bigint,delta int,mtime timestamp,filler char(22)"
|
? "tid int,bid int,aid bigint,delta int,mtime timestamp,filler char(22)"
|
||||||
: "tid int,bid int,aid int,delta int,mtime timestamp,filler char(22)",
|
: "tid int,bid int,aid int,delta int,mtime timestamp,filler char(22)",
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1458,8 +1479,8 @@ init(bool is_no_vacuum)
|
|||||||
{
|
{
|
||||||
"pgbench_accounts",
|
"pgbench_accounts",
|
||||||
scale >= SCALE_32BIT_THRESHOLD
|
scale >= SCALE_32BIT_THRESHOLD
|
||||||
? "aid bigint not null,bid int,abalance int,filler char(84)"
|
? "aid bigint not null,bid int,abalance int,filler char(84)"
|
||||||
: "aid int not null,bid int,abalance int,filler char(84)",
|
: "aid int not null,bid int,abalance int,filler char(84)",
|
||||||
1
|
1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1488,8 +1509,10 @@ init(bool is_no_vacuum)
|
|||||||
int64 k;
|
int64 k;
|
||||||
|
|
||||||
/* used to track elapsed time and estimate of the remaining time */
|
/* used to track elapsed time and estimate of the remaining time */
|
||||||
instr_time start, diff;
|
instr_time start,
|
||||||
double elapsed_sec, remaining_sec;
|
diff;
|
||||||
|
double elapsed_sec,
|
||||||
|
remaining_sec;
|
||||||
int log_interval = 1;
|
int log_interval = 1;
|
||||||
|
|
||||||
if ((con = doConnect()) == NULL)
|
if ((con = doConnect()) == NULL)
|
||||||
@ -1573,9 +1596,11 @@ init(bool is_no_vacuum)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we want to stick with the original logging, print a message each
|
/*
|
||||||
* 100k inserted rows. */
|
* If we want to stick with the original logging, print a message each
|
||||||
if ((! use_quiet) && (j % 100000 == 0))
|
* 100k inserted rows.
|
||||||
|
*/
|
||||||
|
if ((!use_quiet) && (j % 100000 == 0))
|
||||||
{
|
{
|
||||||
INSTR_TIME_SET_CURRENT(diff);
|
INSTR_TIME_SET_CURRENT(diff);
|
||||||
INSTR_TIME_SUBTRACT(diff, start);
|
INSTR_TIME_SUBTRACT(diff, start);
|
||||||
@ -1584,9 +1609,9 @@ init(bool is_no_vacuum)
|
|||||||
remaining_sec = (scale * naccounts - j) * elapsed_sec / j;
|
remaining_sec = (scale * naccounts - j) * elapsed_sec / j;
|
||||||
|
|
||||||
fprintf(stderr, INT64_FORMAT " of " INT64_FORMAT " tuples (%d%%) done (elapsed %.2f s, remaining %.2f s).\n",
|
fprintf(stderr, INT64_FORMAT " of " INT64_FORMAT " tuples (%d%%) done (elapsed %.2f s, remaining %.2f s).\n",
|
||||||
j, (int64)naccounts * scale,
|
j, (int64) naccounts * scale,
|
||||||
(int) (((int64) j * 100) / (naccounts * scale)),
|
(int) (((int64) j * 100) / (naccounts * scale)),
|
||||||
elapsed_sec, remaining_sec);
|
elapsed_sec, remaining_sec);
|
||||||
}
|
}
|
||||||
/* let's not call the timing for each row, but only each 100 rows */
|
/* let's not call the timing for each row, but only each 100 rows */
|
||||||
else if (use_quiet && (j % 100 == 0))
|
else if (use_quiet && (j % 100 == 0))
|
||||||
@ -1598,14 +1623,15 @@ init(bool is_no_vacuum)
|
|||||||
remaining_sec = (scale * naccounts - j) * elapsed_sec / j;
|
remaining_sec = (scale * naccounts - j) * elapsed_sec / j;
|
||||||
|
|
||||||
/* have we reached the next interval (or end)? */
|
/* have we reached the next interval (or end)? */
|
||||||
if ((j == scale * naccounts) || (elapsed_sec >= log_interval * LOG_STEP_SECONDS)) {
|
if ((j == scale * naccounts) || (elapsed_sec >= log_interval * LOG_STEP_SECONDS))
|
||||||
|
{
|
||||||
|
|
||||||
fprintf(stderr, INT64_FORMAT " of " INT64_FORMAT " tuples (%d%%) done (elapsed %.2f s, remaining %.2f s).\n",
|
fprintf(stderr, INT64_FORMAT " of " INT64_FORMAT " tuples (%d%%) done (elapsed %.2f s, remaining %.2f s).\n",
|
||||||
j, (int64)naccounts * scale,
|
j, (int64) naccounts * scale,
|
||||||
(int) (((int64) j * 100) / (naccounts * scale)), elapsed_sec, remaining_sec);
|
(int) (((int64) j * 100) / (naccounts * scale)), elapsed_sec, remaining_sec);
|
||||||
|
|
||||||
/* skip to the next interval */
|
/* skip to the next interval */
|
||||||
log_interval = (int)ceil(elapsed_sec/LOG_STEP_SECONDS);
|
log_interval = (int) ceil(elapsed_sec / LOG_STEP_SECONDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2393,17 +2419,20 @@ main(int argc, char **argv)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (agg_interval > 0 && (! use_log)) {
|
if (agg_interval > 0 && (!use_log))
|
||||||
|
{
|
||||||
fprintf(stderr, "log aggregation is allowed only when actually logging transactions\n");
|
fprintf(stderr, "log aggregation is allowed only when actually logging transactions\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((duration > 0) && (agg_interval > duration)) {
|
if ((duration > 0) && (agg_interval > duration))
|
||||||
|
{
|
||||||
fprintf(stderr, "number of seconds for aggregation (%d) must not be higher that test duration (%d)\n", agg_interval, duration);
|
fprintf(stderr, "number of seconds for aggregation (%d) must not be higher that test duration (%d)\n", agg_interval, duration);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((duration > 0) && (agg_interval > 0) && (duration % agg_interval != 0)) {
|
if ((duration > 0) && (agg_interval > 0) && (duration % agg_interval != 0))
|
||||||
|
{
|
||||||
fprintf(stderr, "duration (%d) must be a multiple of aggregation interval (%d)\n", duration, agg_interval);
|
fprintf(stderr, "duration (%d) must be a multiple of aggregation interval (%d)\n", duration, agg_interval);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -117,14 +117,11 @@ mp_result mp_int_mul_value(mp_int a, int value, mp_int c);
|
|||||||
mp_result mp_int_mul_pow2(mp_int a, int p2, mp_int c);
|
mp_result mp_int_mul_pow2(mp_int a, int p2, mp_int c);
|
||||||
mp_result mp_int_sqr(mp_int a, mp_int c); /* c = a * a */
|
mp_result mp_int_sqr(mp_int a, mp_int c); /* c = a * a */
|
||||||
|
|
||||||
mp_result
|
mp_result mp_int_div(mp_int a, mp_int b, /* q = a / b */
|
||||||
mp_int_div(mp_int a, mp_int b, /* q = a / b */
|
|
||||||
mp_int q, mp_int r); /* r = a % b */
|
mp_int q, mp_int r); /* r = a % b */
|
||||||
mp_result
|
mp_result mp_int_div_value(mp_int a, int value, /* q = a / value */
|
||||||
mp_int_div_value(mp_int a, int value, /* q = a / value */
|
|
||||||
mp_int q, int *r); /* r = a % value */
|
mp_int q, int *r); /* r = a % value */
|
||||||
mp_result
|
mp_result mp_int_div_pow2(mp_int a, int p2, /* q = a / 2^p2 */
|
||||||
mp_int_div_pow2(mp_int a, int p2, /* q = a / 2^p2 */
|
|
||||||
mp_int q, mp_int r); /* r = q % 2^p2 */
|
mp_int q, mp_int r); /* r = q % 2^p2 */
|
||||||
mp_result mp_int_mod(mp_int a, mp_int m, mp_int c); /* c = a % m */
|
mp_result mp_int_mod(mp_int a, mp_int m, mp_int c); /* c = a % m */
|
||||||
|
|
||||||
@ -143,17 +140,13 @@ int mp_int_divisible_value(mp_int a, int v);
|
|||||||
/* Returns k >= 0 such that z = 2^k, if one exists; otherwise < 0 */
|
/* Returns k >= 0 such that z = 2^k, if one exists; otherwise < 0 */
|
||||||
int mp_int_is_pow2(mp_int z);
|
int mp_int_is_pow2(mp_int z);
|
||||||
|
|
||||||
mp_result
|
mp_result mp_int_exptmod(mp_int a, mp_int b, mp_int m,
|
||||||
mp_int_exptmod(mp_int a, mp_int b, mp_int m,
|
|
||||||
mp_int c); /* c = a^b (mod m) */
|
mp_int c); /* c = a^b (mod m) */
|
||||||
mp_result
|
mp_result mp_int_exptmod_evalue(mp_int a, int value,
|
||||||
mp_int_exptmod_evalue(mp_int a, int value,
|
|
||||||
mp_int m, mp_int c); /* c = a^v (mod m) */
|
mp_int m, mp_int c); /* c = a^v (mod m) */
|
||||||
mp_result
|
mp_result mp_int_exptmod_bvalue(int value, mp_int b,
|
||||||
mp_int_exptmod_bvalue(int value, mp_int b,
|
|
||||||
mp_int m, mp_int c); /* c = v^b (mod m) */
|
mp_int m, mp_int c); /* c = v^b (mod m) */
|
||||||
mp_result
|
mp_result mp_int_exptmod_known(mp_int a, mp_int b,
|
||||||
mp_int_exptmod_known(mp_int a, mp_int b,
|
|
||||||
mp_int m, mp_int mu,
|
mp_int m, mp_int mu,
|
||||||
mp_int c); /* c = a^b (mod m) */
|
mp_int c); /* c = a^b (mod m) */
|
||||||
mp_result mp_int_redux_const(mp_int m, mp_int c);
|
mp_result mp_int_redux_const(mp_int m, mp_int c);
|
||||||
@ -162,8 +155,7 @@ mp_result mp_int_invmod(mp_int a, mp_int m, mp_int c); /* c = 1/a (mod m) */
|
|||||||
|
|
||||||
mp_result mp_int_gcd(mp_int a, mp_int b, mp_int c); /* c = gcd(a, b) */
|
mp_result mp_int_gcd(mp_int a, mp_int b, mp_int c); /* c = gcd(a, b) */
|
||||||
|
|
||||||
mp_result
|
mp_result mp_int_egcd(mp_int a, mp_int b, mp_int c, /* c = gcd(a, b) */
|
||||||
mp_int_egcd(mp_int a, mp_int b, mp_int c, /* c = gcd(a, b) */
|
|
||||||
mp_int x, mp_int y); /* c = ax + by */
|
mp_int x, mp_int y); /* c = ax + by */
|
||||||
|
|
||||||
mp_result mp_int_sqrt(mp_int a, mp_int c); /* c = floor(sqrt(q)) */
|
mp_result mp_int_sqrt(mp_int a, mp_int c); /* c = floor(sqrt(q)) */
|
||||||
|
@ -265,8 +265,7 @@ int pgp_s2k_read(PullFilter *src, PGP_S2K *s2k);
|
|||||||
int pgp_s2k_process(PGP_S2K *s2k, int cipher, const uint8 *key, int klen);
|
int pgp_s2k_process(PGP_S2K *s2k, int cipher, const uint8 *key, int klen);
|
||||||
|
|
||||||
typedef struct PGP_CFB PGP_CFB;
|
typedef struct PGP_CFB PGP_CFB;
|
||||||
int
|
int pgp_cfb_create(PGP_CFB **ctx_p, int algo,
|
||||||
pgp_cfb_create(PGP_CFB **ctx_p, int algo,
|
|
||||||
const uint8 *key, int key_len, int recync, uint8 *iv);
|
const uint8 *key, int key_len, int recync, uint8 *iv);
|
||||||
void pgp_cfb_free(PGP_CFB *ctx);
|
void pgp_cfb_free(PGP_CFB *ctx);
|
||||||
int pgp_cfb_encrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst);
|
int pgp_cfb_encrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst);
|
||||||
|
@ -124,7 +124,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
|
|||||||
/* scan the relation */
|
/* scan the relation */
|
||||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
||||||
{
|
{
|
||||||
HTSU_Result htsu;
|
HTSU_Result htsu;
|
||||||
TransactionId xmax;
|
TransactionId xmax;
|
||||||
uint16 infomask;
|
uint16 infomask;
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
|
|||||||
values = (char **) palloc(mydata->ncolumns * sizeof(char *));
|
values = (char **) palloc(mydata->ncolumns * sizeof(char *));
|
||||||
|
|
||||||
values[Atnum_tid] = (char *) DirectFunctionCall1(tidout,
|
values[Atnum_tid] = (char *) DirectFunctionCall1(tidout,
|
||||||
PointerGetDatum(&tuple->t_self));
|
PointerGetDatum(&tuple->t_self));
|
||||||
|
|
||||||
values[Atnum_xmax] = palloc(NCHARS * sizeof(char));
|
values[Atnum_xmax] = palloc(NCHARS * sizeof(char));
|
||||||
snprintf(values[Atnum_xmax], NCHARS, "%d", xmax);
|
snprintf(values[Atnum_xmax], NCHARS, "%d", xmax);
|
||||||
@ -166,7 +166,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
|
|||||||
values[Atnum_ismulti] = pstrdup("true");
|
values[Atnum_ismulti] = pstrdup("true");
|
||||||
|
|
||||||
allow_old = !(infomask & HEAP_LOCK_MASK) &&
|
allow_old = !(infomask & HEAP_LOCK_MASK) &&
|
||||||
(infomask & HEAP_XMAX_LOCK_ONLY);
|
(infomask & HEAP_XMAX_LOCK_ONLY);
|
||||||
nmembers = GetMultiXactIdMembers(xmax, &members, allow_old);
|
nmembers = GetMultiXactIdMembers(xmax, &members, allow_old);
|
||||||
if (nmembers == -1)
|
if (nmembers == -1)
|
||||||
{
|
{
|
||||||
@ -280,8 +280,8 @@ pgrowlocks(PG_FUNCTION_ARGS)
|
|||||||
result = HeapTupleGetDatum(tuple);
|
result = HeapTupleGetDatum(tuple);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* no need to pfree what we allocated; it's on a short-lived memory
|
* no need to pfree what we allocated; it's on a short-lived
|
||||||
* context anyway
|
* memory context anyway
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SRF_RETURN_NEXT(funcctx, result);
|
SRF_RETURN_NEXT(funcctx, result);
|
||||||
|
@ -93,7 +93,7 @@ typedef struct GinIndexStat
|
|||||||
{
|
{
|
||||||
int32 version;
|
int32 version;
|
||||||
|
|
||||||
BlockNumber pending_pages;
|
BlockNumber pending_pages;
|
||||||
int64 pending_tuples;
|
int64 pending_tuples;
|
||||||
} GinIndexStat;
|
} GinIndexStat;
|
||||||
|
|
||||||
@ -324,7 +324,7 @@ pgstatginindex(PG_FUNCTION_ARGS)
|
|||||||
Relation rel;
|
Relation rel;
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
Page page;
|
Page page;
|
||||||
GinMetaPageData *metadata;
|
GinMetaPageData *metadata;
|
||||||
GinIndexStat stats;
|
GinIndexStat stats;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
TupleDesc tupleDesc;
|
TupleDesc tupleDesc;
|
||||||
@ -351,7 +351,7 @@ pgstatginindex(PG_FUNCTION_ARGS)
|
|||||||
if (RELATION_IS_OTHER_TEMP(rel))
|
if (RELATION_IS_OTHER_TEMP(rel))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("cannot access temporary indexes of other sessions")));
|
errmsg("cannot access temporary indexes of other sessions")));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read metapage
|
* Read metapage
|
||||||
|
@ -326,7 +326,7 @@ configure_remote_session(PGconn *conn)
|
|||||||
* anyway. However it makes the regression test outputs more predictable.
|
* anyway. However it makes the regression test outputs more predictable.
|
||||||
*
|
*
|
||||||
* We don't risk setting remote zone equal to ours, since the remote
|
* We don't risk setting remote zone equal to ours, since the remote
|
||||||
* server might use a different timezone database. Instead, use UTC
|
* server might use a different timezone database. Instead, use UTC
|
||||||
* (quoted, because very old servers are picky about case).
|
* (quoted, because very old servers are picky about case).
|
||||||
*/
|
*/
|
||||||
do_sql_command(conn, "SET timezone = 'UTC'");
|
do_sql_command(conn, "SET timezone = 'UTC'");
|
||||||
|
@ -133,7 +133,7 @@ typedef struct PgFdwScanState
|
|||||||
|
|
||||||
/* extracted fdw_private data */
|
/* extracted fdw_private data */
|
||||||
char *query; /* text of SELECT command */
|
char *query; /* text of SELECT command */
|
||||||
List *retrieved_attrs; /* list of retrieved attribute numbers */
|
List *retrieved_attrs; /* list of retrieved attribute numbers */
|
||||||
|
|
||||||
/* for remote query execution */
|
/* for remote query execution */
|
||||||
PGconn *conn; /* connection for the scan */
|
PGconn *conn; /* connection for the scan */
|
||||||
@ -174,7 +174,7 @@ typedef struct PgFdwModifyState
|
|||||||
char *query; /* text of INSERT/UPDATE/DELETE command */
|
char *query; /* text of INSERT/UPDATE/DELETE command */
|
||||||
List *target_attrs; /* list of target attribute numbers */
|
List *target_attrs; /* list of target attribute numbers */
|
||||||
bool has_returning; /* is there a RETURNING clause? */
|
bool has_returning; /* is there a RETURNING clause? */
|
||||||
List *retrieved_attrs; /* attr numbers retrieved by RETURNING */
|
List *retrieved_attrs; /* attr numbers retrieved by RETURNING */
|
||||||
|
|
||||||
/* info about parameters for prepared statement */
|
/* info about parameters for prepared statement */
|
||||||
AttrNumber ctidAttno; /* attnum of input resjunk ctid column */
|
AttrNumber ctidAttno; /* attnum of input resjunk ctid column */
|
||||||
@ -192,7 +192,7 @@ typedef struct PgFdwAnalyzeState
|
|||||||
{
|
{
|
||||||
Relation rel; /* relcache entry for the foreign table */
|
Relation rel; /* relcache entry for the foreign table */
|
||||||
AttInMetadata *attinmeta; /* attribute datatype conversion metadata */
|
AttInMetadata *attinmeta; /* attribute datatype conversion metadata */
|
||||||
List *retrieved_attrs; /* attr numbers retrieved by query */
|
List *retrieved_attrs; /* attr numbers retrieved by query */
|
||||||
|
|
||||||
/* collected sample rows */
|
/* collected sample rows */
|
||||||
HeapTuple *rows; /* array of size targrows */
|
HeapTuple *rows; /* array of size targrows */
|
||||||
@ -424,8 +424,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If the table or the server is configured to use remote estimates,
|
* If the table or the server is configured to use remote estimates,
|
||||||
* identify which user to do remote access as during planning. This
|
* identify which user to do remote access as during planning. This
|
||||||
* should match what ExecCheckRTEPerms() does. If we fail due to lack of
|
* should match what ExecCheckRTEPerms() does. If we fail due to lack of
|
||||||
* permissions, the query would have failed at runtime anyway.
|
* permissions, the query would have failed at runtime anyway.
|
||||||
*/
|
*/
|
||||||
if (fpinfo->use_remote_estimate)
|
if (fpinfo->use_remote_estimate)
|
||||||
@ -447,7 +447,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Identify which attributes will need to be retrieved from the remote
|
* Identify which attributes will need to be retrieved from the remote
|
||||||
* server. These include all attrs needed for joins or final output, plus
|
* server. These include all attrs needed for joins or final output, plus
|
||||||
* all attrs used in the local_conds. (Note: if we end up using a
|
* all attrs used in the local_conds. (Note: if we end up using a
|
||||||
* parameterized scan, it's possible that some of the join clauses will be
|
* parameterized scan, it's possible that some of the join clauses will be
|
||||||
* sent to the remote and thus we wouldn't really need to retrieve the
|
* sent to the remote and thus we wouldn't really need to retrieve the
|
||||||
@ -921,7 +921,7 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags)
|
|||||||
fsstate->query = strVal(list_nth(fsplan->fdw_private,
|
fsstate->query = strVal(list_nth(fsplan->fdw_private,
|
||||||
FdwScanPrivateSelectSql));
|
FdwScanPrivateSelectSql));
|
||||||
fsstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
|
fsstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
|
||||||
FdwScanPrivateRetrievedAttrs);
|
FdwScanPrivateRetrievedAttrs);
|
||||||
|
|
||||||
/* Create contexts for batches of tuples and per-tuple temp workspace. */
|
/* Create contexts for batches of tuples and per-tuple temp workspace. */
|
||||||
fsstate->batch_cxt = AllocSetContextCreate(estate->es_query_cxt,
|
fsstate->batch_cxt = AllocSetContextCreate(estate->es_query_cxt,
|
||||||
@ -1305,7 +1305,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate,
|
|||||||
fmstate->has_returning = intVal(list_nth(fdw_private,
|
fmstate->has_returning = intVal(list_nth(fdw_private,
|
||||||
FdwModifyPrivateHasReturning));
|
FdwModifyPrivateHasReturning));
|
||||||
fmstate->retrieved_attrs = (List *) list_nth(fdw_private,
|
fmstate->retrieved_attrs = (List *) list_nth(fdw_private,
|
||||||
FdwModifyPrivateRetrievedAttrs);
|
FdwModifyPrivateRetrievedAttrs);
|
||||||
|
|
||||||
/* Create context for per-tuple temp workspace. */
|
/* Create context for per-tuple temp workspace. */
|
||||||
fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
|
fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
|
||||||
@ -1903,7 +1903,7 @@ create_cursor(ForeignScanState *node)
|
|||||||
* Notice that we pass NULL for paramTypes, thus forcing the remote server
|
* Notice that we pass NULL for paramTypes, thus forcing the remote server
|
||||||
* to infer types for all parameters. Since we explicitly cast every
|
* to infer types for all parameters. Since we explicitly cast every
|
||||||
* parameter (see deparse.c), the "inference" is trivial and will produce
|
* parameter (see deparse.c), the "inference" is trivial and will produce
|
||||||
* the desired result. This allows us to avoid assuming that the remote
|
* the desired result. This allows us to avoid assuming that the remote
|
||||||
* server has the same OIDs we do for the parameters' types.
|
* server has the same OIDs we do for the parameters' types.
|
||||||
*
|
*
|
||||||
* We don't use a PG_TRY block here, so be careful not to throw error
|
* We don't use a PG_TRY block here, so be careful not to throw error
|
||||||
@ -2488,7 +2488,7 @@ analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate)
|
|||||||
astate->rows[pos] = make_tuple_from_result_row(res, row,
|
astate->rows[pos] = make_tuple_from_result_row(res, row,
|
||||||
astate->rel,
|
astate->rel,
|
||||||
astate->attinmeta,
|
astate->attinmeta,
|
||||||
astate->retrieved_attrs,
|
astate->retrieved_attrs,
|
||||||
astate->temp_cxt);
|
astate->temp_cxt);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
@ -71,6 +71,6 @@ extern void deparseDeleteSql(StringInfo buf, PlannerInfo *root,
|
|||||||
List **retrieved_attrs);
|
List **retrieved_attrs);
|
||||||
extern void deparseAnalyzeSizeSql(StringInfo buf, Relation rel);
|
extern void deparseAnalyzeSizeSql(StringInfo buf, Relation rel);
|
||||||
extern void deparseAnalyzeSql(StringInfo buf, Relation rel,
|
extern void deparseAnalyzeSql(StringInfo buf, Relation rel,
|
||||||
List **retrieved_attrs);
|
List **retrieved_attrs);
|
||||||
|
|
||||||
#endif /* POSTGRES_FDW_H */
|
#endif /* POSTGRES_FDW_H */
|
||||||
|
@ -98,7 +98,7 @@ sepgsql_object_access(ObjectAccessType access,
|
|||||||
case OAT_POST_CREATE:
|
case OAT_POST_CREATE:
|
||||||
{
|
{
|
||||||
ObjectAccessPostCreate *pc_arg = arg;
|
ObjectAccessPostCreate *pc_arg = arg;
|
||||||
bool is_internal;
|
bool is_internal;
|
||||||
|
|
||||||
is_internal = pc_arg ? pc_arg->is_internal : false;
|
is_internal = pc_arg ? pc_arg->is_internal : false;
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ sepgsql_object_access(ObjectAccessType access,
|
|||||||
case DatabaseRelationId:
|
case DatabaseRelationId:
|
||||||
Assert(!is_internal);
|
Assert(!is_internal);
|
||||||
sepgsql_database_post_create(objectId,
|
sepgsql_database_post_create(objectId,
|
||||||
sepgsql_context_info.createdb_dtemplate);
|
sepgsql_context_info.createdb_dtemplate);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NamespaceRelationId:
|
case NamespaceRelationId:
|
||||||
@ -190,8 +190,8 @@ sepgsql_object_access(ObjectAccessType access,
|
|||||||
|
|
||||||
case OAT_POST_ALTER:
|
case OAT_POST_ALTER:
|
||||||
{
|
{
|
||||||
ObjectAccessPostAlter *pa_arg = arg;
|
ObjectAccessPostAlter *pa_arg = arg;
|
||||||
bool is_internal = pa_arg->is_internal;
|
bool is_internal = pa_arg->is_internal;
|
||||||
|
|
||||||
switch (classId)
|
switch (classId)
|
||||||
{
|
{
|
||||||
@ -207,21 +207,21 @@ sepgsql_object_access(ObjectAccessType access,
|
|||||||
|
|
||||||
case RelationRelationId:
|
case RelationRelationId:
|
||||||
if (subId == 0)
|
if (subId == 0)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* A case when we don't want to apply permission
|
* A case when we don't want to apply permission
|
||||||
* check is that relation is internally altered
|
* check is that relation is internally altered
|
||||||
* without user's intention. E.g, no need to
|
* without user's intention. E.g, no need to check
|
||||||
* check on toast table/index to be renamed at
|
* on toast table/index to be renamed at end of
|
||||||
* end of the table rewrites.
|
* the table rewrites.
|
||||||
*/
|
*/
|
||||||
if (is_internal)
|
if (is_internal)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
sepgsql_relation_setattr(objectId);
|
sepgsql_relation_setattr(objectId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
sepgsql_attribute_setattr(objectId, subId);
|
sepgsql_attribute_setattr(objectId, subId);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ProcedureRelationId:
|
case ProcedureRelationId:
|
||||||
@ -238,11 +238,11 @@ sepgsql_object_access(ObjectAccessType access,
|
|||||||
|
|
||||||
case OAT_NAMESPACE_SEARCH:
|
case OAT_NAMESPACE_SEARCH:
|
||||||
{
|
{
|
||||||
ObjectAccessNamespaceSearch *ns_arg = arg;
|
ObjectAccessNamespaceSearch *ns_arg = arg;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If stacked extension already decided not to allow users
|
* If stacked extension already decided not to allow users to
|
||||||
* to search this schema, we just stick with that decision.
|
* search this schema, we just stick with that decision.
|
||||||
*/
|
*/
|
||||||
if (!ns_arg->result)
|
if (!ns_arg->result)
|
||||||
break;
|
break;
|
||||||
|
@ -351,9 +351,8 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
|
|||||||
*
|
*
|
||||||
* Also, db_procedure:entrypoint permission should be checked
|
* Also, db_procedure:entrypoint permission should be checked
|
||||||
* whether this procedure can perform as an entrypoint of the
|
* whether this procedure can perform as an entrypoint of the
|
||||||
* trusted procedure, or not.
|
* trusted procedure, or not. Note that db_procedure:execute
|
||||||
* Note that db_procedure:execute permission shall be checked
|
* permission shall be checked individually.
|
||||||
* individually.
|
|
||||||
*/
|
*/
|
||||||
if (stack->new_label)
|
if (stack->new_label)
|
||||||
{
|
{
|
||||||
|
@ -236,16 +236,16 @@ sepgsql_proc_relabel(Oid functionId, const char *seclabel)
|
|||||||
void
|
void
|
||||||
sepgsql_proc_setattr(Oid functionId)
|
sepgsql_proc_setattr(Oid functionId)
|
||||||
{
|
{
|
||||||
Relation rel;
|
Relation rel;
|
||||||
ScanKeyData skey;
|
ScanKeyData skey;
|
||||||
SysScanDesc sscan;
|
SysScanDesc sscan;
|
||||||
HeapTuple oldtup;
|
HeapTuple oldtup;
|
||||||
HeapTuple newtup;
|
HeapTuple newtup;
|
||||||
Form_pg_proc oldform;
|
Form_pg_proc oldform;
|
||||||
Form_pg_proc newform;
|
Form_pg_proc newform;
|
||||||
uint32 required;
|
uint32 required;
|
||||||
ObjectAddress object;
|
ObjectAddress object;
|
||||||
char *audit_name;
|
char *audit_name;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fetch newer catalog
|
* Fetch newer catalog
|
||||||
@ -297,7 +297,7 @@ sepgsql_proc_setattr(Oid functionId)
|
|||||||
|
|
||||||
sepgsql_avc_check_perms(&object,
|
sepgsql_avc_check_perms(&object,
|
||||||
SEPG_CLASS_DB_PROCEDURE,
|
SEPG_CLASS_DB_PROCEDURE,
|
||||||
required,
|
required,
|
||||||
audit_name,
|
audit_name,
|
||||||
true);
|
true);
|
||||||
/* cleanups */
|
/* cleanups */
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
#include "sepgsql.h"
|
#include "sepgsql.h"
|
||||||
|
|
||||||
static void sepgsql_index_modify(Oid indexOid);
|
static void sepgsql_index_modify(Oid indexOid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sepgsql_attribute_post_create
|
* sepgsql_attribute_post_create
|
||||||
@ -571,13 +571,13 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel)
|
|||||||
void
|
void
|
||||||
sepgsql_relation_setattr(Oid relOid)
|
sepgsql_relation_setattr(Oid relOid)
|
||||||
{
|
{
|
||||||
Relation rel;
|
Relation rel;
|
||||||
ScanKeyData skey;
|
ScanKeyData skey;
|
||||||
SysScanDesc sscan;
|
SysScanDesc sscan;
|
||||||
HeapTuple oldtup;
|
HeapTuple oldtup;
|
||||||
HeapTuple newtup;
|
HeapTuple newtup;
|
||||||
Form_pg_class oldform;
|
Form_pg_class oldform;
|
||||||
Form_pg_class newform;
|
Form_pg_class newform;
|
||||||
ObjectAddress object;
|
ObjectAddress object;
|
||||||
char *audit_name;
|
char *audit_name;
|
||||||
uint16_t tclass;
|
uint16_t tclass;
|
||||||
@ -680,8 +680,8 @@ sepgsql_relation_setattr_extra(Relation catalog,
|
|||||||
AttrNumber anum_relation_id,
|
AttrNumber anum_relation_id,
|
||||||
AttrNumber anum_extra_id)
|
AttrNumber anum_extra_id)
|
||||||
{
|
{
|
||||||
ScanKeyData skey;
|
ScanKeyData skey;
|
||||||
SysScanDesc sscan;
|
SysScanDesc sscan;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
Datum datum;
|
Datum datum;
|
||||||
bool isnull;
|
bool isnull;
|
||||||
@ -708,7 +708,7 @@ sepgsql_relation_setattr_extra(Relation catalog,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* sepgsql_index_modify
|
* sepgsql_index_modify
|
||||||
* Handle index create, update, drop
|
* Handle index create, update, drop
|
||||||
*
|
*
|
||||||
* Unlike other relation kinds, indexes do not have their own security labels,
|
* Unlike other relation kinds, indexes do not have their own security labels,
|
||||||
* so instead of doing checks directly, treat them as extra attributes of their
|
* so instead of doing checks directly, treat them as extra attributes of their
|
||||||
|
@ -81,6 +81,7 @@ sepgsql_schema_post_create(Oid namespaceId)
|
|||||||
tcontext,
|
tcontext,
|
||||||
SEPG_CLASS_DB_SCHEMA,
|
SEPG_CLASS_DB_SCHEMA,
|
||||||
nsp_name);
|
nsp_name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check db_schema:{create}
|
* check db_schema:{create}
|
||||||
*/
|
*/
|
||||||
|
@ -40,7 +40,7 @@ typedef struct TrieChar
|
|||||||
static TrieChar *
|
static TrieChar *
|
||||||
placeChar(TrieChar *node, unsigned char *str, int lenstr, char *replaceTo, int replacelen)
|
placeChar(TrieChar *node, unsigned char *str, int lenstr, char *replaceTo, int replacelen)
|
||||||
{
|
{
|
||||||
TrieChar *curnode;
|
TrieChar *curnode;
|
||||||
|
|
||||||
if (!node)
|
if (!node)
|
||||||
{
|
{
|
||||||
@ -77,7 +77,7 @@ placeChar(TrieChar *node, unsigned char *str, int lenstr, char *replaceTo, int r
|
|||||||
static TrieChar *
|
static TrieChar *
|
||||||
initTrie(char *filename)
|
initTrie(char *filename)
|
||||||
{
|
{
|
||||||
TrieChar *volatile rootTrie = NULL;
|
TrieChar *volatile rootTrie = NULL;
|
||||||
MemoryContext ccxt = CurrentMemoryContext;
|
MemoryContext ccxt = CurrentMemoryContext;
|
||||||
tsearch_readline_state trst;
|
tsearch_readline_state trst;
|
||||||
volatile bool skip;
|
volatile bool skip;
|
||||||
@ -162,8 +162,8 @@ initTrie(char *filename)
|
|||||||
|
|
||||||
if (state >= 3)
|
if (state >= 3)
|
||||||
rootTrie = placeChar(rootTrie,
|
rootTrie = placeChar(rootTrie,
|
||||||
(unsigned char *) src, srclen,
|
(unsigned char *) src, srclen,
|
||||||
trg, trglen);
|
trg, trglen);
|
||||||
|
|
||||||
pfree(line);
|
pfree(line);
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ vacuumlo(const char *database, const struct _param * param)
|
|||||||
*/
|
*/
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
#define PARAMS_ARRAY_SIZE 7
|
#define PARAMS_ARRAY_SIZE 7
|
||||||
|
|
||||||
const char *keywords[PARAMS_ARRAY_SIZE];
|
const char *keywords[PARAMS_ARRAY_SIZE];
|
||||||
const char *values[PARAMS_ARRAY_SIZE];
|
const char *values[PARAMS_ARRAY_SIZE];
|
||||||
|
@ -43,27 +43,27 @@
|
|||||||
|
|
||||||
PG_MODULE_MAGIC;
|
PG_MODULE_MAGIC;
|
||||||
|
|
||||||
void _PG_init(void);
|
void _PG_init(void);
|
||||||
|
|
||||||
/* flags set by signal handlers */
|
/* flags set by signal handlers */
|
||||||
static volatile sig_atomic_t got_sighup = false;
|
static volatile sig_atomic_t got_sighup = false;
|
||||||
static volatile sig_atomic_t got_sigterm = false;
|
static volatile sig_atomic_t got_sigterm = false;
|
||||||
|
|
||||||
/* GUC variables */
|
/* GUC variables */
|
||||||
static int worker_spi_naptime = 10;
|
static int worker_spi_naptime = 10;
|
||||||
static int worker_spi_total_workers = 2;
|
static int worker_spi_total_workers = 2;
|
||||||
|
|
||||||
|
|
||||||
typedef struct worktable
|
typedef struct worktable
|
||||||
{
|
{
|
||||||
const char *schema;
|
const char *schema;
|
||||||
const char *name;
|
const char *name;
|
||||||
} worktable;
|
} worktable;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Signal handler for SIGTERM
|
* Signal handler for SIGTERM
|
||||||
* Set a flag to let the main loop to terminate, and set our latch to wake
|
* Set a flag to let the main loop to terminate, and set our latch to wake
|
||||||
* it up.
|
* it up.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
worker_spi_sigterm(SIGNAL_ARGS)
|
worker_spi_sigterm(SIGNAL_ARGS)
|
||||||
@ -79,8 +79,8 @@ worker_spi_sigterm(SIGNAL_ARGS)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Signal handler for SIGHUP
|
* Signal handler for SIGHUP
|
||||||
* Set a flag to let the main loop to reread the config file, and set
|
* Set a flag to let the main loop to reread the config file, and set
|
||||||
* our latch to wake it up.
|
* our latch to wake it up.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
worker_spi_sighup(SIGNAL_ARGS)
|
worker_spi_sighup(SIGNAL_ARGS)
|
||||||
@ -97,10 +97,10 @@ worker_spi_sighup(SIGNAL_ARGS)
|
|||||||
static void
|
static void
|
||||||
initialize_worker_spi(worktable *table)
|
initialize_worker_spi(worktable *table)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int ntup;
|
int ntup;
|
||||||
bool isnull;
|
bool isnull;
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
|
|
||||||
SetCurrentStatementStartTimestamp();
|
SetCurrentStatementStartTimestamp();
|
||||||
StartTransactionCommand();
|
StartTransactionCommand();
|
||||||
@ -132,11 +132,11 @@ initialize_worker_spi(worktable *table)
|
|||||||
appendStringInfo(&buf,
|
appendStringInfo(&buf,
|
||||||
"CREATE SCHEMA \"%s\" "
|
"CREATE SCHEMA \"%s\" "
|
||||||
"CREATE TABLE \"%s\" ("
|
"CREATE TABLE \"%s\" ("
|
||||||
" type text CHECK (type IN ('total', 'delta')), "
|
" type text CHECK (type IN ('total', 'delta')), "
|
||||||
" value integer)"
|
" value integer)"
|
||||||
"CREATE UNIQUE INDEX \"%s_unique_total\" ON \"%s\" (type) "
|
"CREATE UNIQUE INDEX \"%s_unique_total\" ON \"%s\" (type) "
|
||||||
"WHERE type = 'total'",
|
"WHERE type = 'total'",
|
||||||
table->schema, table->name, table->name, table->name);
|
table->schema, table->name, table->name, table->name);
|
||||||
|
|
||||||
/* set statement start time */
|
/* set statement start time */
|
||||||
SetCurrentStatementStartTimestamp();
|
SetCurrentStatementStartTimestamp();
|
||||||
@ -156,8 +156,8 @@ initialize_worker_spi(worktable *table)
|
|||||||
static void
|
static void
|
||||||
worker_spi_main(void *main_arg)
|
worker_spi_main(void *main_arg)
|
||||||
{
|
{
|
||||||
worktable *table = (worktable *) main_arg;
|
worktable *table = (worktable *) main_arg;
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
|
|
||||||
/* We're now ready to receive signals */
|
/* We're now ready to receive signals */
|
||||||
BackgroundWorkerUnblockSignals();
|
BackgroundWorkerUnblockSignals();
|
||||||
@ -170,7 +170,7 @@ worker_spi_main(void *main_arg)
|
|||||||
initialize_worker_spi(table);
|
initialize_worker_spi(table);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Quote identifiers passed to us. Note that this must be done after
|
* Quote identifiers passed to us. Note that this must be done after
|
||||||
* initialize_worker_spi, because that routine assumes the names are not
|
* initialize_worker_spi, because that routine assumes the names are not
|
||||||
* quoted.
|
* quoted.
|
||||||
*
|
*
|
||||||
@ -200,8 +200,8 @@ worker_spi_main(void *main_arg)
|
|||||||
*/
|
*/
|
||||||
while (!got_sigterm)
|
while (!got_sigterm)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Background workers mustn't call usleep() or any direct equivalent:
|
* Background workers mustn't call usleep() or any direct equivalent:
|
||||||
@ -221,27 +221,27 @@ worker_spi_main(void *main_arg)
|
|||||||
/*
|
/*
|
||||||
* In case of a SIGHUP, just reload the configuration.
|
* In case of a SIGHUP, just reload the configuration.
|
||||||
*/
|
*/
|
||||||
if (got_sighup)
|
if (got_sighup)
|
||||||
{
|
{
|
||||||
got_sighup = false;
|
got_sighup = false;
|
||||||
ProcessConfigFile(PGC_SIGHUP);
|
ProcessConfigFile(PGC_SIGHUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start a transaction on which we can run queries. Note that each
|
* Start a transaction on which we can run queries. Note that each
|
||||||
* StartTransactionCommand() call should be preceded by a
|
* StartTransactionCommand() call should be preceded by a
|
||||||
* SetCurrentStatementStartTimestamp() call, which sets both the time
|
* SetCurrentStatementStartTimestamp() call, which sets both the time
|
||||||
* for the statement we're about the run, and also the transaction
|
* for the statement we're about the run, and also the transaction
|
||||||
* start time. Also, each other query sent to SPI should probably be
|
* start time. Also, each other query sent to SPI should probably be
|
||||||
* preceded by SetCurrentStatementStartTimestamp(), so that statement
|
* preceded by SetCurrentStatementStartTimestamp(), so that statement
|
||||||
* start time is always up to date.
|
* start time is always up to date.
|
||||||
*
|
*
|
||||||
* The SPI_connect() call lets us run queries through the SPI manager,
|
* The SPI_connect() call lets us run queries through the SPI manager,
|
||||||
* and the PushActiveSnapshot() call creates an "active" snapshot which
|
* and the PushActiveSnapshot() call creates an "active" snapshot
|
||||||
* is necessary for queries to have MVCC data to work on.
|
* which is necessary for queries to have MVCC data to work on.
|
||||||
*
|
*
|
||||||
* The pgstat_report_activity() call makes our activity visible through
|
* The pgstat_report_activity() call makes our activity visible
|
||||||
* the pgstat views.
|
* through the pgstat views.
|
||||||
*/
|
*/
|
||||||
SetCurrentStatementStartTimestamp();
|
SetCurrentStatementStartTimestamp();
|
||||||
StartTransactionCommand();
|
StartTransactionCommand();
|
||||||
@ -258,12 +258,12 @@ worker_spi_main(void *main_arg)
|
|||||||
|
|
||||||
if (SPI_processed > 0)
|
if (SPI_processed > 0)
|
||||||
{
|
{
|
||||||
bool isnull;
|
bool isnull;
|
||||||
int32 val;
|
int32 val;
|
||||||
|
|
||||||
val = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[0],
|
val = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[0],
|
||||||
SPI_tuptable->tupdesc,
|
SPI_tuptable->tupdesc,
|
||||||
1, &isnull));
|
1, &isnull));
|
||||||
if (!isnull)
|
if (!isnull)
|
||||||
elog(LOG, "%s: count in %s.%s is now %d",
|
elog(LOG, "%s: count in %s.%s is now %d",
|
||||||
MyBgworkerEntry->bgw_name,
|
MyBgworkerEntry->bgw_name,
|
||||||
@ -291,36 +291,36 @@ worker_spi_main(void *main_arg)
|
|||||||
void
|
void
|
||||||
_PG_init(void)
|
_PG_init(void)
|
||||||
{
|
{
|
||||||
BackgroundWorker worker;
|
BackgroundWorker worker;
|
||||||
worktable *table;
|
worktable *table;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
char name[20];
|
char name[20];
|
||||||
|
|
||||||
/* get the configuration */
|
/* get the configuration */
|
||||||
DefineCustomIntVariable("worker_spi.naptime",
|
DefineCustomIntVariable("worker_spi.naptime",
|
||||||
"Duration between each check (in seconds).",
|
"Duration between each check (in seconds).",
|
||||||
NULL,
|
NULL,
|
||||||
&worker_spi_naptime,
|
&worker_spi_naptime,
|
||||||
10,
|
10,
|
||||||
1,
|
1,
|
||||||
INT_MAX,
|
INT_MAX,
|
||||||
PGC_SIGHUP,
|
PGC_SIGHUP,
|
||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL);
|
NULL);
|
||||||
DefineCustomIntVariable("worker_spi.total_workers",
|
DefineCustomIntVariable("worker_spi.total_workers",
|
||||||
"Number of workers.",
|
"Number of workers.",
|
||||||
NULL,
|
NULL,
|
||||||
&worker_spi_total_workers,
|
&worker_spi_total_workers,
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
100,
|
100,
|
||||||
PGC_POSTMASTER,
|
PGC_POSTMASTER,
|
||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
/* set up common data for all our workers */
|
/* set up common data for all our workers */
|
||||||
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
|
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
|
||||||
|
@ -173,7 +173,6 @@ void
|
|||||||
ginFindParents(GinBtree btree, GinBtreeStack *stack,
|
ginFindParents(GinBtree btree, GinBtreeStack *stack,
|
||||||
BlockNumber rootBlkno)
|
BlockNumber rootBlkno)
|
||||||
{
|
{
|
||||||
|
|
||||||
Page page;
|
Page page;
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
BlockNumber blkno,
|
BlockNumber blkno,
|
||||||
|
@ -610,9 +610,9 @@ gistProcessItup(GISTBuildState *buildstate, IndexTuple itup,
|
|||||||
newtup = gistgetadjusted(indexrel, idxtuple, itup, giststate);
|
newtup = gistgetadjusted(indexrel, idxtuple, itup, giststate);
|
||||||
if (newtup)
|
if (newtup)
|
||||||
{
|
{
|
||||||
blkno = gistbufferinginserttuples(buildstate, buffer, level,
|
blkno = gistbufferinginserttuples(buildstate, buffer, level,
|
||||||
&newtup, 1, childoffnum,
|
&newtup, 1, childoffnum,
|
||||||
InvalidBlockNumber, InvalidOffsetNumber);
|
InvalidBlockNumber, InvalidOffsetNumber);
|
||||||
/* gistbufferinginserttuples() released the buffer */
|
/* gistbufferinginserttuples() released the buffer */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -680,7 +680,7 @@ gistbufferinginserttuples(GISTBuildState *buildstate, Buffer buffer, int level,
|
|||||||
GISTBuildBuffers *gfbb = buildstate->gfbb;
|
GISTBuildBuffers *gfbb = buildstate->gfbb;
|
||||||
List *splitinfo;
|
List *splitinfo;
|
||||||
bool is_split;
|
bool is_split;
|
||||||
BlockNumber placed_to_blk = InvalidBlockNumber;
|
BlockNumber placed_to_blk = InvalidBlockNumber;
|
||||||
|
|
||||||
is_split = gistplacetopage(buildstate->indexrel,
|
is_split = gistplacetopage(buildstate->indexrel,
|
||||||
buildstate->freespace,
|
buildstate->freespace,
|
||||||
|
@ -364,8 +364,9 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
|
|||||||
item->blkno = ItemPointerGetBlockNumber(&it->t_tid);
|
item->blkno = ItemPointerGetBlockNumber(&it->t_tid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* LSN of current page is lsn of parent page for child. We only
|
* LSN of current page is lsn of parent page for child. We
|
||||||
* have a shared lock, so we need to get the LSN atomically.
|
* only have a shared lock, so we need to get the LSN
|
||||||
|
* atomically.
|
||||||
*/
|
*/
|
||||||
item->data.parentlsn = BufferGetLSNAtomic(buffer);
|
item->data.parentlsn = BufferGetLSNAtomic(buffer);
|
||||||
}
|
}
|
||||||
|
@ -414,7 +414,7 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
|
|||||||
* some inserts to go to other equally-good subtrees.
|
* some inserts to go to other equally-good subtrees.
|
||||||
*
|
*
|
||||||
* keep_current_best is -1 if we haven't yet had to make a random choice
|
* keep_current_best is -1 if we haven't yet had to make a random choice
|
||||||
* whether to keep the current best tuple. If we have done so, and
|
* whether to keep the current best tuple. If we have done so, and
|
||||||
* decided to keep it, keep_current_best is 1; if we've decided to
|
* decided to keep it, keep_current_best is 1; if we've decided to
|
||||||
* replace, keep_current_best is 0. (This state will be reset to -1 as
|
* replace, keep_current_best is 0. (This state will be reset to -1 as
|
||||||
* soon as we've made the replacement, but sometimes we make the choice in
|
* soon as we've made the replacement, but sometimes we make the choice in
|
||||||
@ -810,8 +810,8 @@ gistGetFakeLSN(Relation rel)
|
|||||||
if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
|
if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Temporary relations are only accessible in our session, so a
|
* Temporary relations are only accessible in our session, so a simple
|
||||||
* simple backend-local counter will do.
|
* backend-local counter will do.
|
||||||
*/
|
*/
|
||||||
return counter++;
|
return counter++;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ static MemoryContext opCtx; /* working memory for operations */
|
|||||||
* follow-right flag, because that change is not included in the full-page
|
* follow-right flag, because that change is not included in the full-page
|
||||||
* image. To be sure that the intermediate state with the wrong flag value is
|
* image. To be sure that the intermediate state with the wrong flag value is
|
||||||
* not visible to concurrent Hot Standby queries, this function handles
|
* not visible to concurrent Hot Standby queries, this function handles
|
||||||
* restoring the full-page image as well as updating the flag. (Note that
|
* restoring the full-page image as well as updating the flag. (Note that
|
||||||
* we never need to do anything else to the child page in the current WAL
|
* we never need to do anything else to the child page in the current WAL
|
||||||
* action.)
|
* action.)
|
||||||
*/
|
*/
|
||||||
@ -89,7 +89,7 @@ gistRedoPageUpdateRecord(XLogRecPtr lsn, XLogRecord *record)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* We need to acquire and hold lock on target page while updating the left
|
* We need to acquire and hold lock on target page while updating the left
|
||||||
* child page. If we have a full-page image of target page, getting the
|
* child page. If we have a full-page image of target page, getting the
|
||||||
* lock is a side-effect of restoring that image. Note that even if the
|
* lock is a side-effect of restoring that image. Note that even if the
|
||||||
* target page no longer exists, we'll still attempt to replay the change
|
* target page no longer exists, we'll still attempt to replay the change
|
||||||
* on the child page.
|
* on the child page.
|
||||||
|
@ -90,7 +90,7 @@ _hash_doinsert(Relation rel, IndexTuple itup)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If the previous iteration of this loop locked what is still the
|
* If the previous iteration of this loop locked what is still the
|
||||||
* correct target bucket, we are done. Otherwise, drop any old lock
|
* correct target bucket, we are done. Otherwise, drop any old lock
|
||||||
* and lock what now appears to be the correct bucket.
|
* and lock what now appears to be the correct bucket.
|
||||||
*/
|
*/
|
||||||
if (retry)
|
if (retry)
|
||||||
|
@ -210,7 +210,7 @@ _hash_first(IndexScanDesc scan, ScanDirection dir)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If the previous iteration of this loop locked what is still the
|
* If the previous iteration of this loop locked what is still the
|
||||||
* correct target bucket, we are done. Otherwise, drop any old lock
|
* correct target bucket, we are done. Otherwise, drop any old lock
|
||||||
* and lock what now appears to be the correct bucket.
|
* and lock what now appears to be the correct bucket.
|
||||||
*/
|
*/
|
||||||
if (retry)
|
if (retry)
|
||||||
|
@ -120,32 +120,34 @@ static bool ConditionalMultiXactIdWait(MultiXactId multi,
|
|||||||
static const struct
|
static const struct
|
||||||
{
|
{
|
||||||
LOCKMODE hwlock;
|
LOCKMODE hwlock;
|
||||||
MultiXactStatus lockstatus;
|
MultiXactStatus lockstatus;
|
||||||
MultiXactStatus updstatus;
|
MultiXactStatus updstatus;
|
||||||
}
|
}
|
||||||
tupleLockExtraInfo[MaxLockTupleMode + 1] =
|
|
||||||
|
tupleLockExtraInfo[MaxLockTupleMode + 1] =
|
||||||
{
|
{
|
||||||
{ /* LockTupleKeyShare */
|
{ /* LockTupleKeyShare */
|
||||||
AccessShareLock,
|
AccessShareLock,
|
||||||
MultiXactStatusForKeyShare,
|
MultiXactStatusForKeyShare,
|
||||||
-1 /* KeyShare does not allow updating tuples */
|
-1 /* KeyShare does not allow updating tuples */
|
||||||
},
|
},
|
||||||
{ /* LockTupleShare */
|
{ /* LockTupleShare */
|
||||||
RowShareLock,
|
RowShareLock,
|
||||||
MultiXactStatusForShare,
|
MultiXactStatusForShare,
|
||||||
-1 /* Share does not allow updating tuples */
|
-1 /* Share does not allow updating tuples */
|
||||||
},
|
},
|
||||||
{ /* LockTupleNoKeyExclusive */
|
{ /* LockTupleNoKeyExclusive */
|
||||||
ExclusiveLock,
|
ExclusiveLock,
|
||||||
MultiXactStatusForNoKeyUpdate,
|
MultiXactStatusForNoKeyUpdate,
|
||||||
MultiXactStatusNoKeyUpdate
|
MultiXactStatusNoKeyUpdate
|
||||||
},
|
},
|
||||||
{ /* LockTupleExclusive */
|
{ /* LockTupleExclusive */
|
||||||
AccessExclusiveLock,
|
AccessExclusiveLock,
|
||||||
MultiXactStatusForUpdate,
|
MultiXactStatusForUpdate,
|
||||||
MultiXactStatusUpdate
|
MultiXactStatusUpdate
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Get the LOCKMODE for a given MultiXactStatus */
|
/* Get the LOCKMODE for a given MultiXactStatus */
|
||||||
#define LOCKMODE_from_mxstatus(status) \
|
#define LOCKMODE_from_mxstatus(status) \
|
||||||
(tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
|
(tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
|
||||||
@ -168,12 +170,12 @@ tupleLockExtraInfo[MaxLockTupleMode + 1] =
|
|||||||
*/
|
*/
|
||||||
static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
|
static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
|
||||||
{
|
{
|
||||||
LockTupleKeyShare, /* ForKeyShare */
|
LockTupleKeyShare, /* ForKeyShare */
|
||||||
LockTupleShare, /* ForShare */
|
LockTupleShare, /* ForShare */
|
||||||
LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
|
LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
|
||||||
LockTupleExclusive, /* ForUpdate */
|
LockTupleExclusive, /* ForUpdate */
|
||||||
LockTupleNoKeyExclusive, /* NoKeyUpdate */
|
LockTupleNoKeyExclusive, /* NoKeyUpdate */
|
||||||
LockTupleExclusive /* Update */
|
LockTupleExclusive /* Update */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Get the LockTupleMode for a given MultiXactStatus */
|
/* Get the LockTupleMode for a given MultiXactStatus */
|
||||||
@ -365,10 +367,10 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
|
|||||||
* page. That's how index-only scans work fine in hot standby. A crucial
|
* page. That's how index-only scans work fine in hot standby. A crucial
|
||||||
* difference between index-only scans and heap scans is that the
|
* difference between index-only scans and heap scans is that the
|
||||||
* index-only scan completely relies on the visibility map where as heap
|
* index-only scan completely relies on the visibility map where as heap
|
||||||
* scan looks at the page-level PD_ALL_VISIBLE flag. We are not sure if the
|
* scan looks at the page-level PD_ALL_VISIBLE flag. We are not sure if
|
||||||
* page-level flag can be trusted in the same way, because it might get
|
* the page-level flag can be trusted in the same way, because it might
|
||||||
* propagated somehow without being explicitly WAL-logged, e.g. via a full
|
* get propagated somehow without being explicitly WAL-logged, e.g. via a
|
||||||
* page write. Until we can prove that beyond doubt, let's check each
|
* full page write. Until we can prove that beyond doubt, let's check each
|
||||||
* tuple for visibility the hard way.
|
* tuple for visibility the hard way.
|
||||||
*/
|
*/
|
||||||
all_visible = PageIsAllVisible(dp) && !snapshot->takenDuringRecovery;
|
all_visible = PageIsAllVisible(dp) && !snapshot->takenDuringRecovery;
|
||||||
@ -1880,7 +1882,7 @@ heap_get_latest_tid(Relation relation,
|
|||||||
* tuple. Check for XMIN match.
|
* tuple. Check for XMIN match.
|
||||||
*/
|
*/
|
||||||
if (TransactionIdIsValid(priorXmax) &&
|
if (TransactionIdIsValid(priorXmax) &&
|
||||||
!TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
|
!TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
|
||||||
{
|
{
|
||||||
UnlockReleaseBuffer(buffer);
|
UnlockReleaseBuffer(buffer);
|
||||||
break;
|
break;
|
||||||
@ -2488,7 +2490,7 @@ compute_infobits(uint16 infomask, uint16 infomask2)
|
|||||||
((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
|
((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
|
||||||
((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
|
((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
|
||||||
((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
|
((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
|
||||||
/* note we ignore HEAP_XMAX_SHR_LOCK here */
|
/* note we ignore HEAP_XMAX_SHR_LOCK here */
|
||||||
((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
|
((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
|
||||||
((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
|
((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
|
||||||
XLHL_KEYS_UPDATED : 0);
|
XLHL_KEYS_UPDATED : 0);
|
||||||
@ -2730,13 +2732,12 @@ l1:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this is the first possibly-multixact-able operation in the
|
* If this is the first possibly-multixact-able operation in the current
|
||||||
* current transaction, set my per-backend OldestMemberMXactId setting.
|
* transaction, set my per-backend OldestMemberMXactId setting. We can be
|
||||||
* We can be certain that the transaction will never become a member of
|
* certain that the transaction will never become a member of any older
|
||||||
* any older MultiXactIds than that. (We have to do this even if we
|
* MultiXactIds than that. (We have to do this even if we end up just
|
||||||
* end up just using our own TransactionId below, since some other
|
* using our own TransactionId below, since some other backend could
|
||||||
* backend could incorporate our XID into a MultiXact immediately
|
* incorporate our XID into a MultiXact immediately afterwards.)
|
||||||
* afterwards.)
|
|
||||||
*/
|
*/
|
||||||
MultiXactIdSetOldestMember();
|
MultiXactIdSetOldestMember();
|
||||||
|
|
||||||
@ -2846,7 +2847,7 @@ simple_heap_delete(Relation relation, ItemPointer tid)
|
|||||||
|
|
||||||
result = heap_delete(relation, tid,
|
result = heap_delete(relation, tid,
|
||||||
GetCurrentCommandId(true), InvalidSnapshot,
|
GetCurrentCommandId(true), InvalidSnapshot,
|
||||||
true /* wait for commit */,
|
true /* wait for commit */ ,
|
||||||
&hufd);
|
&hufd);
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
@ -2936,7 +2937,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
|
|||||||
bool checked_lockers;
|
bool checked_lockers;
|
||||||
bool locker_remains;
|
bool locker_remains;
|
||||||
TransactionId xmax_new_tuple,
|
TransactionId xmax_new_tuple,
|
||||||
xmax_old_tuple;
|
xmax_old_tuple;
|
||||||
uint16 infomask_old_tuple,
|
uint16 infomask_old_tuple,
|
||||||
infomask2_old_tuple,
|
infomask2_old_tuple,
|
||||||
infomask_new_tuple,
|
infomask_new_tuple,
|
||||||
@ -3006,13 +3007,13 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If we're not updating any "key" column, we can grab a weaker lock type.
|
* If we're not updating any "key" column, we can grab a weaker lock type.
|
||||||
* This allows for more concurrency when we are running simultaneously with
|
* This allows for more concurrency when we are running simultaneously
|
||||||
* foreign key checks.
|
* with foreign key checks.
|
||||||
*
|
*
|
||||||
* Note that if a column gets detoasted while executing the update, but the
|
* Note that if a column gets detoasted while executing the update, but
|
||||||
* value ends up being the same, this test will fail and we will use the
|
* the value ends up being the same, this test will fail and we will use
|
||||||
* stronger lock. This is acceptable; the important case to optimize is
|
* the stronger lock. This is acceptable; the important case to optimize
|
||||||
* updates that don't manipulate key columns, not those that
|
* is updates that don't manipulate key columns, not those that
|
||||||
* serendipitiously arrive at the same key values.
|
* serendipitiously arrive at the same key values.
|
||||||
*/
|
*/
|
||||||
HeapSatisfiesHOTandKeyUpdate(relation, hot_attrs, key_attrs,
|
HeapSatisfiesHOTandKeyUpdate(relation, hot_attrs, key_attrs,
|
||||||
@ -3026,12 +3027,12 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If this is the first possibly-multixact-able operation in the
|
* If this is the first possibly-multixact-able operation in the
|
||||||
* current transaction, set my per-backend OldestMemberMXactId setting.
|
* current transaction, set my per-backend OldestMemberMXactId
|
||||||
* We can be certain that the transaction will never become a member of
|
* setting. We can be certain that the transaction will never become a
|
||||||
* any older MultiXactIds than that. (We have to do this even if we
|
* member of any older MultiXactIds than that. (We have to do this
|
||||||
* end up just using our own TransactionId below, since some other
|
* even if we end up just using our own TransactionId below, since
|
||||||
* backend could incorporate our XID into a MultiXact immediately
|
* some other backend could incorporate our XID into a MultiXact
|
||||||
* afterwards.)
|
* immediately afterwards.)
|
||||||
*/
|
*/
|
||||||
MultiXactIdSetOldestMember();
|
MultiXactIdSetOldestMember();
|
||||||
}
|
}
|
||||||
@ -3064,7 +3065,7 @@ l2:
|
|||||||
}
|
}
|
||||||
else if (result == HeapTupleBeingUpdated && wait)
|
else if (result == HeapTupleBeingUpdated && wait)
|
||||||
{
|
{
|
||||||
TransactionId xwait;
|
TransactionId xwait;
|
||||||
uint16 infomask;
|
uint16 infomask;
|
||||||
bool can_continue = false;
|
bool can_continue = false;
|
||||||
|
|
||||||
@ -3073,13 +3074,14 @@ l2:
|
|||||||
/*
|
/*
|
||||||
* XXX note that we don't consider the "no wait" case here. This
|
* XXX note that we don't consider the "no wait" case here. This
|
||||||
* isn't a problem currently because no caller uses that case, but it
|
* isn't a problem currently because no caller uses that case, but it
|
||||||
* should be fixed if such a caller is introduced. It wasn't a problem
|
* should be fixed if such a caller is introduced. It wasn't a
|
||||||
* previously because this code would always wait, but now that some
|
* problem previously because this code would always wait, but now
|
||||||
* tuple locks do not conflict with one of the lock modes we use, it is
|
* that some tuple locks do not conflict with one of the lock modes we
|
||||||
* possible that this case is interesting to handle specially.
|
* use, it is possible that this case is interesting to handle
|
||||||
|
* specially.
|
||||||
*
|
*
|
||||||
* This may cause failures with third-party code that calls heap_update
|
* This may cause failures with third-party code that calls
|
||||||
* directly.
|
* heap_update directly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* must copy state data before unlocking buffer */
|
/* must copy state data before unlocking buffer */
|
||||||
@ -3109,15 +3111,15 @@ l2:
|
|||||||
* gone (or even not sleep at all in some cases); we need to preserve
|
* gone (or even not sleep at all in some cases); we need to preserve
|
||||||
* it as locker, unless it is gone completely.
|
* it as locker, unless it is gone completely.
|
||||||
*
|
*
|
||||||
* If it's not a multi, we need to check for sleeping conditions before
|
* If it's not a multi, we need to check for sleeping conditions
|
||||||
* actually going to sleep. If the update doesn't conflict with the
|
* before actually going to sleep. If the update doesn't conflict
|
||||||
* locks, we just continue without sleeping (but making sure it is
|
* with the locks, we just continue without sleeping (but making sure
|
||||||
* preserved).
|
* it is preserved).
|
||||||
*/
|
*/
|
||||||
if (infomask & HEAP_XMAX_IS_MULTI)
|
if (infomask & HEAP_XMAX_IS_MULTI)
|
||||||
{
|
{
|
||||||
TransactionId update_xact;
|
TransactionId update_xact;
|
||||||
int remain;
|
int remain;
|
||||||
|
|
||||||
/* wait for multixact */
|
/* wait for multixact */
|
||||||
MultiXactIdWait((MultiXactId) xwait, mxact_status, &remain,
|
MultiXactIdWait((MultiXactId) xwait, mxact_status, &remain,
|
||||||
@ -3135,18 +3137,18 @@ l2:
|
|||||||
goto l2;
|
goto l2;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note that the multixact may not be done by now. It could have
|
* Note that the multixact may not be done by now. It could have
|
||||||
* surviving members; our own xact or other subxacts of this
|
* surviving members; our own xact or other subxacts of this
|
||||||
* backend, and also any other concurrent transaction that locked
|
* backend, and also any other concurrent transaction that locked
|
||||||
* the tuple with KeyShare if we only got TupleLockUpdate. If this
|
* the tuple with KeyShare if we only got TupleLockUpdate. If
|
||||||
* is the case, we have to be careful to mark the updated tuple
|
* this is the case, we have to be careful to mark the updated
|
||||||
* with the surviving members in Xmax.
|
* tuple with the surviving members in Xmax.
|
||||||
*
|
*
|
||||||
* Note that there could have been another update in the MultiXact.
|
* Note that there could have been another update in the
|
||||||
* In that case, we need to check whether it committed or aborted.
|
* MultiXact. In that case, we need to check whether it committed
|
||||||
* If it aborted we are safe to update it again; otherwise there is
|
* or aborted. If it aborted we are safe to update it again;
|
||||||
* an update conflict, and we have to return HeapTupleUpdated
|
* otherwise there is an update conflict, and we have to return
|
||||||
* below.
|
* HeapTupleUpdated below.
|
||||||
*
|
*
|
||||||
* In the LockTupleExclusive case, we still need to preserve the
|
* In the LockTupleExclusive case, we still need to preserve the
|
||||||
* surviving members: those would include the tuple locks we had
|
* surviving members: those would include the tuple locks we had
|
||||||
@ -3167,21 +3169,21 @@ l2:
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If it's just a key-share locker, and we're not changing the
|
* If it's just a key-share locker, and we're not changing the key
|
||||||
* key columns, we don't need to wait for it to end; but we
|
* columns, we don't need to wait for it to end; but we need to
|
||||||
* need to preserve it as locker.
|
* preserve it as locker.
|
||||||
*/
|
*/
|
||||||
if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) && key_intact)
|
if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) && key_intact)
|
||||||
{
|
{
|
||||||
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
|
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* recheck the locker; if someone else changed the tuple while we
|
* recheck the locker; if someone else changed the tuple while
|
||||||
* weren't looking, start over.
|
* we weren't looking, start over.
|
||||||
*/
|
*/
|
||||||
if ((oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
|
if ((oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
|
||||||
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(oldtup.t_data),
|
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(oldtup.t_data),
|
||||||
xwait))
|
xwait))
|
||||||
goto l2;
|
goto l2;
|
||||||
|
|
||||||
can_continue = true;
|
can_continue = true;
|
||||||
@ -3194,13 +3196,13 @@ l2:
|
|||||||
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
|
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* xwait is done, but if xwait had just locked the tuple then some
|
* xwait is done, but if xwait had just locked the tuple then
|
||||||
* other xact could update this tuple before we get to this point.
|
* some other xact could update this tuple before we get to
|
||||||
* Check for xmax change, and start over if so.
|
* this point. Check for xmax change, and start over if so.
|
||||||
*/
|
*/
|
||||||
if ((oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
|
if ((oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
|
||||||
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(oldtup.t_data),
|
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(oldtup.t_data),
|
||||||
xwait))
|
xwait))
|
||||||
goto l2;
|
goto l2;
|
||||||
|
|
||||||
/* Otherwise check if it committed or aborted */
|
/* Otherwise check if it committed or aborted */
|
||||||
@ -3247,8 +3249,8 @@ l2:
|
|||||||
* visible while we were busy locking the buffer, or during some
|
* visible while we were busy locking the buffer, or during some
|
||||||
* subsequent window during which we had it unlocked, we'll have to unlock
|
* subsequent window during which we had it unlocked, we'll have to unlock
|
||||||
* and re-lock, to avoid holding the buffer lock across an I/O. That's a
|
* and re-lock, to avoid holding the buffer lock across an I/O. That's a
|
||||||
* bit unfortunate, especially since we'll now have to recheck whether
|
* bit unfortunate, especially since we'll now have to recheck whether the
|
||||||
* the tuple has been locked or updated under us, but hopefully it won't
|
* tuple has been locked or updated under us, but hopefully it won't
|
||||||
* happen very often.
|
* happen very often.
|
||||||
*/
|
*/
|
||||||
if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
|
if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
|
||||||
@ -3656,9 +3658,9 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Extract the corresponding values. XXX this is pretty inefficient if
|
* Extract the corresponding values. XXX this is pretty inefficient if
|
||||||
* there are many indexed columns. Should HeapSatisfiesHOTandKeyUpdate do a
|
* there are many indexed columns. Should HeapSatisfiesHOTandKeyUpdate do
|
||||||
* single heap_deform_tuple call on each tuple, instead? But that doesn't
|
* a single heap_deform_tuple call on each tuple, instead? But that
|
||||||
* work for system columns ...
|
* doesn't work for system columns ...
|
||||||
*/
|
*/
|
||||||
value1 = heap_getattr(tup1, attrnum, tupdesc, &isnull1);
|
value1 = heap_getattr(tup1, attrnum, tupdesc, &isnull1);
|
||||||
value2 = heap_getattr(tup2, attrnum, tupdesc, &isnull2);
|
value2 = heap_getattr(tup2, attrnum, tupdesc, &isnull2);
|
||||||
@ -3720,12 +3722,12 @@ HeapSatisfiesHOTandKeyUpdate(Relation relation,
|
|||||||
bool *satisfies_hot, bool *satisfies_key,
|
bool *satisfies_hot, bool *satisfies_key,
|
||||||
HeapTuple oldtup, HeapTuple newtup)
|
HeapTuple oldtup, HeapTuple newtup)
|
||||||
{
|
{
|
||||||
int next_hot_attnum;
|
int next_hot_attnum;
|
||||||
int next_key_attnum;
|
int next_key_attnum;
|
||||||
bool hot_result = true;
|
bool hot_result = true;
|
||||||
bool key_result = true;
|
bool key_result = true;
|
||||||
bool key_done = false;
|
bool key_done = false;
|
||||||
bool hot_done = false;
|
bool hot_done = false;
|
||||||
|
|
||||||
next_hot_attnum = bms_first_member(hot_attrs);
|
next_hot_attnum = bms_first_member(hot_attrs);
|
||||||
if (next_hot_attnum == -1)
|
if (next_hot_attnum == -1)
|
||||||
@ -3743,8 +3745,8 @@ HeapSatisfiesHOTandKeyUpdate(Relation relation,
|
|||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
int check_now;
|
int check_now;
|
||||||
bool changed;
|
bool changed;
|
||||||
|
|
||||||
/* both bitmapsets are now empty */
|
/* both bitmapsets are now empty */
|
||||||
if (key_done && hot_done)
|
if (key_done && hot_done)
|
||||||
@ -3813,7 +3815,7 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
|
|||||||
|
|
||||||
result = heap_update(relation, otid, tup,
|
result = heap_update(relation, otid, tup,
|
||||||
GetCurrentCommandId(true), InvalidSnapshot,
|
GetCurrentCommandId(true), InvalidSnapshot,
|
||||||
true /* wait for commit */,
|
true /* wait for commit */ ,
|
||||||
&hufd, &lockmode);
|
&hufd, &lockmode);
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
@ -3843,7 +3845,7 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
|
|||||||
static MultiXactStatus
|
static MultiXactStatus
|
||||||
get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
|
get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
|
||||||
{
|
{
|
||||||
MultiXactStatus retval;
|
MultiXactStatus retval;
|
||||||
|
|
||||||
if (is_update)
|
if (is_update)
|
||||||
retval = tupleLockExtraInfo[mode].updstatus;
|
retval = tupleLockExtraInfo[mode].updstatus;
|
||||||
@ -3933,7 +3935,7 @@ l3:
|
|||||||
uint16 infomask;
|
uint16 infomask;
|
||||||
uint16 infomask2;
|
uint16 infomask2;
|
||||||
bool require_sleep;
|
bool require_sleep;
|
||||||
ItemPointerData t_ctid;
|
ItemPointerData t_ctid;
|
||||||
|
|
||||||
/* must copy state data before unlocking buffer */
|
/* must copy state data before unlocking buffer */
|
||||||
xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
|
xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
|
||||||
@ -3944,22 +3946,22 @@ l3:
|
|||||||
LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
|
LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If any subtransaction of the current top transaction already holds a
|
* If any subtransaction of the current top transaction already holds
|
||||||
* lock as strong or stronger than what we're requesting, we
|
* a lock as strong or stronger than what we're requesting, we
|
||||||
* effectively hold the desired lock already. We *must* succeed
|
* effectively hold the desired lock already. We *must* succeed
|
||||||
* without trying to take the tuple lock, else we will deadlock against
|
* without trying to take the tuple lock, else we will deadlock
|
||||||
* anyone wanting to acquire a stronger lock.
|
* against anyone wanting to acquire a stronger lock.
|
||||||
*/
|
*/
|
||||||
if (infomask & HEAP_XMAX_IS_MULTI)
|
if (infomask & HEAP_XMAX_IS_MULTI)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int nmembers;
|
int nmembers;
|
||||||
MultiXactMember *members;
|
MultiXactMember *members;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't need to allow old multixacts here; if that had been the
|
* We don't need to allow old multixacts here; if that had been
|
||||||
* case, HeapTupleSatisfiesUpdate would have returned MayBeUpdated
|
* the case, HeapTupleSatisfiesUpdate would have returned
|
||||||
* and we wouldn't be here.
|
* MayBeUpdated and we wouldn't be here.
|
||||||
*/
|
*/
|
||||||
nmembers = GetMultiXactIdMembers(xwait, &members, false);
|
nmembers = GetMultiXactIdMembers(xwait, &members, false);
|
||||||
|
|
||||||
@ -3967,7 +3969,7 @@ l3:
|
|||||||
{
|
{
|
||||||
if (TransactionIdIsCurrentTransactionId(members[i].xid))
|
if (TransactionIdIsCurrentTransactionId(members[i].xid))
|
||||||
{
|
{
|
||||||
LockTupleMode membermode;
|
LockTupleMode membermode;
|
||||||
|
|
||||||
membermode = TUPLOCK_from_mxstatus(members[i].status);
|
membermode = TUPLOCK_from_mxstatus(members[i].status);
|
||||||
|
|
||||||
@ -4001,8 +4003,8 @@ l3:
|
|||||||
if (!ConditionalLockTupleTuplock(relation, tid, mode))
|
if (!ConditionalLockTupleTuplock(relation, tid, mode))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
|
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
|
||||||
errmsg("could not obtain lock on row in relation \"%s\"",
|
errmsg("could not obtain lock on row in relation \"%s\"",
|
||||||
RelationGetRelationName(relation))));
|
RelationGetRelationName(relation))));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
LockTupleTuplock(relation, tid, mode);
|
LockTupleTuplock(relation, tid, mode);
|
||||||
@ -4023,34 +4025,34 @@ l3:
|
|||||||
* continue if the key hasn't been modified.
|
* continue if the key hasn't been modified.
|
||||||
*
|
*
|
||||||
* However, if there are updates, we need to walk the update chain
|
* However, if there are updates, we need to walk the update chain
|
||||||
* to mark future versions of the row as locked, too. That way, if
|
* to mark future versions of the row as locked, too. That way,
|
||||||
* somebody deletes that future version, we're protected against
|
* if somebody deletes that future version, we're protected
|
||||||
* the key going away. This locking of future versions could block
|
* against the key going away. This locking of future versions
|
||||||
* momentarily, if a concurrent transaction is deleting a key; or
|
* could block momentarily, if a concurrent transaction is
|
||||||
* it could return a value to the effect that the transaction
|
* deleting a key; or it could return a value to the effect that
|
||||||
* deleting the key has already committed. So we do this before
|
* the transaction deleting the key has already committed. So we
|
||||||
* re-locking the buffer; otherwise this would be prone to
|
* do this before re-locking the buffer; otherwise this would be
|
||||||
* deadlocks.
|
* prone to deadlocks.
|
||||||
*
|
*
|
||||||
* Note that the TID we're locking was grabbed before we unlocked
|
* Note that the TID we're locking was grabbed before we unlocked
|
||||||
* the buffer. For it to change while we're not looking, the other
|
* the buffer. For it to change while we're not looking, the
|
||||||
* properties we're testing for below after re-locking the buffer
|
* other properties we're testing for below after re-locking the
|
||||||
* would also change, in which case we would restart this loop
|
* buffer would also change, in which case we would restart this
|
||||||
* above.
|
* loop above.
|
||||||
*/
|
*/
|
||||||
if (!(infomask2 & HEAP_KEYS_UPDATED))
|
if (!(infomask2 & HEAP_KEYS_UPDATED))
|
||||||
{
|
{
|
||||||
bool updated;
|
bool updated;
|
||||||
|
|
||||||
updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
|
updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there are updates, follow the update chain; bail out
|
* If there are updates, follow the update chain; bail out if
|
||||||
* if that cannot be done.
|
* that cannot be done.
|
||||||
*/
|
*/
|
||||||
if (follow_updates && updated)
|
if (follow_updates && updated)
|
||||||
{
|
{
|
||||||
HTSU_Result res;
|
HTSU_Result res;
|
||||||
|
|
||||||
res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
|
res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
|
||||||
GetCurrentTransactionId(),
|
GetCurrentTransactionId(),
|
||||||
@ -4069,8 +4071,9 @@ l3:
|
|||||||
/*
|
/*
|
||||||
* Make sure it's still an appropriate lock, else start over.
|
* Make sure it's still an appropriate lock, else start over.
|
||||||
* Also, if it wasn't updated before we released the lock, but
|
* Also, if it wasn't updated before we released the lock, but
|
||||||
* is updated now, we start over too; the reason is that we now
|
* is updated now, we start over too; the reason is that we
|
||||||
* need to follow the update chain to lock the new versions.
|
* now need to follow the update chain to lock the new
|
||||||
|
* versions.
|
||||||
*/
|
*/
|
||||||
if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
|
if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
|
||||||
((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
|
((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
|
||||||
@ -4114,20 +4117,20 @@ l3:
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If we're requesting NoKeyExclusive, we might also be able to
|
* If we're requesting NoKeyExclusive, we might also be able to
|
||||||
* avoid sleeping; just ensure that there's no other lock type than
|
* avoid sleeping; just ensure that there's no other lock type
|
||||||
* KeyShare. Note that this is a bit more involved than just
|
* than KeyShare. Note that this is a bit more involved than just
|
||||||
* checking hint bits -- we need to expand the multixact to figure
|
* checking hint bits -- we need to expand the multixact to figure
|
||||||
* out lock modes for each one (unless there was only one such
|
* out lock modes for each one (unless there was only one such
|
||||||
* locker).
|
* locker).
|
||||||
*/
|
*/
|
||||||
if (infomask & HEAP_XMAX_IS_MULTI)
|
if (infomask & HEAP_XMAX_IS_MULTI)
|
||||||
{
|
{
|
||||||
int nmembers;
|
int nmembers;
|
||||||
MultiXactMember *members;
|
MultiXactMember *members;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't need to allow old multixacts here; if that had been
|
* We don't need to allow old multixacts here; if that had
|
||||||
* the case, HeapTupleSatisfiesUpdate would have returned
|
* been the case, HeapTupleSatisfiesUpdate would have returned
|
||||||
* MayBeUpdated and we wouldn't be here.
|
* MayBeUpdated and we wouldn't be here.
|
||||||
*/
|
*/
|
||||||
nmembers = GetMultiXactIdMembers(xwait, &members, false);
|
nmembers = GetMultiXactIdMembers(xwait, &members, false);
|
||||||
@ -4135,15 +4138,15 @@ l3:
|
|||||||
if (nmembers <= 0)
|
if (nmembers <= 0)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* No need to keep the previous xmax here. This is unlikely
|
* No need to keep the previous xmax here. This is
|
||||||
* to happen.
|
* unlikely to happen.
|
||||||
*/
|
*/
|
||||||
require_sleep = false;
|
require_sleep = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
bool allowed = true;
|
bool allowed = true;
|
||||||
|
|
||||||
for (i = 0; i < nmembers; i++)
|
for (i = 0; i < nmembers; i++)
|
||||||
{
|
{
|
||||||
@ -4180,8 +4183,8 @@ l3:
|
|||||||
|
|
||||||
/* if the xmax changed in the meantime, start over */
|
/* if the xmax changed in the meantime, start over */
|
||||||
if ((tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
|
if ((tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
|
||||||
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
|
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
|
||||||
xwait))
|
xwait))
|
||||||
goto l3;
|
goto l3;
|
||||||
/* otherwise, we're good */
|
/* otherwise, we're good */
|
||||||
require_sleep = false;
|
require_sleep = false;
|
||||||
@ -4221,7 +4224,7 @@ l3:
|
|||||||
if (follow_updates &&
|
if (follow_updates &&
|
||||||
!HEAP_XMAX_IS_LOCKED_ONLY(infomask))
|
!HEAP_XMAX_IS_LOCKED_ONLY(infomask))
|
||||||
{
|
{
|
||||||
HTSU_Result res;
|
HTSU_Result res;
|
||||||
|
|
||||||
res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
|
res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
|
||||||
GetCurrentTransactionId(),
|
GetCurrentTransactionId(),
|
||||||
@ -4243,15 +4246,15 @@ l3:
|
|||||||
* for xmax change, and start over if so.
|
* for xmax change, and start over if so.
|
||||||
*/
|
*/
|
||||||
if (!(tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
|
if (!(tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
|
||||||
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
|
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
|
||||||
xwait))
|
xwait))
|
||||||
goto l3;
|
goto l3;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Of course, the multixact might not be done here: if we're
|
* Of course, the multixact might not be done here: if we're
|
||||||
* requesting a light lock mode, other transactions with light
|
* requesting a light lock mode, other transactions with light
|
||||||
* locks could still be alive, as well as locks owned by our
|
* locks could still be alive, as well as locks owned by our
|
||||||
* own xact or other subxacts of this backend. We need to
|
* own xact or other subxacts of this backend. We need to
|
||||||
* preserve the surviving MultiXact members. Note that it
|
* preserve the surviving MultiXact members. Note that it
|
||||||
* isn't absolutely necessary in the latter case, but doing so
|
* isn't absolutely necessary in the latter case, but doing so
|
||||||
* is simpler.
|
* is simpler.
|
||||||
@ -4275,7 +4278,7 @@ l3:
|
|||||||
if (follow_updates &&
|
if (follow_updates &&
|
||||||
!HEAP_XMAX_IS_LOCKED_ONLY(infomask))
|
!HEAP_XMAX_IS_LOCKED_ONLY(infomask))
|
||||||
{
|
{
|
||||||
HTSU_Result res;
|
HTSU_Result res;
|
||||||
|
|
||||||
res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
|
res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
|
||||||
GetCurrentTransactionId(),
|
GetCurrentTransactionId(),
|
||||||
@ -4294,15 +4297,15 @@ l3:
|
|||||||
/*
|
/*
|
||||||
* xwait is done, but if xwait had just locked the tuple then
|
* xwait is done, but if xwait had just locked the tuple then
|
||||||
* some other xact could update this tuple before we get to
|
* some other xact could update this tuple before we get to
|
||||||
* this point. Check for xmax change, and start over if so.
|
* this point. Check for xmax change, and start over if so.
|
||||||
*/
|
*/
|
||||||
if ((tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
|
if ((tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
|
||||||
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
|
!TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
|
||||||
xwait))
|
xwait))
|
||||||
goto l3;
|
goto l3;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Otherwise check if it committed or aborted. Note we cannot
|
* Otherwise check if it committed or aborted. Note we cannot
|
||||||
* be here if the tuple was only locked by somebody who didn't
|
* be here if the tuple was only locked by somebody who didn't
|
||||||
* conflict with us; that should have been handled above. So
|
* conflict with us; that should have been handled above. So
|
||||||
* that transaction must necessarily be gone by now.
|
* that transaction must necessarily be gone by now.
|
||||||
@ -4355,8 +4358,8 @@ failed:
|
|||||||
* for cases where it is a plain TransactionId.
|
* for cases where it is a plain TransactionId.
|
||||||
*
|
*
|
||||||
* Note in particular that this covers the case where we already hold
|
* Note in particular that this covers the case where we already hold
|
||||||
* exclusive lock on the tuple and the caller only wants key share or share
|
* exclusive lock on the tuple and the caller only wants key share or
|
||||||
* lock. It would certainly not do to give up the exclusive lock.
|
* share lock. It would certainly not do to give up the exclusive lock.
|
||||||
*/
|
*/
|
||||||
if (!(old_infomask & (HEAP_XMAX_INVALID |
|
if (!(old_infomask & (HEAP_XMAX_INVALID |
|
||||||
HEAP_XMAX_COMMITTED |
|
HEAP_XMAX_COMMITTED |
|
||||||
@ -4379,13 +4382,12 @@ failed:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this is the first possibly-multixact-able operation in the
|
* If this is the first possibly-multixact-able operation in the current
|
||||||
* current transaction, set my per-backend OldestMemberMXactId setting.
|
* transaction, set my per-backend OldestMemberMXactId setting. We can be
|
||||||
* We can be certain that the transaction will never become a member of
|
* certain that the transaction will never become a member of any older
|
||||||
* any older MultiXactIds than that. (We have to do this even if we
|
* MultiXactIds than that. (We have to do this even if we end up just
|
||||||
* end up just using our own TransactionId below, since some other
|
* using our own TransactionId below, since some other backend could
|
||||||
* backend could incorporate our XID into a MultiXact immediately
|
* incorporate our XID into a MultiXact immediately afterwards.)
|
||||||
* afterwards.)
|
|
||||||
*/
|
*/
|
||||||
MultiXactIdSetOldestMember();
|
MultiXactIdSetOldestMember();
|
||||||
|
|
||||||
@ -4419,11 +4421,11 @@ failed:
|
|||||||
HeapTupleHeaderSetXmax(tuple->t_data, xid);
|
HeapTupleHeaderSetXmax(tuple->t_data, xid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure there is no forward chain link in t_ctid. Note that in the
|
* Make sure there is no forward chain link in t_ctid. Note that in the
|
||||||
* cases where the tuple has been updated, we must not overwrite t_ctid,
|
* cases where the tuple has been updated, we must not overwrite t_ctid,
|
||||||
* because it was set by the updater. Moreover, if the tuple has been
|
* because it was set by the updater. Moreover, if the tuple has been
|
||||||
* updated, we need to follow the update chain to lock the new versions
|
* updated, we need to follow the update chain to lock the new versions of
|
||||||
* of the tuple as well.
|
* the tuple as well.
|
||||||
*/
|
*/
|
||||||
if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
|
if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
|
||||||
tuple->t_data->t_ctid = *tid;
|
tuple->t_data->t_ctid = *tid;
|
||||||
@ -4514,9 +4516,9 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
|
|||||||
TransactionId *result_xmax, uint16 *result_infomask,
|
TransactionId *result_xmax, uint16 *result_infomask,
|
||||||
uint16 *result_infomask2)
|
uint16 *result_infomask2)
|
||||||
{
|
{
|
||||||
TransactionId new_xmax;
|
TransactionId new_xmax;
|
||||||
uint16 new_infomask,
|
uint16 new_infomask,
|
||||||
new_infomask2;
|
new_infomask2;
|
||||||
|
|
||||||
l5:
|
l5:
|
||||||
new_infomask = 0;
|
new_infomask = 0;
|
||||||
@ -4562,11 +4564,11 @@ l5:
|
|||||||
}
|
}
|
||||||
else if (old_infomask & HEAP_XMAX_IS_MULTI)
|
else if (old_infomask & HEAP_XMAX_IS_MULTI)
|
||||||
{
|
{
|
||||||
MultiXactStatus new_status;
|
MultiXactStatus new_status;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Currently we don't allow XMAX_COMMITTED to be set for multis,
|
* Currently we don't allow XMAX_COMMITTED to be set for multis, so
|
||||||
* so cross-check.
|
* cross-check.
|
||||||
*/
|
*/
|
||||||
Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
|
Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
|
||||||
|
|
||||||
@ -4587,10 +4589,11 @@ l5:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If the XMAX is already a MultiXactId, then we need to expand it to
|
* If the XMAX is already a MultiXactId, then we need to expand it to
|
||||||
* include add_to_xmax; but if all the members were lockers and are all
|
* include add_to_xmax; but if all the members were lockers and are
|
||||||
* gone, we can do away with the IS_MULTI bit and just set add_to_xmax
|
* all gone, we can do away with the IS_MULTI bit and just set
|
||||||
* as the only locker/updater. If all lockers are gone and we have an
|
* add_to_xmax as the only locker/updater. If all lockers are gone
|
||||||
* updater that aborted, we can also do without a multi.
|
* and we have an updater that aborted, we can also do without a
|
||||||
|
* multi.
|
||||||
*
|
*
|
||||||
* The cost of doing GetMultiXactIdMembers would be paid by
|
* The cost of doing GetMultiXactIdMembers would be paid by
|
||||||
* MultiXactIdExpand if we weren't to do this, so this check is not
|
* MultiXactIdExpand if we weren't to do this, so this check is not
|
||||||
@ -4624,8 +4627,8 @@ l5:
|
|||||||
* It's a committed update, so we need to preserve him as updater of
|
* It's a committed update, so we need to preserve him as updater of
|
||||||
* the tuple.
|
* the tuple.
|
||||||
*/
|
*/
|
||||||
MultiXactStatus status;
|
MultiXactStatus status;
|
||||||
MultiXactStatus new_status;
|
MultiXactStatus new_status;
|
||||||
|
|
||||||
if (old_infomask2 & HEAP_KEYS_UPDATED)
|
if (old_infomask2 & HEAP_KEYS_UPDATED)
|
||||||
status = MultiXactStatusUpdate;
|
status = MultiXactStatusUpdate;
|
||||||
@ -4633,6 +4636,7 @@ l5:
|
|||||||
status = MultiXactStatusNoKeyUpdate;
|
status = MultiXactStatusNoKeyUpdate;
|
||||||
|
|
||||||
new_status = get_mxact_status_for_lock(mode, is_update);
|
new_status = get_mxact_status_for_lock(mode, is_update);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* since it's not running, it's obviously impossible for the old
|
* since it's not running, it's obviously impossible for the old
|
||||||
* updater to be identical to the current one, so we need not check
|
* updater to be identical to the current one, so we need not check
|
||||||
@ -4648,8 +4652,8 @@ l5:
|
|||||||
* create a new MultiXactId that includes both the old locker or
|
* create a new MultiXactId that includes both the old locker or
|
||||||
* updater and our own TransactionId.
|
* updater and our own TransactionId.
|
||||||
*/
|
*/
|
||||||
MultiXactStatus status;
|
MultiXactStatus status;
|
||||||
MultiXactStatus new_status;
|
MultiXactStatus new_status;
|
||||||
|
|
||||||
if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
|
if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
|
||||||
{
|
{
|
||||||
@ -4668,8 +4672,8 @@ l5:
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* LOCK_ONLY can be present alone only when a page has been
|
* LOCK_ONLY can be present alone only when a page has been
|
||||||
* upgraded by pg_upgrade. But in that case,
|
* upgraded by pg_upgrade. But in that case,
|
||||||
* TransactionIdIsInProgress() should have returned false. We
|
* TransactionIdIsInProgress() should have returned false. We
|
||||||
* assume it's no longer locked in this case.
|
* assume it's no longer locked in this case.
|
||||||
*/
|
*/
|
||||||
elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
|
elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
|
||||||
@ -4696,8 +4700,8 @@ l5:
|
|||||||
*/
|
*/
|
||||||
if (xmax == add_to_xmax)
|
if (xmax == add_to_xmax)
|
||||||
{
|
{
|
||||||
LockTupleMode old_mode = TUPLOCK_from_mxstatus(status);
|
LockTupleMode old_mode = TUPLOCK_from_mxstatus(status);
|
||||||
bool old_isupd = ISUPDATE_from_mxstatus(status);
|
bool old_isupd = ISUPDATE_from_mxstatus(status);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can do this if the new LockTupleMode is higher or equal than
|
* We can do this if the new LockTupleMode is higher or equal than
|
||||||
@ -4728,8 +4732,8 @@ l5:
|
|||||||
* It's a committed update, so we gotta preserve him as updater of the
|
* It's a committed update, so we gotta preserve him as updater of the
|
||||||
* tuple.
|
* tuple.
|
||||||
*/
|
*/
|
||||||
MultiXactStatus status;
|
MultiXactStatus status;
|
||||||
MultiXactStatus new_status;
|
MultiXactStatus new_status;
|
||||||
|
|
||||||
if (old_infomask2 & HEAP_KEYS_UPDATED)
|
if (old_infomask2 & HEAP_KEYS_UPDATED)
|
||||||
status = MultiXactStatusUpdate;
|
status = MultiXactStatusUpdate;
|
||||||
@ -4737,6 +4741,7 @@ l5:
|
|||||||
status = MultiXactStatusNoKeyUpdate;
|
status = MultiXactStatusNoKeyUpdate;
|
||||||
|
|
||||||
new_status = get_mxact_status_for_lock(mode, is_update);
|
new_status = get_mxact_status_for_lock(mode, is_update);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* since it's not running, it's obviously impossible for the old
|
* since it's not running, it's obviously impossible for the old
|
||||||
* updater to be identical to the current one, so we need not check
|
* updater to be identical to the current one, so we need not check
|
||||||
@ -4774,14 +4779,14 @@ static HTSU_Result
|
|||||||
heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
|
heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
|
||||||
LockTupleMode mode)
|
LockTupleMode mode)
|
||||||
{
|
{
|
||||||
ItemPointerData tupid;
|
ItemPointerData tupid;
|
||||||
HeapTupleData mytup;
|
HeapTupleData mytup;
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
uint16 new_infomask,
|
uint16 new_infomask,
|
||||||
new_infomask2,
|
new_infomask2,
|
||||||
old_infomask;
|
old_infomask;
|
||||||
TransactionId xmax,
|
TransactionId xmax,
|
||||||
new_xmax;
|
new_xmax;
|
||||||
|
|
||||||
ItemPointerCopy(tid, &tupid);
|
ItemPointerCopy(tid, &tupid);
|
||||||
|
|
||||||
@ -4802,16 +4807,17 @@ l4:
|
|||||||
xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
|
xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this tuple is updated and the key has been modified (or deleted),
|
* If this tuple is updated and the key has been modified (or
|
||||||
* what we do depends on the status of the updating transaction: if
|
* deleted), what we do depends on the status of the updating
|
||||||
* it's live, we sleep until it finishes; if it has committed, we have
|
* transaction: if it's live, we sleep until it finishes; if it has
|
||||||
* to fail (i.e. return HeapTupleUpdated); if it aborted, we ignore it.
|
* committed, we have to fail (i.e. return HeapTupleUpdated); if it
|
||||||
* For updates that didn't touch the key, we can just plough ahead.
|
* aborted, we ignore it. For updates that didn't touch the key, we
|
||||||
|
* can just plough ahead.
|
||||||
*/
|
*/
|
||||||
if (!(old_infomask & HEAP_XMAX_INVALID) &&
|
if (!(old_infomask & HEAP_XMAX_INVALID) &&
|
||||||
(mytup.t_data->t_infomask2 & HEAP_KEYS_UPDATED))
|
(mytup.t_data->t_infomask2 & HEAP_KEYS_UPDATED))
|
||||||
{
|
{
|
||||||
TransactionId update_xid;
|
TransactionId update_xid;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: we *must* check TransactionIdIsInProgress before
|
* Note: we *must* check TransactionIdIsInProgress before
|
||||||
@ -4832,7 +4838,7 @@ l4:
|
|||||||
goto l4;
|
goto l4;
|
||||||
}
|
}
|
||||||
else if (TransactionIdDidAbort(update_xid))
|
else if (TransactionIdDidAbort(update_xid))
|
||||||
; /* okay to proceed */
|
; /* okay to proceed */
|
||||||
else if (TransactionIdDidCommit(update_xid))
|
else if (TransactionIdDidCommit(update_xid))
|
||||||
{
|
{
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
@ -4861,7 +4867,7 @@ l4:
|
|||||||
{
|
{
|
||||||
xl_heap_lock_updated xlrec;
|
xl_heap_lock_updated xlrec;
|
||||||
XLogRecPtr recptr;
|
XLogRecPtr recptr;
|
||||||
XLogRecData rdata[2];
|
XLogRecData rdata[2];
|
||||||
Page page = BufferGetPage(buf);
|
Page page = BufferGetPage(buf);
|
||||||
|
|
||||||
xlrec.target.node = rel->rd_node;
|
xlrec.target.node = rel->rd_node;
|
||||||
@ -4889,7 +4895,7 @@ l4:
|
|||||||
|
|
||||||
/* if we find the end of update chain, we're done. */
|
/* if we find the end of update chain, we're done. */
|
||||||
if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
|
if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
|
||||||
ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
|
ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
|
||||||
HeapTupleHeaderIsOnlyLocked(mytup.t_data))
|
HeapTupleHeaderIsOnlyLocked(mytup.t_data))
|
||||||
{
|
{
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
@ -4904,13 +4910,13 @@ l4:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* heap_lock_updated_tuple
|
* heap_lock_updated_tuple
|
||||||
* Follow update chain when locking an updated tuple, acquiring locks (row
|
* Follow update chain when locking an updated tuple, acquiring locks (row
|
||||||
* marks) on the updated versions.
|
* marks) on the updated versions.
|
||||||
*
|
*
|
||||||
* The initial tuple is assumed to be already locked.
|
* The initial tuple is assumed to be already locked.
|
||||||
*
|
*
|
||||||
* This function doesn't check visibility, it just inconditionally marks the
|
* This function doesn't check visibility, it just inconditionally marks the
|
||||||
* tuple(s) as locked. If any tuple in the updated chain is being deleted
|
* tuple(s) as locked. If any tuple in the updated chain is being deleted
|
||||||
* concurrently (or updated with the key being modified), sleep until the
|
* concurrently (or updated with the key being modified), sleep until the
|
||||||
* transaction doing it is finished.
|
* transaction doing it is finished.
|
||||||
*
|
*
|
||||||
@ -4932,12 +4938,12 @@ heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If this is the first possibly-multixact-able operation in the
|
* If this is the first possibly-multixact-able operation in the
|
||||||
* current transaction, set my per-backend OldestMemberMXactId setting.
|
* current transaction, set my per-backend OldestMemberMXactId
|
||||||
* We can be certain that the transaction will never become a member of
|
* setting. We can be certain that the transaction will never become a
|
||||||
* any older MultiXactIds than that. (We have to do this even if we
|
* member of any older MultiXactIds than that. (We have to do this
|
||||||
* end up just using our own TransactionId below, since some other
|
* even if we end up just using our own TransactionId below, since
|
||||||
* backend could incorporate our XID into a MultiXact immediately
|
* some other backend could incorporate our XID into a MultiXact
|
||||||
* afterwards.)
|
* immediately afterwards.)
|
||||||
*/
|
*/
|
||||||
MultiXactIdSetOldestMember();
|
MultiXactIdSetOldestMember();
|
||||||
|
|
||||||
@ -5117,9 +5123,9 @@ heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
|
|||||||
HeapTupleHeaderSetXmax(tuple, InvalidTransactionId);
|
HeapTupleHeaderSetXmax(tuple, InvalidTransactionId);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The tuple might be marked either XMAX_INVALID or XMAX_COMMITTED
|
* The tuple might be marked either XMAX_INVALID or XMAX_COMMITTED +
|
||||||
* + LOCKED. Normalize to INVALID just to be sure no one gets
|
* LOCKED. Normalize to INVALID just to be sure no one gets confused.
|
||||||
* confused. Also get rid of the HEAP_KEYS_UPDATED bit.
|
* Also get rid of the HEAP_KEYS_UPDATED bit.
|
||||||
*/
|
*/
|
||||||
tuple->t_infomask &= ~HEAP_XMAX_BITS;
|
tuple->t_infomask &= ~HEAP_XMAX_BITS;
|
||||||
tuple->t_infomask |= HEAP_XMAX_INVALID;
|
tuple->t_infomask |= HEAP_XMAX_INVALID;
|
||||||
@ -5172,13 +5178,13 @@ static void
|
|||||||
GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask,
|
GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask,
|
||||||
uint16 *new_infomask2)
|
uint16 *new_infomask2)
|
||||||
{
|
{
|
||||||
int nmembers;
|
int nmembers;
|
||||||
MultiXactMember *members;
|
MultiXactMember *members;
|
||||||
int i;
|
int i;
|
||||||
uint16 bits = HEAP_XMAX_IS_MULTI;
|
uint16 bits = HEAP_XMAX_IS_MULTI;
|
||||||
uint16 bits2 = 0;
|
uint16 bits2 = 0;
|
||||||
bool has_update = false;
|
bool has_update = false;
|
||||||
LockTupleMode strongest = LockTupleKeyShare;
|
LockTupleMode strongest = LockTupleKeyShare;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We only use this in multis we just created, so they cannot be values
|
* We only use this in multis we just created, so they cannot be values
|
||||||
@ -5188,7 +5194,7 @@ GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask,
|
|||||||
|
|
||||||
for (i = 0; i < nmembers; i++)
|
for (i = 0; i < nmembers; i++)
|
||||||
{
|
{
|
||||||
LockTupleMode mode;
|
LockTupleMode mode;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remember the strongest lock mode held by any member of the
|
* Remember the strongest lock mode held by any member of the
|
||||||
@ -5249,22 +5255,22 @@ GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask,
|
|||||||
static TransactionId
|
static TransactionId
|
||||||
MultiXactIdGetUpdateXid(TransactionId xmax, uint16 t_infomask)
|
MultiXactIdGetUpdateXid(TransactionId xmax, uint16 t_infomask)
|
||||||
{
|
{
|
||||||
TransactionId update_xact = InvalidTransactionId;
|
TransactionId update_xact = InvalidTransactionId;
|
||||||
MultiXactMember *members;
|
MultiXactMember *members;
|
||||||
int nmembers;
|
int nmembers;
|
||||||
|
|
||||||
Assert(!(t_infomask & HEAP_XMAX_LOCK_ONLY));
|
Assert(!(t_infomask & HEAP_XMAX_LOCK_ONLY));
|
||||||
Assert(t_infomask & HEAP_XMAX_IS_MULTI);
|
Assert(t_infomask & HEAP_XMAX_IS_MULTI);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since we know the LOCK_ONLY bit is not set, this cannot be a
|
* Since we know the LOCK_ONLY bit is not set, this cannot be a multi from
|
||||||
* multi from pre-pg_upgrade.
|
* pre-pg_upgrade.
|
||||||
*/
|
*/
|
||||||
nmembers = GetMultiXactIdMembers(xmax, &members, false);
|
nmembers = GetMultiXactIdMembers(xmax, &members, false);
|
||||||
|
|
||||||
if (nmembers > 0)
|
if (nmembers > 0)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < nmembers; i++)
|
for (i = 0; i < nmembers; i++)
|
||||||
{
|
{
|
||||||
@ -5284,6 +5290,7 @@ MultiXactIdGetUpdateXid(TransactionId xmax, uint16 t_infomask)
|
|||||||
members[i].status == MultiXactStatusUpdate);
|
members[i].status == MultiXactStatusUpdate);
|
||||||
update_xact = members[i].xid;
|
update_xact = members[i].xid;
|
||||||
#ifndef USE_ASSERT_CHECKING
|
#ifndef USE_ASSERT_CHECKING
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* in an assert-enabled build, walk the whole array to ensure
|
* in an assert-enabled build, walk the whole array to ensure
|
||||||
* there's no other updater.
|
* there's no other updater.
|
||||||
@ -5300,7 +5307,7 @@ MultiXactIdGetUpdateXid(TransactionId xmax, uint16 t_infomask)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* HeapTupleGetUpdateXid
|
* HeapTupleGetUpdateXid
|
||||||
* As above, but use a HeapTupleHeader
|
* As above, but use a HeapTupleHeader
|
||||||
*
|
*
|
||||||
* See also HeapTupleHeaderGetUpdateXid, which can be used without previously
|
* See also HeapTupleHeaderGetUpdateXid, which can be used without previously
|
||||||
* checking the hint bits.
|
* checking the hint bits.
|
||||||
@ -5314,7 +5321,7 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Do_MultiXactIdWait
|
* Do_MultiXactIdWait
|
||||||
* Actual implementation for the two functions below.
|
* Actual implementation for the two functions below.
|
||||||
*
|
*
|
||||||
* We do this by sleeping on each member using XactLockTableWait. Any
|
* We do this by sleeping on each member using XactLockTableWait. Any
|
||||||
* members that belong to the current backend are *not* waited for, however;
|
* members that belong to the current backend are *not* waited for, however;
|
||||||
@ -5432,7 +5439,7 @@ ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
|
|||||||
* heap_tuple_needs_freeze
|
* heap_tuple_needs_freeze
|
||||||
*
|
*
|
||||||
* Check to see whether any of the XID fields of a tuple (xmin, xmax, xvac)
|
* Check to see whether any of the XID fields of a tuple (xmin, xmax, xvac)
|
||||||
* are older than the specified cutoff XID or MultiXactId. If so, return TRUE.
|
* are older than the specified cutoff XID or MultiXactId. If so, return TRUE.
|
||||||
*
|
*
|
||||||
* It doesn't matter whether the tuple is alive or dead, we are checking
|
* It doesn't matter whether the tuple is alive or dead, we are checking
|
||||||
* to see if a tuple needs to be removed or frozen to avoid wraparound.
|
* to see if a tuple needs to be removed or frozen to avoid wraparound.
|
||||||
@ -6091,7 +6098,7 @@ heap_xlog_freeze(XLogRecPtr lsn, XLogRecord *record)
|
|||||||
{
|
{
|
||||||
xl_heap_freeze *xlrec = (xl_heap_freeze *) XLogRecGetData(record);
|
xl_heap_freeze *xlrec = (xl_heap_freeze *) XLogRecGetData(record);
|
||||||
TransactionId cutoff_xid = xlrec->cutoff_xid;
|
TransactionId cutoff_xid = xlrec->cutoff_xid;
|
||||||
MultiXactId cutoff_multi = xlrec->cutoff_multi;
|
MultiXactId cutoff_multi = xlrec->cutoff_multi;
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
Page page;
|
Page page;
|
||||||
|
|
||||||
@ -6361,7 +6368,7 @@ heap_xlog_delete(XLogRecPtr lsn, XLogRecord *record)
|
|||||||
return;
|
return;
|
||||||
page = (Page) BufferGetPage(buffer);
|
page = (Page) BufferGetPage(buffer);
|
||||||
|
|
||||||
if (lsn <= PageGetLSN(page)) /* changes are applied */
|
if (lsn <= PageGetLSN(page)) /* changes are applied */
|
||||||
{
|
{
|
||||||
UnlockReleaseBuffer(buffer);
|
UnlockReleaseBuffer(buffer);
|
||||||
return;
|
return;
|
||||||
@ -6729,7 +6736,7 @@ heap_xlog_update(XLogRecPtr lsn, XLogRecord *record, bool hot_update)
|
|||||||
goto newt;
|
goto newt;
|
||||||
page = (Page) BufferGetPage(obuffer);
|
page = (Page) BufferGetPage(obuffer);
|
||||||
|
|
||||||
if (lsn <= PageGetLSN(page)) /* changes are applied */
|
if (lsn <= PageGetLSN(page)) /* changes are applied */
|
||||||
{
|
{
|
||||||
if (samepage)
|
if (samepage)
|
||||||
{
|
{
|
||||||
@ -6931,7 +6938,7 @@ heap_xlog_lock(XLogRecPtr lsn, XLogRecord *record)
|
|||||||
return;
|
return;
|
||||||
page = (Page) BufferGetPage(buffer);
|
page = (Page) BufferGetPage(buffer);
|
||||||
|
|
||||||
if (lsn <= PageGetLSN(page)) /* changes are applied */
|
if (lsn <= PageGetLSN(page)) /* changes are applied */
|
||||||
{
|
{
|
||||||
UnlockReleaseBuffer(buffer);
|
UnlockReleaseBuffer(buffer);
|
||||||
return;
|
return;
|
||||||
@ -6962,7 +6969,7 @@ static void
|
|||||||
heap_xlog_lock_updated(XLogRecPtr lsn, XLogRecord *record)
|
heap_xlog_lock_updated(XLogRecPtr lsn, XLogRecord *record)
|
||||||
{
|
{
|
||||||
xl_heap_lock_updated *xlrec =
|
xl_heap_lock_updated *xlrec =
|
||||||
(xl_heap_lock_updated *) XLogRecGetData(record);
|
(xl_heap_lock_updated *) XLogRecGetData(record);
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
Page page;
|
Page page;
|
||||||
OffsetNumber offnum;
|
OffsetNumber offnum;
|
||||||
@ -6983,7 +6990,7 @@ heap_xlog_lock_updated(XLogRecPtr lsn, XLogRecord *record)
|
|||||||
return;
|
return;
|
||||||
page = (Page) BufferGetPage(buffer);
|
page = (Page) BufferGetPage(buffer);
|
||||||
|
|
||||||
if (lsn <= PageGetLSN(page)) /* changes are applied */
|
if (lsn <= PageGetLSN(page)) /* changes are applied */
|
||||||
{
|
{
|
||||||
UnlockReleaseBuffer(buffer);
|
UnlockReleaseBuffer(buffer);
|
||||||
return;
|
return;
|
||||||
@ -7033,7 +7040,7 @@ heap_xlog_inplace(XLogRecPtr lsn, XLogRecord *record)
|
|||||||
return;
|
return;
|
||||||
page = (Page) BufferGetPage(buffer);
|
page = (Page) BufferGetPage(buffer);
|
||||||
|
|
||||||
if (lsn <= PageGetLSN(page)) /* changes are applied */
|
if (lsn <= PageGetLSN(page)) /* changes are applied */
|
||||||
{
|
{
|
||||||
UnlockReleaseBuffer(buffer);
|
UnlockReleaseBuffer(buffer);
|
||||||
return;
|
return;
|
||||||
|
@ -129,7 +129,7 @@ typedef struct RewriteStateData
|
|||||||
* determine tuple visibility */
|
* determine tuple visibility */
|
||||||
TransactionId rs_freeze_xid;/* Xid that will be used as freeze cutoff
|
TransactionId rs_freeze_xid;/* Xid that will be used as freeze cutoff
|
||||||
* point */
|
* point */
|
||||||
MultiXactId rs_freeze_multi;/* MultiXactId that will be used as freeze
|
MultiXactId rs_freeze_multi;/* MultiXactId that will be used as freeze
|
||||||
* cutoff point for multixacts */
|
* cutoff point for multixacts */
|
||||||
MemoryContext rs_cxt; /* for hash tables and entries and tuples in
|
MemoryContext rs_cxt; /* for hash tables and entries and tuples in
|
||||||
* them */
|
* them */
|
||||||
|
@ -292,7 +292,7 @@ visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf,
|
|||||||
*/
|
*/
|
||||||
if (DataChecksumsEnabled())
|
if (DataChecksumsEnabled())
|
||||||
{
|
{
|
||||||
Page heapPage = BufferGetPage(heapBuf);
|
Page heapPage = BufferGetPage(heapBuf);
|
||||||
|
|
||||||
/* caller is expected to set PD_ALL_VISIBLE first */
|
/* caller is expected to set PD_ALL_VISIBLE first */
|
||||||
Assert(PageIsAllVisible(heapPage));
|
Assert(PageIsAllVisible(heapPage));
|
||||||
|
@ -532,8 +532,8 @@ _bt_log_reuse_page(Relation rel, BlockNumber blkno, TransactionId latestRemovedX
|
|||||||
START_CRIT_SECTION();
|
START_CRIT_SECTION();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't do MarkBufferDirty here because we're about to initialise
|
* We don't do MarkBufferDirty here because we're about to initialise the
|
||||||
* the page, and nobody else can see it yet.
|
* page, and nobody else can see it yet.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* XLOG stuff */
|
/* XLOG stuff */
|
||||||
@ -552,8 +552,8 @@ _bt_log_reuse_page(Relation rel, BlockNumber blkno, TransactionId latestRemovedX
|
|||||||
XLogInsert(RM_BTREE_ID, XLOG_BTREE_REUSE_PAGE, rdata);
|
XLogInsert(RM_BTREE_ID, XLOG_BTREE_REUSE_PAGE, rdata);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't do PageSetLSN here because we're about to initialise
|
* We don't do PageSetLSN here because we're about to initialise the
|
||||||
* the page, so no need.
|
* page, so no need.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,7 +373,7 @@ btree_xlog_split(bool onleft, bool isroot,
|
|||||||
* Note that this code ensures that the items remaining on the
|
* Note that this code ensures that the items remaining on the
|
||||||
* left page are in the correct item number order, but it does not
|
* left page are in the correct item number order, but it does not
|
||||||
* reproduce the physical order they would have had. Is this
|
* reproduce the physical order they would have had. Is this
|
||||||
* worth changing? See also _bt_restore_page().
|
* worth changing? See also _bt_restore_page().
|
||||||
*/
|
*/
|
||||||
Page lpage = (Page) BufferGetPage(lbuf);
|
Page lpage = (Page) BufferGetPage(lbuf);
|
||||||
BTPageOpaque lopaque = (BTPageOpaque) PageGetSpecialPointer(lpage);
|
BTPageOpaque lopaque = (BTPageOpaque) PageGetSpecialPointer(lpage);
|
||||||
@ -606,18 +606,18 @@ btree_xlog_delete_get_latestRemovedXid(xl_btree_delete *xlrec)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* In what follows, we have to examine the previous state of the index
|
* In what follows, we have to examine the previous state of the index
|
||||||
* page, as well as the heap page(s) it points to. This is only valid if
|
* page, as well as the heap page(s) it points to. This is only valid if
|
||||||
* WAL replay has reached a consistent database state; which means that
|
* WAL replay has reached a consistent database state; which means that
|
||||||
* the preceding check is not just an optimization, but is *necessary*.
|
* the preceding check is not just an optimization, but is *necessary*. We
|
||||||
* We won't have let in any user sessions before we reach consistency.
|
* won't have let in any user sessions before we reach consistency.
|
||||||
*/
|
*/
|
||||||
if (!reachedConsistency)
|
if (!reachedConsistency)
|
||||||
elog(PANIC, "btree_xlog_delete_get_latestRemovedXid: cannot operate with inconsistent data");
|
elog(PANIC, "btree_xlog_delete_get_latestRemovedXid: cannot operate with inconsistent data");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get index page. If the DB is consistent, this should not fail, nor
|
* Get index page. If the DB is consistent, this should not fail, nor
|
||||||
* should any of the heap page fetches below. If one does, we return
|
* should any of the heap page fetches below. If one does, we return
|
||||||
* InvalidTransactionId to cancel all HS transactions. That's probably
|
* InvalidTransactionId to cancel all HS transactions. That's probably
|
||||||
* overkill, but it's safe, and certainly better than panicking here.
|
* overkill, but it's safe, and certainly better than panicking here.
|
||||||
*/
|
*/
|
||||||
ibuffer = XLogReadBuffer(xlrec->node, xlrec->block, false);
|
ibuffer = XLogReadBuffer(xlrec->node, xlrec->block, false);
|
||||||
@ -701,10 +701,10 @@ btree_xlog_delete_get_latestRemovedXid(xl_btree_delete *xlrec)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX If all heap tuples were LP_DEAD then we will be returning
|
* XXX If all heap tuples were LP_DEAD then we will be returning
|
||||||
* InvalidTransactionId here, causing conflict for all HS
|
* InvalidTransactionId here, causing conflict for all HS transactions.
|
||||||
* transactions. That should happen very rarely (reasoning please?). Also
|
* That should happen very rarely (reasoning please?). Also note that
|
||||||
* note that caller can't tell the difference between this case and the
|
* caller can't tell the difference between this case and the fast path
|
||||||
* fast path exit above. May need to change that in future.
|
* exit above. May need to change that in future.
|
||||||
*/
|
*/
|
||||||
return latestRemovedXid;
|
return latestRemovedXid;
|
||||||
}
|
}
|
||||||
@ -721,7 +721,7 @@ btree_xlog_delete(XLogRecPtr lsn, XLogRecord *record)
|
|||||||
* If we have any conflict processing to do, it must happen before we
|
* If we have any conflict processing to do, it must happen before we
|
||||||
* update the page.
|
* update the page.
|
||||||
*
|
*
|
||||||
* Btree delete records can conflict with standby queries. You might
|
* Btree delete records can conflict with standby queries. You might
|
||||||
* think that vacuum records would conflict as well, but we've handled
|
* think that vacuum records would conflict as well, but we've handled
|
||||||
* that already. XLOG_HEAP2_CLEANUP_INFO records provide the highest xid
|
* that already. XLOG_HEAP2_CLEANUP_INFO records provide the highest xid
|
||||||
* cleaned by the vacuum of the heap and so we can resolve any conflicts
|
* cleaned by the vacuum of the heap and so we can resolve any conflicts
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* clogdesc.c
|
* clogdesc.c
|
||||||
* rmgr descriptor routines for access/transam/clog.c
|
* rmgr descriptor routines for access/transam/clog.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/clogdesc.c
|
* src/backend/access/rmgrdesc/clogdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* dbasedesc.c
|
* dbasedesc.c
|
||||||
* rmgr descriptor routines for commands/dbcommands.c
|
* rmgr descriptor routines for commands/dbcommands.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/dbasedesc.c
|
* src/backend/access/rmgrdesc/dbasedesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* gindesc.c
|
* gindesc.c
|
||||||
* rmgr descriptor routines for access/transam/gin/ginxlog.c
|
* rmgr descriptor routines for access/transam/gin/ginxlog.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/gindesc.c
|
* src/backend/access/rmgrdesc/gindesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* gistdesc.c
|
* gistdesc.c
|
||||||
* rmgr descriptor routines for access/gist/gistxlog.c
|
* rmgr descriptor routines for access/gist/gistxlog.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/gistdesc.c
|
* src/backend/access/rmgrdesc/gistdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* hashdesc.c
|
* hashdesc.c
|
||||||
* rmgr descriptor routines for access/hash/hash.c
|
* rmgr descriptor routines for access/hash/hash.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/hashdesc.c
|
* src/backend/access/rmgrdesc/hashdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* heapdesc.c
|
* heapdesc.c
|
||||||
* rmgr descriptor routines for access/heap/heapam.c
|
* rmgr descriptor routines for access/heap/heapam.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/heapdesc.c
|
* src/backend/access/rmgrdesc/heapdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* mxactdesc.c
|
* mxactdesc.c
|
||||||
* rmgr descriptor routines for access/transam/multixact.c
|
* rmgr descriptor routines for access/transam/multixact.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/mxactdesc.c
|
* src/backend/access/rmgrdesc/mxactdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* nbtdesc.c
|
* nbtdesc.c
|
||||||
* rmgr descriptor routines for access/nbtree/nbtxlog.c
|
* rmgr descriptor routines for access/nbtree/nbtxlog.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/nbtdesc.c
|
* src/backend/access/rmgrdesc/nbtdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* relmapdesc.c
|
* relmapdesc.c
|
||||||
* rmgr descriptor routines for utils/cache/relmapper.c
|
* rmgr descriptor routines for utils/cache/relmapper.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/relmapdesc.c
|
* src/backend/access/rmgrdesc/relmapdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* seqdesc.c
|
* seqdesc.c
|
||||||
* rmgr descriptor routines for commands/sequence.c
|
* rmgr descriptor routines for commands/sequence.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/seqdesc.c
|
* src/backend/access/rmgrdesc/seqdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* smgrdesc.c
|
* smgrdesc.c
|
||||||
* rmgr descriptor routines for catalog/storage.c
|
* rmgr descriptor routines for catalog/storage.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/smgrdesc.c
|
* src/backend/access/rmgrdesc/smgrdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* spgdesc.c
|
* spgdesc.c
|
||||||
* rmgr descriptor routines for access/spgist/spgxlog.c
|
* rmgr descriptor routines for access/spgist/spgxlog.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/spgdesc.c
|
* src/backend/access/rmgrdesc/spgdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* standbydesc.c
|
* standbydesc.c
|
||||||
* rmgr descriptor routines for storage/ipc/standby.c
|
* rmgr descriptor routines for storage/ipc/standby.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/standbydesc.c
|
* src/backend/access/rmgrdesc/standbydesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* tblspcdesc.c
|
* tblspcdesc.c
|
||||||
* rmgr descriptor routines for commands/tablespace.c
|
* rmgr descriptor routines for commands/tablespace.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/tblspcdesc.c
|
* src/backend/access/rmgrdesc/tblspcdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* xactdesc.c
|
* xactdesc.c
|
||||||
* rmgr descriptor routines for access/transam/xact.c
|
* rmgr descriptor routines for access/transam/xact.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/xactdesc.c
|
* src/backend/access/rmgrdesc/xactdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* xlogdesc.c
|
* xlogdesc.c
|
||||||
* rmgr descriptor routines for access/transam/xlog.c
|
* rmgr descriptor routines for access/transam/xlog.c
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* src/backend/access/rmgrdesc/xlogdesc.c
|
* src/backend/access/rmgrdesc/xlogdesc.c
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -45,7 +45,7 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec)
|
|||||||
"tli %u; prev tli %u; fpw %s; xid %u/%u; oid %u; multi %u; offset %u; "
|
"tli %u; prev tli %u; fpw %s; xid %u/%u; oid %u; multi %u; offset %u; "
|
||||||
"oldest xid %u in DB %u; oldest multi %u in DB %u; "
|
"oldest xid %u in DB %u; oldest multi %u in DB %u; "
|
||||||
"oldest running xid %u; %s",
|
"oldest running xid %u; %s",
|
||||||
(uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo,
|
(uint32) (checkpoint->redo >> 32), (uint32) checkpoint->redo,
|
||||||
checkpoint->ThisTimeLineID,
|
checkpoint->ThisTimeLineID,
|
||||||
checkpoint->PrevTimeLineID,
|
checkpoint->PrevTimeLineID,
|
||||||
checkpoint->fullPageWrites ? "true" : "false",
|
checkpoint->fullPageWrites ? "true" : "false",
|
||||||
@ -84,7 +84,8 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec)
|
|||||||
}
|
}
|
||||||
else if (info == XLOG_HINT)
|
else if (info == XLOG_HINT)
|
||||||
{
|
{
|
||||||
BkpBlock *bkp = (BkpBlock *) rec;
|
BkpBlock *bkp = (BkpBlock *) rec;
|
||||||
|
|
||||||
appendStringInfo(buf, "page hint: %s block %u",
|
appendStringInfo(buf, "page hint: %s block %u",
|
||||||
relpathperm(bkp->node, bkp->fork),
|
relpathperm(bkp->node, bkp->fork),
|
||||||
bkp->block);
|
bkp->block);
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
* imposed by page headers, tuple headers, etc, we leave 100 bytes for that
|
* imposed by page headers, tuple headers, etc, we leave 100 bytes for that
|
||||||
* (the actual overhead should be no more than 56 bytes at this writing, so
|
* (the actual overhead should be no more than 56 bytes at this writing, so
|
||||||
* there is slop in this number). So we can safely create prefixes up to
|
* there is slop in this number). So we can safely create prefixes up to
|
||||||
* BLCKSZ - 256 * 16 - 100 bytes long. Unfortunately, because 256 * 16 is
|
* BLCKSZ - 256 * 16 - 100 bytes long. Unfortunately, because 256 * 16 is
|
||||||
* already 4K, there is no safe prefix length when BLCKSZ is less than 8K;
|
* already 4K, there is no safe prefix length when BLCKSZ is less than 8K;
|
||||||
* it is always possible to get "SPGiST inner tuple size exceeds maximum"
|
* it is always possible to get "SPGiST inner tuple size exceeds maximum"
|
||||||
* if there are too many distinct next-byte values at a given place in the
|
* if there are too many distinct next-byte values at a given place in the
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
*
|
*
|
||||||
* The pg_multixact manager is a pg_clog-like manager that stores an array of
|
* The pg_multixact manager is a pg_clog-like manager that stores an array of
|
||||||
* MultiXactMember for each MultiXactId. It is a fundamental part of the
|
* MultiXactMember for each MultiXactId. It is a fundamental part of the
|
||||||
* shared-row-lock implementation. Each MultiXactMember is comprised of a
|
* shared-row-lock implementation. Each MultiXactMember is comprised of a
|
||||||
* TransactionId and a set of flag bits. The name is a bit historical:
|
* TransactionId and a set of flag bits. The name is a bit historical:
|
||||||
* originally, a MultiXactId consisted of more than one TransactionId (except
|
* originally, a MultiXactId consisted of more than one TransactionId (except
|
||||||
* in rare corner cases), hence "multi". Nowadays, however, it's perfectly
|
* in rare corner cases), hence "multi". Nowadays, however, it's perfectly
|
||||||
@ -50,7 +50,7 @@
|
|||||||
* The minimum value in each database is stored in pg_database, and the
|
* The minimum value in each database is stored in pg_database, and the
|
||||||
* global minimum is part of pg_control. Any vacuum that is able to
|
* global minimum is part of pg_control. Any vacuum that is able to
|
||||||
* advance its database's minimum value also computes a new global minimum,
|
* advance its database's minimum value also computes a new global minimum,
|
||||||
* and uses this value to truncate older segments. When new multixactid
|
* and uses this value to truncate older segments. When new multixactid
|
||||||
* values are to be created, care is taken that the counter does not
|
* values are to be created, care is taken that the counter does not
|
||||||
* fall within the wraparound horizon considering the global minimum value.
|
* fall within the wraparound horizon considering the global minimum value.
|
||||||
*
|
*
|
||||||
@ -108,7 +108,7 @@
|
|||||||
* additional flag bits for each TransactionId. To do this without getting
|
* additional flag bits for each TransactionId. To do this without getting
|
||||||
* into alignment issues, we store four bytes of flags, and then the
|
* into alignment issues, we store four bytes of flags, and then the
|
||||||
* corresponding 4 Xids. Each such 5-word (20-byte) set we call a "group", and
|
* corresponding 4 Xids. Each such 5-word (20-byte) set we call a "group", and
|
||||||
* are stored as a whole in pages. Thus, with 8kB BLCKSZ, we keep 409 groups
|
* are stored as a whole in pages. Thus, with 8kB BLCKSZ, we keep 409 groups
|
||||||
* per page. This wastes 12 bytes per page, but that's OK -- simplicity (and
|
* per page. This wastes 12 bytes per page, but that's OK -- simplicity (and
|
||||||
* performance) trumps space efficiency here.
|
* performance) trumps space efficiency here.
|
||||||
*
|
*
|
||||||
@ -177,17 +177,17 @@ typedef struct MultiXactStateData
|
|||||||
MultiXactId lastTruncationPoint;
|
MultiXactId lastTruncationPoint;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* oldest multixact that is still on disk. Anything older than this should
|
* oldest multixact that is still on disk. Anything older than this
|
||||||
* not be consulted.
|
* should not be consulted.
|
||||||
*/
|
*/
|
||||||
MultiXactId oldestMultiXactId;
|
MultiXactId oldestMultiXactId;
|
||||||
Oid oldestMultiXactDB;
|
Oid oldestMultiXactDB;
|
||||||
|
|
||||||
/* support for anti-wraparound measures */
|
/* support for anti-wraparound measures */
|
||||||
MultiXactId multiVacLimit;
|
MultiXactId multiVacLimit;
|
||||||
MultiXactId multiWarnLimit;
|
MultiXactId multiWarnLimit;
|
||||||
MultiXactId multiStopLimit;
|
MultiXactId multiStopLimit;
|
||||||
MultiXactId multiWrapLimit;
|
MultiXactId multiWrapLimit;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Per-backend data starts here. We have two arrays stored in the area
|
* Per-backend data starts here. We have two arrays stored in the area
|
||||||
@ -252,7 +252,7 @@ static MultiXactId *OldestVisibleMXactId;
|
|||||||
* so they will be uninteresting by the time our next transaction starts.
|
* so they will be uninteresting by the time our next transaction starts.
|
||||||
* (XXX not clear that this is correct --- other members of the MultiXact
|
* (XXX not clear that this is correct --- other members of the MultiXact
|
||||||
* could hang around longer than we did. However, it's not clear what a
|
* could hang around longer than we did. However, it's not clear what a
|
||||||
* better policy for flushing old cache entries would be.) FIXME actually
|
* better policy for flushing old cache entries would be.) FIXME actually
|
||||||
* this is plain wrong now that multixact's may contain update Xids.
|
* this is plain wrong now that multixact's may contain update Xids.
|
||||||
*
|
*
|
||||||
* We allocate the cache entries in a memory context that is deleted at
|
* We allocate the cache entries in a memory context that is deleted at
|
||||||
@ -291,7 +291,7 @@ static void RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
|
|||||||
static MultiXactId GetNewMultiXactId(int nmembers, MultiXactOffset *offset);
|
static MultiXactId GetNewMultiXactId(int nmembers, MultiXactOffset *offset);
|
||||||
|
|
||||||
/* MultiXact cache management */
|
/* MultiXact cache management */
|
||||||
static int mxactMemberComparator(const void *arg1, const void *arg2);
|
static int mxactMemberComparator(const void *arg1, const void *arg2);
|
||||||
static MultiXactId mXactCacheGetBySet(int nmembers, MultiXactMember *members);
|
static MultiXactId mXactCacheGetBySet(int nmembers, MultiXactMember *members);
|
||||||
static int mXactCacheGetById(MultiXactId multi, MultiXactMember **members);
|
static int mXactCacheGetById(MultiXactId multi, MultiXactMember **members);
|
||||||
static void mXactCachePut(MultiXactId multi, int nmembers,
|
static void mXactCachePut(MultiXactId multi, int nmembers,
|
||||||
@ -387,15 +387,15 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status)
|
|||||||
multi, xid, mxstatus_to_string(status));
|
multi, xid, mxstatus_to_string(status));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: we don't allow for old multis here. The reason is that the
|
* Note: we don't allow for old multis here. The reason is that the only
|
||||||
* only caller of this function does a check that the multixact is
|
* caller of this function does a check that the multixact is no longer
|
||||||
* no longer running.
|
* running.
|
||||||
*/
|
*/
|
||||||
nmembers = GetMultiXactIdMembers(multi, &members, false);
|
nmembers = GetMultiXactIdMembers(multi, &members, false);
|
||||||
|
|
||||||
if (nmembers < 0)
|
if (nmembers < 0)
|
||||||
{
|
{
|
||||||
MultiXactMember member;
|
MultiXactMember member;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The MultiXactId is obsolete. This can only happen if all the
|
* The MultiXactId is obsolete. This can only happen if all the
|
||||||
@ -430,14 +430,14 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine which of the members of the MultiXactId are still of interest.
|
* Determine which of the members of the MultiXactId are still of
|
||||||
* This is any running transaction, and also any transaction that grabbed
|
* interest. This is any running transaction, and also any transaction
|
||||||
* something stronger than just a lock and was committed. (An update that
|
* that grabbed something stronger than just a lock and was committed.
|
||||||
* aborted is of no interest here.)
|
* (An update that aborted is of no interest here.)
|
||||||
*
|
*
|
||||||
* (Removing dead members is just an optimization, but a useful one.
|
* (Removing dead members is just an optimization, but a useful one. Note
|
||||||
* Note we have the same race condition here as above: j could be 0 at the
|
* we have the same race condition here as above: j could be 0 at the end
|
||||||
* end of the loop.)
|
* of the loop.)
|
||||||
*/
|
*/
|
||||||
newMembers = (MultiXactMember *)
|
newMembers = (MultiXactMember *)
|
||||||
palloc(sizeof(MultiXactMember) * (nmembers + 1));
|
palloc(sizeof(MultiXactMember) * (nmembers + 1));
|
||||||
@ -641,12 +641,12 @@ MultiXactIdSetOldestVisible(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* ReadNextMultiXactId
|
* ReadNextMultiXactId
|
||||||
* Return the next MultiXactId to be assigned, but don't allocate it
|
* Return the next MultiXactId to be assigned, but don't allocate it
|
||||||
*/
|
*/
|
||||||
MultiXactId
|
MultiXactId
|
||||||
ReadNextMultiXactId(void)
|
ReadNextMultiXactId(void)
|
||||||
{
|
{
|
||||||
MultiXactId mxid;
|
MultiXactId mxid;
|
||||||
|
|
||||||
/* XXX we could presumably do this without a lock. */
|
/* XXX we could presumably do this without a lock. */
|
||||||
LWLockAcquire(MultiXactGenLock, LW_SHARED);
|
LWLockAcquire(MultiXactGenLock, LW_SHARED);
|
||||||
@ -722,9 +722,9 @@ CreateMultiXactId(int nmembers, MultiXactMember *members)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX Note: there's a lot of padding space in MultiXactMember. We could
|
* XXX Note: there's a lot of padding space in MultiXactMember. We could
|
||||||
* find a more compact representation of this Xlog record -- perhaps all the
|
* find a more compact representation of this Xlog record -- perhaps all
|
||||||
* status flags in one XLogRecData, then all the xids in another one? Not
|
* the status flags in one XLogRecData, then all the xids in another one?
|
||||||
* clear that it's worth the trouble though.
|
* Not clear that it's worth the trouble though.
|
||||||
*/
|
*/
|
||||||
rdata[0].data = (char *) (&xlrec);
|
rdata[0].data = (char *) (&xlrec);
|
||||||
rdata[0].len = SizeOfMultiXactCreate;
|
rdata[0].len = SizeOfMultiXactCreate;
|
||||||
@ -878,7 +878,7 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
|
|||||||
|
|
||||||
/*----------
|
/*----------
|
||||||
* Check to see if it's safe to assign another MultiXactId. This protects
|
* Check to see if it's safe to assign another MultiXactId. This protects
|
||||||
* against catastrophic data loss due to multixact wraparound. The basic
|
* against catastrophic data loss due to multixact wraparound. The basic
|
||||||
* rules are:
|
* rules are:
|
||||||
*
|
*
|
||||||
* If we're past multiVacLimit, start trying to force autovacuum cycles.
|
* If we're past multiVacLimit, start trying to force autovacuum cycles.
|
||||||
@ -892,7 +892,7 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* For safety's sake, we release MultiXactGenLock while sending
|
* For safety's sake, we release MultiXactGenLock while sending
|
||||||
* signals, warnings, etc. This is not so much because we care about
|
* signals, warnings, etc. This is not so much because we care about
|
||||||
* preserving concurrency in this situation, as to avoid any
|
* preserving concurrency in this situation, as to avoid any
|
||||||
* possibility of deadlock while doing get_database_name(). First,
|
* possibility of deadlock while doing get_database_name(). First,
|
||||||
* copy all the shared values we'll need in this path.
|
* copy all the shared values we'll need in this path.
|
||||||
@ -923,15 +923,15 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
|
|||||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
||||||
errmsg("database is not accepting commands that generate new MultiXactIds to avoid wraparound data loss in database \"%s\"",
|
errmsg("database is not accepting commands that generate new MultiXactIds to avoid wraparound data loss in database \"%s\"",
|
||||||
oldest_datname),
|
oldest_datname),
|
||||||
errhint("Execute a database-wide VACUUM in that database.\n"
|
errhint("Execute a database-wide VACUUM in that database.\n"
|
||||||
"You might also need to commit or roll back old prepared transactions.")));
|
"You might also need to commit or roll back old prepared transactions.")));
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
||||||
errmsg("database is not accepting commands that generate new MultiXactIds to avoid wraparound data loss in database with OID %u",
|
errmsg("database is not accepting commands that generate new MultiXactIds to avoid wraparound data loss in database with OID %u",
|
||||||
oldest_datoid),
|
oldest_datoid),
|
||||||
errhint("Execute a database-wide VACUUM in that database.\n"
|
errhint("Execute a database-wide VACUUM in that database.\n"
|
||||||
"You might also need to commit or roll back old prepared transactions.")));
|
"You might also need to commit or roll back old prepared transactions.")));
|
||||||
}
|
}
|
||||||
else if (!MultiXactIdPrecedes(result, multiWarnLimit))
|
else if (!MultiXactIdPrecedes(result, multiWarnLimit))
|
||||||
{
|
{
|
||||||
@ -943,15 +943,15 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
|
|||||||
(errmsg("database \"%s\" must be vacuumed before %u more MultiXactIds are used",
|
(errmsg("database \"%s\" must be vacuumed before %u more MultiXactIds are used",
|
||||||
oldest_datname,
|
oldest_datname,
|
||||||
multiWrapLimit - result),
|
multiWrapLimit - result),
|
||||||
errhint("Execute a database-wide VACUUM in that database.\n"
|
errhint("Execute a database-wide VACUUM in that database.\n"
|
||||||
"You might also need to commit or roll back old prepared transactions.")));
|
"You might also need to commit or roll back old prepared transactions.")));
|
||||||
else
|
else
|
||||||
ereport(WARNING,
|
ereport(WARNING,
|
||||||
(errmsg("database with OID %u must be vacuumed before %u more MultiXactIds are used",
|
(errmsg("database with OID %u must be vacuumed before %u more MultiXactIds are used",
|
||||||
oldest_datoid,
|
oldest_datoid,
|
||||||
multiWrapLimit - result),
|
multiWrapLimit - result),
|
||||||
errhint("Execute a database-wide VACUUM in that database.\n"
|
errhint("Execute a database-wide VACUUM in that database.\n"
|
||||||
"You might also need to commit or roll back old prepared transactions.")));
|
"You might also need to commit or roll back old prepared transactions.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Re-acquire lock and start over */
|
/* Re-acquire lock and start over */
|
||||||
@ -995,10 +995,10 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
|
|||||||
*
|
*
|
||||||
* We don't care about MultiXactId wraparound here; it will be handled by
|
* We don't care about MultiXactId wraparound here; it will be handled by
|
||||||
* the next iteration. But note that nextMXact may be InvalidMultiXactId
|
* the next iteration. But note that nextMXact may be InvalidMultiXactId
|
||||||
* or the first value on a segment-beginning page after this routine exits,
|
* or the first value on a segment-beginning page after this routine
|
||||||
* so anyone else looking at the variable must be prepared to deal with
|
* exits, so anyone else looking at the variable must be prepared to deal
|
||||||
* either case. Similarly, nextOffset may be zero, but we won't use that
|
* with either case. Similarly, nextOffset may be zero, but we won't use
|
||||||
* as the actual start offset of the next multixact.
|
* that as the actual start offset of the next multixact.
|
||||||
*/
|
*/
|
||||||
(MultiXactState->nextMXact)++;
|
(MultiXactState->nextMXact)++;
|
||||||
|
|
||||||
@ -1066,18 +1066,18 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
|
|||||||
*
|
*
|
||||||
* An ID older than MultiXactState->oldestMultiXactId cannot possibly be
|
* An ID older than MultiXactState->oldestMultiXactId cannot possibly be
|
||||||
* useful; it should have already been frozen by vacuum. We've truncated
|
* useful; it should have already been frozen by vacuum. We've truncated
|
||||||
* the on-disk structures anyway. Returning the wrong values could lead to
|
* the on-disk structures anyway. Returning the wrong values could lead
|
||||||
* an incorrect visibility result. However, to support pg_upgrade we need
|
* to an incorrect visibility result. However, to support pg_upgrade we
|
||||||
* to allow an empty set to be returned regardless, if the caller is
|
* need to allow an empty set to be returned regardless, if the caller is
|
||||||
* willing to accept it; the caller is expected to check that it's an
|
* willing to accept it; the caller is expected to check that it's an
|
||||||
* allowed condition (such as ensuring that the infomask bits set on the
|
* allowed condition (such as ensuring that the infomask bits set on the
|
||||||
* tuple are consistent with the pg_upgrade scenario). If the caller is
|
* tuple are consistent with the pg_upgrade scenario). If the caller is
|
||||||
* expecting this to be called only on recently created multis, then we
|
* expecting this to be called only on recently created multis, then we
|
||||||
* raise an error.
|
* raise an error.
|
||||||
*
|
*
|
||||||
* Conversely, an ID >= nextMXact shouldn't ever be seen here; if it is
|
* Conversely, an ID >= nextMXact shouldn't ever be seen here; if it is
|
||||||
* seen, it implies undetected ID wraparound has occurred. This raises
|
* seen, it implies undetected ID wraparound has occurred. This raises a
|
||||||
* a hard error.
|
* hard error.
|
||||||
*
|
*
|
||||||
* Shared lock is enough here since we aren't modifying any global state.
|
* Shared lock is enough here since we aren't modifying any global state.
|
||||||
* Acquire it just long enough to grab the current counter values. We may
|
* Acquire it just long enough to grab the current counter values. We may
|
||||||
@ -1095,8 +1095,8 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
|
|||||||
{
|
{
|
||||||
ereport(allow_old ? DEBUG1 : ERROR,
|
ereport(allow_old ? DEBUG1 : ERROR,
|
||||||
(errcode(ERRCODE_INTERNAL_ERROR),
|
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||||
errmsg("MultiXactId %u does no longer exist -- apparent wraparound",
|
errmsg("MultiXactId %u does no longer exist -- apparent wraparound",
|
||||||
multi)));
|
multi)));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1349,7 +1349,7 @@ mXactCacheGetById(MultiXactId multi, MultiXactMember **members)
|
|||||||
memcpy(ptr, entry->members, size);
|
memcpy(ptr, entry->members, size);
|
||||||
|
|
||||||
debug_elog3(DEBUG2, "CacheGet: found %s",
|
debug_elog3(DEBUG2, "CacheGet: found %s",
|
||||||
mxid_to_string(multi, entry->nmembers, entry->members));
|
mxid_to_string(multi, entry->nmembers, entry->members));
|
||||||
return entry->nmembers;
|
return entry->nmembers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1423,8 +1423,8 @@ mxstatus_to_string(MultiXactStatus status)
|
|||||||
char *
|
char *
|
||||||
mxid_to_string(MultiXactId multi, int nmembers, MultiXactMember *members)
|
mxid_to_string(MultiXactId multi, int nmembers, MultiXactMember *members)
|
||||||
{
|
{
|
||||||
static char *str = NULL;
|
static char *str = NULL;
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (str != NULL)
|
if (str != NULL)
|
||||||
@ -1721,7 +1721,7 @@ ZeroMultiXactMemberPage(int pageno, bool writeXlog)
|
|||||||
*
|
*
|
||||||
* StartupXLOG has already established nextMXact/nextOffset by calling
|
* StartupXLOG has already established nextMXact/nextOffset by calling
|
||||||
* MultiXactSetNextMXact and/or MultiXactAdvanceNextMXact, and the oldestMulti
|
* MultiXactSetNextMXact and/or MultiXactAdvanceNextMXact, and the oldestMulti
|
||||||
* info from pg_control and/or MultiXactAdvanceOldest. Note that we may
|
* info from pg_control and/or MultiXactAdvanceOldest. Note that we may
|
||||||
* already have replayed WAL data into the SLRU files.
|
* already have replayed WAL data into the SLRU files.
|
||||||
*
|
*
|
||||||
* We don't need any locks here, really; the SLRU locks are taken
|
* We don't need any locks here, really; the SLRU locks are taken
|
||||||
@ -1883,17 +1883,17 @@ MultiXactSetNextMXact(MultiXactId nextMulti,
|
|||||||
void
|
void
|
||||||
SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
|
SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
|
||||||
{
|
{
|
||||||
MultiXactId multiVacLimit;
|
MultiXactId multiVacLimit;
|
||||||
MultiXactId multiWarnLimit;
|
MultiXactId multiWarnLimit;
|
||||||
MultiXactId multiStopLimit;
|
MultiXactId multiStopLimit;
|
||||||
MultiXactId multiWrapLimit;
|
MultiXactId multiWrapLimit;
|
||||||
MultiXactId curMulti;
|
MultiXactId curMulti;
|
||||||
|
|
||||||
Assert(MultiXactIdIsValid(oldest_datminmxid));
|
Assert(MultiXactIdIsValid(oldest_datminmxid));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The place where we actually get into deep trouble is halfway around
|
* The place where we actually get into deep trouble is halfway around
|
||||||
* from the oldest potentially-existing XID/multi. (This calculation is
|
* from the oldest potentially-existing XID/multi. (This calculation is
|
||||||
* probably off by one or two counts for Xids, because the special XIDs
|
* probably off by one or two counts for Xids, because the special XIDs
|
||||||
* reduce the size of the loop a little bit. But we throw in plenty of
|
* reduce the size of the loop a little bit. But we throw in plenty of
|
||||||
* slop below, so it doesn't matter.)
|
* slop below, so it doesn't matter.)
|
||||||
@ -1911,11 +1911,11 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
|
|||||||
multiStopLimit -= FirstMultiXactId;
|
multiStopLimit -= FirstMultiXactId;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We'll start complaining loudly when we get within 10M multis of the stop
|
* We'll start complaining loudly when we get within 10M multis of the
|
||||||
* point. This is kind of arbitrary, but if you let your gas gauge get
|
* stop point. This is kind of arbitrary, but if you let your gas gauge
|
||||||
* down to 1% of full, would you be looking for the next gas station? We
|
* get down to 1% of full, would you be looking for the next gas station?
|
||||||
* need to be fairly liberal about this number because there are lots of
|
* We need to be fairly liberal about this number because there are lots
|
||||||
* scenarios where most transactions are done by automatic clients that
|
* of scenarios where most transactions are done by automatic clients that
|
||||||
* won't pay attention to warnings. (No, we're not gonna make this
|
* won't pay attention to warnings. (No, we're not gonna make this
|
||||||
* configurable. If you know enough to configure it, you know enough to
|
* configurable. If you know enough to configure it, you know enough to
|
||||||
* not get in this kind of trouble in the first place.)
|
* not get in this kind of trouble in the first place.)
|
||||||
@ -1925,8 +1925,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
|
|||||||
multiWarnLimit -= FirstMultiXactId;
|
multiWarnLimit -= FirstMultiXactId;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We'll start trying to force autovacuums when oldest_datminmxid gets
|
* We'll start trying to force autovacuums when oldest_datminmxid gets to
|
||||||
* to be more than 200 million transactions old.
|
* be more than 200 million transactions old.
|
||||||
*/
|
*/
|
||||||
multiVacLimit = oldest_datminmxid + 200000000;
|
multiVacLimit = oldest_datminmxid + 200000000;
|
||||||
if (multiVacLimit < FirstMultiXactId)
|
if (multiVacLimit < FirstMultiXactId)
|
||||||
@ -1945,8 +1945,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
|
|||||||
|
|
||||||
/* Log the info */
|
/* Log the info */
|
||||||
ereport(DEBUG1,
|
ereport(DEBUG1,
|
||||||
(errmsg("MultiXactId wrap limit is %u, limited by database with OID %u",
|
(errmsg("MultiXactId wrap limit is %u, limited by database with OID %u",
|
||||||
multiWrapLimit, oldest_datoid)));
|
multiWrapLimit, oldest_datoid)));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If past the autovacuum force point, immediately signal an autovac
|
* If past the autovacuum force point, immediately signal an autovac
|
||||||
@ -2127,9 +2127,9 @@ ExtendMultiXactMember(MultiXactOffset offset, int nmembers)
|
|||||||
MultiXactId
|
MultiXactId
|
||||||
GetOldestMultiXactId(void)
|
GetOldestMultiXactId(void)
|
||||||
{
|
{
|
||||||
MultiXactId oldestMXact;
|
MultiXactId oldestMXact;
|
||||||
MultiXactId nextMXact;
|
MultiXactId nextMXact;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is the oldest valid value among all the OldestMemberMXactId[] and
|
* This is the oldest valid value among all the OldestMemberMXactId[] and
|
||||||
@ -2168,17 +2168,17 @@ GetOldestMultiXactId(void)
|
|||||||
|
|
||||||
typedef struct mxtruncinfo
|
typedef struct mxtruncinfo
|
||||||
{
|
{
|
||||||
int earliestExistingPage;
|
int earliestExistingPage;
|
||||||
} mxtruncinfo;
|
} mxtruncinfo;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SlruScanDirectory callback
|
* SlruScanDirectory callback
|
||||||
* This callback determines the earliest existing page number.
|
* This callback determines the earliest existing page number.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
SlruScanDirCbFindEarliest(SlruCtl ctl, char *filename, int segpage, void *data)
|
SlruScanDirCbFindEarliest(SlruCtl ctl, char *filename, int segpage, void *data)
|
||||||
{
|
{
|
||||||
mxtruncinfo *trunc = (mxtruncinfo *) data;
|
mxtruncinfo *trunc = (mxtruncinfo *) data;
|
||||||
|
|
||||||
if (trunc->earliestExistingPage == -1 ||
|
if (trunc->earliestExistingPage == -1 ||
|
||||||
ctl->PagePrecedes(segpage, trunc->earliestExistingPage))
|
ctl->PagePrecedes(segpage, trunc->earliestExistingPage))
|
||||||
@ -2186,7 +2186,7 @@ SlruScanDirCbFindEarliest(SlruCtl ctl, char *filename, int segpage, void *data)
|
|||||||
trunc->earliestExistingPage = segpage;
|
trunc->earliestExistingPage = segpage;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false; /* keep going */
|
return false; /* keep going */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2200,16 +2200,16 @@ SlruScanDirCbFindEarliest(SlruCtl ctl, char *filename, int segpage, void *data)
|
|||||||
void
|
void
|
||||||
TruncateMultiXact(MultiXactId oldestMXact)
|
TruncateMultiXact(MultiXactId oldestMXact)
|
||||||
{
|
{
|
||||||
MultiXactOffset oldestOffset;
|
MultiXactOffset oldestOffset;
|
||||||
mxtruncinfo trunc;
|
mxtruncinfo trunc;
|
||||||
MultiXactId earliest;
|
MultiXactId earliest;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note we can't just plow ahead with the truncation; it's possible that
|
* Note we can't just plow ahead with the truncation; it's possible that
|
||||||
* there are no segments to truncate, which is a problem because we are
|
* there are no segments to truncate, which is a problem because we are
|
||||||
* going to attempt to read the offsets page to determine where to truncate
|
* going to attempt to read the offsets page to determine where to
|
||||||
* the members SLRU. So we first scan the directory to determine the
|
* truncate the members SLRU. So we first scan the directory to determine
|
||||||
* earliest offsets page number that we can read without error.
|
* the earliest offsets page number that we can read without error.
|
||||||
*/
|
*/
|
||||||
trunc.earliestExistingPage = -1;
|
trunc.earliestExistingPage = -1;
|
||||||
SlruScanDirectory(MultiXactOffsetCtl, SlruScanDirCbFindEarliest, &trunc);
|
SlruScanDirectory(MultiXactOffsetCtl, SlruScanDirCbFindEarliest, &trunc);
|
||||||
@ -2220,9 +2220,9 @@ TruncateMultiXact(MultiXactId oldestMXact)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* First, compute the safe truncation point for MultiXactMember.
|
* First, compute the safe truncation point for MultiXactMember. This is
|
||||||
* This is the starting offset of the multixact we were passed
|
* the starting offset of the multixact we were passed as MultiXactOffset
|
||||||
* as MultiXactOffset cutoff.
|
* cutoff.
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
int pageno;
|
int pageno;
|
||||||
@ -2380,7 +2380,7 @@ multixact_redo(XLogRecPtr lsn, XLogRecord *record)
|
|||||||
else if (info == XLOG_MULTIXACT_CREATE_ID)
|
else if (info == XLOG_MULTIXACT_CREATE_ID)
|
||||||
{
|
{
|
||||||
xl_multixact_create *xlrec =
|
xl_multixact_create *xlrec =
|
||||||
(xl_multixact_create *) XLogRecGetData(record);
|
(xl_multixact_create *) XLogRecGetData(record);
|
||||||
TransactionId max_xid;
|
TransactionId max_xid;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -2427,12 +2427,12 @@ pg_get_multixact_members(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
MultiXactMember *members;
|
MultiXactMember *members;
|
||||||
int nmembers;
|
int nmembers;
|
||||||
int iter;
|
int iter;
|
||||||
} mxact;
|
} mxact;
|
||||||
MultiXactId mxid = PG_GETARG_UINT32(0);
|
MultiXactId mxid = PG_GETARG_UINT32(0);
|
||||||
mxact *multi;
|
mxact *multi;
|
||||||
FuncCallContext *funccxt;
|
FuncCallContext *funccxt;
|
||||||
|
|
||||||
if (mxid < FirstMultiXactId)
|
if (mxid < FirstMultiXactId)
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* <parentTLI> <switchpoint> <reason>
|
* <parentTLI> <switchpoint> <reason>
|
||||||
*
|
*
|
||||||
* parentTLI ID of the parent timeline
|
* parentTLI ID of the parent timeline
|
||||||
* switchpoint XLogRecPtr of the WAL position where the switch happened
|
* switchpoint XLogRecPtr of the WAL position where the switch happened
|
||||||
* reason human-readable explanation of why the timeline was changed
|
* reason human-readable explanation of why the timeline was changed
|
||||||
*
|
*
|
||||||
* The fields are separated by tabs. Lines beginning with # are comments, and
|
* The fields are separated by tabs. Lines beginning with # are comments, and
|
||||||
@ -49,7 +49,7 @@ restoreTimeLineHistoryFiles(TimeLineID begin, TimeLineID end)
|
|||||||
{
|
{
|
||||||
char path[MAXPGPATH];
|
char path[MAXPGPATH];
|
||||||
char histfname[MAXFNAMELEN];
|
char histfname[MAXFNAMELEN];
|
||||||
TimeLineID tli;
|
TimeLineID tli;
|
||||||
|
|
||||||
for (tli = begin; tli < end; tli++)
|
for (tli = begin; tli < end; tli++)
|
||||||
{
|
{
|
||||||
@ -179,8 +179,8 @@ readTimeLineHistory(TimeLineID targetTLI)
|
|||||||
errhint("Timeline IDs must be less than child timeline's ID.")));
|
errhint("Timeline IDs must be less than child timeline's ID.")));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create one more entry for the "tip" of the timeline, which has no
|
* Create one more entry for the "tip" of the timeline, which has no entry
|
||||||
* entry in the history file.
|
* in the history file.
|
||||||
*/
|
*/
|
||||||
entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
|
entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
|
||||||
entry->tli = targetTLI;
|
entry->tli = targetTLI;
|
||||||
@ -418,7 +418,7 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Prefer link() to rename() here just to be really sure that we don't
|
* Prefer link() to rename() here just to be really sure that we don't
|
||||||
* overwrite an existing file. However, there shouldn't be one, so
|
* overwrite an existing file. However, there shouldn't be one, so
|
||||||
* rename() is an acceptable substitute except for the truly paranoid.
|
* rename() is an acceptable substitute except for the truly paranoid.
|
||||||
*/
|
*/
|
||||||
#if HAVE_WORKING_LINK
|
#if HAVE_WORKING_LINK
|
||||||
@ -530,7 +530,7 @@ writeTimeLineHistoryFile(TimeLineID tli, char *content, int size)
|
|||||||
bool
|
bool
|
||||||
tliInHistory(TimeLineID tli, List *expectedTLEs)
|
tliInHistory(TimeLineID tli, List *expectedTLEs)
|
||||||
{
|
{
|
||||||
ListCell *cell;
|
ListCell *cell;
|
||||||
|
|
||||||
foreach(cell, expectedTLEs)
|
foreach(cell, expectedTLEs)
|
||||||
{
|
{
|
||||||
@ -548,11 +548,12 @@ tliInHistory(TimeLineID tli, List *expectedTLEs)
|
|||||||
TimeLineID
|
TimeLineID
|
||||||
tliOfPointInHistory(XLogRecPtr ptr, List *history)
|
tliOfPointInHistory(XLogRecPtr ptr, List *history)
|
||||||
{
|
{
|
||||||
ListCell *cell;
|
ListCell *cell;
|
||||||
|
|
||||||
foreach(cell, history)
|
foreach(cell, history)
|
||||||
{
|
{
|
||||||
TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
|
TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
|
||||||
|
|
||||||
if ((XLogRecPtrIsInvalid(tle->begin) || tle->begin <= ptr) &&
|
if ((XLogRecPtrIsInvalid(tle->begin) || tle->begin <= ptr) &&
|
||||||
(XLogRecPtrIsInvalid(tle->end) || ptr < tle->end))
|
(XLogRecPtrIsInvalid(tle->end) || ptr < tle->end))
|
||||||
{
|
{
|
||||||
@ -563,7 +564,7 @@ tliOfPointInHistory(XLogRecPtr ptr, List *history)
|
|||||||
|
|
||||||
/* shouldn't happen. */
|
/* shouldn't happen. */
|
||||||
elog(ERROR, "timeline history was not contiguous");
|
elog(ERROR, "timeline history was not contiguous");
|
||||||
return 0; /* keep compiler quiet */
|
return 0; /* keep compiler quiet */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -579,7 +580,7 @@ tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI)
|
|||||||
|
|
||||||
if (nextTLI)
|
if (nextTLI)
|
||||||
*nextTLI = 0;
|
*nextTLI = 0;
|
||||||
foreach (cell, history)
|
foreach(cell, history)
|
||||||
{
|
{
|
||||||
TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
|
TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
|
||||||
|
|
||||||
@ -592,5 +593,5 @@ tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI)
|
|||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errmsg("requested timeline %u is not in this server's history",
|
(errmsg("requested timeline %u is not in this server's history",
|
||||||
tli)));
|
tli)));
|
||||||
return InvalidXLogRecPtr; /* keep compiler quiet */
|
return InvalidXLogRecPtr; /* keep compiler quiet */
|
||||||
}
|
}
|
||||||
|
@ -1024,8 +1024,8 @@ RecordTransactionCommit(void)
|
|||||||
*
|
*
|
||||||
* It's safe to change the delayChkpt flag of our own backend without
|
* It's safe to change the delayChkpt flag of our own backend without
|
||||||
* holding the ProcArrayLock, since we're the only one modifying it.
|
* holding the ProcArrayLock, since we're the only one modifying it.
|
||||||
* This makes checkpoint's determination of which xacts are delayChkpt a
|
* This makes checkpoint's determination of which xacts are delayChkpt
|
||||||
* bit fuzzy, but it doesn't matter.
|
* a bit fuzzy, but it doesn't matter.
|
||||||
*/
|
*/
|
||||||
START_CRIT_SECTION();
|
START_CRIT_SECTION();
|
||||||
MyPgXact->delayChkpt = true;
|
MyPgXact->delayChkpt = true;
|
||||||
@ -4683,12 +4683,11 @@ xact_redo_commit_internal(TransactionId xid, XLogRecPtr lsn,
|
|||||||
* from the template database, and then commit the transaction. If we
|
* from the template database, and then commit the transaction. If we
|
||||||
* crash after all the files have been copied but before the commit, you
|
* crash after all the files have been copied but before the commit, you
|
||||||
* have files in the data directory without an entry in pg_database. To
|
* have files in the data directory without an entry in pg_database. To
|
||||||
* minimize the window
|
* minimize the window for that, we use ForceSyncCommit() to rush the
|
||||||
* for that, we use ForceSyncCommit() to rush the commit record to disk as
|
* commit record to disk as quick as possible. We have the same window
|
||||||
* quick as possible. We have the same window during recovery, and forcing
|
* during recovery, and forcing an XLogFlush() (which updates
|
||||||
* an XLogFlush() (which updates minRecoveryPoint during recovery) helps
|
* minRecoveryPoint during recovery) helps to reduce that problem window,
|
||||||
* to reduce that problem window, for any user that requested
|
* for any user that requested ForceSyncCommit().
|
||||||
* ForceSyncCommit().
|
|
||||||
*/
|
*/
|
||||||
if (XactCompletionForceSyncCommit(xinfo))
|
if (XactCompletionForceSyncCommit(xinfo))
|
||||||
XLogFlush(lsn);
|
XLogFlush(lsn);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -87,9 +87,9 @@ RestoreArchivedFile(char *path, const char *xlogfname,
|
|||||||
* of log segments that weren't yet transferred to the archive.
|
* of log segments that weren't yet transferred to the archive.
|
||||||
*
|
*
|
||||||
* Notice that we don't actually overwrite any files when we copy back
|
* Notice that we don't actually overwrite any files when we copy back
|
||||||
* from archive because the restore_command may inadvertently
|
* from archive because the restore_command may inadvertently restore
|
||||||
* restore inappropriate xlogs, or they may be corrupt, so we may wish to
|
* inappropriate xlogs, or they may be corrupt, so we may wish to fallback
|
||||||
* fallback to the segments remaining in current XLOGDIR later. The
|
* to the segments remaining in current XLOGDIR later. The
|
||||||
* copy-from-archive filename is always the same, ensuring that we don't
|
* copy-from-archive filename is always the same, ensuring that we don't
|
||||||
* run out of disk space on long recoveries.
|
* run out of disk space on long recoveries.
|
||||||
*/
|
*/
|
||||||
@ -433,19 +433,20 @@ KeepFileRestoredFromArchive(char *path, char *xlogfname)
|
|||||||
|
|
||||||
if (stat(xlogfpath, &statbuf) == 0)
|
if (stat(xlogfpath, &statbuf) == 0)
|
||||||
{
|
{
|
||||||
char oldpath[MAXPGPATH];
|
char oldpath[MAXPGPATH];
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
static unsigned int deletedcounter = 1;
|
static unsigned int deletedcounter = 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* On Windows, if another process (e.g a walsender process) holds
|
* On Windows, if another process (e.g a walsender process) holds the
|
||||||
* the file open in FILE_SHARE_DELETE mode, unlink will succeed,
|
* file open in FILE_SHARE_DELETE mode, unlink will succeed, but the
|
||||||
* but the file will still show up in directory listing until the
|
* file will still show up in directory listing until the last handle
|
||||||
* last handle is closed, and we cannot rename the new file in its
|
* is closed, and we cannot rename the new file in its place until
|
||||||
* place until that. To avoid that problem, rename the old file to
|
* that. To avoid that problem, rename the old file to a temporary
|
||||||
* a temporary name first. Use a counter to create a unique
|
* name first. Use a counter to create a unique filename, because the
|
||||||
* filename, because the same file might be restored from the
|
* same file might be restored from the archive multiple times, and a
|
||||||
* archive multiple times, and a walsender could still be holding
|
* walsender could still be holding onto an old deleted version of it.
|
||||||
* onto an old deleted version of it.
|
|
||||||
*/
|
*/
|
||||||
snprintf(oldpath, MAXPGPATH, "%s.deleted%u",
|
snprintf(oldpath, MAXPGPATH, "%s.deleted%u",
|
||||||
xlogfpath, deletedcounter++);
|
xlogfpath, deletedcounter++);
|
||||||
@ -474,17 +475,17 @@ KeepFileRestoredFromArchive(char *path, char *xlogfname)
|
|||||||
path, xlogfpath)));
|
path, xlogfpath)));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create .done file forcibly to prevent the restored segment from
|
* Create .done file forcibly to prevent the restored segment from being
|
||||||
* being archived again later.
|
* archived again later.
|
||||||
*/
|
*/
|
||||||
XLogArchiveForceDone(xlogfname);
|
XLogArchiveForceDone(xlogfname);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the existing file was replaced, since walsenders might have it
|
* If the existing file was replaced, since walsenders might have it open,
|
||||||
* open, request them to reload a currently-open segment. This is only
|
* request them to reload a currently-open segment. This is only required
|
||||||
* required for WAL segments, walsenders don't hold other files open, but
|
* for WAL segments, walsenders don't hold other files open, but there's
|
||||||
* there's no harm in doing this too often, and we don't know what kind
|
* no harm in doing this too often, and we don't know what kind of a file
|
||||||
* of a file we're dealing with here.
|
* we're dealing with here.
|
||||||
*/
|
*/
|
||||||
if (reload)
|
if (reload)
|
||||||
WalSndRqstFileReload();
|
WalSndRqstFileReload();
|
||||||
|
@ -545,8 +545,8 @@ pg_xlog_location_diff(PG_FUNCTION_ARGS)
|
|||||||
* XXX: this won't handle values higher than 2^63 correctly.
|
* XXX: this won't handle values higher than 2^63 correctly.
|
||||||
*/
|
*/
|
||||||
result = DatumGetNumeric(DirectFunctionCall2(numeric_sub,
|
result = DatumGetNumeric(DirectFunctionCall2(numeric_sub,
|
||||||
DirectFunctionCall1(int8_numeric, Int64GetDatum((int64) bytes1)),
|
DirectFunctionCall1(int8_numeric, Int64GetDatum((int64) bytes1)),
|
||||||
DirectFunctionCall1(int8_numeric, Int64GetDatum((int64) bytes2))));
|
DirectFunctionCall1(int8_numeric, Int64GetDatum((int64) bytes2))));
|
||||||
|
|
||||||
PG_RETURN_NUMERIC(result);
|
PG_RETURN_NUMERIC(result);
|
||||||
}
|
}
|
||||||
@ -584,7 +584,7 @@ pg_backup_start_time(PG_FUNCTION_ARGS)
|
|||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode_for_file_access(),
|
(errcode_for_file_access(),
|
||||||
errmsg("could not read file \"%s\": %m",
|
errmsg("could not read file \"%s\": %m",
|
||||||
BACKUP_LABEL_FILE)));
|
BACKUP_LABEL_FILE)));
|
||||||
PG_RETURN_NULL();
|
PG_RETURN_NULL();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,13 +602,13 @@ pg_backup_start_time(PG_FUNCTION_ARGS)
|
|||||||
if (ferror(lfp))
|
if (ferror(lfp))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode_for_file_access(),
|
(errcode_for_file_access(),
|
||||||
errmsg("could not read file \"%s\": %m", BACKUP_LABEL_FILE)));
|
errmsg("could not read file \"%s\": %m", BACKUP_LABEL_FILE)));
|
||||||
|
|
||||||
/* Close the backup label file. */
|
/* Close the backup label file. */
|
||||||
if (FreeFile(lfp))
|
if (FreeFile(lfp))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode_for_file_access(),
|
(errcode_for_file_access(),
|
||||||
errmsg("could not close file \"%s\": %m", BACKUP_LABEL_FILE)));
|
errmsg("could not close file \"%s\": %m", BACKUP_LABEL_FILE)));
|
||||||
|
|
||||||
if (strlen(backup_start_time) == 0)
|
if (strlen(backup_start_time) == 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -221,9 +221,9 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
|
|||||||
targetRecOff = RecPtr % XLOG_BLCKSZ;
|
targetRecOff = RecPtr % XLOG_BLCKSZ;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read the page containing the record into state->readBuf. Request
|
* Read the page containing the record into state->readBuf. Request enough
|
||||||
* enough byte to cover the whole record header, or at least the part of
|
* byte to cover the whole record header, or at least the part of it that
|
||||||
* it that fits on the same page.
|
* fits on the same page.
|
||||||
*/
|
*/
|
||||||
readOff = ReadPageInternal(state,
|
readOff = ReadPageInternal(state,
|
||||||
targetPagePtr,
|
targetPagePtr,
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
extern int optind;
|
extern int optind;
|
||||||
extern char *optarg;
|
extern char *optarg;
|
||||||
|
|
||||||
uint32 bootstrap_data_checksum_version = 0; /* No checksum */
|
uint32 bootstrap_data_checksum_version = 0; /* No checksum */
|
||||||
|
|
||||||
|
|
||||||
#define ALLOC(t, c) ((t *) calloc((unsigned)(c), sizeof(t)))
|
#define ALLOC(t, c) ((t *) calloc((unsigned)(c), sizeof(t)))
|
||||||
@ -67,7 +67,7 @@ static void cleanup(void);
|
|||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
AuxProcType MyAuxProcType = NotAnAuxProcess; /* declared in miscadmin.h */
|
AuxProcType MyAuxProcType = NotAnAuxProcess; /* declared in miscadmin.h */
|
||||||
|
|
||||||
Relation boot_reldesc; /* current relation descriptor */
|
Relation boot_reldesc; /* current relation descriptor */
|
||||||
|
|
||||||
@ -389,7 +389,7 @@ AuxiliaryProcessMain(int argc, char *argv[])
|
|||||||
/*
|
/*
|
||||||
* Assign the ProcSignalSlot for an auxiliary process. Since it
|
* Assign the ProcSignalSlot for an auxiliary process. Since it
|
||||||
* doesn't have a BackendId, the slot is statically allocated based on
|
* doesn't have a BackendId, the slot is statically allocated based on
|
||||||
* the auxiliary process type (MyAuxProcType). Backends use slots
|
* the auxiliary process type (MyAuxProcType). Backends use slots
|
||||||
* indexed in the range from 1 to MaxBackends (inclusive), so we use
|
* indexed in the range from 1 to MaxBackends (inclusive), so we use
|
||||||
* MaxBackends + AuxProcType + 1 as the index of the slot for an
|
* MaxBackends + AuxProcType + 1 as the index of the slot for an
|
||||||
* auxiliary process.
|
* auxiliary process.
|
||||||
|
@ -3419,7 +3419,7 @@ aclcheck_error_col(AclResult aclerr, AclObjectKind objectkind,
|
|||||||
void
|
void
|
||||||
aclcheck_error_type(AclResult aclerr, Oid typeOid)
|
aclcheck_error_type(AclResult aclerr, Oid typeOid)
|
||||||
{
|
{
|
||||||
Oid element_type = get_element_type(typeOid);
|
Oid element_type = get_element_type(typeOid);
|
||||||
|
|
||||||
aclcheck_error(aclerr, ACL_KIND_TYPE, format_type_be(element_type ? element_type : typeOid));
|
aclcheck_error(aclerr, ACL_KIND_TYPE, format_type_be(element_type ? element_type : typeOid));
|
||||||
}
|
}
|
||||||
|
@ -335,7 +335,7 @@ GetNewOid(Relation relation)
|
|||||||
* This is exported separately because there are cases where we want to use
|
* This is exported separately because there are cases where we want to use
|
||||||
* an index that will not be recognized by RelationGetOidIndex: TOAST tables
|
* an index that will not be recognized by RelationGetOidIndex: TOAST tables
|
||||||
* have indexes that are usable, but have multiple columns and are on
|
* have indexes that are usable, but have multiple columns and are on
|
||||||
* ordinary columns rather than a true OID column. This code will work
|
* ordinary columns rather than a true OID column. This code will work
|
||||||
* anyway, so long as the OID is the index's first column. The caller must
|
* anyway, so long as the OID is the index's first column. The caller must
|
||||||
* pass in the actual heap attnum of the OID column, however.
|
* pass in the actual heap attnum of the OID column, however.
|
||||||
*
|
*
|
||||||
|
@ -198,7 +198,7 @@ static void
|
|||||||
deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel,
|
deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keep track of objects for event triggers, if necessary.
|
* Keep track of objects for event triggers, if necessary.
|
||||||
|
@ -98,7 +98,7 @@ static void StoreRelCheck(Relation rel, char *ccname, Node *expr,
|
|||||||
bool is_validated, bool is_local, int inhcount,
|
bool is_validated, bool is_local, int inhcount,
|
||||||
bool is_no_inherit, bool is_internal);
|
bool is_no_inherit, bool is_internal);
|
||||||
static void StoreConstraints(Relation rel, List *cooked_constraints,
|
static void StoreConstraints(Relation rel, List *cooked_constraints,
|
||||||
bool is_internal);
|
bool is_internal);
|
||||||
static bool MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
|
static bool MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
|
||||||
bool allow_merge, bool is_local,
|
bool allow_merge, bool is_local,
|
||||||
bool is_no_inherit);
|
bool is_no_inherit);
|
||||||
@ -870,6 +870,7 @@ AddNewRelationTuple(Relation pg_class_desc,
|
|||||||
* that will do.
|
* that will do.
|
||||||
*/
|
*/
|
||||||
new_rel_reltup->relfrozenxid = RecentXmin;
|
new_rel_reltup->relfrozenxid = RecentXmin;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Similarly, initialize the minimum Multixact to the first value that
|
* Similarly, initialize the minimum Multixact to the first value that
|
||||||
* could possibly be stored in tuples in the table. Running
|
* could possibly be stored in tuples in the table. Running
|
||||||
@ -1915,10 +1916,10 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
|
|||||||
/*
|
/*
|
||||||
* Post creation hook for attribute defaults.
|
* Post creation hook for attribute defaults.
|
||||||
*
|
*
|
||||||
* XXX. ALTER TABLE ALTER COLUMN SET/DROP DEFAULT is implemented
|
* XXX. ALTER TABLE ALTER COLUMN SET/DROP DEFAULT is implemented with a
|
||||||
* with a couple of deletion/creation of the attribute's default entry,
|
* couple of deletion/creation of the attribute's default entry, so the
|
||||||
* so the callee should check existence of an older version of this
|
* callee should check existence of an older version of this entry if it
|
||||||
* entry if it needs to distinguish.
|
* needs to distinguish.
|
||||||
*/
|
*/
|
||||||
InvokeObjectPostCreateHookArg(AttrDefaultRelationId,
|
InvokeObjectPostCreateHookArg(AttrDefaultRelationId,
|
||||||
RelationGetRelid(rel), attnum, is_internal);
|
RelationGetRelid(rel), attnum, is_internal);
|
||||||
@ -2018,7 +2019,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
|
|||||||
is_local, /* conislocal */
|
is_local, /* conislocal */
|
||||||
inhcount, /* coninhcount */
|
inhcount, /* coninhcount */
|
||||||
is_no_inherit, /* connoinherit */
|
is_no_inherit, /* connoinherit */
|
||||||
is_internal); /* internally constructed? */
|
is_internal); /* internally constructed? */
|
||||||
|
|
||||||
pfree(ccbin);
|
pfree(ccbin);
|
||||||
pfree(ccsrc);
|
pfree(ccsrc);
|
||||||
|
@ -293,9 +293,10 @@ RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode,
|
|||||||
Oid namespaceId;
|
Oid namespaceId;
|
||||||
|
|
||||||
namespaceId = LookupExplicitNamespace(relation->schemaname, missing_ok);
|
namespaceId = LookupExplicitNamespace(relation->schemaname, missing_ok);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For missing_ok, allow a non-existant schema name to
|
* For missing_ok, allow a non-existant schema name to
|
||||||
* return InvalidOid.
|
* return InvalidOid.
|
||||||
*/
|
*/
|
||||||
if (namespaceId != myTempNamespace)
|
if (namespaceId != myTempNamespace)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -29,7 +29,7 @@ void
|
|||||||
RunObjectPostCreateHook(Oid classId, Oid objectId, int subId,
|
RunObjectPostCreateHook(Oid classId, Oid objectId, int subId,
|
||||||
bool is_internal)
|
bool is_internal)
|
||||||
{
|
{
|
||||||
ObjectAccessPostCreate pc_arg;
|
ObjectAccessPostCreate pc_arg;
|
||||||
|
|
||||||
/* caller should check, but just in case... */
|
/* caller should check, but just in case... */
|
||||||
Assert(object_access_hook != NULL);
|
Assert(object_access_hook != NULL);
|
||||||
@ -37,9 +37,9 @@ RunObjectPostCreateHook(Oid classId, Oid objectId, int subId,
|
|||||||
memset(&pc_arg, 0, sizeof(ObjectAccessPostCreate));
|
memset(&pc_arg, 0, sizeof(ObjectAccessPostCreate));
|
||||||
pc_arg.is_internal = is_internal;
|
pc_arg.is_internal = is_internal;
|
||||||
|
|
||||||
(*object_access_hook)(OAT_POST_CREATE,
|
(*object_access_hook) (OAT_POST_CREATE,
|
||||||
classId, objectId, subId,
|
classId, objectId, subId,
|
||||||
(void *) &pc_arg);
|
(void *) &pc_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -51,7 +51,7 @@ void
|
|||||||
RunObjectDropHook(Oid classId, Oid objectId, int subId,
|
RunObjectDropHook(Oid classId, Oid objectId, int subId,
|
||||||
int dropflags)
|
int dropflags)
|
||||||
{
|
{
|
||||||
ObjectAccessDrop drop_arg;
|
ObjectAccessDrop drop_arg;
|
||||||
|
|
||||||
/* caller should check, but just in case... */
|
/* caller should check, but just in case... */
|
||||||
Assert(object_access_hook != NULL);
|
Assert(object_access_hook != NULL);
|
||||||
@ -59,9 +59,9 @@ RunObjectDropHook(Oid classId, Oid objectId, int subId,
|
|||||||
memset(&drop_arg, 0, sizeof(ObjectAccessDrop));
|
memset(&drop_arg, 0, sizeof(ObjectAccessDrop));
|
||||||
drop_arg.dropflags = dropflags;
|
drop_arg.dropflags = dropflags;
|
||||||
|
|
||||||
(*object_access_hook)(OAT_DROP,
|
(*object_access_hook) (OAT_DROP,
|
||||||
classId, objectId, subId,
|
classId, objectId, subId,
|
||||||
(void *) &drop_arg);
|
(void *) &drop_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -73,7 +73,7 @@ void
|
|||||||
RunObjectPostAlterHook(Oid classId, Oid objectId, int subId,
|
RunObjectPostAlterHook(Oid classId, Oid objectId, int subId,
|
||||||
Oid auxiliaryId, bool is_internal)
|
Oid auxiliaryId, bool is_internal)
|
||||||
{
|
{
|
||||||
ObjectAccessPostAlter pa_arg;
|
ObjectAccessPostAlter pa_arg;
|
||||||
|
|
||||||
/* caller should check, but just in case... */
|
/* caller should check, but just in case... */
|
||||||
Assert(object_access_hook != NULL);
|
Assert(object_access_hook != NULL);
|
||||||
@ -82,9 +82,9 @@ RunObjectPostAlterHook(Oid classId, Oid objectId, int subId,
|
|||||||
pa_arg.auxiliary_id = auxiliaryId;
|
pa_arg.auxiliary_id = auxiliaryId;
|
||||||
pa_arg.is_internal = is_internal;
|
pa_arg.is_internal = is_internal;
|
||||||
|
|
||||||
(*object_access_hook)(OAT_POST_ALTER,
|
(*object_access_hook) (OAT_POST_ALTER,
|
||||||
classId, objectId, subId,
|
classId, objectId, subId,
|
||||||
(void *) &pa_arg);
|
(void *) &pa_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -95,7 +95,7 @@ RunObjectPostAlterHook(Oid classId, Oid objectId, int subId,
|
|||||||
bool
|
bool
|
||||||
RunNamespaceSearchHook(Oid objectId, bool ereport_on_violation)
|
RunNamespaceSearchHook(Oid objectId, bool ereport_on_violation)
|
||||||
{
|
{
|
||||||
ObjectAccessNamespaceSearch ns_arg;
|
ObjectAccessNamespaceSearch ns_arg;
|
||||||
|
|
||||||
/* caller should check, but just in case... */
|
/* caller should check, but just in case... */
|
||||||
Assert(object_access_hook != NULL);
|
Assert(object_access_hook != NULL);
|
||||||
@ -104,9 +104,9 @@ RunNamespaceSearchHook(Oid objectId, bool ereport_on_violation)
|
|||||||
ns_arg.ereport_on_violation = ereport_on_violation;
|
ns_arg.ereport_on_violation = ereport_on_violation;
|
||||||
ns_arg.result = true;
|
ns_arg.result = true;
|
||||||
|
|
||||||
(*object_access_hook)(OAT_NAMESPACE_SEARCH,
|
(*object_access_hook) (OAT_NAMESPACE_SEARCH,
|
||||||
NamespaceRelationId, objectId, 0,
|
NamespaceRelationId, objectId, 0,
|
||||||
(void *) &ns_arg);
|
(void *) &ns_arg);
|
||||||
|
|
||||||
return ns_arg.result;
|
return ns_arg.result;
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ RunFunctionExecuteHook(Oid objectId)
|
|||||||
/* caller should check, but just in case... */
|
/* caller should check, but just in case... */
|
||||||
Assert(object_access_hook != NULL);
|
Assert(object_access_hook != NULL);
|
||||||
|
|
||||||
(*object_access_hook)(OAT_FUNCTION_EXECUTE,
|
(*object_access_hook) (OAT_FUNCTION_EXECUTE,
|
||||||
ProcedureRelationId, objectId, 0,
|
ProcedureRelationId, objectId, 0,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
@ -94,10 +94,11 @@ typedef struct
|
|||||||
AttrNumber attnum_owner; /* attnum of owner field */
|
AttrNumber attnum_owner; /* attnum of owner field */
|
||||||
AttrNumber attnum_acl; /* attnum of acl field */
|
AttrNumber attnum_acl; /* attnum of acl field */
|
||||||
AclObjectKind acl_kind; /* ACL_KIND_* of this object type */
|
AclObjectKind acl_kind; /* ACL_KIND_* of this object type */
|
||||||
bool is_nsp_name_unique; /* can the nsp/name combination (or name
|
bool is_nsp_name_unique; /* can the nsp/name combination (or
|
||||||
* alone, if there's no namespace) be
|
* name alone, if there's no
|
||||||
* considered an unique identifier for an
|
* namespace) be considered an unique
|
||||||
* object of this class? */
|
* identifier for an object of this
|
||||||
|
* class? */
|
||||||
} ObjectPropertyType;
|
} ObjectPropertyType;
|
||||||
|
|
||||||
static ObjectPropertyType ObjectProperty[] =
|
static ObjectPropertyType ObjectProperty[] =
|
||||||
@ -1443,7 +1444,7 @@ get_object_property_data(Oid class_id)
|
|||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errmsg_internal("unrecognized class id: %u", class_id)));
|
(errmsg_internal("unrecognized class id: %u", class_id)));
|
||||||
|
|
||||||
return NULL; /* keep MSC compiler happy */
|
return NULL; /* keep MSC compiler happy */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1463,14 +1464,14 @@ get_catalog_object_by_oid(Relation catalog, Oid objectId)
|
|||||||
if (oidCacheId > 0)
|
if (oidCacheId > 0)
|
||||||
{
|
{
|
||||||
tuple = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objectId));
|
tuple = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objectId));
|
||||||
if (!HeapTupleIsValid(tuple)) /* should not happen */
|
if (!HeapTupleIsValid(tuple)) /* should not happen */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Oid oidIndexId = get_object_oid_index(classId);
|
Oid oidIndexId = get_object_oid_index(classId);
|
||||||
SysScanDesc scan;
|
SysScanDesc scan;
|
||||||
ScanKeyData skey;
|
ScanKeyData skey;
|
||||||
|
|
||||||
Assert(OidIsValid(oidIndexId));
|
Assert(OidIsValid(oidIndexId));
|
||||||
|
|
||||||
@ -2127,7 +2128,7 @@ getObjectDescription(const ObjectAddress *object)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OCLASS_EVENT_TRIGGER:
|
case OCLASS_EVENT_TRIGGER:
|
||||||
{
|
{
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
|
|
||||||
@ -2137,7 +2138,7 @@ getObjectDescription(const ObjectAddress *object)
|
|||||||
elog(ERROR, "cache lookup failed for event trigger %u",
|
elog(ERROR, "cache lookup failed for event trigger %u",
|
||||||
object->objectId);
|
object->objectId);
|
||||||
appendStringInfo(&buffer, _("event trigger %s"),
|
appendStringInfo(&buffer, _("event trigger %s"),
|
||||||
NameStr(((Form_pg_event_trigger) GETSTRUCT(tup))->evtname));
|
NameStr(((Form_pg_event_trigger) GETSTRUCT(tup))->evtname));
|
||||||
ReleaseSysCache(tup);
|
ReleaseSysCache(tup);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2355,22 +2356,22 @@ pg_identify_object(PG_FUNCTION_ARGS)
|
|||||||
RelationGetDescr(catalog), &isnull);
|
RelationGetDescr(catalog), &isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
elog(ERROR, "invalid null namespace in object %u/%u/%d",
|
elog(ERROR, "invalid null namespace in object %u/%u/%d",
|
||||||
address.classId, address.objectId, address.objectSubId);
|
address.classId, address.objectId, address.objectSubId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We only return the object name if it can be used (together
|
* We only return the object name if it can be used (together with
|
||||||
* with the schema name, if any) as an unique identifier.
|
* the schema name, if any) as an unique identifier.
|
||||||
*/
|
*/
|
||||||
if (get_object_namensp_unique(address.classId))
|
if (get_object_namensp_unique(address.classId))
|
||||||
{
|
{
|
||||||
nameAttnum = get_object_attnum_name(address.classId);
|
nameAttnum = get_object_attnum_name(address.classId);
|
||||||
if (nameAttnum != InvalidAttrNumber)
|
if (nameAttnum != InvalidAttrNumber)
|
||||||
{
|
{
|
||||||
Datum nameDatum;
|
Datum nameDatum;
|
||||||
|
|
||||||
nameDatum = heap_getattr(objtup, nameAttnum,
|
nameDatum = heap_getattr(objtup, nameAttnum,
|
||||||
RelationGetDescr(catalog), &isnull);
|
RelationGetDescr(catalog), &isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
elog(ERROR, "invalid null name in object %u/%u/%d",
|
elog(ERROR, "invalid null name in object %u/%u/%d",
|
||||||
address.classId, address.objectId, address.objectSubId);
|
address.classId, address.objectId, address.objectSubId);
|
||||||
@ -2389,7 +2390,7 @@ pg_identify_object(PG_FUNCTION_ARGS)
|
|||||||
/* schema name */
|
/* schema name */
|
||||||
if (OidIsValid(schema_oid))
|
if (OidIsValid(schema_oid))
|
||||||
{
|
{
|
||||||
const char *schema = quote_identifier(get_namespace_name(schema_oid));
|
const char *schema = quote_identifier(get_namespace_name(schema_oid));
|
||||||
|
|
||||||
values[1] = CStringGetTextDatum(schema);
|
values[1] = CStringGetTextDatum(schema);
|
||||||
nulls[1] = false;
|
nulls[1] = false;
|
||||||
@ -2622,7 +2623,7 @@ getConstraintTypeDescription(StringInfo buffer, Oid constroid)
|
|||||||
{
|
{
|
||||||
Relation constrRel;
|
Relation constrRel;
|
||||||
HeapTuple constrTup;
|
HeapTuple constrTup;
|
||||||
Form_pg_constraint constrForm;
|
Form_pg_constraint constrForm;
|
||||||
|
|
||||||
constrRel = heap_open(ConstraintRelationId, AccessShareLock);
|
constrRel = heap_open(ConstraintRelationId, AccessShareLock);
|
||||||
constrTup = get_catalog_object_by_oid(constrRel, constroid);
|
constrTup = get_catalog_object_by_oid(constrRel, constroid);
|
||||||
@ -2651,7 +2652,7 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid)
|
|||||||
Form_pg_proc procForm;
|
Form_pg_proc procForm;
|
||||||
|
|
||||||
procTup = SearchSysCache1(PROCOID,
|
procTup = SearchSysCache1(PROCOID,
|
||||||
ObjectIdGetDatum(procid));
|
ObjectIdGetDatum(procid));
|
||||||
if (!HeapTupleIsValid(procTup))
|
if (!HeapTupleIsValid(procTup))
|
||||||
elog(ERROR, "cache lookup failed for procedure %u", procid);
|
elog(ERROR, "cache lookup failed for procedure %u", procid);
|
||||||
procForm = (Form_pg_proc) GETSTRUCT(procTup);
|
procForm = (Form_pg_proc) GETSTRUCT(procTup);
|
||||||
@ -2683,7 +2684,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
getRelationIdentity(&buffer, object->objectId);
|
getRelationIdentity(&buffer, object->objectId);
|
||||||
if (object->objectSubId != 0)
|
if (object->objectSubId != 0)
|
||||||
{
|
{
|
||||||
char *attr;
|
char *attr;
|
||||||
|
|
||||||
attr = get_relid_attribute_name(object->objectId,
|
attr = get_relid_attribute_name(object->objectId,
|
||||||
object->objectSubId);
|
object->objectSubId);
|
||||||
@ -2718,8 +2719,8 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
castForm = (Form_pg_cast) GETSTRUCT(tup);
|
castForm = (Form_pg_cast) GETSTRUCT(tup);
|
||||||
|
|
||||||
appendStringInfo(&buffer, "(%s AS %s)",
|
appendStringInfo(&buffer, "(%s AS %s)",
|
||||||
format_type_be_qualified(castForm->castsource),
|
format_type_be_qualified(castForm->castsource),
|
||||||
format_type_be_qualified(castForm->casttarget));
|
format_type_be_qualified(castForm->casttarget));
|
||||||
|
|
||||||
heap_close(castRel, AccessShareLock);
|
heap_close(castRel, AccessShareLock);
|
||||||
break;
|
break;
|
||||||
@ -2729,7 +2730,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
{
|
{
|
||||||
HeapTuple collTup;
|
HeapTuple collTup;
|
||||||
Form_pg_collation coll;
|
Form_pg_collation coll;
|
||||||
char *schema;
|
char *schema;
|
||||||
|
|
||||||
collTup = SearchSysCache1(COLLOID,
|
collTup = SearchSysCache1(COLLOID,
|
||||||
ObjectIdGetDatum(object->objectId));
|
ObjectIdGetDatum(object->objectId));
|
||||||
@ -2740,7 +2741,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
schema = get_namespace_name(coll->collnamespace);
|
schema = get_namespace_name(coll->collnamespace);
|
||||||
appendStringInfoString(&buffer,
|
appendStringInfoString(&buffer,
|
||||||
quote_qualified_identifier(schema,
|
quote_qualified_identifier(schema,
|
||||||
NameStr(coll->collname)));
|
NameStr(coll->collname)));
|
||||||
ReleaseSysCache(collTup);
|
ReleaseSysCache(collTup);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2765,7 +2766,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ObjectAddress domain;
|
ObjectAddress domain;
|
||||||
|
|
||||||
domain.classId = TypeRelationId;
|
domain.classId = TypeRelationId;
|
||||||
domain.objectId = con->contypid;
|
domain.objectId = con->contypid;
|
||||||
@ -2849,7 +2850,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
object->objectId);
|
object->objectId);
|
||||||
langForm = (Form_pg_language) GETSTRUCT(langTup);
|
langForm = (Form_pg_language) GETSTRUCT(langTup);
|
||||||
appendStringInfo(&buffer, "%s",
|
appendStringInfo(&buffer, "%s",
|
||||||
quote_identifier(NameStr(langForm->lanname)));
|
quote_identifier(NameStr(langForm->lanname)));
|
||||||
ReleaseSysCache(langTup);
|
ReleaseSysCache(langTup);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2889,7 +2890,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
appendStringInfo(&buffer,
|
appendStringInfo(&buffer,
|
||||||
"%s",
|
"%s",
|
||||||
quote_qualified_identifier(schema,
|
quote_qualified_identifier(schema,
|
||||||
NameStr(opcForm->opcname)));
|
NameStr(opcForm->opcname)));
|
||||||
appendStringInfo(&buffer, " for %s",
|
appendStringInfo(&buffer, " for %s",
|
||||||
quote_identifier(NameStr(amForm->amname)));
|
quote_identifier(NameStr(amForm->amname)));
|
||||||
|
|
||||||
@ -2935,8 +2936,8 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
|
|
||||||
appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
|
appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
|
||||||
amopForm->amopstrategy,
|
amopForm->amopstrategy,
|
||||||
format_type_be_qualified(amopForm->amoplefttype),
|
format_type_be_qualified(amopForm->amoplefttype),
|
||||||
format_type_be_qualified(amopForm->amoprighttype),
|
format_type_be_qualified(amopForm->amoprighttype),
|
||||||
opfam.data);
|
opfam.data);
|
||||||
|
|
||||||
pfree(opfam.data);
|
pfree(opfam.data);
|
||||||
@ -2979,8 +2980,8 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
|
|
||||||
appendStringInfo(&buffer, "function %d (%s, %s) of %s",
|
appendStringInfo(&buffer, "function %d (%s, %s) of %s",
|
||||||
amprocForm->amprocnum,
|
amprocForm->amprocnum,
|
||||||
format_type_be_qualified(amprocForm->amproclefttype),
|
format_type_be_qualified(amprocForm->amproclefttype),
|
||||||
format_type_be_qualified(amprocForm->amprocrighttype),
|
format_type_be_qualified(amprocForm->amprocrighttype),
|
||||||
opfam.data);
|
opfam.data);
|
||||||
|
|
||||||
pfree(opfam.data);
|
pfree(opfam.data);
|
||||||
@ -3054,7 +3055,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
case OCLASS_TSPARSER:
|
case OCLASS_TSPARSER:
|
||||||
{
|
{
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
Form_pg_ts_parser formParser;
|
Form_pg_ts_parser formParser;
|
||||||
|
|
||||||
tup = SearchSysCache1(TSPARSEROID,
|
tup = SearchSysCache1(TSPARSEROID,
|
||||||
ObjectIdGetDatum(object->objectId));
|
ObjectIdGetDatum(object->objectId));
|
||||||
@ -3063,7 +3064,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
object->objectId);
|
object->objectId);
|
||||||
formParser = (Form_pg_ts_parser) GETSTRUCT(tup);
|
formParser = (Form_pg_ts_parser) GETSTRUCT(tup);
|
||||||
appendStringInfo(&buffer, "%s",
|
appendStringInfo(&buffer, "%s",
|
||||||
quote_identifier(NameStr(formParser->prsname)));
|
quote_identifier(NameStr(formParser->prsname)));
|
||||||
ReleaseSysCache(tup);
|
ReleaseSysCache(tup);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3071,7 +3072,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
case OCLASS_TSDICT:
|
case OCLASS_TSDICT:
|
||||||
{
|
{
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
Form_pg_ts_dict formDict;
|
Form_pg_ts_dict formDict;
|
||||||
|
|
||||||
tup = SearchSysCache1(TSDICTOID,
|
tup = SearchSysCache1(TSDICTOID,
|
||||||
ObjectIdGetDatum(object->objectId));
|
ObjectIdGetDatum(object->objectId));
|
||||||
@ -3080,7 +3081,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
object->objectId);
|
object->objectId);
|
||||||
formDict = (Form_pg_ts_dict) GETSTRUCT(tup);
|
formDict = (Form_pg_ts_dict) GETSTRUCT(tup);
|
||||||
appendStringInfo(&buffer, "%s",
|
appendStringInfo(&buffer, "%s",
|
||||||
quote_identifier(NameStr(formDict->dictname)));
|
quote_identifier(NameStr(formDict->dictname)));
|
||||||
ReleaseSysCache(tup);
|
ReleaseSysCache(tup);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3097,7 +3098,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
object->objectId);
|
object->objectId);
|
||||||
formTmpl = (Form_pg_ts_template) GETSTRUCT(tup);
|
formTmpl = (Form_pg_ts_template) GETSTRUCT(tup);
|
||||||
appendStringInfo(&buffer, "%s",
|
appendStringInfo(&buffer, "%s",
|
||||||
quote_identifier(NameStr(formTmpl->tmplname)));
|
quote_identifier(NameStr(formTmpl->tmplname)));
|
||||||
ReleaseSysCache(tup);
|
ReleaseSysCache(tup);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3121,7 +3122,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
|
|
||||||
case OCLASS_ROLE:
|
case OCLASS_ROLE:
|
||||||
{
|
{
|
||||||
char *username;
|
char *username;
|
||||||
|
|
||||||
username = GetUserNameFromId(object->objectId);
|
username = GetUserNameFromId(object->objectId);
|
||||||
appendStringInfo(&buffer, "%s",
|
appendStringInfo(&buffer, "%s",
|
||||||
@ -3229,11 +3230,11 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
|
|
||||||
appendStringInfo(&buffer,
|
appendStringInfo(&buffer,
|
||||||
"for role %s",
|
"for role %s",
|
||||||
quote_identifier(GetUserNameFromId(defacl->defaclrole)));
|
quote_identifier(GetUserNameFromId(defacl->defaclrole)));
|
||||||
|
|
||||||
if (OidIsValid(defacl->defaclnamespace))
|
if (OidIsValid(defacl->defaclnamespace))
|
||||||
{
|
{
|
||||||
char *schema;
|
char *schema;
|
||||||
|
|
||||||
schema = get_namespace_name(defacl->defaclnamespace);
|
schema = get_namespace_name(defacl->defaclnamespace);
|
||||||
appendStringInfo(&buffer,
|
appendStringInfo(&buffer,
|
||||||
@ -3291,7 +3292,7 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
object->objectId);
|
object->objectId);
|
||||||
trigForm = (Form_pg_event_trigger) GETSTRUCT(tup);
|
trigForm = (Form_pg_event_trigger) GETSTRUCT(tup);
|
||||||
appendStringInfo(&buffer, "%s",
|
appendStringInfo(&buffer, "%s",
|
||||||
quote_identifier(NameStr(trigForm->evtname)));
|
quote_identifier(NameStr(trigForm->evtname)));
|
||||||
ReleaseSysCache(tup);
|
ReleaseSysCache(tup);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -682,7 +682,7 @@ RenameConstraintById(Oid conId, const char *newname)
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
|
AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
|
||||||
Oid newNspId, bool isType, ObjectAddresses *objsMoved)
|
Oid newNspId, bool isType, ObjectAddresses *objsMoved)
|
||||||
{
|
{
|
||||||
Relation conRel;
|
Relation conRel;
|
||||||
ScanKeyData key[1];
|
ScanKeyData key[1];
|
||||||
@ -715,7 +715,7 @@ AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
|
|||||||
while (HeapTupleIsValid((tup = systable_getnext(scan))))
|
while (HeapTupleIsValid((tup = systable_getnext(scan))))
|
||||||
{
|
{
|
||||||
Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
|
Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
|
||||||
ObjectAddress thisobj;
|
ObjectAddress thisobj;
|
||||||
|
|
||||||
thisobj.classId = ConstraintRelationId;
|
thisobj.classId = ConstraintRelationId;
|
||||||
thisobj.objectId = HeapTupleGetOid(tup);
|
thisobj.objectId = HeapTupleGetOid(tup);
|
||||||
|
@ -180,7 +180,7 @@ AddEnumLabel(Oid enumTypeOid,
|
|||||||
const char *newVal,
|
const char *newVal,
|
||||||
const char *neighbor,
|
const char *neighbor,
|
||||||
bool newValIsAfter,
|
bool newValIsAfter,
|
||||||
bool skipIfExists)
|
bool skipIfExists)
|
||||||
{
|
{
|
||||||
Relation pg_enum;
|
Relation pg_enum;
|
||||||
Oid newOid;
|
Oid newOid;
|
||||||
|
@ -92,11 +92,11 @@ validOperatorName(const char *name)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For SQL standard compatibility, '+' and '-' cannot be the last char of a
|
* For SQL standard compatibility, '+' and '-' cannot be the last char of
|
||||||
* multi-char operator unless the operator contains chars that are not in
|
* a multi-char operator unless the operator contains chars that are not
|
||||||
* SQL operators. The idea is to lex '=-' as two operators, but not to
|
* in SQL operators. The idea is to lex '=-' as two operators, but not to
|
||||||
* forbid operator names like '?-' that could not be sequences of standard SQL
|
* forbid operator names like '?-' that could not be sequences of standard
|
||||||
* operators.
|
* SQL operators.
|
||||||
*/
|
*/
|
||||||
if (len > 1 &&
|
if (len > 1 &&
|
||||||
(name[len - 1] == '+' ||
|
(name[len - 1] == '+' ||
|
||||||
|
@ -406,7 +406,7 @@ ProcedureCreate(const char *procedureName,
|
|||||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||||
errmsg("cannot change return type of existing function"),
|
errmsg("cannot change return type of existing function"),
|
||||||
errhint("Use DROP FUNCTION %s first.",
|
errhint("Use DROP FUNCTION %s first.",
|
||||||
format_procedure(HeapTupleGetOid(oldtup)))));
|
format_procedure(HeapTupleGetOid(oldtup)))));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If it returns RECORD, check for possible change of record type
|
* If it returns RECORD, check for possible change of record type
|
||||||
@ -430,7 +430,7 @@ ProcedureCreate(const char *procedureName,
|
|||||||
errmsg("cannot change return type of existing function"),
|
errmsg("cannot change return type of existing function"),
|
||||||
errdetail("Row type defined by OUT parameters is different."),
|
errdetail("Row type defined by OUT parameters is different."),
|
||||||
errhint("Use DROP FUNCTION %s first.",
|
errhint("Use DROP FUNCTION %s first.",
|
||||||
format_procedure(HeapTupleGetOid(oldtup)))));
|
format_procedure(HeapTupleGetOid(oldtup)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -473,7 +473,7 @@ ProcedureCreate(const char *procedureName,
|
|||||||
errmsg("cannot change name of input parameter \"%s\"",
|
errmsg("cannot change name of input parameter \"%s\"",
|
||||||
old_arg_names[j]),
|
old_arg_names[j]),
|
||||||
errhint("Use DROP FUNCTION %s first.",
|
errhint("Use DROP FUNCTION %s first.",
|
||||||
format_procedure(HeapTupleGetOid(oldtup)))));
|
format_procedure(HeapTupleGetOid(oldtup)))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,7 +497,7 @@ ProcedureCreate(const char *procedureName,
|
|||||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||||
errmsg("cannot remove parameter defaults from existing function"),
|
errmsg("cannot remove parameter defaults from existing function"),
|
||||||
errhint("Use DROP FUNCTION %s first.",
|
errhint("Use DROP FUNCTION %s first.",
|
||||||
format_procedure(HeapTupleGetOid(oldtup)))));
|
format_procedure(HeapTupleGetOid(oldtup)))));
|
||||||
|
|
||||||
proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
|
proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
|
||||||
Anum_pg_proc_proargdefaults,
|
Anum_pg_proc_proargdefaults,
|
||||||
@ -524,7 +524,7 @@ ProcedureCreate(const char *procedureName,
|
|||||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||||
errmsg("cannot change data type of existing parameter default value"),
|
errmsg("cannot change data type of existing parameter default value"),
|
||||||
errhint("Use DROP FUNCTION %s first.",
|
errhint("Use DROP FUNCTION %s first.",
|
||||||
format_procedure(HeapTupleGetOid(oldtup)))));
|
format_procedure(HeapTupleGetOid(oldtup)))));
|
||||||
newlc = lnext(newlc);
|
newlc = lnext(newlc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1382,7 +1382,7 @@ shdepReassignOwned(List *roleids, Oid newrole)
|
|||||||
AlterEventTriggerOwner_oid(sdepForm->objid, newrole);
|
AlterEventTriggerOwner_oid(sdepForm->objid, newrole);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Generic alter owner cases */
|
/* Generic alter owner cases */
|
||||||
case CollationRelationId:
|
case CollationRelationId:
|
||||||
case ConversionRelationId:
|
case ConversionRelationId:
|
||||||
case OperatorRelationId:
|
case OperatorRelationId:
|
||||||
|
@ -505,13 +505,12 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record)
|
|||||||
smgrcreate(reln, MAIN_FORKNUM, true);
|
smgrcreate(reln, MAIN_FORKNUM, true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Before we perform the truncation, update minimum recovery point
|
* Before we perform the truncation, update minimum recovery point to
|
||||||
* to cover this WAL record. Once the relation is truncated, there's
|
* cover this WAL record. Once the relation is truncated, there's no
|
||||||
* no going back. The buffer manager enforces the WAL-first rule
|
* going back. The buffer manager enforces the WAL-first rule for
|
||||||
* for normal updates to relation files, so that the minimum recovery
|
* normal updates to relation files, so that the minimum recovery
|
||||||
* point is always updated before the corresponding change in the
|
* point is always updated before the corresponding change in the data
|
||||||
* data file is flushed to disk. We have to do the same manually
|
* file is flushed to disk. We have to do the same manually here.
|
||||||
* here.
|
|
||||||
*
|
*
|
||||||
* Doing this before the truncation means that if the truncation fails
|
* Doing this before the truncation means that if the truncation fails
|
||||||
* for some reason, you cannot start up the system even after restart,
|
* for some reason, you cannot start up the system even after restart,
|
||||||
|
@ -217,13 +217,13 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
|
|||||||
/*
|
/*
|
||||||
* Most of the argument-checking is done inside of AggregateCreate
|
* Most of the argument-checking is done inside of AggregateCreate
|
||||||
*/
|
*/
|
||||||
return AggregateCreate(aggName, /* aggregate name */
|
return AggregateCreate(aggName, /* aggregate name */
|
||||||
aggNamespace, /* namespace */
|
aggNamespace, /* namespace */
|
||||||
aggArgTypes, /* input data type(s) */
|
aggArgTypes, /* input data type(s) */
|
||||||
numArgs,
|
numArgs,
|
||||||
transfuncName, /* step function name */
|
transfuncName, /* step function name */
|
||||||
finalfuncName, /* final function name */
|
finalfuncName, /* final function name */
|
||||||
sortoperatorName, /* sort operator name */
|
sortoperatorName, /* sort operator name */
|
||||||
transTypeId, /* transition data type */
|
transTypeId, /* transition data type */
|
||||||
initval); /* initial condition */
|
initval); /* initial condition */
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
#include "utils/tqual.h"
|
#include "utils/tqual.h"
|
||||||
|
|
||||||
|
|
||||||
static Oid AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid);
|
static Oid AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Raise an error to the effect that an object of the given name is already
|
* Raise an error to the effect that an object of the given name is already
|
||||||
@ -71,7 +71,7 @@ static Oid AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid);
|
|||||||
static void
|
static void
|
||||||
report_name_conflict(Oid classId, const char *name)
|
report_name_conflict(Oid classId, const char *name)
|
||||||
{
|
{
|
||||||
char *msgfmt;
|
char *msgfmt;
|
||||||
|
|
||||||
switch (classId)
|
switch (classId)
|
||||||
{
|
{
|
||||||
@ -100,7 +100,7 @@ report_name_conflict(Oid classId, const char *name)
|
|||||||
static void
|
static void
|
||||||
report_namespace_conflict(Oid classId, const char *name, Oid nspOid)
|
report_namespace_conflict(Oid classId, const char *name, Oid nspOid)
|
||||||
{
|
{
|
||||||
char *msgfmt;
|
char *msgfmt;
|
||||||
|
|
||||||
Assert(OidIsValid(nspOid));
|
Assert(OidIsValid(nspOid));
|
||||||
|
|
||||||
@ -221,10 +221,10 @@ AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for duplicate name (more friendly than unique-index failure).
|
* Check for duplicate name (more friendly than unique-index failure).
|
||||||
* Since this is just a friendliness check, we can just skip it in cases
|
* Since this is just a friendliness check, we can just skip it in cases
|
||||||
* where there isn't suitable support.
|
* where there isn't suitable support.
|
||||||
*/
|
*/
|
||||||
if (classId == ProcedureRelationId)
|
if (classId == ProcedureRelationId)
|
||||||
{
|
{
|
||||||
Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(oldtup);
|
Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(oldtup);
|
||||||
@ -355,9 +355,9 @@ ExecRenameStmt(RenameStmt *stmt)
|
|||||||
case OBJECT_TSPARSER:
|
case OBJECT_TSPARSER:
|
||||||
case OBJECT_TSTEMPLATE:
|
case OBJECT_TSTEMPLATE:
|
||||||
{
|
{
|
||||||
ObjectAddress address;
|
ObjectAddress address;
|
||||||
Relation catalog;
|
Relation catalog;
|
||||||
Relation relation;
|
Relation relation;
|
||||||
|
|
||||||
address = get_object_address(stmt->renameType,
|
address = get_object_address(stmt->renameType,
|
||||||
stmt->object, stmt->objarg,
|
stmt->object, stmt->objarg,
|
||||||
@ -377,7 +377,7 @@ ExecRenameStmt(RenameStmt *stmt)
|
|||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized rename stmt type: %d",
|
elog(ERROR, "unrecognized rename stmt type: %d",
|
||||||
(int) stmt->renameType);
|
(int) stmt->renameType);
|
||||||
return InvalidOid; /* keep compiler happy */
|
return InvalidOid; /* keep compiler happy */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,7 +699,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
|
|||||||
return AlterEventTriggerOwner(strVal(linitial(stmt->object)),
|
return AlterEventTriggerOwner(strVal(linitial(stmt->object)),
|
||||||
newowner);
|
newowner);
|
||||||
|
|
||||||
/* Generic cases */
|
/* Generic cases */
|
||||||
case OBJECT_AGGREGATE:
|
case OBJECT_AGGREGATE:
|
||||||
case OBJECT_COLLATION:
|
case OBJECT_COLLATION:
|
||||||
case OBJECT_CONVERSION:
|
case OBJECT_CONVERSION:
|
||||||
@ -716,7 +716,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
|
|||||||
Relation catalog;
|
Relation catalog;
|
||||||
Relation relation;
|
Relation relation;
|
||||||
Oid classId;
|
Oid classId;
|
||||||
ObjectAddress address;
|
ObjectAddress address;
|
||||||
|
|
||||||
address = get_object_address(stmt->objectType,
|
address = get_object_address(stmt->objectType,
|
||||||
stmt->object,
|
stmt->object,
|
||||||
@ -804,13 +804,13 @@ AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId)
|
|||||||
/* Superusers can bypass permission checks */
|
/* Superusers can bypass permission checks */
|
||||||
if (!superuser())
|
if (!superuser())
|
||||||
{
|
{
|
||||||
AclObjectKind aclkind = get_object_aclkind(classId);
|
AclObjectKind aclkind = get_object_aclkind(classId);
|
||||||
|
|
||||||
/* must be owner */
|
/* must be owner */
|
||||||
if (!has_privs_of_role(GetUserId(), old_ownerId))
|
if (!has_privs_of_role(GetUserId(), old_ownerId))
|
||||||
{
|
{
|
||||||
char *objname;
|
char *objname;
|
||||||
char namebuf[NAMEDATALEN];
|
char namebuf[NAMEDATALEN];
|
||||||
|
|
||||||
if (Anum_name != InvalidAttrNumber)
|
if (Anum_name != InvalidAttrNumber)
|
||||||
{
|
{
|
||||||
@ -833,7 +833,7 @@ AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId)
|
|||||||
/* New owner must have CREATE privilege on namespace */
|
/* New owner must have CREATE privilege on namespace */
|
||||||
if (OidIsValid(namespaceId))
|
if (OidIsValid(namespaceId))
|
||||||
{
|
{
|
||||||
AclResult aclresult;
|
AclResult aclresult;
|
||||||
|
|
||||||
aclresult = pg_namespace_aclcheck(namespaceId, new_ownerId,
|
aclresult = pg_namespace_aclcheck(namespaceId, new_ownerId,
|
||||||
ACL_CREATE);
|
ACL_CREATE);
|
||||||
@ -861,7 +861,7 @@ AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId)
|
|||||||
Anum_acl, RelationGetDescr(rel), &isnull);
|
Anum_acl, RelationGetDescr(rel), &isnull);
|
||||||
if (!isnull)
|
if (!isnull)
|
||||||
{
|
{
|
||||||
Acl *newAcl;
|
Acl *newAcl;
|
||||||
|
|
||||||
newAcl = aclnewowner(DatumGetAclP(datum),
|
newAcl = aclnewowner(DatumGetAclP(datum),
|
||||||
old_ownerId, new_ownerId);
|
old_ownerId, new_ownerId);
|
||||||
|
@ -1147,7 +1147,7 @@ asyncQueueUnregister(void)
|
|||||||
|
|
||||||
Assert(listenChannels == NIL); /* else caller error */
|
Assert(listenChannels == NIL); /* else caller error */
|
||||||
|
|
||||||
if (!amRegisteredListener) /* nothing to do */
|
if (!amRegisteredListener) /* nothing to do */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LWLockAcquire(AsyncQueueLock, LW_SHARED);
|
LWLockAcquire(AsyncQueueLock, LW_SHARED);
|
||||||
@ -1519,7 +1519,7 @@ AtAbort_Notify(void)
|
|||||||
/*
|
/*
|
||||||
* If we LISTEN but then roll back the transaction after PreCommit_Notify,
|
* If we LISTEN but then roll back the transaction after PreCommit_Notify,
|
||||||
* we have registered as a listener but have not made any entry in
|
* we have registered as a listener but have not made any entry in
|
||||||
* listenChannels. In that case, deregister again.
|
* listenChannels. In that case, deregister again.
|
||||||
*/
|
*/
|
||||||
if (amRegisteredListener && listenChannels == NIL)
|
if (amRegisteredListener && listenChannels == NIL)
|
||||||
asyncQueueUnregister();
|
asyncQueueUnregister();
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user