mirror of
https://github.com/postgres/postgres.git
synced 2025-04-20 00:42:27 +03:00
Pre-beta mechanical code beautification.
Run pgindent, pgperltidy, and reformat-dat-files. I manually fixed a couple of comments that pgindent uglified.
This commit is contained in:
parent
93909599cd
commit
23e7b38bfe
@ -21,8 +21,7 @@ diag("Test::More::VERSION: $Test::More::VERSION");
|
|||||||
diag("Time::HiRes::VERSION: $Time::HiRes::VERSION");
|
diag("Time::HiRes::VERSION: $Time::HiRes::VERSION");
|
||||||
|
|
||||||
# Check that if prove is using msys perl it is for an msys target
|
# Check that if prove is using msys perl it is for an msys target
|
||||||
ok(($ENV{__CONFIG_HOST_OS__} || "") eq 'msys',
|
ok( ($ENV{__CONFIG_HOST_OS__} || "") eq 'msys',
|
||||||
"Msys perl used for correct target")
|
"Msys perl used for correct target") if $Config{osname} eq 'msys';
|
||||||
if $Config{osname} eq 'msys';
|
|
||||||
ok(1);
|
ok(1);
|
||||||
done_testing();
|
done_testing();
|
||||||
|
@ -1402,17 +1402,17 @@ check_tuple_attribute(HeapCheckContext *ctx)
|
|||||||
cmid = TOAST_COMPRESS_METHOD(&toast_pointer);
|
cmid = TOAST_COMPRESS_METHOD(&toast_pointer);
|
||||||
switch (cmid)
|
switch (cmid)
|
||||||
{
|
{
|
||||||
/* List of all valid compression method IDs */
|
/* List of all valid compression method IDs */
|
||||||
case TOAST_PGLZ_COMPRESSION_ID:
|
case TOAST_PGLZ_COMPRESSION_ID:
|
||||||
case TOAST_LZ4_COMPRESSION_ID:
|
case TOAST_LZ4_COMPRESSION_ID:
|
||||||
valid = true;
|
valid = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Recognized but invalid compression method ID */
|
/* Recognized but invalid compression method ID */
|
||||||
case TOAST_INVALID_COMPRESSION_ID:
|
case TOAST_INVALID_COMPRESSION_ID:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Intentionally no default here */
|
/* Intentionally no default here */
|
||||||
}
|
}
|
||||||
if (!valid)
|
if (!valid)
|
||||||
report_corruption(ctx,
|
report_corruption(ctx,
|
||||||
|
@ -37,13 +37,13 @@ typedef struct bbsink_shell
|
|||||||
FILE *pipe;
|
FILE *pipe;
|
||||||
} bbsink_shell;
|
} bbsink_shell;
|
||||||
|
|
||||||
void _PG_init(void);
|
void _PG_init(void);
|
||||||
|
|
||||||
static void *shell_check_detail(char *target, char *target_detail);
|
static void *shell_check_detail(char *target, char *target_detail);
|
||||||
static bbsink *shell_get_sink(bbsink *next_sink, void *detail_arg);
|
static bbsink *shell_get_sink(bbsink *next_sink, void *detail_arg);
|
||||||
|
|
||||||
static void bbsink_shell_begin_archive(bbsink *sink,
|
static void bbsink_shell_begin_archive(bbsink *sink,
|
||||||
const char *archive_name);
|
const char *archive_name);
|
||||||
static void bbsink_shell_archive_contents(bbsink *sink, size_t len);
|
static void bbsink_shell_archive_contents(bbsink *sink, size_t len);
|
||||||
static void bbsink_shell_end_archive(bbsink *sink);
|
static void bbsink_shell_end_archive(bbsink *sink);
|
||||||
static void bbsink_shell_begin_manifest(bbsink *sink);
|
static void bbsink_shell_begin_manifest(bbsink *sink);
|
||||||
@ -101,7 +101,7 @@ shell_check_detail(char *target, char *target_detail)
|
|||||||
{
|
{
|
||||||
if (shell_required_role[0] != '\0')
|
if (shell_required_role[0] != '\0')
|
||||||
{
|
{
|
||||||
Oid roleid;
|
Oid roleid;
|
||||||
|
|
||||||
StartTransactionCommand();
|
StartTransactionCommand();
|
||||||
roleid = get_role_oid(shell_required_role, true);
|
roleid = get_role_oid(shell_required_role, true);
|
||||||
@ -125,8 +125,8 @@ static bbsink *
|
|||||||
shell_get_sink(bbsink *next_sink, void *detail_arg)
|
shell_get_sink(bbsink *next_sink, void *detail_arg)
|
||||||
{
|
{
|
||||||
bbsink_shell *sink;
|
bbsink_shell *sink;
|
||||||
bool has_detail_escape = false;
|
bool has_detail_escape = false;
|
||||||
char *c;
|
char *c;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up the bbsink.
|
* Set up the bbsink.
|
||||||
@ -171,15 +171,15 @@ shell_get_sink(bbsink *next_sink, void *detail_arg)
|
|||||||
/*
|
/*
|
||||||
* Since we're passing the string provided by the user to popen(), it will
|
* Since we're passing the string provided by the user to popen(), it will
|
||||||
* be interpreted by the shell, which is a potential security
|
* be interpreted by the shell, which is a potential security
|
||||||
* vulnerability, since the user invoking this module is not necessarily
|
* vulnerability, since the user invoking this module is not necessarily a
|
||||||
* a superuser. To stay out of trouble, we must disallow any shell
|
* superuser. To stay out of trouble, we must disallow any shell
|
||||||
* metacharacters here; to be conservative and keep things simple, we
|
* metacharacters here; to be conservative and keep things simple, we
|
||||||
* allow only alphanumerics.
|
* allow only alphanumerics.
|
||||||
*/
|
*/
|
||||||
if (sink->target_detail != NULL)
|
if (sink->target_detail != NULL)
|
||||||
{
|
{
|
||||||
char *d;
|
char *d;
|
||||||
bool scary = false;
|
bool scary = false;
|
||||||
|
|
||||||
for (d = sink->target_detail; *d != '\0'; ++d)
|
for (d = sink->target_detail; *d != '\0'; ++d)
|
||||||
{
|
{
|
||||||
@ -210,7 +210,7 @@ static char *
|
|||||||
shell_construct_command(char *base_command, const char *filename,
|
shell_construct_command(char *base_command, const char *filename,
|
||||||
char *target_detail)
|
char *target_detail)
|
||||||
{
|
{
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
char *c;
|
char *c;
|
||||||
|
|
||||||
initStringInfo(&buf);
|
initStringInfo(&buf);
|
||||||
@ -271,7 +271,7 @@ shell_construct_command(char *base_command, const char *filename,
|
|||||||
static void
|
static void
|
||||||
shell_finish_command(bbsink_shell *sink)
|
shell_finish_command(bbsink_shell *sink)
|
||||||
{
|
{
|
||||||
int pclose_rc;
|
int pclose_rc;
|
||||||
|
|
||||||
/* There should be a command running. */
|
/* There should be a command running. */
|
||||||
Assert(sink->current_command != NULL);
|
Assert(sink->current_command != NULL);
|
||||||
@ -335,9 +335,8 @@ shell_send_data(bbsink_shell *sink, size_t len)
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The error we're about to throw would shut down the command
|
* The error we're about to throw would shut down the command
|
||||||
* anyway, but we may get a more meaningful error message by
|
* anyway, but we may get a more meaningful error message by doing
|
||||||
* doing this. If not, we'll fall through to the generic error
|
* this. If not, we'll fall through to the generic error below.
|
||||||
* below.
|
|
||||||
*/
|
*/
|
||||||
shell_finish_command(sink);
|
shell_finish_command(sink);
|
||||||
errno = EPIPE;
|
errno = EPIPE;
|
||||||
|
@ -20,11 +20,12 @@ my $node = PostgreSQL::Test::Cluster->new('primary');
|
|||||||
|
|
||||||
# Make sure pg_hba.conf is set up to allow connections from backupuser.
|
# Make sure pg_hba.conf is set up to allow connections from backupuser.
|
||||||
# This is only needed on Windows machines that don't use UNIX sockets.
|
# This is only needed on Windows machines that don't use UNIX sockets.
|
||||||
$node->init('allows_streaming' => 1,
|
$node->init(
|
||||||
'auth_extra' => [ '--create-role', 'backupuser' ]);
|
'allows_streaming' => 1,
|
||||||
|
'auth_extra' => [ '--create-role', 'backupuser' ]);
|
||||||
|
|
||||||
$node->append_conf('postgresql.conf',
|
$node->append_conf('postgresql.conf',
|
||||||
"shared_preload_libraries = 'basebackup_to_shell'");
|
"shared_preload_libraries = 'basebackup_to_shell'");
|
||||||
$node->start;
|
$node->start;
|
||||||
$node->safe_psql('postgres', 'CREATE USER backupuser REPLICATION');
|
$node->safe_psql('postgres', 'CREATE USER backupuser REPLICATION');
|
||||||
$node->safe_psql('postgres', 'CREATE ROLE trustworthy');
|
$node->safe_psql('postgres', 'CREATE ROLE trustworthy');
|
||||||
@ -41,61 +42,61 @@ my @pg_basebackup_cmd = (@pg_basebackup_defs, '-U', 'backupuser', '-Xfetch');
|
|||||||
|
|
||||||
# Can't use this module without setting basebackup_to_shell.command.
|
# Can't use this module without setting basebackup_to_shell.command.
|
||||||
$node->command_fails_like(
|
$node->command_fails_like(
|
||||||
[ @pg_basebackup_cmd, '--target', 'shell' ],
|
[ @pg_basebackup_cmd, '--target', 'shell' ],
|
||||||
qr/shell command for backup is not configured/,
|
qr/shell command for backup is not configured/,
|
||||||
'fails if basebackup_to_shell.command is not set');
|
'fails if basebackup_to_shell.command is not set');
|
||||||
|
|
||||||
# Configure basebackup_to_shell.command and reload the configuation file.
|
# Configure basebackup_to_shell.command and reload the configuation file.
|
||||||
my $backup_path = PostgreSQL::Test::Utils::tempdir;
|
my $backup_path = PostgreSQL::Test::Utils::tempdir;
|
||||||
my $escaped_backup_path = $backup_path;
|
my $escaped_backup_path = $backup_path;
|
||||||
$escaped_backup_path =~ s{\\}{\\\\}g if ($PostgreSQL::Test::Utils::windows_os);
|
$escaped_backup_path =~ s{\\}{\\\\}g
|
||||||
|
if ($PostgreSQL::Test::Utils::windows_os);
|
||||||
my $shell_command =
|
my $shell_command =
|
||||||
$PostgreSQL::Test::Utils::windows_os
|
$PostgreSQL::Test::Utils::windows_os
|
||||||
? qq{$gzip --fast > "$escaped_backup_path\\\\%f.gz"}
|
? qq{$gzip --fast > "$escaped_backup_path\\\\%f.gz"}
|
||||||
: qq{$gzip --fast > "$escaped_backup_path/%f.gz"};
|
: qq{$gzip --fast > "$escaped_backup_path/%f.gz"};
|
||||||
$node->append_conf('postgresql.conf',
|
$node->append_conf('postgresql.conf',
|
||||||
"basebackup_to_shell.command='$shell_command'");
|
"basebackup_to_shell.command='$shell_command'");
|
||||||
$node->reload();
|
$node->reload();
|
||||||
|
|
||||||
# Should work now.
|
# Should work now.
|
||||||
$node->command_ok(
|
$node->command_ok(
|
||||||
[ @pg_basebackup_cmd, '--target', 'shell' ],
|
[ @pg_basebackup_cmd, '--target', 'shell' ],
|
||||||
'backup with no detail: pg_basebackup');
|
'backup with no detail: pg_basebackup');
|
||||||
verify_backup('', $backup_path, "backup with no detail");
|
verify_backup('', $backup_path, "backup with no detail");
|
||||||
|
|
||||||
# Should fail with a detail.
|
# Should fail with a detail.
|
||||||
$node->command_fails_like(
|
$node->command_fails_like(
|
||||||
[ @pg_basebackup_cmd, '--target', 'shell:foo' ],
|
[ @pg_basebackup_cmd, '--target', 'shell:foo' ],
|
||||||
qr/a target detail is not permitted because the configured command does not include %d/,
|
qr/a target detail is not permitted because the configured command does not include %d/,
|
||||||
'fails if detail provided without %d');
|
'fails if detail provided without %d');
|
||||||
|
|
||||||
# Reconfigure to restrict access and require a detail.
|
# Reconfigure to restrict access and require a detail.
|
||||||
$shell_command =
|
$shell_command =
|
||||||
$PostgreSQL::Test::Utils::windows_os
|
$PostgreSQL::Test::Utils::windows_os
|
||||||
? qq{$gzip --fast > "$escaped_backup_path\\\\%d.%f.gz"}
|
? qq{$gzip --fast > "$escaped_backup_path\\\\%d.%f.gz"}
|
||||||
: qq{$gzip --fast > "$escaped_backup_path/%d.%f.gz"};
|
: qq{$gzip --fast > "$escaped_backup_path/%d.%f.gz"};
|
||||||
$node->append_conf('postgresql.conf',
|
$node->append_conf('postgresql.conf',
|
||||||
"basebackup_to_shell.command='$shell_command'");
|
"basebackup_to_shell.command='$shell_command'");
|
||||||
$node->append_conf('postgresql.conf',
|
$node->append_conf('postgresql.conf',
|
||||||
"basebackup_to_shell.required_role='trustworthy'");
|
"basebackup_to_shell.required_role='trustworthy'");
|
||||||
$node->reload();
|
$node->reload();
|
||||||
|
|
||||||
# Should fail due to lack of permission.
|
# Should fail due to lack of permission.
|
||||||
$node->command_fails_like(
|
$node->command_fails_like(
|
||||||
[ @pg_basebackup_cmd, '--target', 'shell' ],
|
[ @pg_basebackup_cmd, '--target', 'shell' ],
|
||||||
qr/permission denied to use basebackup_to_shell/,
|
qr/permission denied to use basebackup_to_shell/,
|
||||||
'fails if required_role not granted');
|
'fails if required_role not granted');
|
||||||
|
|
||||||
# Should fail due to lack of a detail.
|
# Should fail due to lack of a detail.
|
||||||
$node->safe_psql('postgres', 'GRANT trustworthy TO backupuser');
|
$node->safe_psql('postgres', 'GRANT trustworthy TO backupuser');
|
||||||
$node->command_fails_like(
|
$node->command_fails_like(
|
||||||
[ @pg_basebackup_cmd, '--target', 'shell' ],
|
[ @pg_basebackup_cmd, '--target', 'shell' ],
|
||||||
qr/a target detail is required because the configured command includes %d/,
|
qr/a target detail is required because the configured command includes %d/,
|
||||||
'fails if %d is present and detail not given');
|
'fails if %d is present and detail not given');
|
||||||
|
|
||||||
# Should work.
|
# Should work.
|
||||||
$node->command_ok(
|
$node->command_ok([ @pg_basebackup_cmd, '--target', 'shell:bar' ],
|
||||||
[ @pg_basebackup_cmd, '--target', 'shell:bar' ],
|
|
||||||
'backup with detail: pg_basebackup');
|
'backup with detail: pg_basebackup');
|
||||||
verify_backup('bar.', $backup_path, "backup with detail");
|
verify_backup('bar.', $backup_path, "backup with detail");
|
||||||
|
|
||||||
@ -105,30 +106,34 @@ sub verify_backup
|
|||||||
{
|
{
|
||||||
my ($prefix, $backup_dir, $test_name) = @_;
|
my ($prefix, $backup_dir, $test_name) = @_;
|
||||||
|
|
||||||
ok(-f "$backup_dir/${prefix}backup_manifest.gz",
|
ok( -f "$backup_dir/${prefix}backup_manifest.gz",
|
||||||
"$test_name: backup_manifest.gz was created");
|
"$test_name: backup_manifest.gz was created");
|
||||||
ok(-f "$backup_dir/${prefix}base.tar.gz",
|
ok( -f "$backup_dir/${prefix}base.tar.gz",
|
||||||
"$test_name: base.tar.gz was created");
|
"$test_name: base.tar.gz was created");
|
||||||
|
|
||||||
SKIP: {
|
SKIP:
|
||||||
|
{
|
||||||
my $tar = $ENV{TAR};
|
my $tar = $ENV{TAR};
|
||||||
skip "no tar program available", 1 if (!defined $tar || $tar eq '');
|
skip "no tar program available", 1 if (!defined $tar || $tar eq '');
|
||||||
|
|
||||||
# Decompress.
|
# Decompress.
|
||||||
system_or_bail($gzip, '-d',
|
system_or_bail($gzip, '-d',
|
||||||
$backup_dir . '/' . $prefix . 'backup_manifest.gz');
|
$backup_dir . '/' . $prefix . 'backup_manifest.gz');
|
||||||
system_or_bail($gzip, '-d',
|
system_or_bail($gzip, '-d',
|
||||||
$backup_dir . '/' . $prefix . 'base.tar.gz');
|
$backup_dir . '/' . $prefix . 'base.tar.gz');
|
||||||
|
|
||||||
# Untar.
|
# Untar.
|
||||||
my $extract_path = PostgreSQL::Test::Utils::tempdir;
|
my $extract_path = PostgreSQL::Test::Utils::tempdir;
|
||||||
system_or_bail($tar, 'xf', $backup_dir . '/' . $prefix . 'base.tar',
|
system_or_bail($tar, 'xf', $backup_dir . '/' . $prefix . 'base.tar',
|
||||||
'-C', $extract_path);
|
'-C', $extract_path);
|
||||||
|
|
||||||
# Verify.
|
# Verify.
|
||||||
$node->command_ok([ 'pg_verifybackup', '-n',
|
$node->command_ok(
|
||||||
'-m', "${backup_dir}/${prefix}backup_manifest",
|
[
|
||||||
'-e', $extract_path ],
|
'pg_verifybackup', '-n',
|
||||||
"$test_name: backup verifies ok");
|
'-m', "${backup_dir}/${prefix}backup_manifest",
|
||||||
|
'-e', $extract_path
|
||||||
|
],
|
||||||
|
"$test_name: backup verifies ok");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,8 @@
|
|||||||
|
|
||||||
PG_MODULE_MAGIC;
|
PG_MODULE_MAGIC;
|
||||||
|
|
||||||
void _PG_init(void);
|
void _PG_init(void);
|
||||||
void _PG_archive_module_init(ArchiveModuleCallbacks *cb);
|
void _PG_archive_module_init(ArchiveModuleCallbacks *cb);
|
||||||
|
|
||||||
static char *archive_directory = NULL;
|
static char *archive_directory = NULL;
|
||||||
static MemoryContext basic_archive_context;
|
static MemoryContext basic_archive_context;
|
||||||
@ -102,8 +102,8 @@ check_archive_directory(char **newval, void **extra, GucSource source)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* The default value is an empty string, so we have to accept that value.
|
* The default value is an empty string, so we have to accept that value.
|
||||||
* Our check_configured callback also checks for this and prevents archiving
|
* Our check_configured callback also checks for this and prevents
|
||||||
* from proceeding if it is still empty.
|
* archiving from proceeding if it is still empty.
|
||||||
*/
|
*/
|
||||||
if (*newval == NULL || *newval[0] == '\0')
|
if (*newval == NULL || *newval[0] == '\0')
|
||||||
return true;
|
return true;
|
||||||
@ -119,7 +119,7 @@ check_archive_directory(char **newval, void **extra, GucSource source)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do a basic sanity check that the specified archive directory exists. It
|
* Do a basic sanity check that the specified archive directory exists. It
|
||||||
* could be removed at some point in the future, so we still need to be
|
* could be removed at some point in the future, so we still need to be
|
||||||
* prepared for it not to exist in the actual archiving logic.
|
* prepared for it not to exist in the actual archiving logic.
|
||||||
*/
|
*/
|
||||||
@ -155,18 +155,19 @@ basic_archive_file(const char *file, const char *path)
|
|||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We run basic_archive_file_internal() in our own memory context so that we
|
* We run basic_archive_file_internal() in our own memory context so that
|
||||||
* can easily reset it during error recovery (thus avoiding memory leaks).
|
* we can easily reset it during error recovery (thus avoiding memory
|
||||||
|
* leaks).
|
||||||
*/
|
*/
|
||||||
oldcontext = MemoryContextSwitchTo(basic_archive_context);
|
oldcontext = MemoryContextSwitchTo(basic_archive_context);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since the archiver operates at the bottom of the exception stack, ERRORs
|
* Since the archiver operates at the bottom of the exception stack,
|
||||||
* turn into FATALs and cause the archiver process to restart. However,
|
* ERRORs turn into FATALs and cause the archiver process to restart.
|
||||||
* using ereport(ERROR, ...) when there are problems is easy to code and
|
* However, using ereport(ERROR, ...) when there are problems is easy to
|
||||||
* maintain. Therefore, we create our own exception handler to catch ERRORs
|
* code and maintain. Therefore, we create our own exception handler to
|
||||||
* and return false instead of restarting the archiver whenever there is a
|
* catch ERRORs and return false instead of restarting the archiver
|
||||||
* failure.
|
* whenever there is a failure.
|
||||||
*/
|
*/
|
||||||
if (sigsetjmp(local_sigjmp_buf, 1) != 0)
|
if (sigsetjmp(local_sigjmp_buf, 1) != 0)
|
||||||
{
|
{
|
||||||
@ -228,14 +229,14 @@ basic_archive_file_internal(const char *file, const char *path)
|
|||||||
snprintf(destination, MAXPGPATH, "%s/%s", archive_directory, file);
|
snprintf(destination, MAXPGPATH, "%s/%s", archive_directory, file);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* First, check if the file has already been archived. If it already exists
|
* First, check if the file has already been archived. If it already
|
||||||
* and has the same contents as the file we're trying to archive, we can
|
* exists and has the same contents as the file we're trying to archive,
|
||||||
* return success (after ensuring the file is persisted to disk). This
|
* we can return success (after ensuring the file is persisted to disk).
|
||||||
* scenario is possible if the server crashed after archiving the file but
|
* This scenario is possible if the server crashed after archiving the
|
||||||
* before renaming its .ready file to .done.
|
* file but before renaming its .ready file to .done.
|
||||||
*
|
*
|
||||||
* If the archive file already exists but has different contents, something
|
* If the archive file already exists but has different contents,
|
||||||
* might be wrong, so we just fail.
|
* something might be wrong, so we just fail.
|
||||||
*/
|
*/
|
||||||
if (stat(destination, &st) == 0)
|
if (stat(destination, &st) == 0)
|
||||||
{
|
{
|
||||||
@ -274,8 +275,8 @@ basic_archive_file_internal(const char *file, const char *path)
|
|||||||
archive_directory, "archtemp", file, MyProcPid, epoch);
|
archive_directory, "archtemp", file, MyProcPid, epoch);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy the file to its temporary destination. Note that this will fail if
|
* Copy the file to its temporary destination. Note that this will fail
|
||||||
* temp already exists.
|
* if temp already exists.
|
||||||
*/
|
*/
|
||||||
copy_file(unconstify(char *, path), temp);
|
copy_file(unconstify(char *, path), temp);
|
||||||
|
|
||||||
@ -318,9 +319,9 @@ compare_files(const char *file1, const char *file2)
|
|||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
int nbytes = 0;
|
int nbytes = 0;
|
||||||
int buf1_len = 0;
|
int buf1_len = 0;
|
||||||
int buf2_len = 0;
|
int buf2_len = 0;
|
||||||
|
|
||||||
while (buf1_len < CMP_BUF_SIZE)
|
while (buf1_len < CMP_BUF_SIZE)
|
||||||
{
|
{
|
||||||
|
@ -53,8 +53,8 @@ gbt_boollt(const void *a, const void *b, FmgrInfo *flinfo)
|
|||||||
static int
|
static int
|
||||||
gbt_boolkey_cmp(const void *a, const void *b, FmgrInfo *flinfo)
|
gbt_boolkey_cmp(const void *a, const void *b, FmgrInfo *flinfo)
|
||||||
{
|
{
|
||||||
boolKEY *ia = (boolKEY *) (((const Nsrt *) a)->t);
|
boolKEY *ia = (boolKEY *) (((const Nsrt *) a)->t);
|
||||||
boolKEY *ib = (boolKEY *) (((const Nsrt *) b)->t);
|
boolKEY *ib = (boolKEY *) (((const Nsrt *) b)->t);
|
||||||
|
|
||||||
if (ia->lower == ib->lower)
|
if (ia->lower == ib->lower)
|
||||||
{
|
{
|
||||||
|
@ -99,7 +99,7 @@ hstore_to_plpython(PG_FUNCTION_ARGS)
|
|||||||
PyObject *key;
|
PyObject *key;
|
||||||
|
|
||||||
key = PLyUnicode_FromStringAndSize(HSTORE_KEY(entries, base, i),
|
key = PLyUnicode_FromStringAndSize(HSTORE_KEY(entries, base, i),
|
||||||
HSTORE_KEYLEN(entries, i));
|
HSTORE_KEYLEN(entries, i));
|
||||||
if (HSTORE_VALISNULL(entries, i))
|
if (HSTORE_VALISNULL(entries, i))
|
||||||
PyDict_SetItem(dict, key, Py_None);
|
PyDict_SetItem(dict, key, Py_None);
|
||||||
else
|
else
|
||||||
@ -107,7 +107,7 @@ hstore_to_plpython(PG_FUNCTION_ARGS)
|
|||||||
PyObject *value;
|
PyObject *value;
|
||||||
|
|
||||||
value = PLyUnicode_FromStringAndSize(HSTORE_VAL(entries, base, i),
|
value = PLyUnicode_FromStringAndSize(HSTORE_VAL(entries, base, i),
|
||||||
HSTORE_VALLEN(entries, i));
|
HSTORE_VALLEN(entries, i));
|
||||||
PyDict_SetItem(dict, key, value);
|
PyDict_SetItem(dict, key, value);
|
||||||
Py_XDECREF(value);
|
Py_XDECREF(value);
|
||||||
}
|
}
|
||||||
|
@ -63,12 +63,12 @@ brin_page_type(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
/* verify the special space has the expected size */
|
/* verify the special space has the expected size */
|
||||||
if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BrinSpecialSpace)))
|
if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BrinSpecialSpace)))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("input page is not a valid %s page", "BRIN"),
|
errmsg("input page is not a valid %s page", "BRIN"),
|
||||||
errdetail("Expected special size %d, got %d.",
|
errdetail("Expected special size %d, got %d.",
|
||||||
(int) MAXALIGN(sizeof(BrinSpecialSpace)),
|
(int) MAXALIGN(sizeof(BrinSpecialSpace)),
|
||||||
(int) PageGetSpecialSize(page))));
|
(int) PageGetSpecialSize(page))));
|
||||||
|
|
||||||
switch (BrinPageType(page))
|
switch (BrinPageType(page))
|
||||||
{
|
{
|
||||||
@ -103,12 +103,12 @@ verify_brin_page(bytea *raw_page, uint16 type, const char *strtype)
|
|||||||
|
|
||||||
/* verify the special space has the expected size */
|
/* verify the special space has the expected size */
|
||||||
if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BrinSpecialSpace)))
|
if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BrinSpecialSpace)))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("input page is not a valid %s page", "BRIN"),
|
errmsg("input page is not a valid %s page", "BRIN"),
|
||||||
errdetail("Expected special size %d, got %d.",
|
errdetail("Expected special size %d, got %d.",
|
||||||
(int) MAXALIGN(sizeof(BrinSpecialSpace)),
|
(int) MAXALIGN(sizeof(BrinSpecialSpace)),
|
||||||
(int) PageGetSpecialSize(page))));
|
(int) PageGetSpecialSize(page))));
|
||||||
|
|
||||||
/* verify the special space says this page is what we want */
|
/* verify the special space says this page is what we want */
|
||||||
if (BrinPageType(page) != type)
|
if (BrinPageType(page) != type)
|
||||||
|
@ -60,21 +60,21 @@ gist_page_opaque_info(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
/* verify the special space has the expected size */
|
/* verify the special space has the expected size */
|
||||||
if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
|
if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("input page is not a valid %s page", "GiST"),
|
errmsg("input page is not a valid %s page", "GiST"),
|
||||||
errdetail("Expected special size %d, got %d.",
|
errdetail("Expected special size %d, got %d.",
|
||||||
(int) MAXALIGN(sizeof(GISTPageOpaqueData)),
|
(int) MAXALIGN(sizeof(GISTPageOpaqueData)),
|
||||||
(int) PageGetSpecialSize(page))));
|
(int) PageGetSpecialSize(page))));
|
||||||
|
|
||||||
opaq = GistPageGetOpaque(page);
|
opaq = GistPageGetOpaque(page);
|
||||||
if (opaq->gist_page_id != GIST_PAGE_ID)
|
if (opaq->gist_page_id != GIST_PAGE_ID)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("input page is not a valid %s page", "GiST"),
|
errmsg("input page is not a valid %s page", "GiST"),
|
||||||
errdetail("Expected %08x, got %08x.",
|
errdetail("Expected %08x, got %08x.",
|
||||||
GIST_PAGE_ID,
|
GIST_PAGE_ID,
|
||||||
opaq->gist_page_id)));
|
opaq->gist_page_id)));
|
||||||
|
|
||||||
/* Build a tuple descriptor for our result type */
|
/* Build a tuple descriptor for our result type */
|
||||||
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
||||||
@ -138,21 +138,21 @@ gist_page_items_bytea(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
/* verify the special space has the expected size */
|
/* verify the special space has the expected size */
|
||||||
if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
|
if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("input page is not a valid %s page", "GiST"),
|
errmsg("input page is not a valid %s page", "GiST"),
|
||||||
errdetail("Expected special size %d, got %d.",
|
errdetail("Expected special size %d, got %d.",
|
||||||
(int) MAXALIGN(sizeof(GISTPageOpaqueData)),
|
(int) MAXALIGN(sizeof(GISTPageOpaqueData)),
|
||||||
(int) PageGetSpecialSize(page))));
|
(int) PageGetSpecialSize(page))));
|
||||||
|
|
||||||
opaq = GistPageGetOpaque(page);
|
opaq = GistPageGetOpaque(page);
|
||||||
if (opaq->gist_page_id != GIST_PAGE_ID)
|
if (opaq->gist_page_id != GIST_PAGE_ID)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("input page is not a valid %s page", "GiST"),
|
errmsg("input page is not a valid %s page", "GiST"),
|
||||||
errdetail("Expected %08x, got %08x.",
|
errdetail("Expected %08x, got %08x.",
|
||||||
GIST_PAGE_ID,
|
GIST_PAGE_ID,
|
||||||
opaq->gist_page_id)));
|
opaq->gist_page_id)));
|
||||||
|
|
||||||
/* Avoid bogus PageGetMaxOffsetNumber() call with deleted pages */
|
/* Avoid bogus PageGetMaxOffsetNumber() call with deleted pages */
|
||||||
if (GistPageIsDeleted(page))
|
if (GistPageIsDeleted(page))
|
||||||
|
@ -1533,7 +1533,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
|
|||||||
HASH_SEQ_STATUS hash_seq;
|
HASH_SEQ_STATUS hash_seq;
|
||||||
pgssEntry *entry;
|
pgssEntry *entry;
|
||||||
|
|
||||||
/* Superusers or roles with the privileges of pg_read_all_stats members are allowed */
|
/*
|
||||||
|
* Superusers or roles with the privileges of pg_read_all_stats members
|
||||||
|
* are allowed
|
||||||
|
*/
|
||||||
is_allowed_role = has_privs_of_role(userid, ROLE_PG_READ_ALL_STATS);
|
is_allowed_role = has_privs_of_role(userid, ROLE_PG_READ_ALL_STATS);
|
||||||
|
|
||||||
/* hash table must exist already */
|
/* hash table must exist already */
|
||||||
|
@ -47,7 +47,7 @@ static XLogRecPtr ValidateInputLSNs(bool till_end_of_wal,
|
|||||||
XLogRecPtr start_lsn, XLogRecPtr end_lsn);
|
XLogRecPtr start_lsn, XLogRecPtr end_lsn);
|
||||||
static void GetWALRecordsInfo(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
|
static void GetWALRecordsInfo(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
|
||||||
XLogRecPtr end_lsn);
|
XLogRecPtr end_lsn);
|
||||||
static void GetXLogSummaryStats(XLogStats * stats, ReturnSetInfo *rsinfo,
|
static void GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
|
||||||
Datum *values, bool *nulls, uint32 ncols,
|
Datum *values, bool *nulls, uint32 ncols,
|
||||||
bool stats_per_record);
|
bool stats_per_record);
|
||||||
static void FillXLogStatsRow(const char *name, uint64 n, uint64 total_count,
|
static void FillXLogStatsRow(const char *name, uint64 n, uint64 total_count,
|
||||||
@ -102,7 +102,7 @@ InitXLogReaderState(XLogRecPtr lsn, XLogRecPtr *first_record)
|
|||||||
LSN_FORMAT_ARGS(lsn))));
|
LSN_FORMAT_ARGS(lsn))));
|
||||||
|
|
||||||
private_data = (ReadLocalXLogPageNoWaitPrivate *)
|
private_data = (ReadLocalXLogPageNoWaitPrivate *)
|
||||||
palloc0(sizeof(ReadLocalXLogPageNoWaitPrivate));
|
palloc0(sizeof(ReadLocalXLogPageNoWaitPrivate));
|
||||||
|
|
||||||
xlogreader = XLogReaderAllocate(wal_segment_size, NULL,
|
xlogreader = XLogReaderAllocate(wal_segment_size, NULL,
|
||||||
XL_ROUTINE(.page_read = &read_local_xlog_page_no_wait,
|
XL_ROUTINE(.page_read = &read_local_xlog_page_no_wait,
|
||||||
@ -143,7 +143,7 @@ static XLogRecord *
|
|||||||
ReadNextXLogRecord(XLogReaderState *xlogreader, XLogRecPtr first_record)
|
ReadNextXLogRecord(XLogReaderState *xlogreader, XLogRecPtr first_record)
|
||||||
{
|
{
|
||||||
XLogRecord *record;
|
XLogRecord *record;
|
||||||
char *errormsg;
|
char *errormsg;
|
||||||
|
|
||||||
record = XLogReadRecord(xlogreader, &errormsg);
|
record = XLogReadRecord(xlogreader, &errormsg);
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ ReadNextXLogRecord(XLogReaderState *xlogreader, XLogRecPtr first_record)
|
|||||||
|
|
||||||
/* return NULL, if end of WAL is reached */
|
/* return NULL, if end of WAL is reached */
|
||||||
private_data = (ReadLocalXLogPageNoWaitPrivate *)
|
private_data = (ReadLocalXLogPageNoWaitPrivate *)
|
||||||
xlogreader->private_data;
|
xlogreader->private_data;
|
||||||
|
|
||||||
if (private_data->end_of_wal)
|
if (private_data->end_of_wal)
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -181,12 +181,12 @@ GetWALRecordInfo(XLogReaderState *record, XLogRecPtr lsn,
|
|||||||
Datum *values, bool *nulls, uint32 ncols)
|
Datum *values, bool *nulls, uint32 ncols)
|
||||||
{
|
{
|
||||||
const char *id;
|
const char *id;
|
||||||
RmgrData desc;
|
RmgrData desc;
|
||||||
uint32 fpi_len = 0;
|
uint32 fpi_len = 0;
|
||||||
StringInfoData rec_desc;
|
StringInfoData rec_desc;
|
||||||
StringInfoData rec_blk_ref;
|
StringInfoData rec_blk_ref;
|
||||||
uint32 main_data_len;
|
uint32 main_data_len;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
desc = GetRmgr(XLogRecGetRmid(record));
|
desc = GetRmgr(XLogRecGetRmid(record));
|
||||||
id = desc.rm_identify(XLogRecGetInfo(record));
|
id = desc.rm_identify(XLogRecGetInfo(record));
|
||||||
@ -228,9 +228,9 @@ Datum
|
|||||||
pg_get_wal_record_info(PG_FUNCTION_ARGS)
|
pg_get_wal_record_info(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
#define PG_GET_WAL_RECORD_INFO_COLS 11
|
#define PG_GET_WAL_RECORD_INFO_COLS 11
|
||||||
Datum result;
|
Datum result;
|
||||||
Datum values[PG_GET_WAL_RECORD_INFO_COLS];
|
Datum values[PG_GET_WAL_RECORD_INFO_COLS];
|
||||||
bool nulls[PG_GET_WAL_RECORD_INFO_COLS];
|
bool nulls[PG_GET_WAL_RECORD_INFO_COLS];
|
||||||
XLogRecPtr lsn;
|
XLogRecPtr lsn;
|
||||||
XLogRecPtr curr_lsn;
|
XLogRecPtr curr_lsn;
|
||||||
XLogRecPtr first_record;
|
XLogRecPtr first_record;
|
||||||
@ -334,8 +334,8 @@ GetWALRecordsInfo(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
|
|||||||
XLogRecPtr first_record;
|
XLogRecPtr first_record;
|
||||||
XLogReaderState *xlogreader;
|
XLogReaderState *xlogreader;
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
Datum values[PG_GET_WAL_RECORDS_INFO_COLS];
|
Datum values[PG_GET_WAL_RECORDS_INFO_COLS];
|
||||||
bool nulls[PG_GET_WAL_RECORDS_INFO_COLS];
|
bool nulls[PG_GET_WAL_RECORDS_INFO_COLS];
|
||||||
|
|
||||||
SetSingleFuncCall(fcinfo, 0);
|
SetSingleFuncCall(fcinfo, 0);
|
||||||
|
|
||||||
@ -418,11 +418,11 @@ FillXLogStatsRow(const char *name,
|
|||||||
uint64 tot_len, uint64 total_len,
|
uint64 tot_len, uint64 total_len,
|
||||||
Datum *values, bool *nulls, uint32 ncols)
|
Datum *values, bool *nulls, uint32 ncols)
|
||||||
{
|
{
|
||||||
double n_pct,
|
double n_pct,
|
||||||
rec_len_pct,
|
rec_len_pct,
|
||||||
fpi_len_pct,
|
fpi_len_pct,
|
||||||
tot_len_pct;
|
tot_len_pct;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
n_pct = 0;
|
n_pct = 0;
|
||||||
if (total_count != 0)
|
if (total_count != 0)
|
||||||
@ -461,11 +461,11 @@ GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
|
|||||||
Datum *values, bool *nulls, uint32 ncols,
|
Datum *values, bool *nulls, uint32 ncols,
|
||||||
bool stats_per_record)
|
bool stats_per_record)
|
||||||
{
|
{
|
||||||
uint64 total_count = 0;
|
uint64 total_count = 0;
|
||||||
uint64 total_rec_len = 0;
|
uint64 total_rec_len = 0;
|
||||||
uint64 total_fpi_len = 0;
|
uint64 total_fpi_len = 0;
|
||||||
uint64 total_len = 0;
|
uint64 total_len = 0;
|
||||||
int ri;
|
int ri;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Each row shows its percentages of the total, so make a first pass to
|
* Each row shows its percentages of the total, so make a first pass to
|
||||||
@ -488,7 +488,7 @@ GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
|
|||||||
uint64 rec_len;
|
uint64 rec_len;
|
||||||
uint64 fpi_len;
|
uint64 fpi_len;
|
||||||
uint64 tot_len;
|
uint64 tot_len;
|
||||||
RmgrData desc;
|
RmgrData desc;
|
||||||
|
|
||||||
if (!RmgrIdIsValid(ri))
|
if (!RmgrIdIsValid(ri))
|
||||||
continue;
|
continue;
|
||||||
@ -500,7 +500,7 @@ GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
|
|||||||
|
|
||||||
if (stats_per_record)
|
if (stats_per_record)
|
||||||
{
|
{
|
||||||
int rj;
|
int rj;
|
||||||
|
|
||||||
for (rj = 0; rj < MAX_XLINFO_TYPES; rj++)
|
for (rj = 0; rj < MAX_XLINFO_TYPES; rj++)
|
||||||
{
|
{
|
||||||
@ -556,10 +556,10 @@ GetWalStats(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
|
|||||||
#define PG_GET_WAL_STATS_COLS 9
|
#define PG_GET_WAL_STATS_COLS 9
|
||||||
XLogRecPtr first_record;
|
XLogRecPtr first_record;
|
||||||
XLogReaderState *xlogreader;
|
XLogReaderState *xlogreader;
|
||||||
XLogStats stats;
|
XLogStats stats;
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
Datum values[PG_GET_WAL_STATS_COLS];
|
Datum values[PG_GET_WAL_STATS_COLS];
|
||||||
bool nulls[PG_GET_WAL_STATS_COLS];
|
bool nulls[PG_GET_WAL_STATS_COLS];
|
||||||
|
|
||||||
SetSingleFuncCall(fcinfo, 0);
|
SetSingleFuncCall(fcinfo, 0);
|
||||||
|
|
||||||
@ -599,7 +599,7 @@ pg_get_wal_stats(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
XLogRecPtr start_lsn;
|
XLogRecPtr start_lsn;
|
||||||
XLogRecPtr end_lsn;
|
XLogRecPtr end_lsn;
|
||||||
bool stats_per_record;
|
bool stats_per_record;
|
||||||
|
|
||||||
start_lsn = PG_GETARG_LSN(0);
|
start_lsn = PG_GETARG_LSN(0);
|
||||||
end_lsn = PG_GETARG_LSN(1);
|
end_lsn = PG_GETARG_LSN(1);
|
||||||
@ -623,7 +623,7 @@ pg_get_wal_stats_till_end_of_wal(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
XLogRecPtr start_lsn;
|
XLogRecPtr start_lsn;
|
||||||
XLogRecPtr end_lsn = InvalidXLogRecPtr;
|
XLogRecPtr end_lsn = InvalidXLogRecPtr;
|
||||||
bool stats_per_record;
|
bool stats_per_record;
|
||||||
|
|
||||||
start_lsn = PG_GETARG_LSN(0);
|
start_lsn = PG_GETARG_LSN(0);
|
||||||
stats_per_record = PG_GETARG_BOOL(1);
|
stats_per_record = PG_GETARG_BOOL(1);
|
||||||
|
@ -373,7 +373,8 @@ gen_ossl_decrypt(PX_Cipher *c, int padding, const uint8 *data, unsigned dlen,
|
|||||||
uint8 *res, unsigned *rlen)
|
uint8 *res, unsigned *rlen)
|
||||||
{
|
{
|
||||||
OSSLCipher *od = c->ptr;
|
OSSLCipher *od = c->ptr;
|
||||||
int outlen, outlen2;
|
int outlen,
|
||||||
|
outlen2;
|
||||||
|
|
||||||
if (!od->init)
|
if (!od->init)
|
||||||
{
|
{
|
||||||
@ -402,7 +403,8 @@ gen_ossl_encrypt(PX_Cipher *c, int padding, const uint8 *data, unsigned dlen,
|
|||||||
uint8 *res, unsigned *rlen)
|
uint8 *res, unsigned *rlen)
|
||||||
{
|
{
|
||||||
OSSLCipher *od = c->ptr;
|
OSSLCipher *od = c->ptr;
|
||||||
int outlen, outlen2;
|
int outlen,
|
||||||
|
outlen2;
|
||||||
|
|
||||||
if (!od->init)
|
if (!od->init)
|
||||||
{
|
{
|
||||||
|
@ -255,46 +255,46 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
|
|||||||
if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind) ||
|
if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind) ||
|
||||||
rel->rd_rel->relkind == RELKIND_SEQUENCE)
|
rel->rd_rel->relkind == RELKIND_SEQUENCE)
|
||||||
{
|
{
|
||||||
return pgstat_heap(rel, fcinfo);
|
return pgstat_heap(rel, fcinfo);
|
||||||
}
|
}
|
||||||
else if (rel->rd_rel->relkind == RELKIND_INDEX)
|
else if (rel->rd_rel->relkind == RELKIND_INDEX)
|
||||||
{
|
{
|
||||||
switch (rel->rd_rel->relam)
|
switch (rel->rd_rel->relam)
|
||||||
{
|
{
|
||||||
case BTREE_AM_OID:
|
case BTREE_AM_OID:
|
||||||
return pgstat_index(rel, BTREE_METAPAGE + 1,
|
return pgstat_index(rel, BTREE_METAPAGE + 1,
|
||||||
pgstat_btree_page, fcinfo);
|
pgstat_btree_page, fcinfo);
|
||||||
case HASH_AM_OID:
|
case HASH_AM_OID:
|
||||||
return pgstat_index(rel, HASH_METAPAGE + 1,
|
return pgstat_index(rel, HASH_METAPAGE + 1,
|
||||||
pgstat_hash_page, fcinfo);
|
pgstat_hash_page, fcinfo);
|
||||||
case GIST_AM_OID:
|
case GIST_AM_OID:
|
||||||
return pgstat_index(rel, GIST_ROOT_BLKNO + 1,
|
return pgstat_index(rel, GIST_ROOT_BLKNO + 1,
|
||||||
pgstat_gist_page, fcinfo);
|
pgstat_gist_page, fcinfo);
|
||||||
case GIN_AM_OID:
|
case GIN_AM_OID:
|
||||||
err = "gin index";
|
err = "gin index";
|
||||||
break;
|
break;
|
||||||
case SPGIST_AM_OID:
|
case SPGIST_AM_OID:
|
||||||
err = "spgist index";
|
err = "spgist index";
|
||||||
break;
|
break;
|
||||||
case BRIN_AM_OID:
|
case BRIN_AM_OID:
|
||||||
err = "brin index";
|
err = "brin index";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
err = "unknown index";
|
err = "unknown index";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("index \"%s\" (%s) is not supported",
|
errmsg("index \"%s\" (%s) is not supported",
|
||||||
RelationGetRelationName(rel), err)));
|
RelationGetRelationName(rel), err)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("cannot get tuple-level statistics for relation \"%s\"",
|
errmsg("cannot get tuple-level statistics for relation \"%s\"",
|
||||||
RelationGetRelationName(rel)),
|
RelationGetRelationName(rel)),
|
||||||
errdetail_relkind_not_supported(rel->rd_rel->relkind)));
|
errdetail_relkind_not_supported(rel->rd_rel->relkind)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0; /* should not happen */
|
return 0; /* should not happen */
|
||||||
|
@ -654,10 +654,10 @@ do_sql_command_end(PGconn *conn, const char *sql, bool consume_input)
|
|||||||
PGresult *res;
|
PGresult *res;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If requested, consume whatever data is available from the socket.
|
* If requested, consume whatever data is available from the socket. (Note
|
||||||
* (Note that if all data is available, this allows pgfdw_get_result to
|
* that if all data is available, this allows pgfdw_get_result to call
|
||||||
* call PQgetResult without forcing the overhead of WaitLatchOrSocket,
|
* PQgetResult without forcing the overhead of WaitLatchOrSocket, which
|
||||||
* which would be large compared to the overhead of PQconsumeInput.)
|
* would be large compared to the overhead of PQconsumeInput.)
|
||||||
*/
|
*/
|
||||||
if (consume_input && !PQconsumeInput(conn))
|
if (consume_input && !PQconsumeInput(conn))
|
||||||
pgfdw_report_error(ERROR, NULL, conn, false, sql);
|
pgfdw_report_error(ERROR, NULL, conn, false, sql);
|
||||||
@ -1560,6 +1560,7 @@ pgfdw_finish_pre_commit_cleanup(List *pending_entries)
|
|||||||
entry = (ConnCacheEntry *) lfirst(lc);
|
entry = (ConnCacheEntry *) lfirst(lc);
|
||||||
|
|
||||||
Assert(entry->changing_xact_state);
|
Assert(entry->changing_xact_state);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We might already have received the result on the socket, so pass
|
* We might already have received the result on the socket, so pass
|
||||||
* consume_input=true to try to consume it first
|
* consume_input=true to try to consume it first
|
||||||
@ -1634,6 +1635,7 @@ pgfdw_finish_pre_subcommit_cleanup(List *pending_entries, int curlevel)
|
|||||||
entry = (ConnCacheEntry *) lfirst(lc);
|
entry = (ConnCacheEntry *) lfirst(lc);
|
||||||
|
|
||||||
Assert(entry->changing_xact_state);
|
Assert(entry->changing_xact_state);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We might already have received the result on the socket, so pass
|
* We might already have received the result on the socket, so pass
|
||||||
* consume_input=true to try to consume it first
|
* consume_input=true to try to consume it first
|
||||||
|
@ -1243,9 +1243,9 @@ postgresGetForeignPlan(PlannerInfo *root,
|
|||||||
if (best_path->fdw_private)
|
if (best_path->fdw_private)
|
||||||
{
|
{
|
||||||
has_final_sort = boolVal(list_nth(best_path->fdw_private,
|
has_final_sort = boolVal(list_nth(best_path->fdw_private,
|
||||||
FdwPathPrivateHasFinalSort));
|
FdwPathPrivateHasFinalSort));
|
||||||
has_limit = boolVal(list_nth(best_path->fdw_private,
|
has_limit = boolVal(list_nth(best_path->fdw_private,
|
||||||
FdwPathPrivateHasLimit));
|
FdwPathPrivateHasLimit));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_SIMPLE_REL(foreignrel))
|
if (IS_SIMPLE_REL(foreignrel))
|
||||||
@ -1926,7 +1926,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate,
|
|||||||
values_end_len = intVal(list_nth(fdw_private,
|
values_end_len = intVal(list_nth(fdw_private,
|
||||||
FdwModifyPrivateLen));
|
FdwModifyPrivateLen));
|
||||||
has_returning = boolVal(list_nth(fdw_private,
|
has_returning = boolVal(list_nth(fdw_private,
|
||||||
FdwModifyPrivateHasReturning));
|
FdwModifyPrivateHasReturning));
|
||||||
retrieved_attrs = (List *) list_nth(fdw_private,
|
retrieved_attrs = (List *) list_nth(fdw_private,
|
||||||
FdwModifyPrivateRetrievedAttrs);
|
FdwModifyPrivateRetrievedAttrs);
|
||||||
|
|
||||||
@ -2686,11 +2686,11 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags)
|
|||||||
dmstate->query = strVal(list_nth(fsplan->fdw_private,
|
dmstate->query = strVal(list_nth(fsplan->fdw_private,
|
||||||
FdwDirectModifyPrivateUpdateSql));
|
FdwDirectModifyPrivateUpdateSql));
|
||||||
dmstate->has_returning = boolVal(list_nth(fsplan->fdw_private,
|
dmstate->has_returning = boolVal(list_nth(fsplan->fdw_private,
|
||||||
FdwDirectModifyPrivateHasReturning));
|
FdwDirectModifyPrivateHasReturning));
|
||||||
dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
|
dmstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
|
||||||
FdwDirectModifyPrivateRetrievedAttrs);
|
FdwDirectModifyPrivateRetrievedAttrs);
|
||||||
dmstate->set_processed = boolVal(list_nth(fsplan->fdw_private,
|
dmstate->set_processed = boolVal(list_nth(fsplan->fdw_private,
|
||||||
FdwDirectModifyPrivateSetProcessed));
|
FdwDirectModifyPrivateSetProcessed));
|
||||||
|
|
||||||
/* Create context for per-tuple temp workspace. */
|
/* Create context for per-tuple temp workspace. */
|
||||||
dmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
|
dmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
|
||||||
|
@ -300,8 +300,8 @@ pg_decode_begin_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn)
|
|||||||
txn->output_plugin_private = txndata;
|
txn->output_plugin_private = txndata;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If asked to skip empty transactions, we'll emit BEGIN at the point where
|
* If asked to skip empty transactions, we'll emit BEGIN at the point
|
||||||
* the first operation is received for this transaction.
|
* where the first operation is received for this transaction.
|
||||||
*/
|
*/
|
||||||
if (data->skip_empty_xacts)
|
if (data->skip_empty_xacts)
|
||||||
return;
|
return;
|
||||||
@ -360,8 +360,8 @@ pg_decode_begin_prepare_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn)
|
|||||||
txn->output_plugin_private = txndata;
|
txn->output_plugin_private = txndata;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If asked to skip empty transactions, we'll emit BEGIN at the point where
|
* If asked to skip empty transactions, we'll emit BEGIN at the point
|
||||||
* the first operation is received for this transaction.
|
* where the first operation is received for this transaction.
|
||||||
*/
|
*/
|
||||||
if (data->skip_empty_xacts)
|
if (data->skip_empty_xacts)
|
||||||
return;
|
return;
|
||||||
|
@ -663,9 +663,9 @@ init_toast_snapshot(Snapshot toast_snapshot)
|
|||||||
/*
|
/*
|
||||||
* Catalog snapshots can be returned by GetOldestSnapshot() even if not
|
* Catalog snapshots can be returned by GetOldestSnapshot() even if not
|
||||||
* registered or active. That easily hides bugs around not having a
|
* registered or active. That easily hides bugs around not having a
|
||||||
* snapshot set up - most of the time there is a valid catalog
|
* snapshot set up - most of the time there is a valid catalog snapshot.
|
||||||
* snapshot. So additionally insist that the current snapshot is
|
* So additionally insist that the current snapshot is registered or
|
||||||
* registered or active.
|
* active.
|
||||||
*/
|
*/
|
||||||
Assert(HaveRegisteredOrActiveSnapshot());
|
Assert(HaveRegisteredOrActiveSnapshot());
|
||||||
|
|
||||||
|
@ -68,9 +68,9 @@ typedef struct
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Tuple visibility is only computed once for each tuple, for correctness
|
* Tuple visibility is only computed once for each tuple, for correctness
|
||||||
* and efficiency reasons; see comment in heap_page_prune() for
|
* and efficiency reasons; see comment in heap_page_prune() for details.
|
||||||
* details. This is of type int8[,] instead of HTSV_Result[], so we can use
|
* This is of type int8[], instead of HTSV_Result[], so we can use -1 to
|
||||||
* -1 to indicate no visibility has been computed, e.g. for LP_DEAD items.
|
* indicate no visibility has been computed, e.g. for LP_DEAD items.
|
||||||
*
|
*
|
||||||
* Same indexing as ->marked.
|
* Same indexing as ->marked.
|
||||||
*/
|
*/
|
||||||
@ -203,8 +203,8 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
|
|||||||
*/
|
*/
|
||||||
if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
|
if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
|
||||||
{
|
{
|
||||||
int ndeleted,
|
int ndeleted,
|
||||||
nnewlpdead;
|
nnewlpdead;
|
||||||
|
|
||||||
ndeleted = heap_page_prune(relation, buffer, vistest, limited_xmin,
|
ndeleted = heap_page_prune(relation, buffer, vistest, limited_xmin,
|
||||||
limited_ts, &nnewlpdead, NULL);
|
limited_ts, &nnewlpdead, NULL);
|
||||||
@ -267,7 +267,7 @@ heap_page_prune(Relation relation, Buffer buffer,
|
|||||||
GlobalVisState *vistest,
|
GlobalVisState *vistest,
|
||||||
TransactionId old_snap_xmin,
|
TransactionId old_snap_xmin,
|
||||||
TimestampTz old_snap_ts,
|
TimestampTz old_snap_ts,
|
||||||
int *nnewlpdead,
|
int *nnewlpdead,
|
||||||
OffsetNumber *off_loc)
|
OffsetNumber *off_loc)
|
||||||
{
|
{
|
||||||
int ndeleted = 0;
|
int ndeleted = 0;
|
||||||
|
@ -326,7 +326,7 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
|
|||||||
PGRUsage ru0;
|
PGRUsage ru0;
|
||||||
TimestampTz starttime = 0;
|
TimestampTz starttime = 0;
|
||||||
PgStat_Counter startreadtime = 0,
|
PgStat_Counter startreadtime = 0,
|
||||||
startwritetime = 0;
|
startwritetime = 0;
|
||||||
WalUsage startwalusage = pgWalUsage;
|
WalUsage startwalusage = pgWalUsage;
|
||||||
int64 StartPageHit = VacuumPageHit,
|
int64 StartPageHit = VacuumPageHit,
|
||||||
StartPageMiss = VacuumPageMiss,
|
StartPageMiss = VacuumPageMiss,
|
||||||
@ -2232,12 +2232,12 @@ lazy_vacuum(LVRelState *vacrel)
|
|||||||
* dead_items space is not CPU cache resident.
|
* dead_items space is not CPU cache resident.
|
||||||
*
|
*
|
||||||
* We don't take any special steps to remember the LP_DEAD items (such
|
* We don't take any special steps to remember the LP_DEAD items (such
|
||||||
* as counting them in our final update to the stats system) when
|
* as counting them in our final update to the stats system) when the
|
||||||
* the optimization is applied. Though the accounting used in
|
* optimization is applied. Though the accounting used in analyze.c's
|
||||||
* analyze.c's acquire_sample_rows() will recognize the same LP_DEAD
|
* acquire_sample_rows() will recognize the same LP_DEAD items as dead
|
||||||
* items as dead rows in its own stats report, that's okay.
|
* rows in its own stats report, that's okay. The discrepancy should
|
||||||
* The discrepancy should be negligible. If this optimization is ever
|
* be negligible. If this optimization is ever expanded to cover more
|
||||||
* expanded to cover more cases then this may need to be reconsidered.
|
* cases then this may need to be reconsidered.
|
||||||
*/
|
*/
|
||||||
threshold = (double) vacrel->rel_pages * BYPASS_THRESHOLD_PAGES;
|
threshold = (double) vacrel->rel_pages * BYPASS_THRESHOLD_PAGES;
|
||||||
bypass = (vacrel->lpdead_item_pages < threshold &&
|
bypass = (vacrel->lpdead_item_pages < threshold &&
|
||||||
|
@ -411,8 +411,8 @@ xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec, RepOriginI
|
|||||||
parsed.tsId, xlrec->initfileinval);
|
parsed.tsId, xlrec->initfileinval);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the replication origin has been set in this record in the
|
* Check if the replication origin has been set in this record in the same
|
||||||
* same way as PrepareRedoAdd().
|
* way as PrepareRedoAdd().
|
||||||
*/
|
*/
|
||||||
if (origin_id != InvalidRepOriginId)
|
if (origin_id != InvalidRepOriginId)
|
||||||
appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s",
|
appendStringInfo(buf, "; origin: node %u, lsn %X/%X, at %s",
|
||||||
|
@ -210,7 +210,7 @@ XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
|
|||||||
bool detailed_format, StringInfo buf,
|
bool detailed_format, StringInfo buf,
|
||||||
uint32 *fpi_len)
|
uint32 *fpi_len)
|
||||||
{
|
{
|
||||||
int block_id;
|
int block_id;
|
||||||
|
|
||||||
Assert(record != NULL);
|
Assert(record != NULL);
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,decode) \
|
#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,decode) \
|
||||||
{ name, redo, desc, identify, startup, cleanup, mask, decode },
|
{ name, redo, desc, identify, startup, cleanup, mask, decode },
|
||||||
|
|
||||||
RmgrData RmgrTable[RM_MAX_ID + 1] = {
|
RmgrData RmgrTable[RM_MAX_ID + 1] = {
|
||||||
#include "access/rmgrlist.h"
|
#include "access/rmgrlist.h"
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -125,8 +125,8 @@ RegisterCustomRmgr(RmgrId rmid, RmgrData *rmgr)
|
|||||||
|
|
||||||
if (!pg_strcasecmp(RmgrTable[existing_rmid].rm_name, rmgr->rm_name))
|
if (!pg_strcasecmp(RmgrTable[existing_rmid].rm_name, rmgr->rm_name))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errmsg("failed to register custom resource manager \"%s\" with ID %d", rmgr->rm_name, rmid),
|
(errmsg("failed to register custom resource manager \"%s\" with ID %d", rmgr->rm_name, rmid),
|
||||||
errdetail("Existing resource manager with ID %d has the same name.", existing_rmid)));
|
errdetail("Existing resource manager with ID %d has the same name.", existing_rmid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* register it */
|
/* register it */
|
||||||
|
@ -1119,7 +1119,7 @@ StartPrepare(GlobalTransaction gxact)
|
|||||||
if (hdr.nabortstats > 0)
|
if (hdr.nabortstats > 0)
|
||||||
{
|
{
|
||||||
save_state_data(abortstats,
|
save_state_data(abortstats,
|
||||||
hdr.nabortstats * sizeof(xl_xact_stats_item));
|
hdr.nabortstats * sizeof(xl_xact_stats_item));
|
||||||
pfree(abortstats);
|
pfree(abortstats);
|
||||||
}
|
}
|
||||||
if (hdr.ninvalmsgs > 0)
|
if (hdr.ninvalmsgs > 0)
|
||||||
@ -1529,9 +1529,9 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
|
|||||||
bufptr += MAXALIGN(hdr->ncommitrels * sizeof(RelFileNode));
|
bufptr += MAXALIGN(hdr->ncommitrels * sizeof(RelFileNode));
|
||||||
abortrels = (RelFileNode *) bufptr;
|
abortrels = (RelFileNode *) bufptr;
|
||||||
bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode));
|
bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode));
|
||||||
commitstats = (xl_xact_stats_item*) bufptr;
|
commitstats = (xl_xact_stats_item *) bufptr;
|
||||||
bufptr += MAXALIGN(hdr->ncommitstats * sizeof(xl_xact_stats_item));
|
bufptr += MAXALIGN(hdr->ncommitstats * sizeof(xl_xact_stats_item));
|
||||||
abortstats = (xl_xact_stats_item*) bufptr;
|
abortstats = (xl_xact_stats_item *) bufptr;
|
||||||
bufptr += MAXALIGN(hdr->nabortstats * sizeof(xl_xact_stats_item));
|
bufptr += MAXALIGN(hdr->nabortstats * sizeof(xl_xact_stats_item));
|
||||||
invalmsgs = (SharedInvalidationMessage *) bufptr;
|
invalmsgs = (SharedInvalidationMessage *) bufptr;
|
||||||
bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
|
bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
|
||||||
|
@ -435,10 +435,10 @@ typedef struct XLogCtlInsert
|
|||||||
bool fullPageWrites;
|
bool fullPageWrites;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* runningBackups is a counter indicating the number of backups currently in
|
* runningBackups is a counter indicating the number of backups currently
|
||||||
* progress. forcePageWrites is set to true when runningBackups is non-zero.
|
* in progress. forcePageWrites is set to true when runningBackups is
|
||||||
* lastBackupStart is the latest checkpoint redo location used as a starting
|
* non-zero. lastBackupStart is the latest checkpoint redo location used
|
||||||
* point for an online backup.
|
* as a starting point for an online backup.
|
||||||
*/
|
*/
|
||||||
int runningBackups;
|
int runningBackups;
|
||||||
XLogRecPtr lastBackupStart;
|
XLogRecPtr lastBackupStart;
|
||||||
@ -5307,14 +5307,14 @@ StartupXLOG(void)
|
|||||||
* When recovering from a backup (we are in recovery, and archive recovery
|
* When recovering from a backup (we are in recovery, and archive recovery
|
||||||
* was requested), complain if we did not roll forward far enough to reach
|
* was requested), complain if we did not roll forward far enough to reach
|
||||||
* the point where the database is consistent. For regular online
|
* the point where the database is consistent. For regular online
|
||||||
* backup-from-primary, that means reaching the end-of-backup WAL record (at
|
* backup-from-primary, that means reaching the end-of-backup WAL record
|
||||||
* which point we reset backupStartPoint to be Invalid), for
|
* (at which point we reset backupStartPoint to be Invalid), for
|
||||||
* backup-from-replica (which can't inject records into the WAL stream),
|
* backup-from-replica (which can't inject records into the WAL stream),
|
||||||
* that point is when we reach the minRecoveryPoint in pg_control (which
|
* that point is when we reach the minRecoveryPoint in pg_control (which
|
||||||
* we purposfully copy last when backing up from a replica). For pg_rewind
|
* we purposefully copy last when backing up from a replica). For
|
||||||
* (which creates a backup_label with a method of "pg_rewind") or
|
* pg_rewind (which creates a backup_label with a method of "pg_rewind")
|
||||||
* snapshot-style backups (which don't), backupEndRequired will be set to
|
* or snapshot-style backups (which don't), backupEndRequired will be set
|
||||||
* false.
|
* to false.
|
||||||
*
|
*
|
||||||
* Note: it is indeed okay to look at the local variable
|
* Note: it is indeed okay to look at the local variable
|
||||||
* LocalMinRecoveryPoint here, even though ControlFile->minRecoveryPoint
|
* LocalMinRecoveryPoint here, even though ControlFile->minRecoveryPoint
|
||||||
@ -5328,8 +5328,8 @@ StartupXLOG(void)
|
|||||||
/*
|
/*
|
||||||
* Ran off end of WAL before reaching end-of-backup WAL record, or
|
* Ran off end of WAL before reaching end-of-backup WAL record, or
|
||||||
* minRecoveryPoint. That's a bad sign, indicating that you tried to
|
* minRecoveryPoint. That's a bad sign, indicating that you tried to
|
||||||
* recover from an online backup but never called pg_backup_stop(),
|
* recover from an online backup but never called pg_backup_stop(), or
|
||||||
* or you didn't archive all the WAL needed.
|
* you didn't archive all the WAL needed.
|
||||||
*/
|
*/
|
||||||
if (ArchiveRecoveryRequested || ControlFile->backupEndRequired)
|
if (ArchiveRecoveryRequested || ControlFile->backupEndRequired)
|
||||||
{
|
{
|
||||||
@ -8481,8 +8481,8 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
|
|||||||
WALInsertLockAcquireExclusive();
|
WALInsertLockAcquireExclusive();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* It is expected that each do_pg_backup_start() call is matched by exactly
|
* It is expected that each do_pg_backup_start() call is matched by
|
||||||
* one do_pg_backup_stop() call.
|
* exactly one do_pg_backup_stop() call.
|
||||||
*/
|
*/
|
||||||
Assert(XLogCtl->Insert.runningBackups > 0);
|
Assert(XLogCtl->Insert.runningBackups > 0);
|
||||||
XLogCtl->Insert.runningBackups--;
|
XLogCtl->Insert.runningBackups--;
|
||||||
|
@ -497,15 +497,15 @@ XLogArchiveNotify(const char *xlog)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Timeline history files are given the highest archival priority to
|
* Timeline history files are given the highest archival priority to lower
|
||||||
* lower the chance that a promoted standby will choose a timeline that
|
* the chance that a promoted standby will choose a timeline that is
|
||||||
* is already in use. However, the archiver ordinarily tries to gather
|
* already in use. However, the archiver ordinarily tries to gather
|
||||||
* multiple files to archive from each scan of the archive_status
|
* multiple files to archive from each scan of the archive_status
|
||||||
* directory, which means that newly created timeline history files
|
* directory, which means that newly created timeline history files could
|
||||||
* could be left unarchived for a while. To ensure that the archiver
|
* be left unarchived for a while. To ensure that the archiver picks up
|
||||||
* picks up timeline history files as soon as possible, we force the
|
* timeline history files as soon as possible, we force the archiver to
|
||||||
* archiver to scan the archive_status directory the next time it looks
|
* scan the archive_status directory the next time it looks for a file to
|
||||||
* for a file to archive.
|
* archive.
|
||||||
*/
|
*/
|
||||||
if (IsTLHistoryFileName(xlog))
|
if (IsTLHistoryFileName(xlog))
|
||||||
PgArchForceDirScan();
|
PgArchForceDirScan();
|
||||||
|
@ -74,8 +74,8 @@ pg_backup_start(PG_FUNCTION_ARGS)
|
|||||||
errmsg("a backup is already in progress in this session")));
|
errmsg("a backup is already in progress in this session")));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Label file and tablespace map file need to be long-lived, since
|
* Label file and tablespace map file need to be long-lived, since they
|
||||||
* they are read in pg_backup_stop.
|
* are read in pg_backup_stop.
|
||||||
*/
|
*/
|
||||||
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
|
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
|
||||||
label_file = makeStringInfo();
|
label_file = makeStringInfo();
|
||||||
@ -127,8 +127,8 @@ pg_backup_stop(PG_FUNCTION_ARGS)
|
|||||||
errhint("Did you call pg_backup_start()?")));
|
errhint("Did you call pg_backup_start()?")));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stop the backup. Return a copy of the backup label and tablespace map so
|
* Stop the backup. Return a copy of the backup label and tablespace map
|
||||||
* they can be written to disk by the caller.
|
* so they can be written to disk by the caller.
|
||||||
*/
|
*/
|
||||||
stoppoint = do_pg_backup_stop(label_file->data, waitforarchive, NULL);
|
stoppoint = do_pg_backup_stop(label_file->data, waitforarchive, NULL);
|
||||||
|
|
||||||
|
@ -1205,9 +1205,9 @@ read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI,
|
|||||||
* method was used) or if this label came from somewhere else (the only
|
* method was used) or if this label came from somewhere else (the only
|
||||||
* other option today being from pg_rewind). If this was a streamed
|
* other option today being from pg_rewind). If this was a streamed
|
||||||
* backup then we know that we need to play through until we get to the
|
* backup then we know that we need to play through until we get to the
|
||||||
* end of the WAL which was generated during the backup (at which point
|
* end of the WAL which was generated during the backup (at which point we
|
||||||
* we will have reached consistency and backupEndRequired will be reset
|
* will have reached consistency and backupEndRequired will be reset to be
|
||||||
* to be false).
|
* false).
|
||||||
*/
|
*/
|
||||||
if (fscanf(lfp, "BACKUP METHOD: %19s\n", backuptype) == 1)
|
if (fscanf(lfp, "BACKUP METHOD: %19s\n", backuptype) == 1)
|
||||||
{
|
{
|
||||||
@ -2055,10 +2055,9 @@ CheckRecoveryConsistency(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Have we passed our safe starting point? Note that minRecoveryPoint is
|
* Have we passed our safe starting point? Note that minRecoveryPoint is
|
||||||
* known to be incorrectly set if recovering from a backup, until
|
* known to be incorrectly set if recovering from a backup, until the
|
||||||
* the XLOG_BACKUP_END arrives to advise us of the correct
|
* XLOG_BACKUP_END arrives to advise us of the correct minRecoveryPoint.
|
||||||
* minRecoveryPoint. All we know prior to that is that we're not
|
* All we know prior to that is that we're not consistent yet.
|
||||||
* consistent yet.
|
|
||||||
*/
|
*/
|
||||||
if (!reachedConsistency && !backupEndRequired &&
|
if (!reachedConsistency && !backupEndRequired &&
|
||||||
minRecoveryPoint <= lastReplayedEndRecPtr)
|
minRecoveryPoint <= lastReplayedEndRecPtr)
|
||||||
@ -3802,7 +3801,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
|
|||||||
HandleStartupProcInterrupts();
|
HandleStartupProcInterrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
return XLREAD_FAIL; /* not reached */
|
return XLREAD_FAIL; /* not reached */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ void
|
|||||||
XLogRecGetLen(XLogReaderState *record, uint32 *rec_len,
|
XLogRecGetLen(XLogReaderState *record, uint32 *rec_len,
|
||||||
uint32 *fpi_len)
|
uint32 *fpi_len)
|
||||||
{
|
{
|
||||||
int block_id;
|
int block_id;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate the amount of FPI data in the record.
|
* Calculate the amount of FPI data in the record.
|
||||||
@ -53,10 +53,10 @@ XLogRecGetLen(XLogReaderState *record, uint32 *rec_len,
|
|||||||
void
|
void
|
||||||
XLogRecStoreStats(XLogStats *stats, XLogReaderState *record)
|
XLogRecStoreStats(XLogStats *stats, XLogReaderState *record)
|
||||||
{
|
{
|
||||||
RmgrId rmid;
|
RmgrId rmid;
|
||||||
uint8 recid;
|
uint8 recid;
|
||||||
uint32 rec_len;
|
uint32 rec_len;
|
||||||
uint32 fpi_len;
|
uint32 fpi_len;
|
||||||
|
|
||||||
Assert(stats != NULL && record != NULL);
|
Assert(stats != NULL && record != NULL);
|
||||||
|
|
||||||
|
@ -80,10 +80,9 @@ typedef struct xl_invalid_page
|
|||||||
|
|
||||||
static HTAB *invalid_page_tab = NULL;
|
static HTAB *invalid_page_tab = NULL;
|
||||||
|
|
||||||
static int
|
static int read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr,
|
||||||
read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr,
|
int reqLen, XLogRecPtr targetRecPtr,
|
||||||
int reqLen, XLogRecPtr targetRecPtr,
|
char *cur_page, bool wait_for_wal);
|
||||||
char *cur_page, bool wait_for_wal);
|
|
||||||
|
|
||||||
/* Report a reference to an invalid page */
|
/* Report a reference to an invalid page */
|
||||||
static void
|
static void
|
||||||
@ -940,8 +939,8 @@ read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr,
|
|||||||
* archive in the timeline will get renamed to .partial by
|
* archive in the timeline will get renamed to .partial by
|
||||||
* StartupXLOG().
|
* StartupXLOG().
|
||||||
*
|
*
|
||||||
* If that happens after our caller determined the TLI but before
|
* If that happens after our caller determined the TLI but before we
|
||||||
* we actually read the xlog page, we might still try to read from the
|
* actually read the xlog page, we might still try to read from the
|
||||||
* old (now renamed) segment and fail. There's not much we can do
|
* old (now renamed) segment and fail. There's not much we can do
|
||||||
* about this, but it can only happen when we're a leaf of a cascading
|
* about this, but it can only happen when we're a leaf of a cascading
|
||||||
* standby whose primary gets promoted while we're decoding, so a
|
* standby whose primary gets promoted while we're decoding, so a
|
||||||
@ -965,7 +964,7 @@ read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr,
|
|||||||
* end of WAL has been reached.
|
* end of WAL has been reached.
|
||||||
*/
|
*/
|
||||||
private_data = (ReadLocalXLogPageNoWaitPrivate *)
|
private_data = (ReadLocalXLogPageNoWaitPrivate *)
|
||||||
state->private_data;
|
state->private_data;
|
||||||
private_data->end_of_wal = true;
|
private_data->end_of_wal = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -41,12 +41,12 @@ sub ParseHeader
|
|||||||
my $is_varlen = 0;
|
my $is_varlen = 0;
|
||||||
my $is_client_code = 0;
|
my $is_client_code = 0;
|
||||||
|
|
||||||
$catalog{columns} = [];
|
$catalog{columns} = [];
|
||||||
$catalog{toasting} = [];
|
$catalog{toasting} = [];
|
||||||
$catalog{indexing} = [];
|
$catalog{indexing} = [];
|
||||||
$catalog{other_oids} = [];
|
$catalog{other_oids} = [];
|
||||||
$catalog{foreign_keys} = [];
|
$catalog{foreign_keys} = [];
|
||||||
$catalog{client_code} = [];
|
$catalog{client_code} = [];
|
||||||
|
|
||||||
open(my $ifh, '<', $input_file) || die "$input_file: $!";
|
open(my $ifh, '<', $input_file) || die "$input_file: $!";
|
||||||
|
|
||||||
@ -96,7 +96,9 @@ sub ParseHeader
|
|||||||
push @{ $catalog{toasting} },
|
push @{ $catalog{toasting} },
|
||||||
{ parent_table => $1, toast_oid => $2, toast_index_oid => $3 };
|
{ parent_table => $1, toast_oid => $2, toast_index_oid => $3 };
|
||||||
}
|
}
|
||||||
elsif (/^DECLARE_TOAST_WITH_MACRO\(\s*(\w+),\s*(\d+),\s*(\d+),\s*(\w+),\s*(\w+)\)/)
|
elsif (
|
||||||
|
/^DECLARE_TOAST_WITH_MACRO\(\s*(\w+),\s*(\d+),\s*(\d+),\s*(\w+),\s*(\w+)\)/
|
||||||
|
)
|
||||||
{
|
{
|
||||||
push @{ $catalog{toasting} },
|
push @{ $catalog{toasting} },
|
||||||
{
|
{
|
||||||
@ -108,16 +110,17 @@ sub ParseHeader
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
elsif (
|
elsif (
|
||||||
/^DECLARE_(UNIQUE_)?INDEX(_PKEY)?\(\s*(\w+),\s*(\d+),\s*(\w+),\s*(.+)\)/)
|
/^DECLARE_(UNIQUE_)?INDEX(_PKEY)?\(\s*(\w+),\s*(\d+),\s*(\w+),\s*(.+)\)/
|
||||||
|
)
|
||||||
{
|
{
|
||||||
push @{ $catalog{indexing} },
|
push @{ $catalog{indexing} },
|
||||||
{
|
{
|
||||||
is_unique => $1 ? 1 : 0,
|
is_unique => $1 ? 1 : 0,
|
||||||
is_pkey => $2 ? 1 : 0,
|
is_pkey => $2 ? 1 : 0,
|
||||||
index_name => $3,
|
index_name => $3,
|
||||||
index_oid => $4,
|
index_oid => $4,
|
||||||
index_oid_macro => $5,
|
index_oid_macro => $5,
|
||||||
index_decl => $6
|
index_decl => $6
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
elsif (/^DECLARE_OID_DEFINING_MACRO\(\s*(\w+),\s*(\d+)\)/)
|
elsif (/^DECLARE_OID_DEFINING_MACRO\(\s*(\w+),\s*(\d+)\)/)
|
||||||
|
@ -814,7 +814,7 @@ Catalog::RenameTempFile($schemafile, $tmpext);
|
|||||||
Catalog::RenameTempFile($fk_info_file, $tmpext);
|
Catalog::RenameTempFile($fk_info_file, $tmpext);
|
||||||
Catalog::RenameTempFile($constraints_file, $tmpext);
|
Catalog::RenameTempFile($constraints_file, $tmpext);
|
||||||
|
|
||||||
exit ($num_errors != 0 ? 1 : 0);
|
exit($num_errors != 0 ? 1 : 0);
|
||||||
|
|
||||||
#################### Subroutines ########################
|
#################### Subroutines ########################
|
||||||
|
|
||||||
@ -916,11 +916,11 @@ sub morph_row_for_pgattr
|
|||||||
# Copy the type data from pg_type, and add some type-dependent items
|
# Copy the type data from pg_type, and add some type-dependent items
|
||||||
my $type = $types{$atttype};
|
my $type = $types{$atttype};
|
||||||
|
|
||||||
$row->{atttypid} = $type->{oid};
|
$row->{atttypid} = $type->{oid};
|
||||||
$row->{attlen} = $type->{typlen};
|
$row->{attlen} = $type->{typlen};
|
||||||
$row->{attbyval} = $type->{typbyval};
|
$row->{attbyval} = $type->{typbyval};
|
||||||
$row->{attalign} = $type->{typalign};
|
$row->{attalign} = $type->{typalign};
|
||||||
$row->{attstorage} = $type->{typstorage};
|
$row->{attstorage} = $type->{typstorage};
|
||||||
|
|
||||||
# set attndims if it's an array type
|
# set attndims if it's an array type
|
||||||
$row->{attndims} = $type->{typcategory} eq 'A' ? '1' : '0';
|
$row->{attndims} = $type->{typcategory} eq 'A' ? '1' : '0';
|
||||||
|
@ -1198,7 +1198,7 @@ heap_create_with_catalog(const char *relname,
|
|||||||
if (!OidIsValid(binary_upgrade_next_toast_pg_class_relfilenode))
|
if (!OidIsValid(binary_upgrade_next_toast_pg_class_relfilenode))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("toast relfilenode value not set when in binary upgrade mode")));
|
errmsg("toast relfilenode value not set when in binary upgrade mode")));
|
||||||
|
|
||||||
relfilenode = binary_upgrade_next_toast_pg_class_relfilenode;
|
relfilenode = binary_upgrade_next_toast_pg_class_relfilenode;
|
||||||
binary_upgrade_next_toast_pg_class_relfilenode = InvalidOid;
|
binary_upgrade_next_toast_pg_class_relfilenode = InvalidOid;
|
||||||
@ -1265,8 +1265,8 @@ heap_create_with_catalog(const char *relname,
|
|||||||
* remove the disk file again.)
|
* remove the disk file again.)
|
||||||
*
|
*
|
||||||
* NB: Note that passing create_storage = true is correct even for binary
|
* NB: Note that passing create_storage = true is correct even for binary
|
||||||
* upgrade. The storage we create here will be replaced later, but we need
|
* upgrade. The storage we create here will be replaced later, but we
|
||||||
* to have something on disk in the meanwhile.
|
* need to have something on disk in the meanwhile.
|
||||||
*/
|
*/
|
||||||
new_rel_desc = heap_create(relname,
|
new_rel_desc = heap_create(relname,
|
||||||
relnamespace,
|
relnamespace,
|
||||||
@ -3219,9 +3219,8 @@ restart:
|
|||||||
/*
|
/*
|
||||||
* If this constraint has a parent constraint which we have not seen
|
* If this constraint has a parent constraint which we have not seen
|
||||||
* yet, keep track of it for the second loop, below. Tracking parent
|
* yet, keep track of it for the second loop, below. Tracking parent
|
||||||
* constraints allows us to climb up to the top-level constraint
|
* constraints allows us to climb up to the top-level constraint and
|
||||||
* and look for all possible relations referencing the partitioned
|
* look for all possible relations referencing the partitioned table.
|
||||||
* table.
|
|
||||||
*/
|
*/
|
||||||
if (OidIsValid(con->conparentid) &&
|
if (OidIsValid(con->conparentid) &&
|
||||||
!list_member_oid(parent_cons, con->conparentid))
|
!list_member_oid(parent_cons, con->conparentid))
|
||||||
|
@ -928,9 +928,9 @@ index_create(Relation heapRelation,
|
|||||||
binary_upgrade_next_index_pg_class_relfilenode = InvalidOid;
|
binary_upgrade_next_index_pg_class_relfilenode = InvalidOid;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note that we want create_storage = true for binary upgrade.
|
* Note that we want create_storage = true for binary upgrade. The
|
||||||
* The storage we create here will be replaced later, but we need
|
* storage we create here will be replaced later, but we need to
|
||||||
* to have something on disk in the meanwhile.
|
* have something on disk in the meanwhile.
|
||||||
*/
|
*/
|
||||||
Assert(create_storage);
|
Assert(create_storage);
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ RunFunctionExecuteHook(Oid objectId)
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
RunObjectPostCreateHookStr(Oid classId, const char *objectName, int subId,
|
RunObjectPostCreateHookStr(Oid classId, const char *objectName, int subId,
|
||||||
bool is_internal)
|
bool is_internal)
|
||||||
{
|
{
|
||||||
ObjectAccessPostCreate pc_arg;
|
ObjectAccessPostCreate pc_arg;
|
||||||
|
|
||||||
@ -167,8 +167,8 @@ RunObjectPostCreateHookStr(Oid classId, const char *objectName, int subId,
|
|||||||
pc_arg.is_internal = is_internal;
|
pc_arg.is_internal = is_internal;
|
||||||
|
|
||||||
(*object_access_hook_str) (OAT_POST_CREATE,
|
(*object_access_hook_str) (OAT_POST_CREATE,
|
||||||
classId, objectName, subId,
|
classId, objectName, subId,
|
||||||
(void *) &pc_arg);
|
(void *) &pc_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -178,7 +178,7 @@ RunObjectPostCreateHookStr(Oid classId, const char *objectName, int subId,
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
RunObjectDropHookStr(Oid classId, const char *objectName, int subId,
|
RunObjectDropHookStr(Oid classId, const char *objectName, int subId,
|
||||||
int dropflags)
|
int dropflags)
|
||||||
{
|
{
|
||||||
ObjectAccessDrop drop_arg;
|
ObjectAccessDrop drop_arg;
|
||||||
|
|
||||||
@ -189,8 +189,8 @@ RunObjectDropHookStr(Oid classId, const char *objectName, int subId,
|
|||||||
drop_arg.dropflags = dropflags;
|
drop_arg.dropflags = dropflags;
|
||||||
|
|
||||||
(*object_access_hook_str) (OAT_DROP,
|
(*object_access_hook_str) (OAT_DROP,
|
||||||
classId, objectName, subId,
|
classId, objectName, subId,
|
||||||
(void *) &drop_arg);
|
(void *) &drop_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -205,8 +205,8 @@ RunObjectTruncateHookStr(const char *objectName)
|
|||||||
Assert(object_access_hook_str != NULL);
|
Assert(object_access_hook_str != NULL);
|
||||||
|
|
||||||
(*object_access_hook_str) (OAT_TRUNCATE,
|
(*object_access_hook_str) (OAT_TRUNCATE,
|
||||||
RelationRelationId, objectName, 0,
|
RelationRelationId, objectName, 0,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -216,7 +216,7 @@ RunObjectTruncateHookStr(const char *objectName)
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
RunObjectPostAlterHookStr(Oid classId, const char *objectName, int subId,
|
RunObjectPostAlterHookStr(Oid classId, const char *objectName, int subId,
|
||||||
Oid auxiliaryId, bool is_internal)
|
Oid auxiliaryId, bool is_internal)
|
||||||
{
|
{
|
||||||
ObjectAccessPostAlter pa_arg;
|
ObjectAccessPostAlter pa_arg;
|
||||||
|
|
||||||
@ -228,8 +228,8 @@ RunObjectPostAlterHookStr(Oid classId, const char *objectName, int subId,
|
|||||||
pa_arg.is_internal = is_internal;
|
pa_arg.is_internal = is_internal;
|
||||||
|
|
||||||
(*object_access_hook_str) (OAT_POST_ALTER,
|
(*object_access_hook_str) (OAT_POST_ALTER,
|
||||||
classId, objectName, subId,
|
classId, objectName, subId,
|
||||||
(void *) &pa_arg);
|
(void *) &pa_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -250,8 +250,8 @@ RunNamespaceSearchHookStr(const char *objectName, bool ereport_on_violation)
|
|||||||
ns_arg.result = true;
|
ns_arg.result = true;
|
||||||
|
|
||||||
(*object_access_hook_str) (OAT_NAMESPACE_SEARCH,
|
(*object_access_hook_str) (OAT_NAMESPACE_SEARCH,
|
||||||
NamespaceRelationId, objectName, 0,
|
NamespaceRelationId, objectName, 0,
|
||||||
(void *) &ns_arg);
|
(void *) &ns_arg);
|
||||||
|
|
||||||
return ns_arg.result;
|
return ns_arg.result;
|
||||||
}
|
}
|
||||||
@ -268,6 +268,6 @@ RunFunctionExecuteHookStr(const char *objectName)
|
|||||||
Assert(object_access_hook_str != NULL);
|
Assert(object_access_hook_str != NULL);
|
||||||
|
|
||||||
(*object_access_hook_str) (OAT_FUNCTION_EXECUTE,
|
(*object_access_hook_str) (OAT_FUNCTION_EXECUTE,
|
||||||
ProcedureRelationId, objectName, 0,
|
ProcedureRelationId, objectName, 0,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ CreateConstraintEntry(const char *constraintName,
|
|||||||
for (i = 0; i < numFkDeleteSetCols; i++)
|
for (i = 0; i < numFkDeleteSetCols; i++)
|
||||||
fkdatums[i] = Int16GetDatum(fkDeleteSetCols[i]);
|
fkdatums[i] = Int16GetDatum(fkDeleteSetCols[i]);
|
||||||
confdelsetcolsArray = construct_array(fkdatums, numFkDeleteSetCols,
|
confdelsetcolsArray = construct_array(fkdatums, numFkDeleteSetCols,
|
||||||
INT2OID, 2, true, TYPALIGN_SHORT);
|
INT2OID, 2, true, TYPALIGN_SHORT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
confdelsetcolsArray = NULL;
|
confdelsetcolsArray = NULL;
|
||||||
@ -1291,7 +1291,7 @@ DeconstructFkConstraintRow(HeapTuple tuple, int *numfks,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int num_delete_cols;
|
int num_delete_cols;
|
||||||
|
|
||||||
arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
|
arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
|
||||||
if (ARR_NDIM(arr) != 1 ||
|
if (ARR_NDIM(arr) != 1 ||
|
||||||
@ -1301,7 +1301,7 @@ DeconstructFkConstraintRow(HeapTuple tuple, int *numfks,
|
|||||||
num_delete_cols = ARR_DIMS(arr)[0];
|
num_delete_cols = ARR_DIMS(arr)[0];
|
||||||
memcpy(fk_del_set_cols, ARR_DATA_PTR(arr), num_delete_cols * sizeof(int16));
|
memcpy(fk_del_set_cols, ARR_DATA_PTR(arr), num_delete_cols * sizeof(int16));
|
||||||
if ((Pointer) arr != DatumGetPointer(adatum))
|
if ((Pointer) arr != DatumGetPointer(adatum))
|
||||||
pfree(arr); /* free de-toasted copy, if any */
|
pfree(arr); /* free de-toasted copy, if any */
|
||||||
|
|
||||||
*num_fk_del_set_cols = num_delete_cols;
|
*num_fk_del_set_cols = num_delete_cols;
|
||||||
}
|
}
|
||||||
|
@ -378,9 +378,9 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
|
|||||||
check_publication_add_relation(targetrel);
|
check_publication_add_relation(targetrel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Translate column names to attnums and make sure the column list contains
|
* Translate column names to attnums and make sure the column list
|
||||||
* only allowed elements (no system or generated columns etc.). Also build
|
* contains only allowed elements (no system or generated columns etc.).
|
||||||
* an array of attnums, for storing in the catalog.
|
* Also build an array of attnums, for storing in the catalog.
|
||||||
*/
|
*/
|
||||||
publication_translate_columns(pri->relation, pri->columns,
|
publication_translate_columns(pri->relation, pri->columns,
|
||||||
&natts, &attarray);
|
&natts, &attarray);
|
||||||
@ -555,11 +555,11 @@ pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols, MemoryContext mcxt)
|
|||||||
ArrayType *arr;
|
ArrayType *arr;
|
||||||
int nelems;
|
int nelems;
|
||||||
int16 *elems;
|
int16 *elems;
|
||||||
MemoryContext oldcxt = NULL;
|
MemoryContext oldcxt = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If an existing bitmap was provided, use it. Otherwise just use NULL
|
* If an existing bitmap was provided, use it. Otherwise just use NULL and
|
||||||
* and build a new bitmap.
|
* build a new bitmap.
|
||||||
*/
|
*/
|
||||||
if (columns)
|
if (columns)
|
||||||
result = columns;
|
result = columns;
|
||||||
|
@ -340,13 +340,13 @@ RelationTruncate(Relation rel, BlockNumber nblocks)
|
|||||||
* is in progress.
|
* is in progress.
|
||||||
*
|
*
|
||||||
* The truncation operation might drop buffers that the checkpoint
|
* The truncation operation might drop buffers that the checkpoint
|
||||||
* otherwise would have flushed. If it does, then it's essential that
|
* otherwise would have flushed. If it does, then it's essential that the
|
||||||
* the files actually get truncated on disk before the checkpoint record
|
* files actually get truncated on disk before the checkpoint record is
|
||||||
* is written. Otherwise, if reply begins from that checkpoint, the
|
* written. Otherwise, if reply begins from that checkpoint, the
|
||||||
* to-be-truncated blocks might still exist on disk but have older
|
* to-be-truncated blocks might still exist on disk but have older
|
||||||
* contents than expected, which can cause replay to fail. It's OK for
|
* contents than expected, which can cause replay to fail. It's OK for the
|
||||||
* the blocks to not exist on disk at all, but not for them to have the
|
* blocks to not exist on disk at all, but not for them to have the wrong
|
||||||
* wrong contents.
|
* contents.
|
||||||
*/
|
*/
|
||||||
Assert((MyProc->delayChkptFlags & DELAY_CHKPT_COMPLETE) == 0);
|
Assert((MyProc->delayChkptFlags & DELAY_CHKPT_COMPLETE) == 0);
|
||||||
MyProc->delayChkptFlags |= DELAY_CHKPT_COMPLETE;
|
MyProc->delayChkptFlags |= DELAY_CHKPT_COMPLETE;
|
||||||
|
@ -429,7 +429,7 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
|
|||||||
*/
|
*/
|
||||||
if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
|
if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
|
||||||
{
|
{
|
||||||
List *idxs = RelationGetIndexList(onerel);
|
List *idxs = RelationGetIndexList(onerel);
|
||||||
|
|
||||||
Irel = NULL;
|
Irel = NULL;
|
||||||
nindexes = 0;
|
nindexes = 0;
|
||||||
@ -680,10 +680,10 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now report ANALYZE to the cumulative stats system. For regular tables, we do
|
* Now report ANALYZE to the cumulative stats system. For regular tables,
|
||||||
* it only if not doing inherited stats. For partitioned tables, we only
|
* we do it only if not doing inherited stats. For partitioned tables, we
|
||||||
* do it for inherited stats. (We're never called for not-inherited stats
|
* only do it for inherited stats. (We're never called for not-inherited
|
||||||
* on partitioned tables anyway.)
|
* stats on partitioned tables anyway.)
|
||||||
*
|
*
|
||||||
* Reset the changes_since_analyze counter only if we analyzed all
|
* Reset the changes_since_analyze counter only if we analyzed all
|
||||||
* columns; otherwise, there is still work for auto-analyze to do.
|
* columns; otherwise, there is still work for auto-analyze to do.
|
||||||
|
@ -246,8 +246,9 @@ DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_e
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Nondeterministic collations are currently only supported with ICU
|
* Nondeterministic collations are currently only supported with ICU
|
||||||
* because that's the only case where it can actually make a difference.
|
* because that's the only case where it can actually make a
|
||||||
* So we can save writing the code for the other providers.
|
* difference. So we can save writing the code for the other
|
||||||
|
* providers.
|
||||||
*/
|
*/
|
||||||
if (!collisdeterministic && collprovider != COLLPROVIDER_ICU)
|
if (!collisdeterministic && collprovider != COLLPROVIDER_ICU)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -345,7 +345,7 @@ defGetCopyHeaderChoice(DefElem *def)
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
char *sval = defGetString(def);
|
char *sval = defGetString(def);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The set of strings accepted here should match up with the
|
* The set of strings accepted here should match up with the
|
||||||
@ -365,8 +365,8 @@ defGetCopyHeaderChoice(DefElem *def)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("%s requires a Boolean value or \"match\"",
|
errmsg("%s requires a Boolean value or \"match\"",
|
||||||
def->defname)));
|
def->defname)));
|
||||||
return COPY_HEADER_FALSE; /* keep compiler quiet */
|
return COPY_HEADER_FALSE; /* keep compiler quiet */
|
||||||
}
|
}
|
||||||
|
@ -800,7 +800,8 @@ NextCopyFromRawFields(CopyFromState cstate, char ***fields, int *nfields)
|
|||||||
errmsg("column name mismatch in header line field %d: got null value (\"%s\"), expected \"%s\"",
|
errmsg("column name mismatch in header line field %d: got null value (\"%s\"), expected \"%s\"",
|
||||||
fldnum, cstate->opts.null_print, NameStr(attr->attname))));
|
fldnum, cstate->opts.null_print, NameStr(attr->attname))));
|
||||||
|
|
||||||
if (namestrcmp(&attr->attname, colName) != 0) {
|
if (namestrcmp(&attr->attname, colName) != 0)
|
||||||
|
{
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
|
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
|
||||||
errmsg("column name mismatch in header line field %d: got \"%s\", expected \"%s\"",
|
errmsg("column name mismatch in header line field %d: got \"%s\", expected \"%s\"",
|
||||||
|
@ -439,8 +439,8 @@ BeginCopyTo(ParseState *pstate,
|
|||||||
* locks on the source table(s).
|
* locks on the source table(s).
|
||||||
*/
|
*/
|
||||||
rewritten = pg_analyze_and_rewrite_fixedparams(raw_query,
|
rewritten = pg_analyze_and_rewrite_fixedparams(raw_query,
|
||||||
pstate->p_sourcetext, NULL, 0,
|
pstate->p_sourcetext, NULL, 0,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
/* check that we got back something we can work with */
|
/* check that we got back something we can work with */
|
||||||
if (rewritten == NIL)
|
if (rewritten == NIL)
|
||||||
@ -862,7 +862,7 @@ DoCopyTo(CopyToState cstate)
|
|||||||
|
|
||||||
if (cstate->opts.csv_mode)
|
if (cstate->opts.csv_mode)
|
||||||
CopyAttributeOutCSV(cstate, colname, false,
|
CopyAttributeOutCSV(cstate, colname, false,
|
||||||
list_length(cstate->attnumlist) == 1);
|
list_length(cstate->attnumlist) == 1);
|
||||||
else
|
else
|
||||||
CopyAttributeOutText(cstate, colname);
|
CopyAttributeOutText(cstate, colname);
|
||||||
}
|
}
|
||||||
|
@ -201,9 +201,9 @@ CreateDatabaseUsingWalLog(Oid src_dboid, Oid dst_dboid,
|
|||||||
*
|
*
|
||||||
* We typically do not read relation data into shared_buffers without
|
* We typically do not read relation data into shared_buffers without
|
||||||
* holding a relation lock. It's unclear what could go wrong if we
|
* holding a relation lock. It's unclear what could go wrong if we
|
||||||
* skipped it in this case, because nobody can be modifying either
|
* skipped it in this case, because nobody can be modifying either the
|
||||||
* the source or destination database at this point, and we have locks
|
* source or destination database at this point, and we have locks on
|
||||||
* on both databases, too, but let's take the conservative route.
|
* both databases, too, but let's take the conservative route.
|
||||||
*/
|
*/
|
||||||
dstrelid.relId = srcrelid.relId = relinfo->reloid;
|
dstrelid.relId = srcrelid.relId = relinfo->reloid;
|
||||||
LockRelationId(&srcrelid, AccessShareLock);
|
LockRelationId(&srcrelid, AccessShareLock);
|
||||||
@ -274,9 +274,9 @@ ScanSourceDatabasePgClass(Oid tbid, Oid dbid, char *srcpath)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* We can't use a real relcache entry for a relation in some other
|
* We can't use a real relcache entry for a relation in some other
|
||||||
* database, but since we're only going to access the fields related
|
* database, but since we're only going to access the fields related to
|
||||||
* to physical storage, a fake one is good enough. If we didn't do this
|
* physical storage, a fake one is good enough. If we didn't do this and
|
||||||
* and used the smgr layer directly, we would have to worry about
|
* used the smgr layer directly, we would have to worry about
|
||||||
* invalidations.
|
* invalidations.
|
||||||
*/
|
*/
|
||||||
rel = CreateFakeRelcacheEntry(rnode);
|
rel = CreateFakeRelcacheEntry(rnode);
|
||||||
@ -333,10 +333,10 @@ ScanSourceDatabasePgClassPage(Page page, Buffer buf, Oid tbid, Oid dbid,
|
|||||||
char *srcpath, List *rnodelist,
|
char *srcpath, List *rnodelist,
|
||||||
Snapshot snapshot)
|
Snapshot snapshot)
|
||||||
{
|
{
|
||||||
BlockNumber blkno = BufferGetBlockNumber(buf);
|
BlockNumber blkno = BufferGetBlockNumber(buf);
|
||||||
OffsetNumber offnum;
|
OffsetNumber offnum;
|
||||||
OffsetNumber maxoff;
|
OffsetNumber maxoff;
|
||||||
HeapTupleData tuple;
|
HeapTupleData tuple;
|
||||||
|
|
||||||
maxoff = PageGetMaxOffsetNumber(page);
|
maxoff = PageGetMaxOffsetNumber(page);
|
||||||
|
|
||||||
@ -368,10 +368,10 @@ ScanSourceDatabasePgClassPage(Page page, Buffer buf, Oid tbid, Oid dbid,
|
|||||||
CreateDBRelInfo *relinfo;
|
CreateDBRelInfo *relinfo;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ScanSourceDatabasePgClassTuple is in charge of constructing
|
* ScanSourceDatabasePgClassTuple is in charge of constructing a
|
||||||
* a CreateDBRelInfo object for this tuple, but can also decide
|
* CreateDBRelInfo object for this tuple, but can also decide that
|
||||||
* that this tuple isn't something we need to copy. If we do need
|
* this tuple isn't something we need to copy. If we do need to
|
||||||
* to copy the relation, add it to the list.
|
* copy the relation, add it to the list.
|
||||||
*/
|
*/
|
||||||
relinfo = ScanSourceDatabasePgClassTuple(&tuple, tbid, dbid,
|
relinfo = ScanSourceDatabasePgClassTuple(&tuple, tbid, dbid,
|
||||||
srcpath);
|
srcpath);
|
||||||
@ -395,9 +395,9 @@ CreateDBRelInfo *
|
|||||||
ScanSourceDatabasePgClassTuple(HeapTupleData *tuple, Oid tbid, Oid dbid,
|
ScanSourceDatabasePgClassTuple(HeapTupleData *tuple, Oid tbid, Oid dbid,
|
||||||
char *srcpath)
|
char *srcpath)
|
||||||
{
|
{
|
||||||
CreateDBRelInfo *relinfo;
|
CreateDBRelInfo *relinfo;
|
||||||
Form_pg_class classForm;
|
Form_pg_class classForm;
|
||||||
Oid relfilenode = InvalidOid;
|
Oid relfilenode = InvalidOid;
|
||||||
|
|
||||||
classForm = (Form_pg_class) GETSTRUCT(tuple);
|
classForm = (Form_pg_class) GETSTRUCT(tuple);
|
||||||
|
|
||||||
@ -406,11 +406,11 @@ ScanSourceDatabasePgClassTuple(HeapTupleData *tuple, Oid tbid, Oid dbid,
|
|||||||
*
|
*
|
||||||
* Shared objects don't need to be copied, because they are shared.
|
* Shared objects don't need to be copied, because they are shared.
|
||||||
* Objects without storage can't be copied, because there's nothing to
|
* Objects without storage can't be copied, because there's nothing to
|
||||||
* copy. Temporary relations don't need to be copied either, because
|
* copy. Temporary relations don't need to be copied either, because they
|
||||||
* they are inaccessible outside of the session that created them,
|
* are inaccessible outside of the session that created them, which must
|
||||||
* which must be gone already, and couldn't connect to a different database
|
* be gone already, and couldn't connect to a different database if it
|
||||||
* if it still existed. autovacuum will eventually remove the pg_class
|
* still existed. autovacuum will eventually remove the pg_class entries
|
||||||
* entries as well.
|
* as well.
|
||||||
*/
|
*/
|
||||||
if (classForm->reltablespace == GLOBALTABLESPACE_OID ||
|
if (classForm->reltablespace == GLOBALTABLESPACE_OID ||
|
||||||
!RELKIND_HAS_STORAGE(classForm->relkind) ||
|
!RELKIND_HAS_STORAGE(classForm->relkind) ||
|
||||||
@ -702,7 +702,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
|
|||||||
DefElem *dcollate = NULL;
|
DefElem *dcollate = NULL;
|
||||||
DefElem *dctype = NULL;
|
DefElem *dctype = NULL;
|
||||||
DefElem *diculocale = NULL;
|
DefElem *diculocale = NULL;
|
||||||
DefElem *dlocprovider = NULL;
|
DefElem *dlocprovider = NULL;
|
||||||
DefElem *distemplate = NULL;
|
DefElem *distemplate = NULL;
|
||||||
DefElem *dallowconnections = NULL;
|
DefElem *dallowconnections = NULL;
|
||||||
DefElem *dconnlimit = NULL;
|
DefElem *dconnlimit = NULL;
|
||||||
@ -824,10 +824,10 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
|
|||||||
/*
|
/*
|
||||||
* We don't normally permit new databases to be created with
|
* We don't normally permit new databases to be created with
|
||||||
* system-assigned OIDs. pg_upgrade tries to preserve database
|
* system-assigned OIDs. pg_upgrade tries to preserve database
|
||||||
* OIDs, so we can't allow any database to be created with an
|
* OIDs, so we can't allow any database to be created with an OID
|
||||||
* OID that might be in use in a freshly-initialized cluster
|
* that might be in use in a freshly-initialized cluster created
|
||||||
* created by some future version. We assume all such OIDs will
|
* by some future version. We assume all such OIDs will be from
|
||||||
* be from the system-managed OID range.
|
* the system-managed OID range.
|
||||||
*
|
*
|
||||||
* As an exception, however, we permit any OID to be assigned when
|
* As an exception, however, we permit any OID to be assigned when
|
||||||
* allow_system_table_mods=on (so that initdb can assign system
|
* allow_system_table_mods=on (so that initdb can assign system
|
||||||
@ -1348,15 +1348,15 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
|
|||||||
InvokeObjectPostCreateHook(DatabaseRelationId, dboid, 0);
|
InvokeObjectPostCreateHook(DatabaseRelationId, dboid, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we're going to be reading data for the to-be-created database
|
* If we're going to be reading data for the to-be-created database into
|
||||||
* into shared_buffers, take a lock on it. Nobody should know that this
|
* shared_buffers, take a lock on it. Nobody should know that this
|
||||||
* database exists yet, but it's good to maintain the invariant that a
|
* database exists yet, but it's good to maintain the invariant that a
|
||||||
* lock an AccessExclusiveLock on the database is sufficient to drop all
|
* lock an AccessExclusiveLock on the database is sufficient to drop all
|
||||||
* of its buffers without worrying about more being read later.
|
* of its buffers without worrying about more being read later.
|
||||||
*
|
*
|
||||||
* Note that we need to do this before entering the PG_ENSURE_ERROR_CLEANUP
|
* Note that we need to do this before entering the
|
||||||
* block below, because createdb_failure_callback expects this lock to
|
* PG_ENSURE_ERROR_CLEANUP block below, because createdb_failure_callback
|
||||||
* be held already.
|
* expects this lock to be held already.
|
||||||
*/
|
*/
|
||||||
if (dbstrategy == CREATEDB_WAL_LOG)
|
if (dbstrategy == CREATEDB_WAL_LOG)
|
||||||
LockSharedObject(DatabaseRelationId, dboid, 0, AccessShareLock);
|
LockSharedObject(DatabaseRelationId, dboid, 0, AccessShareLock);
|
||||||
|
@ -3833,7 +3833,7 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
|
|||||||
if (rte->tablefunc)
|
if (rte->tablefunc)
|
||||||
if (rte->tablefunc->functype == TFT_XMLTABLE)
|
if (rte->tablefunc->functype == TFT_XMLTABLE)
|
||||||
objectname = "xmltable";
|
objectname = "xmltable";
|
||||||
else /* Must be TFT_JSON_TABLE */
|
else /* Must be TFT_JSON_TABLE */
|
||||||
objectname = "json_table";
|
objectname = "json_table";
|
||||||
else
|
else
|
||||||
objectname = NULL;
|
objectname = NULL;
|
||||||
|
@ -758,10 +758,10 @@ execute_sql_string(const char *sql)
|
|||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
|
stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
|
||||||
sql,
|
sql,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
NULL);
|
NULL);
|
||||||
stmt_list = pg_plan_queries(stmt_list, sql, CURSOR_OPT_PARALLEL_OK, NULL);
|
stmt_list = pg_plan_queries(stmt_list, sql, CURSOR_OPT_PARALLEL_OK, NULL);
|
||||||
|
|
||||||
foreach(lc2, stmt_list)
|
foreach(lc2, stmt_list)
|
||||||
|
@ -332,8 +332,8 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
|
|||||||
/*
|
/*
|
||||||
* Inform cumulative stats system about our activity: basically, we
|
* Inform cumulative stats system about our activity: basically, we
|
||||||
* truncated the matview and inserted some new data. (The concurrent
|
* truncated the matview and inserted some new data. (The concurrent
|
||||||
* code path above doesn't need to worry about this because the inserts
|
* code path above doesn't need to worry about this because the
|
||||||
* and deletes it issues get counted by lower-level code.)
|
* inserts and deletes it issues get counted by lower-level code.)
|
||||||
*/
|
*/
|
||||||
pgstat_count_truncate(matviewRel);
|
pgstat_count_truncate(matviewRel);
|
||||||
if (!stmt->skipData)
|
if (!stmt->skipData)
|
||||||
|
@ -297,7 +297,7 @@ contain_invalid_rfcolumn_walker(Node *node, rf_context *context)
|
|||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
pub_rf_contains_invalid_column(Oid pubid, Relation relation, List *ancestors,
|
pub_rf_contains_invalid_column(Oid pubid, Relation relation, List *ancestors,
|
||||||
bool pubviaroot)
|
bool pubviaroot)
|
||||||
{
|
{
|
||||||
HeapTuple rftuple;
|
HeapTuple rftuple;
|
||||||
Oid relid = RelationGetRelid(relation);
|
Oid relid = RelationGetRelid(relation);
|
||||||
@ -373,7 +373,7 @@ pub_rf_contains_invalid_column(Oid pubid, Relation relation, List *ancestors,
|
|||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
pub_collist_contains_invalid_column(Oid pubid, Relation relation, List *ancestors,
|
pub_collist_contains_invalid_column(Oid pubid, Relation relation, List *ancestors,
|
||||||
bool pubviaroot)
|
bool pubviaroot)
|
||||||
{
|
{
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
Oid relid = RelationGetRelid(relation);
|
Oid relid = RelationGetRelid(relation);
|
||||||
@ -384,8 +384,8 @@ pub_collist_contains_invalid_column(Oid pubid, Relation relation, List *ancestor
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* For a partition, if pubviaroot is true, find the topmost ancestor that
|
* For a partition, if pubviaroot is true, find the topmost ancestor that
|
||||||
* is published via this publication as we need to use its column list
|
* is published via this publication as we need to use its column list for
|
||||||
* for the changes.
|
* the changes.
|
||||||
*
|
*
|
||||||
* Note that even though the column list used is for an ancestor, the
|
* Note that even though the column list used is for an ancestor, the
|
||||||
* REPLICA IDENTITY used will be for the actual child table.
|
* REPLICA IDENTITY used will be for the actual child table.
|
||||||
@ -399,19 +399,19 @@ pub_collist_contains_invalid_column(Oid pubid, Relation relation, List *ancestor
|
|||||||
}
|
}
|
||||||
|
|
||||||
tuple = SearchSysCache2(PUBLICATIONRELMAP,
|
tuple = SearchSysCache2(PUBLICATIONRELMAP,
|
||||||
ObjectIdGetDatum(publish_as_relid),
|
ObjectIdGetDatum(publish_as_relid),
|
||||||
ObjectIdGetDatum(pubid));
|
ObjectIdGetDatum(pubid));
|
||||||
|
|
||||||
if (!HeapTupleIsValid(tuple))
|
if (!HeapTupleIsValid(tuple))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
datum = SysCacheGetAttr(PUBLICATIONRELMAP, tuple,
|
datum = SysCacheGetAttr(PUBLICATIONRELMAP, tuple,
|
||||||
Anum_pg_publication_rel_prattrs,
|
Anum_pg_publication_rel_prattrs,
|
||||||
&isnull);
|
&isnull);
|
||||||
|
|
||||||
if (!isnull)
|
if (!isnull)
|
||||||
{
|
{
|
||||||
int x;
|
int x;
|
||||||
Bitmapset *idattrs;
|
Bitmapset *idattrs;
|
||||||
Bitmapset *columns = NULL;
|
Bitmapset *columns = NULL;
|
||||||
|
|
||||||
@ -429,8 +429,9 @@ pub_collist_contains_invalid_column(Oid pubid, Relation relation, List *ancestor
|
|||||||
/*
|
/*
|
||||||
* Attnums in the bitmap returned by RelationGetIndexAttrBitmap are
|
* Attnums in the bitmap returned by RelationGetIndexAttrBitmap are
|
||||||
* offset (to handle system columns the usual way), while column list
|
* offset (to handle system columns the usual way), while column list
|
||||||
* does not use offset, so we can't do bms_is_subset(). Instead, we have
|
* does not use offset, so we can't do bms_is_subset(). Instead, we
|
||||||
* to loop over the idattrs and check all of them are in the list.
|
* have to loop over the idattrs and check all of them are in the
|
||||||
|
* list.
|
||||||
*/
|
*/
|
||||||
x = -1;
|
x = -1;
|
||||||
while ((x = bms_next_member(idattrs, x)) >= 0)
|
while ((x = bms_next_member(idattrs, x)) >= 0)
|
||||||
@ -440,14 +441,14 @@ pub_collist_contains_invalid_column(Oid pubid, Relation relation, List *ancestor
|
|||||||
/*
|
/*
|
||||||
* If pubviaroot is true, we are validating the column list of the
|
* If pubviaroot is true, we are validating the column list of the
|
||||||
* parent table, but the bitmap contains the replica identity
|
* parent table, but the bitmap contains the replica identity
|
||||||
* information of the child table. The parent/child attnums may not
|
* information of the child table. The parent/child attnums may
|
||||||
* match, so translate them to the parent - get the attname from
|
* not match, so translate them to the parent - get the attname
|
||||||
* the child, and look it up in the parent.
|
* from the child, and look it up in the parent.
|
||||||
*/
|
*/
|
||||||
if (pubviaroot)
|
if (pubviaroot)
|
||||||
{
|
{
|
||||||
/* attribute name in the child table */
|
/* attribute name in the child table */
|
||||||
char *colname = get_attname(relid, attnum, false);
|
char *colname = get_attname(relid, attnum, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine the attnum for the attribute name in parent (we
|
* Determine the attnum for the attribute name in parent (we
|
||||||
@ -720,7 +721,7 @@ TransformPubWhereClauses(List *tables, const char *queryString,
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
CheckPubRelationColumnList(List *tables, const char *queryString,
|
CheckPubRelationColumnList(List *tables, const char *queryString,
|
||||||
bool pubviaroot)
|
bool pubviaroot)
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
@ -864,7 +865,7 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
|
|||||||
publish_via_partition_root);
|
publish_via_partition_root);
|
||||||
|
|
||||||
CheckPubRelationColumnList(rels, pstate->p_sourcetext,
|
CheckPubRelationColumnList(rels, pstate->p_sourcetext,
|
||||||
publish_via_partition_root);
|
publish_via_partition_root);
|
||||||
|
|
||||||
PublicationAddTables(puboid, rels, true, NULL);
|
PublicationAddTables(puboid, rels, true, NULL);
|
||||||
CloseTableList(rels);
|
CloseTableList(rels);
|
||||||
@ -1198,8 +1199,8 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
|
|||||||
|
|
||||||
/* Transform the int2vector column list to a bitmap. */
|
/* Transform the int2vector column list to a bitmap. */
|
||||||
columnListDatum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
|
columnListDatum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
|
||||||
Anum_pg_publication_rel_prattrs,
|
Anum_pg_publication_rel_prattrs,
|
||||||
&isnull);
|
&isnull);
|
||||||
|
|
||||||
if (!isnull)
|
if (!isnull)
|
||||||
oldcolumns = pub_collist_to_bitmapset(NULL, columnListDatum, NULL);
|
oldcolumns = pub_collist_to_bitmapset(NULL, columnListDatum, NULL);
|
||||||
@ -1210,15 +1211,15 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
|
|||||||
foreach(newlc, rels)
|
foreach(newlc, rels)
|
||||||
{
|
{
|
||||||
PublicationRelInfo *newpubrel;
|
PublicationRelInfo *newpubrel;
|
||||||
Oid newrelid;
|
Oid newrelid;
|
||||||
Bitmapset *newcolumns = NULL;
|
Bitmapset *newcolumns = NULL;
|
||||||
|
|
||||||
newpubrel = (PublicationRelInfo *) lfirst(newlc);
|
newpubrel = (PublicationRelInfo *) lfirst(newlc);
|
||||||
newrelid = RelationGetRelid(newpubrel->relation);
|
newrelid = RelationGetRelid(newpubrel->relation);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the new publication has column list, transform it to
|
* If the new publication has column list, transform it to a
|
||||||
* a bitmap too.
|
* bitmap too.
|
||||||
*/
|
*/
|
||||||
if (newpubrel->columns)
|
if (newpubrel->columns)
|
||||||
{
|
{
|
||||||
|
@ -258,9 +258,9 @@ CreateStatistics(CreateStatsStmt *stmt)
|
|||||||
nattnums++;
|
nattnums++;
|
||||||
ReleaseSysCache(atttuple);
|
ReleaseSysCache(atttuple);
|
||||||
}
|
}
|
||||||
else if (IsA(selem->expr, Var)) /* column reference in parens */
|
else if (IsA(selem->expr, Var)) /* column reference in parens */
|
||||||
{
|
{
|
||||||
Var *var = (Var *) selem->expr;
|
Var *var = (Var *) selem->expr;
|
||||||
TypeCacheEntry *type;
|
TypeCacheEntry *type;
|
||||||
|
|
||||||
/* Disallow use of system attributes in extended stats */
|
/* Disallow use of system attributes in extended stats */
|
||||||
@ -297,10 +297,11 @@ CreateStatistics(CreateStatsStmt *stmt)
|
|||||||
while ((k = bms_next_member(attnums, k)) >= 0)
|
while ((k = bms_next_member(attnums, k)) >= 0)
|
||||||
{
|
{
|
||||||
AttrNumber attnum = k + FirstLowInvalidHeapAttributeNumber;
|
AttrNumber attnum = k + FirstLowInvalidHeapAttributeNumber;
|
||||||
|
|
||||||
if (attnum <= 0)
|
if (attnum <= 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("statistics creation on system columns is not supported")));
|
errmsg("statistics creation on system columns is not supported")));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -511,9 +512,9 @@ CreateStatistics(CreateStatsStmt *stmt)
|
|||||||
relation_close(statrel, RowExclusiveLock);
|
relation_close(statrel, RowExclusiveLock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We used to create the pg_statistic_ext_data tuple too, but it's not clear
|
* We used to create the pg_statistic_ext_data tuple too, but it's not
|
||||||
* what value should the stxdinherit flag have (it depends on whether the rel
|
* clear what value should the stxdinherit flag have (it depends on
|
||||||
* is partitioned, contains data, etc.)
|
* whether the rel is partitioned, contains data, etc.)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
InvokeObjectPostCreateHook(StatisticExtRelationId, statoid, 0);
|
InvokeObjectPostCreateHook(StatisticExtRelationId, statoid, 0);
|
||||||
|
@ -1578,13 +1578,13 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
|
|||||||
PG_END_TRY();
|
PG_END_TRY();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tell the cumulative stats system that the subscription is getting dropped.
|
* Tell the cumulative stats system that the subscription is getting
|
||||||
* We can safely report dropping the subscription statistics here if the
|
* dropped. We can safely report dropping the subscription statistics here
|
||||||
* subscription is associated with a replication slot since we cannot run
|
* if the subscription is associated with a replication slot since we
|
||||||
* DROP SUBSCRIPTION inside a transaction block. Subscription statistics
|
* cannot run DROP SUBSCRIPTION inside a transaction block. Subscription
|
||||||
* will be removed later by (auto)vacuum either if it's not associated
|
* statistics will be removed later by (auto)vacuum either if it's not
|
||||||
* with a replication slot or if the message for dropping the subscription
|
* associated with a replication slot or if the message for dropping the
|
||||||
* gets lost.
|
* subscription gets lost.
|
||||||
*/
|
*/
|
||||||
if (slotname)
|
if (slotname)
|
||||||
pgstat_drop_subscription(subid);
|
pgstat_drop_subscription(subid);
|
||||||
|
@ -495,8 +495,8 @@ static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstra
|
|||||||
bool old_check_ok,
|
bool old_check_ok,
|
||||||
Oid parentDelTrigger, Oid parentUpdTrigger);
|
Oid parentDelTrigger, Oid parentUpdTrigger);
|
||||||
static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
|
static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
|
||||||
int numfksetcols, const int16 *fksetcolsattnums,
|
int numfksetcols, const int16 *fksetcolsattnums,
|
||||||
List *fksetcols);
|
List *fksetcols);
|
||||||
static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
|
static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
|
||||||
Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
|
Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
|
||||||
int numfks, int16 *pkattnum, int16 *fkattnum,
|
int numfks, int16 *pkattnum, int16 *fkattnum,
|
||||||
@ -5579,7 +5579,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
|
|||||||
|
|
||||||
foreach(lc, seqlist)
|
foreach(lc, seqlist)
|
||||||
{
|
{
|
||||||
Oid seq_relid = lfirst_oid(lc);
|
Oid seq_relid = lfirst_oid(lc);
|
||||||
|
|
||||||
SequenceChangePersistence(seq_relid, tab->newrelpersistence);
|
SequenceChangePersistence(seq_relid, tab->newrelpersistence);
|
||||||
}
|
}
|
||||||
@ -9448,8 +9448,8 @@ validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < numfksetcols; i++)
|
for (int i = 0; i < numfksetcols; i++)
|
||||||
{
|
{
|
||||||
int16 setcol_attnum = fksetcolsattnums[i];
|
int16 setcol_attnum = fksetcolsattnums[i];
|
||||||
bool seen = false;
|
bool seen = false;
|
||||||
|
|
||||||
for (int j = 0; j < numfks; j++)
|
for (int j = 0; j < numfks; j++)
|
||||||
{
|
{
|
||||||
@ -9462,7 +9462,8 @@ validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
|
|||||||
|
|
||||||
if (!seen)
|
if (!seen)
|
||||||
{
|
{
|
||||||
char *col = strVal(list_nth(fksetcols, i));
|
char *col = strVal(list_nth(fksetcols, i));
|
||||||
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
|
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
|
||||||
errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
|
errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
|
||||||
@ -15890,6 +15891,7 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
|
|||||||
CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
|
CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
|
||||||
InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
|
InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
|
||||||
InvalidOid, is_internal);
|
InvalidOid, is_internal);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Invalidate the relcache for the table, so that after we commit
|
* Invalidate the relcache for the table, so that after we commit
|
||||||
* all sessions will refresh the table's replica identity index
|
* all sessions will refresh the table's replica identity index
|
||||||
@ -17931,12 +17933,12 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
|
|||||||
/*
|
/*
|
||||||
* If the partition we just attached is partitioned itself, invalidate
|
* If the partition we just attached is partitioned itself, invalidate
|
||||||
* relcache for all descendent partitions too to ensure that their
|
* relcache for all descendent partitions too to ensure that their
|
||||||
* rd_partcheck expression trees are rebuilt; partitions already locked
|
* rd_partcheck expression trees are rebuilt; partitions already locked at
|
||||||
* at the beginning of this function.
|
* the beginning of this function.
|
||||||
*/
|
*/
|
||||||
if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
|
if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
|
||||||
{
|
{
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
|
|
||||||
foreach(l, attachrel_children)
|
foreach(l, attachrel_children)
|
||||||
{
|
{
|
||||||
@ -18652,13 +18654,13 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
|
|||||||
/*
|
/*
|
||||||
* If the partition we just detached is partitioned itself, invalidate
|
* If the partition we just detached is partitioned itself, invalidate
|
||||||
* relcache for all descendent partitions too to ensure that their
|
* relcache for all descendent partitions too to ensure that their
|
||||||
* rd_partcheck expression trees are rebuilt; must lock partitions
|
* rd_partcheck expression trees are rebuilt; must lock partitions before
|
||||||
* before doing so, using the same lockmode as what partRel has been
|
* doing so, using the same lockmode as what partRel has been locked with
|
||||||
* locked with by the caller.
|
* by the caller.
|
||||||
*/
|
*/
|
||||||
if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
|
if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
|
||||||
{
|
{
|
||||||
List *children;
|
List *children;
|
||||||
|
|
||||||
children = find_all_inheritors(RelationGetRelid(partRel),
|
children = find_all_inheritors(RelationGetRelid(partRel),
|
||||||
AccessExclusiveLock, NULL);
|
AccessExclusiveLock, NULL);
|
||||||
|
@ -89,7 +89,7 @@ char *default_tablespace = NULL;
|
|||||||
char *temp_tablespaces = NULL;
|
char *temp_tablespaces = NULL;
|
||||||
bool allow_in_place_tablespaces = false;
|
bool allow_in_place_tablespaces = false;
|
||||||
|
|
||||||
Oid binary_upgrade_next_pg_tablespace_oid = InvalidOid;
|
Oid binary_upgrade_next_pg_tablespace_oid = InvalidOid;
|
||||||
|
|
||||||
static void create_tablespace_directories(const char *location,
|
static void create_tablespace_directories(const char *location,
|
||||||
const Oid tablespaceoid);
|
const Oid tablespaceoid);
|
||||||
|
@ -798,11 +798,11 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
|
|||||||
*/
|
*/
|
||||||
if (drolemembers)
|
if (drolemembers)
|
||||||
{
|
{
|
||||||
List *rolemembers = (List *) drolemembers->arg;
|
List *rolemembers = (List *) drolemembers->arg;
|
||||||
|
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
if (stmt->action == +1) /* add members to role */
|
if (stmt->action == +1) /* add members to role */
|
||||||
AddRoleMems(rolename, roleid,
|
AddRoleMems(rolename, roleid,
|
||||||
rolemembers, roleSpecsToIds(rolemembers),
|
rolemembers, roleSpecsToIds(rolemembers),
|
||||||
GetUserId(), false);
|
GetUserId(), false);
|
||||||
|
@ -1409,7 +1409,7 @@ vac_update_relstats(Relation relation,
|
|||||||
*frozenxid_updated = false;
|
*frozenxid_updated = false;
|
||||||
if (TransactionIdIsNormal(frozenxid) && oldfrozenxid != frozenxid)
|
if (TransactionIdIsNormal(frozenxid) && oldfrozenxid != frozenxid)
|
||||||
{
|
{
|
||||||
bool update = false;
|
bool update = false;
|
||||||
|
|
||||||
if (TransactionIdPrecedes(oldfrozenxid, frozenxid))
|
if (TransactionIdPrecedes(oldfrozenxid, frozenxid))
|
||||||
update = true;
|
update = true;
|
||||||
@ -1432,7 +1432,7 @@ vac_update_relstats(Relation relation,
|
|||||||
*minmulti_updated = false;
|
*minmulti_updated = false;
|
||||||
if (MultiXactIdIsValid(minmulti) && oldminmulti != minmulti)
|
if (MultiXactIdIsValid(minmulti) && oldminmulti != minmulti)
|
||||||
{
|
{
|
||||||
bool update = false;
|
bool update = false;
|
||||||
|
|
||||||
if (MultiXactIdPrecedes(oldminmulti, minmulti))
|
if (MultiXactIdPrecedes(oldminmulti, minmulti))
|
||||||
update = true;
|
update = true;
|
||||||
|
@ -112,7 +112,7 @@ typedef enum PVIndVacStatus
|
|||||||
PARALLEL_INDVAC_STATUS_NEED_BULKDELETE,
|
PARALLEL_INDVAC_STATUS_NEED_BULKDELETE,
|
||||||
PARALLEL_INDVAC_STATUS_NEED_CLEANUP,
|
PARALLEL_INDVAC_STATUS_NEED_CLEANUP,
|
||||||
PARALLEL_INDVAC_STATUS_COMPLETED
|
PARALLEL_INDVAC_STATUS_COMPLETED
|
||||||
} PVIndVacStatus;
|
} PVIndVacStatus;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Struct for index vacuum statistics of an index that is used for parallel vacuum.
|
* Struct for index vacuum statistics of an index that is used for parallel vacuum.
|
||||||
|
@ -2504,7 +2504,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
|||||||
if (ctor->type == JSCTOR_JSON_SCALAR)
|
if (ctor->type == JSCTOR_JSON_SCALAR)
|
||||||
{
|
{
|
||||||
bool is_jsonb =
|
bool is_jsonb =
|
||||||
ctor->returning->format->format_type == JS_FORMAT_JSONB;
|
ctor->returning->format->format_type == JS_FORMAT_JSONB;
|
||||||
|
|
||||||
scratch.d.json_constructor.arg_type_cache =
|
scratch.d.json_constructor.arg_type_cache =
|
||||||
palloc(sizeof(*scratch.d.json_constructor.arg_type_cache) * nargs);
|
palloc(sizeof(*scratch.d.json_constructor.arg_type_cache) * nargs);
|
||||||
@ -2666,7 +2666,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
|||||||
{
|
{
|
||||||
cstate->coercion = *coercion;
|
cstate->coercion = *coercion;
|
||||||
cstate->estate = *coercion ?
|
cstate->estate = *coercion ?
|
||||||
ExecInitExprWithCaseValue((Expr *)(*coercion)->expr,
|
ExecInitExprWithCaseValue((Expr *) (*coercion)->expr,
|
||||||
state->parent,
|
state->parent,
|
||||||
caseval, casenull) : NULL;
|
caseval, casenull) : NULL;
|
||||||
}
|
}
|
||||||
|
@ -3978,8 +3978,8 @@ ExecEvalJsonIsPredicate(ExprState *state, ExprEvalStep *op)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do full parsing pass only for uniqueness check or for
|
* Do full parsing pass only for uniqueness check or for JSON text
|
||||||
* JSON text validation.
|
* validation.
|
||||||
*/
|
*/
|
||||||
if (res && (pred->unique_keys || exprtype == TEXTOID))
|
if (res && (pred->unique_keys || exprtype == TEXTOID))
|
||||||
res = json_validate(json, pred->unique_keys, false);
|
res = json_validate(json, pred->unique_keys, false);
|
||||||
@ -4513,20 +4513,20 @@ ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
|
|||||||
if (ctor->type == JSCTOR_JSON_ARRAY)
|
if (ctor->type == JSCTOR_JSON_ARRAY)
|
||||||
res = (is_jsonb ?
|
res = (is_jsonb ?
|
||||||
jsonb_build_array_worker :
|
jsonb_build_array_worker :
|
||||||
json_build_array_worker)(op->d.json_constructor.nargs,
|
json_build_array_worker) (op->d.json_constructor.nargs,
|
||||||
op->d.json_constructor.arg_values,
|
|
||||||
op->d.json_constructor.arg_nulls,
|
|
||||||
op->d.json_constructor.arg_types,
|
|
||||||
op->d.json_constructor.constructor->absent_on_null);
|
|
||||||
else if (ctor->type == JSCTOR_JSON_OBJECT)
|
|
||||||
res = (is_jsonb ?
|
|
||||||
jsonb_build_object_worker :
|
|
||||||
json_build_object_worker)(op->d.json_constructor.nargs,
|
|
||||||
op->d.json_constructor.arg_values,
|
op->d.json_constructor.arg_values,
|
||||||
op->d.json_constructor.arg_nulls,
|
op->d.json_constructor.arg_nulls,
|
||||||
op->d.json_constructor.arg_types,
|
op->d.json_constructor.arg_types,
|
||||||
op->d.json_constructor.constructor->absent_on_null,
|
op->d.json_constructor.constructor->absent_on_null);
|
||||||
op->d.json_constructor.constructor->unique);
|
else if (ctor->type == JSCTOR_JSON_OBJECT)
|
||||||
|
res = (is_jsonb ?
|
||||||
|
jsonb_build_object_worker :
|
||||||
|
json_build_object_worker) (op->d.json_constructor.nargs,
|
||||||
|
op->d.json_constructor.arg_values,
|
||||||
|
op->d.json_constructor.arg_nulls,
|
||||||
|
op->d.json_constructor.arg_types,
|
||||||
|
op->d.json_constructor.constructor->absent_on_null,
|
||||||
|
op->d.json_constructor.constructor->unique);
|
||||||
else if (ctor->type == JSCTOR_JSON_SCALAR)
|
else if (ctor->type == JSCTOR_JSON_SCALAR)
|
||||||
{
|
{
|
||||||
if (op->d.json_constructor.arg_nulls[0])
|
if (op->d.json_constructor.arg_nulls[0])
|
||||||
@ -4622,9 +4622,9 @@ static Datum
|
|||||||
ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
|
ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
|
||||||
Datum res, bool *isNull, void *p, bool *error)
|
Datum res, bool *isNull, void *p, bool *error)
|
||||||
{
|
{
|
||||||
ExprState *estate = p;
|
ExprState *estate = p;
|
||||||
|
|
||||||
if (estate) /* coerce using specified expression */
|
if (estate) /* coerce using specified expression */
|
||||||
return ExecEvalExpr(estate, econtext, isNull);
|
return ExecEvalExpr(estate, econtext, isNull);
|
||||||
|
|
||||||
if (op->d.jsonexpr.jsexpr->op != JSON_EXISTS_OP)
|
if (op->d.jsonexpr.jsexpr->op != JSON_EXISTS_OP)
|
||||||
@ -4696,7 +4696,7 @@ EvalJsonPathVar(void *cxt, char *varName, int varNameLen,
|
|||||||
if (!var->evaluated)
|
if (!var->evaluated)
|
||||||
{
|
{
|
||||||
MemoryContext oldcxt = var->mcxt ?
|
MemoryContext oldcxt = var->mcxt ?
|
||||||
MemoryContextSwitchTo(var->mcxt) : NULL;
|
MemoryContextSwitchTo(var->mcxt) : NULL;
|
||||||
|
|
||||||
var->value = ExecEvalExpr(var->estate, var->econtext, &var->isnull);
|
var->value = ExecEvalExpr(var->estate, var->econtext, &var->isnull);
|
||||||
var->evaluated = true;
|
var->evaluated = true;
|
||||||
@ -4751,9 +4751,8 @@ ExecPrepareJsonItemCoercion(JsonbValue *item,
|
|||||||
|
|
||||||
case jbvString:
|
case jbvString:
|
||||||
coercion = &coercions->string;
|
coercion = &coercions->string;
|
||||||
res = PointerGetDatum(
|
res = PointerGetDatum(cstring_to_text_with_len(item->val.string.val,
|
||||||
cstring_to_text_with_len(item->val.string.val,
|
item->val.string.len));
|
||||||
item->val.string.len));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case jbvNumeric:
|
case jbvNumeric:
|
||||||
@ -4809,8 +4808,8 @@ ExecPrepareJsonItemCoercion(JsonbValue *item,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef Datum (*JsonFunc)(ExprEvalStep *op, ExprContext *econtext,
|
typedef Datum (*JsonFunc) (ExprEvalStep *op, ExprContext *econtext,
|
||||||
Datum item, bool *resnull, void *p, bool *error);
|
Datum item, bool *resnull, void *p, bool *error);
|
||||||
|
|
||||||
static Datum
|
static Datum
|
||||||
ExecEvalJsonExprSubtrans(JsonFunc func, ExprEvalStep *op,
|
ExecEvalJsonExprSubtrans(JsonFunc func, ExprEvalStep *op,
|
||||||
@ -4826,8 +4825,8 @@ ExecEvalJsonExprSubtrans(JsonFunc func, ExprEvalStep *op,
|
|||||||
return func(op, econtext, res, resnull, p, error);
|
return func(op, econtext, res, resnull, p, error);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We should catch exceptions of category ERRCODE_DATA_EXCEPTION
|
* We should catch exceptions of category ERRCODE_DATA_EXCEPTION and
|
||||||
* and execute the corresponding ON ERROR behavior then.
|
* execute the corresponding ON ERROR behavior then.
|
||||||
*/
|
*/
|
||||||
oldcontext = CurrentMemoryContext;
|
oldcontext = CurrentMemoryContext;
|
||||||
oldowner = CurrentResourceOwner;
|
oldowner = CurrentResourceOwner;
|
||||||
@ -4864,7 +4863,8 @@ ExecEvalJsonExprSubtrans(JsonFunc func, ExprEvalStep *op,
|
|||||||
|
|
||||||
ecategory = ERRCODE_TO_CATEGORY(edata->sqlerrcode);
|
ecategory = ERRCODE_TO_CATEGORY(edata->sqlerrcode);
|
||||||
|
|
||||||
if (ecategory != ERRCODE_DATA_EXCEPTION && /* jsonpath and other data errors */
|
if (ecategory != ERRCODE_DATA_EXCEPTION && /* jsonpath and other data
|
||||||
|
* errors */
|
||||||
ecategory != ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION) /* domain errors */
|
ecategory != ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION) /* domain errors */
|
||||||
ReThrowError(edata);
|
ReThrowError(edata);
|
||||||
|
|
||||||
@ -4918,7 +4918,7 @@ ExecEvalJsonExpr(ExprEvalStep *op, ExprContext *econtext,
|
|||||||
if (error && *error)
|
if (error && *error)
|
||||||
return (Datum) 0;
|
return (Datum) 0;
|
||||||
|
|
||||||
if (!jbv) /* NULL or empty */
|
if (!jbv) /* NULL or empty */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
Assert(!empty);
|
Assert(!empty);
|
||||||
@ -4949,21 +4949,23 @@ ExecEvalJsonExpr(ExprEvalStep *op, ExprContext *econtext,
|
|||||||
*error = true;
|
*error = true;
|
||||||
return (Datum) 0;
|
return (Datum) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Coercion via I/O means here that the cast to the target
|
* Coercion via I/O means here that the cast to the target
|
||||||
* type simply does not exist.
|
* type simply does not exist.
|
||||||
*/
|
*/
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
/*
|
|
||||||
* XXX Standard says about a separate error code
|
/*
|
||||||
* ERRCODE_SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE
|
* XXX Standard says about a separate error code
|
||||||
* but does not define its number.
|
* ERRCODE_SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE but
|
||||||
*/
|
* does not define its number.
|
||||||
|
*/
|
||||||
(errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
|
(errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
|
||||||
errmsg("SQL/JSON item cannot be cast to target type")));
|
errmsg("SQL/JSON item cannot be cast to target type")));
|
||||||
}
|
}
|
||||||
else if (!jcstate->estate)
|
else if (!jcstate->estate)
|
||||||
return res; /* no coercion */
|
return res; /* no coercion */
|
||||||
|
|
||||||
/* coerce using specific expression */
|
/* coerce using specific expression */
|
||||||
estate = jcstate->estate;
|
estate = jcstate->estate;
|
||||||
@ -5018,6 +5020,7 @@ ExecEvalJsonExpr(ExprEvalStep *op, ExprContext *econtext,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (jexpr->on_empty->btype == JSON_BEHAVIOR_DEFAULT)
|
if (jexpr->on_empty->btype == JSON_BEHAVIOR_DEFAULT)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute DEFAULT expression as a coercion expression, because
|
* Execute DEFAULT expression as a coercion expression, because
|
||||||
* its result is already coerced to the target type.
|
* its result is already coerced to the target type.
|
||||||
|
@ -575,6 +575,7 @@ ExecReScanIndexScan(IndexScanState *node)
|
|||||||
if (node->iss_ReorderQueue)
|
if (node->iss_ReorderQueue)
|
||||||
{
|
{
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
|
|
||||||
while (!pairingheap_is_empty(node->iss_ReorderQueue))
|
while (!pairingheap_is_empty(node->iss_ReorderQueue))
|
||||||
{
|
{
|
||||||
tuple = reorderqueue_pop(node);
|
tuple = reorderqueue_pop(node);
|
||||||
|
@ -375,7 +375,7 @@ static void
|
|||||||
cache_purge_all(MemoizeState *mstate)
|
cache_purge_all(MemoizeState *mstate)
|
||||||
{
|
{
|
||||||
uint64 evictions = mstate->hashtable->members;
|
uint64 evictions = mstate->hashtable->members;
|
||||||
PlanState *pstate = (PlanState *) mstate;
|
PlanState *pstate = (PlanState *) mstate;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Likely the most efficient way to remove all items is to just reset the
|
* Likely the most efficient way to remove all items is to just reset the
|
||||||
|
@ -831,7 +831,7 @@ ExecInsert(ModifyTableContext *context,
|
|||||||
{
|
{
|
||||||
TupleDesc tdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
|
TupleDesc tdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
|
||||||
TupleDesc plan_tdesc =
|
TupleDesc plan_tdesc =
|
||||||
CreateTupleDescCopy(planSlot->tts_tupleDescriptor);
|
CreateTupleDescCopy(planSlot->tts_tupleDescriptor);
|
||||||
|
|
||||||
resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
|
resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
|
||||||
MakeSingleTupleTableSlot(tdesc, slot->tts_ops);
|
MakeSingleTupleTableSlot(tdesc, slot->tts_ops);
|
||||||
|
@ -2267,10 +2267,10 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
|
stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
|
||||||
src,
|
src,
|
||||||
plan->argtypes,
|
plan->argtypes,
|
||||||
plan->nargs,
|
plan->nargs,
|
||||||
_SPI_current->queryEnv);
|
_SPI_current->queryEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finish filling in the CachedPlanSource */
|
/* Finish filling in the CachedPlanSource */
|
||||||
@ -2504,10 +2504,10 @@ _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
|
stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
|
||||||
src,
|
src,
|
||||||
plan->argtypes,
|
plan->argtypes,
|
||||||
plan->nargs,
|
plan->nargs,
|
||||||
_SPI_current->queryEnv);
|
_SPI_current->queryEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finish filling in the CachedPlanSource */
|
/* Finish filling in the CachedPlanSource */
|
||||||
|
@ -890,8 +890,8 @@ llvm_shutdown(int code, Datum arg)
|
|||||||
* has occurred in the middle of LLVM code. It is not safe to call back
|
* has occurred in the middle of LLVM code. It is not safe to call back
|
||||||
* into LLVM (which is why a FATAL error was thrown).
|
* into LLVM (which is why a FATAL error was thrown).
|
||||||
*
|
*
|
||||||
* We do need to shutdown LLVM in other shutdown cases, otherwise
|
* We do need to shutdown LLVM in other shutdown cases, otherwise e.g.
|
||||||
* e.g. profiling data won't be written out.
|
* profiling data won't be written out.
|
||||||
*/
|
*/
|
||||||
if (llvm_in_fatal_on_oom())
|
if (llvm_in_fatal_on_oom())
|
||||||
{
|
{
|
||||||
|
@ -634,9 +634,9 @@ dshash_seq_next(dshash_seq_status *status)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Not yet holding any partition locks. Need to determine the size of the
|
* Not yet holding any partition locks. Need to determine the size of the
|
||||||
* hash table, it could have been resized since we were looking
|
* hash table, it could have been resized since we were looking last.
|
||||||
* last. Since we iterate in partition order, we can start by
|
* Since we iterate in partition order, we can start by unconditionally
|
||||||
* unconditionally lock partition 0.
|
* lock partition 0.
|
||||||
*
|
*
|
||||||
* Once we hold the lock, no resizing can happen until the scan ends. So
|
* Once we hold the lock, no resizing can happen until the scan ends. So
|
||||||
* we don't need to repeatedly call ensure_valid_bucket_pointers().
|
* we don't need to repeatedly call ensure_valid_bucket_pointers().
|
||||||
|
@ -1967,8 +1967,8 @@ retry:
|
|||||||
* because no code should expect latches to survive across
|
* because no code should expect latches to survive across
|
||||||
* CHECK_FOR_INTERRUPTS().
|
* CHECK_FOR_INTERRUPTS().
|
||||||
*/
|
*/
|
||||||
ResetLatch(MyLatch);
|
ResetLatch(MyLatch);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2343,7 +2343,7 @@ _copyJsonReturning(const JsonReturning *from)
|
|||||||
static JsonValueExpr *
|
static JsonValueExpr *
|
||||||
_copyJsonValueExpr(const JsonValueExpr *from)
|
_copyJsonValueExpr(const JsonValueExpr *from)
|
||||||
{
|
{
|
||||||
JsonValueExpr *newnode = makeNode(JsonValueExpr);
|
JsonValueExpr *newnode = makeNode(JsonValueExpr);
|
||||||
|
|
||||||
COPY_NODE_FIELD(raw_expr);
|
COPY_NODE_FIELD(raw_expr);
|
||||||
COPY_NODE_FIELD(formatted_expr);
|
COPY_NODE_FIELD(formatted_expr);
|
||||||
@ -2358,7 +2358,7 @@ _copyJsonValueExpr(const JsonValueExpr *from)
|
|||||||
static JsonParseExpr *
|
static JsonParseExpr *
|
||||||
_copyJsonParseExpr(const JsonParseExpr *from)
|
_copyJsonParseExpr(const JsonParseExpr *from)
|
||||||
{
|
{
|
||||||
JsonParseExpr *newnode = makeNode(JsonParseExpr);
|
JsonParseExpr *newnode = makeNode(JsonParseExpr);
|
||||||
|
|
||||||
COPY_NODE_FIELD(expr);
|
COPY_NODE_FIELD(expr);
|
||||||
COPY_NODE_FIELD(output);
|
COPY_NODE_FIELD(output);
|
||||||
@ -2488,7 +2488,7 @@ _copyJsonObjectAgg(const JsonObjectAgg *from)
|
|||||||
static JsonOutput *
|
static JsonOutput *
|
||||||
_copyJsonOutput(const JsonOutput *from)
|
_copyJsonOutput(const JsonOutput *from)
|
||||||
{
|
{
|
||||||
JsonOutput *newnode = makeNode(JsonOutput);
|
JsonOutput *newnode = makeNode(JsonOutput);
|
||||||
|
|
||||||
COPY_NODE_FIELD(typeName);
|
COPY_NODE_FIELD(typeName);
|
||||||
COPY_NODE_FIELD(returning);
|
COPY_NODE_FIELD(returning);
|
||||||
@ -2550,7 +2550,7 @@ _copyJsonArrayQueryConstructor(const JsonArrayQueryConstructor *from)
|
|||||||
static JsonExpr *
|
static JsonExpr *
|
||||||
_copyJsonExpr(const JsonExpr *from)
|
_copyJsonExpr(const JsonExpr *from)
|
||||||
{
|
{
|
||||||
JsonExpr *newnode = makeNode(JsonExpr);
|
JsonExpr *newnode = makeNode(JsonExpr);
|
||||||
|
|
||||||
COPY_SCALAR_FIELD(op);
|
COPY_SCALAR_FIELD(op);
|
||||||
COPY_NODE_FIELD(formatted_expr);
|
COPY_NODE_FIELD(formatted_expr);
|
||||||
@ -2614,7 +2614,7 @@ _copyJsonItemCoercions(const JsonItemCoercions *from)
|
|||||||
static JsonFuncExpr *
|
static JsonFuncExpr *
|
||||||
_copyJsonFuncExpr(const JsonFuncExpr *from)
|
_copyJsonFuncExpr(const JsonFuncExpr *from)
|
||||||
{
|
{
|
||||||
JsonFuncExpr *newnode = makeNode(JsonFuncExpr);
|
JsonFuncExpr *newnode = makeNode(JsonFuncExpr);
|
||||||
|
|
||||||
COPY_SCALAR_FIELD(op);
|
COPY_SCALAR_FIELD(op);
|
||||||
COPY_NODE_FIELD(common);
|
COPY_NODE_FIELD(common);
|
||||||
@ -2651,7 +2651,7 @@ _copyJsonIsPredicate(const JsonIsPredicate *from)
|
|||||||
static JsonBehavior *
|
static JsonBehavior *
|
||||||
_copyJsonBehavior(const JsonBehavior *from)
|
_copyJsonBehavior(const JsonBehavior *from)
|
||||||
{
|
{
|
||||||
JsonBehavior *newnode = makeNode(JsonBehavior);
|
JsonBehavior *newnode = makeNode(JsonBehavior);
|
||||||
|
|
||||||
COPY_SCALAR_FIELD(btype);
|
COPY_SCALAR_FIELD(btype);
|
||||||
COPY_NODE_FIELD(default_expr);
|
COPY_NODE_FIELD(default_expr);
|
||||||
@ -2665,7 +2665,7 @@ _copyJsonBehavior(const JsonBehavior *from)
|
|||||||
static JsonCommon *
|
static JsonCommon *
|
||||||
_copyJsonCommon(const JsonCommon *from)
|
_copyJsonCommon(const JsonCommon *from)
|
||||||
{
|
{
|
||||||
JsonCommon *newnode = makeNode(JsonCommon);
|
JsonCommon *newnode = makeNode(JsonCommon);
|
||||||
|
|
||||||
COPY_NODE_FIELD(expr);
|
COPY_NODE_FIELD(expr);
|
||||||
COPY_NODE_FIELD(pathspec);
|
COPY_NODE_FIELD(pathspec);
|
||||||
@ -2682,7 +2682,7 @@ _copyJsonCommon(const JsonCommon *from)
|
|||||||
static JsonArgument *
|
static JsonArgument *
|
||||||
_copyJsonArgument(const JsonArgument *from)
|
_copyJsonArgument(const JsonArgument *from)
|
||||||
{
|
{
|
||||||
JsonArgument *newnode = makeNode(JsonArgument);
|
JsonArgument *newnode = makeNode(JsonArgument);
|
||||||
|
|
||||||
COPY_NODE_FIELD(val);
|
COPY_NODE_FIELD(val);
|
||||||
COPY_STRING_FIELD(name);
|
COPY_STRING_FIELD(name);
|
||||||
@ -2696,7 +2696,7 @@ _copyJsonArgument(const JsonArgument *from)
|
|||||||
static JsonTable *
|
static JsonTable *
|
||||||
_copyJsonTable(const JsonTable *from)
|
_copyJsonTable(const JsonTable *from)
|
||||||
{
|
{
|
||||||
JsonTable *newnode = makeNode(JsonTable);
|
JsonTable *newnode = makeNode(JsonTable);
|
||||||
|
|
||||||
COPY_NODE_FIELD(common);
|
COPY_NODE_FIELD(common);
|
||||||
COPY_NODE_FIELD(columns);
|
COPY_NODE_FIELD(columns);
|
||||||
@ -5480,7 +5480,7 @@ _copyExtensibleNode(const ExtensibleNode *from)
|
|||||||
static Integer *
|
static Integer *
|
||||||
_copyInteger(const Integer *from)
|
_copyInteger(const Integer *from)
|
||||||
{
|
{
|
||||||
Integer *newnode = makeNode(Integer);
|
Integer *newnode = makeNode(Integer);
|
||||||
|
|
||||||
COPY_SCALAR_FIELD(ival);
|
COPY_SCALAR_FIELD(ival);
|
||||||
|
|
||||||
@ -5500,7 +5500,7 @@ _copyFloat(const Float *from)
|
|||||||
static Boolean *
|
static Boolean *
|
||||||
_copyBoolean(const Boolean *from)
|
_copyBoolean(const Boolean *from)
|
||||||
{
|
{
|
||||||
Boolean *newnode = makeNode(Boolean);
|
Boolean *newnode = makeNode(Boolean);
|
||||||
|
|
||||||
COPY_SCALAR_FIELD(boolval);
|
COPY_SCALAR_FIELD(boolval);
|
||||||
|
|
||||||
@ -5520,7 +5520,7 @@ _copyString(const String *from)
|
|||||||
static BitString *
|
static BitString *
|
||||||
_copyBitString(const BitString *from)
|
_copyBitString(const BitString *from)
|
||||||
{
|
{
|
||||||
BitString *newnode = makeNode(BitString);
|
BitString *newnode = makeNode(BitString);
|
||||||
|
|
||||||
COPY_STRING_FIELD(bsval);
|
COPY_STRING_FIELD(bsval);
|
||||||
|
|
||||||
|
@ -2802,8 +2802,7 @@ static bool
|
|||||||
_equalA_Const(const A_Const *a, const A_Const *b)
|
_equalA_Const(const A_Const *a, const A_Const *b)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Hack for in-line val field. Also val is not valid is isnull is
|
* Hack for in-line val field. Also val is not valid is isnull is true.
|
||||||
* true.
|
|
||||||
*/
|
*/
|
||||||
if (!a->isnull && !b->isnull &&
|
if (!a->isnull && !b->isnull &&
|
||||||
!equal(&a->val, &b->val))
|
!equal(&a->val, &b->val))
|
||||||
|
@ -1003,7 +1003,7 @@ exprCollation(const Node *expr)
|
|||||||
break;
|
break;
|
||||||
case T_JsonExpr:
|
case T_JsonExpr:
|
||||||
{
|
{
|
||||||
JsonExpr *jexpr = (JsonExpr *) expr;
|
JsonExpr *jexpr = (JsonExpr *) expr;
|
||||||
JsonCoercion *coercion = jexpr->result_coercion;
|
JsonCoercion *coercion = jexpr->result_coercion;
|
||||||
|
|
||||||
if (!coercion)
|
if (!coercion)
|
||||||
@ -1239,7 +1239,8 @@ exprSetCollation(Node *expr, Oid collation)
|
|||||||
if (ctor->coercion)
|
if (ctor->coercion)
|
||||||
exprSetCollation((Node *) ctor->coercion, collation);
|
exprSetCollation((Node *) ctor->coercion, collation);
|
||||||
else
|
else
|
||||||
Assert(!OidIsValid(collation)); /* result is always a json[b] type */
|
Assert(!OidIsValid(collation)); /* result is always a
|
||||||
|
* json[b] type */
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_JsonIsPredicate:
|
case T_JsonIsPredicate:
|
||||||
@ -1247,7 +1248,7 @@ exprSetCollation(Node *expr, Oid collation)
|
|||||||
break;
|
break;
|
||||||
case T_JsonExpr:
|
case T_JsonExpr:
|
||||||
{
|
{
|
||||||
JsonExpr *jexpr = (JsonExpr *) expr;
|
JsonExpr *jexpr = (JsonExpr *) expr;
|
||||||
JsonCoercion *coercion = jexpr->result_coercion;
|
JsonCoercion *coercion = jexpr->result_coercion;
|
||||||
|
|
||||||
if (!coercion)
|
if (!coercion)
|
||||||
@ -2496,7 +2497,7 @@ expression_tree_walker(Node *node,
|
|||||||
return walker(((JsonIsPredicate *) node)->expr, context);
|
return walker(((JsonIsPredicate *) node)->expr, context);
|
||||||
case T_JsonExpr:
|
case T_JsonExpr:
|
||||||
{
|
{
|
||||||
JsonExpr *jexpr = (JsonExpr *) node;
|
JsonExpr *jexpr = (JsonExpr *) node;
|
||||||
|
|
||||||
if (walker(jexpr->formatted_expr, context))
|
if (walker(jexpr->formatted_expr, context))
|
||||||
return true;
|
return true;
|
||||||
@ -3568,8 +3569,8 @@ expression_tree_mutator(Node *node,
|
|||||||
break;
|
break;
|
||||||
case T_JsonExpr:
|
case T_JsonExpr:
|
||||||
{
|
{
|
||||||
JsonExpr *jexpr = (JsonExpr *) node;
|
JsonExpr *jexpr = (JsonExpr *) node;
|
||||||
JsonExpr *newnode;
|
JsonExpr *newnode;
|
||||||
|
|
||||||
FLATCOPY(newnode, jexpr, JsonExpr);
|
FLATCOPY(newnode, jexpr, JsonExpr);
|
||||||
MUTATE(newnode->path_spec, jexpr->path_spec, Node *);
|
MUTATE(newnode->path_spec, jexpr->path_spec, Node *);
|
||||||
@ -4545,7 +4546,7 @@ raw_expression_tree_walker(Node *node,
|
|||||||
break;
|
break;
|
||||||
case T_JsonTableColumn:
|
case T_JsonTableColumn:
|
||||||
{
|
{
|
||||||
JsonTableColumn *jtc = (JsonTableColumn *) node;
|
JsonTableColumn *jtc = (JsonTableColumn *) node;
|
||||||
|
|
||||||
if (walker(jtc->typeName, context))
|
if (walker(jtc->typeName, context))
|
||||||
return true;
|
return true;
|
||||||
|
@ -3613,8 +3613,8 @@ static void
|
|||||||
_outFloat(StringInfo str, const Float *node)
|
_outFloat(StringInfo str, const Float *node)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We assume the value is a valid numeric literal and so does not
|
* We assume the value is a valid numeric literal and so does not need
|
||||||
* need quoting.
|
* quoting.
|
||||||
*/
|
*/
|
||||||
appendStringInfoString(str, node->fval);
|
appendStringInfoString(str, node->fval);
|
||||||
}
|
}
|
||||||
@ -3629,8 +3629,8 @@ static void
|
|||||||
_outString(StringInfo str, const String *node)
|
_outString(StringInfo str, const String *node)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We use outToken to provide escaping of the string's content,
|
* We use outToken to provide escaping of the string's content, but we
|
||||||
* but we don't want it to do anything with an empty string.
|
* don't want it to do anything with an empty string.
|
||||||
*/
|
*/
|
||||||
appendStringInfoChar(str, '"');
|
appendStringInfoChar(str, '"');
|
||||||
if (node->sval[0] != '\0')
|
if (node->sval[0] != '\0')
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
Integer *
|
Integer *
|
||||||
makeInteger(int i)
|
makeInteger(int i)
|
||||||
{
|
{
|
||||||
Integer *v = makeNode(Integer);
|
Integer *v = makeNode(Integer);
|
||||||
|
|
||||||
v->ival = i;
|
v->ival = i;
|
||||||
return v;
|
return v;
|
||||||
@ -48,7 +48,7 @@ makeFloat(char *numericStr)
|
|||||||
Boolean *
|
Boolean *
|
||||||
makeBoolean(bool val)
|
makeBoolean(bool val)
|
||||||
{
|
{
|
||||||
Boolean *v = makeNode(Boolean);
|
Boolean *v = makeNode(Boolean);
|
||||||
|
|
||||||
v->boolval = val;
|
v->boolval = val;
|
||||||
return v;
|
return v;
|
||||||
|
@ -1777,17 +1777,18 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When building a fractional path, determine a cheapest fractional
|
* When building a fractional path, determine a cheapest
|
||||||
* path for each child relation too. Looking at startup and total
|
* fractional path for each child relation too. Looking at startup
|
||||||
* costs is not enough, because the cheapest fractional path may be
|
* and total costs is not enough, because the cheapest fractional
|
||||||
* dominated by two separate paths (one for startup, one for total).
|
* path may be dominated by two separate paths (one for startup,
|
||||||
|
* one for total).
|
||||||
*
|
*
|
||||||
* When needed (building fractional path), determine the cheapest
|
* When needed (building fractional path), determine the cheapest
|
||||||
* fractional path too.
|
* fractional path too.
|
||||||
*/
|
*/
|
||||||
if (root->tuple_fraction > 0)
|
if (root->tuple_fraction > 0)
|
||||||
{
|
{
|
||||||
double path_fraction = (1.0 / root->tuple_fraction);
|
double path_fraction = (1.0 / root->tuple_fraction);
|
||||||
|
|
||||||
cheapest_fractional =
|
cheapest_fractional =
|
||||||
get_cheapest_fractional_path_for_pathkeys(childrel->pathlist,
|
get_cheapest_fractional_path_for_pathkeys(childrel->pathlist,
|
||||||
@ -1796,8 +1797,8 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
path_fraction);
|
path_fraction);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we found no path with matching pathkeys, use the cheapest
|
* If we found no path with matching pathkeys, use the
|
||||||
* total path instead.
|
* cheapest total path instead.
|
||||||
*
|
*
|
||||||
* XXX We might consider partially sorted paths too (with an
|
* XXX We might consider partially sorted paths too (with an
|
||||||
* incremental sort on top). But we'd have to build all the
|
* incremental sort on top). But we'd have to build all the
|
||||||
|
@ -1794,7 +1794,7 @@ is_fake_var(Expr *expr)
|
|||||||
static double
|
static double
|
||||||
get_width_cost_multiplier(PlannerInfo *root, Expr *expr)
|
get_width_cost_multiplier(PlannerInfo *root, Expr *expr)
|
||||||
{
|
{
|
||||||
double width = -1.0; /* fake value */
|
double width = -1.0; /* fake value */
|
||||||
|
|
||||||
if (IsA(expr, RelabelType))
|
if (IsA(expr, RelabelType))
|
||||||
expr = (Expr *) ((RelabelType *) expr)->arg;
|
expr = (Expr *) ((RelabelType *) expr)->arg;
|
||||||
@ -1802,17 +1802,17 @@ get_width_cost_multiplier(PlannerInfo *root, Expr *expr)
|
|||||||
/* Try to find actual stat in corresponding relation */
|
/* Try to find actual stat in corresponding relation */
|
||||||
if (IsA(expr, Var))
|
if (IsA(expr, Var))
|
||||||
{
|
{
|
||||||
Var *var = (Var *) expr;
|
Var *var = (Var *) expr;
|
||||||
|
|
||||||
if (var->varno > 0 && var->varno < root->simple_rel_array_size)
|
if (var->varno > 0 && var->varno < root->simple_rel_array_size)
|
||||||
{
|
{
|
||||||
RelOptInfo *rel = root->simple_rel_array[var->varno];
|
RelOptInfo *rel = root->simple_rel_array[var->varno];
|
||||||
|
|
||||||
if (rel != NULL &&
|
if (rel != NULL &&
|
||||||
var->varattno >= rel->min_attr &&
|
var->varattno >= rel->min_attr &&
|
||||||
var->varattno <= rel->max_attr)
|
var->varattno <= rel->max_attr)
|
||||||
{
|
{
|
||||||
int ndx = var->varattno - rel->min_attr;
|
int ndx = var->varattno - rel->min_attr;
|
||||||
|
|
||||||
if (rel->attr_widths[ndx] > 0)
|
if (rel->attr_widths[ndx] > 0)
|
||||||
width = rel->attr_widths[ndx];
|
width = rel->attr_widths[ndx];
|
||||||
@ -1823,7 +1823,7 @@ get_width_cost_multiplier(PlannerInfo *root, Expr *expr)
|
|||||||
/* Didn't find any actual stats, try using type width instead. */
|
/* Didn't find any actual stats, try using type width instead. */
|
||||||
if (width < 0.0)
|
if (width < 0.0)
|
||||||
{
|
{
|
||||||
Node *node = (Node*) expr;
|
Node *node = (Node *) expr;
|
||||||
|
|
||||||
width = get_typavgwidth(exprType(node), exprTypmod(node));
|
width = get_typavgwidth(exprType(node), exprTypmod(node));
|
||||||
}
|
}
|
||||||
@ -1832,17 +1832,17 @@ get_width_cost_multiplier(PlannerInfo *root, Expr *expr)
|
|||||||
* Values are passed as Datum type, so comparisons can't be cheaper than
|
* Values are passed as Datum type, so comparisons can't be cheaper than
|
||||||
* comparing a Datum value.
|
* comparing a Datum value.
|
||||||
*
|
*
|
||||||
* FIXME I find this reasoning questionable. We may pass int2, and comparing
|
* FIXME I find this reasoning questionable. We may pass int2, and
|
||||||
* it is probably a bit cheaper than comparing a bigint.
|
* comparing it is probably a bit cheaper than comparing a bigint.
|
||||||
*/
|
*/
|
||||||
if (width <= sizeof(Datum))
|
if (width <= sizeof(Datum))
|
||||||
return 1.0;
|
return 1.0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We consider the cost of a comparison not to be directly proportional to
|
* We consider the cost of a comparison not to be directly proportional to
|
||||||
* width of the argument, because widths of the arguments could be slightly
|
* width of the argument, because widths of the arguments could be
|
||||||
* different (we only know the average width for the whole column). So we
|
* slightly different (we only know the average width for the whole
|
||||||
* use log16(width) as an estimate.
|
* column). So we use log16(width) as an estimate.
|
||||||
*/
|
*/
|
||||||
return 1.0 + 0.125 * LOG2(width / sizeof(Datum));
|
return 1.0 + 0.125 * LOG2(width / sizeof(Datum));
|
||||||
}
|
}
|
||||||
@ -1902,23 +1902,23 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
|
|||||||
bool heapSort)
|
bool heapSort)
|
||||||
{
|
{
|
||||||
Cost per_tuple_cost = 0.0;
|
Cost per_tuple_cost = 0.0;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
List *pathkeyExprs = NIL;
|
List *pathkeyExprs = NIL;
|
||||||
double tuplesPerPrevGroup = tuples;
|
double tuplesPerPrevGroup = tuples;
|
||||||
double totalFuncCost = 1.0;
|
double totalFuncCost = 1.0;
|
||||||
bool has_fake_var = false;
|
bool has_fake_var = false;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
Oid prev_datatype = InvalidOid;
|
Oid prev_datatype = InvalidOid;
|
||||||
List *cache_varinfos = NIL;
|
List *cache_varinfos = NIL;
|
||||||
|
|
||||||
/* fallback if pathkeys is unknown */
|
/* fallback if pathkeys is unknown */
|
||||||
if (list_length(pathkeys) == 0)
|
if (list_length(pathkeys) == 0)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If we'll use a bounded heap-sort keeping just K tuples in memory, for
|
* If we'll use a bounded heap-sort keeping just K tuples in memory,
|
||||||
* a total number of tuple comparisons of N log2 K; but the constant
|
* for a total number of tuple comparisons of N log2 K; but the
|
||||||
* factor is a bit higher than for quicksort. Tweak it so that the cost
|
* constant factor is a bit higher than for quicksort. Tweak it so
|
||||||
* curve is continuous at the crossover point.
|
* that the cost curve is continuous at the crossover point.
|
||||||
*/
|
*/
|
||||||
output_tuples = (heapSort) ? 2.0 * output_tuples : tuples;
|
output_tuples = (heapSort) ? 2.0 * output_tuples : tuples;
|
||||||
per_tuple_cost += 2.0 * cpu_operator_cost * LOG2(output_tuples);
|
per_tuple_cost += 2.0 * cpu_operator_cost * LOG2(output_tuples);
|
||||||
@ -1930,17 +1930,17 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Computing total cost of sorting takes into account:
|
* Computing total cost of sorting takes into account the per-column
|
||||||
* - per column comparison function cost
|
* comparison function cost. We try to compute the needed number of
|
||||||
* - we try to compute needed number of comparison per column
|
* comparisons per column.
|
||||||
*/
|
*/
|
||||||
foreach(lc, pathkeys)
|
foreach(lc, pathkeys)
|
||||||
{
|
{
|
||||||
PathKey *pathkey = (PathKey*) lfirst(lc);
|
PathKey *pathkey = (PathKey *) lfirst(lc);
|
||||||
EquivalenceMember *em;
|
EquivalenceMember *em;
|
||||||
double nGroups,
|
double nGroups,
|
||||||
correctedNGroups;
|
correctedNGroups;
|
||||||
Cost funcCost = 1.0;
|
Cost funcCost = 1.0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We believe that equivalence members aren't very different, so, to
|
* We believe that equivalence members aren't very different, so, to
|
||||||
@ -1985,10 +1985,10 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
|
|||||||
pathkeyExprs = lappend(pathkeyExprs, em->em_expr);
|
pathkeyExprs = lappend(pathkeyExprs, em->em_expr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need to calculate the number of comparisons for this column, which
|
* We need to calculate the number of comparisons for this column,
|
||||||
* requires knowing the group size. So we estimate the number of groups
|
* which requires knowing the group size. So we estimate the number of
|
||||||
* by calling estimate_num_groups_incremental(), which estimates the
|
* groups by calling estimate_num_groups_incremental(), which
|
||||||
* group size for "new" pathkeys.
|
* estimates the group size for "new" pathkeys.
|
||||||
*
|
*
|
||||||
* Note: estimate_num_groups_incremental does not handle fake Vars, so
|
* Note: estimate_num_groups_incremental does not handle fake Vars, so
|
||||||
* use a default estimate otherwise.
|
* use a default estimate otherwise.
|
||||||
@ -1999,26 +1999,30 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
|
|||||||
&cache_varinfos,
|
&cache_varinfos,
|
||||||
list_length(pathkeyExprs) - 1);
|
list_length(pathkeyExprs) - 1);
|
||||||
else if (tuples > 4.0)
|
else if (tuples > 4.0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use geometric mean as estimation if there are no stats.
|
* Use geometric mean as estimation if there are no stats.
|
||||||
*
|
*
|
||||||
* We don't use DEFAULT_NUM_DISTINCT here, because that’s used for
|
* We don't use DEFAULT_NUM_DISTINCT here, because that's used for
|
||||||
* a single column, but here we’re dealing with multiple columns.
|
* a single column, but here we're dealing with multiple columns.
|
||||||
*/
|
*/
|
||||||
nGroups = ceil(2.0 + sqrt(tuples) * (i + 1) / list_length(pathkeys));
|
nGroups = ceil(2.0 + sqrt(tuples) * (i + 1) / list_length(pathkeys));
|
||||||
else
|
else
|
||||||
nGroups = tuples;
|
nGroups = tuples;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Presorted keys are not considered in the cost above, but we still do
|
* Presorted keys are not considered in the cost above, but we still
|
||||||
* have to compare them in the qsort comparator. So make sure to factor
|
* do have to compare them in the qsort comparator. So make sure to
|
||||||
* in the cost in that case.
|
* factor in the cost in that case.
|
||||||
*/
|
*/
|
||||||
if (i >= nPresortedKeys)
|
if (i >= nPresortedKeys)
|
||||||
{
|
{
|
||||||
if (heapSort)
|
if (heapSort)
|
||||||
{
|
{
|
||||||
/* have to keep at least one group, and a multiple of group size */
|
/*
|
||||||
|
* have to keep at least one group, and a multiple of group
|
||||||
|
* size
|
||||||
|
*/
|
||||||
correctedNGroups = ceil(output_tuples / tuplesPerPrevGroup);
|
correctedNGroups = ceil(output_tuples / tuplesPerPrevGroup);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2033,19 +2037,20 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
|
|||||||
i++;
|
i++;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Uniform distributions with all groups being of the same size are the
|
* Uniform distributions with all groups being of the same size are
|
||||||
* best case, with nice smooth behavior. Real-world distributions tend
|
* the best case, with nice smooth behavior. Real-world distributions
|
||||||
* not to be uniform, though, and we don’t have any reliable easy-to-use
|
* tend not to be uniform, though, and we don't have any reliable
|
||||||
* information. As a basic defense against skewed distributions, we use
|
* easy-to-use information. As a basic defense against skewed
|
||||||
* a 1.5 factor to make the expected group a bit larger, but we need to
|
* distributions, we use a 1.5 factor to make the expected group a bit
|
||||||
* be careful not to make the group larger than in the preceding step.
|
* larger, but we need to be careful not to make the group larger than
|
||||||
|
* in the preceding step.
|
||||||
*/
|
*/
|
||||||
tuplesPerPrevGroup = Min(tuplesPerPrevGroup,
|
tuplesPerPrevGroup = Min(tuplesPerPrevGroup,
|
||||||
ceil(1.5 * tuplesPerPrevGroup / nGroups));
|
ceil(1.5 * tuplesPerPrevGroup / nGroups));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Once we get single-row group, it means tuples in the group are unique
|
* Once we get single-row group, it means tuples in the group are
|
||||||
* and we can skip all remaining columns.
|
* unique and we can skip all remaining columns.
|
||||||
*/
|
*/
|
||||||
if (tuplesPerPrevGroup <= 1.0)
|
if (tuplesPerPrevGroup <= 1.0)
|
||||||
break;
|
break;
|
||||||
@ -2057,15 +2062,15 @@ compute_cpu_sort_cost(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
|
|||||||
per_tuple_cost *= cpu_operator_cost;
|
per_tuple_cost *= cpu_operator_cost;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Accordingly to "Introduction to algorithms", Thomas H. Cormen, Charles E.
|
* Accordingly to "Introduction to algorithms", Thomas H. Cormen, Charles
|
||||||
* Leiserson, Ronald L. Rivest, ISBN 0-07-013143-0, quicksort estimation
|
* E. Leiserson, Ronald L. Rivest, ISBN 0-07-013143-0, quicksort
|
||||||
* formula has additional term proportional to number of tuples (See Chapter
|
* estimation formula has additional term proportional to number of tuples
|
||||||
* 8.2 and Theorem 4.1). That affects cases with a low number of tuples,
|
* (see Chapter 8.2 and Theorem 4.1). That affects cases with a low number
|
||||||
* approximately less than 1e4. We could implement it as an additional
|
* of tuples, approximately less than 1e4. We could implement it as an
|
||||||
* multiplier under the logarithm, but we use a bit more complex formula
|
* additional multiplier under the logarithm, but we use a bit more
|
||||||
* which takes into account the number of unique tuples and it’s not clear
|
* complex formula which takes into account the number of unique tuples
|
||||||
* how to combine the multiplier with the number of groups. Estimate it as
|
* and it's not clear how to combine the multiplier with the number of
|
||||||
* 10 in cpu_operator_cost unit.
|
* groups. Estimate it as 10 cpu_operator_cost units.
|
||||||
*/
|
*/
|
||||||
per_tuple_cost += 10 * cpu_operator_cost;
|
per_tuple_cost += 10 * cpu_operator_cost;
|
||||||
|
|
||||||
@ -2082,7 +2087,7 @@ cost_sort_estimate(PlannerInfo *root, List *pathkeys, int nPresortedKeys,
|
|||||||
double tuples)
|
double tuples)
|
||||||
{
|
{
|
||||||
return compute_cpu_sort_cost(root, pathkeys, nPresortedKeys,
|
return compute_cpu_sort_cost(root, pathkeys, nPresortedKeys,
|
||||||
0, tuples, tuples, false);
|
0, tuples, tuples, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -685,9 +685,9 @@ get_eclass_for_sort_expr(PlannerInfo *root,
|
|||||||
/*
|
/*
|
||||||
* Match!
|
* Match!
|
||||||
*
|
*
|
||||||
* Copy the sortref if it wasn't set yet. That may happen if the
|
* Copy the sortref if it wasn't set yet. That may happen if
|
||||||
* ec was constructed from WHERE clause, i.e. it doesn't have a
|
* the ec was constructed from WHERE clause, i.e. it doesn't
|
||||||
* target reference at all.
|
* have a target reference at all.
|
||||||
*/
|
*/
|
||||||
if (cur_ec->ec_sortref == 0 && sortref > 0)
|
if (cur_ec->ec_sortref == 0 && sortref > 0)
|
||||||
cur_ec->ec_sortref = sortref;
|
cur_ec->ec_sortref = sortref;
|
||||||
|
@ -1258,7 +1258,7 @@ sort_inner_and_outer(PlannerInfo *root,
|
|||||||
|
|
||||||
foreach(l, all_pathkeys)
|
foreach(l, all_pathkeys)
|
||||||
{
|
{
|
||||||
PathKey *front_pathkey = (PathKey *) lfirst(l);
|
PathKey *front_pathkey = (PathKey *) lfirst(l);
|
||||||
List *cur_mergeclauses;
|
List *cur_mergeclauses;
|
||||||
List *outerkeys;
|
List *outerkeys;
|
||||||
List *innerkeys;
|
List *innerkeys;
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
#include "utils/selfuncs.h"
|
#include "utils/selfuncs.h"
|
||||||
|
|
||||||
/* Consider reordering of GROUP BY keys? */
|
/* Consider reordering of GROUP BY keys? */
|
||||||
bool enable_group_by_reordering = true;
|
bool enable_group_by_reordering = true;
|
||||||
|
|
||||||
static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
|
static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
|
||||||
static bool matches_boolean_partition_clause(RestrictInfo *rinfo,
|
static bool matches_boolean_partition_clause(RestrictInfo *rinfo,
|
||||||
@ -352,7 +352,7 @@ int
|
|||||||
group_keys_reorder_by_pathkeys(List *pathkeys, List **group_pathkeys,
|
group_keys_reorder_by_pathkeys(List *pathkeys, List **group_pathkeys,
|
||||||
List **group_clauses)
|
List **group_clauses)
|
||||||
{
|
{
|
||||||
List *new_group_pathkeys= NIL,
|
List *new_group_pathkeys = NIL,
|
||||||
*new_group_clauses = NIL;
|
*new_group_clauses = NIL;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
int n;
|
int n;
|
||||||
@ -365,16 +365,16 @@ group_keys_reorder_by_pathkeys(List *pathkeys, List **group_pathkeys,
|
|||||||
* there's a matching GROUP BY key. If we find one, we append it to the
|
* there's a matching GROUP BY key. If we find one, we append it to the
|
||||||
* list, and do the same for the clauses.
|
* list, and do the same for the clauses.
|
||||||
*
|
*
|
||||||
* Once we find the first pathkey without a matching GROUP BY key, the rest
|
* Once we find the first pathkey without a matching GROUP BY key, the
|
||||||
* of the pathkeys are useless and can't be used to evaluate the grouping,
|
* rest of the pathkeys are useless and can't be used to evaluate the
|
||||||
* so we abort the loop and ignore the remaining pathkeys.
|
* grouping, so we abort the loop and ignore the remaining pathkeys.
|
||||||
*
|
*
|
||||||
* XXX Pathkeys are built in a way to allow simply comparing pointers.
|
* XXX Pathkeys are built in a way to allow simply comparing pointers.
|
||||||
*/
|
*/
|
||||||
foreach(lc, pathkeys)
|
foreach(lc, pathkeys)
|
||||||
{
|
{
|
||||||
PathKey *pathkey = (PathKey *) lfirst(lc);
|
PathKey *pathkey = (PathKey *) lfirst(lc);
|
||||||
SortGroupClause *sgc;
|
SortGroupClause *sgc;
|
||||||
|
|
||||||
/* abort on first mismatch */
|
/* abort on first mismatch */
|
||||||
if (!list_member_ptr(*group_pathkeys, pathkey))
|
if (!list_member_ptr(*group_pathkeys, pathkey))
|
||||||
@ -403,13 +403,14 @@ group_keys_reorder_by_pathkeys(List *pathkeys, List **group_pathkeys,
|
|||||||
/*
|
/*
|
||||||
* Used to generate all permutations of a pathkey list.
|
* Used to generate all permutations of a pathkey list.
|
||||||
*/
|
*/
|
||||||
typedef struct PathkeyMutatorState {
|
typedef struct PathkeyMutatorState
|
||||||
|
{
|
||||||
List *elemsList;
|
List *elemsList;
|
||||||
ListCell **elemCells;
|
ListCell **elemCells;
|
||||||
void **elems;
|
void **elems;
|
||||||
int *positions;
|
int *positions;
|
||||||
int mutatorNColumns;
|
int mutatorNColumns;
|
||||||
int count;
|
int count;
|
||||||
} PathkeyMutatorState;
|
} PathkeyMutatorState;
|
||||||
|
|
||||||
|
|
||||||
@ -428,9 +429,9 @@ typedef struct PathkeyMutatorState {
|
|||||||
static void
|
static void
|
||||||
PathkeyMutatorInit(PathkeyMutatorState *state, List *elems, int start, int end)
|
PathkeyMutatorInit(PathkeyMutatorState *state, List *elems, int start, int end)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int n = end - start;
|
int n = end - start;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
memset(state, 0, sizeof(*state));
|
memset(state, 0, sizeof(*state));
|
||||||
|
|
||||||
@ -438,8 +439,8 @@ PathkeyMutatorInit(PathkeyMutatorState *state, List *elems, int start, int end)
|
|||||||
|
|
||||||
state->elemsList = list_copy(elems);
|
state->elemsList = list_copy(elems);
|
||||||
|
|
||||||
state->elems = palloc(sizeof(void*) * n);
|
state->elems = palloc(sizeof(void *) * n);
|
||||||
state->elemCells = palloc(sizeof(ListCell*) * n);
|
state->elemCells = palloc(sizeof(ListCell *) * n);
|
||||||
state->positions = palloc(sizeof(int) * n);
|
state->positions = palloc(sizeof(int) * n);
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
@ -459,10 +460,10 @@ PathkeyMutatorInit(PathkeyMutatorState *state, List *elems, int start, int end)
|
|||||||
static void
|
static void
|
||||||
PathkeyMutatorSwap(int *a, int i, int j)
|
PathkeyMutatorSwap(int *a, int i, int j)
|
||||||
{
|
{
|
||||||
int s = a[i];
|
int s = a[i];
|
||||||
|
|
||||||
a[i] = a[j];
|
a[i] = a[j];
|
||||||
a[j] = s;
|
a[j] = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -471,7 +472,10 @@ PathkeyMutatorSwap(int *a, int i, int j)
|
|||||||
static bool
|
static bool
|
||||||
PathkeyMutatorNextSet(int *a, int n)
|
PathkeyMutatorNextSet(int *a, int n)
|
||||||
{
|
{
|
||||||
int j, k, l, r;
|
int j,
|
||||||
|
k,
|
||||||
|
l,
|
||||||
|
r;
|
||||||
|
|
||||||
j = n - 2;
|
j = n - 2;
|
||||||
|
|
||||||
@ -507,7 +511,7 @@ PathkeyMutatorNextSet(int *a, int n)
|
|||||||
static List *
|
static List *
|
||||||
PathkeyMutatorNext(PathkeyMutatorState *state)
|
PathkeyMutatorNext(PathkeyMutatorState *state)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
state->count++;
|
state->count++;
|
||||||
|
|
||||||
@ -528,9 +532,9 @@ PathkeyMutatorNext(PathkeyMutatorState *state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* update the list cells to point to the right elements */
|
/* update the list cells to point to the right elements */
|
||||||
for(i = 0; i < state->mutatorNColumns; i++)
|
for (i = 0; i < state->mutatorNColumns; i++)
|
||||||
lfirst(state->elemCells[i]) =
|
lfirst(state->elemCells[i]) =
|
||||||
(void *) state->elems[ state->positions[i] - 1 ];
|
(void *) state->elems[state->positions[i] - 1];
|
||||||
|
|
||||||
return state->elemsList;
|
return state->elemsList;
|
||||||
}
|
}
|
||||||
@ -541,7 +545,7 @@ PathkeyMutatorNext(PathkeyMutatorState *state)
|
|||||||
typedef struct PathkeySortCost
|
typedef struct PathkeySortCost
|
||||||
{
|
{
|
||||||
Cost cost;
|
Cost cost;
|
||||||
PathKey *pathkey;
|
PathKey *pathkey;
|
||||||
} PathkeySortCost;
|
} PathkeySortCost;
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -581,41 +585,42 @@ get_cheapest_group_keys_order(PlannerInfo *root, double nrows,
|
|||||||
List **group_pathkeys, List **group_clauses,
|
List **group_pathkeys, List **group_clauses,
|
||||||
int n_preordered)
|
int n_preordered)
|
||||||
{
|
{
|
||||||
List *new_group_pathkeys = NIL,
|
List *new_group_pathkeys = NIL,
|
||||||
*new_group_clauses = NIL,
|
*new_group_clauses = NIL,
|
||||||
*var_group_pathkeys;
|
*var_group_pathkeys;
|
||||||
|
|
||||||
ListCell *cell;
|
ListCell *cell;
|
||||||
PathkeyMutatorState mstate;
|
PathkeyMutatorState mstate;
|
||||||
double cheapest_sort_cost = -1.0;
|
double cheapest_sort_cost = -1.0;
|
||||||
|
|
||||||
int nFreeKeys;
|
int nFreeKeys;
|
||||||
int nToPermute;
|
int nToPermute;
|
||||||
|
|
||||||
/* If there are less than 2 unsorted pathkeys, we're done. */
|
/* If there are less than 2 unsorted pathkeys, we're done. */
|
||||||
if (list_length(*group_pathkeys) - n_preordered < 2)
|
if (list_length(*group_pathkeys) - n_preordered < 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We could exhaustively cost all possible orderings of the pathkeys, but for
|
* We could exhaustively cost all possible orderings of the pathkeys, but
|
||||||
* a large number of pathkeys it might be prohibitively expensive. So we try
|
* for a large number of pathkeys it might be prohibitively expensive. So
|
||||||
* to apply simple cheap heuristics first - we sort the pathkeys by sort cost
|
* we try to apply simple cheap heuristics first - we sort the pathkeys by
|
||||||
* (as if the pathkey was sorted independently) and then check only the four
|
* sort cost (as if the pathkey was sorted independently) and then check
|
||||||
* cheapest pathkeys. The remaining pathkeys are kept ordered by cost.
|
* only the four cheapest pathkeys. The remaining pathkeys are kept
|
||||||
|
* ordered by cost.
|
||||||
*
|
*
|
||||||
* XXX This is a very simple heuristics, but likely to work fine for most
|
* XXX This is a very simple heuristics, but likely to work fine for most
|
||||||
* cases (because the number of GROUP BY clauses tends to be lower than 4).
|
* cases (because the number of GROUP BY clauses tends to be lower than
|
||||||
* But it ignores how the number of distinct values in each pathkey affects
|
* 4). But it ignores how the number of distinct values in each pathkey
|
||||||
* the following steps. It might be better to use "more expensive" pathkey
|
* affects the following steps. It might be better to use "more expensive"
|
||||||
* first if it has many distinct values, because it then limits the number
|
* pathkey first if it has many distinct values, because it then limits
|
||||||
* of comparisons for the remaining pathkeys. But evaluating that is likely
|
* the number of comparisons for the remaining pathkeys. But evaluating
|
||||||
* quite the expensive.
|
* that is likely quite the expensive.
|
||||||
*/
|
*/
|
||||||
nFreeKeys = list_length(*group_pathkeys) - n_preordered;
|
nFreeKeys = list_length(*group_pathkeys) - n_preordered;
|
||||||
nToPermute = 4;
|
nToPermute = 4;
|
||||||
if (nFreeKeys > nToPermute)
|
if (nFreeKeys > nToPermute)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
PathkeySortCost *costs = palloc(sizeof(PathkeySortCost) * nFreeKeys);
|
PathkeySortCost *costs = palloc(sizeof(PathkeySortCost) * nFreeKeys);
|
||||||
|
|
||||||
/* skip the pre-ordered pathkeys */
|
/* skip the pre-ordered pathkeys */
|
||||||
@ -624,7 +629,7 @@ get_cheapest_group_keys_order(PlannerInfo *root, double nrows,
|
|||||||
/* estimate cost for sorting individual pathkeys */
|
/* estimate cost for sorting individual pathkeys */
|
||||||
for (i = 0; cell != NULL; i++, (cell = lnext(*group_pathkeys, cell)))
|
for (i = 0; cell != NULL; i++, (cell = lnext(*group_pathkeys, cell)))
|
||||||
{
|
{
|
||||||
List *to_cost = list_make1(lfirst(cell));
|
List *to_cost = list_make1(lfirst(cell));
|
||||||
|
|
||||||
Assert(i < nFreeKeys);
|
Assert(i < nFreeKeys);
|
||||||
|
|
||||||
@ -658,28 +663,29 @@ get_cheapest_group_keys_order(PlannerInfo *root, double nrows,
|
|||||||
Assert(list_length(new_group_pathkeys) == list_length(*group_pathkeys));
|
Assert(list_length(new_group_pathkeys) == list_length(*group_pathkeys));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate pathkey lists with permutations of the first nToPermute pathkeys.
|
* Generate pathkey lists with permutations of the first nToPermute
|
||||||
|
* pathkeys.
|
||||||
*
|
*
|
||||||
* XXX We simply calculate sort cost for each individual pathkey list, but
|
* XXX We simply calculate sort cost for each individual pathkey list, but
|
||||||
* there's room for two dynamic programming optimizations here. Firstly, we
|
* there's room for two dynamic programming optimizations here. Firstly,
|
||||||
* may pass the current "best" cost to cost_sort_estimate so that it can
|
* we may pass the current "best" cost to cost_sort_estimate so that it
|
||||||
* "abort" if the estimated pathkeys list exceeds it. Secondly, it could pass
|
* can "abort" if the estimated pathkeys list exceeds it. Secondly, it
|
||||||
* the return information about the position when it exceeded the cost, and
|
* could pass the return information about the position when it exceeded
|
||||||
* we could skip all permutations with the same prefix.
|
* the cost, and we could skip all permutations with the same prefix.
|
||||||
*
|
*
|
||||||
* Imagine we've already found ordering with cost C1, and we're evaluating
|
* Imagine we've already found ordering with cost C1, and we're evaluating
|
||||||
* another ordering - cost_sort_estimate() calculates cost by adding the
|
* another ordering - cost_sort_estimate() calculates cost by adding the
|
||||||
* pathkeys one by one (more or less), and the cost only grows. If at any
|
* pathkeys one by one (more or less), and the cost only grows. If at any
|
||||||
* point it exceeds C1, it can't possibly be "better" so we can discard it.
|
* point it exceeds C1, it can't possibly be "better" so we can discard
|
||||||
* But we also know that we can discard all ordering with the same prefix,
|
* it. But we also know that we can discard all ordering with the same
|
||||||
* because if we're estimating (a,b,c,d) and we exceed C1 at (a,b) then the
|
* prefix, because if we're estimating (a,b,c,d) and we exceed C1 at (a,b)
|
||||||
* same thing will happen for any ordering with this prefix.
|
* then the same thing will happen for any ordering with this prefix.
|
||||||
*/
|
*/
|
||||||
PathkeyMutatorInit(&mstate, new_group_pathkeys, n_preordered, n_preordered + nToPermute);
|
PathkeyMutatorInit(&mstate, new_group_pathkeys, n_preordered, n_preordered + nToPermute);
|
||||||
|
|
||||||
while((var_group_pathkeys = PathkeyMutatorNext(&mstate)) != NIL)
|
while ((var_group_pathkeys = PathkeyMutatorNext(&mstate)) != NIL)
|
||||||
{
|
{
|
||||||
Cost cost;
|
Cost cost;
|
||||||
|
|
||||||
cost = cost_sort_estimate(root, var_group_pathkeys, n_preordered, nrows);
|
cost = cost_sort_estimate(root, var_group_pathkeys, n_preordered, nrows);
|
||||||
|
|
||||||
@ -694,11 +700,11 @@ get_cheapest_group_keys_order(PlannerInfo *root, double nrows,
|
|||||||
/* Reorder the group clauses according to the reordered pathkeys. */
|
/* Reorder the group clauses according to the reordered pathkeys. */
|
||||||
foreach(cell, new_group_pathkeys)
|
foreach(cell, new_group_pathkeys)
|
||||||
{
|
{
|
||||||
PathKey *pathkey = (PathKey *) lfirst(cell);
|
PathKey *pathkey = (PathKey *) lfirst(cell);
|
||||||
|
|
||||||
new_group_clauses = lappend(new_group_clauses,
|
new_group_clauses = lappend(new_group_clauses,
|
||||||
get_sortgroupref_clause(pathkey->pk_eclass->ec_sortref,
|
get_sortgroupref_clause(pathkey->pk_eclass->ec_sortref,
|
||||||
*group_clauses));
|
*group_clauses));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Just append the rest GROUP BY clauses */
|
/* Just append the rest GROUP BY clauses */
|
||||||
@ -745,8 +751,8 @@ get_useful_group_keys_orderings(PlannerInfo *root, double nrows,
|
|||||||
PathKeyInfo *info;
|
PathKeyInfo *info;
|
||||||
int n_preordered = 0;
|
int n_preordered = 0;
|
||||||
|
|
||||||
List *pathkeys = group_pathkeys;
|
List *pathkeys = group_pathkeys;
|
||||||
List *clauses = group_clauses;
|
List *clauses = group_clauses;
|
||||||
|
|
||||||
/* always return at least the original pathkeys/clauses */
|
/* always return at least the original pathkeys/clauses */
|
||||||
info = makeNode(PathKeyInfo);
|
info = makeNode(PathKeyInfo);
|
||||||
@ -756,9 +762,9 @@ get_useful_group_keys_orderings(PlannerInfo *root, double nrows,
|
|||||||
infos = lappend(infos, info);
|
infos = lappend(infos, info);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Should we try generating alternative orderings of the group keys? If not,
|
* Should we try generating alternative orderings of the group keys? If
|
||||||
* we produce only the order specified in the query, i.e. the optimization
|
* not, we produce only the order specified in the query, i.e. the
|
||||||
* is effectively disabled.
|
* optimization is effectively disabled.
|
||||||
*/
|
*/
|
||||||
if (!enable_group_by_reordering)
|
if (!enable_group_by_reordering)
|
||||||
return infos;
|
return infos;
|
||||||
@ -782,8 +788,9 @@ get_useful_group_keys_orderings(PlannerInfo *root, double nrows,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the path is sorted in some way, try reordering the group keys to match
|
* If the path is sorted in some way, try reordering the group keys to
|
||||||
* as much of the ordering as possible - we get this sort for free (mostly).
|
* match as much of the ordering as possible - we get this sort for free
|
||||||
|
* (mostly).
|
||||||
*
|
*
|
||||||
* We must not do this when there are no grouping sets, because those use
|
* We must not do this when there are no grouping sets, because those use
|
||||||
* more complex logic to decide the ordering.
|
* more complex logic to decide the ordering.
|
||||||
@ -2400,8 +2407,8 @@ pathkeys_useful_for_ordering(PlannerInfo *root, List *pathkeys)
|
|||||||
static int
|
static int
|
||||||
pathkeys_useful_for_grouping(PlannerInfo *root, List *pathkeys)
|
pathkeys_useful_for_grouping(PlannerInfo *root, List *pathkeys)
|
||||||
{
|
{
|
||||||
ListCell *key;
|
ListCell *key;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
/* no special ordering requested for grouping */
|
/* no special ordering requested for grouping */
|
||||||
if (root->group_pathkeys == NIL)
|
if (root->group_pathkeys == NIL)
|
||||||
@ -2414,7 +2421,7 @@ pathkeys_useful_for_grouping(PlannerInfo *root, List *pathkeys)
|
|||||||
/* walk the pathkeys and search for matching group key */
|
/* walk the pathkeys and search for matching group key */
|
||||||
foreach(key, pathkeys)
|
foreach(key, pathkeys)
|
||||||
{
|
{
|
||||||
PathKey *pathkey = (PathKey *) lfirst(key);
|
PathKey *pathkey = (PathKey *) lfirst(key);
|
||||||
|
|
||||||
/* no matching group key, we're done */
|
/* no matching group key, we're done */
|
||||||
if (!list_member_ptr(root->group_pathkeys, pathkey))
|
if (!list_member_ptr(root->group_pathkeys, pathkey))
|
||||||
|
@ -1162,8 +1162,8 @@ mark_async_capable_plan(Plan *plan, Path *path)
|
|||||||
case T_ProjectionPath:
|
case T_ProjectionPath:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the generated plan node includes a Result node for
|
* If the generated plan node includes a Result node for the
|
||||||
* the projection, we can't execute it asynchronously.
|
* projection, we can't execute it asynchronously.
|
||||||
*/
|
*/
|
||||||
if (IsA(plan, Result))
|
if (IsA(plan, Result))
|
||||||
return false;
|
return false;
|
||||||
|
@ -6250,7 +6250,7 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
|
|||||||
Assert(list_length(pathkey_orderings) > 0);
|
Assert(list_length(pathkey_orderings) > 0);
|
||||||
|
|
||||||
/* process all potentially interesting grouping reorderings */
|
/* process all potentially interesting grouping reorderings */
|
||||||
foreach (lc2, pathkey_orderings)
|
foreach(lc2, pathkey_orderings)
|
||||||
{
|
{
|
||||||
bool is_sorted;
|
bool is_sorted;
|
||||||
int presorted_keys = 0;
|
int presorted_keys = 0;
|
||||||
@ -6283,8 +6283,8 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
|
|||||||
else if (parse->hasAggs)
|
else if (parse->hasAggs)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We have aggregation, possibly with plain GROUP BY. Make
|
* We have aggregation, possibly with plain GROUP BY.
|
||||||
* an AggPath.
|
* Make an AggPath.
|
||||||
*/
|
*/
|
||||||
add_path(grouped_rel, (Path *)
|
add_path(grouped_rel, (Path *)
|
||||||
create_agg_path(root,
|
create_agg_path(root,
|
||||||
@ -6301,8 +6301,8 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
|
|||||||
else if (group_clauses)
|
else if (group_clauses)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We have GROUP BY without aggregation or grouping sets.
|
* We have GROUP BY without aggregation or grouping
|
||||||
* Make a GroupPath.
|
* sets. Make a GroupPath.
|
||||||
*/
|
*/
|
||||||
add_path(grouped_rel, (Path *)
|
add_path(grouped_rel, (Path *)
|
||||||
create_group_path(root,
|
create_group_path(root,
|
||||||
@ -6321,8 +6321,8 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Now we may consider incremental sort on this path, but only
|
* Now we may consider incremental sort on this path, but only
|
||||||
* when the path is not already sorted and when incremental sort
|
* when the path is not already sorted and when incremental
|
||||||
* is enabled.
|
* sort is enabled.
|
||||||
*/
|
*/
|
||||||
if (is_sorted || !enable_incremental_sort)
|
if (is_sorted || !enable_incremental_sort)
|
||||||
continue;
|
continue;
|
||||||
@ -6335,8 +6335,9 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We should have already excluded pathkeys of length 1 because
|
* We should have already excluded pathkeys of length 1
|
||||||
* then presorted_keys > 0 would imply is_sorted was true.
|
* because then presorted_keys > 0 would imply is_sorted was
|
||||||
|
* true.
|
||||||
*/
|
*/
|
||||||
Assert(list_length(root->group_pathkeys) != 1);
|
Assert(list_length(root->group_pathkeys) != 1);
|
||||||
|
|
||||||
@ -6357,8 +6358,8 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
|
|||||||
else if (parse->hasAggs)
|
else if (parse->hasAggs)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We have aggregation, possibly with plain GROUP BY. Make an
|
* We have aggregation, possibly with plain GROUP BY. Make
|
||||||
* AggPath.
|
* an AggPath.
|
||||||
*/
|
*/
|
||||||
add_path(grouped_rel, (Path *)
|
add_path(grouped_rel, (Path *)
|
||||||
create_agg_path(root,
|
create_agg_path(root,
|
||||||
@ -6375,8 +6376,8 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
|
|||||||
else if (parse->groupClause)
|
else if (parse->groupClause)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We have GROUP BY without aggregation or grouping sets. Make
|
* We have GROUP BY without aggregation or grouping sets.
|
||||||
* a GroupPath.
|
* Make a GroupPath.
|
||||||
*/
|
*/
|
||||||
add_path(grouped_rel, (Path *)
|
add_path(grouped_rel, (Path *)
|
||||||
create_group_path(root,
|
create_group_path(root,
|
||||||
@ -6421,7 +6422,7 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
|
|||||||
Assert(list_length(pathkey_orderings) > 0);
|
Assert(list_length(pathkey_orderings) > 0);
|
||||||
|
|
||||||
/* process all potentially interesting grouping reorderings */
|
/* process all potentially interesting grouping reorderings */
|
||||||
foreach (lc2, pathkey_orderings)
|
foreach(lc2, pathkey_orderings)
|
||||||
{
|
{
|
||||||
bool is_sorted;
|
bool is_sorted;
|
||||||
int presorted_keys = 0;
|
int presorted_keys = 0;
|
||||||
@ -6435,8 +6436,8 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
|
|||||||
&presorted_keys);
|
&presorted_keys);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Insert a Sort node, if required. But there's no point in
|
* Insert a Sort node, if required. But there's no point
|
||||||
* sorting anything but the cheapest path.
|
* in sorting anything but the cheapest path.
|
||||||
*/
|
*/
|
||||||
if (!is_sorted)
|
if (!is_sorted)
|
||||||
{
|
{
|
||||||
@ -6471,24 +6472,30 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
|
|||||||
dNumGroups));
|
dNumGroups));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now we may consider incremental sort on this path, but only
|
* Now we may consider incremental sort on this path, but
|
||||||
* when the path is not already sorted and when incremental
|
* only when the path is not already sorted and when
|
||||||
* sort is enabled.
|
* incremental sort is enabled.
|
||||||
*/
|
*/
|
||||||
if (is_sorted || !enable_incremental_sort)
|
if (is_sorted || !enable_incremental_sort)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Restore the input path (we might have added Sort on top). */
|
/*
|
||||||
|
* Restore the input path (we might have added Sort on
|
||||||
|
* top).
|
||||||
|
*/
|
||||||
path = path_original;
|
path = path_original;
|
||||||
|
|
||||||
/* no shared prefix, not point in building incremental sort */
|
/*
|
||||||
|
* no shared prefix, not point in building incremental
|
||||||
|
* sort
|
||||||
|
*/
|
||||||
if (presorted_keys == 0)
|
if (presorted_keys == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We should have already excluded pathkeys of length 1
|
* We should have already excluded pathkeys of length 1
|
||||||
* because then presorted_keys > 0 would imply is_sorted was
|
* because then presorted_keys > 0 would imply is_sorted
|
||||||
* true.
|
* was true.
|
||||||
*/
|
*/
|
||||||
Assert(list_length(root->group_pathkeys) != 1);
|
Assert(list_length(root->group_pathkeys) != 1);
|
||||||
|
|
||||||
@ -6741,7 +6748,7 @@ create_partial_grouping_paths(PlannerInfo *root,
|
|||||||
Assert(list_length(pathkey_orderings) > 0);
|
Assert(list_length(pathkey_orderings) > 0);
|
||||||
|
|
||||||
/* process all potentially interesting grouping reorderings */
|
/* process all potentially interesting grouping reorderings */
|
||||||
foreach (lc2, pathkey_orderings)
|
foreach(lc2, pathkey_orderings)
|
||||||
{
|
{
|
||||||
bool is_sorted;
|
bool is_sorted;
|
||||||
int presorted_keys = 0;
|
int presorted_keys = 0;
|
||||||
@ -6874,7 +6881,7 @@ create_partial_grouping_paths(PlannerInfo *root,
|
|||||||
Assert(list_length(pathkey_orderings) > 0);
|
Assert(list_length(pathkey_orderings) > 0);
|
||||||
|
|
||||||
/* process all potentially interesting grouping reorderings */
|
/* process all potentially interesting grouping reorderings */
|
||||||
foreach (lc2, pathkey_orderings)
|
foreach(lc2, pathkey_orderings)
|
||||||
{
|
{
|
||||||
bool is_sorted;
|
bool is_sorted;
|
||||||
int presorted_keys = 0;
|
int presorted_keys = 0;
|
||||||
@ -6924,8 +6931,8 @@ create_partial_grouping_paths(PlannerInfo *root,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Now we may consider incremental sort on this path, but only
|
* Now we may consider incremental sort on this path, but only
|
||||||
* when the path is not already sorted and when incremental sort
|
* when the path is not already sorted and when incremental
|
||||||
* is enabled.
|
* sort is enabled.
|
||||||
*/
|
*/
|
||||||
if (is_sorted || !enable_incremental_sort)
|
if (is_sorted || !enable_incremental_sort)
|
||||||
continue;
|
continue;
|
||||||
@ -6938,8 +6945,9 @@ create_partial_grouping_paths(PlannerInfo *root,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We should have already excluded pathkeys of length 1 because
|
* We should have already excluded pathkeys of length 1
|
||||||
* then presorted_keys > 0 would imply is_sorted was true.
|
* because then presorted_keys > 0 would imply is_sorted was
|
||||||
|
* true.
|
||||||
*/
|
*/
|
||||||
Assert(list_length(root->group_pathkeys) != 1);
|
Assert(list_length(root->group_pathkeys) != 1);
|
||||||
|
|
||||||
|
@ -391,7 +391,7 @@ contain_mutable_functions_walker(Node *node, void *context)
|
|||||||
const JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
|
const JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
bool is_jsonb =
|
bool is_jsonb =
|
||||||
ctor->returning->format->format_type == JS_FORMAT_JSONB;
|
ctor->returning->format->format_type == JS_FORMAT_JSONB;
|
||||||
|
|
||||||
/* Check argument_type => json[b] conversions */
|
/* Check argument_type => json[b] conversions */
|
||||||
foreach(lc, ctor->args)
|
foreach(lc, ctor->args)
|
||||||
@ -899,7 +899,7 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
|
|||||||
/* JsonExpr is parallel-unsafe if subtransactions can be used. */
|
/* JsonExpr is parallel-unsafe if subtransactions can be used. */
|
||||||
else if (IsA(node, JsonExpr))
|
else if (IsA(node, JsonExpr))
|
||||||
{
|
{
|
||||||
JsonExpr *jsexpr = (JsonExpr *) node;
|
JsonExpr *jsexpr = (JsonExpr *) node;
|
||||||
|
|
||||||
if (ExecEvalJsonNeedsSubTransaction(jsexpr, NULL))
|
if (ExecEvalJsonNeedsSubTransaction(jsexpr, NULL))
|
||||||
{
|
{
|
||||||
@ -3581,7 +3581,7 @@ eval_const_expressions_mutator(Node *node,
|
|||||||
context->case_val = raw;
|
context->case_val = raw;
|
||||||
|
|
||||||
formatted = eval_const_expressions_mutator((Node *) jve->formatted_expr,
|
formatted = eval_const_expressions_mutator((Node *) jve->formatted_expr,
|
||||||
context);
|
context);
|
||||||
|
|
||||||
context->case_val = save_case_val;
|
context->case_val = save_case_val;
|
||||||
|
|
||||||
@ -5315,7 +5315,7 @@ pull_paramids_walker(Node *node, Bitmapset **context)
|
|||||||
return false;
|
return false;
|
||||||
if (IsA(node, Param))
|
if (IsA(node, Param))
|
||||||
{
|
{
|
||||||
Param *param = (Param *)node;
|
Param *param = (Param *) node;
|
||||||
|
|
||||||
*context = bms_add_member(*context, param->paramid);
|
*context = bms_add_member(*context, param->paramid);
|
||||||
return false;
|
return false;
|
||||||
|
@ -968,102 +968,102 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
|
|||||||
|
|
||||||
if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind))
|
if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind))
|
||||||
{
|
{
|
||||||
table_relation_estimate_size(rel, attr_widths, pages, tuples,
|
table_relation_estimate_size(rel, attr_widths, pages, tuples,
|
||||||
allvisfrac);
|
allvisfrac);
|
||||||
}
|
}
|
||||||
else if (rel->rd_rel->relkind == RELKIND_INDEX)
|
else if (rel->rd_rel->relkind == RELKIND_INDEX)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* XXX: It'd probably be good to move this into a callback, individual
|
||||||
|
* index types e.g. know if they have a metapage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* it has storage, ok to call the smgr */
|
||||||
|
curpages = RelationGetNumberOfBlocks(rel);
|
||||||
|
|
||||||
|
/* report estimated # pages */
|
||||||
|
*pages = curpages;
|
||||||
|
/* quick exit if rel is clearly empty */
|
||||||
|
if (curpages == 0)
|
||||||
|
{
|
||||||
|
*tuples = 0;
|
||||||
|
*allvisfrac = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* coerce values in pg_class to more desirable types */
|
||||||
|
relpages = (BlockNumber) rel->rd_rel->relpages;
|
||||||
|
reltuples = (double) rel->rd_rel->reltuples;
|
||||||
|
relallvisible = (BlockNumber) rel->rd_rel->relallvisible;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Discount the metapage while estimating the number of tuples. This
|
||||||
|
* is a kluge because it assumes more than it ought to about index
|
||||||
|
* structure. Currently it's OK for btree, hash, and GIN indexes but
|
||||||
|
* suspect for GiST indexes.
|
||||||
|
*/
|
||||||
|
if (relpages > 0)
|
||||||
|
{
|
||||||
|
curpages--;
|
||||||
|
relpages--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* estimate number of tuples from previous tuple density */
|
||||||
|
if (reltuples >= 0 && relpages > 0)
|
||||||
|
density = reltuples / (double) relpages;
|
||||||
|
else
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* XXX: It'd probably be good to move this into a callback,
|
* If we have no data because the relation was never vacuumed,
|
||||||
* individual index types e.g. know if they have a metapage.
|
* estimate tuple width from attribute datatypes. We assume here
|
||||||
|
* that the pages are completely full, which is OK for tables
|
||||||
|
* (since they've presumably not been VACUUMed yet) but is
|
||||||
|
* probably an overestimate for indexes. Fortunately
|
||||||
|
* get_relation_info() can clamp the overestimate to the parent
|
||||||
|
* table's size.
|
||||||
|
*
|
||||||
|
* Note: this code intentionally disregards alignment
|
||||||
|
* considerations, because (a) that would be gilding the lily
|
||||||
|
* considering how crude the estimate is, and (b) it creates
|
||||||
|
* platform dependencies in the default plans which are kind of a
|
||||||
|
* headache for regression testing.
|
||||||
|
*
|
||||||
|
* XXX: Should this logic be more index specific?
|
||||||
*/
|
*/
|
||||||
|
int32 tuple_width;
|
||||||
|
|
||||||
/* it has storage, ok to call the smgr */
|
tuple_width = get_rel_data_width(rel, attr_widths);
|
||||||
curpages = RelationGetNumberOfBlocks(rel);
|
tuple_width += MAXALIGN(SizeofHeapTupleHeader);
|
||||||
|
tuple_width += sizeof(ItemIdData);
|
||||||
|
/* note: integer division is intentional here */
|
||||||
|
density = (BLCKSZ - SizeOfPageHeaderData) / tuple_width;
|
||||||
|
}
|
||||||
|
*tuples = rint(density * (double) curpages);
|
||||||
|
|
||||||
/* report estimated # pages */
|
/*
|
||||||
*pages = curpages;
|
* We use relallvisible as-is, rather than scaling it up like we do
|
||||||
/* quick exit if rel is clearly empty */
|
* for the pages and tuples counts, on the theory that any pages added
|
||||||
if (curpages == 0)
|
* since the last VACUUM are most likely not marked all-visible. But
|
||||||
{
|
* costsize.c wants it converted to a fraction.
|
||||||
*tuples = 0;
|
*/
|
||||||
*allvisfrac = 0;
|
if (relallvisible == 0 || curpages <= 0)
|
||||||
return;
|
*allvisfrac = 0;
|
||||||
}
|
else if ((double) relallvisible >= curpages)
|
||||||
|
*allvisfrac = 1;
|
||||||
/* coerce values in pg_class to more desirable types */
|
else
|
||||||
relpages = (BlockNumber) rel->rd_rel->relpages;
|
*allvisfrac = (double) relallvisible / curpages;
|
||||||
reltuples = (double) rel->rd_rel->reltuples;
|
|
||||||
relallvisible = (BlockNumber) rel->rd_rel->relallvisible;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Discount the metapage while estimating the number of tuples.
|
|
||||||
* This is a kluge because it assumes more than it ought to about
|
|
||||||
* index structure. Currently it's OK for btree, hash, and GIN
|
|
||||||
* indexes but suspect for GiST indexes.
|
|
||||||
*/
|
|
||||||
if (relpages > 0)
|
|
||||||
{
|
|
||||||
curpages--;
|
|
||||||
relpages--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* estimate number of tuples from previous tuple density */
|
|
||||||
if (reltuples >= 0 && relpages > 0)
|
|
||||||
density = reltuples / (double) relpages;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* If we have no data because the relation was never vacuumed,
|
|
||||||
* estimate tuple width from attribute datatypes. We assume
|
|
||||||
* here that the pages are completely full, which is OK for
|
|
||||||
* tables (since they've presumably not been VACUUMed yet) but
|
|
||||||
* is probably an overestimate for indexes. Fortunately
|
|
||||||
* get_relation_info() can clamp the overestimate to the
|
|
||||||
* parent table's size.
|
|
||||||
*
|
|
||||||
* Note: this code intentionally disregards alignment
|
|
||||||
* considerations, because (a) that would be gilding the lily
|
|
||||||
* considering how crude the estimate is, and (b) it creates
|
|
||||||
* platform dependencies in the default plans which are kind
|
|
||||||
* of a headache for regression testing.
|
|
||||||
*
|
|
||||||
* XXX: Should this logic be more index specific?
|
|
||||||
*/
|
|
||||||
int32 tuple_width;
|
|
||||||
|
|
||||||
tuple_width = get_rel_data_width(rel, attr_widths);
|
|
||||||
tuple_width += MAXALIGN(SizeofHeapTupleHeader);
|
|
||||||
tuple_width += sizeof(ItemIdData);
|
|
||||||
/* note: integer division is intentional here */
|
|
||||||
density = (BLCKSZ - SizeOfPageHeaderData) / tuple_width;
|
|
||||||
}
|
|
||||||
*tuples = rint(density * (double) curpages);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We use relallvisible as-is, rather than scaling it up like we
|
|
||||||
* do for the pages and tuples counts, on the theory that any
|
|
||||||
* pages added since the last VACUUM are most likely not marked
|
|
||||||
* all-visible. But costsize.c wants it converted to a fraction.
|
|
||||||
*/
|
|
||||||
if (relallvisible == 0 || curpages <= 0)
|
|
||||||
*allvisfrac = 0;
|
|
||||||
else if ((double) relallvisible >= curpages)
|
|
||||||
*allvisfrac = 1;
|
|
||||||
else
|
|
||||||
*allvisfrac = (double) relallvisible / curpages;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Just use whatever's in pg_class. This covers foreign tables,
|
* Just use whatever's in pg_class. This covers foreign tables,
|
||||||
* sequences, and also relkinds without storage (shouldn't get
|
* sequences, and also relkinds without storage (shouldn't get here?);
|
||||||
* here?); see initializations in AddNewRelationTuple(). Note
|
* see initializations in AddNewRelationTuple(). Note that FDW must
|
||||||
* that FDW must cope if reltuples is -1!
|
* cope if reltuples is -1!
|
||||||
*/
|
*/
|
||||||
*pages = rel->rd_rel->relpages;
|
*pages = rel->rd_rel->relpages;
|
||||||
*tuples = rel->rd_rel->reltuples;
|
*tuples = rel->rd_rel->reltuples;
|
||||||
*allvisfrac = 0;
|
*allvisfrac = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,8 +104,8 @@ static bool test_raw_expression_coverage(Node *node, void *context);
|
|||||||
*/
|
*/
|
||||||
Query *
|
Query *
|
||||||
parse_analyze_fixedparams(RawStmt *parseTree, const char *sourceText,
|
parse_analyze_fixedparams(RawStmt *parseTree, const char *sourceText,
|
||||||
const Oid *paramTypes, int numParams,
|
const Oid *paramTypes, int numParams,
|
||||||
QueryEnvironment *queryEnv)
|
QueryEnvironment *queryEnv)
|
||||||
{
|
{
|
||||||
ParseState *pstate = make_parsestate(NULL);
|
ParseState *pstate = make_parsestate(NULL);
|
||||||
Query *query;
|
Query *query;
|
||||||
@ -2076,8 +2076,8 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
|
|||||||
ListCell *ltl;
|
ListCell *ltl;
|
||||||
ListCell *rtl;
|
ListCell *rtl;
|
||||||
const char *context;
|
const char *context;
|
||||||
bool recursive = (pstate->p_parent_cte &&
|
bool recursive = (pstate->p_parent_cte &&
|
||||||
pstate->p_parent_cte->cterecursive);
|
pstate->p_parent_cte->cterecursive);
|
||||||
|
|
||||||
context = (stmt->op == SETOP_UNION ? "UNION" :
|
context = (stmt->op == SETOP_UNION ? "UNION" :
|
||||||
(stmt->op == SETOP_INTERSECT ? "INTERSECT" :
|
(stmt->op == SETOP_INTERSECT ? "INTERSECT" :
|
||||||
@ -2231,7 +2231,10 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
|
|||||||
setup_parser_errposition_callback(&pcbstate, pstate,
|
setup_parser_errposition_callback(&pcbstate, pstate,
|
||||||
bestlocation);
|
bestlocation);
|
||||||
|
|
||||||
/* If it's a recursive union, we need to require hashing support. */
|
/*
|
||||||
|
* If it's a recursive union, we need to require hashing
|
||||||
|
* support.
|
||||||
|
*/
|
||||||
op->groupClauses = lappend(op->groupClauses,
|
op->groupClauses = lappend(op->groupClauses,
|
||||||
makeSortGroupClauseForSetOp(rescoltype, recursive));
|
makeSortGroupClauseForSetOp(rescoltype, recursive));
|
||||||
|
|
||||||
|
@ -2004,7 +2004,7 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist,
|
|||||||
}
|
}
|
||||||
if (IsA(node, A_Const))
|
if (IsA(node, A_Const))
|
||||||
{
|
{
|
||||||
A_Const *aconst = castNode(A_Const, node);
|
A_Const *aconst = castNode(A_Const, node);
|
||||||
int targetlist_pos = 0;
|
int targetlist_pos = 0;
|
||||||
int target_pos;
|
int target_pos;
|
||||||
|
|
||||||
|
@ -692,8 +692,11 @@ assign_collations_walker(Node *node, assign_collations_context *context)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_JsonExpr:
|
case T_JsonExpr:
|
||||||
/* Context item and PASSING arguments are already
|
|
||||||
* marked with collations in parse_expr.c. */
|
/*
|
||||||
|
* Context item and PASSING arguments are already
|
||||||
|
* marked with collations in parse_expr.c.
|
||||||
|
*/
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
|
@ -3277,7 +3277,7 @@ transformJsonValueExprExt(ParseState *pstate, JsonValueExpr *ve,
|
|||||||
|
|
||||||
if (exprtype == JSONOID || exprtype == JSONBOID)
|
if (exprtype == JSONOID || exprtype == JSONBOID)
|
||||||
{
|
{
|
||||||
format = JS_FORMAT_DEFAULT; /* do not format json[b] types */
|
format = JS_FORMAT_DEFAULT; /* do not format json[b] types */
|
||||||
ereport(WARNING,
|
ereport(WARNING,
|
||||||
(errmsg("FORMAT JSON has no effect for json and jsonb types"),
|
(errmsg("FORMAT JSON has no effect for json and jsonb types"),
|
||||||
parser_errposition(pstate, ve->format->location)));
|
parser_errposition(pstate, ve->format->location)));
|
||||||
@ -3316,7 +3316,7 @@ transformJsonValueExprExt(ParseState *pstate, JsonValueExpr *ve,
|
|||||||
format = default_format;
|
format = default_format;
|
||||||
}
|
}
|
||||||
else if (exprtype == JSONOID || exprtype == JSONBOID)
|
else if (exprtype == JSONOID || exprtype == JSONBOID)
|
||||||
format = JS_FORMAT_DEFAULT; /* do not format json[b] types */
|
format = JS_FORMAT_DEFAULT; /* do not format json[b] types */
|
||||||
else
|
else
|
||||||
format = default_format;
|
format = default_format;
|
||||||
|
|
||||||
@ -3364,13 +3364,13 @@ transformJsonValueExprExt(ParseState *pstate, JsonValueExpr *ve,
|
|||||||
FuncExpr *fexpr;
|
FuncExpr *fexpr;
|
||||||
Oid fnoid;
|
Oid fnoid;
|
||||||
|
|
||||||
if (cast_is_needed) /* only CAST is allowed */
|
if (cast_is_needed) /* only CAST is allowed */
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_CANNOT_COERCE),
|
(errcode(ERRCODE_CANNOT_COERCE),
|
||||||
errmsg("cannot cast type %s to %s",
|
errmsg("cannot cast type %s to %s",
|
||||||
format_type_be(exprtype),
|
format_type_be(exprtype),
|
||||||
format_type_be(targettype)),
|
format_type_be(targettype)),
|
||||||
parser_errposition(pstate, location)));
|
parser_errposition(pstate, location)));
|
||||||
|
|
||||||
fnoid = targettype == JSONOID ? F_TO_JSON : F_TO_JSONB;
|
fnoid = targettype == JSONOID ? F_TO_JSON : F_TO_JSONB;
|
||||||
fexpr = makeFuncExpr(fnoid, targettype, list_make1(expr),
|
fexpr = makeFuncExpr(fnoid, targettype, list_make1(expr),
|
||||||
@ -3444,7 +3444,7 @@ checkJsonOutputFormat(ParseState *pstate, const JsonFormat *format,
|
|||||||
if (format->format_type == JS_FORMAT_JSON)
|
if (format->format_type == JS_FORMAT_JSON)
|
||||||
{
|
{
|
||||||
JsonEncoding enc = format->encoding != JS_ENC_DEFAULT ?
|
JsonEncoding enc = format->encoding != JS_ENC_DEFAULT ?
|
||||||
format->encoding : JS_ENC_UTF8;
|
format->encoding : JS_ENC_UTF8;
|
||||||
|
|
||||||
if (targettype != BYTEAOID &&
|
if (targettype != BYTEAOID &&
|
||||||
format->encoding != JS_ENC_DEFAULT)
|
format->encoding != JS_ENC_DEFAULT)
|
||||||
@ -3583,6 +3583,7 @@ coerceJsonFuncExpr(ParseState *pstate, Node *expr,
|
|||||||
list_make2(texpr, enc),
|
list_make2(texpr, enc),
|
||||||
InvalidOid, InvalidOid,
|
InvalidOid, InvalidOid,
|
||||||
COERCE_EXPLICIT_CALL);
|
COERCE_EXPLICIT_CALL);
|
||||||
|
|
||||||
fexpr->location = location;
|
fexpr->location = location;
|
||||||
|
|
||||||
return (Node *) fexpr;
|
return (Node *) fexpr;
|
||||||
@ -3591,7 +3592,7 @@ coerceJsonFuncExpr(ParseState *pstate, Node *expr,
|
|||||||
/* try to coerce expression to the output type */
|
/* try to coerce expression to the output type */
|
||||||
res = coerce_to_target_type(pstate, expr, exprtype,
|
res = coerce_to_target_type(pstate, expr, exprtype,
|
||||||
returning->typid, returning->typmod,
|
returning->typid, returning->typmod,
|
||||||
/* XXX throwing errors when casting to char(N) */
|
/* XXX throwing errors when casting to char(N) */
|
||||||
COERCION_EXPLICIT,
|
COERCION_EXPLICIT,
|
||||||
COERCE_EXPLICIT_CAST,
|
COERCE_EXPLICIT_CAST,
|
||||||
location);
|
location);
|
||||||
@ -3616,7 +3617,7 @@ makeJsonConstructorExpr(ParseState *pstate, JsonConstructorType type,
|
|||||||
Node *placeholder;
|
Node *placeholder;
|
||||||
Node *coercion;
|
Node *coercion;
|
||||||
Oid intermediate_typid =
|
Oid intermediate_typid =
|
||||||
returning->format->format_type == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
|
returning->format->format_type == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
|
||||||
|
|
||||||
jsctor->args = args;
|
jsctor->args = args;
|
||||||
jsctor->func = fexpr;
|
jsctor->func = fexpr;
|
||||||
@ -3694,7 +3695,7 @@ static Node *
|
|||||||
transformJsonArrayQueryConstructor(ParseState *pstate,
|
transformJsonArrayQueryConstructor(ParseState *pstate,
|
||||||
JsonArrayQueryConstructor *ctor)
|
JsonArrayQueryConstructor *ctor)
|
||||||
{
|
{
|
||||||
SubLink *sublink = makeNode(SubLink);
|
SubLink *sublink = makeNode(SubLink);
|
||||||
SelectStmt *select = makeNode(SelectStmt);
|
SelectStmt *select = makeNode(SelectStmt);
|
||||||
RangeSubselect *range = makeNode(RangeSubselect);
|
RangeSubselect *range = makeNode(RangeSubselect);
|
||||||
Alias *alias = makeNode(Alias);
|
Alias *alias = makeNode(Alias);
|
||||||
@ -3766,8 +3767,8 @@ transformJsonAggConstructor(ParseState *pstate, JsonAggConstructor *agg_ctor,
|
|||||||
Oid aggfnoid;
|
Oid aggfnoid;
|
||||||
Node *node;
|
Node *node;
|
||||||
Expr *aggfilter = agg_ctor->agg_filter ? (Expr *)
|
Expr *aggfilter = agg_ctor->agg_filter ? (Expr *)
|
||||||
transformWhereClause(pstate, agg_ctor->agg_filter,
|
transformWhereClause(pstate, agg_ctor->agg_filter,
|
||||||
EXPR_KIND_FILTER, "FILTER") : NULL;
|
EXPR_KIND_FILTER, "FILTER") : NULL;
|
||||||
|
|
||||||
aggfnoid = DatumGetInt32(DirectFunctionCall1(regprocin,
|
aggfnoid = DatumGetInt32(DirectFunctionCall1(regprocin,
|
||||||
CStringGetDatum(aggfn)));
|
CStringGetDatum(aggfn)));
|
||||||
@ -3809,7 +3810,7 @@ transformJsonAggConstructor(ParseState *pstate, JsonAggConstructor *agg_ctor,
|
|||||||
aggref->aggtype = aggtype;
|
aggref->aggtype = aggtype;
|
||||||
|
|
||||||
/* aggcollid and inputcollid will be set by parse_collate.c */
|
/* aggcollid and inputcollid will be set by parse_collate.c */
|
||||||
aggref->aggtranstype = InvalidOid; /* will be set by planner */
|
aggref->aggtranstype = InvalidOid; /* will be set by planner */
|
||||||
/* aggargtypes will be set by transformAggregateCall */
|
/* aggargtypes will be set by transformAggregateCall */
|
||||||
/* aggdirectargs and args will be set by transformAggregateCall */
|
/* aggdirectargs and args will be set by transformAggregateCall */
|
||||||
/* aggorder and aggdistinct will be set by transformAggregateCall */
|
/* aggorder and aggdistinct will be set by transformAggregateCall */
|
||||||
@ -3818,7 +3819,7 @@ transformJsonAggConstructor(ParseState *pstate, JsonAggConstructor *agg_ctor,
|
|||||||
aggref->aggvariadic = false;
|
aggref->aggvariadic = false;
|
||||||
aggref->aggkind = AGGKIND_NORMAL;
|
aggref->aggkind = AGGKIND_NORMAL;
|
||||||
/* agglevelsup will be set by transformAggregateCall */
|
/* agglevelsup will be set by transformAggregateCall */
|
||||||
aggref->aggsplit = AGGSPLIT_SIMPLE; /* planner might change this */
|
aggref->aggsplit = AGGSPLIT_SIMPLE; /* planner might change this */
|
||||||
aggref->location = agg_ctor->location;
|
aggref->location = agg_ctor->location;
|
||||||
|
|
||||||
transformAggregateCall(pstate, aggref, args, agg_ctor->agg_order, false);
|
transformAggregateCall(pstate, aggref, args, agg_ctor->agg_order, false);
|
||||||
@ -3860,14 +3861,13 @@ transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg)
|
|||||||
{
|
{
|
||||||
if (agg->absent_on_null)
|
if (agg->absent_on_null)
|
||||||
if (agg->unique)
|
if (agg->unique)
|
||||||
aggfnname = "pg_catalog.jsonb_object_agg_unique_strict"; /* F_JSONB_OBJECT_AGG_UNIQUE_STRICT */
|
aggfnname = "pg_catalog.jsonb_object_agg_unique_strict"; /* F_JSONB_OBJECT_AGG_UNIQUE_STRICT */
|
||||||
else
|
else
|
||||||
aggfnname = "pg_catalog.jsonb_object_agg_strict"; /* F_JSONB_OBJECT_AGG_STRICT */
|
aggfnname = "pg_catalog.jsonb_object_agg_strict"; /* F_JSONB_OBJECT_AGG_STRICT */
|
||||||
|
else if (agg->unique)
|
||||||
|
aggfnname = "pg_catalog.jsonb_object_agg_unique"; /* F_JSONB_OBJECT_AGG_UNIQUE */
|
||||||
else
|
else
|
||||||
if (agg->unique)
|
aggfnname = "pg_catalog.jsonb_object_agg"; /* F_JSONB_OBJECT_AGG */
|
||||||
aggfnname = "pg_catalog.jsonb_object_agg_unique"; /* F_JSONB_OBJECT_AGG_UNIQUE */
|
|
||||||
else
|
|
||||||
aggfnname = "pg_catalog.jsonb_object_agg"; /* F_JSONB_OBJECT_AGG */
|
|
||||||
|
|
||||||
aggtype = JSONBOID;
|
aggtype = JSONBOID;
|
||||||
}
|
}
|
||||||
@ -3877,12 +3877,11 @@ transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg)
|
|||||||
if (agg->unique)
|
if (agg->unique)
|
||||||
aggfnname = "pg_catalog.json_object_agg_unique_strict"; /* F_JSON_OBJECT_AGG_UNIQUE_STRICT */
|
aggfnname = "pg_catalog.json_object_agg_unique_strict"; /* F_JSON_OBJECT_AGG_UNIQUE_STRICT */
|
||||||
else
|
else
|
||||||
aggfnname = "pg_catalog.json_object_agg_strict"; /* F_JSON_OBJECT_AGG_STRICT */
|
aggfnname = "pg_catalog.json_object_agg_strict"; /* F_JSON_OBJECT_AGG_STRICT */
|
||||||
|
else if (agg->unique)
|
||||||
|
aggfnname = "pg_catalog.json_object_agg_unique"; /* F_JSON_OBJECT_AGG_UNIQUE */
|
||||||
else
|
else
|
||||||
if (agg->unique)
|
aggfnname = "pg_catalog.json_object_agg"; /* F_JSON_OBJECT_AGG */
|
||||||
aggfnname = "pg_catalog.json_object_agg_unique"; /* F_JSON_OBJECT_AGG_UNIQUE */
|
|
||||||
else
|
|
||||||
aggfnname = "pg_catalog.json_object_agg"; /* F_JSON_OBJECT_AGG */
|
|
||||||
|
|
||||||
aggtype = JSONOID;
|
aggtype = JSONOID;
|
||||||
}
|
}
|
||||||
@ -4209,7 +4208,7 @@ coerceJsonExpr(ParseState *pstate, Node *expr, const JsonReturning *returning)
|
|||||||
* Transform a JSON output clause of JSON_VALUE and JSON_QUERY.
|
* Transform a JSON output clause of JSON_VALUE and JSON_QUERY.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
transformJsonFuncExprOutput(ParseState *pstate, JsonFuncExpr *func,
|
transformJsonFuncExprOutput(ParseState *pstate, JsonFuncExpr *func,
|
||||||
JsonExpr *jsexpr)
|
JsonExpr *jsexpr)
|
||||||
{
|
{
|
||||||
Node *expr = jsexpr->formatted_expr;
|
Node *expr = jsexpr->formatted_expr;
|
||||||
@ -4333,19 +4332,19 @@ initJsonItemCoercions(ParseState *pstate, JsonItemCoercions *coercions,
|
|||||||
Oid typid;
|
Oid typid;
|
||||||
} *p,
|
} *p,
|
||||||
coercionTypids[] =
|
coercionTypids[] =
|
||||||
{
|
{
|
||||||
{ &coercions->null, UNKNOWNOID },
|
{&coercions->null, UNKNOWNOID},
|
||||||
{ &coercions->string, TEXTOID },
|
{&coercions->string, TEXTOID},
|
||||||
{ &coercions->numeric, NUMERICOID },
|
{&coercions->numeric, NUMERICOID},
|
||||||
{ &coercions->boolean, BOOLOID },
|
{&coercions->boolean, BOOLOID},
|
||||||
{ &coercions->date, DATEOID },
|
{&coercions->date, DATEOID},
|
||||||
{ &coercions->time, TIMEOID },
|
{&coercions->time, TIMEOID},
|
||||||
{ &coercions->timetz, TIMETZOID },
|
{&coercions->timetz, TIMETZOID},
|
||||||
{ &coercions->timestamp, TIMESTAMPOID },
|
{&coercions->timestamp, TIMESTAMPOID},
|
||||||
{ &coercions->timestamptz, TIMESTAMPTZOID },
|
{&coercions->timestamptz, TIMESTAMPTZOID},
|
||||||
{ &coercions->composite, contextItemTypeId },
|
{&coercions->composite, contextItemTypeId},
|
||||||
{ NULL, InvalidOid }
|
{NULL, InvalidOid}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (p = coercionTypids; p->coercion; p++)
|
for (p = coercionTypids; p->coercion; p++)
|
||||||
*p->coercion = initJsonItemCoercion(pstate, p->typid, returning);
|
*p->coercion = initJsonItemCoercion(pstate, p->typid, returning);
|
||||||
@ -4512,7 +4511,7 @@ static Node *
|
|||||||
transformJsonParseExpr(ParseState *pstate, JsonParseExpr *jsexpr)
|
transformJsonParseExpr(ParseState *pstate, JsonParseExpr *jsexpr)
|
||||||
{
|
{
|
||||||
JsonReturning *returning = transformJsonConstructorRet(pstate, jsexpr->output,
|
JsonReturning *returning = transformJsonConstructorRet(pstate, jsexpr->output,
|
||||||
"JSON()");
|
"JSON()");
|
||||||
Node *arg;
|
Node *arg;
|
||||||
|
|
||||||
if (jsexpr->unique_keys)
|
if (jsexpr->unique_keys)
|
||||||
@ -4544,8 +4543,8 @@ transformJsonParseExpr(ParseState *pstate, JsonParseExpr *jsexpr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return makeJsonConstructorExpr(pstate, JSCTOR_JSON_PARSE, list_make1(arg), NULL,
|
return makeJsonConstructorExpr(pstate, JSCTOR_JSON_PARSE, list_make1(arg), NULL,
|
||||||
returning, jsexpr->unique_keys, false,
|
returning, jsexpr->unique_keys, false,
|
||||||
jsexpr->location);
|
jsexpr->location);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4556,13 +4555,13 @@ transformJsonScalarExpr(ParseState *pstate, JsonScalarExpr *jsexpr)
|
|||||||
{
|
{
|
||||||
Node *arg = transformExprRecurse(pstate, (Node *) jsexpr->expr);
|
Node *arg = transformExprRecurse(pstate, (Node *) jsexpr->expr);
|
||||||
JsonReturning *returning = transformJsonConstructorRet(pstate, jsexpr->output,
|
JsonReturning *returning = transformJsonConstructorRet(pstate, jsexpr->output,
|
||||||
"JSON_SCALAR()");
|
"JSON_SCALAR()");
|
||||||
|
|
||||||
if (exprType(arg) == UNKNOWNOID)
|
if (exprType(arg) == UNKNOWNOID)
|
||||||
arg = coerce_to_specific_type(pstate, arg, TEXTOID, "JSON_SCALAR");
|
arg = coerce_to_specific_type(pstate, arg, TEXTOID, "JSON_SCALAR");
|
||||||
|
|
||||||
return makeJsonConstructorExpr(pstate, JSCTOR_JSON_SCALAR, list_make1(arg), NULL,
|
return makeJsonConstructorExpr(pstate, JSCTOR_JSON_SCALAR, list_make1(arg), NULL,
|
||||||
returning, false, false, jsexpr->location);
|
returning, false, false, jsexpr->location);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4586,5 +4585,5 @@ transformJsonSerializeExpr(ParseState *pstate, JsonSerializeExpr *expr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return makeJsonConstructorExpr(pstate, JSCTOR_JSON_SERIALIZE, list_make1(arg),
|
return makeJsonConstructorExpr(pstate, JSCTOR_JSON_SERIALIZE, list_make1(arg),
|
||||||
NULL, returning, false, false, expr->location);
|
NULL, returning, false, false, expr->location);
|
||||||
}
|
}
|
||||||
|
@ -33,31 +33,31 @@
|
|||||||
/* Context for JSON_TABLE transformation */
|
/* Context for JSON_TABLE transformation */
|
||||||
typedef struct JsonTableContext
|
typedef struct JsonTableContext
|
||||||
{
|
{
|
||||||
ParseState *pstate; /* parsing state */
|
ParseState *pstate; /* parsing state */
|
||||||
JsonTable *table; /* untransformed node */
|
JsonTable *table; /* untransformed node */
|
||||||
TableFunc *tablefunc; /* transformed node */
|
TableFunc *tablefunc; /* transformed node */
|
||||||
List *pathNames; /* list of all path and columns names */
|
List *pathNames; /* list of all path and columns names */
|
||||||
int pathNameId; /* path name id counter */
|
int pathNameId; /* path name id counter */
|
||||||
Oid contextItemTypid; /* type oid of context item (json/jsonb) */
|
Oid contextItemTypid; /* type oid of context item (json/jsonb) */
|
||||||
} JsonTableContext;
|
} JsonTableContext;
|
||||||
|
|
||||||
static JsonTableParent * transformJsonTableColumns(JsonTableContext *cxt,
|
static JsonTableParent *transformJsonTableColumns(JsonTableContext *cxt,
|
||||||
JsonTablePlan *plan,
|
JsonTablePlan *plan,
|
||||||
List *columns,
|
List *columns,
|
||||||
char *pathSpec,
|
char *pathSpec,
|
||||||
char **pathName,
|
char **pathName,
|
||||||
int location);
|
int location);
|
||||||
|
|
||||||
static Node *
|
static Node *
|
||||||
makeStringConst(char *str, int location)
|
makeStringConst(char *str, int location)
|
||||||
{
|
{
|
||||||
A_Const *n = makeNode(A_Const);
|
A_Const *n = makeNode(A_Const);
|
||||||
|
|
||||||
n->val.node.type = T_String;
|
n->val.node.type = T_String;
|
||||||
n->val.sval.sval = str;
|
n->val.sval.sval = str;
|
||||||
n->location = location;
|
n->location = location;
|
||||||
|
|
||||||
return (Node *)n;
|
return (Node *) n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -122,7 +122,7 @@ transformJsonTableColumn(JsonTableColumn *jtc, Node *contextItemExpr,
|
|||||||
static bool
|
static bool
|
||||||
isJsonTablePathNameDuplicate(JsonTableContext *cxt, const char *pathname)
|
isJsonTablePathNameDuplicate(JsonTableContext *cxt, const char *pathname)
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
foreach(lc, cxt->pathNames)
|
foreach(lc, cxt->pathNames)
|
||||||
{
|
{
|
||||||
@ -342,7 +342,7 @@ transformJsonTableChildPlan(JsonTableContext *cxt, JsonTablePlan *plan,
|
|||||||
foreach(lc, columns)
|
foreach(lc, columns)
|
||||||
{
|
{
|
||||||
JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc));
|
JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc));
|
||||||
Node *node;
|
Node *node;
|
||||||
|
|
||||||
if (jtc->coltype != JTC_NESTED)
|
if (jtc->coltype != JTC_NESTED)
|
||||||
continue;
|
continue;
|
||||||
@ -369,10 +369,10 @@ transformJsonTableChildPlan(JsonTableContext *cxt, JsonTablePlan *plan,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Node *node1 =
|
Node *node1 = transformJsonTableChildPlan(cxt, plan->plan1,
|
||||||
transformJsonTableChildPlan(cxt, plan->plan1, columns);
|
columns);
|
||||||
Node *node2 =
|
Node *node2 = transformJsonTableChildPlan(cxt, plan->plan2,
|
||||||
transformJsonTableChildPlan(cxt, plan->plan2, columns);
|
columns);
|
||||||
|
|
||||||
return makeJsonTableSiblingJoin(plan->join_type == JSTPJ_CROSS,
|
return makeJsonTableSiblingJoin(plan->join_type == JSTPJ_CROSS,
|
||||||
node1, node2);
|
node1, node2);
|
||||||
@ -396,7 +396,7 @@ transformJsonTableChildPlan(JsonTableContext *cxt, JsonTablePlan *plan,
|
|||||||
static bool
|
static bool
|
||||||
typeIsComposite(Oid typid)
|
typeIsComposite(Oid typid)
|
||||||
{
|
{
|
||||||
char typtype;
|
char typtype;
|
||||||
|
|
||||||
if (typid == JSONOID ||
|
if (typid == JSONOID ||
|
||||||
typid == JSONBOID ||
|
typid == JSONBOID ||
|
||||||
@ -406,7 +406,7 @@ typeIsComposite(Oid typid)
|
|||||||
|
|
||||||
typtype = get_typtype(typid);
|
typtype = get_typtype(typid);
|
||||||
|
|
||||||
if (typtype == TYPTYPE_COMPOSITE)
|
if (typtype == TYPTYPE_COMPOSITE)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (typtype == TYPTYPE_DOMAIN)
|
if (typtype == TYPTYPE_DOMAIN)
|
||||||
@ -424,7 +424,7 @@ appendJsonTableColumns(JsonTableContext *cxt, List *columns)
|
|||||||
JsonTable *jt = cxt->table;
|
JsonTable *jt = cxt->table;
|
||||||
TableFunc *tf = cxt->tablefunc;
|
TableFunc *tf = cxt->tablefunc;
|
||||||
bool errorOnError = jt->on_error &&
|
bool errorOnError = jt->on_error &&
|
||||||
jt->on_error->btype == JSON_BEHAVIOR_ERROR;
|
jt->on_error->btype == JSON_BEHAVIOR_ERROR;
|
||||||
|
|
||||||
foreach(col, columns)
|
foreach(col, columns)
|
||||||
{
|
{
|
||||||
@ -436,24 +436,23 @@ appendJsonTableColumns(JsonTableContext *cxt, List *columns)
|
|||||||
if (rawc->name)
|
if (rawc->name)
|
||||||
{
|
{
|
||||||
/* make sure column names are unique */
|
/* make sure column names are unique */
|
||||||
ListCell *colname;
|
ListCell *colname;
|
||||||
|
|
||||||
foreach(colname, tf->colnames)
|
foreach(colname, tf->colnames)
|
||||||
if (!strcmp((const char *) colname, rawc->name))
|
if (!strcmp((const char *) colname, rawc->name))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("column name \"%s\" is not unique",
|
errmsg("column name \"%s\" is not unique",
|
||||||
rawc->name),
|
rawc->name),
|
||||||
parser_errposition(pstate, rawc->location)));
|
parser_errposition(pstate, rawc->location)));
|
||||||
|
|
||||||
tf->colnames = lappend(tf->colnames,
|
tf->colnames = lappend(tf->colnames,
|
||||||
makeString(pstrdup(rawc->name)));
|
makeString(pstrdup(rawc->name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine the type and typmod for the new column. FOR
|
* Determine the type and typmod for the new column. FOR ORDINALITY
|
||||||
* ORDINALITY columns are INTEGER by standard; the others are
|
* columns are INTEGER by standard; the others are user-specified.
|
||||||
* user-specified.
|
|
||||||
*/
|
*/
|
||||||
switch (rawc->coltype)
|
switch (rawc->coltype)
|
||||||
{
|
{
|
||||||
@ -517,8 +516,8 @@ appendJsonTableColumns(JsonTableContext *cxt, List *columns)
|
|||||||
tf->coltypmods = lappend_int(tf->coltypmods, typmod);
|
tf->coltypmods = lappend_int(tf->coltypmods, typmod);
|
||||||
tf->colcollations = lappend_oid(tf->colcollations,
|
tf->colcollations = lappend_oid(tf->colcollations,
|
||||||
type_is_collatable(typid)
|
type_is_collatable(typid)
|
||||||
? DEFAULT_COLLATION_OID
|
? DEFAULT_COLLATION_OID
|
||||||
: InvalidOid);
|
: InvalidOid);
|
||||||
tf->colvalexprs = lappend(tf->colvalexprs, colexpr);
|
tf->colvalexprs = lappend(tf->colvalexprs, colexpr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -571,7 +570,7 @@ transformJsonTableColumns(JsonTableContext *cxt, JsonTablePlan *plan,
|
|||||||
errdetail("JSON_TABLE columns must contain "
|
errdetail("JSON_TABLE columns must contain "
|
||||||
"explicit AS pathname specification if "
|
"explicit AS pathname specification if "
|
||||||
"explicit PLAN clause is used"),
|
"explicit PLAN clause is used"),
|
||||||
parser_errposition(cxt->pstate, location)));
|
parser_errposition(cxt->pstate, location)));
|
||||||
|
|
||||||
*pathName = generateJsonTablePathName(cxt);
|
*pathName = generateJsonTablePathName(cxt);
|
||||||
}
|
}
|
||||||
@ -662,14 +661,15 @@ transformJsonTable(ParseState *pstate, JsonTable *jt)
|
|||||||
|
|
||||||
registerAllJsonTableColumns(&cxt, jt->columns);
|
registerAllJsonTableColumns(&cxt, jt->columns);
|
||||||
|
|
||||||
#if 0 /* XXX it' unclear from the standard whether root path name is mandatory or not */
|
#if 0 /* XXX it' unclear from the standard whether
|
||||||
|
* root path name is mandatory or not */
|
||||||
if (plan && plan->plan_type != JSTP_DEFAULT && !rootPathName)
|
if (plan && plan->plan_type != JSTP_DEFAULT && !rootPathName)
|
||||||
{
|
{
|
||||||
/* Assign root path name and create corresponding plan node */
|
/* Assign root path name and create corresponding plan node */
|
||||||
JsonTablePlan *rootNode = makeNode(JsonTablePlan);
|
JsonTablePlan *rootNode = makeNode(JsonTablePlan);
|
||||||
JsonTablePlan *rootPlan = (JsonTablePlan *)
|
JsonTablePlan *rootPlan = (JsonTablePlan *)
|
||||||
makeJsonTableJoinedPlan(JSTPJ_OUTER, (Node *) rootNode,
|
makeJsonTableJoinedPlan(JSTPJ_OUTER, (Node *) rootNode,
|
||||||
(Node *) plan, jt->location);
|
(Node *) plan, jt->location);
|
||||||
|
|
||||||
rootPathName = generateJsonTablePathName(&cxt);
|
rootPathName = generateJsonTablePathName(&cxt);
|
||||||
|
|
||||||
|
@ -382,55 +382,56 @@ make_const(ParseState *pstate, A_Const *aconst)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case T_Float:
|
case T_Float:
|
||||||
{
|
|
||||||
/* could be an oversize integer as well as a float ... */
|
|
||||||
|
|
||||||
int64 val64;
|
|
||||||
char *endptr;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
val64 = strtoi64(aconst->val.fval.fval, &endptr, 10);
|
|
||||||
if (errno == 0 && *endptr == '\0')
|
|
||||||
{
|
{
|
||||||
/*
|
/* could be an oversize integer as well as a float ... */
|
||||||
* It might actually fit in int32. Probably only INT_MIN can
|
|
||||||
* occur, but we'll code the test generally just to be sure.
|
|
||||||
*/
|
|
||||||
int32 val32 = (int32) val64;
|
|
||||||
|
|
||||||
if (val64 == (int64) val32)
|
int64 val64;
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
val64 = strtoi64(aconst->val.fval.fval, &endptr, 10);
|
||||||
|
if (errno == 0 && *endptr == '\0')
|
||||||
{
|
{
|
||||||
val = Int32GetDatum(val32);
|
/*
|
||||||
|
* It might actually fit in int32. Probably only INT_MIN
|
||||||
|
* can occur, but we'll code the test generally just to be
|
||||||
|
* sure.
|
||||||
|
*/
|
||||||
|
int32 val32 = (int32) val64;
|
||||||
|
|
||||||
typeid = INT4OID;
|
if (val64 == (int64) val32)
|
||||||
typelen = sizeof(int32);
|
{
|
||||||
typebyval = true;
|
val = Int32GetDatum(val32);
|
||||||
|
|
||||||
|
typeid = INT4OID;
|
||||||
|
typelen = sizeof(int32);
|
||||||
|
typebyval = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
val = Int64GetDatum(val64);
|
||||||
|
|
||||||
|
typeid = INT8OID;
|
||||||
|
typelen = sizeof(int64);
|
||||||
|
typebyval = FLOAT8PASSBYVAL; /* int8 and float8 alike */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
val = Int64GetDatum(val64);
|
/* arrange to report location if numeric_in() fails */
|
||||||
|
setup_parser_errposition_callback(&pcbstate, pstate, aconst->location);
|
||||||
|
val = DirectFunctionCall3(numeric_in,
|
||||||
|
CStringGetDatum(aconst->val.fval.fval),
|
||||||
|
ObjectIdGetDatum(InvalidOid),
|
||||||
|
Int32GetDatum(-1));
|
||||||
|
cancel_parser_errposition_callback(&pcbstate);
|
||||||
|
|
||||||
typeid = INT8OID;
|
typeid = NUMERICOID;
|
||||||
typelen = sizeof(int64);
|
typelen = -1; /* variable len */
|
||||||
typebyval = FLOAT8PASSBYVAL; /* int8 and float8 alike */
|
typebyval = false;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
/* arrange to report location if numeric_in() fails */
|
|
||||||
setup_parser_errposition_callback(&pcbstate, pstate, aconst->location);
|
|
||||||
val = DirectFunctionCall3(numeric_in,
|
|
||||||
CStringGetDatum(aconst->val.fval.fval),
|
|
||||||
ObjectIdGetDatum(InvalidOid),
|
|
||||||
Int32GetDatum(-1));
|
|
||||||
cancel_parser_errposition_callback(&pcbstate);
|
|
||||||
|
|
||||||
typeid = NUMERICOID;
|
|
||||||
typelen = -1; /* variable len */
|
|
||||||
typebyval = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case T_Boolean:
|
case T_Boolean:
|
||||||
val = BoolGetDatum(boolVal(&aconst->val));
|
val = BoolGetDatum(boolVal(&aconst->val));
|
||||||
|
@ -65,7 +65,7 @@ static bool query_contains_extern_params_walker(Node *node, void *context);
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
setup_parse_fixed_parameters(ParseState *pstate,
|
setup_parse_fixed_parameters(ParseState *pstate,
|
||||||
const Oid *paramTypes, int numParams)
|
const Oid *paramTypes, int numParams)
|
||||||
{
|
{
|
||||||
FixedParamState *parstate = palloc(sizeof(FixedParamState));
|
FixedParamState *parstate = palloc(sizeof(FixedParamState));
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ setup_parse_fixed_parameters(ParseState *pstate,
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
setup_parse_variable_parameters(ParseState *pstate,
|
setup_parse_variable_parameters(ParseState *pstate,
|
||||||
Oid **paramTypes, int *numParams)
|
Oid **paramTypes, int *numParams)
|
||||||
{
|
{
|
||||||
VarParamState *parstate = palloc(sizeof(VarParamState));
|
VarParamState *parstate = palloc(sizeof(VarParamState));
|
||||||
|
|
||||||
|
@ -1990,7 +1990,7 @@ addRangeTableEntryForTableFunc(ParseState *pstate,
|
|||||||
{
|
{
|
||||||
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
||||||
char *refname = alias ? alias->aliasname :
|
char *refname = alias ? alias->aliasname :
|
||||||
pstrdup(tf->functype == TFT_XMLTABLE ? "xmltable" : "json_table");
|
pstrdup(tf->functype == TFT_XMLTABLE ? "xmltable" : "json_table");
|
||||||
Alias *eref;
|
Alias *eref;
|
||||||
int numaliases;
|
int numaliases;
|
||||||
|
|
||||||
|
@ -91,8 +91,8 @@ RelationGetPartitionDesc(Relation rel, bool omit_detached)
|
|||||||
* cached descriptor too. We determine that based on the pg_inherits.xmin
|
* cached descriptor too. We determine that based on the pg_inherits.xmin
|
||||||
* that was saved alongside that descriptor: if the xmin that was not in
|
* that was saved alongside that descriptor: if the xmin that was not in
|
||||||
* progress for that active snapshot is also not in progress for the
|
* progress for that active snapshot is also not in progress for the
|
||||||
* current active snapshot, then we can use it. Otherwise build one
|
* current active snapshot, then we can use it. Otherwise build one from
|
||||||
* from scratch.
|
* scratch.
|
||||||
*/
|
*/
|
||||||
if (omit_detached &&
|
if (omit_detached &&
|
||||||
rel->rd_partdesc_nodetached &&
|
rel->rd_partdesc_nodetached &&
|
||||||
|
@ -984,7 +984,8 @@ rebuild_database_list(Oid newdb)
|
|||||||
hctl.keysize = sizeof(Oid);
|
hctl.keysize = sizeof(Oid);
|
||||||
hctl.entrysize = sizeof(avl_dbase);
|
hctl.entrysize = sizeof(avl_dbase);
|
||||||
hctl.hcxt = tmpcxt;
|
hctl.hcxt = tmpcxt;
|
||||||
dbhash = hash_create("autovacuum db hash", 20, &hctl, /* magic number here FIXME */
|
dbhash = hash_create("autovacuum db hash", 20, &hctl, /* magic number here
|
||||||
|
* FIXME */
|
||||||
HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
|
HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
|
||||||
|
|
||||||
/* start by inserting the new database */
|
/* start by inserting the new database */
|
||||||
@ -1683,12 +1684,12 @@ AutoVacWorkerMain(int argc, char *argv[])
|
|||||||
char dbname[NAMEDATALEN];
|
char dbname[NAMEDATALEN];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Report autovac startup to the cumulative stats system. We deliberately do
|
* Report autovac startup to the cumulative stats system. We
|
||||||
* this before InitPostgres, so that the last_autovac_time will get
|
* deliberately do this before InitPostgres, so that the
|
||||||
* updated even if the connection attempt fails. This is to prevent
|
* last_autovac_time will get updated even if the connection attempt
|
||||||
* autovac from getting "stuck" repeatedly selecting an unopenable
|
* fails. This is to prevent autovac from getting "stuck" repeatedly
|
||||||
* database, rather than making any progress on stuff it can connect
|
* selecting an unopenable database, rather than making any progress
|
||||||
* to.
|
* on stuff it can connect to.
|
||||||
*/
|
*/
|
||||||
pgstat_report_autovac(dbid);
|
pgstat_report_autovac(dbid);
|
||||||
|
|
||||||
|
@ -826,9 +826,9 @@ StartBackgroundWorker(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a per-backend PGPROC struct in shared memory, except in the
|
* Create a per-backend PGPROC struct in shared memory, except in the
|
||||||
* EXEC_BACKEND case where this was done in SubPostmasterMain. We must
|
* EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
|
||||||
* do this before we can use LWLocks (and in the EXEC_BACKEND case we
|
* this before we can use LWLocks (and in the EXEC_BACKEND case we already
|
||||||
* already had to do some stuff with LWLocks).
|
* had to do some stuff with LWLocks).
|
||||||
*/
|
*/
|
||||||
#ifndef EXEC_BACKEND
|
#ifndef EXEC_BACKEND
|
||||||
InitProcess();
|
InitProcess();
|
||||||
|
@ -81,15 +81,14 @@ typedef struct PgArchData
|
|||||||
int pgprocno; /* pgprocno of archiver process */
|
int pgprocno; /* pgprocno of archiver process */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Forces a directory scan in pgarch_readyXlog(). Protected by
|
* Forces a directory scan in pgarch_readyXlog(). Protected by arch_lck.
|
||||||
* arch_lck.
|
|
||||||
*/
|
*/
|
||||||
bool force_dir_scan;
|
bool force_dir_scan;
|
||||||
|
|
||||||
slock_t arch_lck;
|
slock_t arch_lck;
|
||||||
} PgArchData;
|
} PgArchData;
|
||||||
|
|
||||||
char *XLogArchiveLibrary = "";
|
char *XLogArchiveLibrary = "";
|
||||||
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
@ -143,7 +142,7 @@ static bool pgarch_readyXlog(char *xlog);
|
|||||||
static void pgarch_archiveDone(char *xlog);
|
static void pgarch_archiveDone(char *xlog);
|
||||||
static void pgarch_die(int code, Datum arg);
|
static void pgarch_die(int code, Datum arg);
|
||||||
static void HandlePgArchInterrupts(void);
|
static void HandlePgArchInterrupts(void);
|
||||||
static int ready_file_comparator(Datum a, Datum b, void *arg);
|
static int ready_file_comparator(Datum a, Datum b, void *arg);
|
||||||
static void LoadArchiveLibrary(void);
|
static void LoadArchiveLibrary(void);
|
||||||
static void call_archive_module_shutdown_callback(int code, Datum arg);
|
static void call_archive_module_shutdown_callback(int code, Datum arg);
|
||||||
|
|
||||||
@ -579,13 +578,13 @@ pgarch_readyXlog(char *xlog)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If we still have stored file names from the previous directory scan,
|
* If we still have stored file names from the previous directory scan,
|
||||||
* try to return one of those. We check to make sure the status file
|
* try to return one of those. We check to make sure the status file is
|
||||||
* is still present, as the archive_command for a previous file may
|
* still present, as the archive_command for a previous file may have
|
||||||
* have already marked it done.
|
* already marked it done.
|
||||||
*/
|
*/
|
||||||
while (arch_files->arch_files_size > 0)
|
while (arch_files->arch_files_size > 0)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
char status_file[MAXPGPATH];
|
char status_file[MAXPGPATH];
|
||||||
char *arch_file;
|
char *arch_file;
|
||||||
|
|
||||||
@ -655,8 +654,8 @@ pgarch_readyXlog(char *xlog)
|
|||||||
CStringGetDatum(basename), NULL) > 0)
|
CStringGetDatum(basename), NULL) > 0)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Remove the lowest priority file and add the current one to
|
* Remove the lowest priority file and add the current one to the
|
||||||
* the heap.
|
* heap.
|
||||||
*/
|
*/
|
||||||
arch_file = DatumGetCString(binaryheap_remove_first(arch_files->arch_heap));
|
arch_file = DatumGetCString(binaryheap_remove_first(arch_files->arch_heap));
|
||||||
strcpy(arch_file, basename);
|
strcpy(arch_file, basename);
|
||||||
@ -677,8 +676,8 @@ pgarch_readyXlog(char *xlog)
|
|||||||
binaryheap_build(arch_files->arch_heap);
|
binaryheap_build(arch_files->arch_heap);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fill arch_files array with the files to archive in ascending order
|
* Fill arch_files array with the files to archive in ascending order of
|
||||||
* of priority.
|
* priority.
|
||||||
*/
|
*/
|
||||||
arch_files->arch_files_size = arch_files->arch_heap->bh_size;
|
arch_files->arch_files_size = arch_files->arch_heap->bh_size;
|
||||||
for (int i = 0; i < arch_files->arch_files_size; i++)
|
for (int i = 0; i < arch_files->arch_files_size; i++)
|
||||||
@ -702,10 +701,10 @@ pgarch_readyXlog(char *xlog)
|
|||||||
static int
|
static int
|
||||||
ready_file_comparator(Datum a, Datum b, void *arg)
|
ready_file_comparator(Datum a, Datum b, void *arg)
|
||||||
{
|
{
|
||||||
char *a_str = DatumGetCString(a);
|
char *a_str = DatumGetCString(a);
|
||||||
char *b_str = DatumGetCString(b);
|
char *b_str = DatumGetCString(b);
|
||||||
bool a_history = IsTLHistoryFileName(a_str);
|
bool a_history = IsTLHistoryFileName(a_str);
|
||||||
bool b_history = IsTLHistoryFileName(b_str);
|
bool b_history = IsTLHistoryFileName(b_str);
|
||||||
|
|
||||||
/* Timeline history files always have the highest priority. */
|
/* Timeline history files always have the highest priority. */
|
||||||
if (a_history != b_history)
|
if (a_history != b_history)
|
||||||
@ -793,8 +792,8 @@ HandlePgArchInterrupts(void)
|
|||||||
if (archiveLibChanged)
|
if (archiveLibChanged)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Call the currently loaded archive module's shutdown callback, if
|
* Call the currently loaded archive module's shutdown callback,
|
||||||
* one is defined.
|
* if one is defined.
|
||||||
*/
|
*/
|
||||||
call_archive_module_shutdown_callback(0, 0);
|
call_archive_module_shutdown_callback(0, 0);
|
||||||
|
|
||||||
@ -803,8 +802,8 @@ HandlePgArchInterrupts(void)
|
|||||||
* load the new one, but there is presently no mechanism for
|
* load the new one, but there is presently no mechanism for
|
||||||
* unloading a library (see the comment above
|
* unloading a library (see the comment above
|
||||||
* internal_load_library()). To deal with this, we simply restart
|
* internal_load_library()). To deal with this, we simply restart
|
||||||
* the archiver. The new archive module will be loaded when the new
|
* the archiver. The new archive module will be loaded when the
|
||||||
* archiver process starts up.
|
* new archiver process starts up.
|
||||||
*/
|
*/
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("restarting archiver process because value of "
|
(errmsg("restarting archiver process because value of "
|
||||||
@ -828,9 +827,8 @@ LoadArchiveLibrary(void)
|
|||||||
memset(&ArchiveContext, 0, sizeof(ArchiveModuleCallbacks));
|
memset(&ArchiveContext, 0, sizeof(ArchiveModuleCallbacks));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If shell archiving is enabled, use our special initialization
|
* If shell archiving is enabled, use our special initialization function.
|
||||||
* function. Otherwise, load the library and call its
|
* Otherwise, load the library and call its _PG_archive_module_init().
|
||||||
* _PG_archive_module_init().
|
|
||||||
*/
|
*/
|
||||||
if (XLogArchiveLibrary[0] == '\0')
|
if (XLogArchiveLibrary[0] == '\0')
|
||||||
archive_init = shell_archive_init;
|
archive_init = shell_archive_init;
|
||||||
|
@ -2859,8 +2859,8 @@ pmdie(SIGNAL_ARGS)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If we reached normal running, we go straight to waiting for
|
* If we reached normal running, we go straight to waiting for
|
||||||
* client backends to exit. If already in PM_STOP_BACKENDS or
|
* client backends to exit. If already in PM_STOP_BACKENDS or a
|
||||||
* a later state, do not change it.
|
* later state, do not change it.
|
||||||
*/
|
*/
|
||||||
if (pmState == PM_RUN || pmState == PM_HOT_STANDBY)
|
if (pmState == PM_RUN || pmState == PM_HOT_STANDBY)
|
||||||
connsAllowed = false;
|
connsAllowed = false;
|
||||||
|
@ -75,7 +75,7 @@ static volatile sig_atomic_t startup_progress_timer_expired = false;
|
|||||||
/*
|
/*
|
||||||
* Time between progress updates for long-running startup operations.
|
* Time between progress updates for long-running startup operations.
|
||||||
*/
|
*/
|
||||||
int log_startup_progress_interval = 10000; /* 10 sec */
|
int log_startup_progress_interval = 10000; /* 10 sec */
|
||||||
|
|
||||||
/* Signal handlers */
|
/* Signal handlers */
|
||||||
static void StartupProcTriggerHandler(SIGNAL_ARGS);
|
static void StartupProcTriggerHandler(SIGNAL_ARGS);
|
||||||
|
@ -297,9 +297,9 @@ HandleWalWriterInterrupts(void)
|
|||||||
/*
|
/*
|
||||||
* Force reporting remaining WAL statistics at process exit.
|
* Force reporting remaining WAL statistics at process exit.
|
||||||
*
|
*
|
||||||
* Since pgstat_report_wal is invoked with 'force' is false in main loop
|
* Since pgstat_report_wal is invoked with 'force' is false in main
|
||||||
* to avoid overloading the cumulative stats system, there may exist
|
* loop to avoid overloading the cumulative stats system, there may
|
||||||
* unreported stats counters for the WAL writer.
|
* exist unreported stats counters for the WAL writer.
|
||||||
*/
|
*/
|
||||||
pgstat_report_wal(true);
|
pgstat_report_wal(true);
|
||||||
|
|
||||||
|
@ -234,8 +234,8 @@ pg_set_regex_collation(Oid collation)
|
|||||||
if (!OidIsValid(collation))
|
if (!OidIsValid(collation))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* This typically means that the parser could not resolve a
|
* This typically means that the parser could not resolve a conflict
|
||||||
* conflict of implicit collations, so report it that way.
|
* of implicit collations, so report it that way.
|
||||||
*/
|
*/
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INDETERMINATE_COLLATION),
|
(errcode(ERRCODE_INDETERMINATE_COLLATION),
|
||||||
@ -253,9 +253,9 @@ pg_set_regex_collation(Oid collation)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* NB: pg_newlocale_from_collation will fail if not HAVE_LOCALE_T;
|
* NB: pg_newlocale_from_collation will fail if not HAVE_LOCALE_T; the
|
||||||
* the case of pg_regex_locale != 0 but not HAVE_LOCALE_T does not
|
* case of pg_regex_locale != 0 but not HAVE_LOCALE_T does not have to
|
||||||
* have to be considered below.
|
* be considered below.
|
||||||
*/
|
*/
|
||||||
pg_regex_locale = pg_newlocale_from_collation(collation);
|
pg_regex_locale = pg_newlocale_from_collation(collation);
|
||||||
|
|
||||||
|
@ -312,7 +312,7 @@ AddWALInfoToBackupManifest(backup_manifest_info *manifest, XLogRecPtr startptr,
|
|||||||
* Finalize the backup manifest, and send it to the client.
|
* Finalize the backup manifest, and send it to the client.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
SendBackupManifest(backup_manifest_info *manifest, bbsink * sink)
|
SendBackupManifest(backup_manifest_info *manifest, bbsink *sink)
|
||||||
{
|
{
|
||||||
uint8 checksumbuf[PG_SHA256_DIGEST_LENGTH];
|
uint8 checksumbuf[PG_SHA256_DIGEST_LENGTH];
|
||||||
char checksumstringbuf[PG_SHA256_DIGEST_STRING_LENGTH];
|
char checksumstringbuf[PG_SHA256_DIGEST_STRING_LENGTH];
|
||||||
|
@ -124,18 +124,18 @@ bbsink_copystream_begin_backup(bbsink *sink)
|
|||||||
{
|
{
|
||||||
bbsink_copystream *mysink = (bbsink_copystream *) sink;
|
bbsink_copystream *mysink = (bbsink_copystream *) sink;
|
||||||
bbsink_state *state = sink->bbs_state;
|
bbsink_state *state = sink->bbs_state;
|
||||||
char *buf;
|
char *buf;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize buffer. We ultimately want to send the archive and manifest
|
* Initialize buffer. We ultimately want to send the archive and manifest
|
||||||
* data by means of CopyData messages where the payload portion of each
|
* data by means of CopyData messages where the payload portion of each
|
||||||
* message begins with a type byte. However, basebackup.c expects the
|
* message begins with a type byte. However, basebackup.c expects the
|
||||||
* buffer to be aligned, so we can't just allocate one extra byte for the
|
* buffer to be aligned, so we can't just allocate one extra byte for the
|
||||||
* type byte. Instead, allocate enough extra bytes that the portion of
|
* type byte. Instead, allocate enough extra bytes that the portion of the
|
||||||
* the buffer we reveal to our callers can be aligned, while leaving room
|
* buffer we reveal to our callers can be aligned, while leaving room to
|
||||||
* to slip the type byte in just beforehand. That will allow us to ship
|
* slip the type byte in just beforehand. That will allow us to ship the
|
||||||
* the data with a single call to pq_putmessage and without needing any
|
* data with a single call to pq_putmessage and without needing any extra
|
||||||
* extra copying.
|
* copying.
|
||||||
*/
|
*/
|
||||||
buf = palloc(mysink->base.bbs_buffer_length + MAXIMUM_ALIGNOF);
|
buf = palloc(mysink->base.bbs_buffer_length + MAXIMUM_ALIGNOF);
|
||||||
mysink->msgbuffer = buf + (MAXIMUM_ALIGNOF - 1);
|
mysink->msgbuffer = buf + (MAXIMUM_ALIGNOF - 1);
|
||||||
|
@ -68,7 +68,7 @@ bbsink_gzip_new(bbsink *next, pg_compress_specification *compress)
|
|||||||
return NULL; /* keep compiler quiet */
|
return NULL; /* keep compiler quiet */
|
||||||
#else
|
#else
|
||||||
bbsink_gzip *sink;
|
bbsink_gzip *sink;
|
||||||
int compresslevel;
|
int compresslevel;
|
||||||
|
|
||||||
Assert(next != NULL);
|
Assert(next != NULL);
|
||||||
|
|
||||||
@ -118,8 +118,8 @@ static void
|
|||||||
bbsink_gzip_begin_archive(bbsink *sink, const char *archive_name)
|
bbsink_gzip_begin_archive(bbsink *sink, const char *archive_name)
|
||||||
{
|
{
|
||||||
bbsink_gzip *mysink = (bbsink_gzip *) sink;
|
bbsink_gzip *mysink = (bbsink_gzip *) sink;
|
||||||
char *gz_archive_name;
|
char *gz_archive_name;
|
||||||
z_stream *zs = &mysink->zstream;
|
z_stream *zs = &mysink->zstream;
|
||||||
|
|
||||||
/* Initialize compressor object. */
|
/* Initialize compressor object. */
|
||||||
memset(zs, 0, sizeof(z_stream));
|
memset(zs, 0, sizeof(z_stream));
|
||||||
@ -129,10 +129,10 @@ bbsink_gzip_begin_archive(bbsink *sink, const char *archive_name)
|
|||||||
zs->avail_out = sink->bbs_next->bbs_buffer_length;
|
zs->avail_out = sink->bbs_next->bbs_buffer_length;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need to use deflateInit2() rather than deflateInit() here so that
|
* We need to use deflateInit2() rather than deflateInit() here so that we
|
||||||
* we can request a gzip header rather than a zlib header. Otherwise, we
|
* can request a gzip header rather than a zlib header. Otherwise, we want
|
||||||
* want to supply the same values that would have been used by default
|
* to supply the same values that would have been used by default if we
|
||||||
* if we had just called deflateInit().
|
* had just called deflateInit().
|
||||||
*
|
*
|
||||||
* Per the documentation for deflateInit2, the third argument must be
|
* Per the documentation for deflateInit2, the third argument must be
|
||||||
* Z_DEFLATED; the fourth argument is the number of "window bits", by
|
* Z_DEFLATED; the fourth argument is the number of "window bits", by
|
||||||
@ -147,9 +147,8 @@ bbsink_gzip_begin_archive(bbsink *sink, const char *archive_name)
|
|||||||
errmsg("could not initialize compression library"));
|
errmsg("could not initialize compression library"));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add ".gz" to the archive name. Note that the pg_basebackup -z
|
* Add ".gz" to the archive name. Note that the pg_basebackup -z produces
|
||||||
* produces archives named ".tar.gz" rather than ".tgz", so we match
|
* archives named ".tar.gz" rather than ".tgz", so we match that here.
|
||||||
* that here.
|
|
||||||
*/
|
*/
|
||||||
gz_archive_name = psprintf("%s.gz", archive_name);
|
gz_archive_name = psprintf("%s.gz", archive_name);
|
||||||
Assert(sink->bbs_next != NULL);
|
Assert(sink->bbs_next != NULL);
|
||||||
@ -172,7 +171,7 @@ static void
|
|||||||
bbsink_gzip_archive_contents(bbsink *sink, size_t len)
|
bbsink_gzip_archive_contents(bbsink *sink, size_t len)
|
||||||
{
|
{
|
||||||
bbsink_gzip *mysink = (bbsink_gzip *) sink;
|
bbsink_gzip *mysink = (bbsink_gzip *) sink;
|
||||||
z_stream *zs = &mysink->zstream;
|
z_stream *zs = &mysink->zstream;
|
||||||
|
|
||||||
/* Compress data from input buffer. */
|
/* Compress data from input buffer. */
|
||||||
zs->next_in = (uint8 *) mysink->base.bbs_buffer;
|
zs->next_in = (uint8 *) mysink->base.bbs_buffer;
|
||||||
@ -180,7 +179,7 @@ bbsink_gzip_archive_contents(bbsink *sink, size_t len)
|
|||||||
|
|
||||||
while (zs->avail_in > 0)
|
while (zs->avail_in > 0)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
/* Write output data into unused portion of output buffer. */
|
/* Write output data into unused portion of output buffer. */
|
||||||
Assert(mysink->bytes_written < mysink->base.bbs_next->bbs_buffer_length);
|
Assert(mysink->bytes_written < mysink->base.bbs_next->bbs_buffer_length);
|
||||||
@ -230,7 +229,7 @@ static void
|
|||||||
bbsink_gzip_end_archive(bbsink *sink)
|
bbsink_gzip_end_archive(bbsink *sink)
|
||||||
{
|
{
|
||||||
bbsink_gzip *mysink = (bbsink_gzip *) sink;
|
bbsink_gzip *mysink = (bbsink_gzip *) sink;
|
||||||
z_stream *zs = &mysink->zstream;
|
z_stream *zs = &mysink->zstream;
|
||||||
|
|
||||||
/* There is no more data available. */
|
/* There is no more data available. */
|
||||||
zs->next_in = (uint8 *) mysink->base.bbs_buffer;
|
zs->next_in = (uint8 *) mysink->base.bbs_buffer;
|
||||||
@ -238,7 +237,7 @@ bbsink_gzip_end_archive(bbsink *sink)
|
|||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
/* Write output data into unused portion of output buffer. */
|
/* Write output data into unused portion of output buffer. */
|
||||||
Assert(mysink->bytes_written < mysink->base.bbs_next->bbs_buffer_length);
|
Assert(mysink->bytes_written < mysink->base.bbs_next->bbs_buffer_length);
|
||||||
@ -248,8 +247,8 @@ bbsink_gzip_end_archive(bbsink *sink)
|
|||||||
mysink->base.bbs_next->bbs_buffer_length - mysink->bytes_written;
|
mysink->base.bbs_next->bbs_buffer_length - mysink->bytes_written;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* As bbsink_gzip_archive_contents, but pass Z_FINISH since there
|
* As bbsink_gzip_archive_contents, but pass Z_FINISH since there is
|
||||||
* is no more input.
|
* no more input.
|
||||||
*/
|
*/
|
||||||
res = deflate(zs, Z_FINISH);
|
res = deflate(zs, Z_FINISH);
|
||||||
if (res == Z_STREAM_ERROR)
|
if (res == Z_STREAM_ERROR)
|
||||||
@ -260,8 +259,8 @@ bbsink_gzip_end_archive(bbsink *sink)
|
|||||||
mysink->base.bbs_next->bbs_buffer_length - zs->avail_out;
|
mysink->base.bbs_next->bbs_buffer_length - zs->avail_out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Apparently we had no data in the output buffer and deflate()
|
* Apparently we had no data in the output buffer and deflate() was
|
||||||
* was not able to add any. We must be done.
|
* not able to add any. We must be done.
|
||||||
*/
|
*/
|
||||||
if (mysink->bytes_written == 0)
|
if (mysink->bytes_written == 0)
|
||||||
break;
|
break;
|
||||||
|
@ -68,7 +68,7 @@ bbsink_lz4_new(bbsink *next, pg_compress_specification *compress)
|
|||||||
return NULL; /* keep compiler quiet */
|
return NULL; /* keep compiler quiet */
|
||||||
#else
|
#else
|
||||||
bbsink_lz4 *sink;
|
bbsink_lz4 *sink;
|
||||||
int compresslevel;
|
int compresslevel;
|
||||||
|
|
||||||
Assert(next != NULL);
|
Assert(next != NULL);
|
||||||
|
|
||||||
|
@ -77,10 +77,11 @@ bbsink_server_new(bbsink *next, char *pathname)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* It's not a good idea to store your backups in the same directory that
|
* It's not a good idea to store your backups in the same directory that
|
||||||
* you're backing up. If we allowed a relative path here, that could easily
|
* you're backing up. If we allowed a relative path here, that could
|
||||||
* happen accidentally, so we don't. The user could still accomplish the
|
* easily happen accidentally, so we don't. The user could still
|
||||||
* same thing by including the absolute path to $PGDATA in the pathname,
|
* accomplish the same thing by including the absolute path to $PGDATA in
|
||||||
* but that's likely an intentional bad decision rather than an accident.
|
* the pathname, but that's likely an intentional bad decision rather than
|
||||||
|
* an accident.
|
||||||
*/
|
*/
|
||||||
if (!is_absolute_path(pathname))
|
if (!is_absolute_path(pathname))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -90,14 +91,15 @@ bbsink_server_new(bbsink *next, char *pathname)
|
|||||||
switch (pg_check_dir(pathname))
|
switch (pg_check_dir(pathname))
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Does not exist, so create it using the same permissions we'd use
|
* Does not exist, so create it using the same permissions we'd
|
||||||
* for a new subdirectory of the data directory itself.
|
* use for a new subdirectory of the data directory itself.
|
||||||
*/
|
*/
|
||||||
if (MakePGDirectory(pathname) < 0)
|
if (MakePGDirectory(pathname) < 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode_for_file_access(),
|
(errcode_for_file_access(),
|
||||||
errmsg("could not create directory \"%s\": %m", pathname)));
|
errmsg("could not create directory \"%s\": %m", pathname)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -80,9 +80,9 @@ BaseBackupAddTarget(char *name,
|
|||||||
/*
|
/*
|
||||||
* We found one, so update it.
|
* We found one, so update it.
|
||||||
*
|
*
|
||||||
* It is probably not a great idea to call BaseBackupAddTarget
|
* It is probably not a great idea to call BaseBackupAddTarget for
|
||||||
* for the same name multiple times, but if it happens, this
|
* the same name multiple times, but if it happens, this seems
|
||||||
* seems like the sanest behavior.
|
* like the sanest behavior.
|
||||||
*/
|
*/
|
||||||
ttype->check_detail = check_detail;
|
ttype->check_detail = check_detail;
|
||||||
ttype->get_sink = get_sink;
|
ttype->get_sink = get_sink;
|
||||||
@ -91,9 +91,9 @@ BaseBackupAddTarget(char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We use TopMemoryContext for allocations here to make sure that the
|
* We use TopMemoryContext for allocations here to make sure that the data
|
||||||
* data we need doesn't vanish under us; that's also why we copy the
|
* we need doesn't vanish under us; that's also why we copy the target
|
||||||
* target name into a newly-allocated chunk of memory.
|
* name into a newly-allocated chunk of memory.
|
||||||
*/
|
*/
|
||||||
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
|
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
|
||||||
ttype = palloc(sizeof(BaseBackupTargetType));
|
ttype = palloc(sizeof(BaseBackupTargetType));
|
||||||
|
@ -108,9 +108,9 @@ bbsink_zstd_begin_backup(bbsink *sink)
|
|||||||
if ((compress->options & PG_COMPRESSION_OPTION_WORKERS) != 0)
|
if ((compress->options & PG_COMPRESSION_OPTION_WORKERS) != 0)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* On older versions of libzstd, this option does not exist, and trying
|
* On older versions of libzstd, this option does not exist, and
|
||||||
* to set it will fail. Similarly for newer versions if they are
|
* trying to set it will fail. Similarly for newer versions if they
|
||||||
* compiled without threading support.
|
* are compiled without threading support.
|
||||||
*/
|
*/
|
||||||
ret = ZSTD_CCtx_setParameter(mysink->cctx, ZSTD_c_nbWorkers,
|
ret = ZSTD_CCtx_setParameter(mysink->cctx, ZSTD_c_nbWorkers,
|
||||||
compress->workers);
|
compress->workers);
|
||||||
|
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