forked from huawei/openGauss-server
Compare commits
73 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
72f4d68de6 | |
|
|
62e3bed09b | |
|
|
7d4d95a5ad | |
|
|
240f61c595 | |
|
|
a0d1a547f8 | |
|
|
df804a8a91 | |
|
|
91917dcfaf | |
|
|
e2163187ca | |
|
|
1d7eadd778 | |
|
|
b335ef9af7 | |
|
|
572d99d03c | |
|
|
0b0959fc69 | |
|
|
238ee19a59 | |
|
|
d199d90a71 | |
|
|
2a77a3cebc | |
|
|
214d01ddb6 | |
|
|
4f024161f0 | |
|
|
82d5ac65b0 | |
|
|
7edda5e2a4 | |
|
|
e10c482649 | |
|
|
0a29277070 | |
|
|
96dad83a1d | |
|
|
d433ee097a | |
|
|
a590e0b47f | |
|
|
7f8b900e69 | |
|
|
f14255f040 | |
|
|
5af581f12a | |
|
|
26793e3bf2 | |
|
|
dbcf479205 | |
|
|
ae11cad2c3 | |
|
|
57bc5c4a37 | |
|
|
fb449157d0 | |
|
|
6c5898646e | |
|
|
c1335bebc9 | |
|
|
b2c6e04eab | |
|
|
16d32d3978 | |
|
|
3e14e85fdf | |
|
|
438bba32ff | |
|
|
d82eba4790 | |
|
|
4b7080c8ef | |
|
|
5e1d9b79f6 | |
|
|
51c355c7b6 | |
|
|
4c0a495a18 | |
|
|
cbb8fd609b | |
|
|
475170ca0c | |
|
|
7f9a1f5428 | |
|
|
c04e377cdc | |
|
|
6380ca0f4d | |
|
|
4898f04a3c | |
|
|
11a1a5c9ad | |
|
|
a30ae9d5ad | |
|
|
0b70c7ab70 | |
|
|
f719422916 | |
|
|
88dcc7f99e | |
|
|
dbd9958443 | |
|
|
38b5a0ac95 | |
|
|
a548f5c3c6 | |
|
|
eb4e54d4eb | |
|
|
3f7a909887 | |
|
|
d8b89ceea5 | |
|
|
2250adfd4b | |
|
|
fd34b5da2a | |
|
|
7f97d633f1 | |
|
|
6ba4c95f5f | |
|
|
5755feee5d | |
|
|
cd5d44d66c | |
|
|
2d657cddbe | |
|
|
ccd674c159 | |
|
|
8292238381 | |
|
|
5054ddd002 | |
|
|
48a5033c18 | |
|
|
811a9afcd9 | |
|
|
bb0aa02eb0 |
|
|
@ -72,7 +72,7 @@ select_package_command
|
||||||
export PLAT_FORM_STR=$(sh "${ROOT_DIR}/src/get_PlatForm_str.sh")
|
export PLAT_FORM_STR=$(sh "${ROOT_DIR}/src/get_PlatForm_str.sh")
|
||||||
if [ "${PLAT_FORM_STR}"x == "Failed"x -o "${PLAT_FORM_STR}"x == ""x ]
|
if [ "${PLAT_FORM_STR}"x == "Failed"x -o "${PLAT_FORM_STR}"x == ""x ]
|
||||||
then
|
then
|
||||||
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64) platform."
|
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64), Asianux platform."
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -96,16 +96,21 @@ elif [[ "$PLAT_FORM_STR" =~ "kylin" ]]; then
|
||||||
if [ "$PLATFORM_ARCH"X == "aarch64"X ];then
|
if [ "$PLATFORM_ARCH"X == "aarch64"X ];then
|
||||||
GAUSSDB_EXTRA_FLAGS=" -D__USE_NUMA"
|
GAUSSDB_EXTRA_FLAGS=" -D__USE_NUMA"
|
||||||
fi
|
fi
|
||||||
|
elif [[ "$PLAT_FORM_STR" =~ "asianux" ]]; then
|
||||||
|
dist_version="Asianux"
|
||||||
|
if [ "$PLATFORM_ARCH"X == "aarch64"X ];then
|
||||||
|
GAUSSDB_EXTRA_FLAGS=" -D__USE_NUMA"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64) platform."
|
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64), Asianux platform."
|
||||||
echo "Kernel is $kernel"
|
echo "Kernel is $kernel"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
##add platform architecture information
|
##add platform architecture information
|
||||||
if [ "$PLATFORM_ARCH"X == "aarch64"X ] ; then
|
if [ "$PLATFORM_ARCH"X == "aarch64"X ] ; then
|
||||||
if [ "$dist_version" != "openEuler" ] && [ "$dist_version" != "EulerOS" ] && [ "$dist_version" != "Kylin" ] ; then
|
if [ "$dist_version" != "openEuler" ] && [ "$dist_version" != "EulerOS" ] && [ "$dist_version" != "Kylin" ] && [ "$dist_version" != "Asianux" ]; then
|
||||||
echo "We only support NUMA on openEuler(aarch64), EulerOS(aarch64), Kylin(aarch64) platform."
|
echo "We only support NUMA on openEuler(aarch64), EulerOS(aarch64), Kylin(aarch64), Asianux platform."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ Complete list of usable sgml source files in this directory.
|
||||||
<!ENTITY alterOperator SYSTEM "alter_operator.sgml">
|
<!ENTITY alterOperator SYSTEM "alter_operator.sgml">
|
||||||
<!ENTITY alterOperatorClass SYSTEM "alter_opclass.sgml">
|
<!ENTITY alterOperatorClass SYSTEM "alter_opclass.sgml">
|
||||||
<!ENTITY alterOperatorFamily SYSTEM "alter_opfamily.sgml">
|
<!ENTITY alterOperatorFamily SYSTEM "alter_opfamily.sgml">
|
||||||
|
<!ENTITY alterProcedure SYSTEM "alter_procedure.sgml">
|
||||||
<!ENTITY alterRole SYSTEM "alter_role.sgml">
|
<!ENTITY alterRole SYSTEM "alter_role.sgml">
|
||||||
<!ENTITY alterSchema SYSTEM "alter_schema.sgml">
|
<!ENTITY alterSchema SYSTEM "alter_schema.sgml">
|
||||||
<!ENTITY alterServer SYSTEM "alter_server.sgml">
|
<!ENTITY alterServer SYSTEM "alter_server.sgml">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<refentry id="sql-alterprocedure">
|
||||||
|
<indexterm zone="sql-alterprocedure">
|
||||||
|
<primary>ALTER PROCEDURE</primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
|
<refmeta>
|
||||||
|
<refentrytitle>ALTER PROCEDURE</refentrytitle>
|
||||||
|
<manvolnum>7</manvolnum>
|
||||||
|
<refmiscinfo>SQL - Language Statements</refmiscinfo>
|
||||||
|
</refmeta>
|
||||||
|
|
||||||
|
<refnamediv>
|
||||||
|
<refname>ALTER PROCEDURE</refname>
|
||||||
|
<refpurpose>change the definition of a procedure</refpurpose>
|
||||||
|
</refnamediv>
|
||||||
|
|
||||||
|
<refsynopsisdiv>
|
||||||
|
<synopsis>
|
||||||
|
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
|
||||||
|
<replaceable class="parameter">action</replaceable> [ ... ] [ RESTRICT ]
|
||||||
|
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
|
||||||
|
RENAME TO <replaceable>new_name</replaceable>
|
||||||
|
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
|
||||||
|
OWNER TO { <replaceable>new_owner</replaceable> | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
|
||||||
|
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
|
||||||
|
SET SCHEMA <replaceable>new_schema</replaceable>
|
||||||
|
|
||||||
|
<phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
|
||||||
|
|
||||||
|
[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
||||||
|
SET <replaceable class="parameter">configuration_parameter</replaceable> { TO | = } { <replaceable class="parameter">value</replaceable> | DEFAULT }
|
||||||
|
SET <replaceable class="parameter">configuration_parameter</replaceable> FROM CURRENT
|
||||||
|
RESET <replaceable class="parameter">configuration_parameter</replaceable>
|
||||||
|
RESET ALL
|
||||||
|
</synopsis>
|
||||||
|
</refsynopsisdiv>
|
||||||
|
</refentry>
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<refsynopsisdiv>
|
<refsynopsisdiv>
|
||||||
<synopsis>
|
<synopsis>
|
||||||
DROP TABLE [ IF EXISTS ]
|
DROP TABLE [ IF EXISTS ]
|
||||||
{[schema.]table_name} [, ...] [ CASCADE | RESTRICT ] [ PURGE ]};
|
{[schema.]table_name} [, ...] [ CASCADE | RESTRICT ] [ PURGE ];
|
||||||
</synopsis>
|
</synopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
</refentry>
|
</refentry>
|
||||||
|
|
@ -5923,7 +5923,7 @@ int main(int argc, char** argv)
|
||||||
&option_index)) != -1)
|
&option_index)) != -1)
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
while ((c = getopt_long(argc, argv, "b:cD:e:fi:G:l:m:M:N:o:O:p:P:r:R:v:x:sS:t:u:U:wWZ:dqL:T:Q:", long_options,
|
while ((c = getopt_long(argc, argv, "b:cD:e:fi:G:l:m:M:N:o:O:p:P:r:R:v:x:sS:t:u:U:wWZ:C:dqL:T:Q:", long_options,
|
||||||
&option_index)) != -1)
|
&option_index)) != -1)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,7 @@ char* all_data_nodename_list = NULL;
|
||||||
const uint32 USTORE_UPGRADE_VERSION = 92368;
|
const uint32 USTORE_UPGRADE_VERSION = 92368;
|
||||||
const uint32 PACKAGE_ENHANCEMENT = 92444;
|
const uint32 PACKAGE_ENHANCEMENT = 92444;
|
||||||
const uint32 SUBSCRIPTION_VERSION = 92580;
|
const uint32 SUBSCRIPTION_VERSION = 92580;
|
||||||
|
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
|
||||||
|
|
||||||
#ifdef DUMPSYSLOG
|
#ifdef DUMPSYSLOG
|
||||||
char* syslogpath = NULL;
|
char* syslogpath = NULL;
|
||||||
|
|
@ -4444,23 +4445,16 @@ void getSubscriptions(Archive *fout)
|
||||||
int i_subslotname;
|
int i_subslotname;
|
||||||
int i_subsynccommit;
|
int i_subsynccommit;
|
||||||
int i_subpublications;
|
int i_subpublications;
|
||||||
int i, ntups;
|
int i_subbinary;
|
||||||
|
int i;
|
||||||
|
int ntups;
|
||||||
|
|
||||||
if (no_subscriptions || GetVersionNum(fout) < SUBSCRIPTION_VERSION) {
|
if (no_subscriptions || GetVersionNum(fout) < SUBSCRIPTION_VERSION) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isExecUserSuperRole(fout)) {
|
if (!isExecUserSuperRole(fout)) {
|
||||||
res = ExecuteSqlQuery(fout,
|
|
||||||
"SELECT count(*) FROM pg_subscription "
|
|
||||||
"WHERE subdbid = (SELECT oid FROM pg_catalog.pg_database"
|
|
||||||
" WHERE datname = current_database())",
|
|
||||||
PGRES_TUPLES_OK);
|
|
||||||
uint64 n = (res != NULL) ? strtoul(PQgetvalue(res, 0, 0), NULL, 10) : 0;
|
|
||||||
if (n > 0) {
|
|
||||||
write_msg(NULL, "WARNING: subscriptions not dumped because current user is not a superuser\n");
|
write_msg(NULL, "WARNING: subscriptions not dumped because current user is not a superuser\n");
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4469,14 +4463,20 @@ void getSubscriptions(Archive *fout)
|
||||||
resetPQExpBuffer(query);
|
resetPQExpBuffer(query);
|
||||||
|
|
||||||
/* Get the subscriptions in current database. */
|
/* Get the subscriptions in current database. */
|
||||||
appendPQExpBuffer(query,
|
appendPQExpBuffer(query, "SELECT s.tableoid, s.oid, s.subname,"
|
||||||
"SELECT s.tableoid, s.oid, s.subname,"
|
"(%s s.subowner) AS rolname, s.subconninfo, s.subslotname, "
|
||||||
"(%s s.subowner) AS rolname, "
|
"s.subsynccommit, s.subpublications, \n", username_subquery);
|
||||||
" s.subconninfo, s.subslotname, s.subsynccommit, s.subpublications "
|
|
||||||
"FROM pg_catalog.pg_subscription s "
|
if (GetVersionNum(fout) >= SUBSCRIPTION_BINARY_VERSION_NUM) {
|
||||||
|
appendPQExpBuffer(query, " s.subbinary\n");
|
||||||
|
} else {
|
||||||
|
appendPQExpBuffer(query, " false AS subbinary\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
appendPQExpBuffer(query, "FROM pg_catalog.pg_subscription s "
|
||||||
"WHERE s.subdbid = (SELECT oid FROM pg_catalog.pg_database"
|
"WHERE s.subdbid = (SELECT oid FROM pg_catalog.pg_database"
|
||||||
" WHERE datname = current_database())",
|
" WHERE datname = current_database())");
|
||||||
username_subquery);
|
|
||||||
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
|
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
|
||||||
|
|
||||||
ntups = PQntuples(res);
|
ntups = PQntuples(res);
|
||||||
|
|
@ -4494,6 +4494,7 @@ void getSubscriptions(Archive *fout)
|
||||||
i_subslotname = PQfnumber(res, "subslotname");
|
i_subslotname = PQfnumber(res, "subslotname");
|
||||||
i_subsynccommit = PQfnumber(res, "subsynccommit");
|
i_subsynccommit = PQfnumber(res, "subsynccommit");
|
||||||
i_subpublications = PQfnumber(res, "subpublications");
|
i_subpublications = PQfnumber(res, "subpublications");
|
||||||
|
i_subbinary = PQfnumber(res, "subbinary");
|
||||||
|
|
||||||
subinfo = (SubscriptionInfo *)pg_malloc(ntups * sizeof(SubscriptionInfo));
|
subinfo = (SubscriptionInfo *)pg_malloc(ntups * sizeof(SubscriptionInfo));
|
||||||
|
|
||||||
|
|
@ -4512,6 +4513,7 @@ void getSubscriptions(Archive *fout)
|
||||||
}
|
}
|
||||||
subinfo[i].subsynccommit = gs_strdup(PQgetvalue(res, i, i_subsynccommit));
|
subinfo[i].subsynccommit = gs_strdup(PQgetvalue(res, i, i_subsynccommit));
|
||||||
subinfo[i].subpublications = gs_strdup(PQgetvalue(res, i, i_subpublications));
|
subinfo[i].subpublications = gs_strdup(PQgetvalue(res, i, i_subpublications));
|
||||||
|
subinfo[i].subbinary = gs_strdup(PQgetvalue(res, i, i_subbinary));
|
||||||
|
|
||||||
if (strlen(subinfo[i].rolname) == 0) {
|
if (strlen(subinfo[i].rolname) == 0) {
|
||||||
write_msg(NULL, "WARNING: owner of subscription \"%s\" appears to be invalid\n", subinfo[i].dobj.name);
|
write_msg(NULL, "WARNING: owner of subscription \"%s\" appears to be invalid\n", subinfo[i].dobj.name);
|
||||||
|
|
@ -4578,6 +4580,10 @@ static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
|
||||||
appendPQExpBufferStr(query, "NONE");
|
appendPQExpBufferStr(query, "NONE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcmp(subinfo->subbinary, "t") == 0) {
|
||||||
|
appendPQExpBuffer(query, ", binary = true");
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(subinfo->subsynccommit, "off") != 0) {
|
if (strcmp(subinfo->subsynccommit, "off") != 0) {
|
||||||
appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
|
appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
|
||||||
}
|
}
|
||||||
|
|
@ -10780,6 +10786,11 @@ static void dumpDirectory(Archive* fout)
|
||||||
char* dirpath = NULL;
|
char* dirpath = NULL;
|
||||||
char* diracl = NULL;
|
char* diracl = NULL;
|
||||||
|
|
||||||
|
if (!isExecUserSuperRole(fout)) {
|
||||||
|
write_msg(NULL, "WARNING: directory not dumped because current user is not a superuser\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Make sure we are in proper schema */
|
/* Make sure we are in proper schema */
|
||||||
selectSourceSchema(fout, "pg_catalog");
|
selectSourceSchema(fout, "pg_catalog");
|
||||||
|
|
||||||
|
|
@ -21389,6 +21400,11 @@ static void dumpSynonym(Archive* fout)
|
||||||
PQExpBuffer q;
|
PQExpBuffer q;
|
||||||
PQExpBuffer delq;
|
PQExpBuffer delq;
|
||||||
|
|
||||||
|
if (!isExecUserSuperRole(fout)) {
|
||||||
|
write_msg(NULL, "WARNING: synonym not dumped because current user is not a superuser\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
selectSourceSchema(fout, "pg_catalog");
|
selectSourceSchema(fout, "pg_catalog");
|
||||||
query = createPQExpBuffer();
|
query = createPQExpBuffer();
|
||||||
printfPQExpBuffer(query,
|
printfPQExpBuffer(query,
|
||||||
|
|
|
||||||
|
|
@ -498,6 +498,7 @@ typedef struct _SubscriptionInfo {
|
||||||
char *subslotname;
|
char *subslotname;
|
||||||
char *subsynccommit;
|
char *subsynccommit;
|
||||||
char *subpublications;
|
char *subpublications;
|
||||||
|
char *subbinary;
|
||||||
} SubscriptionInfo;
|
} SubscriptionInfo;
|
||||||
|
|
||||||
/* global decls */
|
/* global decls */
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@
|
||||||
it will be backuped up in external dirs */
|
it will be backuped up in external dirs */
|
||||||
parray *pgdata_nobackup_dir = NULL;
|
parray *pgdata_nobackup_dir = NULL;
|
||||||
|
|
||||||
|
/* list of logical replication slots */
|
||||||
|
parray *logical_replslot = NULL;
|
||||||
|
|
||||||
static int standby_message_timeout_local = 10 ; /* 10 sec = default */
|
static int standby_message_timeout_local = 10 ; /* 10 sec = default */
|
||||||
static XLogRecPtr stop_backup_lsn = InvalidXLogRecPtr;
|
static XLogRecPtr stop_backup_lsn = InvalidXLogRecPtr;
|
||||||
static XLogRecPtr stop_stream_lsn = InvalidXLogRecPtr;
|
static XLogRecPtr stop_stream_lsn = InvalidXLogRecPtr;
|
||||||
|
|
@ -89,10 +92,11 @@ static void backup_cleanup(bool fatal, void *userdata);
|
||||||
|
|
||||||
static void *backup_files(void *arg);
|
static void *backup_files(void *arg);
|
||||||
|
|
||||||
static void do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs);
|
static void do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs,
|
||||||
|
bool backup_replslots);
|
||||||
|
|
||||||
static void pg_start_backup(const char *label, bool smooth, pgBackup *backup,
|
static void pg_start_backup(const char *label, bool smooth, pgBackup *backup,
|
||||||
PGNodeInfo *nodeInfo, PGconn *conn);
|
PGNodeInfo *nodeInfo, PGconn *conn, bool backup_replslots);
|
||||||
static void pg_stop_backup(pgBackup *backup, PGconn *pg_startbackup_conn, PGNodeInfo *nodeInfo);
|
static void pg_stop_backup(pgBackup *backup, PGconn *pg_startbackup_conn, PGNodeInfo *nodeInfo);
|
||||||
static int checkpoint_timeout(PGconn *backup_conn);
|
static int checkpoint_timeout(PGconn *backup_conn);
|
||||||
|
|
||||||
|
|
@ -558,7 +562,7 @@ static void sync_files(parray *database_map, const char *database_path, parray *
|
||||||
* Move files from 'pgdata' to a subdirectory in 'backup_path'.
|
* Move files from 'pgdata' to a subdirectory in 'backup_path'.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs)
|
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs, bool backup_replslots)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
char database_path[MAXPGPATH];
|
char database_path[MAXPGPATH];
|
||||||
|
|
@ -591,7 +595,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
|
||||||
securec_check_c(rc, "\0", "\0");
|
securec_check_c(rc, "\0", "\0");
|
||||||
|
|
||||||
/* Call pg_start_backup function in openGauss connect */
|
/* Call pg_start_backup function in openGauss connect */
|
||||||
pg_start_backup(label, smooth_checkpoint, ¤t, nodeInfo, backup_conn);
|
pg_start_backup(label, smooth_checkpoint, ¤t, nodeInfo, backup_conn, backup_replslots);
|
||||||
|
|
||||||
/* Obtain current timeline */
|
/* Obtain current timeline */
|
||||||
#if PG_VERSION_NUM >= 90600
|
#if PG_VERSION_NUM >= 90600
|
||||||
|
|
@ -624,10 +628,10 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
|
||||||
/* list files with the logical path. omit $PGDATA */
|
/* list files with the logical path. omit $PGDATA */
|
||||||
if (fio_is_remote(FIO_DB_HOST))
|
if (fio_is_remote(FIO_DB_HOST))
|
||||||
fio_list_dir(backup_files_list, instance_config.pgdata,
|
fio_list_dir(backup_files_list, instance_config.pgdata,
|
||||||
true, true, false, backup_logs, true, 0);
|
true, true, false, backup_logs, true, 0, backup_replslots);
|
||||||
else
|
else
|
||||||
dir_list_file(backup_files_list, instance_config.pgdata,
|
dir_list_file(backup_files_list, instance_config.pgdata,
|
||||||
true, true, false, backup_logs, true, 0, FIO_LOCAL_HOST);
|
true, true, false, backup_logs, true, 0, FIO_LOCAL_HOST, backup_replslots);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get database_map (name to oid) for use in partial restore feature.
|
* Get database_map (name to oid) for use in partial restore feature.
|
||||||
|
|
@ -749,6 +753,11 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
|
||||||
}
|
}
|
||||||
pgdata_nobackup_dir = NULL;
|
pgdata_nobackup_dir = NULL;
|
||||||
|
|
||||||
|
if (logical_replslot) {
|
||||||
|
free_dir_list(logical_replslot);
|
||||||
|
}
|
||||||
|
logical_replslot = NULL;
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
if (backup_list)
|
if (backup_list)
|
||||||
{
|
{
|
||||||
|
|
@ -849,7 +858,7 @@ static void do_after_backup()
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
|
do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
|
||||||
bool no_validate, bool no_sync, bool backup_logs)
|
bool no_validate, bool no_sync, bool backup_logs, bool backup_replslots)
|
||||||
{
|
{
|
||||||
PGconn *backup_conn = NULL;
|
PGconn *backup_conn = NULL;
|
||||||
PGNodeInfo nodeInfo;
|
PGNodeInfo nodeInfo;
|
||||||
|
|
@ -925,7 +934,7 @@ do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
|
||||||
add_note(¤t, set_backup_params->note);
|
add_note(¤t, set_backup_params->note);
|
||||||
|
|
||||||
/* backup data */
|
/* backup data */
|
||||||
do_backup_instance(backup_conn, &nodeInfo, no_sync, backup_logs);
|
do_backup_instance(backup_conn, &nodeInfo, no_sync, backup_logs, backup_replslots);
|
||||||
pgut_atexit_pop(backup_cleanup, NULL);
|
pgut_atexit_pop(backup_cleanup, NULL);
|
||||||
|
|
||||||
/* compute size of wal files of this backup stored in the archive */
|
/* compute size of wal files of this backup stored in the archive */
|
||||||
|
|
@ -1034,13 +1043,15 @@ confirm_block_size(PGconn *conn, const char *name, int blcksz)
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
pg_start_backup(const char *label, bool smooth, pgBackup *backup,
|
pg_start_backup(const char *label, bool smooth, pgBackup *backup,
|
||||||
PGNodeInfo *nodeInfo, PGconn *conn)
|
PGNodeInfo *nodeInfo, PGconn *conn, bool backup_replslots)
|
||||||
{
|
{
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
const char *params[2];
|
const char *params[2];
|
||||||
uint32 lsn_hi;
|
uint32 lsn_hi;
|
||||||
uint32 lsn_lo;
|
uint32 lsn_lo;
|
||||||
int ret;
|
int ret;
|
||||||
|
int i;
|
||||||
|
XLogRecPtr startLsn;
|
||||||
|
|
||||||
params[0] = label;
|
params[0] = label;
|
||||||
|
|
||||||
|
|
@ -1068,7 +1079,33 @@ pg_start_backup(const char *label, bool smooth, pgBackup *backup,
|
||||||
XLogDataFromLSN(ret, PQgetvalue(res, 0, 0), &lsn_hi, &lsn_lo);
|
XLogDataFromLSN(ret, PQgetvalue(res, 0, 0), &lsn_hi, &lsn_lo);
|
||||||
securec_check_for_sscanf_s(ret, 2, "\0", "\0");
|
securec_check_for_sscanf_s(ret, 2, "\0", "\0");
|
||||||
/* Calculate LSN */
|
/* Calculate LSN */
|
||||||
backup->start_lsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
|
startLsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
|
||||||
|
|
||||||
|
if (backup_replslots) {
|
||||||
|
logical_replslot = parray_new();
|
||||||
|
/* query for logical replication slots of subscriptions */
|
||||||
|
res = pgut_execute(conn,
|
||||||
|
"SELECT slot_name, restart_lsn FROM pg_catalog.pg_get_replication_slots()"
|
||||||
|
"WHERE slot_type = 'logical' AND plugin = 'pgoutput'", 0, NULL);
|
||||||
|
if (PQntuples(res) == 0) {
|
||||||
|
elog(LOG, "logical replication slots for subscriptions not found");
|
||||||
|
} else {
|
||||||
|
XLogRecPtr repslotLsn;
|
||||||
|
|
||||||
|
for (i = 0; i < PQntuples(res); i++) {
|
||||||
|
XLogDataFromLSN(ret, PQgetvalue(res, i, 1), &lsn_hi, &lsn_lo);
|
||||||
|
securec_check_for_sscanf_s(ret, 2, "\0", "\0");
|
||||||
|
repslotLsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
|
||||||
|
startLsn = Min(startLsn, repslotLsn);
|
||||||
|
|
||||||
|
char* slotname = pg_strdup(PQgetvalue(res, i, 0));
|
||||||
|
parray_append(logical_replslot, slotname);
|
||||||
|
}
|
||||||
|
elog(WARNING, "logical replication slots for subscriptions will be backed up. "
|
||||||
|
"If don't use them after restoring, please drop them to avoid affecting xlog recycling.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
backup->start_lsn = startLsn;
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,13 +42,6 @@ const char *pgdata_exclude_dir[] =
|
||||||
(const char *)"pg_stat_tmp",
|
(const char *)"pg_stat_tmp",
|
||||||
(const char *)"pgsql_tmp",
|
(const char *)"pgsql_tmp",
|
||||||
|
|
||||||
/*
|
|
||||||
* It is generally not useful to backup the contents of this directory even
|
|
||||||
* if the intention is to restore to another master. See backup.sgml for a
|
|
||||||
* more detailed description.
|
|
||||||
*/
|
|
||||||
(const char *)"pg_replslot",
|
|
||||||
|
|
||||||
/* Contents removed on startup, see dsm_cleanup_for_mmap(). */
|
/* Contents removed on startup, see dsm_cleanup_for_mmap(). */
|
||||||
(const char *)"pg_dynshmem",
|
(const char *)"pg_dynshmem",
|
||||||
|
|
||||||
|
|
@ -68,7 +61,7 @@ const char *pgdata_exclude_dir[] =
|
||||||
(const char *)"pg_subtrans",
|
(const char *)"pg_subtrans",
|
||||||
|
|
||||||
/* end of list */
|
/* end of list */
|
||||||
NULL, /* pg_log will be set later */
|
NULL, /* pg_log and pg_replslot will be set later */
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -128,17 +121,20 @@ may be removed int the future */
|
||||||
|
|
||||||
static int pgCompareString(const void *str1, const void *str2);
|
static int pgCompareString(const void *str1, const void *str2);
|
||||||
|
|
||||||
static char dir_check_file(pgFile *file, bool backup_logs);
|
static char dir_check_file(pgFile *file, bool backup_logs, bool backup_replslots);
|
||||||
static char check_in_tablespace(pgFile *file, bool in_tablespace);
|
static char check_in_tablespace(pgFile *file, bool in_tablespace);
|
||||||
static char check_db_dir(pgFile *file);
|
static char check_db_dir(pgFile *file);
|
||||||
static char check_digit_file(pgFile *file);
|
static char check_digit_file(pgFile *file);
|
||||||
static char check_nobackup_dir(pgFile *file);
|
static char check_nobackup_dir(pgFile *file);
|
||||||
static void dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
|
static void dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
|
||||||
bool exclude, bool follow_symlink, bool backup_logs,
|
bool exclude, bool follow_symlink, bool backup_logs,
|
||||||
bool skip_hidden, int external_dir_num, fio_location location);
|
bool skip_hidden, int external_dir_num, fio_location location,
|
||||||
|
bool backup_replslots);
|
||||||
static void opt_path_map(ConfigOption *opt, const char *arg,
|
static void opt_path_map(ConfigOption *opt, const char *arg,
|
||||||
TablespaceList *list, const char *type);
|
TablespaceList *list, const char *type);
|
||||||
|
|
||||||
|
char check_logical_replslot_dir(const char *rel_path);
|
||||||
|
|
||||||
/* Tablespace mapping */
|
/* Tablespace mapping */
|
||||||
static TablespaceList tablespace_dirs = {NULL, NULL};
|
static TablespaceList tablespace_dirs = {NULL, NULL};
|
||||||
/* Extra directories mapping */
|
/* Extra directories mapping */
|
||||||
|
|
@ -538,7 +534,7 @@ db_map_entry_free(void *entry)
|
||||||
void
|
void
|
||||||
dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink,
|
dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink,
|
||||||
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num,
|
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num,
|
||||||
fio_location location)
|
fio_location location, bool backup_replslots)
|
||||||
{
|
{
|
||||||
pgFile *file;
|
pgFile *file;
|
||||||
|
|
||||||
|
|
@ -565,7 +561,7 @@ dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink
|
||||||
parray_append(files, file);
|
parray_append(files, file);
|
||||||
|
|
||||||
dir_list_file_internal(files, file, root, exclude, follow_symlink,
|
dir_list_file_internal(files, file, root, exclude, follow_symlink,
|
||||||
backup_logs, skip_hidden, external_dir_num, location);
|
backup_logs, skip_hidden, external_dir_num, location, backup_replslots);
|
||||||
|
|
||||||
if (!add_root)
|
if (!add_root)
|
||||||
pgFileFree(file);
|
pgFileFree(file);
|
||||||
|
|
@ -589,7 +585,7 @@ dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink
|
||||||
* - datafiles
|
* - datafiles
|
||||||
*/
|
*/
|
||||||
static char
|
static char
|
||||||
dir_check_file(pgFile *file, bool backup_logs)
|
dir_check_file(pgFile *file, bool backup_logs, bool backup_replslots)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int sscanf_res;
|
int sscanf_res;
|
||||||
|
|
@ -652,6 +648,29 @@ dir_check_file(pgFile *file, bool backup_logs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Backup pg_replslot if it is specified.
|
||||||
|
* It is generally not useful to backup the contents of this directory even
|
||||||
|
* if the intention is to restore to another master. See backup.sgml for a
|
||||||
|
* more detailed description.
|
||||||
|
*/
|
||||||
|
if (!backup_replslots) {
|
||||||
|
if (strcmp(file->rel_path, PG_REPLSLOT_DIR) == 0) {
|
||||||
|
/* Skip */
|
||||||
|
elog(VERBOSE, "Excluding directory content: %s", file->rel_path);
|
||||||
|
return CHECK_EXCLUDE_FALSE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Check file that under pg_replslot and judge whether it
|
||||||
|
* belonged to logical replication slots for subscriptions.
|
||||||
|
*/
|
||||||
|
if (strcmp(file->rel_path, PG_REPLSLOT_DIR) != 0 &&
|
||||||
|
path_is_prefix_of_path(PG_REPLSLOT_DIR, file->rel_path)) {
|
||||||
|
return check_logical_replslot_dir(file->rel_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = check_nobackup_dir(file);
|
ret = check_nobackup_dir(file);
|
||||||
if (ret != -1) { /* -1 means need backup */
|
if (ret != -1) { /* -1 means need backup */
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -749,6 +768,35 @@ static char check_nobackup_dir(pgFile *file)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char check_logical_replslot_dir(const char *rel_path)
|
||||||
|
{
|
||||||
|
char ret = CHECK_FALSE;
|
||||||
|
int i = 0;
|
||||||
|
char *tmp = pg_strdup(rel_path);
|
||||||
|
char *p;
|
||||||
|
#define DIRECTORY_DELIMITER "/"
|
||||||
|
|
||||||
|
if (logical_replslot) {
|
||||||
|
/* extract slot name from rel_path, such as sub1 from pg_replslot/sub1/snap */
|
||||||
|
p = strtok(tmp, DIRECTORY_DELIMITER);
|
||||||
|
if (p != NULL) {
|
||||||
|
p = strtok(NULL, DIRECTORY_DELIMITER);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; p != NULL && i < (int)parray_num(logical_replslot); i++) {
|
||||||
|
char *slotName = (char *)parray_get(logical_replslot, i);
|
||||||
|
if (strcmp(p, slotName) == 0) {
|
||||||
|
pfree(tmp);
|
||||||
|
return CHECK_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = CHECK_TRUE;
|
||||||
|
}
|
||||||
|
pfree(tmp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static char check_db_dir(pgFile *file)
|
static char check_db_dir(pgFile *file)
|
||||||
{
|
{
|
||||||
char ret = -1;
|
char ret = -1;
|
||||||
|
|
@ -889,7 +937,8 @@ bool SkipSomeDirFile(pgFile *file, struct dirent *dent, bool skipHidden)
|
||||||
static void
|
static void
|
||||||
dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
|
dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
|
||||||
bool exclude, bool follow_symlink, bool backup_logs,
|
bool exclude, bool follow_symlink, bool backup_logs,
|
||||||
bool skip_hidden, int external_dir_num, fio_location location)
|
bool skip_hidden, int external_dir_num, fio_location location,
|
||||||
|
bool backup_replslots)
|
||||||
{
|
{
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
struct dirent *dent;
|
struct dirent *dent;
|
||||||
|
|
@ -937,7 +986,7 @@ dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
|
||||||
|
|
||||||
if (exclude)
|
if (exclude)
|
||||||
{
|
{
|
||||||
check_res = dir_check_file(file, backup_logs);
|
check_res = dir_check_file(file, backup_logs, backup_replslots);
|
||||||
if (check_res == CHECK_FALSE)
|
if (check_res == CHECK_FALSE)
|
||||||
{
|
{
|
||||||
/* Skip */
|
/* Skip */
|
||||||
|
|
@ -963,7 +1012,7 @@ dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
|
||||||
*/
|
*/
|
||||||
if (S_ISDIR(file->mode))
|
if (S_ISDIR(file->mode))
|
||||||
dir_list_file_internal(files, file, child, exclude, follow_symlink,
|
dir_list_file_internal(files, file, child, exclude, follow_symlink,
|
||||||
backup_logs, skip_hidden, external_dir_num, location);
|
backup_logs, skip_hidden, external_dir_num, location, backup_replslots);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errno && errno != ENOENT)
|
if (errno && errno != ENOENT)
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ typedef struct
|
||||||
bool exclusive_backup;
|
bool exclusive_backup;
|
||||||
bool skip_hidden;
|
bool skip_hidden;
|
||||||
int external_dir_num;
|
int external_dir_num;
|
||||||
|
bool backup_replslots;
|
||||||
} fio_list_dir_request;
|
} fio_list_dir_request;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
|
@ -1794,7 +1795,7 @@ cleanup:
|
||||||
/* Compile the array of files located on remote machine in directory root */
|
/* Compile the array of files located on remote machine in directory root */
|
||||||
void fio_list_dir(parray *files, const char *root, bool exclude,
|
void fio_list_dir(parray *files, const char *root, bool exclude,
|
||||||
bool follow_symlink, bool add_root, bool backup_logs,
|
bool follow_symlink, bool add_root, bool backup_logs,
|
||||||
bool skip_hidden, int external_dir_num)
|
bool skip_hidden, int external_dir_num, bool backup_replslots)
|
||||||
{
|
{
|
||||||
fio_header hdr;
|
fio_header hdr;
|
||||||
fio_list_dir_request req;
|
fio_list_dir_request req;
|
||||||
|
|
@ -1811,6 +1812,7 @@ void fio_list_dir(parray *files, const char *root, bool exclude,
|
||||||
req.exclusive_backup = exclusive_backup;
|
req.exclusive_backup = exclusive_backup;
|
||||||
req.skip_hidden = skip_hidden;
|
req.skip_hidden = skip_hidden;
|
||||||
req.external_dir_num = external_dir_num;
|
req.external_dir_num = external_dir_num;
|
||||||
|
req.backup_replslots = backup_replslots;
|
||||||
|
|
||||||
hdr.cop = FIO_LIST_DIR;
|
hdr.cop = FIO_LIST_DIR;
|
||||||
hdr.size = sizeof(req);
|
hdr.size = sizeof(req);
|
||||||
|
|
@ -1870,7 +1872,14 @@ void fio_list_dir(parray *files, const char *root, bool exclude,
|
||||||
securec_check_ss_c(nRet, "\0", "\0");
|
securec_check_ss_c(nRet, "\0", "\0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check file that under pg_replslot and judge whether it
|
||||||
|
* belonged to logical replication slots for subscriptions.
|
||||||
|
*/
|
||||||
|
if (backup_replslots && strcmp(buf, PG_REPLSLOT_DIR) != 0 &&
|
||||||
|
path_is_prefix_of_path(PG_REPLSLOT_DIR, buf) && check_logical_replslot_dir(file->rel_path) != 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
parray_append(files, file);
|
parray_append(files, file);
|
||||||
}
|
}
|
||||||
|
|
@ -1914,7 +1923,7 @@ static void fio_list_dir_impl(int out, char* buf)
|
||||||
|
|
||||||
dir_list_file(file_files, req->path, req->exclude, req->follow_symlink,
|
dir_list_file(file_files, req->path, req->exclude, req->follow_symlink,
|
||||||
req->add_root, req->backup_logs, req->skip_hidden,
|
req->add_root, req->backup_logs, req->skip_hidden,
|
||||||
req->external_dir_num, FIO_LOCAL_HOST);
|
req->external_dir_num, FIO_LOCAL_HOST, req->backup_replslots);
|
||||||
|
|
||||||
/* send information about files to the main process */
|
/* send information about files to the main process */
|
||||||
for (i = 0; i < (int)parray_num(file_files); i++)
|
for (i = 0; i < (int)parray_num(file_files); i++)
|
||||||
|
|
|
||||||
|
|
@ -163,5 +163,7 @@ extern z_off_t fio_gzseek(gzFile f, z_off_t offset, int whence);
|
||||||
extern const char* fio_gzerror(gzFile file, int *errnum);
|
extern const char* fio_gzerror(gzFile file, int *errnum);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern char check_logical_replslot_dir(const char *rel_path);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,7 @@ void help_pg_probackup(void)
|
||||||
printf(_(" [--remote-port=port] [--ssh-options=ssh_options]\n"));
|
printf(_(" [--remote-port=port] [--ssh-options=ssh_options]\n"));
|
||||||
printf(_(" [--remote-libpath=libpath]\n"));
|
printf(_(" [--remote-libpath=libpath]\n"));
|
||||||
printf(_(" [--ttl=interval] [--expire-time=time]\n"));
|
printf(_(" [--ttl=interval] [--expire-time=time]\n"));
|
||||||
|
printf(_(" [--backup-pg-replslot]\n"));
|
||||||
printf(_(" [--help]\n"));
|
printf(_(" [--help]\n"));
|
||||||
|
|
||||||
printf(_("\n %s restore -B backup-path --instance=instance_name\n"), PROGRAM_NAME);
|
printf(_("\n %s restore -B backup-path --instance=instance_name\n"), PROGRAM_NAME);
|
||||||
|
|
@ -420,6 +421,7 @@ static void help_backup(void)
|
||||||
printf(_(" [--remote-port=port] [--ssh-options=ssh_options]\n"));
|
printf(_(" [--remote-port=port] [--ssh-options=ssh_options]\n"));
|
||||||
printf(_(" [--remote-libpath=libpath]\n"));
|
printf(_(" [--remote-libpath=libpath]\n"));
|
||||||
printf(_(" [--ttl=interval] [--expire-time=time]\n\n"));
|
printf(_(" [--ttl=interval] [--expire-time=time]\n\n"));
|
||||||
|
printf(_(" [--backup-pg-replslot]\n"));
|
||||||
|
|
||||||
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
|
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
|
||||||
printf(_(" --instance=instance_name name of the instance\n"));
|
printf(_(" --instance=instance_name name of the instance\n"));
|
||||||
|
|
@ -441,6 +443,7 @@ static void help_backup(void)
|
||||||
printf(_(" --note=text add note to backup\n"));
|
printf(_(" --note=text add note to backup\n"));
|
||||||
printf(_(" (example: --note='backup before app update to v13.1')\n"));
|
printf(_(" (example: --note='backup before app update to v13.1')\n"));
|
||||||
printf(_(" --archive-timeout=timeout wait timeout for WAL segment archiving (default: 5min)\n"));
|
printf(_(" --archive-timeout=timeout wait timeout for WAL segment archiving (default: 5min)\n"));
|
||||||
|
printf(_(" --backup-pg-replslot] backup of '%s' directory\n"), PG_REPLSLOT_DIR);
|
||||||
|
|
||||||
printf(_("\n Logging options:\n"));
|
printf(_("\n Logging options:\n"));
|
||||||
printf(_(" --log-level-console=log-level-console\n"));
|
printf(_(" --log-level-console=log-level-console\n"));
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ int rw_timeout = 0;
|
||||||
|
|
||||||
/* backup options */
|
/* backup options */
|
||||||
bool backup_logs = false;
|
bool backup_logs = false;
|
||||||
|
bool backup_replslots = false;
|
||||||
bool smooth_checkpoint;
|
bool smooth_checkpoint;
|
||||||
char *remote_agent;
|
char *remote_agent;
|
||||||
static char *backup_note = NULL;
|
static char *backup_note = NULL;
|
||||||
|
|
@ -186,6 +187,7 @@ static ConfigOption cmd_options[] =
|
||||||
{ 'b', 145, "wal", &delete_wal, SOURCE_CMD_STRICT },
|
{ 'b', 145, "wal", &delete_wal, SOURCE_CMD_STRICT },
|
||||||
{ 'b', 146, "expired", &delete_expired, SOURCE_CMD_STRICT },
|
{ 'b', 146, "expired", &delete_expired, SOURCE_CMD_STRICT },
|
||||||
{ 's', 172, "status", &delete_status, SOURCE_CMD_STRICT },
|
{ 's', 172, "status", &delete_status, SOURCE_CMD_STRICT },
|
||||||
|
{ 'b', 186, "backup-pg-replslot", &backup_replslots, SOURCE_CMD_STRICT},
|
||||||
|
|
||||||
{ 'b', 147, "force", &force, SOURCE_CMD_STRICT },
|
{ 'b', 147, "force", &force, SOURCE_CMD_STRICT },
|
||||||
{ 'b', 148, "compress", &compress_shortcut, SOURCE_CMD_STRICT },
|
{ 'b', 148, "compress", &compress_shortcut, SOURCE_CMD_STRICT },
|
||||||
|
|
@ -550,7 +552,7 @@ static int do_actual_operate()
|
||||||
elog(ERROR, "required parameter not specified: BACKUP_MODE "
|
elog(ERROR, "required parameter not specified: BACKUP_MODE "
|
||||||
"(-b, --backup-mode)");
|
"(-b, --backup-mode)");
|
||||||
|
|
||||||
return do_backup(start_time, set_backup_params, no_validate, no_sync, backup_logs);
|
return do_backup(start_time, set_backup_params, no_validate, no_sync, backup_logs, backup_replslots);
|
||||||
}
|
}
|
||||||
case RESTORE_CMD:
|
case RESTORE_CMD:
|
||||||
return do_restore_or_validate(current.backup_id,
|
return do_restore_or_validate(current.backup_id,
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ extern const char *PROGRAM_FULL_PATH;
|
||||||
#define HEADER_MAP "page_header_map"
|
#define HEADER_MAP "page_header_map"
|
||||||
#define HEADER_MAP_TMP "page_header_map_tmp"
|
#define HEADER_MAP_TMP "page_header_map_tmp"
|
||||||
#define PG_RELATIVE_TBLSPC_DIR "pg_location"
|
#define PG_RELATIVE_TBLSPC_DIR "pg_location"
|
||||||
|
#define PG_REPLSLOT_DIR "pg_replslot"
|
||||||
|
|
||||||
/* Timeout defaults */
|
/* Timeout defaults */
|
||||||
#define ARCHIVE_TIMEOUT_DEFAULT 300
|
#define ARCHIVE_TIMEOUT_DEFAULT 300
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,9 @@ extern bool smooth_checkpoint;
|
||||||
it will be backuped up in external dirs */
|
it will be backuped up in external dirs */
|
||||||
extern parray *pgdata_nobackup_dir;
|
extern parray *pgdata_nobackup_dir;
|
||||||
|
|
||||||
|
/* list of logical replication slots */
|
||||||
|
extern parray *logical_replslot;
|
||||||
|
|
||||||
/* remote probackup options */
|
/* remote probackup options */
|
||||||
extern char* remote_agent;
|
extern char* remote_agent;
|
||||||
|
|
||||||
|
|
@ -89,7 +92,7 @@ extern const char *pgdata_exclude_dir[];
|
||||||
|
|
||||||
/* in backup.c */
|
/* in backup.c */
|
||||||
extern int do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
|
extern int do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
|
||||||
bool no_validate, bool no_sync, bool backup_logs);
|
bool no_validate, bool no_sync, bool backup_logs, bool backup_replslots);
|
||||||
extern BackupMode parse_backup_mode(const char *value);
|
extern BackupMode parse_backup_mode(const char *value);
|
||||||
extern const char *deparse_backup_mode(BackupMode mode);
|
extern const char *deparse_backup_mode(BackupMode mode);
|
||||||
extern void process_block_change(ForkNumber forknum, const RelFileNode rnode,
|
extern void process_block_change(ForkNumber forknum, const RelFileNode rnode,
|
||||||
|
|
@ -239,7 +242,8 @@ extern const char* deparse_compress_alg(int alg);
|
||||||
/* in dir.c */
|
/* in dir.c */
|
||||||
extern void dir_list_file(parray *files, const char *root, bool exclude,
|
extern void dir_list_file(parray *files, const char *root, bool exclude,
|
||||||
bool follow_symlink, bool add_root, bool backup_logs,
|
bool follow_symlink, bool add_root, bool backup_logs,
|
||||||
bool skip_hidden, int external_dir_num, fio_location location);
|
bool skip_hidden, int external_dir_num, fio_location location,
|
||||||
|
bool backup_replslots = false);
|
||||||
|
|
||||||
extern void create_data_directories(parray *dest_files,
|
extern void create_data_directories(parray *dest_files,
|
||||||
const char *data_dir,
|
const char *data_dir,
|
||||||
|
|
@ -432,7 +436,8 @@ extern int fio_send_file(const char *from_fullpath, const char *to_fullpath, FIL
|
||||||
pgFile *file, char **errormsg);
|
pgFile *file, char **errormsg);
|
||||||
|
|
||||||
extern void fio_list_dir(parray *files, const char *root, bool exclude, bool follow_symlink,
|
extern void fio_list_dir(parray *files, const char *root, bool exclude, bool follow_symlink,
|
||||||
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num);
|
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num,
|
||||||
|
bool backup_replslots = false);
|
||||||
|
|
||||||
extern bool pgut_rmtree(const char *path, bool rmtopdir, bool strict);
|
extern bool pgut_rmtree(const char *path, bool rmtopdir, bool strict);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6230,7 +6230,7 @@ Datum GetPartBoundaryByTuple(Relation rel, HeapTuple tuple)
|
||||||
return Timestamp2Boundarys(rel, Align2UpBoundary(value, partMap->intervalValue, boundaryTs));
|
return Timestamp2Boundarys(rel, Align2UpBoundary(value, partMap->intervalValue, boundaryTs));
|
||||||
}
|
}
|
||||||
|
|
||||||
Oid AddNewIntervalPartition(Relation rel, void* insertTuple)
|
Oid AddNewIntervalPartition(Relation rel, void* insertTuple, bool isDDL)
|
||||||
{
|
{
|
||||||
Relation pgPartRel = NULL;
|
Relation pgPartRel = NULL;
|
||||||
Oid newPartOid = InvalidOid;
|
Oid newPartOid = InvalidOid;
|
||||||
|
|
@ -6327,7 +6327,13 @@ Oid AddNewIntervalPartition(Relation rel, void* insertTuple)
|
||||||
*/
|
*/
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If add interval partition in the DDL, do not need to change the csn
|
||||||
|
* because the scn has been changed in the DDL.
|
||||||
|
*/
|
||||||
|
if (!isDDL) {
|
||||||
UpdatePgObjectChangecsn(RelationGetRelid(rel), rel->rd_rel->relkind);
|
UpdatePgObjectChangecsn(RelationGetRelid(rel), rel->rd_rel->relkind);
|
||||||
|
}
|
||||||
|
|
||||||
return newPartOid;
|
return newPartOid;
|
||||||
}
|
}
|
||||||
|
|
@ -7113,7 +7119,7 @@ int lookupHBucketid(oidvector *buckets, int low, int2 bktId)
|
||||||
* Description :
|
* Description :
|
||||||
* Notes :
|
* Notes :
|
||||||
*/
|
*/
|
||||||
Oid heapTupleGetPartitionId(Relation rel, void *tuple)
|
Oid heapTupleGetPartitionId(Relation rel, void *tuple, bool isDDL)
|
||||||
{
|
{
|
||||||
Oid partitionid = InvalidOid;
|
Oid partitionid = InvalidOid;
|
||||||
|
|
||||||
|
|
@ -7140,7 +7146,7 @@ Oid heapTupleGetPartitionId(Relation rel, void *tuple)
|
||||||
(errcode(ERRCODE_NO_DATA_FOUND), errmsg("inserted partition key does not map to any table partition")));
|
(errcode(ERRCODE_NO_DATA_FOUND), errmsg("inserted partition key does not map to any table partition")));
|
||||||
} break;
|
} break;
|
||||||
case PART_AREA_INTERVAL: {
|
case PART_AREA_INTERVAL: {
|
||||||
return AddNewIntervalPartition(rel, tuple);
|
return AddNewIntervalPartition(rel, tuple, isDDL);
|
||||||
} break;
|
} break;
|
||||||
case PART_AREA_LIST: {
|
case PART_AREA_LIST: {
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ static_assert(sizeof(false) == sizeof(char), "illegal bool size");
|
||||||
static struct HTAB* nameHash = NULL;
|
static struct HTAB* nameHash = NULL;
|
||||||
static struct HTAB* oidHash = NULL;
|
static struct HTAB* oidHash = NULL;
|
||||||
|
|
||||||
/* for b_sql_plugin */
|
/* for dolphin */
|
||||||
struct HTAB* b_nameHash = NULL;
|
struct HTAB* b_nameHash = NULL;
|
||||||
struct HTAB* b_oidHash = NULL;
|
struct HTAB* b_oidHash = NULL;
|
||||||
|
|
||||||
|
|
@ -118,7 +118,7 @@ static const FuncGroup* NameHashTableAccess(HASHACTION action, const char* name,
|
||||||
|
|
||||||
Assert(name != NULL);
|
Assert(name != NULL);
|
||||||
|
|
||||||
if (DB_IS_CMPT(B_FORMAT) && b_nameHash != NULL && u_sess->attr.attr_sql.b_sql_plugin) {
|
if (DB_IS_CMPT(B_FORMAT) && b_nameHash != NULL && u_sess->attr.attr_sql.dolphin) {
|
||||||
result = (HashEntryNameToFuncGroup *)hash_search(b_nameHash, &temp_name, action, &found);
|
result = (HashEntryNameToFuncGroup *)hash_search(b_nameHash, &temp_name, action, &found);
|
||||||
} else {
|
} else {
|
||||||
result = (HashEntryNameToFuncGroup *)hash_search(nameHash, &temp_name, action, &found);
|
result = (HashEntryNameToFuncGroup *)hash_search(nameHash, &temp_name, action, &found);
|
||||||
|
|
@ -144,7 +144,7 @@ static const Builtin_func* OidHashTableAccess(HASHACTION action, Oid oid, const
|
||||||
bool found = false;
|
bool found = false;
|
||||||
Assert(oid > 0);
|
Assert(oid > 0);
|
||||||
|
|
||||||
if (DB_IS_CMPT(B_FORMAT) && b_oidHash != NULL && u_sess->attr.attr_sql.b_sql_plugin) {
|
if (DB_IS_CMPT(B_FORMAT) && b_oidHash != NULL && u_sess->attr.attr_sql.dolphin) {
|
||||||
result = (HashEntryOidToBuiltinFunc *)hash_search(b_oidHash, &oid, action, &found);
|
result = (HashEntryOidToBuiltinFunc *)hash_search(b_oidHash, &oid, action, &found);
|
||||||
} else {
|
} else {
|
||||||
result = (HashEntryOidToBuiltinFunc *)hash_search(oidHash, &oid, action, &found);
|
result = (HashEntryOidToBuiltinFunc *)hash_search(oidHash, &oid, action, &found);
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
#include "replication/worker_internal.h"
|
|
||||||
|
|
||||||
static List *textarray_to_stringlist(ArrayType *textarray);
|
static List *textarray_to_stringlist(ArrayType *textarray);
|
||||||
|
|
||||||
|
|
@ -91,6 +90,13 @@ Subscription *GetSubscription(Oid subid, bool missing_ok)
|
||||||
}
|
}
|
||||||
sub->publications = textarray_to_stringlist(DatumGetArrayTypeP(datum));
|
sub->publications = textarray_to_stringlist(DatumGetArrayTypeP(datum));
|
||||||
|
|
||||||
|
datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subbinary, &isnull);
|
||||||
|
if (unlikely(isnull)) {
|
||||||
|
sub->binary = false;
|
||||||
|
} else {
|
||||||
|
sub->binary = DatumGetBool(datum);
|
||||||
|
}
|
||||||
|
|
||||||
ReleaseSysCache(tup);
|
ReleaseSysCache(tup);
|
||||||
|
|
||||||
return sub;
|
return sub;
|
||||||
|
|
@ -183,7 +189,7 @@ char *get_subscription_name(Oid subid, bool missing_ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear the list content, only deal with DefElem and string content */
|
/* Clear the list content, only deal with DefElem and string content */
|
||||||
static void ClearListContent(List *list)
|
void ClearListContent(List *list)
|
||||||
{
|
{
|
||||||
ListCell *cell = NULL;
|
ListCell *cell = NULL;
|
||||||
foreach(cell, list) {
|
foreach(cell, list) {
|
||||||
|
|
@ -203,25 +209,6 @@ static void ClearListContent(List *list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Decrypt conninfo for subscription.
|
|
||||||
* IMPORTANT: caller should clear and free the memory after using it immediately
|
|
||||||
*/
|
|
||||||
char *DecryptConninfo(char *encryptConninfo)
|
|
||||||
{
|
|
||||||
const char* sensitiveOptionsArray[] = {"password"};
|
|
||||||
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
|
|
||||||
List *defList = ConninfoToDefList(encryptConninfo);
|
|
||||||
DecryptOptions(defList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
|
||||||
char *decryptConninfo = DefListToString(defList);
|
|
||||||
|
|
||||||
/* defList has plain content, clear it before free */
|
|
||||||
ClearListContent(defList);
|
|
||||||
list_free_ext(defList);
|
|
||||||
/* IMPORTANT: caller should clear and free the memory after using it immediately */
|
|
||||||
return decryptConninfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert text array to list of strings.
|
* Convert text array to list of strings.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@
|
||||||
|
|
||||||
THR_LOCAL bool skip_read_extern_fields = false;
|
THR_LOCAL bool skip_read_extern_fields = false;
|
||||||
|
|
||||||
|
#define IS_DATANODE_BUT_NOT_SINGLENODE (IS_PGXC_DATANODE && !IS_SINGLE_NODE)
|
||||||
/*
|
/*
|
||||||
* Macros to simplify reading of different kinds of fields. Use these
|
* Macros to simplify reading of different kinds of fields. Use these
|
||||||
* wherever possible to reduce the chance for silly typos. Note that these
|
* wherever possible to reduce the chance for silly typos. Note that these
|
||||||
|
|
@ -414,7 +415,10 @@ THR_LOCAL bool skip_read_extern_fields = false;
|
||||||
token = pg_strtok(&length); \
|
token = pg_strtok(&length); \
|
||||||
token = pg_strtok(&length); \
|
token = pg_strtok(&length); \
|
||||||
exprtypenamespace = nullable_string(token, length); \
|
exprtypenamespace = nullable_string(token, length); \
|
||||||
|
/* No need to reset field on CN or singlenode, keep pg_strtok() for forward compatibility */ \
|
||||||
|
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
|
||||||
local_node->fldname = get_typeoid(get_namespace_oid(exprtypenamespace, false), exprtypename); \
|
local_node->fldname = get_typeoid(get_namespace_oid(exprtypenamespace, false), exprtypename); \
|
||||||
|
} \
|
||||||
pfree_ext(exprtypename); \
|
pfree_ext(exprtypename); \
|
||||||
pfree_ext(exprtypenamespace); \
|
pfree_ext(exprtypenamespace); \
|
||||||
} \
|
} \
|
||||||
|
|
@ -493,9 +497,30 @@ THR_LOCAL bool skip_read_extern_fields = false;
|
||||||
token = pg_strtok(&length); \
|
token = pg_strtok(&length); \
|
||||||
token = pg_strtok(&length); \
|
token = pg_strtok(&length); \
|
||||||
funcnamespace = nullable_string(token, length); \
|
funcnamespace = nullable_string(token, length); \
|
||||||
if (IS_PGXC_DATANODE && !skip_read_extern_fields) { \
|
bool notfound = false; \
|
||||||
local_node->fldname = \
|
if (IS_DATANODE_BUT_NOT_SINGLENODE && !skip_read_extern_fields) { \
|
||||||
get_func_oid(funcname, get_namespace_oid(funcnamespace, false), (Expr*)local_node); \
|
Oid funcoid = InvalidOid; \
|
||||||
|
do { \
|
||||||
|
Oid nspid = get_namespace_oid(funcnamespace, true); \
|
||||||
|
if (!OidIsValid(nspid)) { \
|
||||||
|
notfound = true; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
funcoid = get_func_oid(funcname, nspid, (Expr*)local_node); \
|
||||||
|
} while (0); \
|
||||||
|
if (notfound || !OidIsValid(funcoid)) { \
|
||||||
|
ereport(ERROR, \
|
||||||
|
(errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_OBJECT), \
|
||||||
|
errmsg("Cannot identify function %s.%s while deserializing field.", \
|
||||||
|
funcname, funcnamespace), \
|
||||||
|
errdetail("Function with oid %u or its namespace may be renamed", \
|
||||||
|
local_node->fldname), \
|
||||||
|
errhint("Please rebuild column defalt expression, views etc. that are" \
|
||||||
|
" related to this renamed object."), \
|
||||||
|
errcause("Object renamed after recorded as nodetree."), \
|
||||||
|
erraction("Rebuild relevant object."))); \
|
||||||
|
} \
|
||||||
|
local_node->fldname = funcoid; \
|
||||||
} \
|
} \
|
||||||
pfree_ext(funcname); \
|
pfree_ext(funcname); \
|
||||||
pfree_ext(funcnamespace); \
|
pfree_ext(funcnamespace); \
|
||||||
|
|
@ -525,7 +550,7 @@ THR_LOCAL bool skip_read_extern_fields = false;
|
||||||
token = pg_strtok(&length); \
|
token = pg_strtok(&length); \
|
||||||
token = pg_strtok(&length); \
|
token = pg_strtok(&length); \
|
||||||
oprrightname = nullable_string(token, length); \
|
oprrightname = nullable_string(token, length); \
|
||||||
if (IS_PGXC_DATANODE) { \
|
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
|
||||||
namespaceId = get_namespace_oid(opnamespace, false); \
|
namespaceId = get_namespace_oid(opnamespace, false); \
|
||||||
oprleft = get_typeoid(namespaceId, oprleftname); \
|
oprleft = get_typeoid(namespaceId, oprleftname); \
|
||||||
oprright = oprleft; \
|
oprright = oprleft; \
|
||||||
|
|
@ -568,7 +593,7 @@ THR_LOCAL bool skip_read_extern_fields = false;
|
||||||
token = pg_strtok(&length); \
|
token = pg_strtok(&length); \
|
||||||
token = pg_strtok(&length); \
|
token = pg_strtok(&length); \
|
||||||
oprrightname = nullable_string(token, length); \
|
oprrightname = nullable_string(token, length); \
|
||||||
if (IS_PGXC_DATANODE) { \
|
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
|
||||||
namespaceId = get_namespace_oid(opnamespace, false); \
|
namespaceId = get_namespace_oid(opnamespace, false); \
|
||||||
oprleft = get_typeoid(namespaceId, oprleftname); \
|
oprleft = get_typeoid(namespaceId, oprleftname); \
|
||||||
oprright = oprleft; \
|
oprright = oprleft; \
|
||||||
|
|
@ -2126,14 +2151,21 @@ static FuncExpr* _readFuncExpr(void)
|
||||||
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("NULL seqNamespace for nextval()")));
|
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("NULL seqNamespace for nextval()")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_PGXC_COORDINATOR && !skip_read_extern_fields) {
|
if (IS_DATANODE_BUT_NOT_SINGLENODE && !skip_read_extern_fields) {
|
||||||
Oid seqid = get_valid_relname_relid(seqNamespace, seqName);
|
|
||||||
|
|
||||||
if (OidIsValid(seqid)) {
|
Oid seqid = get_valid_relname_relid(seqNamespace, seqName, true);
|
||||||
Const* firstArg = (Const*)linitial(local_node->args);
|
Const* firstArg = (Const*)linitial(local_node->args);
|
||||||
|
if (OidIsValid(seqid)) {
|
||||||
if (firstArg != NULL) {
|
if (firstArg != NULL) {
|
||||||
firstArg->constvalue = ObjectIdGetDatum(seqid);
|
firstArg->constvalue = ObjectIdGetDatum(seqid);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
|
errmsg("Cannot identify sequence %s.%s while deserializing field.", seqNamespace, seqName),
|
||||||
|
errdetail("Sequence with oid %u or its namespace may be renamed",
|
||||||
|
DatumGetObjectId(firstArg->constvalue)),
|
||||||
|
errhint("Please rebuild column defalt expression, views etc. that are related to this sequence"),
|
||||||
|
errcause("Object renamed after recorded as nodetree."), erraction("Rebuild relevant object.")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pfree_ext(seqName);
|
pfree_ext(seqName);
|
||||||
|
|
|
||||||
|
|
@ -1455,6 +1455,13 @@ FuncCandidateList sort_candidate_func_list(FuncCandidateList oldCandidates)
|
||||||
candidates[smallestIndex] = NULL;
|
candidates[smallestIndex] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (candidates[i] != NULL) {
|
||||||
|
lastCandidate->next = candidates[i];
|
||||||
|
lastCandidate = lastCandidate->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastCandidate->next = NULL;
|
||||||
pfree(candidates);
|
pfree(candidates);
|
||||||
return sortedCandidates;
|
return sortedCandidates;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ void GlobalBaseDefCache::RemoveElemFromBucket(GlobalBaseEntry *base)
|
||||||
if (is_relation) {
|
if (is_relation) {
|
||||||
GlobalRelationEntry *entry = (GlobalRelationEntry *)base;
|
GlobalRelationEntry *entry = (GlobalRelationEntry *)base;
|
||||||
uint64 rel_size = GetRelEstimateSize(entry);
|
uint64 rel_size = GetRelEstimateSize(entry);
|
||||||
pg_atomic_fetch_sub_u64(&m_base_space, rel_size);
|
pg_atomic_fetch_sub_u64(&m_base_space, AllocSetContextUsedSpace(((AllocSet)entry->rel_mem_manager)));
|
||||||
m_db_entry->MemoryEstimateSub(rel_size);
|
m_db_entry->MemoryEstimateSub(rel_size);
|
||||||
} else {
|
} else {
|
||||||
GlobalPartitionEntry *entry = (GlobalPartitionEntry *)base;
|
GlobalPartitionEntry *entry = (GlobalPartitionEntry *)base;
|
||||||
|
|
@ -77,7 +77,7 @@ void GlobalBaseDefCache::AddHeadToBucket(Index hash_index, GlobalBaseEntry *base
|
||||||
if (is_relation) {
|
if (is_relation) {
|
||||||
GlobalRelationEntry *entry = (GlobalRelationEntry *)base;
|
GlobalRelationEntry *entry = (GlobalRelationEntry *)base;
|
||||||
uint64 rel_size = GetRelEstimateSize(entry);
|
uint64 rel_size = GetRelEstimateSize(entry);
|
||||||
pg_atomic_fetch_add_u64(&m_base_space, rel_size);
|
pg_atomic_fetch_add_u64(&m_base_space, AllocSetContextUsedSpace(((AllocSet)entry->rel_mem_manager)));
|
||||||
m_db_entry->MemoryEstimateAdd(rel_size);
|
m_db_entry->MemoryEstimateAdd(rel_size);
|
||||||
} else {
|
} else {
|
||||||
GlobalPartitionEntry *entry = (GlobalPartitionEntry *)base;
|
GlobalPartitionEntry *entry = (GlobalPartitionEntry *)base;
|
||||||
|
|
|
||||||
|
|
@ -659,7 +659,27 @@ void GlobalSysDBCache::InitSysCacheRelIds()
|
||||||
*/
|
*/
|
||||||
void GlobalSysDBCache::RefreshHotStandby()
|
void GlobalSysDBCache::RefreshHotStandby()
|
||||||
{
|
{
|
||||||
|
if (!EnableGlobalSysCache()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
hot_standby = (t_thrd.postmaster_cxt.HaShmData->current_mode != STANDBY_MODE || XLogStandbyInfoActive());
|
hot_standby = (t_thrd.postmaster_cxt.HaShmData->current_mode != STANDBY_MODE || XLogStandbyInfoActive());
|
||||||
|
if (hot_standby || !m_is_inited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* clean all */
|
||||||
|
for (int hash_index = 0; hash_index < m_nbuckets; hash_index ++) {
|
||||||
|
PthreadRWlockRdlock(LOCAL_SYSDB_RESOWNER, &m_db_locks[hash_index]);
|
||||||
|
for (Dlelem * elt = DLGetTail(m_bucket_list.GetBucket(hash_index)); elt != NULL;) {
|
||||||
|
GlobalSysDBCacheEntry *entry = (GlobalSysDBCacheEntry *)DLE_VAL(elt);
|
||||||
|
elt = DLGetPred(elt);
|
||||||
|
entry->ResetDBCache<true>();
|
||||||
|
}
|
||||||
|
PthreadRWlockUnlock(LOCAL_SYSDB_RESOWNER, &m_db_locks[hash_index]);
|
||||||
|
}
|
||||||
|
if (m_global_shared_db_entry != NULL) {
|
||||||
|
m_global_shared_db_entry->ResetDBCache<true>();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalSysDBCache::Init(MemoryContext parent)
|
void GlobalSysDBCache::Init(MemoryContext parent)
|
||||||
|
|
@ -1272,6 +1292,15 @@ int ResizeHashBucket(int origin_nbucket, DynamicHashBucketStrategy strategy)
|
||||||
return cc_nbuckets;
|
return cc_nbuckets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NotifyGscRecoveryStarted()
|
||||||
|
{
|
||||||
|
if (!EnableGlobalSysCache()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_instance.global_sysdbcache.recovery_finished = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void NotifyGscRecoveryFinished()
|
void NotifyGscRecoveryFinished()
|
||||||
{
|
{
|
||||||
if (EnableGlobalSysCache()) {
|
if (EnableGlobalSysCache()) {
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ void GlobalSysTabCache::InvalidTuples(int cache_id, uint32 hash_value, bool rese
|
||||||
|
|
||||||
/* maybe upgrade from version before v5r2c00, the cacheid is out of order
|
/* maybe upgrade from version before v5r2c00, the cacheid is out of order
|
||||||
* whatever, we cache nothing except relmap, so just ignore the catcache invalmsg */
|
* whatever, we cache nothing except relmap, so just ignore the catcache invalmsg */
|
||||||
if (unlikely(!g_instance.global_sysdbcache.recovery_finished) && m_global_systupcaches[cache_id] == NULL) {
|
if (unlikely(!g_instance.global_sysdbcache.recovery_finished && m_global_systupcaches[cache_id] == NULL)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ Partition LocalPartDefCache::SearchPartitionFromGlobalCopy(Oid part_oid)
|
||||||
if (!g_instance.global_sysdbcache.hot_standby) {
|
if (!g_instance.global_sysdbcache.hot_standby) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
|
if (unlikely(!IsPrimaryRecoveryFinished())) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
uint32 hash_value = oid_hash((void *)&(part_oid), sizeof(Oid));
|
uint32 hash_value = oid_hash((void *)&(part_oid), sizeof(Oid));
|
||||||
|
|
@ -165,7 +165,7 @@ static bool IsPartOidStoreInGlobal(Oid part_oid)
|
||||||
if (!g_instance.global_sysdbcache.hot_standby) {
|
if (!g_instance.global_sysdbcache.hot_standby) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
|
if (unlikely(!IsPrimaryRecoveryFinished())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (g_instance.global_sysdbcache.StopInsertGSC()) {
|
if (g_instance.global_sysdbcache.StopInsertGSC()) {
|
||||||
|
|
|
||||||
|
|
@ -433,7 +433,7 @@ LocalCatCTup *LocalSysTupCache::SearchTupleFromGlobal(Datum *arguments, uint32 h
|
||||||
bool bypass_gsc = HistoricSnapshotActive() ||
|
bool bypass_gsc = HistoricSnapshotActive() ||
|
||||||
m_global_systupcache->enable_rls ||
|
m_global_systupcache->enable_rls ||
|
||||||
!g_instance.global_sysdbcache.hot_standby ||
|
!g_instance.global_sysdbcache.hot_standby ||
|
||||||
unlikely(!g_instance.global_sysdbcache.recovery_finished);
|
unlikely(!IsPrimaryRecoveryFinished());
|
||||||
if (invalid_entries.ExistTuple(hash_value) || bypass_gsc) {
|
if (invalid_entries.ExistTuple(hash_value) || bypass_gsc) {
|
||||||
global_ct = m_global_systupcache->SearchTupleFromFile(hash_value, arguments, true);
|
global_ct = m_global_systupcache->SearchTupleFromFile(hash_value, arguments, true);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -585,7 +585,7 @@ LocalCatCList *LocalSysTupCache::SearchListFromGlobal(int nkeys, Datum *argument
|
||||||
bool bypass_gsc = HistoricSnapshotActive() ||
|
bool bypass_gsc = HistoricSnapshotActive() ||
|
||||||
m_global_systupcache->enable_rls ||
|
m_global_systupcache->enable_rls ||
|
||||||
!g_instance.global_sysdbcache.hot_standby ||
|
!g_instance.global_sysdbcache.hot_standby ||
|
||||||
unlikely(!g_instance.global_sysdbcache.recovery_finished);
|
unlikely(!IsPrimaryRecoveryFinished());
|
||||||
GlobalCatCList *global_cl;
|
GlobalCatCList *global_cl;
|
||||||
if (invalid_entries.ExistList() || bypass_gsc) {
|
if (invalid_entries.ExistList() || bypass_gsc) {
|
||||||
global_cl = m_global_systupcache->SearchListFromFile(hash_value, nkeys, arguments, true);
|
global_cl = m_global_systupcache->SearchListFromFile(hash_value, nkeys, arguments, true);
|
||||||
|
|
@ -703,7 +703,7 @@ LocalCatCTup *LocalSysTupCache::SearchTupleFromGlobalForProcAllArgs(
|
||||||
bool bypass_gsc = HistoricSnapshotActive() ||
|
bool bypass_gsc = HistoricSnapshotActive() ||
|
||||||
m_global_systupcache->enable_rls ||
|
m_global_systupcache->enable_rls ||
|
||||||
!g_instance.global_sysdbcache.hot_standby ||
|
!g_instance.global_sysdbcache.hot_standby ||
|
||||||
unlikely(!g_instance.global_sysdbcache.recovery_finished);
|
unlikely(!IsPrimaryRecoveryFinished());
|
||||||
if (invalid_entries.ExistTuple(hash_value) || bypass_gsc) {
|
if (invalid_entries.ExistTuple(hash_value) || bypass_gsc) {
|
||||||
global_ct = m_global_systupcache->SearchTupleFromFileWithArgModes(hash_value, arguments, argModes, true);
|
global_ct = m_global_systupcache->SearchTupleFromFileWithArgModes(hash_value, arguments, argModes, true);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ Relation LocalTabDefCache::SearchRelationFromGlobalCopy(Oid rel_oid)
|
||||||
if (!g_instance.global_sysdbcache.hot_standby) {
|
if (!g_instance.global_sysdbcache.hot_standby) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
|
if (unlikely(!IsPrimaryRecoveryFinished())) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
uint32 hash_value = oid_hash((void *)&(rel_oid), sizeof(Oid));
|
uint32 hash_value = oid_hash((void *)&(rel_oid), sizeof(Oid));
|
||||||
|
|
@ -190,7 +190,7 @@ static bool IsRelOidStoreInGlobal(Oid rel_oid)
|
||||||
if (!g_instance.global_sysdbcache.hot_standby) {
|
if (!g_instance.global_sysdbcache.hot_standby) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
|
if (unlikely(!IsPrimaryRecoveryFinished())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (g_instance.global_sysdbcache.StopInsertGSC()) {
|
if (g_instance.global_sysdbcache.StopInsertGSC()) {
|
||||||
|
|
|
||||||
|
|
@ -1723,7 +1723,7 @@ char* get_relname_relid_extend(
|
||||||
extern bool StreamTopConsumerAmI();
|
extern bool StreamTopConsumerAmI();
|
||||||
|
|
||||||
/* same as get_relname_relid except we check for cache invalidation here */
|
/* same as get_relname_relid except we check for cache invalidation here */
|
||||||
Oid get_valid_relname_relid(const char* relnamespace, const char* relname)
|
Oid get_valid_relname_relid(const char* relnamespace, const char* relname, bool nsp_missing_ok)
|
||||||
{
|
{
|
||||||
Oid nspid = InvalidOid;
|
Oid nspid = InvalidOid;
|
||||||
Oid oldnspid = InvalidOid;
|
Oid oldnspid = InvalidOid;
|
||||||
|
|
@ -1747,7 +1747,10 @@ Oid get_valid_relname_relid(const char* relnamespace, const char* relname)
|
||||||
if (EnableLocalSysCache()) {
|
if (EnableLocalSysCache()) {
|
||||||
thrd_inval_count = t_thrd.lsc_cxt.lsc->inval_cxt.SIMCounter;
|
thrd_inval_count = t_thrd.lsc_cxt.lsc->inval_cxt.SIMCounter;
|
||||||
}
|
}
|
||||||
nspid = get_namespace_oid(relnamespace, false);
|
nspid = get_namespace_oid(relnamespace, nsp_missing_ok);
|
||||||
|
if (!OidIsValid(nspid)) {
|
||||||
|
return InvalidOid;
|
||||||
|
}
|
||||||
relid = get_relname_relid(relname, nspid);
|
relid = get_relname_relid(relname, nspid);
|
||||||
/*
|
/*
|
||||||
* In bootstrap processing mode, we don't bother with locking
|
* In bootstrap processing mode, we don't bother with locking
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ bool open_join_children = true;
|
||||||
bool will_shutdown = false;
|
bool will_shutdown = false;
|
||||||
|
|
||||||
/* hard-wired binary version number */
|
/* hard-wired binary version number */
|
||||||
const uint32 GRAND_VERSION_NUM = 92605;
|
const uint32 GRAND_VERSION_NUM = 92606;
|
||||||
|
|
||||||
const uint32 PREDPUSH_SAME_LEVEL_VERSION_NUM = 92522;
|
const uint32 PREDPUSH_SAME_LEVEL_VERSION_NUM = 92522;
|
||||||
const uint32 UPSERT_WHERE_VERSION_NUM = 92514;
|
const uint32 UPSERT_WHERE_VERSION_NUM = 92514;
|
||||||
|
|
@ -101,6 +101,7 @@ const uint32 PRIVS_DIRECTORY_VERSION_NUM = 92460;
|
||||||
const uint32 COMMENT_RECORD_PARAM_VERSION_NUM = 92484;
|
const uint32 COMMENT_RECORD_PARAM_VERSION_NUM = 92484;
|
||||||
const uint32 SCAN_BATCH_MODE_VERSION_NUM = 92568;
|
const uint32 SCAN_BATCH_MODE_VERSION_NUM = 92568;
|
||||||
const uint32 PUBLICATION_VERSION_NUM = 92580;
|
const uint32 PUBLICATION_VERSION_NUM = 92580;
|
||||||
|
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
|
||||||
|
|
||||||
/* Version number of the guc parameter backend_version added in V500R001C20 */
|
/* Version number of the guc parameter backend_version added in V500R001C20 */
|
||||||
const uint32 V5R1C20_BACKEND_VERSION_NUM = 92305;
|
const uint32 V5R1C20_BACKEND_VERSION_NUM = 92305;
|
||||||
|
|
|
||||||
|
|
@ -2712,8 +2712,8 @@ void PostgresInitializer::InitExtensionVariable()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check whether the extension has been created */
|
/* check whether the extension has been created */
|
||||||
const char* b_sql_plugin = "b_sql_plugin";
|
const char* dolphin = "dolphin";
|
||||||
u_sess->attr.attr_sql.b_sql_plugin = CheckIfExtensionExists(b_sql_plugin);
|
u_sess->attr.attr_sql.dolphin = CheckIfExtensionExists(dolphin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostgresInitializer::FinishInit()
|
void PostgresInitializer::FinishInit()
|
||||||
|
|
|
||||||
|
|
@ -10340,7 +10340,16 @@ check_sql_expr(const char *stmt, int location, int leaderlen)
|
||||||
|
|
||||||
oldCxt = MemoryContextSwitchTo(u_sess->plsql_cxt.curr_compile_context->compile_tmp_cxt);
|
oldCxt = MemoryContextSwitchTo(u_sess->plsql_cxt.curr_compile_context->compile_tmp_cxt);
|
||||||
u_sess->plsql_cxt.plpgsql_yylloc = plpgsql_yylloc;
|
u_sess->plsql_cxt.plpgsql_yylloc = plpgsql_yylloc;
|
||||||
(void) raw_parser(stmt);
|
RawParserHook parser_hook= raw_parser;
|
||||||
|
#ifndef ENABLE_MULTIPLE_NODES
|
||||||
|
if (u_sess->attr.attr_sql.dolphin) {
|
||||||
|
int id = GetCustomParserId();
|
||||||
|
if (id >= 0 && g_instance.raw_parser_hook[id] != NULL) {
|
||||||
|
parser_hook = (RawParserHook)g_instance.raw_parser_hook[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
(void)parser_hook(stmt, NULL);
|
||||||
MemoryContextSwitchTo(oldCxt);
|
MemoryContextSwitchTo(oldCxt);
|
||||||
|
|
||||||
/* Restore former ereport callback */
|
/* Restore former ereport callback */
|
||||||
|
|
|
||||||
|
|
@ -222,6 +222,7 @@ NON_EXEC_STATIC void PercentileMain()
|
||||||
g_instance.stat_cxt.force_process = false;
|
g_instance.stat_cxt.force_process = false;
|
||||||
sleep(SLEEP_INTERVAL);
|
sleep(SLEEP_INTERVAL);
|
||||||
}
|
}
|
||||||
|
elog(LOG, "instrumention percentile ended");
|
||||||
gs_thread_exit(0);
|
gs_thread_exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,11 @@ import logging
|
||||||
try:
|
try:
|
||||||
from .dao.gsql_execute import GSqlExecute
|
from .dao.gsql_execute import GSqlExecute
|
||||||
from .dao.execute_factory import ExecuteFactory
|
from .dao.execute_factory import ExecuteFactory
|
||||||
|
from .mcts import MCTS
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from dao.gsql_execute import GSqlExecute
|
from dao.gsql_execute import GSqlExecute
|
||||||
from dao.execute_factory import ExecuteFactory
|
from dao.execute_factory import ExecuteFactory
|
||||||
|
from mcts import MCTS
|
||||||
|
|
||||||
ENABLE_MULTI_NODE = False
|
ENABLE_MULTI_NODE = False
|
||||||
SAMPLE_NUM = 5
|
SAMPLE_NUM = 5
|
||||||
|
|
@ -192,7 +194,10 @@ class IndexAdvisor:
|
||||||
self.workload_used_index))
|
self.workload_used_index))
|
||||||
if DRIVER:
|
if DRIVER:
|
||||||
self.db.close_conn()
|
self.db.close_conn()
|
||||||
|
if MAX_INDEX_STORAGE:
|
||||||
|
opt_config = MCTS(self.workload_info[0], atomic_config_total, candidate_indexes,
|
||||||
|
MAX_INDEX_STORAGE, MAX_INDEX_NUM)
|
||||||
|
else:
|
||||||
opt_config = greedy_determine_opt_config(self.workload_info[0], atomic_config_total,
|
opt_config = greedy_determine_opt_config(self.workload_info[0], atomic_config_total,
|
||||||
candidate_indexes, self.index_cost_total[0])
|
candidate_indexes, self.index_cost_total[0])
|
||||||
self.retain_lower_cost_index(candidate_indexes)
|
self.retain_lower_cost_index(candidate_indexes)
|
||||||
|
|
@ -943,7 +948,7 @@ def check_parameter(args):
|
||||||
raise argparse.ArgumentTypeError("%s is an invalid positive int value" %
|
raise argparse.ArgumentTypeError("%s is an invalid positive int value" %
|
||||||
args.max_index_num)
|
args.max_index_num)
|
||||||
if args.max_index_storage is not None and args.max_index_storage <= 0:
|
if args.max_index_storage is not None and args.max_index_storage <= 0:
|
||||||
raise argparse.ArgumentTypeError("%s is an invalid positive int value" %
|
raise argparse.ArgumentTypeError("%s is an invalid positive float value" %
|
||||||
args.max_index_storage)
|
args.max_index_storage)
|
||||||
JSON_TYPE = args.json
|
JSON_TYPE = args.json
|
||||||
MAX_INDEX_NUM = args.max_index_num
|
MAX_INDEX_NUM = args.max_index_num
|
||||||
|
|
@ -971,7 +976,7 @@ def main(argv):
|
||||||
arg_parser.add_argument(
|
arg_parser.add_argument(
|
||||||
"--max_index_num", help="Maximum number of suggested indexes", type=int)
|
"--max_index_num", help="Maximum number of suggested indexes", type=int)
|
||||||
arg_parser.add_argument("--max_index_storage",
|
arg_parser.add_argument("--max_index_storage",
|
||||||
help="Maximum storage of suggested indexes/MB", type=int)
|
help="Maximum storage of suggested indexes/MB", type=float)
|
||||||
arg_parser.add_argument("--multi_iter_mode", action='store_true',
|
arg_parser.add_argument("--multi_iter_mode", action='store_true',
|
||||||
help="Whether to use multi-iteration algorithm", default=False)
|
help="Whether to use multi-iteration algorithm", default=False)
|
||||||
arg_parser.add_argument("--multi_node", action='store_true',
|
arg_parser.add_argument("--multi_node", action='store_true',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,397 @@
|
||||||
|
import sys
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
import copy
|
||||||
|
|
||||||
|
STORAGE_THRESHOLD = 0
|
||||||
|
AVAILABLE_CHOICES = None
|
||||||
|
ATOMIC_CHOICES = None
|
||||||
|
WORKLOAD_INFO = None
|
||||||
|
MAX_INDEX_NUM = 0
|
||||||
|
|
||||||
|
|
||||||
|
def is_same_index(index, compared_index):
|
||||||
|
return index.table == compared_index.table and \
|
||||||
|
index.columns == compared_index.columns and \
|
||||||
|
index.index_type == compared_index.index_type
|
||||||
|
|
||||||
|
|
||||||
|
def atomic_config_is_valid(atomic_config, config):
|
||||||
|
# if candidate indexes contains all atomic index of current config1, then record it
|
||||||
|
for atomic_index in atomic_config:
|
||||||
|
is_exist = False
|
||||||
|
for index in config:
|
||||||
|
if is_same_index(index, atomic_index):
|
||||||
|
index.storage = atomic_index.storage
|
||||||
|
is_exist = True
|
||||||
|
break
|
||||||
|
if not is_exist:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def find_subsets_num(choice):
|
||||||
|
atomic_subsets_num = []
|
||||||
|
for pos, atomic in enumerate(ATOMIC_CHOICES):
|
||||||
|
if not atomic or len(atomic) > len(choice):
|
||||||
|
continue
|
||||||
|
# find valid atomic index
|
||||||
|
if atomic_config_is_valid(atomic, choice):
|
||||||
|
atomic_subsets_num.append(pos)
|
||||||
|
# find the same atomic index as the candidate index
|
||||||
|
if len(atomic) == 1 and (is_same_index(choice[-1], atomic[0])):
|
||||||
|
choice[-1].atomic_pos = pos
|
||||||
|
return atomic_subsets_num
|
||||||
|
|
||||||
|
|
||||||
|
def find_best_benefit(choice):
|
||||||
|
atomic_subsets_num = find_subsets_num(choice)
|
||||||
|
total_benefit = 0
|
||||||
|
for ind, obj in enumerate(WORKLOAD_INFO):
|
||||||
|
# calculate the best benefit for the current sql
|
||||||
|
max_benefit = 0
|
||||||
|
for pos in atomic_subsets_num:
|
||||||
|
if (obj.cost_list[0] - obj.cost_list[pos]) > max_benefit:
|
||||||
|
max_benefit = obj.cost_list[0] - obj.cost_list[pos]
|
||||||
|
total_benefit += max_benefit
|
||||||
|
return total_benefit
|
||||||
|
|
||||||
|
|
||||||
|
def get_diff(available_choices, choices):
|
||||||
|
except_choices = copy.copy(available_choices)
|
||||||
|
for i in available_choices:
|
||||||
|
for j in choices:
|
||||||
|
if is_same_index(i, j):
|
||||||
|
except_choices.remove(i)
|
||||||
|
return except_choices
|
||||||
|
|
||||||
|
|
||||||
|
class State(object):
|
||||||
|
"""
|
||||||
|
The game state of the Monte Carlo tree search,
|
||||||
|
the state data recorded under a certain Node node,
|
||||||
|
including the current game score, the current number of game rounds,
|
||||||
|
and the execution record from the beginning to the current.
|
||||||
|
|
||||||
|
It is necessary to realize whether the current state has reached the end of the game state,
|
||||||
|
and support the operation of randomly fetching from the Action collection.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.current_storage = 0.0
|
||||||
|
self.current_benefit = 0.0
|
||||||
|
# record the sum of choices up to the current state
|
||||||
|
self.accumulation_choices = []
|
||||||
|
# record available choices of current state
|
||||||
|
self.available_choices = []
|
||||||
|
self.displayable_choices = []
|
||||||
|
|
||||||
|
def get_available_choices(self):
|
||||||
|
return self.available_choices
|
||||||
|
|
||||||
|
def set_available_choices(self, choices):
|
||||||
|
self.available_choices = choices
|
||||||
|
|
||||||
|
def get_current_storage(self):
|
||||||
|
return self.current_storage
|
||||||
|
|
||||||
|
def set_current_storage(self, value):
|
||||||
|
self.current_storage = value
|
||||||
|
|
||||||
|
def get_current_benefit(self):
|
||||||
|
return self.current_benefit
|
||||||
|
|
||||||
|
def set_current_benefit(self, value):
|
||||||
|
self.current_benefit = value
|
||||||
|
|
||||||
|
def get_accumulation_choices(self):
|
||||||
|
return self.accumulation_choices
|
||||||
|
|
||||||
|
def set_accumulation_choices(self, choices):
|
||||||
|
self.accumulation_choices = choices
|
||||||
|
|
||||||
|
def is_terminal(self):
|
||||||
|
# the current node is a leaf node
|
||||||
|
return len(self.accumulation_choices) == MAX_INDEX_NUM
|
||||||
|
|
||||||
|
def compute_benefit(self):
|
||||||
|
return self.current_benefit
|
||||||
|
|
||||||
|
def get_next_state_with_random_choice(self):
|
||||||
|
# ensure that the choices taken are not repeated
|
||||||
|
if not self.available_choices:
|
||||||
|
return None
|
||||||
|
random_choice = random.choice([choice for choice in self.available_choices])
|
||||||
|
self.available_choices.remove(random_choice)
|
||||||
|
choice = copy.copy(self.accumulation_choices)
|
||||||
|
choice.append(random_choice)
|
||||||
|
benefit = find_best_benefit(choice)
|
||||||
|
# if current choice not satisfy restrictions, then continue get next choice
|
||||||
|
if benefit <= self.current_benefit or \
|
||||||
|
self.current_storage + random_choice.storage > STORAGE_THRESHOLD:
|
||||||
|
return self.get_next_state_with_random_choice()
|
||||||
|
|
||||||
|
next_state = State()
|
||||||
|
# initialize the properties of the new state
|
||||||
|
next_state.set_accumulation_choices(choice)
|
||||||
|
next_state.set_current_benefit(benefit)
|
||||||
|
next_state.set_current_storage(self.current_storage + random_choice.storage)
|
||||||
|
next_state.set_available_choices(get_diff(AVAILABLE_CHOICES, choice))
|
||||||
|
return next_state
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
self.displayable_choices = ['{}: {}'.format(choice.table, choice.columns)
|
||||||
|
for choice in self.accumulation_choices]
|
||||||
|
return "reward: {}, storage :{}, choices: {}".format(
|
||||||
|
self.current_benefit, self.current_storage, self.displayable_choices)
|
||||||
|
|
||||||
|
|
||||||
|
class Node(object):
|
||||||
|
"""
|
||||||
|
The Node of the Monte Carlo tree search tree contains the parent node and
|
||||||
|
current point information,
|
||||||
|
which is used to calculate the traversal times and quality value of the UCB,
|
||||||
|
and the State of the Node selected by the game.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.visit_number = 0
|
||||||
|
self.quality = 0.0
|
||||||
|
|
||||||
|
self.parent = None
|
||||||
|
self.children = []
|
||||||
|
self.state = None
|
||||||
|
|
||||||
|
def get_parent(self):
|
||||||
|
return self.parent
|
||||||
|
|
||||||
|
def set_parent(self, parent):
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
|
def get_children(self):
|
||||||
|
return self.children
|
||||||
|
|
||||||
|
def expand_child(self, node):
|
||||||
|
node.set_parent(self)
|
||||||
|
self.children.append(node)
|
||||||
|
|
||||||
|
def set_state(self, state):
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
def get_state(self):
|
||||||
|
return self.state
|
||||||
|
|
||||||
|
def get_visit_number(self):
|
||||||
|
return self.visit_number
|
||||||
|
|
||||||
|
def set_visit_number(self, number):
|
||||||
|
self.visit_number = number
|
||||||
|
|
||||||
|
def update_visit_number(self):
|
||||||
|
self.visit_number += 1
|
||||||
|
|
||||||
|
def get_quality_value(self):
|
||||||
|
return self.quality
|
||||||
|
|
||||||
|
def set_quality_value(self, value):
|
||||||
|
self.quality = value
|
||||||
|
|
||||||
|
def update_quality_value(self, reward):
|
||||||
|
self.quality += reward
|
||||||
|
|
||||||
|
def is_all_expand(self):
|
||||||
|
return len(self.children) == \
|
||||||
|
len(AVAILABLE_CHOICES) - len(self.get_state().get_accumulation_choices())
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Node: {}, Q/N: {}/{}, State: {}".format(
|
||||||
|
hash(self), self.quality, self.visit_number, self.state)
|
||||||
|
|
||||||
|
|
||||||
|
def tree_policy(node):
|
||||||
|
"""
|
||||||
|
In the Selection and Expansion stages of Monte Carlo tree search,
|
||||||
|
the node that needs to be searched (such as the root node) is passed in,
|
||||||
|
and the best node that needs to be expanded is returned
|
||||||
|
according to the exploration/exploitation algorithm.
|
||||||
|
Note that if the node is a leaf node, it will be returned directly.
|
||||||
|
|
||||||
|
The basic strategy is to first find the child nodes that have not been selected at present,
|
||||||
|
and select them randomly if there are more than one. If both are selected,
|
||||||
|
find the one with the largest UCB value that has weighed exploration/exploitation,
|
||||||
|
and randomly select if the UCB values are equal.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# check if the current node is leaf node
|
||||||
|
while node and not node.get_state().is_terminal():
|
||||||
|
|
||||||
|
if node.is_all_expand():
|
||||||
|
node = best_child(node, True)
|
||||||
|
else:
|
||||||
|
# return the new sub node
|
||||||
|
sub_node = expand(node)
|
||||||
|
# when there is no node that satisfies the condition in the remaining nodes,
|
||||||
|
# this state is empty
|
||||||
|
if sub_node.get_state():
|
||||||
|
return sub_node
|
||||||
|
|
||||||
|
# return the leaf node
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
def default_policy(node):
|
||||||
|
"""
|
||||||
|
In the Simulation stage of Monte Carlo tree search, input a node that needs to be expanded,
|
||||||
|
create a new node after random operation, and return the reward of the new node.
|
||||||
|
Note that the input node should not be a child node,
|
||||||
|
and there are unexecuted Actions that can be expendable.
|
||||||
|
|
||||||
|
The basic strategy is to choose the Action at random.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# get the state of the game
|
||||||
|
current_state = copy.deepcopy(node.get_state())
|
||||||
|
|
||||||
|
# run until the game over
|
||||||
|
while not current_state.is_terminal():
|
||||||
|
# pick one random action to play and get next state
|
||||||
|
next_state = current_state.get_next_state_with_random_choice()
|
||||||
|
if not next_state:
|
||||||
|
break
|
||||||
|
current_state = next_state
|
||||||
|
|
||||||
|
final_state_reward = current_state.compute_benefit()
|
||||||
|
return final_state_reward
|
||||||
|
|
||||||
|
|
||||||
|
def expand(node):
|
||||||
|
"""
|
||||||
|
Enter a node, expand a new node on the node, use the random method to execute the Action,
|
||||||
|
and return the new node. Note that it is necessary to ensure that the newly
|
||||||
|
added nodes are different from other node Action
|
||||||
|
"""
|
||||||
|
|
||||||
|
new_state = node.get_state().get_next_state_with_random_choice()
|
||||||
|
sub_node = Node()
|
||||||
|
sub_node.set_state(new_state)
|
||||||
|
node.expand_child(sub_node)
|
||||||
|
|
||||||
|
return sub_node
|
||||||
|
|
||||||
|
|
||||||
|
def best_child(node, is_exploration):
|
||||||
|
"""
|
||||||
|
Using the UCB algorithm,
|
||||||
|
select the child node with the highest score after weighing the exploration and exploitation.
|
||||||
|
Note that if it is the prediction stage,
|
||||||
|
the current Q-value score with the highest score is directly selected.
|
||||||
|
"""
|
||||||
|
|
||||||
|
best_score = -sys.maxsize
|
||||||
|
best_sub_node = None
|
||||||
|
|
||||||
|
# travel all sub nodes to find the best one
|
||||||
|
for sub_node in node.get_children():
|
||||||
|
# The children nodes of the node contains the children node whose state is empty,
|
||||||
|
# this kind of node comes from the node that does not meet the conditions.
|
||||||
|
if not sub_node.get_state():
|
||||||
|
continue
|
||||||
|
# ignore exploration for inference
|
||||||
|
if is_exploration:
|
||||||
|
C = 1 / math.sqrt(2.0)
|
||||||
|
else:
|
||||||
|
C = 0.0
|
||||||
|
|
||||||
|
# UCB = quality / times + C * sqrt(2 * ln(total_times) / times)
|
||||||
|
left = sub_node.get_quality_value() / sub_node.get_visit_number()
|
||||||
|
right = 2.0 * math.log(node.get_visit_number()) / sub_node.get_visit_number()
|
||||||
|
score = left + C * math.sqrt(right)
|
||||||
|
# get the maximum score, while filtering nodes that do not meet the space constraints and
|
||||||
|
# nodes that have no revenue
|
||||||
|
if score > best_score \
|
||||||
|
and sub_node.get_state().get_current_storage() <= STORAGE_THRESHOLD \
|
||||||
|
and sub_node.get_state().get_current_benefit() > 0:
|
||||||
|
best_sub_node = sub_node
|
||||||
|
best_score = score
|
||||||
|
|
||||||
|
return best_sub_node
|
||||||
|
|
||||||
|
|
||||||
|
def backpropagate(node, reward):
|
||||||
|
"""
|
||||||
|
In the Backpropagation stage of Monte Carlo tree search,
|
||||||
|
input the node that needs to be expended and the reward of the newly executed Action,
|
||||||
|
feed it back to the expend node and all upstream nodes,
|
||||||
|
and update the corresponding data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# update util the root node
|
||||||
|
while node is not None:
|
||||||
|
# update the visit number
|
||||||
|
node.update_visit_number()
|
||||||
|
|
||||||
|
# update the quality value
|
||||||
|
node.update_quality_value(reward)
|
||||||
|
|
||||||
|
# change the node to the parent node
|
||||||
|
node = node.parent
|
||||||
|
|
||||||
|
|
||||||
|
def monte_carlo_tree_search(node):
|
||||||
|
"""
|
||||||
|
Implement the Monte Carlo tree search algorithm, pass in a root node,
|
||||||
|
expand new nodes and update data according to the
|
||||||
|
tree structure that has been explored before in a limited time,
|
||||||
|
and then return as long as the child node with the highest exploitation.
|
||||||
|
|
||||||
|
When making predictions,
|
||||||
|
you only need to select the node with the largest exploitation according to the Q value,
|
||||||
|
and find the next optimal node.
|
||||||
|
"""
|
||||||
|
|
||||||
|
computation_budget = len(AVAILABLE_CHOICES) * 3
|
||||||
|
|
||||||
|
# run as much as possible under the computation budget
|
||||||
|
for i in range(computation_budget):
|
||||||
|
# 1. find the best node to expand
|
||||||
|
expand_node = tree_policy(node)
|
||||||
|
if not expand_node:
|
||||||
|
# when it is None, it means that all nodes are added but no nodes meet the space limit
|
||||||
|
break
|
||||||
|
# 2. random get next action and get reward
|
||||||
|
reward = default_policy(expand_node)
|
||||||
|
|
||||||
|
# 3. update all passing nodes with reward
|
||||||
|
backpropagate(expand_node, reward)
|
||||||
|
|
||||||
|
# get the best next node
|
||||||
|
best_next_node = best_child(node, False)
|
||||||
|
|
||||||
|
return best_next_node
|
||||||
|
|
||||||
|
|
||||||
|
def MCTS(workload_info, atomic_choices, available_choices, storage_threshold, max_index_num):
|
||||||
|
global ATOMIC_CHOICES, STORAGE_THRESHOLD, WORKLOAD_INFO, AVAILABLE_CHOICES, MAX_INDEX_NUM
|
||||||
|
WORKLOAD_INFO = workload_info
|
||||||
|
AVAILABLE_CHOICES = available_choices
|
||||||
|
ATOMIC_CHOICES = atomic_choices
|
||||||
|
STORAGE_THRESHOLD = storage_threshold
|
||||||
|
MAX_INDEX_NUM = max_index_num if max_index_num else len(available_choices)
|
||||||
|
|
||||||
|
# create the initialized state and initialized node
|
||||||
|
init_state = State()
|
||||||
|
choices = copy.copy(available_choices)
|
||||||
|
init_state.set_available_choices(choices)
|
||||||
|
init_node = Node()
|
||||||
|
init_node.set_state(init_state)
|
||||||
|
current_node = init_node
|
||||||
|
|
||||||
|
opt_config = []
|
||||||
|
# set the rounds to play
|
||||||
|
for i in range(len(AVAILABLE_CHOICES)):
|
||||||
|
if current_node:
|
||||||
|
current_node = monte_carlo_tree_search(current_node)
|
||||||
|
if current_node:
|
||||||
|
opt_config = current_node.state.accumulation_choices
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return opt_config
|
||||||
|
|
@ -27,6 +27,7 @@ from collections.abc import Iterable
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
import index_advisor_workload as iaw
|
import index_advisor_workload as iaw
|
||||||
|
import mcts
|
||||||
|
|
||||||
|
|
||||||
def hash_any(obj):
|
def hash_any(obj):
|
||||||
|
|
@ -227,6 +228,32 @@ select * from student_range_part1 where credit=1;
|
||||||
|
|
||||||
class IndexAdvisorTester(unittest.TestCase):
|
class IndexAdvisorTester(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_mcts(self):
|
||||||
|
storage_threshold = 12
|
||||||
|
index1 = iaw.IndexItem('public.a', 'col1', index_type='global')
|
||||||
|
index2 = iaw.IndexItem('public.b', 'col1', index_type='global')
|
||||||
|
index3 = iaw.IndexItem('public.c', 'col1', index_type='global')
|
||||||
|
index4 = iaw.IndexItem('public.d', 'col1', index_type='global')
|
||||||
|
|
||||||
|
atomic_index1 = iaw.IndexItem('public.a', 'col1', index_type='global')
|
||||||
|
atomic_index2 = iaw.IndexItem('public.b', 'col1', index_type='global')
|
||||||
|
atomic_index3 = iaw.IndexItem('public.c', 'col1', index_type='global')
|
||||||
|
atomic_index4 = iaw.IndexItem('public.d', 'col1', index_type='global')
|
||||||
|
|
||||||
|
atomic_index1.storage = 10
|
||||||
|
atomic_index2.storage = 4
|
||||||
|
atomic_index3.storage = 7
|
||||||
|
available_choices = [index1, index2, index3, index4]
|
||||||
|
atomic_choices = [[], [atomic_index2], [atomic_index1], [atomic_index3],
|
||||||
|
[atomic_index2, atomic_index3], [atomic_index4]]
|
||||||
|
query = iaw.QueryItem('select * from gia_01', 1)
|
||||||
|
query.cost_list = [10, 7, 5, 9, 4, 11]
|
||||||
|
workload_info = [query]
|
||||||
|
|
||||||
|
results = mcts.MCTS(workload_info, atomic_choices, available_choices, storage_threshold, 2)
|
||||||
|
self.assertLessEqual([index1.atomic_pos, index2.atomic_pos, index3.atomic_pos], [2, 1, 3])
|
||||||
|
self.assertSetEqual({results[0].table, results[1].table}, {'public.b', 'public.c'})
|
||||||
|
|
||||||
def test_get_indexable_columns(self):
|
def test_get_indexable_columns(self):
|
||||||
tables = 'table1 table2 table2 table3 table3 table3'.split()
|
tables = 'table1 table2 table2 table3 table3 table3'.split()
|
||||||
columns = 'col1,col2 col2 col3 col1,col2 col2,col3 col2,col5'.split()
|
columns = 'col1,col2 col2 col3 col1,col2 col2,col3 col2,col5'.split()
|
||||||
|
|
|
||||||
|
|
@ -90,8 +90,11 @@ static void DropExtensionInListIsSupported(List* objname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Enable DROP operation of the above objects during inplace upgrade or support_extended_features is true */
|
||||||
|
if (!u_sess->attr.attr_common.IsInplaceUpgrade && !g_instance.attr.attr_common.support_extended_features) {
|
||||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXTENSION is not yet supported.")));
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXTENSION is not yet supported.")));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @Description: drop one or more objects.
|
* @Description: drop one or more objects.
|
||||||
|
|
|
||||||
|
|
@ -1175,7 +1175,7 @@ void CreateExtension(CreateExtensionStmt* stmt)
|
||||||
FEATURE_NOT_PUBLIC_ERROR("EXTENSION is not yet supported.");
|
FEATURE_NOT_PUBLIC_ERROR("EXTENSION is not yet supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pg_strcasecmp(stmt->extname, "b_sql_plugin") == 0 && !DB_IS_CMPT(B_FORMAT)) {
|
if (pg_strcasecmp(stmt->extname, "dolphin") == 0 && !DB_IS_CMPT(B_FORMAT)) {
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errmsg("please create extension \"%s\" with B type DBCOMPATIBILITY", stmt->extname)));
|
(errmsg("please create extension \"%s\" with B type DBCOMPATIBILITY", stmt->extname)));
|
||||||
}
|
}
|
||||||
|
|
@ -1418,8 +1418,8 @@ void CreateExtension(CreateExtensionStmt* stmt)
|
||||||
|
|
||||||
u_sess->exec_cxt.extension_is_valid = true;
|
u_sess->exec_cxt.extension_is_valid = true;
|
||||||
|
|
||||||
if (pg_strcasecmp(stmt->extname, "b_sql_plugin") == 0) {
|
if (pg_strcasecmp(stmt->extname, "dolphin") == 0) {
|
||||||
u_sess->attr.attr_sql.b_sql_plugin = true;
|
u_sess->attr.attr_sql.dolphin = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
#include "utils/array.h"
|
#include "utils/array.h"
|
||||||
#include "utils/acl.h"
|
#include "utils/acl.h"
|
||||||
|
|
||||||
static void ConnectPublisher(char *conninfo, char* slotname);
|
static bool ConnectPublisher(char* conninfo, char* slotname);
|
||||||
static void CreateSlotInPublisher(char *slotname);
|
static void CreateSlotInPublisher(char *slotname);
|
||||||
static void ValidateReplicationSlot(char *slotname, List *publications);
|
static void ValidateReplicationSlot(char *slotname, List *publications);
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@ static void ValidateReplicationSlot(char *slotname, List *publications);
|
||||||
* accommodate that.
|
* accommodate that.
|
||||||
*/
|
*/
|
||||||
static void parse_subscription_options(const List *options, char **conninfo, List **publications, bool *enabled_given,
|
static void parse_subscription_options(const List *options, char **conninfo, List **publications, bool *enabled_given,
|
||||||
bool *enabled, bool *slot_name_given, char **slot_name, char **synchronous_commit)
|
bool *enabled, bool *slot_name_given, char **slot_name, char **synchronous_commit, bool *binary_given, bool *binary)
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
|
|
@ -76,6 +76,10 @@ static void parse_subscription_options(const List *options, char **conninfo, Lis
|
||||||
if (synchronous_commit) {
|
if (synchronous_commit) {
|
||||||
*synchronous_commit = NULL;
|
*synchronous_commit = NULL;
|
||||||
}
|
}
|
||||||
|
if (binary) {
|
||||||
|
*binary_given = false;
|
||||||
|
*binary = false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Parse options */
|
/* Parse options */
|
||||||
foreach (lc, options) {
|
foreach (lc, options) {
|
||||||
|
|
@ -124,6 +128,15 @@ static void parse_subscription_options(const List *options, char **conninfo, Lis
|
||||||
/* Test if the given value is valid for synchronous_commit GUC. */
|
/* Test if the given value is valid for synchronous_commit GUC. */
|
||||||
(void)set_config_option("synchronous_commit", *synchronous_commit, PGC_BACKEND, PGC_S_TEST, GUC_ACTION_SET,
|
(void)set_config_option("synchronous_commit", *synchronous_commit, PGC_BACKEND, PGC_S_TEST, GUC_ACTION_SET,
|
||||||
false, 0, false);
|
false, 0, false);
|
||||||
|
} else if (strcmp(defel->defname, "binary") == 0 && binary) {
|
||||||
|
if (*binary_given) {
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("conflicting or redundant options")));
|
||||||
|
}
|
||||||
|
|
||||||
|
*binary_given = true;
|
||||||
|
*binary = defGetBoolean(defel);
|
||||||
} else {
|
} else {
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR), errmsg("unrecognized subscription parameter: %s", defel->defname)));
|
(errcode(ERRCODE_SYNTAX_ERROR), errmsg("unrecognized subscription parameter: %s", defel->defname)));
|
||||||
|
|
@ -197,26 +210,82 @@ static Datum publicationListToArray(List *publist)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* connect publisher and create slot.
|
* Parse the original connection string which is encrypted, poll all hosts and ports,
|
||||||
* the input conninfo should be encrypt, we will decrypt password inside
|
* and try to connect to the publisher.
|
||||||
|
* When checkRemoteMode is true, the remotemode must be normal or primary.
|
||||||
|
* Return true to indicate successful connection.
|
||||||
*/
|
*/
|
||||||
static void ConnectPublisher(char *conninfo, char *slotname)
|
bool AttemptConnectPublisher(const char *conninfoOriginal, char* slotname, bool checkRemoteMode)
|
||||||
|
{
|
||||||
|
size_t conninfoLen = strlen(conninfoOriginal) + 1;
|
||||||
|
|
||||||
|
char* conninfo = NULL;
|
||||||
|
StringInfoData conninfoWithoutHostport;
|
||||||
|
initStringInfo(&conninfoWithoutHostport);
|
||||||
|
HostPort* hostPortList[MAX_REPLNODE_NUM] = {NULL};
|
||||||
|
ParseConninfo(conninfoOriginal, &conninfoWithoutHostport, hostPortList);
|
||||||
|
if (hostPortList[0] == NULL) {
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg(
|
||||||
|
"invalid connection string syntax, missing host and port")));
|
||||||
|
}
|
||||||
|
bool connectSuccess = false;
|
||||||
|
conninfo = (char*)palloc(conninfoLen * sizeof(char));
|
||||||
|
for (int i = 0; i < MAX_REPLNODE_NUM; ++i) {
|
||||||
|
if (hostPortList[i] == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int ret = snprintf_s(conninfo, conninfoLen, conninfoLen - 1,
|
||||||
|
"%s host=%s port=%s", conninfoWithoutHostport.data,
|
||||||
|
hostPortList[i]->host, hostPortList[i]->port);
|
||||||
|
securec_check_ss(ret, "\0", "\0");
|
||||||
|
|
||||||
|
connectSuccess = ConnectPublisher(conninfo, slotname);
|
||||||
|
if (!connectSuccess) {
|
||||||
|
/* try next host */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!checkRemoteMode) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ServerMode publisherServerMde = IdentifyRemoteMode();
|
||||||
|
if (publisherServerMde == NORMAL_MODE || publisherServerMde == PRIMARY_MODE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* it's a standby, try next host */
|
||||||
|
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
|
||||||
|
connectSuccess = false;
|
||||||
|
}
|
||||||
|
pfree_ext(conninfo);
|
||||||
|
|
||||||
|
/* clean up */
|
||||||
|
FreeStringInfo(&conninfoWithoutHostport);
|
||||||
|
for (int i = 0; i < MAX_REPLNODE_NUM; ++i) {
|
||||||
|
if (hostPortList[i] == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pfree_ext(hostPortList[i]->host);
|
||||||
|
pfree_ext(hostPortList[i]->port);
|
||||||
|
pfree_ext(hostPortList[i]);
|
||||||
|
}
|
||||||
|
return connectSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* connect to publisher with conninfo
|
||||||
|
*/
|
||||||
|
static bool ConnectPublisher(char* conninfo, char* slotname)
|
||||||
{
|
{
|
||||||
/* Try to connect to the publisher. */
|
/* Try to connect to the publisher. */
|
||||||
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
|
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
|
||||||
SpinLockAcquire(&walrcv->mutex);
|
SpinLockAcquire(&walrcv->mutex);
|
||||||
walrcv->conn_target = REPCONNTARGET_PUBLICATION;
|
walrcv->conn_target = REPCONNTARGET_PUBLICATION;
|
||||||
SpinLockRelease(&walrcv->mutex);
|
SpinLockRelease(&walrcv->mutex);
|
||||||
|
char* decryptConninfo = EncryptOrDecryptConninfo(conninfo, 'D');
|
||||||
char *decryptConninfo = DecryptConninfo(conninfo);
|
|
||||||
bool connectSuccess = (WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_connect(decryptConninfo, NULL, slotname, -1);
|
bool connectSuccess = (WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_connect(decryptConninfo, NULL, slotname, -1);
|
||||||
int rc = memset_s(decryptConninfo, strlen(decryptConninfo), 0, strlen(decryptConninfo));
|
int rc = memset_s(decryptConninfo, strlen(decryptConninfo), 0, strlen(decryptConninfo));
|
||||||
securec_check(rc, "", "");
|
securec_check(rc, "", "");
|
||||||
pfree_ext(decryptConninfo);
|
pfree_ext(decryptConninfo);
|
||||||
|
return connectSuccess;
|
||||||
if (!connectSuccess) {
|
|
||||||
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("could not connect to the publisher")));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -293,9 +362,10 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
||||||
bool enabled_given = false;
|
bool enabled_given = false;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
char *synchronous_commit;
|
char *synchronous_commit;
|
||||||
char *conninfo;
|
|
||||||
char *slotname;
|
char *slotname;
|
||||||
bool slotname_given;
|
bool slotname_given;
|
||||||
|
bool binary;
|
||||||
|
bool binary_given;
|
||||||
char originname[NAMEDATALEN];
|
char originname[NAMEDATALEN];
|
||||||
List *publications;
|
List *publications;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
@ -305,7 +375,7 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
||||||
* Connection and publication should not be specified here.
|
* Connection and publication should not be specified here.
|
||||||
*/
|
*/
|
||||||
parse_subscription_options(stmt->options, NULL, NULL, &enabled_given, &enabled, &slotname_given, &slotname,
|
parse_subscription_options(stmt->options, NULL, NULL, &enabled_given, &enabled, &slotname_given, &slotname,
|
||||||
&synchronous_commit);
|
&synchronous_commit, &binary_given, &binary);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since creating a replication slot is not transactional, rolling back
|
* Since creating a replication slot is not transactional, rolling back
|
||||||
|
|
@ -333,11 +403,10 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
||||||
synchronous_commit = "off";
|
synchronous_commit = "off";
|
||||||
}
|
}
|
||||||
|
|
||||||
conninfo = stmt->conninfo;
|
|
||||||
publications = stmt->publication;
|
publications = stmt->publication;
|
||||||
|
|
||||||
/* Check the connection info string. */
|
/* Check the connection info string. */
|
||||||
libpqrcv_check_conninfo(conninfo);
|
libpqrcv_check_conninfo(stmt->conninfo);
|
||||||
|
|
||||||
/* Everything ok, form a new tuple. */
|
/* Everything ok, form a new tuple. */
|
||||||
rc = memset_s(values, sizeof(values), 0, sizeof(values));
|
rc = memset_s(values, sizeof(values), 0, sizeof(values));
|
||||||
|
|
@ -349,18 +418,12 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
||||||
values[Anum_pg_subscription_subname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->subname));
|
values[Anum_pg_subscription_subname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->subname));
|
||||||
values[Anum_pg_subscription_subowner - 1] = ObjectIdGetDatum(owner);
|
values[Anum_pg_subscription_subowner - 1] = ObjectIdGetDatum(owner);
|
||||||
values[Anum_pg_subscription_subenabled - 1] = BoolGetDatum(enabled);
|
values[Anum_pg_subscription_subenabled - 1] = BoolGetDatum(enabled);
|
||||||
|
values[Anum_pg_subscription_subbinary - 1] = BoolGetDatum(binary);
|
||||||
|
|
||||||
/* encrypt conninfo */
|
/* encrypt conninfo */
|
||||||
List *conninfoList = ConninfoToDefList(stmt->conninfo);
|
char *encryptConninfo = EncryptOrDecryptConninfo(stmt->conninfo, 'E');
|
||||||
/* Sensitive options for subscription, will be encrypted when saved to catalog. */
|
|
||||||
const char* sensitiveOptionsArray[] = {"password"};
|
|
||||||
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
|
|
||||||
EncryptGenericOptions(conninfoList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
|
||||||
char *encryptConninfo = DefListToString(conninfoList);
|
|
||||||
|
|
||||||
values[Anum_pg_subscription_subconninfo - 1] = CStringGetTextDatum(encryptConninfo);
|
values[Anum_pg_subscription_subconninfo - 1] = CStringGetTextDatum(encryptConninfo);
|
||||||
|
|
||||||
pfree_ext(conninfoList);
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
if (!slotname_given) {
|
if (!slotname_given) {
|
||||||
slotname = stmt->subname;
|
slotname = stmt->subname;
|
||||||
|
|
@ -396,11 +459,14 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
||||||
*/
|
*/
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
Assert(slotname);
|
Assert(slotname);
|
||||||
ConnectPublisher(encryptConninfo, slotname);
|
|
||||||
|
if (!AttemptConnectPublisher(encryptConninfo, slotname, true)) {
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("Failed to connect to publisher.")));
|
||||||
|
}
|
||||||
|
|
||||||
CreateSlotInPublisher(slotname);
|
CreateSlotInPublisher(slotname);
|
||||||
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
|
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
pfree_ext(encryptConninfo);
|
pfree_ext(encryptConninfo);
|
||||||
heap_close(rel, RowExclusiveLock);
|
heap_close(rel, RowExclusiveLock);
|
||||||
rc = memset_s(stmt->conninfo, strlen(stmt->conninfo), 0, strlen(stmt->conninfo));
|
rc = memset_s(stmt->conninfo, strlen(stmt->conninfo), 0, strlen(stmt->conninfo));
|
||||||
|
|
@ -439,6 +505,8 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
||||||
Oid subid;
|
Oid subid;
|
||||||
bool enabled_given = false;
|
bool enabled_given = false;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
bool binary_given;
|
||||||
|
bool binary;
|
||||||
char *synchronous_commit;
|
char *synchronous_commit;
|
||||||
char *conninfo;
|
char *conninfo;
|
||||||
char *slot_name;
|
char *slot_name;
|
||||||
|
|
@ -473,7 +541,7 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
||||||
|
|
||||||
/* Parse options. */
|
/* Parse options. */
|
||||||
parse_subscription_options(stmt->options, &conninfo, &publications, &enabled_given, &enabled, &slotname_given,
|
parse_subscription_options(stmt->options, &conninfo, &publications, &enabled_given, &enabled, &slotname_given,
|
||||||
&slot_name, &synchronous_commit);
|
&slot_name, &synchronous_commit, &binary_given, &binary);
|
||||||
|
|
||||||
/* Form a new tuple. */
|
/* Form a new tuple. */
|
||||||
rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls));
|
rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls));
|
||||||
|
|
@ -490,23 +558,15 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
||||||
if (conninfo) {
|
if (conninfo) {
|
||||||
/* Check the connection info string. */
|
/* Check the connection info string. */
|
||||||
libpqrcv_check_conninfo(conninfo);
|
libpqrcv_check_conninfo(conninfo);
|
||||||
|
encryptConninfo = EncryptOrDecryptConninfo(conninfo, 'E');
|
||||||
/* encrypt conninfo */
|
rc = memset_s(conninfo, strlen(conninfo), 0, strlen(conninfo));
|
||||||
List *conninfoList = ConninfoToDefList(conninfo);
|
securec_check(rc, "\0", "\0");
|
||||||
/* Sensitive options for subscription, will be encrypted when saved to catalog. */
|
|
||||||
const char* sensitiveOptionsArray[] = {"password"};
|
|
||||||
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
|
|
||||||
EncryptGenericOptions(conninfoList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
|
||||||
encryptConninfo = DefListToString(conninfoList);
|
|
||||||
needFreeConninfo = true;
|
|
||||||
|
|
||||||
values[Anum_pg_subscription_subconninfo - 1] = CStringGetTextDatum(encryptConninfo);
|
values[Anum_pg_subscription_subconninfo - 1] = CStringGetTextDatum(encryptConninfo);
|
||||||
replaces[Anum_pg_subscription_subconninfo - 1] = true;
|
replaces[Anum_pg_subscription_subconninfo - 1] = true;
|
||||||
|
needFreeConninfo = true;
|
||||||
|
|
||||||
pfree_ext(conninfoList);
|
/* need to check whether new conninfo can be used to connect to new publisher */
|
||||||
|
|
||||||
if (sub->enabled || (enabled_given && enabled)) {
|
if (sub->enabled || (enabled_given && enabled)) {
|
||||||
/* we need to check whether new conninfo can be used to connect to new publisher */
|
|
||||||
checkConn = true;
|
checkConn = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -548,6 +608,10 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
||||||
values[Anum_pg_subscription_subsynccommit - 1] = CStringGetTextDatum(synchronous_commit);
|
values[Anum_pg_subscription_subsynccommit - 1] = CStringGetTextDatum(synchronous_commit);
|
||||||
replaces[Anum_pg_subscription_subsynccommit - 1] = true;
|
replaces[Anum_pg_subscription_subsynccommit - 1] = true;
|
||||||
}
|
}
|
||||||
|
if (binary_given) {
|
||||||
|
values[Anum_pg_subscription_subbinary - 1] = BoolGetDatum(binary);
|
||||||
|
replaces[Anum_pg_subscription_subbinary - 1] = true;
|
||||||
|
}
|
||||||
if (publications != NIL) {
|
if (publications != NIL) {
|
||||||
values[Anum_pg_subscription_subpublications - 1] = publicationListToArray(publications);
|
values[Anum_pg_subscription_subpublications - 1] = publicationListToArray(publications);
|
||||||
replaces[Anum_pg_subscription_subpublications - 1] = true;
|
replaces[Anum_pg_subscription_subpublications - 1] = true;
|
||||||
|
|
@ -570,16 +634,18 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
||||||
if (sub->enabled && !enabled) {
|
if (sub->enabled && !enabled) {
|
||||||
ereport(ERROR, (errmsg("If you want to deactivate this subscription, use DROP SUBSCRIPTION.")));
|
ereport(ERROR, (errmsg("If you want to deactivate this subscription, use DROP SUBSCRIPTION.")));
|
||||||
}
|
}
|
||||||
/* enable subscription */
|
/* enabling subscription, but slot hasn't been created,
|
||||||
if (!sub->enabled && enabled) {
|
* then mark createSlot to true.
|
||||||
/* if slot hasn't been created, then create it */
|
*/
|
||||||
if (!sub->slotname || !*(sub->slotname)) {
|
if (!sub->enabled && enabled && (!sub->slotname || !*(sub->slotname))) {
|
||||||
createSlot = true;
|
createSlot = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (checkConn || createSlot || validateSlot) {
|
if (checkConn || createSlot || validateSlot) {
|
||||||
ConnectPublisher(encryptConninfo, finalSlotName);
|
if (!AttemptConnectPublisher(encryptConninfo, finalSlotName, true)) {
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg(
|
||||||
|
checkConn ? "The new conninfo cannot connect to new publisher." : "Failed to connect to publisher.")));
|
||||||
|
}
|
||||||
|
|
||||||
if (createSlot) {
|
if (createSlot) {
|
||||||
CreateSlotInPublisher(finalSlotName);
|
CreateSlotInPublisher(finalSlotName);
|
||||||
|
|
@ -597,12 +663,6 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
||||||
if (needFreeConninfo) {
|
if (needFreeConninfo) {
|
||||||
pfree_ext(encryptConninfo);
|
pfree_ext(encryptConninfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conninfo) {
|
|
||||||
rc = memset_s(conninfo, strlen(conninfo), 0, strlen(conninfo));
|
|
||||||
securec_check(rc, "", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
return myself;
|
return myself;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -753,7 +813,11 @@ void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
|
||||||
initStringInfo(&cmd);
|
initStringInfo(&cmd);
|
||||||
appendStringInfo(&cmd, "DROP_REPLICATION_SLOT %s", quote_identifier(slotname));
|
appendStringInfo(&cmd, "DROP_REPLICATION_SLOT %s", quote_identifier(slotname));
|
||||||
|
|
||||||
ConnectPublisher(conninfo, slotname);
|
if (!AttemptConnectPublisher(conninfo, slotname, true)) {
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg(
|
||||||
|
"could not connect to publisher.")));
|
||||||
|
}
|
||||||
|
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
{
|
{
|
||||||
int sqlstate = 0;
|
int sqlstate = 0;
|
||||||
|
|
@ -779,6 +843,7 @@ void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
|
||||||
|
|
||||||
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
|
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
|
||||||
|
|
||||||
|
pfree_ext(conninfo);
|
||||||
pfree(cmd.data);
|
pfree(cmd.data);
|
||||||
heap_close(rel, NoLock);
|
heap_close(rel, NoLock);
|
||||||
}
|
}
|
||||||
|
|
@ -908,3 +973,149 @@ void RenameSubscription(List *oldname, const char *newname)
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the host or port string into a string array,
|
||||||
|
* where host and port are separated by ",".
|
||||||
|
* input: conn --- host or port string separated by ","
|
||||||
|
* output: connArray --- host or port string array
|
||||||
|
* return: the length of connArray
|
||||||
|
* for example:
|
||||||
|
* (1):
|
||||||
|
* conn = 1.1.1.1,2.2.2.2,...,9.9.9.9
|
||||||
|
* connArray = {
|
||||||
|
* 1,.1.1.1,
|
||||||
|
* 2.2.2.2,
|
||||||
|
* ...,
|
||||||
|
* 9.9.9.9
|
||||||
|
* }
|
||||||
|
* return 9
|
||||||
|
* (2):
|
||||||
|
* conn = 1,2,...,9
|
||||||
|
* connArray = {1,2,...,9}
|
||||||
|
* return 9
|
||||||
|
*/
|
||||||
|
static int HostsPortsToArray(const char* conn, char** connArray)
|
||||||
|
{
|
||||||
|
if (conn == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
char* cp = NULL;
|
||||||
|
char* cur = NULL;
|
||||||
|
char *buf = pstrdup(conn);
|
||||||
|
|
||||||
|
cp = buf;
|
||||||
|
int i = 0;
|
||||||
|
while (*cp) {
|
||||||
|
cur = cp;
|
||||||
|
while (*cp && *cp != ',') {
|
||||||
|
++cp;
|
||||||
|
}
|
||||||
|
if (*cp == ',') {
|
||||||
|
*cp = '\0';
|
||||||
|
++cp;
|
||||||
|
}
|
||||||
|
if (i >= MAX_REPLNODE_NUM) {
|
||||||
|
ereport(ERROR, (errmsg("Currently, a maximum of %d servers are "
|
||||||
|
"supported.", MAX_REPLNODE_NUM)));
|
||||||
|
}
|
||||||
|
connArray[i++] = pstrdup(cur);
|
||||||
|
|
||||||
|
if (*cp == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pfree(buf);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* parse host and port
|
||||||
|
*/
|
||||||
|
static void ParseHostPort(char* hoststr, char* portstr, HostPort** hostPortList)
|
||||||
|
{
|
||||||
|
char* hosts[MAX_REPLNODE_NUM] = {NULL};
|
||||||
|
char* ports[MAX_REPLNODE_NUM] = {NULL};
|
||||||
|
int hostNum = HostsPortsToArray(hoststr, hosts);
|
||||||
|
int portNum = HostsPortsToArray(portstr, ports);
|
||||||
|
if (hostNum != portNum) {
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("The number of host and port are inconsistent.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < hostNum; ++i) {
|
||||||
|
hostPortList[i] = (HostPort*)palloc(sizeof(HostPort));
|
||||||
|
hostPortList[i]->host = hosts[i];
|
||||||
|
hostPortList[i]->port = ports[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse conninfo
|
||||||
|
* conninfo format:
|
||||||
|
* 'dbname=abc user=username password=xxxx host=ip1,ip2,...,ip9 port=p1,p2,...,p9'
|
||||||
|
* after parsing:
|
||||||
|
* conninfoWithoutHostPort:
|
||||||
|
* 'dbname=abc user=username password=xxxx'
|
||||||
|
* hostPortList:
|
||||||
|
* {
|
||||||
|
* {host=ip1, port=p1},
|
||||||
|
* {host=ip2, port=p2},
|
||||||
|
* ...
|
||||||
|
* {host=ip9, port=p9}
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
void ParseConninfo(const char* conninfo, StringInfoData* conninfoWithoutHostPort, HostPort** hostPortList)
|
||||||
|
{
|
||||||
|
List* conninfoList = ConninfoToDefList(conninfo);
|
||||||
|
ListCell* l = NULL;
|
||||||
|
|
||||||
|
char* hostStr = NULL;
|
||||||
|
char* portStr = NULL;
|
||||||
|
foreach (l, conninfoList) {
|
||||||
|
DefElem* defel = (DefElem*)lfirst(l);
|
||||||
|
if (pg_strcasecmp(defel->defname, "host") == 0) {
|
||||||
|
hostStr = defGetString(defel);
|
||||||
|
} else if (pg_strcasecmp(defel->defname, "port") == 0) {
|
||||||
|
portStr = defGetString(defel);
|
||||||
|
} else {
|
||||||
|
appendStringInfo(conninfoWithoutHostPort, "%s=%s ", defel->defname, defGetString(defel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hostPortList != NULL) {
|
||||||
|
ParseHostPort(hostStr, portStr, hostPortList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* encrypt conninfo when action = 'E'
|
||||||
|
* decrypt conninfo when action = 'D'
|
||||||
|
* conninfoNew: encrypted or decrypted conninfo
|
||||||
|
*/
|
||||||
|
char* EncryptOrDecryptConninfo(const char* conninfo, const char action)
|
||||||
|
{
|
||||||
|
/* parse conninfo to list */
|
||||||
|
List *conninfoList = ConninfoToDefList(conninfo);
|
||||||
|
/* Sensitive options for subscription */
|
||||||
|
const char* sensitiveOptionsArray[] = {"password"};
|
||||||
|
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
|
||||||
|
switch (action) {
|
||||||
|
/* Encrypt */
|
||||||
|
case 'E':
|
||||||
|
EncryptGenericOptions(conninfoList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Decrypt */
|
||||||
|
case 'D':
|
||||||
|
DecryptOptions(conninfoList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* conninfoNew = DefListToString(conninfoList);
|
||||||
|
ClearListContent(conninfoList);
|
||||||
|
list_free_ext(conninfoList);
|
||||||
|
|
||||||
|
return conninfoNew;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23175,7 +23175,7 @@ static void checkValidationForExchangeTable(Relation partTableRel, Relation ordT
|
||||||
int2 bucketId = InvalidBktId;
|
int2 bucketId = InvalidBktId;
|
||||||
|
|
||||||
// get right partition oid for the tuple
|
// get right partition oid for the tuple
|
||||||
targetPartOid = heapTupleGetPartitionId(partTableRel, (HeapTuple) tuple);
|
targetPartOid = heapTupleGetPartitionId(partTableRel, (HeapTuple)tuple, true);
|
||||||
|
|
||||||
searchFakeReationForPartitionOid(
|
searchFakeReationForPartitionOid(
|
||||||
partRelHTAB, CurrentMemoryContext, partTableRel, targetPartOid, partRel, part, RowExclusiveLock);
|
partRelHTAB, CurrentMemoryContext, partTableRel, targetPartOid, partRel, part, RowExclusiveLock);
|
||||||
|
|
@ -24797,7 +24797,8 @@ static Oid AddTemporaryPartitionForAlterPartitions(const AlterTableCmd* cmd, Rel
|
||||||
destPartOid = AddTemporaryHashPartitionForAlterPartitions(cmd, partTableRel, partSeq, renameTargetPart);
|
destPartOid = AddTemporaryHashPartitionForAlterPartitions(cmd, partTableRel, partSeq, renameTargetPart);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PART_TYPE_RANGE: {
|
case PART_TYPE_RANGE:
|
||||||
|
case PART_TYPE_INTERVAL: {
|
||||||
destPartOid = AddTemporaryRangePartitionForAlterPartitions(cmd, partTableRel, partSeq, renameTargetPart);
|
destPartOid = AddTemporaryRangePartitionForAlterPartitions(cmd, partTableRel, partSeq, renameTargetPart);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -25098,11 +25099,11 @@ static void readTuplesAndInsertInternal(Relation tempTableRel, Relation partTabl
|
||||||
|
|
||||||
/* tableam_tops_copy_tuple is not ready so we add UStore hack path */
|
/* tableam_tops_copy_tuple is not ready so we add UStore hack path */
|
||||||
copyTuple = tableam_tops_copy_tuple(tuple);
|
copyTuple = tableam_tops_copy_tuple(tuple);
|
||||||
targetPartOid = heapTupleGetPartitionId(partTableRel, (void *)tuple);
|
targetPartOid = heapTupleGetPartitionId(partTableRel, (void *)tuple, true);
|
||||||
searchFakeReationForPartitionOid(
|
searchFakeReationForPartitionOid(
|
||||||
partRelHTAB, CurrentMemoryContext, partTableRel, targetPartOid, partRel, part, RowExclusiveLock);
|
partRelHTAB, CurrentMemoryContext, partTableRel, targetPartOid, partRel, part, RowExclusiveLock);
|
||||||
if (RelationIsSubPartitioned(partTableRel)) {
|
if (RelationIsSubPartitioned(partTableRel)) {
|
||||||
targetPartOid = heapTupleGetPartitionId(partRel, (void *)tuple);
|
targetPartOid = heapTupleGetPartitionId(partRel, (void *)tuple, true);
|
||||||
searchFakeReationForPartitionOid(partRelHTAB, CurrentMemoryContext, partRel, targetPartOid, subPartRel,
|
searchFakeReationForPartitionOid(partRelHTAB, CurrentMemoryContext, partRel, targetPartOid, subPartRel,
|
||||||
subPart, RowExclusiveLock);
|
subPart, RowExclusiveLock);
|
||||||
partRel = subPartRel;
|
partRel = subPartRel;
|
||||||
|
|
|
||||||
|
|
@ -1181,6 +1181,17 @@ static Node* pull_up_simple_subquery(PlannerInfo* root, Node* jtnode, RangeTblEn
|
||||||
return jtnode;
|
return jtnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We must flatten any join alias Vars in the subquery's targetlist,
|
||||||
|
* because pulling up the subquery's subqueries might have changed their
|
||||||
|
* expansions into arbitrary expressions, which could affect
|
||||||
|
* pullup_replace_vars' decisions about whether PlaceHolderVar wrappers
|
||||||
|
* are needed for tlist entries. (Likely it'd be better to do
|
||||||
|
* flatten_join_alias_vars on the whole query tree at some earlier stage,
|
||||||
|
* maybe even in the rewriter; but for now let's just fix this case here.)
|
||||||
|
*/
|
||||||
|
subquery->targetList = (List *) flatten_join_alias_vars(subroot, (Node *) subquery->targetList);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adjust level-0 varnos in subquery so that we can append its rangetable
|
* Adjust level-0 varnos in subquery so that we can append its rangetable
|
||||||
* to upper query's. We have to fix the subquery's append_rel_list as
|
* to upper query's. We have to fix the subquery's append_rel_list as
|
||||||
|
|
|
||||||
|
|
@ -1263,7 +1263,7 @@ static void ckpt_pagewriter_main_thread_loop(void)
|
||||||
HandlePageWriterMainInterrupts();
|
HandlePageWriterMainInterrupts();
|
||||||
|
|
||||||
candidate_num = get_curr_candidate_nums(false) + get_curr_candidate_nums(true);
|
candidate_num = get_curr_candidate_nums(false) + get_curr_candidate_nums(true);
|
||||||
if (candidate_num == 0) {
|
if (candidate_num == 0 && !t_thrd.pagewriter_cxt.shutdown_requested) {
|
||||||
/* wakeup sub thread scan the buffer pool, init the candidate list */
|
/* wakeup sub thread scan the buffer pool, init the candidate list */
|
||||||
wakeup_sub_thread();
|
wakeup_sub_thread();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -825,10 +825,10 @@ void client_read_ended(void)
|
||||||
#define INIT_PLUGIN_OBJECT "init_plugin_object"
|
#define INIT_PLUGIN_OBJECT "init_plugin_object"
|
||||||
void InitBSqlPluginHookIfNeeded()
|
void InitBSqlPluginHookIfNeeded()
|
||||||
{
|
{
|
||||||
const char* b_sql_plugin = "b_sql_plugin";
|
const char* dolphin = "dolphin";
|
||||||
CFunInfo tmpCF;
|
CFunInfo tmpCF;
|
||||||
|
|
||||||
tmpCF = load_external_function(b_sql_plugin, INIT_PLUGIN_OBJECT, false, false);
|
tmpCF = load_external_function(dolphin, INIT_PLUGIN_OBJECT, false, false);
|
||||||
if (tmpCF.user_fn != NULL) {
|
if (tmpCF.user_fn != NULL) {
|
||||||
((void* (*)(void))(tmpCF.user_fn))();
|
((void* (*)(void))(tmpCF.user_fn))();
|
||||||
}
|
}
|
||||||
|
|
@ -862,10 +862,12 @@ List* pg_parse_query(const char* query_string, List** query_string_locationlist)
|
||||||
|
|
||||||
List* (*parser_hook)(const char*, List**) = raw_parser;
|
List* (*parser_hook)(const char*, List**) = raw_parser;
|
||||||
#ifndef ENABLE_MULTIPLE_NODES
|
#ifndef ENABLE_MULTIPLE_NODES
|
||||||
|
if (u_sess->attr.attr_sql.dolphin) {
|
||||||
int id = GetCustomParserId();
|
int id = GetCustomParserId();
|
||||||
if (id >= 0 && g_instance.raw_parser_hook[id] != NULL) {
|
if (id >= 0 && g_instance.raw_parser_hook[id] != NULL) {
|
||||||
parser_hook = (List* (*)(const char*, List**))g_instance.raw_parser_hook[id];
|
parser_hook = (List* (*)(const char*, List**))g_instance.raw_parser_hook[id];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
raw_parsetree_list = parser_hook(query_string, query_string_locationlist);
|
raw_parsetree_list = parser_hook(query_string, query_string_locationlist);
|
||||||
|
|
||||||
|
|
@ -6112,6 +6114,9 @@ void ProcessInterrupts(void)
|
||||||
|
|
||||||
/* The logical replication launcher can be stopped at any time. */
|
/* The logical replication launcher can be stopped at any time. */
|
||||||
proc_exit(0);
|
proc_exit(0);
|
||||||
|
} else if (IsLogicalWorker()) {
|
||||||
|
ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN),
|
||||||
|
errmsg("terminating logical replication worker due to administrator command")));
|
||||||
#endif
|
#endif
|
||||||
} else if (IsTxnSnapCapturerProcess()) {
|
} else if (IsTxnSnapCapturerProcess()) {
|
||||||
ereport(FATAL,
|
ereport(FATAL,
|
||||||
|
|
@ -7569,7 +7574,7 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam
|
||||||
init_set_params_htab();
|
init_set_params_htab();
|
||||||
|
|
||||||
#ifndef ENABLE_MULTIPLE_NODES
|
#ifndef ENABLE_MULTIPLE_NODES
|
||||||
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.b_sql_plugin) {
|
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.dolphin) {
|
||||||
InitBSqlPluginHookIfNeeded();
|
InitBSqlPluginHookIfNeeded();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -848,7 +848,7 @@ static bool InitSession(knl_session_context* session)
|
||||||
t_thrd.proc_cxt.PostInit->InitSession();
|
t_thrd.proc_cxt.PostInit->InitSession();
|
||||||
|
|
||||||
#ifndef ENABLE_MULTIPLE_NODES
|
#ifndef ENABLE_MULTIPLE_NODES
|
||||||
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.b_sql_plugin) {
|
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.dolphin) {
|
||||||
InitBSqlPluginHookIfNeeded();
|
InitBSqlPluginHookIfNeeded();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -720,7 +720,6 @@ static Datum ExecEvalWholeRowVar(
|
||||||
{
|
{
|
||||||
Var* variable = (Var*)wrvstate->xprstate.expr;
|
Var* variable = (Var*)wrvstate->xprstate.expr;
|
||||||
TupleTableSlot* slot = NULL;
|
TupleTableSlot* slot = NULL;
|
||||||
TupleDesc slot_tupdesc;
|
|
||||||
bool needslow = false;
|
bool needslow = false;
|
||||||
|
|
||||||
if (isDone != NULL)
|
if (isDone != NULL)
|
||||||
|
|
@ -802,21 +801,13 @@ static Datum ExecEvalWholeRowVar(
|
||||||
if (wrvstate->wrv_junkFilter != NULL)
|
if (wrvstate->wrv_junkFilter != NULL)
|
||||||
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
|
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
|
||||||
|
|
||||||
slot_tupdesc = slot->tts_tupleDescriptor;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If it's a RECORD Var, we'll use the slot's type ID info. It's likely
|
|
||||||
* that the slot's type is also RECORD; if so, make sure it's been
|
|
||||||
* "blessed", so that the Datum can be interpreted later.
|
|
||||||
*
|
|
||||||
* If the Var identifies a named composite type, we must check that the
|
* If the Var identifies a named composite type, we must check that the
|
||||||
* actual tuple type is compatible with it.
|
* actual tuple type is compatible with it.
|
||||||
*/
|
*/
|
||||||
if (variable->vartype == RECORDOID) {
|
if (variable->vartype != RECORDOID) {
|
||||||
if (slot_tupdesc->tdtypeid == RECORDOID && slot_tupdesc->tdtypmod < 0)
|
|
||||||
assign_record_type_typmod(slot_tupdesc);
|
|
||||||
} else {
|
|
||||||
TupleDesc var_tupdesc;
|
TupleDesc var_tupdesc;
|
||||||
|
TupleDesc slot_tupdesc;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -833,6 +824,8 @@ static Datum ExecEvalWholeRowVar(
|
||||||
*/
|
*/
|
||||||
var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
|
var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
|
||||||
|
|
||||||
|
slot_tupdesc = slot->tts_tupleDescriptor;
|
||||||
|
|
||||||
if (var_tupdesc->natts != slot_tupdesc->natts)
|
if (var_tupdesc->natts != slot_tupdesc->natts)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
|
@ -886,6 +879,7 @@ static Datum ExecEvalWholeRowFast(
|
||||||
{
|
{
|
||||||
Var* variable = (Var*)wrvstate->xprstate.expr;
|
Var* variable = (Var*)wrvstate->xprstate.expr;
|
||||||
TupleTableSlot* slot = NULL;
|
TupleTableSlot* slot = NULL;
|
||||||
|
TupleDesc slot_tupdesc;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
TupleDesc tupleDesc;
|
TupleDesc tupleDesc;
|
||||||
HeapTupleHeader dtuple;
|
HeapTupleHeader dtuple;
|
||||||
|
|
@ -915,6 +909,17 @@ static Datum ExecEvalWholeRowFast(
|
||||||
if (wrvstate->wrv_junkFilter != NULL)
|
if (wrvstate->wrv_junkFilter != NULL)
|
||||||
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
|
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it's a RECORD Var, we'll use the slot's type ID info. It's likely
|
||||||
|
* that the slot's type is also RECORD; if so, make sure it's been
|
||||||
|
* "blessed", so that the Datum can be interpreted later.
|
||||||
|
*/
|
||||||
|
slot_tupdesc = slot->tts_tupleDescriptor;
|
||||||
|
if (variable->vartype == RECORDOID) {
|
||||||
|
if (slot_tupdesc->tdtypeid == RECORDOID && slot_tupdesc->tdtypmod < 0)
|
||||||
|
assign_record_type_typmod(slot_tupdesc);
|
||||||
|
}
|
||||||
|
|
||||||
tuple = ExecFetchSlotTuple(slot);
|
tuple = ExecFetchSlotTuple(slot);
|
||||||
tupleDesc = slot->tts_tupleDescriptor;
|
tupleDesc = slot->tts_tupleDescriptor;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -322,7 +322,6 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
|
||||||
}
|
}
|
||||||
if (found) {
|
if (found) {
|
||||||
/* Found tuple, try to lock it in the lockmode. */
|
/* Found tuple, try to lock it in the lockmode. */
|
||||||
outslot->tts_tuple = ExecMaterializeSlot(outslot);
|
|
||||||
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
|
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
|
||||||
/*
|
/*
|
||||||
* If the tuple is locked, wait for locking transaction to finish
|
* If the tuple is locked, wait for locking transaction to finish
|
||||||
|
|
@ -346,12 +345,16 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
|
||||||
ItemPointer tid = tableam_tops_get_t_self(targetRel, outslot->tts_tuple);
|
ItemPointer tid = tableam_tops_get_t_self(targetRel, outslot->tts_tuple);
|
||||||
|
|
||||||
if (RelationIsUstoreFormat(targetRel)) {
|
if (RelationIsUstoreFormat(targetRel)) {
|
||||||
|
/* materialize the slot, so we can visit it after the scan is end */
|
||||||
|
outslot->tts_tuple = UHeapMaterialize(outslot);
|
||||||
ItemPointerCopy(tid, &UHeaplocktup.ctid);
|
ItemPointerCopy(tid, &UHeaplocktup.ctid);
|
||||||
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
|
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
|
||||||
securec_check(rc, "\0", "\0");
|
securec_check(rc, "\0", "\0");
|
||||||
UHeaplocktup.disk_tuple = &tbuf.hdr;
|
UHeaplocktup.disk_tuple = &tbuf.hdr;
|
||||||
locktup = &UHeaplocktup;
|
locktup = &UHeaplocktup;
|
||||||
} else {
|
} else {
|
||||||
|
/* materialize the slot, so we can visit it after the scan is end */
|
||||||
|
outslot->tts_tuple = ExecMaterializeSlot(outslot);
|
||||||
ItemPointerCopy(tid, &heaplocktup.t_self);
|
ItemPointerCopy(tid, &heaplocktup.t_self);
|
||||||
locktup = &heaplocktup;
|
locktup = &heaplocktup;
|
||||||
}
|
}
|
||||||
|
|
@ -401,6 +404,10 @@ static bool tuple_equals_slot(TupleDesc desc, const Tuple tup, TupleTableSlot *s
|
||||||
/* Check equality of the attributes. */
|
/* Check equality of the attributes. */
|
||||||
for (attrnum = 0; attrnum < desc->natts; attrnum++) {
|
for (attrnum = 0; attrnum < desc->natts; attrnum++) {
|
||||||
TypeCacheEntry *typentry;
|
TypeCacheEntry *typentry;
|
||||||
|
/* skip generate column */
|
||||||
|
if (GetGeneratedCol(desc, attrnum)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* If one value is NULL and other is not, then they are certainly not
|
* If one value is NULL and other is not, then they are certainly not
|
||||||
* equal
|
* equal
|
||||||
|
|
@ -478,7 +485,6 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
|
ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
|
||||||
outslot->tts_tuple = ExecMaterializeSlot(outslot);
|
|
||||||
|
|
||||||
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
|
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
|
||||||
/*
|
/*
|
||||||
|
|
@ -511,12 +517,16 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
|
||||||
ItemPointer tid = tableam_tops_get_t_self(rel, outslot->tts_tuple);
|
ItemPointer tid = tableam_tops_get_t_self(rel, outslot->tts_tuple);
|
||||||
|
|
||||||
if (RelationIsUstoreFormat(targetRel)) {
|
if (RelationIsUstoreFormat(targetRel)) {
|
||||||
|
/* materialize the slot, so we can visit it after the scan is end */
|
||||||
|
outslot->tts_tuple = UHeapMaterialize(outslot);
|
||||||
ItemPointerCopy(tid, &UHeaplocktup.ctid);
|
ItemPointerCopy(tid, &UHeaplocktup.ctid);
|
||||||
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
|
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
|
||||||
securec_check(rc, "\0", "\0");
|
securec_check(rc, "\0", "\0");
|
||||||
UHeaplocktup.disk_tuple = &tbuf.hdr;
|
UHeaplocktup.disk_tuple = &tbuf.hdr;
|
||||||
locktup = &UHeaplocktup;
|
locktup = &UHeaplocktup;
|
||||||
} else {
|
} else {
|
||||||
|
/* materialize the slot, so we can visit it after the scan is end */
|
||||||
|
outslot->tts_tuple = ExecMaterializeSlot(outslot);
|
||||||
ItemPointerCopy(tid, &heaplocktup.t_self);
|
ItemPointerCopy(tid, &heaplocktup.t_self);
|
||||||
locktup = &heaplocktup;
|
locktup = &heaplocktup;
|
||||||
}
|
}
|
||||||
|
|
@ -653,7 +663,6 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
|
||||||
/* Compute stored generated columns */
|
/* Compute stored generated columns */
|
||||||
if (rel->rd_att->constr && rel->rd_att->constr->has_generated_stored) {
|
if (rel->rd_att->constr && rel->rd_att->constr->has_generated_stored) {
|
||||||
ExecComputeStoredGenerated(resultRelInfo, estate, slot, tuple, CMD_UPDATE);
|
ExecComputeStoredGenerated(resultRelInfo, estate, slot, tuple, CMD_UPDATE);
|
||||||
tuple = slot->tts_tuple;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check the constraints of the tuple */
|
/* Check the constraints of the tuple */
|
||||||
|
|
@ -672,10 +681,11 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
|
||||||
rowMovement = true;
|
rowMovement = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tuple = slot->tts_tuple;
|
||||||
CommandId cid = GetCurrentCommandId(true);
|
CommandId cid = GetCurrentCommandId(true);
|
||||||
/* OK, update the tuple and index entries for it */
|
/* OK, update the tuple and index entries for it */
|
||||||
if (!rowMovement) {
|
if (!rowMovement) {
|
||||||
res = tableam_tuple_update(targetRelation, parentRelation, searchSlotTid, slot->tts_tuple, cid,
|
res = tableam_tuple_update(targetRelation, parentRelation, searchSlotTid, tuple, cid,
|
||||||
InvalidSnapshot, estate->es_snapshot, true, &oldslot, &tmfd, &updateIndexes, &modifiedIdxAttrs,
|
InvalidSnapshot, estate->es_snapshot, true, &oldslot, &tmfd, &updateIndexes, &modifiedIdxAttrs,
|
||||||
false, allowInplaceUpdate);
|
false, allowInplaceUpdate);
|
||||||
CheckTupleModifyRes(res);
|
CheckTupleModifyRes(res);
|
||||||
|
|
|
||||||
|
|
@ -2947,9 +2947,9 @@ void SetOneOfCompressOption(DefElem* defElem, TableCreateSupport* tableCreateSup
|
||||||
} else if (pg_strcasecmp(defname, "compress_level") == 0) {
|
} else if (pg_strcasecmp(defname, "compress_level") == 0) {
|
||||||
tableCreateSupport->compressLevel = true;
|
tableCreateSupport->compressLevel = true;
|
||||||
} else if (pg_strcasecmp(defname, "compress_byte_convert") == 0) {
|
} else if (pg_strcasecmp(defname, "compress_byte_convert") == 0) {
|
||||||
tableCreateSupport->compressByteConvert = true;
|
tableCreateSupport->compressByteConvert = defGetBoolean(defElem);
|
||||||
} else if (pg_strcasecmp(defname, "compress_diff_convert") == 0) {
|
} else if (pg_strcasecmp(defname, "compress_diff_convert") == 0) {
|
||||||
tableCreateSupport->compressDiffConvert = true;
|
tableCreateSupport->compressDiffConvert = defGetBoolean(defElem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8965,6 +8965,8 @@ void StartupXLOG(void)
|
||||||
g_instance.comm_cxt.predo_cxt.redoPf.redo_done_time = 0;
|
g_instance.comm_cxt.predo_cxt.redoPf.redo_done_time = 0;
|
||||||
pg_atomic_write_u32(&(g_instance.comm_cxt.localinfo_cxt.is_finish_redo), 0);
|
pg_atomic_write_u32(&(g_instance.comm_cxt.localinfo_cxt.is_finish_redo), 0);
|
||||||
|
|
||||||
|
NotifyGscRecoveryStarted();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize WAL insert status array and the flush index - lastWalStatusEntryFlushed.
|
* Initialize WAL insert status array and the flush index - lastWalStatusEntryFlushed.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1818,3 +1818,47 @@ void UHeapSlotStoreUHeapTuple(UHeapTuple utuple, TupleTableSlot *slot, bool shou
|
||||||
/* Mark extracted state invalid */
|
/* Mark extracted state invalid */
|
||||||
slot->tts_nvalid = 0;
|
slot->tts_nvalid = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make the contents of the uheap table's slot contents solely depend on the slot(make them a local copy),
|
||||||
|
* and not on underlying external resources like another memory context, buffers etc.
|
||||||
|
*
|
||||||
|
* @pram slot: slot to be materialized.
|
||||||
|
*/
|
||||||
|
Tuple UHeapMaterialize(TupleTableSlot *slot)
|
||||||
|
{
|
||||||
|
Assert(!slot->tts_isempty);
|
||||||
|
Assert(slot->tts_tupslotTableAm == TAM_USTORE);
|
||||||
|
Assert(slot->tts_tupleDescriptor != NULL);
|
||||||
|
/*
|
||||||
|
* If we have a regular physical tuple, and it's locally palloc'd, we have
|
||||||
|
* nothing to do.
|
||||||
|
*/
|
||||||
|
if (slot->tts_tuple && slot->tts_shouldFree) {
|
||||||
|
return slot->tts_tuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Otherwise, copy or build a physical tuple, and store it into the slot.
|
||||||
|
*
|
||||||
|
* We may be called in a context that is shorter-lived than the tuple
|
||||||
|
* slot, but we have to ensure that the materialized tuple will survive
|
||||||
|
* anyway.
|
||||||
|
*/
|
||||||
|
MemoryContext old_context = MemoryContextSwitchTo(slot->tts_mcxt);
|
||||||
|
if (slot->tts_tuple != NULL) {
|
||||||
|
slot->tts_tuple = UHeapCopyTuple((UHeapTuple)slot->tts_tuple);
|
||||||
|
} else {
|
||||||
|
slot->tts_tuple = UHeapFormTuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull);
|
||||||
|
}
|
||||||
|
slot->tts_shouldFree = true;
|
||||||
|
MemoryContextSwitchTo(old_context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Have to deform from scratch, otherwise tts_values[] entries could point
|
||||||
|
* into the non-materialized tuple (which might be gone when accessed).
|
||||||
|
*/
|
||||||
|
slot->tts_nvalid = 0;
|
||||||
|
return slot->tts_tuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -238,16 +238,20 @@ public:
|
||||||
return m_purpose;
|
return m_purpose;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GcStartTxnMTtests()
|
GcEpochType GcStartInnerTxn()
|
||||||
{
|
{
|
||||||
if (m_gcEpoch != GetGlobalEpoch())
|
|
||||||
m_gcEpoch = GetGlobalEpoch();
|
m_gcEpoch = GetGlobalEpoch();
|
||||||
|
|
||||||
|
return m_gcEpoch;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GcEndTxnMTtests()
|
void GcEndInnerTxn(bool clean_gc)
|
||||||
{
|
{
|
||||||
|
if (clean_gc) {
|
||||||
RunQuicese();
|
RunQuicese();
|
||||||
}
|
}
|
||||||
|
m_gcEpoch = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void GcStartTxn()
|
void GcStartTxn()
|
||||||
{
|
{
|
||||||
|
|
@ -272,6 +276,7 @@ public:
|
||||||
RunQuicese();
|
RunQuicese();
|
||||||
m_managerLock.unlock();
|
m_managerLock.unlock();
|
||||||
}
|
}
|
||||||
|
m_gcEpoch = 0;
|
||||||
m_isTxnStarted = false;
|
m_isTxnStarted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,7 +318,6 @@ public:
|
||||||
{
|
{
|
||||||
if (m_performGcEpoch != g_gcActiveEpoch)
|
if (m_performGcEpoch != g_gcActiveEpoch)
|
||||||
HardQuiesce(m_rcuFreeCount);
|
HardQuiesce(m_rcuFreeCount);
|
||||||
m_gcEpoch = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Clean all object at the end of the session */
|
/** @brief Clean all object at the end of the session */
|
||||||
|
|
|
||||||
|
|
@ -258,6 +258,15 @@ bool Index::IndexInsert(Sentinel*& outputSentinel, const Key* key, uint32_t pid,
|
||||||
outputSentinel = IndexInsertImpl(key, sentinel, inserted, pid);
|
outputSentinel = IndexInsertImpl(key, sentinel, inserted, pid);
|
||||||
// sync between rollback/delete and insert
|
// sync between rollback/delete and insert
|
||||||
if (inserted == false) {
|
if (inserted == false) {
|
||||||
|
if (unlikely(outputSentinel == nullptr)) {
|
||||||
|
MOT_REPORT_ERROR(
|
||||||
|
MOT_ERROR_OOM, "Index Insert", "Failed to insert sentinel to index %s", m_name.c_str());
|
||||||
|
rc = RC_MEMORY_ALLOCATION_ERROR;
|
||||||
|
m_sentinelPool->Release<Sentinel>(sentinel);
|
||||||
|
sentinel = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Spin if the counter is 0 - aborting in parallel or sentinel is marks for commit
|
// Spin if the counter is 0 - aborting in parallel or sentinel is marks for commit
|
||||||
if (outputSentinel->RefCountUpdate(INC, pid) == RC_OK)
|
if (outputSentinel->RefCountUpdate(INC, pid) == RC_OK)
|
||||||
retryInsert = false;
|
retryInsert = false;
|
||||||
|
|
@ -300,8 +309,16 @@ Sentinel* Index::IndexInsert(const Key* key, Row* row, uint32_t pid)
|
||||||
// no need to report to full error stack
|
// no need to report to full error stack
|
||||||
SetLastError(MOT_ERROR_UNIQUE_VIOLATION, MOT_SEVERITY_NORMAL);
|
SetLastError(MOT_ERROR_UNIQUE_VIOLATION, MOT_SEVERITY_NORMAL);
|
||||||
m_sentinelPool->Release<Sentinel>(sentinel);
|
m_sentinelPool->Release<Sentinel>(sentinel);
|
||||||
|
sentinel = nullptr;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} else {
|
} else {
|
||||||
|
if (inserted == false) {
|
||||||
|
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Index Insert", "Failed to insert sentinel to index %s", m_name.c_str());
|
||||||
|
m_sentinelPool->Release<Sentinel>(sentinel);
|
||||||
|
sentinel = nullptr;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (GetIndexOrder() == IndexOrder::INDEX_ORDER_PRIMARY) {
|
if (GetIndexOrder() == IndexOrder::INDEX_ORDER_PRIMARY) {
|
||||||
sentinel->SetPrimaryIndex();
|
sentinel->SetPrimaryIndex();
|
||||||
sentinel->SetNextPtr(row);
|
sentinel->SetNextPtr(row);
|
||||||
|
|
@ -343,9 +360,6 @@ Sentinel* Index::IndexReadHeader(const Key* key, uint32_t pid) const
|
||||||
Sentinel* Index::IndexRemove(const Key* key, uint32_t pid)
|
Sentinel* Index::IndexRemove(const Key* key, uint32_t pid)
|
||||||
{
|
{
|
||||||
Sentinel* sentinel = IndexRemoveImpl(key, pid);
|
Sentinel* sentinel = IndexRemoveImpl(key, pid);
|
||||||
|
|
||||||
MOT_ASSERT(sentinel != nullptr);
|
|
||||||
MOT_ASSERT(sentinel->GetCounter() == 0);
|
|
||||||
return sentinel;
|
return sentinel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,12 +56,22 @@ void* basic_table<P>::insert(MOT::Key const* const& key, void* const& entry, boo
|
||||||
5. Update the the key slice, keylen, key suffix and key's value in the
|
5. Update the the key slice, keylen, key suffix and key's value in the
|
||||||
leaf
|
leaf
|
||||||
6. Add the key's location in permutation's back (key is not visible for
|
6. Add the key's location in permutation's back (key is not visible for
|
||||||
readers yet) As key's location is not part of the permutation yet, the key
|
readers yet) as key's location is not part of the permutation yet, the key
|
||||||
is not reachable (aka not present). In addition, the leaf is still locked.
|
is not reachable (aka not present). In addition, the leaf is still locked.
|
||||||
Unlocking the node and enter the key into the permutation will be done
|
Unlocking the node and enter the key into the permutation will be done
|
||||||
later in finish_insert (called from lp.finish). */
|
later in finish_insert (done in lp.finish function). */
|
||||||
|
|
||||||
bool found = lp.find_insert(*mtSessionThreadInfo);
|
bool found = false;
|
||||||
|
if (!lp.find_insert(*mtSessionThreadInfo, found)) {
|
||||||
|
// Failed to insert key due to memory allocation failure.
|
||||||
|
MOT_ASSERT(!mtSessionThreadInfo->non_disruptive_error());
|
||||||
|
MOT_ASSERT(found == false);
|
||||||
|
lp.finish(0, *mtSessionThreadInfo);
|
||||||
|
result = false;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOT_ASSERT(mtSessionThreadInfo->non_disruptive_error());
|
||||||
|
|
||||||
// If the key is new (not previously existing) then we record the entry under
|
// If the key is new (not previously existing) then we record the entry under
|
||||||
// that key
|
// that key
|
||||||
|
|
|
||||||
|
|
@ -33,15 +33,15 @@ namespace Masstree {
|
||||||
template <typename P>
|
template <typename P>
|
||||||
struct gc_layer_rcu_callback_ng : public P::threadinfo_type::mrcu_callback {
|
struct gc_layer_rcu_callback_ng : public P::threadinfo_type::mrcu_callback {
|
||||||
typedef typename P::threadinfo_type threadinfo;
|
typedef typename P::threadinfo_type threadinfo;
|
||||||
node_base<P>* root_;
|
node_base<P>** root_ref_;
|
||||||
int len_;
|
int len_;
|
||||||
size_t size_;
|
size_t size_;
|
||||||
MOT::MasstreePrimaryIndex* index_;
|
MOT::MasstreePrimaryIndex* index_;
|
||||||
char s_[0];
|
char s_[0];
|
||||||
gc_layer_rcu_callback_ng(node_base<P>* root, Str prefix, size_t size)
|
gc_layer_rcu_callback_ng(node_base<P>** root_ref, Str prefix, size_t size)
|
||||||
: root_(root), len_(prefix.length()), size_(size), index_(mtSessionThreadInfo->get_working_index())
|
: root_ref_(root_ref), len_(prefix.length()), size_(size), index_(mtSessionThreadInfo->get_working_index())
|
||||||
{
|
{
|
||||||
errno_t erc = memcpy_s(s_, size_, prefix.data(), len_);
|
errno_t erc = memcpy_s(s_, len_, prefix.data(), len_);
|
||||||
securec_check(erc, "\0", "\0");
|
securec_check(erc, "\0", "\0");
|
||||||
}
|
}
|
||||||
size_t operator()(bool drop_index);
|
size_t operator()(bool drop_index);
|
||||||
|
|
@ -51,7 +51,7 @@ struct gc_layer_rcu_callback_ng : public P::threadinfo_type::mrcu_callback {
|
||||||
return size_;
|
return size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void make(node_base<P>* root, Str prefix, threadinfo& ti);
|
static void make(node_base<P>** root_ref, Str prefix, threadinfo& ti);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename P>
|
template <typename P>
|
||||||
|
|
@ -60,8 +60,8 @@ size_t gc_layer_rcu_callback_ng<P>::operator()(bool drop_index)
|
||||||
// If drop_index == true, all index's pools are going to be cleaned, so we can skip gc_layer call (which might add
|
// If drop_index == true, all index's pools are going to be cleaned, so we can skip gc_layer call (which might add
|
||||||
// more elements into GC)
|
// more elements into GC)
|
||||||
if (drop_index == false) {
|
if (drop_index == false) {
|
||||||
// GC layer remove might delete elements from tree and add them to the limbolist. Index must be provided to
|
// GC layer remove might delete elements from tree and might create new gc layer removal requests and add them to GC.
|
||||||
// allow access to the memory pools.
|
// Index must be provided to allow access to the memory pools.
|
||||||
mtSessionThreadInfo->set_working_index(index_);
|
mtSessionThreadInfo->set_working_index(index_);
|
||||||
(*this)(*mtSessionThreadInfo);
|
(*this)(*mtSessionThreadInfo);
|
||||||
mtSessionThreadInfo->set_working_index(NULL);
|
mtSessionThreadInfo->set_working_index(NULL);
|
||||||
|
|
@ -73,30 +73,33 @@ size_t gc_layer_rcu_callback_ng<P>::operator()(bool drop_index)
|
||||||
template <typename P>
|
template <typename P>
|
||||||
void gc_layer_rcu_callback_ng<P>::operator()(threadinfo& ti)
|
void gc_layer_rcu_callback_ng<P>::operator()(threadinfo& ti)
|
||||||
{
|
{
|
||||||
// root_ node while creating gc_layer_rcu_callback_ng might not be the current root. Find updated tree's root.
|
masstree_invariant(root_ref_);
|
||||||
while (!root_->is_root()) {
|
|
||||||
root_ = root_->maybe_parent();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If root was already deleted, do nothing.
|
tcursor<P> node_cursor(root_ref_, s_, len_);
|
||||||
if (root_->deleted()) {
|
bool do_remove = node_cursor.gc_layer(ti);
|
||||||
return;
|
if (!do_remove || !node_cursor.finish_remove(ti)) {
|
||||||
}
|
|
||||||
|
|
||||||
tcursor<P> node_cursor(root_, s_, len_);
|
|
||||||
if (!node_cursor.gc_layer(ti) || !node_cursor.finish_remove(ti)) {
|
|
||||||
node_cursor.n_->unlock();
|
node_cursor.n_->unlock();
|
||||||
}
|
}
|
||||||
|
ti.add_nodes_to_gc();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename P>
|
template <typename P>
|
||||||
void gc_layer_rcu_callback_ng<P>::make(node_base<P>* root, Str prefix, threadinfo& ti)
|
void gc_layer_rcu_callback_ng<P>::make(node_base<P>** root_ref, Str prefix, threadinfo& ti)
|
||||||
{
|
{
|
||||||
size_t sz = prefix.len + sizeof(gc_layer_rcu_callback_ng<P>);
|
size_t sz = prefix.len + sizeof(gc_layer_rcu_callback_ng<P>);
|
||||||
// As we are using slab allocator to allocate the memory, sz is will updated in ti.allocate with the real allocated
|
// As we are using slab allocator for allocation, sz is will be updated by ti.allocate with the real allocation
|
||||||
// size
|
// size. We need this size for GC deallocation size report
|
||||||
void* data = ti.allocate(sz, memtag_masstree_gc, &sz /*OUT PARAM*/);
|
void* data = ti.allocate(sz, memtag_masstree_gc, &sz /* IN/OUT PARAM */);
|
||||||
gc_layer_rcu_callback_ng<P>* cb = new (data) gc_layer_rcu_callback_ng<P>(root, prefix, sz);
|
if (!data) {
|
||||||
|
// If allocation fails, gc layer removal command will not be added to GC and this layer wont be removed.
|
||||||
|
// We might deal with this issue in the future by replacing the current mechanism with one of the following options:
|
||||||
|
// 1. Use thread local GC layer removal object (per threadinfo) and keep list of key suffixes to clean (also in threadinfo)
|
||||||
|
// 2. Move this feature to VACUUM process: Create special iterator that adds GC Layer callbacks when it finds empty layers
|
||||||
|
ti.set_last_error(MT_MERR_GC_LAYER_REMOVAL_MAKE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gc_layer_rcu_callback_ng<P>* cb = new (data) gc_layer_rcu_callback_ng<P>(root_ref, prefix, sz);
|
||||||
ti.rcu_register(cb, sz);
|
ti.rcu_register(cb, sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,14 +69,17 @@ Sentinel* MasstreePrimaryIndex::IndexInsertImpl(const Key* key, Sentinel* sentin
|
||||||
mtSessionThreadInfo->set_gc_session(
|
mtSessionThreadInfo->set_gc_session(
|
||||||
MOTEngine::GetInstance()->GetCurrentGcSession()); // set current GC session in thread-pooled envelope
|
MOTEngine::GetInstance()->GetCurrentGcSession()); // set current GC session in thread-pooled envelope
|
||||||
|
|
||||||
|
mtSessionThreadInfo->set_last_error(MT_MERR_OK);
|
||||||
|
|
||||||
existingItem = m_index.insert(key, sentinel, inserted, pid);
|
existingItem = m_index.insert(key, sentinel, inserted, pid);
|
||||||
|
|
||||||
mtSessionThreadInfo->set_gc_session(NULL);
|
mtSessionThreadInfo->set_gc_session(NULL);
|
||||||
mtSessionThreadInfo->set_working_index(NULL);
|
mtSessionThreadInfo->set_working_index(NULL);
|
||||||
|
|
||||||
if (!inserted) { // key mapping already exists in unique index
|
if (!inserted && existingItem) { // key mapping already exists in unique index
|
||||||
result = reinterpret_cast<Sentinel*>(existingItem);
|
result = reinterpret_cast<Sentinel*>(existingItem);
|
||||||
} // otherwise return null pointer
|
} // otherwise return null pointer (if !inserted && !existingItem, Key does not exist and insertation failed due to
|
||||||
|
// memory issue)
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -108,6 +111,8 @@ Sentinel* MasstreePrimaryIndex::IndexRemoveImpl(const Key* key, uint32_t pid)
|
||||||
mtSessionThreadInfo->set_gc_session(
|
mtSessionThreadInfo->set_gc_session(
|
||||||
MOTEngine::GetInstance()->GetCurrentGcSession()); // set current GC session in thread-pooled envelope
|
MOTEngine::GetInstance()->GetCurrentGcSession()); // set current GC session in thread-pooled envelope
|
||||||
|
|
||||||
|
mtSessionThreadInfo->set_last_error(MT_MERR_OK);
|
||||||
|
|
||||||
output = m_index.remove(key->GetKeyBuf(), key->GetKeyLength(), result, pid);
|
output = m_index.remove(key->GetKeyBuf(), key->GetKeyLength(), result, pid);
|
||||||
|
|
||||||
mtSessionThreadInfo->set_gc_session(NULL);
|
mtSessionThreadInfo->set_gc_session(NULL);
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
#include "masstree/mot_masstree_struct.hpp"
|
#include "masstree/mot_masstree_struct.hpp"
|
||||||
#include "masstree/mot_masstree_iterator.hpp"
|
#include "masstree/mot_masstree_iterator.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include "mot_engine.h"
|
||||||
|
|
||||||
namespace MOT {
|
namespace MOT {
|
||||||
/**
|
/**
|
||||||
|
|
@ -302,11 +303,38 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Print Masstree pools memory consumption details to log.
|
* @brief Print Masstree pools memory consumption details to log.
|
||||||
*/
|
*/
|
||||||
virtual void PrintPoolsStats()
|
virtual void PrintPoolsStats(LogLevel level = LogLevel::LL_DEBUG)
|
||||||
{
|
{
|
||||||
m_leafsPool->Print("Leafs pool: ");
|
m_leafsPool->Print("Leafs pool", level);
|
||||||
m_internodesPool->Print("Internode pool: ");
|
m_internodesPool->Print("Internode pool", level);
|
||||||
m_ksuffixSlab->Print("Ksuffix slab: ");
|
m_ksuffixSlab->Print("Ksuffix slab", level);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void GetLeafsPoolStats(uint64_t& objSize, uint64_t& numUsedObj, uint64_t& totalSize, uint64_t& netto)
|
||||||
|
{
|
||||||
|
PoolStatsSt stats = {};
|
||||||
|
m_leafsPool->GetStats(stats);
|
||||||
|
|
||||||
|
objSize = stats.m_objSize;
|
||||||
|
numUsedObj = stats.m_totalObjCount - stats.m_freeObjCount;
|
||||||
|
totalSize = stats.m_poolCount * stats.m_poolGrossSize;
|
||||||
|
netto = numUsedObj * objSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void GetInternodesPoolStats(uint64_t& objSize, uint64_t& numUsedObj, uint64_t& totalSize, uint64_t& netto)
|
||||||
|
{
|
||||||
|
PoolStatsSt stats = {};
|
||||||
|
m_internodesPool->GetStats(stats);
|
||||||
|
|
||||||
|
objSize = stats.m_objSize;
|
||||||
|
numUsedObj = stats.m_totalObjCount - stats.m_freeObjCount;
|
||||||
|
totalSize = stats.m_poolCount * stats.m_poolGrossSize;
|
||||||
|
netto = numUsedObj * objSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual PoolStatsSt* GetKsuffixSlabStats()
|
||||||
|
{
|
||||||
|
return m_ksuffixSlab->GetStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -316,6 +344,8 @@ public:
|
||||||
{
|
{
|
||||||
m_initialized = false;
|
m_initialized = false;
|
||||||
DestroyPools();
|
DestroyPools();
|
||||||
|
// remove masstree's root pointer (not valid anymore)
|
||||||
|
*(m_index.root_ref()) = nullptr;
|
||||||
|
|
||||||
return IndexInitImpl(NULL);
|
return IndexInitImpl(NULL);
|
||||||
}
|
}
|
||||||
|
|
@ -332,7 +362,7 @@ public:
|
||||||
* @param tag Hint to determine which pool to use.
|
* @param tag Hint to determine which pool to use.
|
||||||
* @return Pointer to allocated memory.
|
* @return Pointer to allocated memory.
|
||||||
*/
|
*/
|
||||||
void* AllocateMem(int& size, enum memtag tag)
|
virtual void* AllocateMem(int& size, enum memtag tag)
|
||||||
{
|
{
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case memtag_masstree_leaf:
|
case memtag_masstree_leaf:
|
||||||
|
|
@ -360,7 +390,7 @@ public:
|
||||||
* @param Pointer to allocated memory.
|
* @param Pointer to allocated memory.
|
||||||
* @return True if deallocation succeeded.
|
* @return True if deallocation succeeded.
|
||||||
*/
|
*/
|
||||||
bool DeallocateMem(void* ptr, int size, enum memtag tag)
|
virtual bool DeallocateMem(void* ptr, int size, enum memtag tag)
|
||||||
{
|
{
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case memtag_masstree_leaf:
|
case memtag_masstree_leaf:
|
||||||
|
|
@ -431,10 +461,16 @@ public:
|
||||||
{
|
{
|
||||||
// If dropIndex == true, all index's pools are going to be cleaned, so we skip the release here
|
// If dropIndex == true, all index's pools are going to be cleaned, so we skip the release here
|
||||||
mtSessionThreadInfo->set_gc_session(GetCurrentGcSession());
|
mtSessionThreadInfo->set_gc_session(GetCurrentGcSession());
|
||||||
|
GcEpochType local_epoch =
|
||||||
|
GetSessionManager()->GetCurrentSessionContext()->GetTxnManager()->GetGcSession()->GcStartInnerTxn();
|
||||||
|
|
||||||
size_t allocationSize = (*static_cast<mrcu_callback*>(gcRemoveLayerFuncObjPtr))(dropIndex);
|
size_t allocationSize = (*static_cast<mrcu_callback*>(gcRemoveLayerFuncObjPtr))(dropIndex);
|
||||||
|
|
||||||
if (dropIndex == false) {
|
if (dropIndex == false) {
|
||||||
((SlabAllocator*)slab)->Release(gcRemoveLayerFuncObjPtr, allocationSize);
|
((SlabAllocator*)slab)->Release(gcRemoveLayerFuncObjPtr, allocationSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetSessionManager()->GetCurrentSessionContext()->GetTxnManager()->GetGcSession()->GcEndInnerTxn(false);
|
||||||
mtSessionThreadInfo->set_gc_session(NULL);
|
mtSessionThreadInfo->set_gc_session(NULL);
|
||||||
return allocationSize;
|
return allocationSize;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -582,6 +582,7 @@ Row* Table::RemoveKeyFromIndex(Row* row, Sentinel* sentinel, uint64_t tid, GcMan
|
||||||
#endif
|
#endif
|
||||||
currSentinel = ix->IndexRemove(&key, tid);
|
currSentinel = ix->IndexRemove(&key, tid);
|
||||||
MOT_ASSERT(currSentinel == sentinel);
|
MOT_ASSERT(currSentinel == sentinel);
|
||||||
|
MOT_ASSERT(currSentinel->GetCounter() == 0);
|
||||||
if (likely(gc != nullptr)) {
|
if (likely(gc != nullptr)) {
|
||||||
if (ix->GetIndexOrder() == IndexOrder::INDEX_ORDER_PRIMARY) {
|
if (ix->GetIndexOrder() == IndexOrder::INDEX_ORDER_PRIMARY) {
|
||||||
OutputRow = currSentinel->GetData();
|
OutputRow = currSentinel->GetData();
|
||||||
|
|
|
||||||
|
|
@ -413,6 +413,8 @@ RC TxnManager::RollbackInsert(Access* ac)
|
||||||
#endif
|
#endif
|
||||||
outputSen = index_->IndexRemove(&m_key, GetThdId());
|
outputSen = index_->IndexRemove(&m_key, GetThdId());
|
||||||
MOT_ASSERT(outputSen != nullptr);
|
MOT_ASSERT(outputSen != nullptr);
|
||||||
|
MOT_ASSERT(outputSen->GetCounter() == 0);
|
||||||
|
|
||||||
GcSessionRecordRcu(index_->GetIndexId(), outputSen, nullptr, Index::SentinelDtor, SENTINEL_SIZE(index_));
|
GcSessionRecordRcu(index_->GetIndexId(), outputSen, nullptr, Index::SentinelDtor, SENTINEL_SIZE(index_));
|
||||||
// If we are the owner of the key and insert on top of a deleted row,
|
// If we are the owner of the key and insert on top of a deleted row,
|
||||||
// lets check if we can reclaim the deleted row
|
// lets check if we can reclaim the deleted row
|
||||||
|
|
@ -820,6 +822,8 @@ void TxnInsertAction::CleanupOptimisticInsert(
|
||||||
MOT_ASSERT(pIndexInsertResult->GetCounter() == 0);
|
MOT_ASSERT(pIndexInsertResult->GetCounter() == 0);
|
||||||
Sentinel* outputSen = index_->IndexRemove(currentItem->m_key, m_manager->GetThdId());
|
Sentinel* outputSen = index_->IndexRemove(currentItem->m_key, m_manager->GetThdId());
|
||||||
MOT_ASSERT(outputSen != nullptr);
|
MOT_ASSERT(outputSen != nullptr);
|
||||||
|
MOT_ASSERT(outputSen->GetCounter() == 0);
|
||||||
|
|
||||||
m_manager->GcSessionRecordRcu(
|
m_manager->GcSessionRecordRcu(
|
||||||
index_->GetIndexId(), outputSen, nullptr, Index::SentinelDtor, SENTINEL_SIZE(index_));
|
index_->GetIndexId(), outputSen, nullptr, Index::SentinelDtor, SENTINEL_SIZE(index_));
|
||||||
m_manager->m_accessMgr->IncreaseTableStat(table);
|
m_manager->m_accessMgr->IncreaseTableStat(table);
|
||||||
|
|
|
||||||
|
|
@ -255,8 +255,14 @@ void StartRemoteStreaming(const LibpqrcvConnectParam *options)
|
||||||
stringlist_to_identifierstr(t_thrd.libwalreceiver_cxt.streamConn, options->publicationNames);
|
stringlist_to_identifierstr(t_thrd.libwalreceiver_cxt.streamConn, options->publicationNames);
|
||||||
appendStringInfo(&cmd, ", publication_names %s",
|
appendStringInfo(&cmd, ", publication_names %s",
|
||||||
PQescapeLiteral(t_thrd.libwalreceiver_cxt.streamConn, pubnames_str, strlen(pubnames_str)));
|
PQescapeLiteral(t_thrd.libwalreceiver_cxt.streamConn, pubnames_str, strlen(pubnames_str)));
|
||||||
appendStringInfoChar(&cmd, ')');
|
|
||||||
pfree(pubnames_str);
|
pfree(pubnames_str);
|
||||||
|
|
||||||
|
if (options->binary && PQserverVersion(t_thrd.libwalreceiver_cxt.streamConn) >= 90204) {
|
||||||
|
appendStringInfoString(&cmd, ", binary 'true'");
|
||||||
|
ereport(DEBUG5, (errmsg("append binary true")));
|
||||||
|
}
|
||||||
|
|
||||||
|
appendStringInfoChar(&cmd, ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
PGresult *res = libpqrcv_PQexec(cmd.data);
|
PGresult *res = libpqrcv_PQexec(cmd.data);
|
||||||
|
|
@ -420,7 +426,7 @@ void IdentifyRemoteSystem(bool checkRemote)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* identify remote mode, should do this after connect success. */
|
/* identify remote mode, should do this after connect success. */
|
||||||
static ServerMode IdentifyRemoteMode()
|
ServerMode IdentifyRemoteMode()
|
||||||
{
|
{
|
||||||
Assert(t_thrd.libwalreceiver_cxt.streamConn != NULL);
|
Assert(t_thrd.libwalreceiver_cxt.streamConn != NULL);
|
||||||
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
|
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
|
||||||
|
|
@ -443,7 +449,9 @@ static ServerMode IdentifyRemoteMode()
|
||||||
num_fields)));
|
num_fields)));
|
||||||
}
|
}
|
||||||
remoteMode = (ServerMode)pg_strtoint32(PQgetvalue(res, 0, 0));
|
remoteMode = (ServerMode)pg_strtoint32(PQgetvalue(res, 0, 0));
|
||||||
if (!t_thrd.walreceiver_cxt.AmWalReceiverForFailover && (!IS_PRIMARY_NORMAL(remoteMode)) &&
|
if (walrcv->conn_target != REPCONNTARGET_PUBLICATION &&
|
||||||
|
!t_thrd.walreceiver_cxt.AmWalReceiverForFailover &&
|
||||||
|
(!IS_PRIMARY_NORMAL(remoteMode)) &&
|
||||||
/* remoteMode of cascade standby is a standby */
|
/* remoteMode of cascade standby is a standby */
|
||||||
!t_thrd.xlog_cxt.is_cascade_standby && !IS_SHARED_STORAGE_MODE) {
|
!t_thrd.xlog_cxt.is_cascade_standby && !IS_SHARED_STORAGE_MODE) {
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,7 @@ void LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResourceOwner tmpOwner = t_thrd.utils_cxt.CurrentResourceOwner;
|
||||||
/* cast so we get a warning when new rmgrs are added */
|
/* cast so we get a warning when new rmgrs are added */
|
||||||
switch ((RmgrIds)XLogRecGetRmid(record)) {
|
switch ((RmgrIds)XLogRecGetRmid(record)) {
|
||||||
/*
|
/*
|
||||||
|
|
@ -257,6 +258,7 @@ void LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
t_thrd.utils_cxt.CurrentResourceOwner = tmpOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
#include "replication/logicalproto.h"
|
#include "replication/logicalproto.h"
|
||||||
#include "utils/builtins.h"
|
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
|
@ -28,7 +27,7 @@
|
||||||
static const int LOGICALREP_IS_REPLICA_IDENTITY = 1;
|
static const int LOGICALREP_IS_REPLICA_IDENTITY = 1;
|
||||||
|
|
||||||
static void logicalrep_write_attrs(StringInfo out, Relation rel);
|
static void logicalrep_write_attrs(StringInfo out, Relation rel);
|
||||||
static void logicalrep_write_tuple(StringInfo out, Relation rel, HeapTuple tuple);
|
static void logicalrep_write_tuple(StringInfo out, Relation rel, HeapTuple tuple, bool binary);
|
||||||
|
|
||||||
static void logicalrep_read_attrs(StringInfo in, LogicalRepRelation *rel);
|
static void logicalrep_read_attrs(StringInfo in, LogicalRepRelation *rel);
|
||||||
static void logicalrep_read_tuple(StringInfo in, LogicalRepTupleData *tuple);
|
static void logicalrep_read_tuple(StringInfo in, LogicalRepTupleData *tuple);
|
||||||
|
|
@ -115,7 +114,7 @@ void logicalrep_write_origin(StringInfo out, const char *origin, XLogRecPtr orig
|
||||||
/*
|
/*
|
||||||
* Write INSERT to the output stream.
|
* Write INSERT to the output stream.
|
||||||
*/
|
*/
|
||||||
void logicalrep_write_insert(StringInfo out, Relation rel, HeapTuple newtuple)
|
void logicalrep_write_insert(StringInfo out, Relation rel, HeapTuple newtuple, bool binary)
|
||||||
{
|
{
|
||||||
pq_sendbyte(out, 'I'); /* action INSERT */
|
pq_sendbyte(out, 'I'); /* action INSERT */
|
||||||
|
|
||||||
|
|
@ -123,7 +122,7 @@ void logicalrep_write_insert(StringInfo out, Relation rel, HeapTuple newtuple)
|
||||||
pq_sendint32(out, RelationGetRelid(rel));
|
pq_sendint32(out, RelationGetRelid(rel));
|
||||||
|
|
||||||
pq_sendbyte(out, 'N'); /* new tuple follows */
|
pq_sendbyte(out, 'N'); /* new tuple follows */
|
||||||
logicalrep_write_tuple(out, rel, newtuple);
|
logicalrep_write_tuple(out, rel, newtuple, binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -151,7 +150,7 @@ LogicalRepRelId logicalrep_read_insert(StringInfo in, LogicalRepTupleData *newtu
|
||||||
/*
|
/*
|
||||||
* Write UPDATE to the output stream.
|
* Write UPDATE to the output stream.
|
||||||
*/
|
*/
|
||||||
void logicalrep_write_update(StringInfo out, Relation rel, HeapTuple oldtuple, HeapTuple newtuple)
|
void logicalrep_write_update(StringInfo out, Relation rel, HeapTuple oldtuple, HeapTuple newtuple, bool binary)
|
||||||
{
|
{
|
||||||
pq_sendbyte(out, 'U'); /* action UPDATE */
|
pq_sendbyte(out, 'U'); /* action UPDATE */
|
||||||
|
|
||||||
|
|
@ -166,11 +165,11 @@ void logicalrep_write_update(StringInfo out, Relation rel, HeapTuple oldtuple, H
|
||||||
pq_sendbyte(out, 'O'); /* old tuple follows */
|
pq_sendbyte(out, 'O'); /* old tuple follows */
|
||||||
else
|
else
|
||||||
pq_sendbyte(out, 'K'); /* old key follows */
|
pq_sendbyte(out, 'K'); /* old key follows */
|
||||||
logicalrep_write_tuple(out, rel, oldtuple);
|
logicalrep_write_tuple(out, rel, oldtuple, binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
pq_sendbyte(out, 'N'); /* new tuple follows */
|
pq_sendbyte(out, 'N'); /* new tuple follows */
|
||||||
logicalrep_write_tuple(out, rel, newtuple);
|
logicalrep_write_tuple(out, rel, newtuple, binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -213,7 +212,7 @@ LogicalRepRelId logicalrep_read_update(StringInfo in, bool *has_oldtuple, Logica
|
||||||
/*
|
/*
|
||||||
* Write DELETE to the output stream.
|
* Write DELETE to the output stream.
|
||||||
*/
|
*/
|
||||||
void logicalrep_write_delete(StringInfo out, Relation rel, HeapTuple oldtuple)
|
void logicalrep_write_delete(StringInfo out, Relation rel, HeapTuple oldtuple, bool binary)
|
||||||
{
|
{
|
||||||
char relreplident = RelationGetRelReplident(rel);
|
char relreplident = RelationGetRelReplident(rel);
|
||||||
Assert(relreplident == REPLICA_IDENTITY_DEFAULT ||
|
Assert(relreplident == REPLICA_IDENTITY_DEFAULT ||
|
||||||
|
|
@ -229,7 +228,7 @@ void logicalrep_write_delete(StringInfo out, Relation rel, HeapTuple oldtuple)
|
||||||
else
|
else
|
||||||
pq_sendbyte(out, 'K'); /* old key follows */
|
pq_sendbyte(out, 'K'); /* old key follows */
|
||||||
|
|
||||||
logicalrep_write_tuple(out, rel, oldtuple);
|
logicalrep_write_tuple(out, rel, oldtuple, binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -344,7 +343,7 @@ void logicalrep_read_typ(StringInfo in, LogicalRepTyp *ltyp)
|
||||||
/*
|
/*
|
||||||
* Write a tuple to the outputstream, in the most efficient format possible.
|
* Write a tuple to the outputstream, in the most efficient format possible.
|
||||||
*/
|
*/
|
||||||
static void logicalrep_write_tuple(StringInfo out, Relation rel, HeapTuple tuple)
|
static void logicalrep_write_tuple(StringInfo out, Relation rel, HeapTuple tuple, bool binary)
|
||||||
{
|
{
|
||||||
TupleDesc desc;
|
TupleDesc desc;
|
||||||
Datum values[MaxTupleAttributeNumber];
|
Datum values[MaxTupleAttributeNumber];
|
||||||
|
|
@ -371,7 +370,6 @@ static void logicalrep_write_tuple(StringInfo out, Relation rel, HeapTuple tuple
|
||||||
HeapTuple typtup;
|
HeapTuple typtup;
|
||||||
Form_pg_type typclass;
|
Form_pg_type typclass;
|
||||||
Form_pg_attribute att = desc->attrs[i];
|
Form_pg_attribute att = desc->attrs[i];
|
||||||
char *outputstr;
|
|
||||||
|
|
||||||
/* skip dropped columns */
|
/* skip dropped columns */
|
||||||
if (att->attisdropped || GetGeneratedCol(desc, i)) {
|
if (att->attisdropped || GetGeneratedCol(desc, i)) {
|
||||||
|
|
@ -379,7 +377,7 @@ static void logicalrep_write_tuple(StringInfo out, Relation rel, HeapTuple tuple
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isnull[i]) {
|
if (isnull[i]) {
|
||||||
pq_sendbyte(out, 'n'); /* null column */
|
pq_sendbyte(out, LOGICALREP_COLUMN_NULL); /* null column */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -388,7 +386,28 @@ static void logicalrep_write_tuple(StringInfo out, Relation rel, HeapTuple tuple
|
||||||
elog(ERROR, "cache lookup failed for type %u", att->atttypid);
|
elog(ERROR, "cache lookup failed for type %u", att->atttypid);
|
||||||
typclass = (Form_pg_type)GETSTRUCT(typtup);
|
typclass = (Form_pg_type)GETSTRUCT(typtup);
|
||||||
|
|
||||||
pq_sendbyte(out, 't'); /* 'text' data follows */
|
/*
|
||||||
|
* Send in binary if requested and type has suitable send function.
|
||||||
|
*/
|
||||||
|
if (binary && OidIsValid(typclass->typsend)) {
|
||||||
|
bytea* outputbytes = NULL;
|
||||||
|
pq_sendbyte(out, LOGICALREP_COLUMN_BINARY);
|
||||||
|
if (!typclass->typbyval && typclass->typlen == -1) {
|
||||||
|
/* definitely detoasted Datum */
|
||||||
|
Datum val = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
|
||||||
|
outputbytes = OidSendFunctionCall(typclass->typsend, val);
|
||||||
|
} else {
|
||||||
|
outputbytes = OidSendFunctionCall(typclass->typsend, values[i]);
|
||||||
|
}
|
||||||
|
int len = VARSIZE(outputbytes) - VARHDRSZ;
|
||||||
|
pq_sendint(out, len, 4); /* length */
|
||||||
|
pq_sendbytes(out, VARDATA(outputbytes), len); /* data */
|
||||||
|
if (outputbytes != NULL) {
|
||||||
|
pfree(outputbytes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
char* outputstr = NULL;
|
||||||
|
pq_sendbyte(out, LOGICALREP_COLUMN_TEXT);
|
||||||
if (!typclass->typbyval && typclass->typlen == -1) {
|
if (!typclass->typbyval && typclass->typlen == -1) {
|
||||||
/* definitely detoasted Datum */
|
/* definitely detoasted Datum */
|
||||||
Datum val = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
|
Datum val = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
|
||||||
|
|
@ -397,52 +416,61 @@ static void logicalrep_write_tuple(StringInfo out, Relation rel, HeapTuple tuple
|
||||||
outputstr = OidOutputFunctionCall(typclass->typoutput, values[i]);
|
outputstr = OidOutputFunctionCall(typclass->typoutput, values[i]);
|
||||||
}
|
}
|
||||||
pq_sendcountedtext(out, outputstr, strlen(outputstr), false);
|
pq_sendcountedtext(out, outputstr, strlen(outputstr), false);
|
||||||
|
if (outputstr != NULL) {
|
||||||
pfree(outputstr);
|
pfree(outputstr);
|
||||||
|
}
|
||||||
|
}
|
||||||
ReleaseSysCache(typtup);
|
ReleaseSysCache(typtup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read tuple in remote format from stream.
|
* Read tuple in logical replication format from stream.
|
||||||
*
|
|
||||||
* The returned tuple points into the input stringinfo.
|
|
||||||
*/
|
*/
|
||||||
static void logicalrep_read_tuple(StringInfo in, LogicalRepTupleData *tuple)
|
static void logicalrep_read_tuple(StringInfo in, LogicalRepTupleData *tuple)
|
||||||
{
|
{
|
||||||
uint16 i;
|
|
||||||
uint16 natts;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
/* Get number of attributes. */
|
/* Get number of attributes. */
|
||||||
natts = pq_getmsgint(in, sizeof(uint16));
|
uint16 natts = pq_getmsgint(in, sizeof(uint16));
|
||||||
|
|
||||||
rc = memset_s(tuple->changed, sizeof(tuple->changed), 0, sizeof(tuple->changed));
|
/* Allocate space for per-column values; zero out unused StringInfoDatas */
|
||||||
securec_check(rc, "", "");
|
tuple->colvalues = (StringInfoData *) palloc0(natts * sizeof(StringInfoData));
|
||||||
|
tuple->colstatus = (char *) palloc(natts * sizeof(char));
|
||||||
tuple->ncols = natts;
|
tuple->ncols = natts;
|
||||||
|
|
||||||
/* Read the data */
|
/* Read the data */
|
||||||
for (i = 0; i < natts; i++) {
|
for (uint16 i = 0; i < natts; i++) {
|
||||||
char kind;
|
char kind = pq_getmsgbyte(in);
|
||||||
|
tuple->colstatus[i] = kind;
|
||||||
kind = pq_getmsgbyte(in);
|
uint32 len;
|
||||||
|
StringInfo value = &tuple->colvalues[i];
|
||||||
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case 'n': /* null */
|
case LOGICALREP_COLUMN_NULL: /* null */
|
||||||
tuple->values[i] = NULL;
|
/* nothing more to do */
|
||||||
tuple->changed[i] = true;
|
|
||||||
break;
|
break;
|
||||||
case 't': { /* text formatted value */
|
case LOGICALREP_COLUMN_TEXT:
|
||||||
uint32 len;
|
|
||||||
tuple->changed[i] = true;
|
|
||||||
|
|
||||||
len = pq_getmsgint(in, sizeof(uint32)); /* read length */
|
len = pq_getmsgint(in, sizeof(uint32)); /* read length */
|
||||||
|
|
||||||
/* and data */
|
/* and data */
|
||||||
tuple->values[i] = (char *)palloc(len + 1);
|
value->data = (char *) palloc((len + 1) * sizeof(char));
|
||||||
pq_copymsgbytes(in, tuple->values[i], len);
|
pq_copymsgbytes(in, value->data, len);
|
||||||
tuple->values[i][len] = '\0';
|
value->data[len] = '\0';
|
||||||
|
/* make StringInfo fully valid */
|
||||||
|
value->len = len;
|
||||||
|
value->cursor = 0;
|
||||||
|
value->maxlen = len;
|
||||||
|
break;
|
||||||
|
case LOGICALREP_COLUMN_BINARY:
|
||||||
|
len = pq_getmsgint(in, sizeof(uint32)); /* read length */
|
||||||
|
|
||||||
|
/* and data */
|
||||||
|
value->data = (char *)palloc0((len + 1) * sizeof(char));
|
||||||
|
pq_copymsgbytes(in, value->data, len);
|
||||||
|
/* make StringInfo fully valid */
|
||||||
|
value->len = len;
|
||||||
|
value->cursor = 0;
|
||||||
|
value->maxlen = len;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized data representation type '%c'", kind);
|
elog(ERROR, "unrecognized data representation type '%c'", kind);
|
||||||
break;
|
break;
|
||||||
|
|
@ -451,7 +479,7 @@ static void logicalrep_read_tuple(StringInfo in, LogicalRepTupleData *tuple)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write relation attributes to the stream.
|
* Write relation attributes metadata to the stream.
|
||||||
*/
|
*/
|
||||||
static void logicalrep_write_attrs(StringInfo out, Relation rel)
|
static void logicalrep_write_attrs(StringInfo out, Relation rel)
|
||||||
{
|
{
|
||||||
|
|
@ -504,7 +532,7 @@ static void logicalrep_write_attrs(StringInfo out, Relation rel)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read relation attribute names from the stream.
|
* Read relation attribute metadata from the stream.
|
||||||
*/
|
*/
|
||||||
static void logicalrep_read_attrs(StringInfo in, LogicalRepRelation *rel)
|
static void logicalrep_read_attrs(StringInfo in, LogicalRepRelation *rel)
|
||||||
{
|
{
|
||||||
|
|
@ -573,3 +601,25 @@ static const char *logicalrep_read_namespace(StringInfo in)
|
||||||
|
|
||||||
return nspname;
|
return nspname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write conninfo to the output stream.
|
||||||
|
*/
|
||||||
|
void logicalrep_write_conninfo(StringInfo out, char* conninfo)
|
||||||
|
{
|
||||||
|
pq_sendbyte(out, 'S'); /* action */
|
||||||
|
|
||||||
|
pq_writestring(out, conninfo); /* conninfo follows */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read conninfo from stream.
|
||||||
|
*/
|
||||||
|
void logicalrep_read_conninfo(StringInfo in, char** conninfo)
|
||||||
|
{
|
||||||
|
const char* conninfoTemp = pq_getmsgstring(in);
|
||||||
|
size_t conninfoLen = strlen(conninfoTemp) + 1;
|
||||||
|
*conninfo = (char*)palloc(conninfoLen);
|
||||||
|
int rc = strcpy_s(*conninfo, conninfoLen, conninfoTemp);
|
||||||
|
securec_check(rc, "", "");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@
|
||||||
#include "catalog/pg_partition_fn.h"
|
#include "catalog/pg_partition_fn.h"
|
||||||
|
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
|
#include "commands/subscriptioncmds.h"
|
||||||
|
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "executor/node/nodeModifyTable.h"
|
#include "executor/node/nodeModifyTable.h"
|
||||||
|
|
@ -111,6 +112,8 @@ static void store_flush_position(XLogRecPtr remote_lsn);
|
||||||
static void reread_subscription(void);
|
static void reread_subscription(void);
|
||||||
static void ApplyWorkerProcessMsg(char type, StringInfo s, XLogRecPtr *lastRcv);
|
static void ApplyWorkerProcessMsg(char type, StringInfo s, XLogRecPtr *lastRcv);
|
||||||
static void apply_dispatch(StringInfo s);
|
static void apply_dispatch(StringInfo s);
|
||||||
|
static void apply_handle_conninfo(StringInfo s);
|
||||||
|
static void UpdateConninfo(char* standbysInfo);
|
||||||
|
|
||||||
/* SIGHUP: set flag to re-read config file at next convenient time */
|
/* SIGHUP: set flag to re-read config file at next convenient time */
|
||||||
static void LogicalrepWorkerSighub(SIGNAL_ARGS)
|
static void LogicalrepWorkerSighub(SIGNAL_ARGS)
|
||||||
|
|
@ -118,18 +121,6 @@ static void LogicalrepWorkerSighub(SIGNAL_ARGS)
|
||||||
t_thrd.applyworker_cxt.got_SIGHUP = true;
|
t_thrd.applyworker_cxt.got_SIGHUP = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SIGTERM: time to die */
|
|
||||||
static void LogicalrepWorkerSigterm(SIGNAL_ARGS)
|
|
||||||
{
|
|
||||||
int saveErrno = errno;
|
|
||||||
|
|
||||||
t_thrd.applyworker_cxt.got_SIGTERM = true;
|
|
||||||
if (t_thrd.proc)
|
|
||||||
SetLatch(&t_thrd.proc->procLatch);
|
|
||||||
|
|
||||||
errno = saveErrno;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure that we started local transaction.
|
* Make sure that we started local transaction.
|
||||||
*
|
*
|
||||||
|
|
@ -265,11 +256,11 @@ static void slot_store_error_callback(void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store data in C string form into slot.
|
* Store tuple data into slot.
|
||||||
* This is similar to BuildTupleFromCStrings but TupleTableSlot fits our
|
*
|
||||||
* use better.
|
* Incoming data can be either text or binary format.
|
||||||
*/
|
*/
|
||||||
static void slot_store_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel, char **values)
|
static void slot_store_data(TupleTableSlot *slot, LogicalRepRelMapEntry *rel, LogicalRepTupleData *tupleData)
|
||||||
{
|
{
|
||||||
int natts = slot->tts_tupleDescriptor->natts;
|
int natts = slot->tts_tupleDescriptor->natts;
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -286,19 +277,52 @@ static void slot_store_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel
|
||||||
errcallback.previous = t_thrd.log_cxt.error_context_stack;
|
errcallback.previous = t_thrd.log_cxt.error_context_stack;
|
||||||
t_thrd.log_cxt.error_context_stack = &errcallback;
|
t_thrd.log_cxt.error_context_stack = &errcallback;
|
||||||
|
|
||||||
/* Call the "in" function for each non-dropped attribute */
|
/* Call the "in" function for each non-dropped, non-null attribute */
|
||||||
for (i = 0; i < natts; i++) {
|
for (i = 0; i < natts; i++) {
|
||||||
Form_pg_attribute att = slot->tts_tupleDescriptor->attrs[i];
|
Form_pg_attribute att = slot->tts_tupleDescriptor->attrs[i];
|
||||||
int remoteattnum = rel->attrmap[i];
|
int remoteattnum = rel->attrmap[i];
|
||||||
|
|
||||||
|
if (!att->attisdropped && remoteattnum >= 0) {
|
||||||
|
StringInfo colvalue = &tupleData->colvalues[remoteattnum];
|
||||||
|
errarg.remote_attnum = remoteattnum;
|
||||||
|
|
||||||
|
if (tupleData->colstatus[remoteattnum] == LOGICALREP_COLUMN_TEXT) {
|
||||||
Oid typinput;
|
Oid typinput;
|
||||||
Oid typioparam;
|
Oid typioparam;
|
||||||
|
|
||||||
if (!att->attisdropped && remoteattnum >= 0 && values[remoteattnum] != NULL) {
|
|
||||||
errarg.remote_attnum = remoteattnum;
|
|
||||||
|
|
||||||
getTypeInputInfo(att->atttypid, &typinput, &typioparam);
|
getTypeInputInfo(att->atttypid, &typinput, &typioparam);
|
||||||
slot->tts_values[i] = OidInputFunctionCall(typinput, values[remoteattnum], typioparam, att->atttypmod);
|
slot->tts_values[i] = OidInputFunctionCall(typinput, colvalue->data, typioparam, att->atttypmod);
|
||||||
slot->tts_isnull[i] = false;
|
slot->tts_isnull[i] = false;
|
||||||
|
} else if (tupleData->colstatus[remoteattnum] == LOGICALREP_COLUMN_BINARY) {
|
||||||
|
Oid typreceive;
|
||||||
|
Oid typioparam;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In some code paths we may be asked to re-parse the same
|
||||||
|
* tuple data. Reset the StringInfo's cursor so that works.
|
||||||
|
*/
|
||||||
|
colvalue->cursor = 0;
|
||||||
|
|
||||||
|
getTypeBinaryInputInfo(att->atttypid, &typreceive, &typioparam);
|
||||||
|
slot->tts_values[i] = OidReceiveFunctionCall(typreceive, colvalue, typioparam, att->atttypmod);
|
||||||
|
|
||||||
|
/* Trouble if it didn't eat the whole buffer */
|
||||||
|
if (colvalue->cursor != colvalue->len) {
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
||||||
|
errmsg("incorrect binary data format in logical replication column %d",
|
||||||
|
remoteattnum + 1)));
|
||||||
|
}
|
||||||
|
slot->tts_isnull[i] = false;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* NULL value from remote. (We don't expect to see
|
||||||
|
* LOGICALREP_COLUMN_UNCHANGED here, but if we do, treat it as
|
||||||
|
* NULL.)
|
||||||
|
*/
|
||||||
|
slot->tts_values[i] = (Datum) 0;
|
||||||
|
slot->tts_isnull[i] = true;
|
||||||
|
}
|
||||||
|
/* Reset attnum for error callback */
|
||||||
errarg.remote_attnum = -1;
|
errarg.remote_attnum = -1;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
|
|
@ -318,18 +342,19 @@ static void slot_store_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Replace selected columns with user data provided as C strings.
|
* Replace updated columns with data from the LogicalRepTupleData struct.
|
||||||
* This is somewhat similar to heap_modify_tuple but also calls the type
|
* This is somewhat similar to heap_modify_tuple but also calls the type
|
||||||
* input functions on the user data.
|
* input functions on the user data.
|
||||||
* "slot" is filled with a copy of the tuple in "srcslot", with
|
*
|
||||||
* columns selected by the "replaces" array replaced with data values
|
* "slot" is filled with a copy of the tuple in "srcslot", replacing
|
||||||
* from "values".
|
* columns provided in "tupleData" and leaving others as-is.
|
||||||
|
*
|
||||||
* Caution: unreplaced pass-by-ref columns in "slot" will point into the
|
* Caution: unreplaced pass-by-ref columns in "slot" will point into the
|
||||||
* storage for "srcslot". This is OK for current usage, but someday we may
|
* storage for "srcslot". This is OK for current usage, but someday we may
|
||||||
* need to materialize "slot" at the end to make it independent of "srcslot".
|
* need to materialize "slot" at the end to make it independent of "srcslot".
|
||||||
*/
|
*/
|
||||||
static void slot_modify_cstrings(TupleTableSlot *slot, TupleTableSlot *srcslot, LogicalRepRelMapEntry *rel,
|
static void slot_modify_data(TupleTableSlot *slot, TupleTableSlot *srcslot, LogicalRepRelMapEntry *rel,
|
||||||
char **values, const bool *replaces)
|
LogicalRepTupleData *tupleData)
|
||||||
{
|
{
|
||||||
int natts = slot->tts_tupleDescriptor->natts;
|
int natts = slot->tts_tupleDescriptor->natts;
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -364,24 +389,48 @@ static void slot_modify_cstrings(TupleTableSlot *slot, TupleTableSlot *srcslot,
|
||||||
Form_pg_attribute att = slot->tts_tupleDescriptor->attrs[i];
|
Form_pg_attribute att = slot->tts_tupleDescriptor->attrs[i];
|
||||||
int remoteattnum = rel->attrmap[i];
|
int remoteattnum = rel->attrmap[i];
|
||||||
|
|
||||||
if (remoteattnum < 0 || !replaces[remoteattnum]) {
|
if (remoteattnum < 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values[remoteattnum] != NULL) {
|
if (tupleData->colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) {
|
||||||
|
StringInfo colvalue = &tupleData->colvalues[remoteattnum];
|
||||||
|
errarg.remote_attnum = remoteattnum;
|
||||||
|
|
||||||
|
if (tupleData->colstatus[remoteattnum] == LOGICALREP_COLUMN_TEXT) {
|
||||||
Oid typinput;
|
Oid typinput;
|
||||||
Oid typioparam;
|
Oid typioparam;
|
||||||
|
|
||||||
errarg.remote_attnum = remoteattnum;
|
|
||||||
|
|
||||||
getTypeInputInfo(att->atttypid, &typinput, &typioparam);
|
getTypeInputInfo(att->atttypid, &typinput, &typioparam);
|
||||||
slot->tts_values[i] = OidInputFunctionCall(typinput, values[remoteattnum], typioparam, att->atttypmod);
|
slot->tts_values[i] = OidInputFunctionCall(typinput, colvalue->data, typioparam, att->atttypmod);
|
||||||
|
slot->tts_isnull[i] = false;
|
||||||
|
} else if (tupleData->colstatus[remoteattnum] == LOGICALREP_COLUMN_BINARY) {
|
||||||
|
Oid typreceive;
|
||||||
|
Oid typioparam;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In some code paths we may be asked to re-parse the same
|
||||||
|
* tuple data. Reset the StringInfo's cursor so that works.
|
||||||
|
*/
|
||||||
|
colvalue->cursor = 0;
|
||||||
|
|
||||||
|
getTypeBinaryInputInfo(att->atttypid, &typreceive, &typioparam);
|
||||||
|
slot->tts_values[i] = OidReceiveFunctionCall(typreceive, colvalue, typioparam, att->atttypmod);
|
||||||
|
|
||||||
|
/* Trouble if it didn't eat the whole buffer */
|
||||||
|
if (colvalue->cursor != colvalue->len) {
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
||||||
|
errmsg("incorrect binary data format in logical replication column %d", remoteattnum + 1)));
|
||||||
|
}
|
||||||
slot->tts_isnull[i] = false;
|
slot->tts_isnull[i] = false;
|
||||||
errarg.remote_attnum = -1;
|
|
||||||
} else {
|
} else {
|
||||||
|
/* must be LOGICALREP_COLUMN_NULL */
|
||||||
slot->tts_values[i] = (Datum) 0;
|
slot->tts_values[i] = (Datum) 0;
|
||||||
slot->tts_isnull[i] = true;
|
slot->tts_isnull[i] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errarg.remote_attnum = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pop the error context stack */
|
/* Pop the error context stack */
|
||||||
|
|
@ -516,7 +565,7 @@ static void apply_handle_insert(StringInfo s)
|
||||||
PushActiveSnapshot(GetTransactionSnapshot());
|
PushActiveSnapshot(GetTransactionSnapshot());
|
||||||
/* Process and store remote tuple in the slot */
|
/* Process and store remote tuple in the slot */
|
||||||
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
|
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
|
||||||
slot_store_cstrings(remoteslot, rel, newtup.values);
|
slot_store_data(remoteslot, rel, &newtup);
|
||||||
slot_fill_defaults(rel, estate, remoteslot);
|
slot_fill_defaults(rel, estate, remoteslot);
|
||||||
MemoryContextSwitchTo(oldctx);
|
MemoryContextSwitchTo(oldctx);
|
||||||
|
|
||||||
|
|
@ -646,7 +695,7 @@ static void apply_handle_update(StringInfo s)
|
||||||
int remoteattnum = rel->attrmap[i];
|
int remoteattnum = rel->attrmap[i];
|
||||||
if (!att->attisdropped && remoteattnum >= 0) {
|
if (!att->attisdropped && remoteattnum >= 0) {
|
||||||
Assert(remoteattnum < newtup.ncols);
|
Assert(remoteattnum < newtup.ncols);
|
||||||
if (newtup.changed[i]) {
|
if (newtup.colstatus[i] != LOGICALREP_COLUMN_UNCHANGED) {
|
||||||
target_rte->updatedCols = bms_add_member(target_rte->updatedCols,
|
target_rte->updatedCols = bms_add_member(target_rte->updatedCols,
|
||||||
i + 1 - FirstLowInvalidHeapAttributeNumber);
|
i + 1 - FirstLowInvalidHeapAttributeNumber);
|
||||||
}
|
}
|
||||||
|
|
@ -660,7 +709,7 @@ static void apply_handle_update(StringInfo s)
|
||||||
|
|
||||||
/* Build the search tuple. */
|
/* Build the search tuple. */
|
||||||
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
|
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
|
||||||
slot_store_cstrings(remoteslot, rel, has_oldtup ? oldtup.values : newtup.values);
|
slot_store_data(remoteslot, rel, has_oldtup ? &oldtup : &newtup);
|
||||||
MemoryContextSwitchTo(oldctx);
|
MemoryContextSwitchTo(oldctx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -683,7 +732,7 @@ static void apply_handle_update(StringInfo s)
|
||||||
if (found) {
|
if (found) {
|
||||||
/* Process and store remote tuple in the slot */
|
/* Process and store remote tuple in the slot */
|
||||||
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
|
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
|
||||||
slot_modify_cstrings(remoteslot, localslot, rel, newtup.values, newtup.changed);
|
slot_modify_data(remoteslot, localslot, rel, &newtup);
|
||||||
MemoryContextSwitchTo(oldctx);
|
MemoryContextSwitchTo(oldctx);
|
||||||
|
|
||||||
EvalPlanQualSetSlot(&epqstate, remoteslot);
|
EvalPlanQualSetSlot(&epqstate, remoteslot);
|
||||||
|
|
@ -748,7 +797,7 @@ static void apply_handle_delete(StringInfo s)
|
||||||
|
|
||||||
/* Find the tuple using the replica identity index. */
|
/* Find the tuple using the replica identity index. */
|
||||||
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
|
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
|
||||||
slot_store_cstrings(remoteslot, rel, oldtup.values);
|
slot_store_data(remoteslot, rel, &oldtup);
|
||||||
MemoryContextSwitchTo(oldctx);
|
MemoryContextSwitchTo(oldctx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -824,6 +873,9 @@ static void apply_dispatch(StringInfo s)
|
||||||
case 'O':
|
case 'O':
|
||||||
apply_handle_origin(s);
|
apply_handle_origin(s);
|
||||||
break;
|
break;
|
||||||
|
case 'S':
|
||||||
|
apply_handle_conninfo(s);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION),
|
ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION),
|
||||||
errmsg("invalid logical replication message type \"%c\"", action)));
|
errmsg("invalid logical replication message type \"%c\"", action)));
|
||||||
|
|
@ -1004,13 +1056,15 @@ static void ApplyLoop(void)
|
||||||
/* mark as idle, before starting to loop */
|
/* mark as idle, before starting to loop */
|
||||||
pgstat_report_activity(STATE_IDLE, NULL);
|
pgstat_report_activity(STATE_IDLE, NULL);
|
||||||
|
|
||||||
while (!t_thrd.applyworker_cxt.got_SIGTERM) {
|
for (;;) {
|
||||||
MemoryContextSwitchTo(t_thrd.applyworker_cxt.messageContext);
|
MemoryContextSwitchTo(t_thrd.applyworker_cxt.messageContext);
|
||||||
|
|
||||||
int len;
|
int len;
|
||||||
char *buf = NULL;
|
char *buf = NULL;
|
||||||
unsigned char type;
|
unsigned char type;
|
||||||
|
|
||||||
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
/* Wait a while for data to arrive */
|
/* Wait a while for data to arrive */
|
||||||
if ((WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_receive(NAPTIME_PER_CYCLE, &type, &buf, &len)) {
|
if ((WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_receive(NAPTIME_PER_CYCLE, &type, &buf, &len)) {
|
||||||
StringInfoData s;
|
StringInfoData s;
|
||||||
|
|
@ -1224,6 +1278,17 @@ static void reread_subscription(void)
|
||||||
proc_exit(0);
|
proc_exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exit if any parameter that affects the remote connection was changed.
|
||||||
|
* The launcher will start a new worker.
|
||||||
|
*/
|
||||||
|
if (strcmp(newsub->name, t_thrd.applyworker_cxt.mySubscription->name) != 0 ||
|
||||||
|
newsub->binary != t_thrd.applyworker_cxt.mySubscription->binary) {
|
||||||
|
ereport(LOG, (errmsg("logical replication apply worker for subscription \"%s\" "
|
||||||
|
"will restart because of a parameter change", t_thrd.applyworker_cxt.mySubscription->name)));
|
||||||
|
proc_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
/* !slotname should never happen when enabled is true. */
|
/* !slotname should never happen when enabled is true. */
|
||||||
Assert(newsub->slotname);
|
Assert(newsub->slotname);
|
||||||
|
|
||||||
|
|
@ -1296,7 +1361,7 @@ void ApplyWorkerMain()
|
||||||
*/
|
*/
|
||||||
gspqsignal(SIGHUP, LogicalrepWorkerSighub);
|
gspqsignal(SIGHUP, LogicalrepWorkerSighub);
|
||||||
gspqsignal(SIGINT, StatementCancelHandler);
|
gspqsignal(SIGINT, StatementCancelHandler);
|
||||||
gspqsignal(SIGTERM, LogicalrepWorkerSigterm);
|
gspqsignal(SIGTERM, die);
|
||||||
|
|
||||||
gspqsignal(SIGQUIT, quickdie);
|
gspqsignal(SIGQUIT, quickdie);
|
||||||
gspqsignal(SIGALRM, handle_sig_alarm);
|
gspqsignal(SIGALRM, handle_sig_alarm);
|
||||||
|
|
@ -1422,14 +1487,9 @@ void ApplyWorkerMain()
|
||||||
|
|
||||||
CommitTransactionCommand();
|
CommitTransactionCommand();
|
||||||
|
|
||||||
char *decryptConnInfo = DecryptConninfo(t_thrd.applyworker_cxt.mySubscription->conninfo);
|
if (!AttemptConnectPublisher(t_thrd.applyworker_cxt.mySubscription->conninfo,
|
||||||
bool connectSuccess = (WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_connect(decryptConnInfo, NULL,
|
t_thrd.applyworker_cxt.mySubscription->name, true)) {
|
||||||
t_thrd.applyworker_cxt.mySubscription->name, -1);
|
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("Failed to connect to publisher.")));
|
||||||
rc = memset_s(decryptConnInfo, strlen(decryptConnInfo), 0, strlen(decryptConnInfo));
|
|
||||||
securec_check(rc, "", "");
|
|
||||||
pfree_ext(decryptConnInfo);
|
|
||||||
if (!connectSuccess) {
|
|
||||||
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("could not connect to the publisher")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1447,6 +1507,7 @@ void ApplyWorkerMain()
|
||||||
options.slotname = t_thrd.applyworker_cxt.mySubscription->slotname;
|
options.slotname = t_thrd.applyworker_cxt.mySubscription->slotname;
|
||||||
options.protoVersion = LOGICALREP_PROTO_VERSION_NUM;
|
options.protoVersion = LOGICALREP_PROTO_VERSION_NUM;
|
||||||
options.publicationNames = t_thrd.applyworker_cxt.mySubscription->publications;
|
options.publicationNames = t_thrd.applyworker_cxt.mySubscription->publications;
|
||||||
|
options.binary = t_thrd.applyworker_cxt.mySubscription->binary;
|
||||||
|
|
||||||
/* Start streaming from the slot. */
|
/* Start streaming from the slot. */
|
||||||
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_startstreaming(&options);
|
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_startstreaming(&options);
|
||||||
|
|
@ -1595,3 +1656,72 @@ char* DefListToString(const List *defList)
|
||||||
return buf.data;
|
return buf.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle conninfo update message.
|
||||||
|
*/
|
||||||
|
static void apply_handle_conninfo(StringInfo s)
|
||||||
|
{
|
||||||
|
char* standbysInfo = NULL;
|
||||||
|
logicalrep_read_conninfo(s, &standbysInfo);
|
||||||
|
UpdateConninfo(standbysInfo);
|
||||||
|
pfree_ext(standbysInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UpdateConninfo(char* standbysInfo)
|
||||||
|
{
|
||||||
|
Relation rel;
|
||||||
|
bool nulls[Natts_pg_subscription];
|
||||||
|
bool replaces[Natts_pg_subscription];
|
||||||
|
Datum values[Natts_pg_subscription];
|
||||||
|
HeapTuple tup;
|
||||||
|
Subscription* sub = t_thrd.applyworker_cxt.mySubscription;
|
||||||
|
Oid subid = sub->oid;
|
||||||
|
|
||||||
|
StartTransactionCommand();
|
||||||
|
rel = heap_open(SubscriptionRelationId, RowExclusiveLock);
|
||||||
|
/* Fetch the existing tuple. */
|
||||||
|
tup = SearchSysCacheCopy2(SUBSCRIPTIONNAME, u_sess->proc_cxt.MyDatabaseId,
|
||||||
|
CStringGetDatum(t_thrd.applyworker_cxt.mySubscription->name));
|
||||||
|
if (!HeapTupleIsValid(tup)) {
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("subscription \"%s\" does not exist",
|
||||||
|
t_thrd.applyworker_cxt.mySubscription->name)));
|
||||||
|
}
|
||||||
|
subid = HeapTupleGetOid(tup);
|
||||||
|
|
||||||
|
/* Form a new tuple. */
|
||||||
|
int rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls));
|
||||||
|
securec_check(rc, "", "");
|
||||||
|
rc = memset_s(values, sizeof(values), 0, sizeof(values));
|
||||||
|
securec_check(rc, "", "");
|
||||||
|
rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces));
|
||||||
|
securec_check(rc, "", "");
|
||||||
|
|
||||||
|
/* get conninfoWithoutHostport */
|
||||||
|
StringInfoData conninfoWithoutHostport;
|
||||||
|
initStringInfo(&conninfoWithoutHostport);
|
||||||
|
ParseConninfo(sub->conninfo, &conninfoWithoutHostport, (HostPort**)NULL);
|
||||||
|
|
||||||
|
/* join conninfoWithoutHostport together with standbysinfo */
|
||||||
|
appendStringInfo(&conninfoWithoutHostport, " %s", standbysInfo);
|
||||||
|
/* Replace connection information */
|
||||||
|
values[Anum_pg_subscription_subconninfo - 1] = CStringGetTextDatum(conninfoWithoutHostport.data);
|
||||||
|
replaces[Anum_pg_subscription_subconninfo - 1] = true;
|
||||||
|
tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls, replaces);
|
||||||
|
|
||||||
|
/* Update the catalog. */
|
||||||
|
simple_heap_update(rel, &tup->t_self, tup);
|
||||||
|
CatalogUpdateIndexes(rel, tup);
|
||||||
|
|
||||||
|
heap_close(rel, RowExclusiveLock);
|
||||||
|
CommitTransactionCommand();
|
||||||
|
|
||||||
|
ereport(LOG, (errmsg("Update conninfo successfully, new conninfo %s.", standbysInfo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is current process a logical replication worker?
|
||||||
|
*/
|
||||||
|
bool IsLogicalWorker(void)
|
||||||
|
{
|
||||||
|
return t_thrd.applyworker_cxt.curWorker != NULL;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
#include "catalog/pg_publication.h"
|
#include "catalog/pg_publication.h"
|
||||||
|
|
||||||
|
#include "commands/defrem.h"
|
||||||
|
|
||||||
#include "replication/logical.h"
|
#include "replication/logical.h"
|
||||||
#include "replication/logicalproto.h"
|
#include "replication/logicalproto.h"
|
||||||
#include "replication/origin.h"
|
#include "replication/origin.h"
|
||||||
|
|
@ -44,6 +46,8 @@ static bool pgoutput_origin_filter(LogicalDecodingContext *ctx, RepOriginId orig
|
||||||
|
|
||||||
static List *LoadPublications(List *pubnames);
|
static List *LoadPublications(List *pubnames);
|
||||||
static void publication_invalidation_cb(Datum arg, int cacheid, uint32 hashvalue);
|
static void publication_invalidation_cb(Datum arg, int cacheid, uint32 hashvalue);
|
||||||
|
static bool ReplconninfoChanged();
|
||||||
|
static void GetConninfo(StringInfoData* standbysInfo);
|
||||||
|
|
||||||
/* Entry in the map used to remember which relation schemas we sent. */
|
/* Entry in the map used to remember which relation schemas we sent. */
|
||||||
typedef struct RelationSyncEntry {
|
typedef struct RelationSyncEntry {
|
||||||
|
|
@ -79,6 +83,9 @@ static void parse_output_parameters(List *options, PGOutputData *data)
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
bool protocol_version_given = false;
|
bool protocol_version_given = false;
|
||||||
bool publication_names_given = false;
|
bool publication_names_given = false;
|
||||||
|
bool binary_option_given = false;
|
||||||
|
|
||||||
|
data->binary = false;
|
||||||
|
|
||||||
foreach (lc, options) {
|
foreach (lc, options) {
|
||||||
DefElem *defel = (DefElem *)lfirst(lc);
|
DefElem *defel = (DefElem *)lfirst(lc);
|
||||||
|
|
@ -108,6 +115,12 @@ static void parse_output_parameters(List *options, PGOutputData *data)
|
||||||
|
|
||||||
if (!SplitIdentifierString(strVal(defel->arg), ',', &(data->publication_names)))
|
if (!SplitIdentifierString(strVal(defel->arg), ',', &(data->publication_names)))
|
||||||
ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid publication_names syntax")));
|
ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid publication_names syntax")));
|
||||||
|
} else if (strcmp(defel->defname, "binary") == 0) {
|
||||||
|
if (binary_option_given)
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options")));
|
||||||
|
binary_option_given = true;
|
||||||
|
|
||||||
|
data->binary = defGetBoolean(defel);
|
||||||
} else
|
} else
|
||||||
elog(ERROR, "unrecognized pgoutput option: %s", defel->defname);
|
elog(ERROR, "unrecognized pgoutput option: %s", defel->defname);
|
||||||
}
|
}
|
||||||
|
|
@ -206,6 +219,22 @@ static void pgoutput_commit_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *t
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
OutputPluginPrepareWrite(ctx, true);
|
||||||
logicalrep_write_commit(ctx->out, txn, commit_lsn);
|
logicalrep_write_commit(ctx->out, txn, commit_lsn);
|
||||||
OutputPluginWrite(ctx, true);
|
OutputPluginWrite(ctx, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send the newest connecttion information to the subscriber,
|
||||||
|
* when the connection information about the standby changes.
|
||||||
|
*/
|
||||||
|
if (ReplconninfoChanged()) {
|
||||||
|
StringInfoData standbysInfo;
|
||||||
|
initStringInfo(&standbysInfo);
|
||||||
|
|
||||||
|
GetConninfo(&standbysInfo);
|
||||||
|
OutputPluginPrepareWrite(ctx, true);
|
||||||
|
logicalrep_write_conninfo(ctx->out, standbysInfo.data);
|
||||||
|
OutputPluginWrite(ctx, true);
|
||||||
|
|
||||||
|
FreeStringInfo(&standbysInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -300,19 +329,19 @@ static void pgoutput_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
|
||||||
switch (change->action) {
|
switch (change->action) {
|
||||||
case REORDER_BUFFER_CHANGE_INSERT:
|
case REORDER_BUFFER_CHANGE_INSERT:
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
OutputPluginPrepareWrite(ctx, true);
|
||||||
logicalrep_write_insert(ctx->out, relation, &change->data.tp.newtuple->tuple);
|
logicalrep_write_insert(ctx->out, relation, &change->data.tp.newtuple->tuple, data->binary);
|
||||||
OutputPluginWrite(ctx, true);
|
OutputPluginWrite(ctx, true);
|
||||||
break;
|
break;
|
||||||
case REORDER_BUFFER_CHANGE_UINSERT:
|
case REORDER_BUFFER_CHANGE_UINSERT:
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
OutputPluginPrepareWrite(ctx, true);
|
||||||
logicalrep_write_insert(ctx->out, relation, (HeapTuple)(&change->data.utp.newtuple->tuple));
|
logicalrep_write_insert(ctx->out, relation, (HeapTuple)(&change->data.utp.newtuple->tuple), data->binary);
|
||||||
OutputPluginWrite(ctx, true);
|
OutputPluginWrite(ctx, true);
|
||||||
break;
|
break;
|
||||||
case REORDER_BUFFER_CHANGE_UPDATE: {
|
case REORDER_BUFFER_CHANGE_UPDATE: {
|
||||||
HeapTuple oldtuple = change->data.tp.oldtuple ? &change->data.tp.oldtuple->tuple : NULL;
|
HeapTuple oldtuple = change->data.tp.oldtuple ? &change->data.tp.oldtuple->tuple : NULL;
|
||||||
|
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
OutputPluginPrepareWrite(ctx, true);
|
||||||
logicalrep_write_update(ctx->out, relation, oldtuple, &change->data.tp.newtuple->tuple);
|
logicalrep_write_update(ctx->out, relation, oldtuple, &change->data.tp.newtuple->tuple, data->binary);
|
||||||
OutputPluginWrite(ctx, true);
|
OutputPluginWrite(ctx, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -320,14 +349,14 @@ static void pgoutput_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
|
||||||
HeapTuple oldtuple = change->data.utp.oldtuple ? ((HeapTuple)(&change->data.utp.oldtuple->tuple)) : NULL;
|
HeapTuple oldtuple = change->data.utp.oldtuple ? ((HeapTuple)(&change->data.utp.oldtuple->tuple)) : NULL;
|
||||||
|
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
OutputPluginPrepareWrite(ctx, true);
|
||||||
logicalrep_write_update(ctx->out, relation, oldtuple, (HeapTuple)(&change->data.utp.newtuple->tuple));
|
logicalrep_write_update(ctx->out, relation, oldtuple, (HeapTuple)(&change->data.utp.newtuple->tuple), data->binary);
|
||||||
OutputPluginWrite(ctx, true);
|
OutputPluginWrite(ctx, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case REORDER_BUFFER_CHANGE_DELETE:
|
case REORDER_BUFFER_CHANGE_DELETE:
|
||||||
if (change->data.tp.oldtuple) {
|
if (change->data.tp.oldtuple) {
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
OutputPluginPrepareWrite(ctx, true);
|
||||||
logicalrep_write_delete(ctx->out, relation, &change->data.tp.oldtuple->tuple);
|
logicalrep_write_delete(ctx->out, relation, &change->data.tp.oldtuple->tuple, data->binary);
|
||||||
OutputPluginWrite(ctx, true);
|
OutputPluginWrite(ctx, true);
|
||||||
} else
|
} else
|
||||||
elog(DEBUG1, "didn't send DELETE change because of missing oldtuple");
|
elog(DEBUG1, "didn't send DELETE change because of missing oldtuple");
|
||||||
|
|
@ -335,7 +364,7 @@ static void pgoutput_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
|
||||||
case REORDER_BUFFER_CHANGE_UDELETE:
|
case REORDER_BUFFER_CHANGE_UDELETE:
|
||||||
if (change->data.utp.oldtuple) {
|
if (change->data.utp.oldtuple) {
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
OutputPluginPrepareWrite(ctx, true);
|
||||||
logicalrep_write_delete(ctx->out, relation, (HeapTuple)(&change->data.utp.oldtuple->tuple));
|
logicalrep_write_delete(ctx->out, relation, (HeapTuple)(&change->data.utp.oldtuple->tuple), data->binary);
|
||||||
OutputPluginWrite(ctx, true);
|
OutputPluginWrite(ctx, true);
|
||||||
} else
|
} else
|
||||||
elog(DEBUG1, "didn't send DELETE change because of missing oldtuple");
|
elog(DEBUG1, "didn't send DELETE change because of missing oldtuple");
|
||||||
|
|
@ -605,3 +634,43 @@ static void rel_sync_cache_publication_cb(Datum arg, int cacheid, uint32 hashval
|
||||||
entry->pubactions.pubdelete = false;
|
entry->pubactions.pubdelete = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void GetConninfo(StringInfoData* standbysInfo)
|
||||||
|
{
|
||||||
|
bool primaryJoined = false;
|
||||||
|
StringInfoData hosts;
|
||||||
|
StringInfoData ports;
|
||||||
|
initStringInfo(&hosts);
|
||||||
|
initStringInfo(&ports);
|
||||||
|
for (int i = 1; i < MAX_REPLNODE_NUM + 1; ++i) {
|
||||||
|
t_thrd.postmaster_cxt.ReplConnChangeType[i] = 0;
|
||||||
|
if (t_thrd.postmaster_cxt.ReplConnArray[i] == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!primaryJoined) {
|
||||||
|
appendStringInfo(&hosts, "%s,%s",
|
||||||
|
t_thrd.postmaster_cxt.ReplConnArray[i]->localhost,
|
||||||
|
t_thrd.postmaster_cxt.ReplConnArray[i]->remotehost);
|
||||||
|
appendStringInfo(&ports, "%d,%d",
|
||||||
|
t_thrd.postmaster_cxt.ReplConnArray[i]->localport,
|
||||||
|
t_thrd.postmaster_cxt.ReplConnArray[i]->remoteport);
|
||||||
|
primaryJoined = true;
|
||||||
|
} else {
|
||||||
|
appendStringInfo(&hosts, ",%s",
|
||||||
|
t_thrd.postmaster_cxt.ReplConnArray[i]->remotehost);
|
||||||
|
appendStringInfo(&ports, ",%d",
|
||||||
|
t_thrd.postmaster_cxt.ReplConnArray[i]->remoteport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appendStringInfo(standbysInfo, "host=%s port=%s", hosts.data, ports.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool ReplconninfoChanged()
|
||||||
|
{
|
||||||
|
for (int i = 1; i < MAX_REPLNODE_NUM; ++i) {
|
||||||
|
if (t_thrd.postmaster_cxt.ReplConnChangeType[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1934,14 +1934,18 @@ ArchiveConfig* formArchiveConfigFromStr(char *content, bool encrypted)
|
||||||
ArchiveConnConfig* conn_config = NULL;
|
ArchiveConnConfig* conn_config = NULL;
|
||||||
char* media_type = NULL;
|
char* media_type = NULL;
|
||||||
errno_t rc = EOK;
|
errno_t rc = EOK;
|
||||||
/* SplitIdentifierString will change origin string */
|
char *content_copy = NULL;
|
||||||
char *content_copy = pstrdup(content);
|
|
||||||
char *tmp = NULL;
|
char *tmp = NULL;
|
||||||
List* elemlist = NIL;
|
List* elemlist = NIL;
|
||||||
int param_num = 0;
|
int param_num = 0;
|
||||||
size_t elem_index = 0;
|
size_t elem_index = 0;
|
||||||
archive_config = (ArchiveConfig *)palloc0(sizeof(ArchiveConfig));
|
archive_config = (ArchiveConfig *)palloc0(sizeof(ArchiveConfig));
|
||||||
char decryptSecretAccessKeyStr[DEST_CIPHER_LENGTH] = {'\0'};
|
char decryptSecretAccessKeyStr[DEST_CIPHER_LENGTH] = {'\0'};
|
||||||
|
if (content == NULL || strlen(content) == 0) {
|
||||||
|
goto FAILURE;
|
||||||
|
}
|
||||||
|
/* SplitIdentifierString will change origin string */
|
||||||
|
content_copy = pstrdup(content);
|
||||||
/* Parse string into list of identifiers */
|
/* Parse string into list of identifiers */
|
||||||
if (!SplitIdentifierString(content_copy, ';', &elemlist, false, false)) {
|
if (!SplitIdentifierString(content_copy, ';', &elemlist, false, false)) {
|
||||||
goto FAILURE;
|
goto FAILURE;
|
||||||
|
|
|
||||||
|
|
@ -401,6 +401,7 @@ int WalSenderMain(void)
|
||||||
t_thrd.walsender_cxt.server_run_mode = t_thrd.postmaster_cxt.HaShmData->current_mode;
|
t_thrd.walsender_cxt.server_run_mode = t_thrd.postmaster_cxt.HaShmData->current_mode;
|
||||||
|
|
||||||
SetHaWalSenderChannel();
|
SetHaWalSenderChannel();
|
||||||
|
Assert(t_thrd.utils_cxt.CurrentResourceOwner != NULL);
|
||||||
/* Handle handshake messages before streaming */
|
/* Handle handshake messages before streaming */
|
||||||
WalSndHandshake();
|
WalSndHandshake();
|
||||||
|
|
||||||
|
|
@ -496,6 +497,7 @@ static void WalSndHandshake(void)
|
||||||
|
|
||||||
while (!repCxt.replicationStarted) {
|
while (!repCxt.replicationStarted) {
|
||||||
int firstchar;
|
int firstchar;
|
||||||
|
Assert(t_thrd.utils_cxt.CurrentResourceOwner != NULL);
|
||||||
|
|
||||||
WalSndSetState(WALSNDSTATE_STARTUP);
|
WalSndSetState(WALSNDSTATE_STARTUP);
|
||||||
set_ps_display("idle", false);
|
set_ps_display("idle", false);
|
||||||
|
|
@ -2033,6 +2035,8 @@ bool isLogicalSlotExist(char* slotName)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void IdentifyCommand(Node* cmd_node, ReplicationCxt* repCxt, const char *cmd_string){
|
static void IdentifyCommand(Node* cmd_node, ReplicationCxt* repCxt, const char *cmd_string){
|
||||||
|
ResourceOwner tmpOwner = t_thrd.utils_cxt.CurrentResourceOwner;
|
||||||
|
Assert(tmpOwner != NULL);
|
||||||
switch (cmd_node->type) {
|
switch (cmd_node->type) {
|
||||||
case T_IdentifySystemCmd:
|
case T_IdentifySystemCmd:
|
||||||
IdentifySystem();
|
IdentifySystem();
|
||||||
|
|
@ -2140,7 +2144,7 @@ static void IdentifyCommand(Node* cmd_node, ReplicationCxt* repCxt, const char *
|
||||||
ereport(FATAL,
|
ereport(FATAL,
|
||||||
(errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid standby query string: %s", cmd_string)));
|
(errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid standby query string: %s", cmd_string)));
|
||||||
}
|
}
|
||||||
|
t_thrd.utils_cxt.CurrentResourceOwner = tmpOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -3596,8 +3600,12 @@ static int WalSndLoop(WalSndSendDataCallback send_data)
|
||||||
t_thrd.walsender_cxt.last_logical_xlog_advanced_timestamp = GetCurrentTimestamp();
|
t_thrd.walsender_cxt.last_logical_xlog_advanced_timestamp = GetCurrentTimestamp();
|
||||||
t_thrd.walsender_cxt.waiting_for_ping_response = false;
|
t_thrd.walsender_cxt.waiting_for_ping_response = false;
|
||||||
|
|
||||||
|
ResourceOwner tmpOwner = t_thrd.utils_cxt.CurrentResourceOwner;
|
||||||
|
Assert(!IsTransactionOrTransactionBlock() &&
|
||||||
|
strcmp(ResourceOwnerGetName(tmpOwner), "walsender top-level resource owner") == 0);
|
||||||
/* Loop forever, unless we get an error */
|
/* Loop forever, unless we get an error */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
t_thrd.utils_cxt.CurrentResourceOwner = tmpOwner;
|
||||||
TimestampTz now;
|
TimestampTz now;
|
||||||
|
|
||||||
/* Clear any already-pending wakeups */
|
/* Clear any already-pending wakeups */
|
||||||
|
|
@ -4006,6 +4014,7 @@ static int WalSndLoop(WalSndSendDataCallback send_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t_thrd.utils_cxt.CurrentResourceOwner = tmpOwner;
|
||||||
WalSndShutdown();
|
WalSndShutdown();
|
||||||
return 1; /* keep the compiler quiet */
|
return 1; /* keep the compiler quiet */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -394,8 +394,6 @@ void mdcreate(SMgrRelation reln, ForkNumber forkNum, bool isRedo)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
int save_errno = errno;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* During bootstrap, there are cases where a system relation will be
|
* During bootstrap, there are cases where a system relation will be
|
||||||
* accessed (by internal backend processes) before the bootstrap
|
* accessed (by internal backend processes) before the bootstrap
|
||||||
|
|
@ -407,26 +405,7 @@ void mdcreate(SMgrRelation reln, ForkNumber forkNum, bool isRedo)
|
||||||
* new catalogs can by no means be used by other relations, we simply
|
* new catalogs can by no means be used by other relations, we simply
|
||||||
* truncate them.
|
* truncate them.
|
||||||
*/
|
*/
|
||||||
if (isRedo || IsBootstrapProcessingMode() ||
|
fd = RetryDataFileIdOpenFile(isRedo, path, filenode, flags);
|
||||||
(u_sess->attr.attr_common.IsInplaceUpgrade && filenode.rnode.node.relNode < FirstNormalObjectId)) {
|
|
||||||
ADIO_RUN()
|
|
||||||
{
|
|
||||||
flags = O_RDWR | PG_BINARY | O_DIRECT | (u_sess->attr.attr_common.IsInplaceUpgrade ? O_TRUNC : 0);
|
|
||||||
}
|
|
||||||
ADIO_ELSE()
|
|
||||||
{
|
|
||||||
flags = O_RDWR | PG_BINARY | (u_sess->attr.attr_common.IsInplaceUpgrade ? O_TRUNC : 0);
|
|
||||||
}
|
|
||||||
ADIO_END();
|
|
||||||
|
|
||||||
fd = DataFileIdOpenFile(path, filenode, flags, 0600);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fd < 0) {
|
|
||||||
/* be sure to report the error reported by create, not open */
|
|
||||||
errno = save_errno;
|
|
||||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not create file \"%s\": %m", path)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
File fd_pca = -1;
|
File fd_pca = -1;
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,29 @@ then
|
||||||
plat_form_str=ubuntu18.04_"$cpu_bit"
|
plat_form_str=ubuntu18.04_"$cpu_bit"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
##################################################################################
|
||||||
|
# the modification here will also lead to the synchronous modification of the 3rd-party compilation library path
|
||||||
|
# PR link : https://gitee.com/opengauss/openGauss-third_party/pulls/130
|
||||||
|
# redflag platform
|
||||||
|
# the result form like this: asianux_x86_64
|
||||||
|
##################################################################################
|
||||||
|
if [ "$kernel"x = "redflag"x ]
|
||||||
|
then
|
||||||
|
plat_form_str=asianux7.6_"$cpu_bit"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
##################################################################################
|
||||||
|
# the modification here will also lead to the synchronous modification of the 3rd-party compilation library path
|
||||||
|
# PR link : https://gitee.com/opengauss/openGauss-third_party/pulls/130
|
||||||
|
# asianux platform
|
||||||
|
# the result form like this: asianux_aarch64
|
||||||
|
##################################################################################
|
||||||
|
if [ "$kernel"x = "asianux"x ]
|
||||||
|
then
|
||||||
|
plat_form_str=asianux7.5_"$cpu_bit"
|
||||||
|
fi
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
#
|
#
|
||||||
# other platform
|
# other platform
|
||||||
|
|
|
||||||
|
|
@ -314,4 +314,5 @@ template<bool hasnull>
|
||||||
void UHeapFillDiskTuple(TupleDesc tupleDesc, Datum *values, const bool *isnull, UHeapDiskTupleData *diskTuple,
|
void UHeapFillDiskTuple(TupleDesc tupleDesc, Datum *values, const bool *isnull, UHeapDiskTupleData *diskTuple,
|
||||||
uint32 dataSize, bool enableReverseBitmap, bool enableReserve);
|
uint32 dataSize, bool enableReverseBitmap, bool enableReserve);
|
||||||
void CheckTupleValidity(Relation rel, UHeapTuple utuple);
|
void CheckTupleValidity(Relation rel, UHeapTuple utuple);
|
||||||
|
Tuple UHeapMaterialize(TupleTableSlot *slot);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ extern void addNewPartitionTuple(Relation pg_part_desc, Partition new_part_desc,
|
||||||
Datum interval, Datum maxValues, Datum transitionPoint, Datum reloptions);
|
Datum interval, Datum maxValues, Datum transitionPoint, Datum reloptions);
|
||||||
|
|
||||||
extern void heap_truncate_one_part(Relation rel , Oid partOid);
|
extern void heap_truncate_one_part(Relation rel , Oid partOid);
|
||||||
extern Oid heapTupleGetPartitionId(Relation rel, void *tuple);
|
extern Oid heapTupleGetPartitionId(Relation rel, void *tuple, bool isDDL = false);
|
||||||
extern Oid heapTupleGetSubPartitionId(Relation rel, void *tuple);
|
extern Oid heapTupleGetSubPartitionId(Relation rel, void *tuple);
|
||||||
extern void heap_truncate(List *relids);
|
extern void heap_truncate(List *relids);
|
||||||
extern void heap_truncate_one_rel(Relation rel);
|
extern void heap_truncate_one_rel(Relation rel);
|
||||||
|
|
@ -240,7 +240,7 @@ extern char* make_column_map(TupleDesc tuple_desc);
|
||||||
*/
|
*/
|
||||||
extern bool* CheckPartkeyHasTimestampwithzone(Relation partTableRel, bool isForSubPartition = false);
|
extern bool* CheckPartkeyHasTimestampwithzone(Relation partTableRel, bool isForSubPartition = false);
|
||||||
|
|
||||||
extern Oid AddNewIntervalPartition(Relation rel, void* insertTuple);
|
extern Oid AddNewIntervalPartition(Relation rel, void* insertTuple, bool isDDL = false);
|
||||||
|
|
||||||
extern int GetIndexKeyAttsByTuple(Relation relation, HeapTuple indexTuple);
|
extern int GetIndexKeyAttsByTuple(Relation relation, HeapTuple indexTuple);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,13 +50,15 @@ CATALOG(pg_subscription,6126) BKI_SHARED_RELATION BKI_ROWTYPE_OID(6128) BKI_SCHE
|
||||||
NameData subslotname; /* Slot name on publisher */
|
NameData subslotname; /* Slot name on publisher */
|
||||||
text subsynccommit; /* Synchronous commit setting for worker */
|
text subsynccommit; /* Synchronous commit setting for worker */
|
||||||
text subpublications[1]; /* List of publications subscribed to */
|
text subpublications[1]; /* List of publications subscribed to */
|
||||||
|
bool subbinary; /* True if the subscription wants the
|
||||||
|
* publisher to send data in binary */
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
FormData_pg_subscription;
|
FormData_pg_subscription;
|
||||||
|
|
||||||
typedef FormData_pg_subscription *Form_pg_subscription;
|
typedef FormData_pg_subscription *Form_pg_subscription;
|
||||||
|
|
||||||
#define Natts_pg_subscription 8
|
#define Natts_pg_subscription 9
|
||||||
#define Anum_pg_subscription_subdbid 1
|
#define Anum_pg_subscription_subdbid 1
|
||||||
#define Anum_pg_subscription_subname 2
|
#define Anum_pg_subscription_subname 2
|
||||||
#define Anum_pg_subscription_subowner 3
|
#define Anum_pg_subscription_subowner 3
|
||||||
|
|
@ -65,6 +67,7 @@ typedef FormData_pg_subscription *Form_pg_subscription;
|
||||||
#define Anum_pg_subscription_subslotname 6
|
#define Anum_pg_subscription_subslotname 6
|
||||||
#define Anum_pg_subscription_subsynccommit 7
|
#define Anum_pg_subscription_subsynccommit 7
|
||||||
#define Anum_pg_subscription_subpublications 8
|
#define Anum_pg_subscription_subpublications 8
|
||||||
|
#define Anum_pg_subscription_subbinary 9
|
||||||
|
|
||||||
|
|
||||||
typedef struct Subscription {
|
typedef struct Subscription {
|
||||||
|
|
@ -77,6 +80,7 @@ typedef struct Subscription {
|
||||||
char *slotname; /* Name of the replication slot */
|
char *slotname; /* Name of the replication slot */
|
||||||
char *synccommit; /* Synchronous commit setting for worker */
|
char *synccommit; /* Synchronous commit setting for worker */
|
||||||
List *publications; /* List of publication names to subscribe to */
|
List *publications; /* List of publication names to subscribe to */
|
||||||
|
bool binary; /* Indicates if the subscription wants data in binary format */
|
||||||
} Subscription;
|
} Subscription;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -86,6 +90,7 @@ extern Oid get_subscription_oid(const char *subname, bool missing_ok);
|
||||||
extern char *get_subscription_name(Oid subid, bool missing_ok);
|
extern char *get_subscription_name(Oid subid, bool missing_ok);
|
||||||
|
|
||||||
extern int CountDBSubscriptions(Oid dbid);
|
extern int CountDBSubscriptions(Oid dbid);
|
||||||
extern char *DecryptConninfo(char *encryptConninfo);
|
extern void ClearListContent(List *list);
|
||||||
|
|
||||||
|
|
||||||
#endif /* PG_SUBSCRIPTION_H */
|
#endif /* PG_SUBSCRIPTION_H */
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@
|
||||||
|
|
||||||
#include "nodes/parsenodes.h"
|
#include "nodes/parsenodes.h"
|
||||||
|
|
||||||
|
typedef struct HostPort {
|
||||||
|
char* host;
|
||||||
|
char* port;
|
||||||
|
} HostPort;
|
||||||
|
|
||||||
extern ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel);
|
extern ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel);
|
||||||
extern ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt);
|
extern ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt);
|
||||||
extern void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel);
|
extern void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel);
|
||||||
|
|
@ -24,6 +29,12 @@ extern void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel);
|
||||||
extern ObjectAddress AlterSubscriptionOwner(const char *name, Oid newOwnerId);
|
extern ObjectAddress AlterSubscriptionOwner(const char *name, Oid newOwnerId);
|
||||||
extern void AlterSubscriptionOwner_oid(Oid subid, Oid newOwnerId);
|
extern void AlterSubscriptionOwner_oid(Oid subid, Oid newOwnerId);
|
||||||
extern void RenameSubscription(List* oldname, const char* newname);
|
extern void RenameSubscription(List* oldname, const char* newname);
|
||||||
|
extern void AddStandbysInfo(char* standbysInfo);
|
||||||
|
extern void DropStandbysInfo(char* standbysInfo);
|
||||||
|
|
||||||
|
extern void ParseConninfo(const char* conninfo, StringInfoData* conninfoWithoutHostPort, HostPort** hostPortList);
|
||||||
|
extern char* EncryptOrDecryptConninfo(const char* conninfo, const char action);
|
||||||
|
extern bool AttemptConnectPublisher(const char *conninfoOriginal, char* slotname, bool checkRemoteMode);
|
||||||
|
|
||||||
#endif /* SUBSCRIPTIONCMDS_H */
|
#endif /* SUBSCRIPTIONCMDS_H */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ typedef struct knl_session_attr_sql {
|
||||||
#ifndef ENABLE_MULTIPLE_NODES
|
#ifndef ENABLE_MULTIPLE_NODES
|
||||||
bool enable_custom_parser;
|
bool enable_custom_parser;
|
||||||
#endif
|
#endif
|
||||||
bool b_sql_plugin;
|
bool dolphin;
|
||||||
} knl_session_attr_sql;
|
} knl_session_attr_sql;
|
||||||
|
|
||||||
#endif /* SRC_INCLUDE_KNL_KNL_SESSION_ATTR_SQL */
|
#endif /* SRC_INCLUDE_KNL_KNL_SESSION_ATTR_SQL */
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ extern const uint32 SUPPORT_DATA_REPAIR;
|
||||||
extern const uint32 SCAN_BATCH_MODE_VERSION_NUM;
|
extern const uint32 SCAN_BATCH_MODE_VERSION_NUM;
|
||||||
extern const uint32 RELMAP_4K_VERSION_NUM;
|
extern const uint32 RELMAP_4K_VERSION_NUM;
|
||||||
extern const uint32 PUBLICATION_VERSION_NUM;
|
extern const uint32 PUBLICATION_VERSION_NUM;
|
||||||
|
extern const uint32 SUBSCRIPTION_BINARY_VERSION_NUM;
|
||||||
extern const uint32 ANALYZER_HOOK_VERSION_NUM;
|
extern const uint32 ANALYZER_HOOK_VERSION_NUM;
|
||||||
extern const uint32 SUPPORT_HASH_XLOG_VERSION_NUM;
|
extern const uint32 SUPPORT_HASH_XLOG_VERSION_NUM;
|
||||||
extern const uint32 PITR_INIT_VERSION_NUM;
|
extern const uint32 PITR_INIT_VERSION_NUM;
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ typedef struct LibpqrcvConnectParam {
|
||||||
bool logical;
|
bool logical;
|
||||||
uint32 protoVersion; /* Logical protocol version */
|
uint32 protoVersion; /* Logical protocol version */
|
||||||
List *publicationNames; /* String list of publications */
|
List *publicationNames; /* String list of publications */
|
||||||
|
bool binary; /* Ask publisher to use binary */
|
||||||
}LibpqrcvConnectParam;
|
}LibpqrcvConnectParam;
|
||||||
|
|
||||||
extern int32 pg_atoi(char* s, int size, int c);
|
extern int32 pg_atoi(char* s, int size, int c);
|
||||||
|
|
@ -53,5 +54,6 @@ extern bool libpqrcv_command(const char *cmd, char **err, int *sqlstate);
|
||||||
extern void IdentifyRemoteSystem(bool checkRemote);
|
extern void IdentifyRemoteSystem(bool checkRemote);
|
||||||
extern void CreateRemoteReplicationSlot(XLogRecPtr startpoint, const char* slotname, bool isLogical);
|
extern void CreateRemoteReplicationSlot(XLogRecPtr startpoint, const char* slotname, bool isLogical);
|
||||||
extern void StartRemoteStreaming(const LibpqrcvConnectParam *options);
|
extern void StartRemoteStreaming(const LibpqrcvConnectParam *options);
|
||||||
|
extern ServerMode IdentifyRemoteMode();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
#include "access/extreme_rto/spsc_blocking_queue.h"
|
#include "access/extreme_rto/spsc_blocking_queue.h"
|
||||||
#include "access/parallel_recovery/redo_item.h"
|
#include "access/parallel_recovery/redo_item.h"
|
||||||
|
|
||||||
|
#include "nodes/parsenodes_common.h"
|
||||||
#include "nodes/replnodes.h"
|
#include "nodes/replnodes.h"
|
||||||
#include "access/ustore/knl_utuple.h"
|
#include "access/ustore/knl_utuple.h"
|
||||||
#include "replication/logical_queue.h"
|
#include "replication/logical_queue.h"
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,21 @@
|
||||||
* Keep in mind that the columns correspond to the *remote* table.
|
* Keep in mind that the columns correspond to the *remote* table.
|
||||||
*/
|
*/
|
||||||
typedef struct LogicalRepTupleData {
|
typedef struct LogicalRepTupleData {
|
||||||
char *values[MaxTupleAttributeNumber]; /* value in out function format or NULL if values is NULL */
|
/* Array of StringInfos, one per column; some may be unused */
|
||||||
bool changed[MaxTupleAttributeNumber]; /* marker for changed/unchanged values */
|
StringInfoData *colvalues;
|
||||||
|
/* Array of markers for null/unchanged/text/binary, one per column */
|
||||||
|
char *colstatus;
|
||||||
/* Length of above arrays */
|
/* Length of above arrays */
|
||||||
int ncols;
|
int ncols;
|
||||||
} LogicalRepTupleData;
|
} LogicalRepTupleData;
|
||||||
|
|
||||||
|
/* Possible values for LogicalRepTupleData.colstatus[colnum] */
|
||||||
|
/* These values are also used in the on-the-wire protocol */
|
||||||
|
#define LOGICALREP_COLUMN_NULL 'n'
|
||||||
|
#define LOGICALREP_COLUMN_UNCHANGED 'u'
|
||||||
|
#define LOGICALREP_COLUMN_TEXT 't'
|
||||||
|
#define LOGICALREP_COLUMN_BINARY 'b' /* added in PG14 */
|
||||||
|
|
||||||
typedef uint32 LogicalRepRelId;
|
typedef uint32 LogicalRepRelId;
|
||||||
|
|
||||||
/* Relation information */
|
/* Relation information */
|
||||||
|
|
@ -81,16 +90,18 @@ extern void logicalrep_read_begin(StringInfo in, LogicalRepBeginData *begin_data
|
||||||
extern void logicalrep_write_commit(StringInfo out, ReorderBufferTXN *txn, XLogRecPtr commit_lsn);
|
extern void logicalrep_write_commit(StringInfo out, ReorderBufferTXN *txn, XLogRecPtr commit_lsn);
|
||||||
extern void logicalrep_read_commit(StringInfo in, LogicalRepCommitData *commit_data);
|
extern void logicalrep_read_commit(StringInfo in, LogicalRepCommitData *commit_data);
|
||||||
extern void logicalrep_write_origin(StringInfo out, const char *origin, XLogRecPtr origin_lsn);
|
extern void logicalrep_write_origin(StringInfo out, const char *origin, XLogRecPtr origin_lsn);
|
||||||
extern void logicalrep_write_insert(StringInfo out, Relation rel, HeapTuple newtuple);
|
extern void logicalrep_write_insert(StringInfo out, Relation rel, HeapTuple newtuple, bool binary);
|
||||||
extern LogicalRepRelId logicalrep_read_insert(StringInfo in, LogicalRepTupleData *newtup);
|
extern LogicalRepRelId logicalrep_read_insert(StringInfo in, LogicalRepTupleData *newtup);
|
||||||
extern void logicalrep_write_update(StringInfo out, Relation rel, HeapTuple oldtuple, HeapTuple newtuple);
|
extern void logicalrep_write_update(StringInfo out, Relation rel, HeapTuple oldtuple, HeapTuple newtuple, bool binary);
|
||||||
extern LogicalRepRelId logicalrep_read_update(StringInfo in, bool *has_oldtuple, LogicalRepTupleData *oldtup,
|
extern LogicalRepRelId logicalrep_read_update(StringInfo in, bool *has_oldtuple, LogicalRepTupleData *oldtup,
|
||||||
LogicalRepTupleData *newtup);
|
LogicalRepTupleData *newtup);
|
||||||
extern void logicalrep_write_delete(StringInfo out, Relation rel, HeapTuple oldtuple);
|
extern void logicalrep_write_delete(StringInfo out, Relation rel, HeapTuple oldtuple, bool binary);
|
||||||
extern LogicalRepRelId logicalrep_read_delete(StringInfo in, LogicalRepTupleData *oldtup);
|
extern LogicalRepRelId logicalrep_read_delete(StringInfo in, LogicalRepTupleData *oldtup);
|
||||||
extern void logicalrep_write_rel(StringInfo out, Relation rel);
|
extern void logicalrep_write_rel(StringInfo out, Relation rel);
|
||||||
extern LogicalRepRelation *logicalrep_read_rel(StringInfo in);
|
extern LogicalRepRelation *logicalrep_read_rel(StringInfo in);
|
||||||
extern void logicalrep_write_typ(StringInfo out, Oid typoid);
|
extern void logicalrep_write_typ(StringInfo out, Oid typoid);
|
||||||
extern void logicalrep_read_typ(StringInfo out, LogicalRepTyp *ltyp);
|
extern void logicalrep_read_typ(StringInfo out, LogicalRepTyp *ltyp);
|
||||||
|
extern void logicalrep_write_conninfo(StringInfo out, char* conninfo);
|
||||||
|
extern void logicalrep_read_conninfo(StringInfo in, char** conninfo);
|
||||||
|
|
||||||
#endif /* LOGICALREP_PROTO_H */
|
#endif /* LOGICALREP_PROTO_H */
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,6 @@
|
||||||
#define LOGICALWORKER_H
|
#define LOGICALWORKER_H
|
||||||
|
|
||||||
extern void ApplyWorkerMain();
|
extern void ApplyWorkerMain();
|
||||||
|
extern bool IsLogicalWorker(void);
|
||||||
|
|
||||||
#endif /* LOGICALWORKER_H */
|
#endif /* LOGICALWORKER_H */
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ typedef struct PGOutputData {
|
||||||
|
|
||||||
List *publication_names;
|
List *publication_names;
|
||||||
List *publications;
|
List *publications;
|
||||||
|
bool binary;
|
||||||
} PGOutputData;
|
} PGOutputData;
|
||||||
|
|
||||||
#endif /* PGOUTPUT_H */
|
#endif /* PGOUTPUT_H */
|
||||||
|
|
|
||||||
|
|
@ -229,10 +229,15 @@ private:
|
||||||
bool m_syscache_relids[FirstNormalObjectId];
|
bool m_syscache_relids[FirstNormalObjectId];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
void NotifyGscRecoveryStarted();
|
||||||
void NotifyGscRecoveryFinished();
|
void NotifyGscRecoveryFinished();
|
||||||
extern Datum gs_gsc_dbstat_info(PG_FUNCTION_ARGS);
|
extern Datum gs_gsc_dbstat_info(PG_FUNCTION_ARGS);
|
||||||
extern Datum gs_gsc_clean(PG_FUNCTION_ARGS);
|
extern Datum gs_gsc_clean(PG_FUNCTION_ARGS);
|
||||||
extern Datum gs_gsc_catalog_detail(PG_FUNCTION_ARGS);
|
extern Datum gs_gsc_catalog_detail(PG_FUNCTION_ARGS);
|
||||||
extern Datum gs_gsc_table_detail(PG_FUNCTION_ARGS);
|
extern Datum gs_gsc_table_detail(PG_FUNCTION_ARGS);
|
||||||
extern int ResizeHashBucket(int origin_nbucket, DynamicHashBucketStrategy strategy);
|
extern int ResizeHashBucket(int origin_nbucket, DynamicHashBucketStrategy strategy);
|
||||||
|
|
||||||
|
/* only primary node check recovery, recovery from upgrade may have misorder cacheids */
|
||||||
|
#define IsPrimaryRecoveryFinished() \
|
||||||
|
(g_instance.global_sysdbcache.recovery_finished || t_thrd.postmaster_cxt.HaShmData->current_mode == STANDBY_MODE)
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -213,7 +213,7 @@ extern bool check_rel_is_partitioned(Oid relid);
|
||||||
extern Oid partid_get_parentid(Oid partid);
|
extern Oid partid_get_parentid(Oid partid);
|
||||||
extern bool is_not_strict_agg(Oid funcOid);
|
extern bool is_not_strict_agg(Oid funcOid);
|
||||||
extern bool is_pgxc_class_table(Oid tableoid);
|
extern bool is_pgxc_class_table(Oid tableoid);
|
||||||
extern Oid get_valid_relname_relid(const char* relnamespace, const char* relname);
|
extern Oid get_valid_relname_relid(const char* relnamespace, const char* relname, bool nsp_missing_ok = false);
|
||||||
extern bool get_func_iswindow(Oid funcid);
|
extern bool get_func_iswindow(Oid funcid);
|
||||||
extern char get_func_prokind(Oid funcid);
|
extern char get_func_prokind(Oid funcid);
|
||||||
extern char get_typecategory(Oid typid);
|
extern char get_typecategory(Oid typid);
|
||||||
|
|
|
||||||
|
|
@ -1385,6 +1385,8 @@ typedef struct PlDebugEntry {
|
||||||
PLpgSQL_function* func;
|
PLpgSQL_function* func;
|
||||||
} PlDebugEntry;
|
} PlDebugEntry;
|
||||||
|
|
||||||
|
typedef List* (*RawParserHook)(const char*, List**);
|
||||||
|
|
||||||
const int MAXINT8LEN = 25;
|
const int MAXINT8LEN = 25;
|
||||||
const int DEFAULT_DEBUG_BUF_SIZE = 1024;
|
const int DEFAULT_DEBUG_BUF_SIZE = 1024;
|
||||||
const long NANOSECOND_PER_SECOND = 1000000000L; /* 1s */
|
const long NANOSECOND_PER_SECOND = 1000000000L; /* 1s */
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
--
|
||||||
|
-- check for over-optimization of whole-row Var referencing an Append plan
|
||||||
|
--
|
||||||
|
create table int4_test(f1 int4);
|
||||||
|
select (select q from (select 1,2,3 where f1>0 union all select 4,5,6 where f1<=0) q) from int4_test;
|
||||||
|
q
|
||||||
|
---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
select (select q from (select 1,2,3 where f1>0 union all select 4,5,6.0 where f1<=0) q) from int4_test;
|
||||||
|
q
|
||||||
|
---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
insert into int4_test(f1) values(' 0 ');
|
||||||
|
insert into int4_test(f1) values('123456 ');
|
||||||
|
select * from int4_test;
|
||||||
|
f1
|
||||||
|
--------
|
||||||
|
0
|
||||||
|
123456
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
select (select q from (select 1,2,3 where f1>0 union all select 4,5,6 where f1<=0) q) from int4_test;
|
||||||
|
q
|
||||||
|
---------
|
||||||
|
(4,5,6)
|
||||||
|
(1,2,3)
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
select (select q from (select 1,2,3 where f1>0 union all select 4,5,6.0 where f1<=0) q) from int4_test;
|
||||||
|
q
|
||||||
|
-----------
|
||||||
|
(4,5,6.0)
|
||||||
|
(1,2,3)
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
create schema deserialize_func1;
|
||||||
|
set current_schema = deserialize_func1;
|
||||||
|
create sequence test_seq;
|
||||||
|
create or replace function test_func() return int
|
||||||
|
as
|
||||||
|
begin
|
||||||
|
return 1;
|
||||||
|
end;
|
||||||
|
/
|
||||||
|
create table t1(key int, id int default nextval('test_seq'));
|
||||||
|
create table t2(key int, id int default test_func());
|
||||||
|
select * from pg_get_tabledef('deserialize_func1.t1');
|
||||||
|
pg_get_tabledef
|
||||||
|
------------------------------------------------------
|
||||||
|
SET search_path = deserialize_func1; +
|
||||||
|
CREATE TABLE t1 ( +
|
||||||
|
key integer, +
|
||||||
|
id integer DEFAULT nextval('test_seq'::regclass)+
|
||||||
|
) +
|
||||||
|
WITH (orientation=row, compression=no);
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select * from pg_get_tabledef('deserialize_func1.t2');
|
||||||
|
pg_get_tabledef
|
||||||
|
-----------------------------------------
|
||||||
|
SET search_path = deserialize_func1; +
|
||||||
|
CREATE TABLE t2 ( +
|
||||||
|
key integer, +
|
||||||
|
id integer DEFAULT test_func() +
|
||||||
|
) +
|
||||||
|
WITH (orientation=row, compression=no);
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
alter schema deserialize_func1 rename to deserialize_func2;
|
||||||
|
select * from pg_get_tabledef('deserialize_func2.t1');
|
||||||
|
pg_get_tabledef
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
SET search_path = deserialize_func2; +
|
||||||
|
CREATE TABLE t1 ( +
|
||||||
|
key integer, +
|
||||||
|
id integer DEFAULT nextval('deserialize_func2.test_seq'::regclass)+
|
||||||
|
) +
|
||||||
|
WITH (orientation=row, compression=no);
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select * from pg_get_tabledef('deserialize_func2.t2');
|
||||||
|
pg_get_tabledef
|
||||||
|
------------------------------------------------------
|
||||||
|
SET search_path = deserialize_func2; +
|
||||||
|
CREATE TABLE t2 ( +
|
||||||
|
key integer, +
|
||||||
|
id integer DEFAULT deserialize_func2.test_func()+
|
||||||
|
) +
|
||||||
|
WITH (orientation=row, compression=no);
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
alter table deserialize_func2.t1 alter column id drop default;
|
||||||
|
alter table deserialize_func2.t1 alter column id set default nextval('deserialize_func2.test_seq');
|
||||||
|
select * from pg_get_tabledef('deserialize_func2.t1');
|
||||||
|
pg_get_tabledef
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
SET search_path = deserialize_func2; +
|
||||||
|
CREATE TABLE t1 ( +
|
||||||
|
key integer, +
|
||||||
|
id integer DEFAULT nextval('deserialize_func2.test_seq'::regclass)+
|
||||||
|
) +
|
||||||
|
WITH (orientation=row, compression=no);
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
alter table deserialize_func2.t2 alter column id drop default;
|
||||||
|
alter table deserialize_func2.t2 alter column id set default deserialize_func2.test_func();
|
||||||
|
select * from pg_get_tabledef('deserialize_func2.t2');
|
||||||
|
pg_get_tabledef
|
||||||
|
------------------------------------------------------
|
||||||
|
SET search_path = deserialize_func2; +
|
||||||
|
CREATE TABLE t2 ( +
|
||||||
|
key integer, +
|
||||||
|
id integer DEFAULT deserialize_func2.test_func()+
|
||||||
|
) +
|
||||||
|
WITH (orientation=row, compression=no);
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
set current_schema = deserialize_func2;
|
||||||
|
create type type1 as (a int);
|
||||||
|
create view v1 as select '(1)'::type1;
|
||||||
|
alter type type1 rename to type2;
|
||||||
|
select pg_get_viewdef('v1');
|
||||||
|
pg_get_viewdef
|
||||||
|
-------------------------------
|
||||||
|
SELECT '(1)'::type2 AS type1;
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
drop view v1 cascade;
|
||||||
|
create table atest12 as select x as a, 10001-x as b from generate_series(1, 10000) x;
|
||||||
|
create function func1(integer, integer) returns boolean as $$begin return $1 < $2; end $$ language plpgsql immutable;
|
||||||
|
create operator <<< (procedure=func1, leftarg=integer, rightarg=integer, restrict=scalarltsel);
|
||||||
|
create view atest12v as select * from atest12 where b <<< 5;
|
||||||
|
select pg_get_viewdef('atest12v');
|
||||||
|
pg_get_viewdef
|
||||||
|
-------------------------------------------------
|
||||||
|
SELECT * FROM atest12 WHERE (atest12.b <<< 5);
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
drop schema if exists deserialize_func1 cascade;
|
||||||
|
NOTICE: schema "deserialize_func1" does not exist, skipping
|
||||||
|
drop schema if exists deserialize_func2 cascade;
|
||||||
|
NOTICE: drop cascades to 9 other objects
|
||||||
|
DETAIL: drop cascades to sequence test_seq
|
||||||
|
drop cascades to function test_func()
|
||||||
|
drop cascades to table t1
|
||||||
|
drop cascades to table t2
|
||||||
|
drop cascades to type type2
|
||||||
|
drop cascades to table atest12
|
||||||
|
drop cascades to function func1(integer,integer)
|
||||||
|
drop cascades to operator <<<(integer,integer)
|
||||||
|
drop cascades to view atest12v
|
||||||
|
|
@ -132,11 +132,14 @@ DROP EXTENSION test_extension_exists;
|
||||||
ERROR: extension "test_extension_exists" does not exist
|
ERROR: extension "test_extension_exists" does not exist
|
||||||
DROP EXTENSION IF EXISTS test_extension_exists;
|
DROP EXTENSION IF EXISTS test_extension_exists;
|
||||||
NOTICE: extension "test_extension_exists" does not exist, skipping
|
NOTICE: extension "test_extension_exists" does not exist, skipping
|
||||||
-- exists but doesn't support drop
|
-- only extension in white list support drop, but the guc support_extended_features is true in pg_regress mode, so hdfs_fdw can be droped
|
||||||
DROP EXTENSION plpgsql;
|
CREATE DATABASE ext_test_db;
|
||||||
ERROR: EXTENSION is not yet supported.
|
\c ext_test_db
|
||||||
DROP EXTENSION IF EXISTS plpgsql;
|
DROP EXTENSION hdfs_fdw;
|
||||||
ERROR: EXTENSION is not yet supported.
|
DROP EXTENSION IF EXISTS hdfs_fdw;
|
||||||
|
NOTICE: extension "hdfs_fdw" does not exist, skipping
|
||||||
|
\c regression
|
||||||
|
DROP DATABASE ext_test_db;
|
||||||
-- functions
|
-- functions
|
||||||
DROP FUNCTION test_function_exists();
|
DROP FUNCTION test_function_exists();
|
||||||
ERROR: function test_function_exists does not exist
|
ERROR: function test_function_exists does not exist
|
||||||
|
|
|
||||||
|
|
@ -215,3 +215,107 @@ select * from interval_normal_exchange where logdate > '2020-06-01' order by log
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
drop table interval_normal_exchange;
|
drop table interval_normal_exchange;
|
||||||
|
drop table table_001;
|
||||||
|
ERROR: table "table_001" does not exist
|
||||||
|
create table table_001(
|
||||||
|
COL_1 smallint,
|
||||||
|
COL_2 char(5),
|
||||||
|
COL_3 int,
|
||||||
|
COL_4 date,
|
||||||
|
COL_5 boolean,
|
||||||
|
COL_6 nchar(5),
|
||||||
|
COL_7 float
|
||||||
|
);
|
||||||
|
drop table partition_table_001;
|
||||||
|
ERROR: table "partition_table_001" does not exist
|
||||||
|
create table partition_table_001(
|
||||||
|
COL_1 smallint,
|
||||||
|
COL_2 char(5),
|
||||||
|
COL_3 int,
|
||||||
|
COL_4 date,
|
||||||
|
COL_5 boolean,
|
||||||
|
COL_6 nchar(5),
|
||||||
|
COL_7 float
|
||||||
|
)
|
||||||
|
PARTITION BY RANGE (COL_4)
|
||||||
|
INTERVAL ('1 month')
|
||||||
|
(
|
||||||
|
PARTITION partition_table_001_p1 VALUES LESS THAN ('2020-03-01'),
|
||||||
|
PARTITION partition_table_001_p2 VALUES LESS THAN ('2020-04-01'),
|
||||||
|
PARTITION partition_table_001_p3 VALUES LESS THAN ('2020-05-01')
|
||||||
|
);
|
||||||
|
insert into partition_table_001 values (1,'aaa',1,'2020-02-23',true,'aaa',1.1);
|
||||||
|
insert into partition_table_001 values (2,'bbb',2,'2020-03-23',false,'bbb',2.2);
|
||||||
|
insert into partition_table_001 values (3,'ccc',3,'2020-04-23',true,'ccc',3.3);
|
||||||
|
insert into partition_table_001 values (4,'ddd',4,'2020-05-23',false,'ddd',4.4);
|
||||||
|
insert into partition_table_001 values (5,'eee',5,'2020-06-23',true,'eee',5.5);
|
||||||
|
insert into partition_table_001 values (6,'fff',6,'2020-07-23',false,'fff',6.6);
|
||||||
|
ALTER TABLE partition_table_001 EXCHANGE PARTITION (sys_p1) WITH TABLE table_001;
|
||||||
|
select * from table_001 order by 1;
|
||||||
|
col_1 | col_2 | col_3 | col_4 | col_5 | col_6 | col_7
|
||||||
|
-------+-------+-------+--------------------------+-------+-------+-------
|
||||||
|
4 | ddd | 4 | Sat May 23 00:00:00 2020 | f | ddd | 4.4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select * from partition_table_001 order by 1;
|
||||||
|
col_1 | col_2 | col_3 | col_4 | col_5 | col_6 | col_7
|
||||||
|
-------+-------+-------+--------------------------+-------+-------+-------
|
||||||
|
1 | aaa | 1 | Sun Feb 23 00:00:00 2020 | t | aaa | 1.1
|
||||||
|
2 | bbb | 2 | Mon Mar 23 00:00:00 2020 | f | bbb | 2.2
|
||||||
|
3 | ccc | 3 | Thu Apr 23 00:00:00 2020 | t | ccc | 3.3
|
||||||
|
5 | eee | 5 | Tue Jun 23 00:00:00 2020 | t | eee | 5.5
|
||||||
|
6 | fff | 6 | Thu Jul 23 00:00:00 2020 | f | fff | 6.6
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
select relname, parttype, partstrategy, boundaries from pg_partition
|
||||||
|
where parentid = (select oid from pg_class where relname = 'partition_table_001')
|
||||||
|
order by relname;
|
||||||
|
relname | parttype | partstrategy | boundaries
|
||||||
|
------------------------+----------+--------------+------------------------------
|
||||||
|
partition_table_001 | r | i |
|
||||||
|
partition_table_001_p1 | p | r | {2020-03-01}
|
||||||
|
partition_table_001_p2 | p | r | {2020-04-01}
|
||||||
|
partition_table_001_p3 | p | r | {2020-05-01}
|
||||||
|
sys_p1 | p | i | {"Mon Jun 01 00:00:00 2020"}
|
||||||
|
sys_p2 | p | i | {"Wed Jul 01 00:00:00 2020"}
|
||||||
|
sys_p3 | p | i | {"Sat Aug 01 00:00:00 2020"}
|
||||||
|
(7 rows)
|
||||||
|
|
||||||
|
ALTER TABLE partition_table_001 EXCHANGE PARTITION (sys_p1) WITH TABLE table_001;
|
||||||
|
select * from table_001 order by 1;
|
||||||
|
col_1 | col_2 | col_3 | col_4 | col_5 | col_6 | col_7
|
||||||
|
-------+-------+-------+-------+-------+-------+-------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
select * from partition_table_001 order by 1;
|
||||||
|
col_1 | col_2 | col_3 | col_4 | col_5 | col_6 | col_7
|
||||||
|
-------+-------+-------+--------------------------+-------+-------+-------
|
||||||
|
1 | aaa | 1 | Sun Feb 23 00:00:00 2020 | t | aaa | 1.1
|
||||||
|
2 | bbb | 2 | Mon Mar 23 00:00:00 2020 | f | bbb | 2.2
|
||||||
|
3 | ccc | 3 | Thu Apr 23 00:00:00 2020 | t | ccc | 3.3
|
||||||
|
4 | ddd | 4 | Sat May 23 00:00:00 2020 | f | ddd | 4.4
|
||||||
|
5 | eee | 5 | Tue Jun 23 00:00:00 2020 | t | eee | 5.5
|
||||||
|
6 | fff | 6 | Thu Jul 23 00:00:00 2020 | f | fff | 6.6
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
insert into table_001 values (7,'eee',7,'2020-08-23',true,'eee',7.7);
|
||||||
|
ALTER TABLE partition_table_001 EXCHANGE PARTITION (sys_p1) WITH TABLE table_001 with validation verbose;
|
||||||
|
select * from table_001 order by 1;
|
||||||
|
col_1 | col_2 | col_3 | col_4 | col_5 | col_6 | col_7
|
||||||
|
-------+-------+-------+--------------------------+-------+-------+-------
|
||||||
|
4 | ddd | 4 | Sat May 23 00:00:00 2020 | f | ddd | 4.4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select * from partition_table_001 order by 1;
|
||||||
|
col_1 | col_2 | col_3 | col_4 | col_5 | col_6 | col_7
|
||||||
|
-------+-------+-------+--------------------------+-------+-------+-------
|
||||||
|
1 | aaa | 1 | Sun Feb 23 00:00:00 2020 | t | aaa | 1.1
|
||||||
|
2 | bbb | 2 | Mon Mar 23 00:00:00 2020 | f | bbb | 2.2
|
||||||
|
3 | ccc | 3 | Thu Apr 23 00:00:00 2020 | t | ccc | 3.3
|
||||||
|
5 | eee | 5 | Tue Jun 23 00:00:00 2020 | t | eee | 5.5
|
||||||
|
6 | fff | 6 | Thu Jul 23 00:00:00 2020 | f | fff | 6.6
|
||||||
|
7 | eee | 7 | Sun Aug 23 00:00:00 2020 | t | eee | 7.7
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
drop table table_001;
|
||||||
|
drop table partition_table_001;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
CREATE TABLE t11 (
|
||||||
|
sec_code character(6) NOT NULL,
|
||||||
|
issue_type character(3) NOT NULL
|
||||||
|
);
|
||||||
|
CREATE TABLE t22 (
|
||||||
|
company_code character(6) NOT NULL,
|
||||||
|
list_profit_flag character(1) NOT NULL
|
||||||
|
);
|
||||||
|
CREATE TABLE t33 (
|
||||||
|
company_code character(6) NOT NULL,
|
||||||
|
issue_flag character(6) NOT NULL
|
||||||
|
);
|
||||||
|
insert into t11 values(1,'S04');
|
||||||
|
insert into t22 values(1,'Y');
|
||||||
|
insert into t33 values(1,'1');
|
||||||
|
insert into t33 values(2,'2');
|
||||||
|
select
|
||||||
|
T5.issue_type
|
||||||
|
,T6.is_type
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select T1.sec_code
|
||||||
|
,issue_type
|
||||||
|
--,case when list_profit_flag='Y' then '是' else '否' end as list_profit_flag
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select sec_code
|
||||||
|
,case when issue_type in ('S04','S05','S06','S09') then '增发'
|
||||||
|
when issue_type in ('S01','S02') then '首发'
|
||||||
|
else '其他' end as issue_type
|
||||||
|
from t11
|
||||||
|
)T1
|
||||||
|
left join
|
||||||
|
(
|
||||||
|
select list_profit_flag
|
||||||
|
,company_code
|
||||||
|
from t22
|
||||||
|
)T2
|
||||||
|
on T1.sec_code=T2.company_code
|
||||||
|
)T5
|
||||||
|
full join
|
||||||
|
(
|
||||||
|
SELECT CASE
|
||||||
|
WHEN issue_flag = ANY (ARRAY['1'::bpchar, '3'::bpchar, '11'::bpchar]) THEN '首发'::text
|
||||||
|
ELSE '增发'::text
|
||||||
|
END AS is_type from t33
|
||||||
|
)T6
|
||||||
|
on T5.issue_type=T6.is_type
|
||||||
|
;
|
||||||
|
issue_type | is_type
|
||||||
|
------------+---------
|
||||||
|
增发 | 增发
|
||||||
|
| 首发
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
|
@ -89,5 +89,8 @@ ERROR: Can not use compress option in ustore index.
|
||||||
-- segment
|
-- segment
|
||||||
CREATE TABLE unspported_feature.segment_table(id int, c1 text) WITH(compresstype=2, segment=on); --failed
|
CREATE TABLE unspported_feature.segment_table(id int, c1 text) WITH(compresstype=2, segment=on); --failed
|
||||||
ERROR: only row orientation table support compresstype.
|
ERROR: only row orientation table support compresstype.
|
||||||
CREATE INDEX on unspported_feature.index_test(c1) WITH(compresstype=2, segment=on); --faled
|
CREATE INDEX on unspported_feature.index_test(c1) WITH(compresstype=2, segment=on); --failed
|
||||||
ERROR: Can not use compress option in segment storage.
|
ERROR: Can not use compress option in segment storage.
|
||||||
|
-- set compress_diff_convert
|
||||||
|
create table unspported_feature.compress_byte_test(id int) with (compresstype=2, compress_byte_convert=false, compress_diff_convert = true); -- failed
|
||||||
|
ERROR: compress_diff_convert should be used with compress_byte_convert.
|
||||||
|
|
|
||||||
|
|
@ -1062,6 +1062,73 @@ select /*+ indexscan(tg_hash i_tg_hash_local_a) */ * from tg_hash where a < 9;
|
||||||
(81 rows)
|
(81 rows)
|
||||||
|
|
||||||
drop table tg_hash;
|
drop table tg_hash;
|
||||||
|
drop table if exists test_ugi_045;
|
||||||
|
NOTICE: table "test_ugi_045" does not exist, skipping
|
||||||
|
create table test_ugi_045
|
||||||
|
(
|
||||||
|
c_id integer not null,
|
||||||
|
c_date DATE,
|
||||||
|
c_info varchar(20) not null
|
||||||
|
)
|
||||||
|
partition by range(c_date)
|
||||||
|
interval('2 day')
|
||||||
|
(
|
||||||
|
partition p1 values less than ('2021-06-01 00:00:00'),
|
||||||
|
partition p2 values less than ('2021-06-03 00:00:00'),
|
||||||
|
partition p3 values less than ('2021-06-05 00:00:00'),
|
||||||
|
partition p4 values less than ('2021-06-07 00:00:00'),
|
||||||
|
partition p5 values less than ('2021-06-09 00:00:00')
|
||||||
|
);
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(1, '2021-05-30 00:00:00', '1-1');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(2, '2021-05-31 00:00:00', '1-1');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(3, '2021-06-01 00:00:00', '1-1');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(4, '2021-06-02 00:00:00', '1-2');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(5, '2021-06-03 00:00:00', '1-2');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(6, '2021-06-04 00:00:00', '1-3');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(7, '2021-06-05 00:00:00', '1-3');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(8, '2021-06-06 00:00:00', '1-4');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(9, '2021-06-07 00:00:00', '1-4');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(10, '2021-06-08 00:00:00', '1-5');
|
||||||
|
create index global_index_id_045 on test_ugi_045(c_date) global;
|
||||||
|
create index global_index_info_045 on test_ugi_045(c_info) global;
|
||||||
|
select *from test_ugi_045;
|
||||||
|
c_id | c_date | c_info
|
||||||
|
------+---------------------+--------
|
||||||
|
1 | 2021-05-30 00:00:00 | 1-1
|
||||||
|
2 | 2021-05-31 00:00:00 | 1-1
|
||||||
|
3 | 2021-06-01 00:00:00 | 1-1
|
||||||
|
4 | 2021-06-02 00:00:00 | 1-2
|
||||||
|
5 | 2021-06-03 00:00:00 | 1-2
|
||||||
|
6 | 2021-06-04 00:00:00 | 1-3
|
||||||
|
7 | 2021-06-05 00:00:00 | 1-3
|
||||||
|
8 | 2021-06-06 00:00:00 | 1-4
|
||||||
|
9 | 2021-06-07 00:00:00 | 1-4
|
||||||
|
10 | 2021-06-08 00:00:00 | 1-5
|
||||||
|
(10 rows)
|
||||||
|
|
||||||
|
alter table test_ugi_045 truncate partition p3 update global index;
|
||||||
|
set enable_seqscan = off;
|
||||||
|
set enable_bitmapscan = off;
|
||||||
|
select *from test_ugi_045;
|
||||||
|
c_id | c_date | c_info
|
||||||
|
------+---------------------+--------
|
||||||
|
1 | 2021-05-30 00:00:00 | 1-1
|
||||||
|
2 | 2021-05-31 00:00:00 | 1-1
|
||||||
|
3 | 2021-06-01 00:00:00 | 1-1
|
||||||
|
4 | 2021-06-02 00:00:00 | 1-2
|
||||||
|
7 | 2021-06-05 00:00:00 | 1-3
|
||||||
|
8 | 2021-06-06 00:00:00 | 1-4
|
||||||
|
9 | 2021-06-07 00:00:00 | 1-4
|
||||||
|
10 | 2021-06-08 00:00:00 | 1-5
|
||||||
|
(8 rows)
|
||||||
|
|
||||||
|
select *from test_ugi_045 where c_info = '1-2';
|
||||||
|
c_id | c_date | c_info
|
||||||
|
------+---------------------+--------
|
||||||
|
4 | 2021-06-02 00:00:00 | 1-2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
drop table if exists test_ugi_045;
|
||||||
-- 清理过程
|
-- 清理过程
|
||||||
drop view check_truncate_results;
|
drop view check_truncate_results;
|
||||||
drop materialized view pg_partition_before_truncate;
|
drop materialized view pg_partition_before_truncate;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ CREATE SUBSCRIPTION testsub CONNECTION 'foo';
|
||||||
CREATE SUBSCRIPTION testsub PUBLICATION foo;
|
CREATE SUBSCRIPTION testsub PUBLICATION foo;
|
||||||
-- fail - could not connect to the publisher
|
-- fail - could not connect to the publisher
|
||||||
create subscription testsub2 connection 'host=abc' publication pub;
|
create subscription testsub2 connection 'host=abc' publication pub;
|
||||||
|
create subscription testsub2 connection 'host=abc port=12345' publication pub;
|
||||||
set client_min_messages to error;
|
set client_min_messages to error;
|
||||||
-- fail - syntax error, invalid connection string syntax: missing "="
|
-- fail - syntax error, invalid connection string syntax: missing "="
|
||||||
CREATE SUBSCRIPTION testsub CONNECTION 'testconn' PUBLICATION testpub;
|
CREATE SUBSCRIPTION testsub CONNECTION 'testconn' PUBLICATION testpub;
|
||||||
|
|
@ -32,12 +33,15 @@ CREATE SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist' PUBLICATION testpub
|
||||||
CREATE SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (ENABLED=false, slot_name='testsub', synchronous_commit=off);
|
CREATE SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (ENABLED=false, slot_name='testsub', synchronous_commit=off);
|
||||||
-- create SUBSCRIPTION with conninfo in two single quote, used to check mask string bug
|
-- create SUBSCRIPTION with conninfo in two single quote, used to check mask string bug
|
||||||
CREATE SUBSCRIPTION testsub_maskconninfo CONNECTION 'host=''1.2.3.4'' port=''12345'' user=''username'' dbname=''postgres'' password=''password_1234''' PUBLICATION testpub WITH (ENABLED=false, slot_name='testsub', synchronous_commit=off);
|
CREATE SUBSCRIPTION testsub_maskconninfo CONNECTION 'host=''1.2.3.4'' port=''12345'' user=''username'' dbname=''postgres'' password=''password_1234''' PUBLICATION testpub WITH (ENABLED=false, slot_name='testsub', synchronous_commit=off);
|
||||||
|
-- fail - The number of host and port are inconsistent
|
||||||
|
create subscription sub1 connection 'dbname=postgres user=pubusr password=Huawei@123 host=192.168.0.38,192.168.0.38,192.168.0.38 port=14001,14501' publication pub1;
|
||||||
|
-- fail - a maximum of 9 servers are supported
|
||||||
|
create subscription sub1 connection 'dbname=postgres user=pubusr password=Huawei@123 host=192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38 port=14001,14501' publication pub1;
|
||||||
-- alter connection
|
-- alter connection
|
||||||
ALTER SUBSCRIPTION testsub CONNECTION 'host=''1.2.3.4'' port=''12345'' user=''username'' dbname=''postgres'' password=''password_1234''';
|
ALTER SUBSCRIPTION testsub CONNECTION 'host=''1.2.3.4'' port=''12345'' user=''username'' dbname=''postgres'' password=''password_1234''';
|
||||||
ALTER SUBSCRIPTION testsub CONNECTION 'dbname=does_not_exist';
|
ALTER SUBSCRIPTION testsub CONNECTION 'dbname=does_not_exist';
|
||||||
reset client_min_messages;
|
reset client_min_messages;
|
||||||
select subname, pg_get_userbyid(subowner) as Owner, subenabled, subconninfo, subpublications from pg_subscription where subname='testsub';
|
select subname, pg_get_userbyid(subowner) as Owner, subenabled, subconninfo, subpublications, subbinary from pg_subscription where subname='testsub';
|
||||||
--- alter subscription
|
--- alter subscription
|
||||||
------ set publication
|
------ set publication
|
||||||
ALTER SUBSCRIPTION testsub SET PUBLICATION testpub2, testpub3;
|
ALTER SUBSCRIPTION testsub SET PUBLICATION testpub2, testpub3;
|
||||||
|
|
@ -57,6 +61,9 @@ select subname, subenabled, subsynccommit from pg_subscription where subname='t
|
||||||
ALTER SUBSCRIPTION testsub SET (slot_name='testsub');
|
ALTER SUBSCRIPTION testsub SET (slot_name='testsub');
|
||||||
-- alter owner
|
-- alter owner
|
||||||
ALTER SUBSCRIPTION testsub owner to regress_subscription_user2;
|
ALTER SUBSCRIPTION testsub owner to regress_subscription_user2;
|
||||||
|
-- alter subbinary to true
|
||||||
|
ALTER SUBSCRIPTION testsub SET (binary=true);
|
||||||
|
select subname, subbinary from pg_subscription where subname='testsub';
|
||||||
--rename
|
--rename
|
||||||
ALTER SUBSCRIPTION testsub rename to testsub_rename;
|
ALTER SUBSCRIPTION testsub rename to testsub_rename;
|
||||||
--- inside a transaction block
|
--- inside a transaction block
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,10 @@ LINE 1: CREATE SUBSCRIPTION testsub PUBLICATION foo;
|
||||||
^
|
^
|
||||||
-- fail - could not connect to the publisher
|
-- fail - could not connect to the publisher
|
||||||
create subscription testsub2 connection 'host=abc' publication pub;
|
create subscription testsub2 connection 'host=abc' publication pub;
|
||||||
|
ERROR: The number of host and port are inconsistent.
|
||||||
|
create subscription testsub2 connection 'host=abc port=12345' publication pub;
|
||||||
WARNING: apply worker could not connect to the remote server
|
WARNING: apply worker could not connect to the remote server
|
||||||
ERROR: could not connect to the publisher
|
ERROR: Failed to connect to publisher.
|
||||||
set client_min_messages to error;
|
set client_min_messages to error;
|
||||||
-- fail - syntax error, invalid connection string syntax: missing "="
|
-- fail - syntax error, invalid connection string syntax: missing "="
|
||||||
CREATE SUBSCRIPTION testsub CONNECTION 'testconn' PUBLICATION testpub;
|
CREATE SUBSCRIPTION testsub CONNECTION 'testconn' PUBLICATION testpub;
|
||||||
|
|
@ -72,14 +74,20 @@ ERROR: unrecognized subscription parameter: create_slot
|
||||||
CREATE SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (ENABLED=false, slot_name='testsub', synchronous_commit=off);
|
CREATE SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (ENABLED=false, slot_name='testsub', synchronous_commit=off);
|
||||||
-- create SUBSCRIPTION with conninfo in two single quote, used to check mask string bug
|
-- create SUBSCRIPTION with conninfo in two single quote, used to check mask string bug
|
||||||
CREATE SUBSCRIPTION testsub_maskconninfo CONNECTION 'host=''1.2.3.4'' port=''12345'' user=''username'' dbname=''postgres'' password=''password_1234''' PUBLICATION testpub WITH (ENABLED=false, slot_name='testsub', synchronous_commit=off);
|
CREATE SUBSCRIPTION testsub_maskconninfo CONNECTION 'host=''1.2.3.4'' port=''12345'' user=''username'' dbname=''postgres'' password=''password_1234''' PUBLICATION testpub WITH (ENABLED=false, slot_name='testsub', synchronous_commit=off);
|
||||||
|
-- fail - The number of host and port are inconsistent
|
||||||
|
create subscription sub1 connection 'dbname=postgres user=pubusr password=Huawei@123 host=192.168.0.38,192.168.0.38,192.168.0.38 port=14001,14501' publication pub1;
|
||||||
|
ERROR: The number of host and port are inconsistent.
|
||||||
|
-- fail - a maximum of 9 servers are supported
|
||||||
|
create subscription sub1 connection 'dbname=postgres user=pubusr password=Huawei@123 host=192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38,192.168.0.38 port=14001,14501' publication pub1;
|
||||||
|
ERROR: Currently, a maximum of 9 servers are supported.
|
||||||
-- alter connection
|
-- alter connection
|
||||||
ALTER SUBSCRIPTION testsub CONNECTION 'host=''1.2.3.4'' port=''12345'' user=''username'' dbname=''postgres'' password=''password_1234''';
|
ALTER SUBSCRIPTION testsub CONNECTION 'host=''1.2.3.4'' port=''12345'' user=''username'' dbname=''postgres'' password=''password_1234''';
|
||||||
ALTER SUBSCRIPTION testsub CONNECTION 'dbname=does_not_exist';
|
ALTER SUBSCRIPTION testsub CONNECTION 'dbname=does_not_exist';
|
||||||
reset client_min_messages;
|
reset client_min_messages;
|
||||||
select subname, pg_get_userbyid(subowner) as Owner, subenabled, subconninfo, subpublications from pg_subscription where subname='testsub';
|
select subname, pg_get_userbyid(subowner) as Owner, subenabled, subconninfo, subpublications, subbinary from pg_subscription where subname='testsub';
|
||||||
subname | owner | subenabled | subconninfo | subpublications
|
subname | owner | subenabled | subconninfo | subpublications | subbinary
|
||||||
---------+---------------------------+------------+------------------------+-----------------
|
---------+---------------------------+------------+------------------------+-----------------+-----------
|
||||||
testsub | regress_subscription_user | f | dbname=does_not_exist | {testpub}
|
testsub | regress_subscription_user | f | dbname=does_not_exist | {testpub} | f
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
--- alter subscription
|
--- alter subscription
|
||||||
|
|
@ -122,6 +130,14 @@ ALTER SUBSCRIPTION testsub SET (slot_name='testsub');
|
||||||
ERROR: Currently enabled=false, cannot change slot_name to a non-null value.
|
ERROR: Currently enabled=false, cannot change slot_name to a non-null value.
|
||||||
-- alter owner
|
-- alter owner
|
||||||
ALTER SUBSCRIPTION testsub owner to regress_subscription_user2;
|
ALTER SUBSCRIPTION testsub owner to regress_subscription_user2;
|
||||||
|
-- alter subbinary to true
|
||||||
|
ALTER SUBSCRIPTION testsub SET (binary=true);
|
||||||
|
select subname, subbinary from pg_subscription where subname='testsub';
|
||||||
|
subname | subbinary
|
||||||
|
---------+-----------
|
||||||
|
testsub | t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
--rename
|
--rename
|
||||||
ALTER SUBSCRIPTION testsub rename to testsub_rename;
|
ALTER SUBSCRIPTION testsub rename to testsub_rename;
|
||||||
--- inside a transaction block
|
--- inside a transaction block
|
||||||
|
|
@ -134,8 +150,7 @@ COMMIT;
|
||||||
-- -- active SUBSCRIPTION
|
-- -- active SUBSCRIPTION
|
||||||
BEGIN;
|
BEGIN;
|
||||||
ALTER SUBSCRIPTION testsub_rename ENABLE;
|
ALTER SUBSCRIPTION testsub_rename ENABLE;
|
||||||
WARNING: apply worker could not connect to the remote server
|
ERROR: invalid connection string syntax, missing host and port
|
||||||
ERROR: could not connect to the publisher
|
|
||||||
select subname, subenabled from pg_subscription where subname='testsub_rename';
|
select subname, subenabled from pg_subscription where subname='testsub_rename';
|
||||||
ERROR: current transaction is aborted, commands ignored until end of transaction block, firstChar[Q]
|
ERROR: current transaction is aborted, commands ignored until end of transaction block, firstChar[Q]
|
||||||
ALTER SUBSCRIPTION testsub_rename SET (ENABLED=false);
|
ALTER SUBSCRIPTION testsub_rename SET (ENABLED=false);
|
||||||
|
|
@ -281,10 +296,11 @@ SELECT object_name,detail_info FROM pg_query_audit('2022-01-13 9:30:00', '2031-1
|
||||||
testsub_maskconninfo | ALTER SUBSCRIPTION testsub_maskconninfo SET (conninfo='*************************************************************************************************************************;
|
testsub_maskconninfo | ALTER SUBSCRIPTION testsub_maskconninfo SET (conninfo='*************************************************************************************************************************;
|
||||||
testsub | ALTER SUBSCRIPTION testsub SET (synchronous_commit=on);
|
testsub | ALTER SUBSCRIPTION testsub SET (synchronous_commit=on);
|
||||||
testsub | ALTER SUBSCRIPTION testsub owner to regress_subscription_user2;
|
testsub | ALTER SUBSCRIPTION testsub owner to regress_subscription_user2;
|
||||||
|
testsub | ALTER SUBSCRIPTION testsub SET (binary=true);
|
||||||
testsub | ALTER SUBSCRIPTION testsub rename to testsub_rename;
|
testsub | ALTER SUBSCRIPTION testsub rename to testsub_rename;
|
||||||
testsub_rename | DROP SUBSCRIPTION IF EXISTS testsub_rename;
|
testsub_rename | DROP SUBSCRIPTION IF EXISTS testsub_rename;
|
||||||
testsub_maskconninfo | DROP SUBSCRIPTION IF EXISTS testsub_maskconninfo;
|
testsub_maskconninfo | DROP SUBSCRIPTION IF EXISTS testsub_maskconninfo;
|
||||||
(14 rows)
|
(15 rows)
|
||||||
|
|
||||||
--clear audit log
|
--clear audit log
|
||||||
SELECT pg_delete_audit('1012-11-10', '3012-11-11');
|
SELECT pg_delete_audit('1012-11-10', '3012-11-11');
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ test: pldeveloper_gs_source
|
||||||
test: index_advisor
|
test: index_advisor
|
||||||
test: pl_debugger_server pl_debugger_client
|
test: pl_debugger_server pl_debugger_client
|
||||||
test: update_for_wait_s1 update_for_wait_s2
|
test: update_for_wait_s1 update_for_wait_s2
|
||||||
test: plan_hint plan_hint_set plan_hint_no_expand plan_hint_iud null_test_opt
|
test: plan_hint plan_hint_set plan_hint_no_expand plan_hint_iud null_test_opt deserialize_func
|
||||||
test: large_sequence int16 gs_dump_sequence
|
test: large_sequence int16 gs_dump_sequence
|
||||||
test: gs_dump_tableof
|
test: gs_dump_tableof
|
||||||
test: analyze_commands
|
test: analyze_commands
|
||||||
|
|
@ -916,3 +916,5 @@ test: fdw_audit
|
||||||
test: gs_global_config_audit
|
test: gs_global_config_audit
|
||||||
test: detail
|
test: detail
|
||||||
test: gs_dump_encrypt
|
test: gs_dump_encrypt
|
||||||
|
test: composite_datum_record
|
||||||
|
test: join_test_alias
|
||||||
|
|
|
||||||
|
|
@ -5238,7 +5238,7 @@ static void check_global_variables()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BASE_PGXC_LIKE_MACRO_NUM 1401
|
#define BASE_PGXC_LIKE_MACRO_NUM 1402
|
||||||
static void check_pgxc_like_macros()
|
static void check_pgxc_like_macros()
|
||||||
{
|
{
|
||||||
#ifdef BUILD_BY_CMAKE
|
#ifdef BUILD_BY_CMAKE
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
--
|
||||||
|
-- check for over-optimization of whole-row Var referencing an Append plan
|
||||||
|
--
|
||||||
|
create table int4_test(f1 int4);
|
||||||
|
select (select q from (select 1,2,3 where f1>0 union all select 4,5,6 where f1<=0) q) from int4_test;
|
||||||
|
select (select q from (select 1,2,3 where f1>0 union all select 4,5,6.0 where f1<=0) q) from int4_test;
|
||||||
|
insert into int4_test(f1) values(' 0 ');
|
||||||
|
insert into int4_test(f1) values('123456 ');
|
||||||
|
select * from int4_test;
|
||||||
|
select (select q from (select 1,2,3 where f1>0 union all select 4,5,6 where f1<=0) q) from int4_test;
|
||||||
|
select (select q from (select 1,2,3 where f1>0 union all select 4,5,6.0 where f1<=0) q) from int4_test;
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
create schema deserialize_func1;
|
||||||
|
set current_schema = deserialize_func1;
|
||||||
|
|
||||||
|
create sequence test_seq;
|
||||||
|
|
||||||
|
create or replace function test_func() return int
|
||||||
|
as
|
||||||
|
begin
|
||||||
|
return 1;
|
||||||
|
end;
|
||||||
|
/
|
||||||
|
|
||||||
|
create table t1(key int, id int default nextval('test_seq'));
|
||||||
|
|
||||||
|
create table t2(key int, id int default test_func());
|
||||||
|
|
||||||
|
select * from pg_get_tabledef('deserialize_func1.t1');
|
||||||
|
|
||||||
|
select * from pg_get_tabledef('deserialize_func1.t2');
|
||||||
|
|
||||||
|
alter schema deserialize_func1 rename to deserialize_func2;
|
||||||
|
|
||||||
|
select * from pg_get_tabledef('deserialize_func2.t1');
|
||||||
|
|
||||||
|
select * from pg_get_tabledef('deserialize_func2.t2');
|
||||||
|
|
||||||
|
alter table deserialize_func2.t1 alter column id drop default;
|
||||||
|
|
||||||
|
alter table deserialize_func2.t1 alter column id set default nextval('deserialize_func2.test_seq');
|
||||||
|
|
||||||
|
select * from pg_get_tabledef('deserialize_func2.t1');
|
||||||
|
|
||||||
|
alter table deserialize_func2.t2 alter column id drop default;
|
||||||
|
|
||||||
|
alter table deserialize_func2.t2 alter column id set default deserialize_func2.test_func();
|
||||||
|
|
||||||
|
select * from pg_get_tabledef('deserialize_func2.t2');
|
||||||
|
|
||||||
|
set current_schema = deserialize_func2;
|
||||||
|
|
||||||
|
create type type1 as (a int);
|
||||||
|
|
||||||
|
create view v1 as select '(1)'::type1;
|
||||||
|
|
||||||
|
alter type type1 rename to type2;
|
||||||
|
|
||||||
|
select pg_get_viewdef('v1');
|
||||||
|
|
||||||
|
drop view v1 cascade;
|
||||||
|
|
||||||
|
create table atest12 as select x as a, 10001-x as b from generate_series(1, 10000) x;
|
||||||
|
|
||||||
|
create function func1(integer, integer) returns boolean as $$begin return $1 < $2; end $$ language plpgsql immutable;
|
||||||
|
|
||||||
|
create operator <<< (procedure=func1, leftarg=integer, rightarg=integer, restrict=scalarltsel);
|
||||||
|
|
||||||
|
create view atest12v as select * from atest12 where b <<< 5;
|
||||||
|
|
||||||
|
select pg_get_viewdef('atest12v');
|
||||||
|
|
||||||
|
drop schema if exists deserialize_func1 cascade;
|
||||||
|
|
||||||
|
drop schema if exists deserialize_func2 cascade;
|
||||||
|
|
@ -145,9 +145,13 @@ DROP TEXT SEARCH CONFIGURATION test_tsconfig_exists;
|
||||||
-- doesn't exists
|
-- doesn't exists
|
||||||
DROP EXTENSION test_extension_exists;
|
DROP EXTENSION test_extension_exists;
|
||||||
DROP EXTENSION IF EXISTS test_extension_exists;
|
DROP EXTENSION IF EXISTS test_extension_exists;
|
||||||
-- exists but doesn't support drop
|
-- only extension in white list support drop, but the guc support_extended_features is true in pg_regress mode, so hdfs_fdw can be droped
|
||||||
DROP EXTENSION plpgsql;
|
CREATE DATABASE ext_test_db;
|
||||||
DROP EXTENSION IF EXISTS plpgsql;
|
\c ext_test_db
|
||||||
|
DROP EXTENSION hdfs_fdw;
|
||||||
|
DROP EXTENSION IF EXISTS hdfs_fdw;
|
||||||
|
\c regression
|
||||||
|
DROP DATABASE ext_test_db;
|
||||||
|
|
||||||
-- functions
|
-- functions
|
||||||
DROP FUNCTION test_function_exists();
|
DROP FUNCTION test_function_exists();
|
||||||
|
|
|
||||||
|
|
@ -97,3 +97,61 @@ select * from interval_exchange_test order by logdate;
|
||||||
select * from interval_normal_exchange order by logdate;
|
select * from interval_normal_exchange order by logdate;
|
||||||
select * from interval_normal_exchange where logdate > '2020-06-01' order by logdate;
|
select * from interval_normal_exchange where logdate > '2020-06-01' order by logdate;
|
||||||
drop table interval_normal_exchange;
|
drop table interval_normal_exchange;
|
||||||
|
|
||||||
|
drop table table_001;
|
||||||
|
create table table_001(
|
||||||
|
COL_1 smallint,
|
||||||
|
COL_2 char(5),
|
||||||
|
COL_3 int,
|
||||||
|
COL_4 date,
|
||||||
|
COL_5 boolean,
|
||||||
|
COL_6 nchar(5),
|
||||||
|
COL_7 float
|
||||||
|
);
|
||||||
|
|
||||||
|
drop table partition_table_001;
|
||||||
|
create table partition_table_001(
|
||||||
|
COL_1 smallint,
|
||||||
|
COL_2 char(5),
|
||||||
|
COL_3 int,
|
||||||
|
COL_4 date,
|
||||||
|
COL_5 boolean,
|
||||||
|
COL_6 nchar(5),
|
||||||
|
COL_7 float
|
||||||
|
)
|
||||||
|
PARTITION BY RANGE (COL_4)
|
||||||
|
INTERVAL ('1 month')
|
||||||
|
(
|
||||||
|
PARTITION partition_table_001_p1 VALUES LESS THAN ('2020-03-01'),
|
||||||
|
PARTITION partition_table_001_p2 VALUES LESS THAN ('2020-04-01'),
|
||||||
|
PARTITION partition_table_001_p3 VALUES LESS THAN ('2020-05-01')
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into partition_table_001 values (1,'aaa',1,'2020-02-23',true,'aaa',1.1);
|
||||||
|
insert into partition_table_001 values (2,'bbb',2,'2020-03-23',false,'bbb',2.2);
|
||||||
|
insert into partition_table_001 values (3,'ccc',3,'2020-04-23',true,'ccc',3.3);
|
||||||
|
insert into partition_table_001 values (4,'ddd',4,'2020-05-23',false,'ddd',4.4);
|
||||||
|
insert into partition_table_001 values (5,'eee',5,'2020-06-23',true,'eee',5.5);
|
||||||
|
insert into partition_table_001 values (6,'fff',6,'2020-07-23',false,'fff',6.6);
|
||||||
|
|
||||||
|
ALTER TABLE partition_table_001 EXCHANGE PARTITION (sys_p1) WITH TABLE table_001;
|
||||||
|
select * from table_001 order by 1;
|
||||||
|
select * from partition_table_001 order by 1;
|
||||||
|
|
||||||
|
select relname, parttype, partstrategy, boundaries from pg_partition
|
||||||
|
where parentid = (select oid from pg_class where relname = 'partition_table_001')
|
||||||
|
order by relname;
|
||||||
|
|
||||||
|
ALTER TABLE partition_table_001 EXCHANGE PARTITION (sys_p1) WITH TABLE table_001;
|
||||||
|
select * from table_001 order by 1;
|
||||||
|
select * from partition_table_001 order by 1;
|
||||||
|
|
||||||
|
insert into table_001 values (7,'eee',7,'2020-08-23',true,'eee',7.7);
|
||||||
|
|
||||||
|
ALTER TABLE partition_table_001 EXCHANGE PARTITION (sys_p1) WITH TABLE table_001 with validation verbose;
|
||||||
|
|
||||||
|
select * from table_001 order by 1;
|
||||||
|
select * from partition_table_001 order by 1;
|
||||||
|
|
||||||
|
drop table table_001;
|
||||||
|
drop table partition_table_001;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
CREATE TABLE t11 (
|
||||||
|
sec_code character(6) NOT NULL,
|
||||||
|
issue_type character(3) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE t22 (
|
||||||
|
company_code character(6) NOT NULL,
|
||||||
|
list_profit_flag character(1) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE t33 (
|
||||||
|
company_code character(6) NOT NULL,
|
||||||
|
issue_flag character(6) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into t11 values(1,'S04');
|
||||||
|
insert into t22 values(1,'Y');
|
||||||
|
insert into t33 values(1,'1');
|
||||||
|
insert into t33 values(2,'2');
|
||||||
|
|
||||||
|
select
|
||||||
|
T5.issue_type
|
||||||
|
,T6.is_type
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select T1.sec_code
|
||||||
|
,issue_type
|
||||||
|
--,case when list_profit_flag='Y' then '是' else '否' end as list_profit_flag
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select sec_code
|
||||||
|
,case when issue_type in ('S04','S05','S06','S09') then '增发'
|
||||||
|
when issue_type in ('S01','S02') then '首发'
|
||||||
|
else '其他' end as issue_type
|
||||||
|
from t11
|
||||||
|
)T1
|
||||||
|
left join
|
||||||
|
(
|
||||||
|
select list_profit_flag
|
||||||
|
,company_code
|
||||||
|
from t22
|
||||||
|
)T2
|
||||||
|
on T1.sec_code=T2.company_code
|
||||||
|
)T5
|
||||||
|
full join
|
||||||
|
(
|
||||||
|
SELECT CASE
|
||||||
|
WHEN issue_flag = ANY (ARRAY['1'::bpchar, '3'::bpchar, '11'::bpchar]) THEN '首发'::text
|
||||||
|
ELSE '增发'::text
|
||||||
|
END AS is_type from t33
|
||||||
|
)T6
|
||||||
|
on T5.issue_type=T6.is_type
|
||||||
|
;
|
||||||
|
|
@ -57,4 +57,6 @@ CREATE TABLE unspported_feature.ustore_table(id int, c1 text) WITH(compresstype=
|
||||||
CREATE INDEX tbl_pc_idx1 on unspported_feature.index_test(c1) WITH(compresstype=2, storage_type=ustore); --failed
|
CREATE INDEX tbl_pc_idx1 on unspported_feature.index_test(c1) WITH(compresstype=2, storage_type=ustore); --failed
|
||||||
-- segment
|
-- segment
|
||||||
CREATE TABLE unspported_feature.segment_table(id int, c1 text) WITH(compresstype=2, segment=on); --failed
|
CREATE TABLE unspported_feature.segment_table(id int, c1 text) WITH(compresstype=2, segment=on); --failed
|
||||||
CREATE INDEX on unspported_feature.index_test(c1) WITH(compresstype=2, segment=on); --faled
|
CREATE INDEX on unspported_feature.index_test(c1) WITH(compresstype=2, segment=on); --failed
|
||||||
|
-- set compress_diff_convert
|
||||||
|
create table unspported_feature.compress_byte_test(id int) with (compresstype=2, compress_byte_convert=false, compress_diff_convert = true); -- failed
|
||||||
|
|
@ -192,6 +192,46 @@ explain(costs off) select /*+ indexscan(tg_hash i_tg_hash_local_a) */ * from tg_
|
||||||
select /*+ indexscan(tg_hash i_tg_hash_local_a) */ * from tg_hash where a < 9;
|
select /*+ indexscan(tg_hash i_tg_hash_local_a) */ * from tg_hash where a < 9;
|
||||||
|
|
||||||
drop table tg_hash;
|
drop table tg_hash;
|
||||||
|
|
||||||
|
drop table if exists test_ugi_045;
|
||||||
|
create table test_ugi_045
|
||||||
|
(
|
||||||
|
c_id integer not null,
|
||||||
|
c_date DATE,
|
||||||
|
c_info varchar(20) not null
|
||||||
|
)
|
||||||
|
partition by range(c_date)
|
||||||
|
interval('2 day')
|
||||||
|
(
|
||||||
|
partition p1 values less than ('2021-06-01 00:00:00'),
|
||||||
|
partition p2 values less than ('2021-06-03 00:00:00'),
|
||||||
|
partition p3 values less than ('2021-06-05 00:00:00'),
|
||||||
|
partition p4 values less than ('2021-06-07 00:00:00'),
|
||||||
|
partition p5 values less than ('2021-06-09 00:00:00')
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(1, '2021-05-30 00:00:00', '1-1');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(2, '2021-05-31 00:00:00', '1-1');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(3, '2021-06-01 00:00:00', '1-1');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(4, '2021-06-02 00:00:00', '1-2');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(5, '2021-06-03 00:00:00', '1-2');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(6, '2021-06-04 00:00:00', '1-3');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(7, '2021-06-05 00:00:00', '1-3');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(8, '2021-06-06 00:00:00', '1-4');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(9, '2021-06-07 00:00:00', '1-4');
|
||||||
|
insert into test_ugi_045(c_id, c_date, c_info) values(10, '2021-06-08 00:00:00', '1-5');
|
||||||
|
|
||||||
|
create index global_index_id_045 on test_ugi_045(c_date) global;
|
||||||
|
create index global_index_info_045 on test_ugi_045(c_info) global;
|
||||||
|
select *from test_ugi_045;
|
||||||
|
|
||||||
|
alter table test_ugi_045 truncate partition p3 update global index;
|
||||||
|
set enable_seqscan = off;
|
||||||
|
set enable_bitmapscan = off;
|
||||||
|
select *from test_ugi_045;
|
||||||
|
select *from test_ugi_045 where c_info = '1-2';
|
||||||
|
drop table if exists test_ugi_045;
|
||||||
|
|
||||||
-- 清理过程
|
-- 清理过程
|
||||||
drop view check_truncate_results;
|
drop view check_truncate_results;
|
||||||
drop materialized view pg_partition_before_truncate;
|
drop materialized view pg_partition_before_truncate;
|
||||||
Loading…
Reference in New Issue