Compare commits

..

4 Commits

Author SHA1 Message Date
opengauss-bot c6cf59f29f
!1669 add wm_concat function to use internal datatype
Merge pull request !1669 from 吕辉/wm_concat
2022-05-24 01:44:32 +00:00
opengauss-bot a5410b7f86
!1667 修复enable_global_syscache关闭时连接B兼容性数据库的core问题
Merge pull request !1667 from chenxiaobin/3.0.0
2022-04-11 11:11:06 +00:00
lvhui c711c7dc5f add wm_concat in InternalAggIsSupported 2022-04-11 17:41:56 +08:00
chenxiaobin19 3bea35e8c1 修复enable_global_syscache关闭时连接B兼容性数据库的core问题 2022-04-08 11:11:57 +08:00
143 changed files with 4320 additions and 8527 deletions

View File

@ -72,7 +72,7 @@ select_package_command
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 ]
then
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64), Asianux platform."
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64) platform."
exit 1;
fi
@ -96,21 +96,16 @@ elif [[ "$PLAT_FORM_STR" =~ "kylin" ]]; then
if [ "$PLATFORM_ARCH"X == "aarch64"X ];then
GAUSSDB_EXTRA_FLAGS=" -D__USE_NUMA"
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
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64), Asianux platform."
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64) platform."
echo "Kernel is $kernel"
exit 1
fi
##add platform architecture information
if [ "$PLATFORM_ARCH"X == "aarch64"X ] ; 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), Asianux platform."
if [ "$dist_version" != "openEuler" ] && [ "$dist_version" != "EulerOS" ] && [ "$dist_version" != "Kylin" ] ; then
echo "We only support NUMA on openEuler(aarch64), EulerOS(aarch64), Kylin(aarch64) platform."
exit 1
fi
fi

View File

@ -26,7 +26,6 @@ Complete list of usable sgml source files in this directory.
<!ENTITY alterOperator SYSTEM "alter_operator.sgml">
<!ENTITY alterOperatorClass SYSTEM "alter_opclass.sgml">
<!ENTITY alterOperatorFamily SYSTEM "alter_opfamily.sgml">
<!ENTITY alterProcedure SYSTEM "alter_procedure.sgml">
<!ENTITY alterRole SYSTEM "alter_role.sgml">
<!ENTITY alterSchema SYSTEM "alter_schema.sgml">
<!ENTITY alterServer SYSTEM "alter_server.sgml">

View File

@ -1,37 +0,0 @@
<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>

View File

@ -11,7 +11,7 @@
<refsynopsisdiv>
<synopsis>
DROP TABLE [ IF EXISTS ]
{[schema.]table_name} [, ...] [ CASCADE | RESTRICT ] [ PURGE ];
{[schema.]table_name} [, ...] [ CASCADE | RESTRICT ] [ PURGE ]};
</synopsis>
</refsynopsisdiv>
</refentry>

View File

@ -27,14 +27,7 @@
#include "securec_check.h"
#include "cipher.h"
#include "crypt.h"
/*
function name: crypt_malloc_zero
description: Distribute internal memory
arguments: An integer that designates the size of internal memory distributed
return value: A pointer of type void*
NoteIf the size of internal memory distributed is zero, it's unreasonable. The size should be greater than zero.
At the same time, if malloc fails, program would exit.
*/
void* crypt_malloc_zero(size_t size)
{
void* ret = NULL;

View File

@ -34,14 +34,6 @@
static int check_key_num(const char* password);
static void create_child_dir(const char* pathdir);
/*
function name: check_path
description: Check if the string delivered has the character that should not be included
arguments: A pointer to string that its type is const char
return value: void
Notenone
*/
void check_path(const char *path_name)
{
const char* danger_character_list[] = {"|",
@ -77,14 +69,6 @@ void check_path(const char *path_name)
}
}
/*
function name: check_key_num
description: Check if the password is a null string, if so, then the password is invalid.
At the same time, the function check if the length of password exceeds MAX_CRYPT_LEN, if so, print the error.
arguments: A pointer to string that its type is const char
return value: An integer that its type is static int
NoteThe length of password should not be zero, and never exceeds MAX_CRYPT_LEN
*/
static int check_key_num(const char* password)
{
int key_len = 0;

View File

@ -5923,7 +5923,7 @@ int main(int argc, char** argv)
&option_index)) != -1)
#endif
#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:C: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:dqL:T:Q:", long_options,
&option_index)) != -1)
#endif
#endif

View File

@ -233,7 +233,6 @@ char* all_data_nodename_list = NULL;
const uint32 USTORE_UPGRADE_VERSION = 92368;
const uint32 PACKAGE_ENHANCEMENT = 92444;
const uint32 SUBSCRIPTION_VERSION = 92580;
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
#ifdef DUMPSYSLOG
char* syslogpath = NULL;
@ -4445,16 +4444,23 @@ void getSubscriptions(Archive *fout)
int i_subslotname;
int i_subsynccommit;
int i_subpublications;
int i_subbinary;
int i;
int ntups;
int i, ntups;
if (no_subscriptions || GetVersionNum(fout) < SUBSCRIPTION_VERSION) {
return;
}
if (!isExecUserSuperRole(fout)) {
write_msg(NULL, "WARNING: subscriptions not dumped because current user is not a superuser\n");
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");
}
PQclear(res);
return;
}
@ -4463,20 +4469,14 @@ void getSubscriptions(Archive *fout)
resetPQExpBuffer(query);
/* Get the subscriptions in current database. */
appendPQExpBuffer(query, "SELECT s.tableoid, s.oid, s.subname,"
"(%s s.subowner) AS rolname, s.subconninfo, s.subslotname, "
"s.subsynccommit, s.subpublications, \n", username_subquery);
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 "
appendPQExpBuffer(query,
"SELECT s.tableoid, s.oid, s.subname,"
"(%s s.subowner) AS rolname, "
" s.subconninfo, s.subslotname, s.subsynccommit, s.subpublications "
"FROM pg_catalog.pg_subscription s "
"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);
ntups = PQntuples(res);
@ -4494,7 +4494,6 @@ void getSubscriptions(Archive *fout)
i_subslotname = PQfnumber(res, "subslotname");
i_subsynccommit = PQfnumber(res, "subsynccommit");
i_subpublications = PQfnumber(res, "subpublications");
i_subbinary = PQfnumber(res, "subbinary");
subinfo = (SubscriptionInfo *)pg_malloc(ntups * sizeof(SubscriptionInfo));
@ -4513,7 +4512,6 @@ void getSubscriptions(Archive *fout)
}
subinfo[i].subsynccommit = gs_strdup(PQgetvalue(res, i, i_subsynccommit));
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) {
write_msg(NULL, "WARNING: owner of subscription \"%s\" appears to be invalid\n", subinfo[i].dobj.name);
@ -4580,10 +4578,6 @@ static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
appendPQExpBufferStr(query, "NONE");
}
if (strcmp(subinfo->subbinary, "t") == 0) {
appendPQExpBuffer(query, ", binary = true");
}
if (strcmp(subinfo->subsynccommit, "off") != 0) {
appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
}
@ -10786,11 +10780,6 @@ static void dumpDirectory(Archive* fout)
char* dirpath = 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 */
selectSourceSchema(fout, "pg_catalog");
@ -21400,11 +21389,6 @@ static void dumpSynonym(Archive* fout)
PQExpBuffer q;
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");
query = createPQExpBuffer();
printfPQExpBuffer(query,

View File

@ -498,7 +498,6 @@ typedef struct _SubscriptionInfo {
char *subslotname;
char *subsynccommit;
char *subpublications;
char *subbinary;
} SubscriptionInfo;
/* global decls */

View File

@ -31,9 +31,6 @@
it will be backuped up in external dirs */
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 XLogRecPtr stop_backup_lsn = InvalidXLogRecPtr;
static XLogRecPtr stop_stream_lsn = InvalidXLogRecPtr;
@ -92,11 +89,10 @@ static void backup_cleanup(bool fatal, void *userdata);
static void *backup_files(void *arg);
static void do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs,
bool backup_replslots);
static void do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs);
static void pg_start_backup(const char *label, bool smooth, pgBackup *backup,
PGNodeInfo *nodeInfo, PGconn *conn, bool backup_replslots);
PGNodeInfo *nodeInfo, PGconn *conn);
static void pg_stop_backup(pgBackup *backup, PGconn *pg_startbackup_conn, PGNodeInfo *nodeInfo);
static int checkpoint_timeout(PGconn *backup_conn);
@ -562,7 +558,7 @@ static void sync_files(parray *database_map, const char *database_path, parray *
* Move files from 'pgdata' to a subdirectory in 'backup_path'.
*/
static void
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs, bool backup_replslots)
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs)
{
int i;
char database_path[MAXPGPATH];
@ -595,7 +591,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
securec_check_c(rc, "\0", "\0");
/* Call pg_start_backup function in openGauss connect */
pg_start_backup(label, smooth_checkpoint, &current, nodeInfo, backup_conn, backup_replslots);
pg_start_backup(label, smooth_checkpoint, &current, nodeInfo, backup_conn);
/* Obtain current timeline */
#if PG_VERSION_NUM >= 90600
@ -628,10 +624,10 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
/* list files with the logical path. omit $PGDATA */
if (fio_is_remote(FIO_DB_HOST))
fio_list_dir(backup_files_list, instance_config.pgdata,
true, true, false, backup_logs, true, 0, backup_replslots);
true, true, false, backup_logs, true, 0);
else
dir_list_file(backup_files_list, instance_config.pgdata,
true, true, false, backup_logs, true, 0, FIO_LOCAL_HOST, backup_replslots);
true, true, false, backup_logs, true, 0, FIO_LOCAL_HOST);
/*
* Get database_map (name to oid) for use in partial restore feature.
@ -753,11 +749,6 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
}
pgdata_nobackup_dir = NULL;
if (logical_replslot) {
free_dir_list(logical_replslot);
}
logical_replslot = NULL;
/* Cleanup */
if (backup_list)
{
@ -858,7 +849,7 @@ static void do_after_backup()
*/
int
do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
bool no_validate, bool no_sync, bool backup_logs, bool backup_replslots)
bool no_validate, bool no_sync, bool backup_logs)
{
PGconn *backup_conn = NULL;
PGNodeInfo nodeInfo;
@ -934,7 +925,7 @@ do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
add_note(&current, set_backup_params->note);
/* backup data */
do_backup_instance(backup_conn, &nodeInfo, no_sync, backup_logs, backup_replslots);
do_backup_instance(backup_conn, &nodeInfo, no_sync, backup_logs);
pgut_atexit_pop(backup_cleanup, NULL);
/* compute size of wal files of this backup stored in the archive */
@ -1043,15 +1034,13 @@ confirm_block_size(PGconn *conn, const char *name, int blcksz)
*/
static void
pg_start_backup(const char *label, bool smooth, pgBackup *backup,
PGNodeInfo *nodeInfo, PGconn *conn, bool backup_replslots)
PGNodeInfo *nodeInfo, PGconn *conn)
{
PGresult *res;
const char *params[2];
uint32 lsn_hi;
uint32 lsn_lo;
int ret;
int i;
XLogRecPtr startLsn;
params[0] = label;
@ -1079,33 +1068,7 @@ pg_start_backup(const char *label, bool smooth, pgBackup *backup,
XLogDataFromLSN(ret, PQgetvalue(res, 0, 0), &lsn_hi, &lsn_lo);
securec_check_for_sscanf_s(ret, 2, "\0", "\0");
/* Calculate LSN */
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;
backup->start_lsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
PQclear(res);
}

View File

@ -42,6 +42,13 @@ const char *pgdata_exclude_dir[] =
(const char *)"pg_stat_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(). */
(const char *)"pg_dynshmem",
@ -61,7 +68,7 @@ const char *pgdata_exclude_dir[] =
(const char *)"pg_subtrans",
/* end of list */
NULL, /* pg_log and pg_replslot will be set later */
NULL, /* pg_log will be set later */
NULL
};
@ -121,20 +128,17 @@ may be removed int the future */
static int pgCompareString(const void *str1, const void *str2);
static char dir_check_file(pgFile *file, bool backup_logs, bool backup_replslots);
static char dir_check_file(pgFile *file, bool backup_logs);
static char check_in_tablespace(pgFile *file, bool in_tablespace);
static char check_db_dir(pgFile *file);
static char check_digit_file(pgFile *file);
static char check_nobackup_dir(pgFile *file);
static void dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
bool exclude, bool follow_symlink, bool backup_logs,
bool skip_hidden, int external_dir_num, fio_location location,
bool backup_replslots);
bool skip_hidden, int external_dir_num, fio_location location);
static void opt_path_map(ConfigOption *opt, const char *arg,
TablespaceList *list, const char *type);
char check_logical_replslot_dir(const char *rel_path);
/* Tablespace mapping */
static TablespaceList tablespace_dirs = {NULL, NULL};
/* Extra directories mapping */
@ -534,7 +538,7 @@ db_map_entry_free(void *entry)
void
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,
fio_location location, bool backup_replslots)
fio_location location)
{
pgFile *file;
@ -561,7 +565,7 @@ dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink
parray_append(files, file);
dir_list_file_internal(files, file, root, exclude, follow_symlink,
backup_logs, skip_hidden, external_dir_num, location, backup_replslots);
backup_logs, skip_hidden, external_dir_num, location);
if (!add_root)
pgFileFree(file);
@ -585,7 +589,7 @@ dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink
* - datafiles
*/
static char
dir_check_file(pgFile *file, bool backup_logs, bool backup_replslots)
dir_check_file(pgFile *file, bool backup_logs)
{
int i;
int sscanf_res;
@ -648,29 +652,6 @@ dir_check_file(pgFile *file, bool backup_logs, bool backup_replslots)
}
}
/*
* 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);
if (ret != -1) { /* -1 means need backup */
return ret;
@ -768,35 +749,6 @@ static char check_nobackup_dir(pgFile *file)
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)
{
char ret = -1;
@ -937,8 +889,7 @@ bool SkipSomeDirFile(pgFile *file, struct dirent *dent, bool skipHidden)
static void
dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
bool exclude, bool follow_symlink, bool backup_logs,
bool skip_hidden, int external_dir_num, fio_location location,
bool backup_replslots)
bool skip_hidden, int external_dir_num, fio_location location)
{
DIR *dir;
struct dirent *dent;
@ -986,7 +937,7 @@ dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
if (exclude)
{
check_res = dir_check_file(file, backup_logs, backup_replslots);
check_res = dir_check_file(file, backup_logs);
if (check_res == CHECK_FALSE)
{
/* Skip */
@ -1012,7 +963,7 @@ dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
*/
if (S_ISDIR(file->mode))
dir_list_file_internal(files, file, child, exclude, follow_symlink,
backup_logs, skip_hidden, external_dir_num, location, backup_replslots);
backup_logs, skip_hidden, external_dir_num, location);
}
if (errno && errno != ENOENT)

View File

@ -51,7 +51,6 @@ typedef struct
bool exclusive_backup;
bool skip_hidden;
int external_dir_num;
bool backup_replslots;
} fio_list_dir_request;
typedef struct
@ -1795,7 +1794,7 @@ cleanup:
/* Compile the array of files located on remote machine in directory root */
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 backup_replslots)
bool skip_hidden, int external_dir_num)
{
fio_header hdr;
fio_list_dir_request req;
@ -1812,7 +1811,6 @@ void fio_list_dir(parray *files, const char *root, bool exclude,
req.exclusive_backup = exclusive_backup;
req.skip_hidden = skip_hidden;
req.external_dir_num = external_dir_num;
req.backup_replslots = backup_replslots;
hdr.cop = FIO_LIST_DIR;
hdr.size = sizeof(req);
@ -1872,14 +1870,7 @@ void fio_list_dir(parray *files, const char *root, bool exclude,
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);
}
@ -1923,7 +1914,7 @@ static void fio_list_dir_impl(int out, char* buf)
dir_list_file(file_files, req->path, req->exclude, req->follow_symlink,
req->add_root, req->backup_logs, req->skip_hidden,
req->external_dir_num, FIO_LOCAL_HOST, req->backup_replslots);
req->external_dir_num, FIO_LOCAL_HOST);
/* send information about files to the main process */
for (i = 0; i < (int)parray_num(file_files); i++)

View File

@ -163,7 +163,5 @@ extern z_off_t fio_gzseek(gzFile f, z_off_t offset, int whence);
extern const char* fio_gzerror(gzFile file, int *errnum);
#endif
extern char check_logical_replslot_dir(const char *rel_path);
#endif

View File

@ -154,7 +154,6 @@ void help_pg_probackup(void)
printf(_(" [--remote-port=port] [--ssh-options=ssh_options]\n"));
printf(_(" [--remote-libpath=libpath]\n"));
printf(_(" [--ttl=interval] [--expire-time=time]\n"));
printf(_(" [--backup-pg-replslot]\n"));
printf(_(" [--help]\n"));
printf(_("\n %s restore -B backup-path --instance=instance_name\n"), PROGRAM_NAME);
@ -421,7 +420,6 @@ static void help_backup(void)
printf(_(" [--remote-port=port] [--ssh-options=ssh_options]\n"));
printf(_(" [--remote-libpath=libpath]\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(_(" --instance=instance_name name of the instance\n"));
@ -443,7 +441,6 @@ static void help_backup(void)
printf(_(" --note=text add note to backup\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(_(" --backup-pg-replslot] backup of '%s' directory\n"), PG_REPLSLOT_DIR);
printf(_("\n Logging options:\n"));
printf(_(" --log-level-console=log-level-console\n"));

View File

@ -77,7 +77,6 @@ int rw_timeout = 0;
/* backup options */
bool backup_logs = false;
bool backup_replslots = false;
bool smooth_checkpoint;
char *remote_agent;
static char *backup_note = NULL;
@ -187,7 +186,6 @@ static ConfigOption cmd_options[] =
{ 'b', 145, "wal", &delete_wal, SOURCE_CMD_STRICT },
{ 'b', 146, "expired", &delete_expired, 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', 148, "compress", &compress_shortcut, SOURCE_CMD_STRICT },
@ -552,7 +550,7 @@ static int do_actual_operate()
elog(ERROR, "required parameter not specified: BACKUP_MODE "
"(-b, --backup-mode)");
return do_backup(start_time, set_backup_params, no_validate, no_sync, backup_logs, backup_replslots);
return do_backup(start_time, set_backup_params, no_validate, no_sync, backup_logs);
}
case RESTORE_CMD:
return do_restore_or_validate(current.backup_id,

View File

@ -69,7 +69,6 @@ extern const char *PROGRAM_FULL_PATH;
#define HEADER_MAP "page_header_map"
#define HEADER_MAP_TMP "page_header_map_tmp"
#define PG_RELATIVE_TBLSPC_DIR "pg_location"
#define PG_REPLSLOT_DIR "pg_replslot"
/* Timeout defaults */
#define ARCHIVE_TIMEOUT_DEFAULT 300

View File

@ -54,9 +54,6 @@ extern bool smooth_checkpoint;
it will be backuped up in external dirs */
extern parray *pgdata_nobackup_dir;
/* list of logical replication slots */
extern parray *logical_replslot;
/* remote probackup options */
extern char* remote_agent;
@ -92,7 +89,7 @@ extern const char *pgdata_exclude_dir[];
/* in backup.c */
extern int do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
bool no_validate, bool no_sync, bool backup_logs, bool backup_replslots);
bool no_validate, bool no_sync, bool backup_logs);
extern BackupMode parse_backup_mode(const char *value);
extern const char *deparse_backup_mode(BackupMode mode);
extern void process_block_change(ForkNumber forknum, const RelFileNode rnode,
@ -242,8 +239,7 @@ extern const char* deparse_compress_alg(int alg);
/* in dir.c */
extern void 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, fio_location location,
bool backup_replslots = false);
bool skip_hidden, int external_dir_num, fio_location location);
extern void create_data_directories(parray *dest_files,
const char *data_dir,
@ -436,8 +432,7 @@ extern int fio_send_file(const char *from_fullpath, const char *to_fullpath, FIL
pgFile *file, char **errormsg);
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 backup_replslots = false);
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num);
extern bool pgut_rmtree(const char *path, bool rmtopdir, bool strict);

View File

@ -6230,7 +6230,7 @@ Datum GetPartBoundaryByTuple(Relation rel, HeapTuple tuple)
return Timestamp2Boundarys(rel, Align2UpBoundary(value, partMap->intervalValue, boundaryTs));
}
Oid AddNewIntervalPartition(Relation rel, void* insertTuple, bool isDDL)
Oid AddNewIntervalPartition(Relation rel, void* insertTuple)
{
Relation pgPartRel = NULL;
Oid newPartOid = InvalidOid;
@ -6327,13 +6327,7 @@ Oid AddNewIntervalPartition(Relation rel, void* insertTuple, bool isDDL)
*/
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;
}
@ -7119,7 +7113,7 @@ int lookupHBucketid(oidvector *buckets, int low, int2 bktId)
* Description :
* Notes :
*/
Oid heapTupleGetPartitionId(Relation rel, void *tuple, bool isDDL)
Oid heapTupleGetPartitionId(Relation rel, void *tuple)
{
Oid partitionid = InvalidOid;
@ -7146,7 +7140,7 @@ Oid heapTupleGetPartitionId(Relation rel, void *tuple, bool isDDL)
(errcode(ERRCODE_NO_DATA_FOUND), errmsg("inserted partition key does not map to any table partition")));
} break;
case PART_AREA_INTERVAL: {
return AddNewIntervalPartition(rel, tuple, isDDL);
return AddNewIntervalPartition(rel, tuple);
} break;
case PART_AREA_LIST: {
ereport(ERROR,

View File

@ -52,7 +52,7 @@ static_assert(sizeof(false) == sizeof(char), "illegal bool size");
static struct HTAB* nameHash = NULL;
static struct HTAB* oidHash = NULL;
/* for dolphin */
/* for b_sql_plugin */
struct HTAB* b_nameHash = NULL;
struct HTAB* b_oidHash = NULL;
@ -118,7 +118,7 @@ static const FuncGroup* NameHashTableAccess(HASHACTION action, const char* name,
Assert(name != NULL);
if (DB_IS_CMPT(B_FORMAT) && b_nameHash != NULL && u_sess->attr.attr_sql.dolphin) {
if (DB_IS_CMPT(B_FORMAT) && b_nameHash != NULL && u_sess->attr.attr_sql.b_sql_plugin) {
result = (HashEntryNameToFuncGroup *)hash_search(b_nameHash, &temp_name, action, &found);
} else {
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;
Assert(oid > 0);
if (DB_IS_CMPT(B_FORMAT) && b_oidHash != NULL && u_sess->attr.attr_sql.dolphin) {
if (DB_IS_CMPT(B_FORMAT) && b_oidHash != NULL && u_sess->attr.attr_sql.b_sql_plugin) {
result = (HashEntryOidToBuiltinFunc *)hash_search(b_oidHash, &oid, action, &found);
} else {
result = (HashEntryOidToBuiltinFunc *)hash_search(oidHash, &oid, action, &found);

View File

@ -28,6 +28,7 @@
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/syscache.h"
#include "replication/worker_internal.h"
static List *textarray_to_stringlist(ArrayType *textarray);
@ -90,13 +91,6 @@ Subscription *GetSubscription(Oid subid, bool missing_ok)
}
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);
return sub;
@ -189,7 +183,7 @@ char *get_subscription_name(Oid subid, bool missing_ok)
}
/* Clear the list content, only deal with DefElem and string content */
void ClearListContent(List *list)
static void ClearListContent(List *list)
{
ListCell *cell = NULL;
foreach(cell, list) {
@ -209,6 +203,25 @@ 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.
*

View File

@ -309,7 +309,6 @@ bool pg_md5_encrypt(const char* passwd, const char* salt, size_t salt_len, char*
{
size_t passwd_len = strlen(passwd);
errno_t rc = EOK;
/* the length of salt and password is <= SIZE_MAX */
#ifndef WIN32
if (unlikely(passwd_len >= SIZE_MAX - salt_len)) {
return false;
@ -323,7 +322,6 @@ bool pg_md5_encrypt(const char* passwd, const char* salt, size_t salt_len, char*
char* crypt_buf = (char*)malloc(passwd_len + salt_len + 1);
bool ret = false;
/* the buffer is not exist */
if (crypt_buf == NULL)
return false;

View File

@ -772,15 +772,6 @@ bool pg_sha256_encrypt_for_md5(const char* password, const char* salt, size_t sa
return true;
}
/*
* @Description: calculate the encrypted password for GsSm3.
* @const char* password : the password need be encrypted.
* @const char* salt_s : the content fo the slat.
* @size_t salt_len : the length fo the slat.
* @char* buf : the buffer to store the encrypted key with GsSm3.
* @char* client_key_buf : the buffer to store the key of client.
* @int iteration_count : to record the number of the iteration.
*/
bool GsSm3Encrypt(
const char* password, const char* salt_s, size_t salt_len, char* buf, char* client_key_buf, int iteration_count)
{
@ -808,7 +799,6 @@ bool GsSm3Encrypt(
}
password_len = strlen(password);
/* Tranform string(64Bytes) to binary(32Bytes) */
sha_hex_to_bytes32(salt, (char*)salt_s);
/* calculate k */
pkcs_ret = PKCS5_PBKDF2_HMAC((char*)password,

View File

@ -70,7 +70,6 @@
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
* wherever possible to reduce the chance for silly typos. Note that these
@ -402,27 +401,24 @@ THR_LOCAL bool skip_read_extern_fields = false;
token = pg_strtok(&length); /* skip :fldname */ \
local_node->fldname = _readBitmapset()
#define READ_TYPEINFO_FIELD(fldname) \
do { \
if (local_node->fldname >= FirstBootstrapObjectId) { \
IF_EXIST(exprtypename) \
{ \
char* exprtypename = NULL; \
char* exprtypenamespace = NULL; \
token = pg_strtok(&length); \
token = pg_strtok(&length); \
exprtypename = nullable_string(token, length); \
token = pg_strtok(&length); \
token = pg_strtok(&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); \
} \
pfree_ext(exprtypename); \
pfree_ext(exprtypenamespace); \
} \
} \
#define READ_TYPEINFO_FIELD(fldname) \
do { \
if (local_node->fldname >= FirstBootstrapObjectId) { \
IF_EXIST(exprtypename) \
{ \
char* exprtypename = NULL; \
char* exprtypenamespace = NULL; \
token = pg_strtok(&length); \
token = pg_strtok(&length); \
exprtypename = nullable_string(token, length); \
token = pg_strtok(&length); \
token = pg_strtok(&length); \
exprtypenamespace = nullable_string(token, length); \
local_node->fldname = get_typeoid(get_namespace_oid(exprtypenamespace, false), exprtypename); \
pfree_ext(exprtypename); \
pfree_ext(exprtypenamespace); \
} \
} \
} while (0)
#define READ_TYPEINFO(typePtr) \
@ -497,30 +493,9 @@ THR_LOCAL bool skip_read_extern_fields = false;
token = pg_strtok(&length); \
token = pg_strtok(&length); \
funcnamespace = nullable_string(token, length); \
bool notfound = false; \
if (IS_DATANODE_BUT_NOT_SINGLENODE && !skip_read_extern_fields) { \
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; \
if (IS_PGXC_DATANODE && !skip_read_extern_fields) { \
local_node->fldname = \
get_func_oid(funcname, get_namespace_oid(funcnamespace, false), (Expr*)local_node); \
} \
pfree_ext(funcname); \
pfree_ext(funcnamespace); \
@ -550,7 +525,7 @@ THR_LOCAL bool skip_read_extern_fields = false;
token = pg_strtok(&length); \
token = pg_strtok(&length); \
oprrightname = nullable_string(token, length); \
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
if (IS_PGXC_DATANODE) { \
namespaceId = get_namespace_oid(opnamespace, false); \
oprleft = get_typeoid(namespaceId, oprleftname); \
oprright = oprleft; \
@ -593,7 +568,7 @@ THR_LOCAL bool skip_read_extern_fields = false;
token = pg_strtok(&length); \
token = pg_strtok(&length); \
oprrightname = nullable_string(token, length); \
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
if (IS_PGXC_DATANODE) { \
namespaceId = get_namespace_oid(opnamespace, false); \
oprleft = get_typeoid(namespaceId, oprleftname); \
oprright = oprleft; \
@ -2151,21 +2126,14 @@ static FuncExpr* _readFuncExpr(void)
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("NULL seqNamespace for nextval()")));
}
if (IS_DATANODE_BUT_NOT_SINGLENODE && !skip_read_extern_fields) {
if (!IS_PGXC_COORDINATOR && !skip_read_extern_fields) {
Oid seqid = get_valid_relname_relid(seqNamespace, seqName);
Oid seqid = get_valid_relname_relid(seqNamespace, seqName, true);
Const* firstArg = (Const*)linitial(local_node->args);
if (OidIsValid(seqid)) {
Const* firstArg = (Const*)linitial(local_node->args);
if (firstArg != NULL) {
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);

File diff suppressed because it is too large Load Diff

View File

@ -1454,14 +1454,7 @@ FuncCandidateList sort_candidate_func_list(FuncCandidateList oldCandidates)
}
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);
return sortedCandidates;
}

View File

@ -61,7 +61,7 @@ void GlobalBaseDefCache::RemoveElemFromBucket(GlobalBaseEntry *base)
if (is_relation) {
GlobalRelationEntry *entry = (GlobalRelationEntry *)base;
uint64 rel_size = GetRelEstimateSize(entry);
pg_atomic_fetch_sub_u64(&m_base_space, AllocSetContextUsedSpace(((AllocSet)entry->rel_mem_manager)));
pg_atomic_fetch_sub_u64(&m_base_space, rel_size);
m_db_entry->MemoryEstimateSub(rel_size);
} else {
GlobalPartitionEntry *entry = (GlobalPartitionEntry *)base;
@ -77,7 +77,7 @@ void GlobalBaseDefCache::AddHeadToBucket(Index hash_index, GlobalBaseEntry *base
if (is_relation) {
GlobalRelationEntry *entry = (GlobalRelationEntry *)base;
uint64 rel_size = GetRelEstimateSize(entry);
pg_atomic_fetch_add_u64(&m_base_space, AllocSetContextUsedSpace(((AllocSet)entry->rel_mem_manager)));
pg_atomic_fetch_add_u64(&m_base_space, rel_size);
m_db_entry->MemoryEstimateAdd(rel_size);
} else {
GlobalPartitionEntry *entry = (GlobalPartitionEntry *)base;
@ -400,4 +400,4 @@ GlobalBaseDefCache::GlobalBaseDefCache(Oid db_oid, bool is_shared, GlobalSysDBCa
m_base_space = 0;
m_obj_locks = NULL;
m_db_entry = entry;
}
}

View File

@ -659,27 +659,7 @@ void GlobalSysDBCache::InitSysCacheRelIds()
*/
void GlobalSysDBCache::RefreshHotStandby()
{
if (!EnableGlobalSysCache()) {
return;
}
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)
@ -1292,18 +1272,9 @@ int ResizeHashBucket(int origin_nbucket, DynamicHashBucketStrategy strategy)
return cc_nbuckets;
}
void NotifyGscRecoveryStarted()
{
if (!EnableGlobalSysCache()) {
return;
}
g_instance.global_sysdbcache.recovery_finished = false;
}
void NotifyGscRecoveryFinished()
{
if (EnableGlobalSysCache()) {
g_instance.global_sysdbcache.recovery_finished = true;
}
}
}

View File

@ -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
* 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;
}

View File

@ -74,7 +74,7 @@ Partition LocalPartDefCache::SearchPartitionFromGlobalCopy(Oid part_oid)
if (!g_instance.global_sysdbcache.hot_standby) {
return NULL;
}
if (unlikely(!IsPrimaryRecoveryFinished())) {
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
return NULL;
}
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) {
return false;
}
if (unlikely(!IsPrimaryRecoveryFinished())) {
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
return false;
}
if (g_instance.global_sysdbcache.StopInsertGSC()) {
@ -456,4 +456,4 @@ Partition LocalPartDefCache::PartitionIdGetPartition(Oid part_oid, StorageType s
}
return pd;
}
}

View File

@ -433,7 +433,7 @@ LocalCatCTup *LocalSysTupCache::SearchTupleFromGlobal(Datum *arguments, uint32 h
bool bypass_gsc = HistoricSnapshotActive() ||
m_global_systupcache->enable_rls ||
!g_instance.global_sysdbcache.hot_standby ||
unlikely(!IsPrimaryRecoveryFinished());
unlikely(!g_instance.global_sysdbcache.recovery_finished);
if (invalid_entries.ExistTuple(hash_value) || bypass_gsc) {
global_ct = m_global_systupcache->SearchTupleFromFile(hash_value, arguments, true);
} else {
@ -585,7 +585,7 @@ LocalCatCList *LocalSysTupCache::SearchListFromGlobal(int nkeys, Datum *argument
bool bypass_gsc = HistoricSnapshotActive() ||
m_global_systupcache->enable_rls ||
!g_instance.global_sysdbcache.hot_standby ||
unlikely(!IsPrimaryRecoveryFinished());
unlikely(!g_instance.global_sysdbcache.recovery_finished);
GlobalCatCList *global_cl;
if (invalid_entries.ExistList() || bypass_gsc) {
global_cl = m_global_systupcache->SearchListFromFile(hash_value, nkeys, arguments, true);
@ -703,7 +703,7 @@ LocalCatCTup *LocalSysTupCache::SearchTupleFromGlobalForProcAllArgs(
bool bypass_gsc = HistoricSnapshotActive() ||
m_global_systupcache->enable_rls ||
!g_instance.global_sysdbcache.hot_standby ||
unlikely(!IsPrimaryRecoveryFinished());
unlikely(!g_instance.global_sysdbcache.recovery_finished);
if (invalid_entries.ExistTuple(hash_value) || bypass_gsc) {
global_ct = m_global_systupcache->SearchTupleFromFileWithArgModes(hash_value, arguments, argModes, true);
} else {

View File

@ -93,7 +93,7 @@ Relation LocalTabDefCache::SearchRelationFromGlobalCopy(Oid rel_oid)
if (!g_instance.global_sysdbcache.hot_standby) {
return NULL;
}
if (unlikely(!IsPrimaryRecoveryFinished())) {
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
return NULL;
}
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) {
return false;
}
if (unlikely(!IsPrimaryRecoveryFinished())) {
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
return false;
}
if (g_instance.global_sysdbcache.StopInsertGSC()) {
@ -1137,4 +1137,4 @@ void LocalTabDefCache::ResetInitFlag()
m_is_inited_phase3 = false;
m_db_id = InvalidOid;
}
}

View File

@ -1723,7 +1723,7 @@ char* get_relname_relid_extend(
extern bool StreamTopConsumerAmI();
/* same as get_relname_relid except we check for cache invalidation here */
Oid get_valid_relname_relid(const char* relnamespace, const char* relname, bool nsp_missing_ok)
Oid get_valid_relname_relid(const char* relnamespace, const char* relname)
{
Oid nspid = InvalidOid;
Oid oldnspid = InvalidOid;
@ -1747,10 +1747,7 @@ Oid get_valid_relname_relid(const char* relnamespace, const char* relname, bool
if (EnableLocalSysCache()) {
thrd_inval_count = t_thrd.lsc_cxt.lsc->inval_cxt.SIMCounter;
}
nspid = get_namespace_oid(relnamespace, nsp_missing_ok);
if (!OidIsValid(nspid)) {
return InvalidOid;
}
nspid = get_namespace_oid(relnamespace, false);
relid = get_relname_relid(relname, nspid);
/*
* In bootstrap processing mode, we don't bother with locking

View File

@ -59,7 +59,7 @@ bool open_join_children = true;
bool will_shutdown = false;
/* hard-wired binary version number */
const uint32 GRAND_VERSION_NUM = 92606;
const uint32 GRAND_VERSION_NUM = 92605;
const uint32 PREDPUSH_SAME_LEVEL_VERSION_NUM = 92522;
const uint32 UPSERT_WHERE_VERSION_NUM = 92514;
@ -101,7 +101,6 @@ const uint32 PRIVS_DIRECTORY_VERSION_NUM = 92460;
const uint32 COMMENT_RECORD_PARAM_VERSION_NUM = 92484;
const uint32 SCAN_BATCH_MODE_VERSION_NUM = 92568;
const uint32 PUBLICATION_VERSION_NUM = 92580;
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
/* Version number of the guc parameter backend_version added in V500R001C20 */
const uint32 V5R1C20_BACKEND_VERSION_NUM = 92305;

View File

@ -2712,8 +2712,8 @@ void PostgresInitializer::InitExtensionVariable()
}
/* check whether the extension has been created */
const char* dolphin = "dolphin";
u_sess->attr.attr_sql.dolphin = CheckIfExtensionExists(dolphin);
const char* b_sql_plugin = "b_sql_plugin";
u_sess->attr.attr_sql.b_sql_plugin = CheckIfExtensionExists(b_sql_plugin);
}
void PostgresInitializer::FinishInit()

View File

@ -10340,16 +10340,7 @@ check_sql_expr(const char *stmt, int location, int leaderlen)
oldCxt = MemoryContextSwitchTo(u_sess->plsql_cxt.curr_compile_context->compile_tmp_cxt);
u_sess->plsql_cxt.plpgsql_yylloc = plpgsql_yylloc;
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);
(void) raw_parser(stmt);
MemoryContextSwitchTo(oldCxt);
/* Restore former ereport callback */

View File

@ -44,22 +44,11 @@ static int g_iPosBlackList = 0;
/* array store for black list */
static BBOX_BLACKLIST_STRU g_stBlackList[BBOX_BLACK_LIST_COUNT_MAX];
/*
function name: BBOX_DetermineMsb
description: The function should judge the mode that PC uses to store data is Big-endian/Little-endian.
arguments: void
return value: An integer that indicates the mode is Big-endian/Little-endian,
if it is ELFDATA2LSB, the mode is Little-endian,
if it is ELFDATA2MSB, the mode is Big-endian.
noteThe way that this function judge the mode that PC uses to store data is through a union variable unProbe,
at first we give its first member variable sShortInt a value BBOX_MSB_LSB_INT of type short, then its second
member variable cSplit[sizeof(short)] equaling to cSplit[2] would have the equal value of the first. Finally we
just need to compare BBOX_LITTER_BITS and BBOX_HIGH_BITS, namely the low byte and high byte of
BBOX_MSB_LSB_INT, with unProbe.cSplit[0] and unProbe.cSplit[1], if they are correspondingly equal, the mode is
Little-endian, else is the Big-endian.
date: 2022/8/2
contact tel: 18720816902
*/
/*
* Determines whether the byte order of the local machine is large or small
* return : ELFDATA2LSB - large
* : ELFDATA2MSB - small
*/
int BBOX_DetermineMsb(void)
{
union INT_PROBE {

View File

@ -51,19 +51,8 @@ struct PIPE_IDS {
static struct PIPE_IDS astPipeIds[BBOX_MAX_PIDS];
/*
function name: bbox_strncmp
description: To compare two substrings, the pointers pszSrc and pszTarget store their host strings'addresses.
arguments: Two pointers of type const char*, pointing to two strings needed to be compared.
An integer indicates the number of characters at the former of two strings that
will be compared.
return value: Type s32, an interger.
If it's zero, then the former substrings of string pszSrc and pszTarget are same,
else it indicates the difference between the first two characters that these two
strings can't match.
noteThe two pointers shouldn't be null. The last argument shouldn't less than zero.
date: 2022/8/2
contact tel: 18720816902
*/
* compare string pszSrc and pszTarget
*/
s32 bbox_strncmp(const char* pszSrc, const char* pszTarget, s32 count)
{
signed char cRes = 0;
@ -79,20 +68,8 @@ s32 bbox_strncmp(const char* pszSrc, const char* pszTarget, s32 count)
}
/*
function name: bbox_strcmp
description: compare two strings, the pointer pszSrc and pszTarget store their addresses.
arguments: Two pointers of type const char*, pointing to two strings needed to be compared.
An integer indicates the number of characters at the former of two strings that
will be compared.
return value: Type s32, an interger.
If it's zero, then the former substrings of string pszSrc and pszTarget are same,
else if it's 1, then it indicates between first two characters that these two
strings can't match, the character of first string that pszSrc points is greater,
else if it's -1, the character of second string that pszTarget points is greater.
noteThe two pointers shouldn't be null. The last argument shouldn't less than zero.
date: 2022/8/2
contact tel:same
*/
* compare string pszSrc and pszTarget
*/
s32 bbox_strcmp(const char* pszSrc, const char* pszTarget)
{
unsigned char c1, c2;
@ -113,15 +90,8 @@ s32 bbox_strcmp(const char* pszSrc, const char* pszTarget)
}
/*
function name: bbox_strlen
description: Calculate the length of string.
arguments: An pointer that indicates the address of a string.
return value: Type s32, an integer indicating the length of string.
note: the length of string=(address of the last character not '\0'-address of the first character)/sizeof(char), and sizeof(char)
equals to 1, so the length of string=(address of the last character not '\0'-address of the first character).
date: 2022/8/2
contact tel:same
*/
* get the length of string pszString
*/
s32 bbox_strlen(const char* pszString)
{
const char* pszTemp = NULL;
@ -135,16 +105,8 @@ s32 bbox_strlen(const char* pszString)
}
/*
function name: bbox_strnlen
description: Calculate the length of string, but having some restrictive conditions.
arguments: An pointer that indicates the address of a string.
And an integer that indicates the maxlenth.
return value: Type s32, an integer indicating the length of string.
note: If the length of string exceed the argument count, then return the length of string,
else return the argument count.
date: 2022/8/2
contact tel:same
*/
* get the length of string pszString
*/
s32 bbox_strnlen(const char* pszString, s32 count)
{
const char* pszTemp = NULL;
@ -157,16 +119,8 @@ s32 bbox_strnlen(const char* pszString, s32 count)
}
/*
function name: bbox_atoi
description: Convert a string that includes continuous digital characters to an integer,
if the first character of the string is '-', then we will return a negative result.
arguments: An pointer that indicates the address of a string.
return value: Type s32, an integer indicating the result of string converted.
note: I think the function isn't perfect, though it's not a core function. For example, what about
the condition that the first character of the string is '+'?
date: 2022/8/2
contact tel:same
*/
* convert a string to interger
*/
s32 bbox_atoi(const char* pszString)
{
s32 n = 0;
@ -186,18 +140,10 @@ s32 bbox_atoi(const char* pszString)
return iNeg ? -n : n;
}
/*
function name: bbox_memcmp
description: Compare former count bytes in ASCII of data stored in two areas that pointers cs and ct direct.
arguments: Two pointers to areas of memory, and an integer indicating the max counts compared.
return value: Type s32, an integer.
If the value returned is 0, then the data stored in two areas destined are same,
else if is 1, then between two first data in ASCII of byte different, cs's is greater,
else if is -1, then ct's is greater.
note: The two pointers should not be null, it's dangerous.
date: 2022/8/2
contact tel: same
*/
* compare memory
*/
s32 bbox_memcmp(const void* cs, const void* ct, s32 count)
{
const unsigned char *su1 = NULL;
@ -213,18 +159,8 @@ s32 bbox_memcmp(const void* cs, const void* ct, s32 count)
}
/*
function name: bbox_strstr
description: Judge if the string s2 directs is substring of string s1 directs.
arguments: Two pointers of type const char*, pointing to two strings.
return value: Type char*, a pointer. Actually it's a address, if s2 directs a
null string, then return the address of the first character of s1,
if the string s2 directs isn't substring of string s1 directs, return
null, if the string s2 directs is substring of string s1 directs, then return
the address of first character matched.
note: The two pointers should not be null, it's dangerous.
date: 2022/8/2
contact tel: same
*/
* search string l2 in l1
*/
char* bbox_strstr(const char* s1, const char* s2)
{
int l1, l2;
@ -246,17 +182,8 @@ char* bbox_strstr(const char* s1, const char* s2)
}
/*
function name: bbox_mkdir
description: We distinguish parent directory and child directory through character '/',
normally through a for loop, we can make sure all directories above the directory
we want to creat exist, finally we will creat the flag directory after its parent.
arguments: A pointers of type const char*, pointing to one strings, which indicates the filename and its full path.
return value: An integer of type s32, if it's RET_ERR, then we fail to make a directory, else if it's RET_OK then we succeed.
note: Take care the last non-null character of the string needed to be '/', and once if flag directory's
ancestors aren't exist, the function return RET_ERR.
date: 2022/8/2
contact tel: same
*/
* make a directory
*/
s32 bbox_mkdir(const char* pszDir)
{
char szDirName[BBOX_TMP_LEN_32 * 16];
@ -301,16 +228,8 @@ s32 bbox_mkdir(const char* pszDir)
}
/*
function name: bbox_GetFreePid
description: Through a for loop, we search a free pipe in a structure array, to an array element if its
member variable isUsed's value is 0, we return the array element's another member variable
stPid's address.
arguments: void
return value: An pointer of type struct PIPE_ID* or NULL.
note: none
date: 2022/8/2
contact tel: same
*/
* search free pipe id
*/
struct PIPE_ID* bbox_GetFreePid(void)
{
u32 i;
@ -326,14 +245,8 @@ struct PIPE_ID* bbox_GetFreePid(void)
}
/*
function name: bbox_PutPid
description: Release the occupied pipe.
arguments: A pointer of type struct PIPE_ID*.
return value: void
note: If the argument pointer is null, then there is no need to free the storage, the function ends.
date: 2022/8/2
contact tel: same
*/
* Release the occupied pipeid
*/
void bbox_PutPid(struct PIPE_ID* pstPid)
{
struct PIPE_IDS* pstPids = NULL;
@ -348,16 +261,8 @@ void bbox_PutPid(struct PIPE_ID* pstPid)
}
/*
function name: bbox_FindPid
description: In all occupied pipes, the function search the flag pipe through compare all structure
array elements's member variable stPid's member variable iFd with the function
argument iFd, if they are equal, then return the addres of this array elements.
arguments: An integer that indicates a file's file handle.
return value: A pointer of type struct PIPE_ID* or NULL.
note: none
date: 2022/8/2
contact tel: same
*/
* find available pipe id by file handle
*/
struct PIPE_ID* bbox_FindPid(int iFd)
{
u32 i;
@ -376,17 +281,8 @@ struct PIPE_ID* bbox_FindPid(int iFd)
}
/*
function name: sys_popen
description: The function gets a free pipe by function bbox_GetFreePid, if normally, then creat a pipe
through sys_pipe, andcreat a child process through function sys_fork, execute a shell command
to run a process.
arguments: One pointer to a string that represents command line, another pointer of type const char*
indicates that the file file handle directs is used in the this mode.
return value: A pointer of type struct PIPE_ID* or NULL.
note: The string that indicates pszMode should only be "r" or "w",
date: 2022/8/2
contact tel: same
*/
* run popen
*/
s32 sys_popen(char* pszCmd, const char* pszMode)
{
struct PIPE_ID* volatile stCurPid = NULL;
@ -491,15 +387,8 @@ s32 sys_popen(char* pszCmd, const char* pszMode)
}
/*
function name: sys_pclose
description: The function has an contrary action to function sys_popen, it close the pipe
that sys_popen open.
arguments: iFd, an integer that indicates a file handle.
return value: An integer that indicates the final status of the process working before.
note: none
date: 2022/8/2
contact tel: same
*/
* close file handle
*/
int sys_pclose(s32 iFd)
{
struct PIPE_ID* pstCur = NULL;
@ -522,17 +411,8 @@ int sys_pclose(s32 iFd)
}
/*
function name: bbox_listdir
description: The function list all files below this path in directory.
arguments: The first argument is a pointer to a string representing a file path, all files below
this path will be listed in directory. The second argument is a pointer to a callback
function. The last is a pointer of type void*, it indicates a command line.
return value: An integer that indicates the result of function, if normal, it's RET_OK, else
it's RET_ERR.
note: The path that the first argument represents should be absolute path, take care.
date: 2022/8/2
contact tel: same
*/
* list file in directory
*/
s32 bbox_listdir(const char* pstPath, BBOX_LIST_DIR_CALLBACK callback, void* pArgs)
{
struct linux_dirent* pstEntry = NULL;

View File

@ -57,37 +57,23 @@ void bbox_initlog(int iLogScreen)
}
/*
function name: bbox_itoc
description: Convert an integer to a character.
arguments: An integer needed to be converted.
return value: An character that corresponds to the function's integer argument.
note: The integer argument can be converted in radices more than decimalism.
date: 2022/8/2
contact tel: 18720816902
*/
* convert int to string
*/
inline char bbox_itoc(u8 sNum)
{
return (char)((sNum < 10) ? (sNum + 48) : (sNum + 87));
}
/*
function name: bbox_put_dox
description: Conversion of number systems.
arguments: The first argument pCallback is a pointer to a callback function, we
use it to reverse the final result. The second argument is a pointer of
type void* used as a argument of function pCallback. The third argument
piCount is a pointer of type int, an offset pointer, also be used as a argument
of pCallback. The fourth argument is an integer of 32 bits, it indicates the buffer
size pCallback uses.The fifth argument uNum is a decimal integer that will
be converted to an integer in another radix. The sixth argument is used as
base to conversion of number systems. The last argument indicates the integer
after converted is a negative integer or not.
return value: An integer, indicating if the function pCallback work successfully.
note: The argument uNum should be a positive integer, after conversion of number systems
the sign will be appended to string's tail.
date: 2022/8/2
contact tel: 18720816902
*/
* convert int to string
* in : pCallback - call back function
* ptr - private data to call this function
* piCount - offset pointer
* iSize - buffer size
* uNum - the variable to convert
* sSys - type of variable
* isNeg - is negative
* return : need call back
*/
s32 bbox_put_dox(BBOX_vnprintCallBack pCallback, void* ptr, s32* piCount, u32 iSize, u64 uNum, s32 sSys, s32 isNeg)
{
s64 i = 0;
@ -122,21 +108,15 @@ s32 bbox_put_dox(BBOX_vnprintCallBack pCallback, void* ptr, s32* piCount, u32 iS
return iRet;
}
/*
function name: bbox_vsnprintf
description: The function is used to print string in corresponding array.
arguments: The first argument is a pointer to a callback function, the next is a
pointer to private data to call this function, also to buffer.
The third is used to destine buffer size. The forth is used to destine
the print format of deferent string, the last is a pointer to variable parameter list.
return value: An integer, if iSize is big enough, then the return value is the length of
string been written in destined memory successfully, not include '\0',
if function makes errors, the return value is a negative integer.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
* simple signal-safe function vsnprintf
* in : pCallback - call back function
* ptr - private data to call this function
* iSize - buffer size
* pFmt - format type
* ap - parameter list pointer¸ñʽ
* return : length of string
*/
s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const char* pFmt, va_list ap)
{
@ -255,20 +235,13 @@ s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const c
}
/*
function name: bbox_SnprintCallback
description: The function is used to print string in corresponding array, usually
used as the first argument of function bbox_vsnprintf.
arguments: The first argument is a character waited to be written into buffer that
pPtr directs, the second argument directs a buffer area, the third is a
pointer to an integera used to record the count to call this callback function,
at the same time, it represents the count of characters written into buffer, it's
a pointer so that we can conveniently modify data storedin it. The last
argument destines the size of buffer, it represents the limit of length.
return value: An integer, if written successfully, it's RET_OK, else it's RET_ERR.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
* call back function of snprintf_s
* in : c - string to calculate
* pPtr - pointer to buffer
* piCount - count of character
* iSize - limit of length
* return : length of string
*/
s32 bbox_SnprintCallback(char c, void* pPtr, s32* piCount, s32 iSize)
{
char** pszBuff = (char**)pPtr;

View File

@ -64,14 +64,8 @@ u8 g_szAltStackMem[BBOX_ALT_STACKSIZE]; /* independent thread stack memory */
BBOX_ATOMIC_STRU g_isBusy = BBOX_ATOMIC_INIT(0); /* whether deal with core file. */
/*
function name: BBOX_ReserveZeroStack
description: The function creat a empty stack, and its size depend on argument count.
arguments: An integer of type s32, namely int, it destines the storage of stack.
return value: void
note: The stack this function creats is actually a character array.
date: 2022/8/3
contact tel: 18720816902
*/
* reserved count bytes on current stack, and set 0
*/
void BBOX_ReserveZeroStack(s32 count)
{
char buff[count];
@ -101,14 +95,8 @@ s32 BBOX_CloneRun(u32 uFlags, s32 (*pFn)(void*), void* pArg, ...)
}
/*
function name: BBOX_GetTaskNumber
description: When get a path to specific process, this function will return count of threads below it.
arguments: A pointer of type char*, including a path to specific process.
return value: An integer that indicates the count of threads below specific process.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
* get count of thread
*/
s32 BBOX_GetTaskNumber(char* szTaskPath)
{
struct kernel_stat stProcSB = {0};
@ -142,17 +130,8 @@ s32 BBOX_GetTaskNumber(char* szTaskPath)
}
/*
function name: BBOX_GetTaskId
description: When get a path to specific process, this function will return count of threads below it.
arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*,
we use it as a structure array to store requisite thread infomation, the next argument destines
the max size of the array that the first argument destines. The last argument is a pointer of type
char*, including a path to specific process.
return value: An integer that indicates the count of threads stored in structure array.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
* get thread pid
*/
s32 BBOX_GetTaskId(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iSize, char* szTaskPath)
{
s32 iProc = -1;
@ -235,19 +214,13 @@ errout:
}
/*
function name: BBOX_PtraceAttachPid
description: The function is used to check the process whose id stored in structure array pstTaskInfo work normally.
arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*,
it is used as a structure array that has stored requisite thread infomation, the next argument destines
the size of the array that the first argument destines, namely how many elements the array has.
The last argument is an integer to decide if need to check if the trace to destined process
work normally, if normal, corresponding element of array pstTaskInfo's member variable cIsAttached
will change from 0 to 1.
return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
* a ptrace debug thread
* in : TASK_ATTACH_INFO - thread information
* iPidCount - count of thread information
* iDoPtraceCheck - check if ptrace success
* return : 0 - success
* err code - failed
*/
s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s32 iDoPtraceCheck)
{
u32 i;
@ -299,18 +272,13 @@ s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s3
}
/*
function name: BBOX_DetachAllThread
description: The function is used to cancel checking the process whose id stored in structure array pstTaskInfo
work normally, "work normally" means in array pstTaskInfo corresponding element's member
variable cIsAttached's value is 1.
arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*,
it is used as a structure array that has stored requisite thread infomation, the next argument destines
the size of the array that the first argument destines, namely how many elements the array has.
return value: void
note: none
date: 2022/8/3
contact tel: 18720816902
*/
* cancel ptrace debug thread
* in : TASK_ATTACH_INFO - thread information
* iPidCount - count of thread information
* iDoPtraceCheck - check if ptrace success
* return : 0 - success
* err code - failed
*/
void BBOX_DetachAllThread(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount)
{
u32 i;
@ -355,18 +323,12 @@ void BBOX_CheckResumeThread(void* pArgs)
}
/*
function name: BBOX_PtraceAndRun
description: When get a path to specific process, this function will trace the threads below it, and get the
information for example how many threads work normally then store it in pstArgs.
arguments: The first argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*,
what matters is its member variable callback function pointer, the next argument destines
the max count of the thread. The last argument is a pointer of type char*, including a path
to specific process.
return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
* ptrace thread and run function.
* in : pstArgs - information of callback function
* iMaxThreadCount - max count of thread
* pszProcSelfTask - /proc/[pid]/task of current tracked thread.
* return 0 if success else err code.
*/
s32 BBOX_PtraceAndRun(struct BBOX_ListParams* pstArgs, s32 iMaxThreadCount, char* pszProcSelfTask)
{
struct TASK_ATTACH_INFO stTaskInfo[iMaxThreadCount];
@ -445,15 +407,8 @@ errout:
}
/*
function name: BBOX_PrintFailedLog
description: Write log infomation into specific file, if errors arise, print the infomation about errors.
arguments: The only argument is a pointer of type const char* to a filename string, if this file doesn't
exist, we will creat a new file named it.
return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
* print log information if export failed.
*/
void BBOX_PrintFailedLog(const char* pFileName)
{
ssize_t iRet = 0;
@ -482,15 +437,8 @@ void BBOX_PrintFailedLog(const char* pFileName)
}
/*
function name: BBOX_ListThread
description: Export thread information.
arguments: The only argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*,
what matters is its member variable callback function pointer and thread infomation.
return value: void
note: none
date: 2022/8/3
contact tel: 18720816902
*/
* export thread information.
*/
void BBOX_ListThread(struct BBOX_ListParams* pstArgs)
{
pid_t ppid = 0;
@ -597,18 +545,12 @@ errout:
}
/*
function name: BBOX_GetClonePidResult
description: The function get the status of child process at first, then according to it assign pstArgs's
member variables iError and iResult appropriate values.
arguments: The first argument is a integer named iClonePid, it represents the pid of child process.
The second argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*,
what matters is its member variable callback function pointer and thread infomation.
The third argument is a integer indicating error code.
return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR.
note: none
date: 2022/8/3
contact tel: 18720816902
*/
* get return value of child process
* in : iClonePid - PID of child process
* pstArgs - parameter
* iCloneErrno - err code
* return 0 if success else failed.
*/
s32 BBOX_GetClonePidResult(pid_t iClonePid, struct BBOX_ListParams* pstArgs, s32 iCloneErrno)
{
s32 iStatus = 0;

View File

@ -57,22 +57,6 @@ BlacklistItem g_blacklist_items[] = {
{DATA_WRITER_QUEUE, "DATA_WRITER_QUEUE", false}
};
/*
function name: coredump_handler
description: When a program is abnormal, but the exception appears in the core of process and wasn't caught,
The function will generate a file to store the information about memory of process, status of register
and running stack.
arguments: The first argument is an integer indicating signal code that usually used in program of processing
signal as variable.
The second argument is a structure pointer of type siginfo_t*, the memory that this pointer
directs stores comprehensive information about signal, for example, which process sends
and which user sends.
The third argument is a pointer of type void*, other kinds of pointers can directly used here.
return value: void
note: none
date: 2022/8/4
contact tel: 18720816902
*/
static void coredump_handler(int sig, siginfo_t *si, void *uc)
{
static volatile int64 first_tid = INVALID_TID;
@ -100,19 +84,8 @@ static void coredump_handler(int sig, siginfo_t *si, void *uc)
}
/*
function name: bbox_handler
description: Handle signal conditions for bbox.
arguments: The first argument is an integer indicating signal code that usually used in program of processing
signal as variable.
The second argument is a structure pointer of type siginfo_t*, the memory that this pointer
directs stores comprehensive information about signal, for example, which process sends
and which user sends.
The third argument is a pointer of type void*, other kinds of pointers can directly used here.
return value: void
note: none
date: 2022/8/4
contact tel: 18720816902
*/
* bbox_handler - handle signal conditions for bbox
*/
static void bbox_handler(int sig, siginfo_t *si, void *uc)
{
static volatile int64 first_tid = INVALID_TID;
@ -152,16 +125,8 @@ static void bbox_handler(int sig, siginfo_t *si, void *uc)
}
/*
function name: get_bbox_coredump_pattern_path
description: Get the core dump file's path from the file "/proc/sys/kernel/core_pattern".
arguments: The first argument is a pointer to string, we use it to store core dump file's path acquired
from the file "/proc/sys/kernel/core_pattern", the next argument is the number of characters
reading from the file "/proc/sys/kernel/core_pattern", all len-1 characters or less if appear '\n'.
return value: void
note: none
date: 2022/8/4
contact tel: 18720816902
*/
* get_bbox_coredump_pattern_path - get the core dump path from the file "/proc/sys/kernel/core_pattern"
*/
static void get_bbox_coredump_pattern_path(char* path, Size len)
{
FILE* fp = NULL;
@ -191,17 +156,7 @@ static void get_bbox_coredump_pattern_path(char* path, Size len)
}
}
/*
function name: build_bbox_corepath
description: Get the core dump file's path.
arguments: The first argument is a pointer to string, we use it to store core dump file's path,
the next argument is the size of the path's name, the last argument is a pointer
to string that indicates maybe store a path to configure the core dump file.
return value: void
note: none
date: 2022/8/4
contact tel: 18720816902
*/
/* compute directory into which bbox dump core files are saved. */
static void build_bbox_corepath(char *bbox_core_path, Size path_size, char *config_path)
{
struct stat stat_buf;
@ -277,15 +232,6 @@ void assign_bbox_corepath(const char* newval, void* extra)
return;
}
/*
function name: show_bbox_dump_path
description: Get the dump file's path.
arguments: void
return value: A pointer of type const char*, directing the path to dump or NULL.
note: none
date: 2022/8/4
contact tel: 18720816902
*/
const char* show_bbox_dump_path(void)
{
const char* path = g_bbox_dump_path;
@ -293,15 +239,6 @@ const char* show_bbox_dump_path(void)
return (path != NULL) ? path : "";
}
/*
function name: split_string_into_blacklist
description: Get all strings been divided into character ',' in source string.
arguments: A pointer of type const char*, directing the source string.
return value: A pointer of type static List*.
note: none
date: 2022/8/4
contact tel: 18720816902
*/
static List* split_string_into_blacklist(const char* source)
{
List *result = NIL;
@ -327,6 +264,7 @@ static List* split_string_into_blacklist(const char* source)
return result;
}
bool check_bbox_blacklist(char** newval, void** extra, GucSource source)
{
if (t_thrd.proc_cxt.MyProcPid != PostmasterPid)
@ -464,15 +402,10 @@ void bbox_blacklist_remove(BlacklistIndex item, void* addr)
}
/*
function name: CheckFilenameValid
description: Check if the filename is in line with norms, or if dangerous characters appear
the filename is invalid.
arguments: A pointer to string indicating filename.
return value: An integer, if function works normally, the value is RET_OK, else it's RET_ERR.
note: none
date: 2022/8/4
contact tel: 18720816902
*/
* @Description: check the value from environment variablethe to prevent command injection.
* @in input_env_value : the input value need be checked.
*
*/
int CheckFilenameValid(const char* inputEnvValue)
{
const int maxLen = 1024;

View File

@ -45,15 +45,6 @@
static bool CommCheckFilterMatch(const char *filter, int len, const char *ip, int port);
/*
function name: SetCPUAffinity
description: The function set the affinity of CPU or CPUs destined by argument cpu_id.
arguments: An integer representing the id of one CPU or more.
return value: void
note: none
date: 2022/8/5
contact: 18720816902
*/
void SetCPUAffinity(int cpu_id)
{
cpu_set_t mask;
@ -279,15 +270,6 @@ IPAddrType CommLibNetGetIPType(unsigned int ip)
#define CMD_STR_MAX 512
#define CMD_OUTPUT_BUFFER_SIZE 1024
/*
function name: CommCheckLtranProcess
description: The function check if the process currently working has loaded transactions.
arguments: void
return value: 0 or 1, if 1, then at least one loaded transcation exists, if 0, no one.
note: none
date: 2022/8/5
contact: 18720816902
*/
int CommCheckLtranProcess()
{
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
@ -405,21 +387,6 @@ static T GetCommProxySubParameter(const char* str_attr, const char* key)
return res;
}
/*
function name: ParseCommProxyNumaBind
description: Get the ids of CPU to bind process with specific CPU.
arguments: The first argument is a pointer of type const char* to a string that indicating
the id of CPUs below NUMA, not necessarily all CPUs.
The second argument is an integer telling us we will get CPUs' id from which position
of array str_attr.
The third argument tells us the number of NUMA system framework.
The fourth argument is a pointer to an integer array used to store CPUs' id gotten
from string str_attr, we can use these ids to bind specific CPU.
return value: void
note: none
date: 2022/8/5
contact: 18720816902
*/
static void ParseCommProxyNumaBind(
const char* str_attr, const int pos, const int numa_num, int* numa_bind)
{
@ -520,22 +487,6 @@ bool ParseCommProxyAttr(CommProxyConfig* config)
return true;
}
/*
function name: CommCheckFilterMatch
description: This function compare the ip and port allowed with ip and port gotten from
Filter, if they are correspondingly same, it will return true value.
arguments: The first argument is a pointer of type const char* to a string that indicating
the id and port of the request been sent to Filter, the id and port have been
separated by character ':'.
The second argument is an integer telling us we the length of the string first
argument directs.
The third argument tells us the ip allowed.
The fourth argument tells us the port allowed.
return value: static bool
note: none
date: 2022/8/4
contact: 18720816902
*/
static bool CommCheckFilterMatch(const char *filter, int len, const char *ip, int port)
{
char *str_ip = NULL;

View File

@ -175,24 +175,6 @@ void UpdateTxRxStats(int msg_level)
last_rx_nbytes = current_rx_nbytes;
}
/*
function name: parse_monitor_sock_queue
description: Compare the string recv_buffer with "sockqueue fd:fd", the "fd"
after character ':' is an integer indicating file descriptor. If recv_buffer
accords with the format, the function will takes next action to see if
fd is 0, which represents stdin, so the function ends with returned value 0.
If fd isn't 0, compare the third argument type with ParseMonitorTypeSet,
if equal, then get a structure variable including socket descriptor
destined by the fd gotten from the first argument, if it's NULL, we can
write "fd:[%d], type:[normal fd], no sock queue" into send_buffer.
arguments: The first argument is a pointer to a string indicating request infomation.
The second argument is a pointer to a string to store sent infomation.
The third argument tells the kind of socket request.
return value: 0 or 1.
note: none
date: 2022/8/5
contact tel: 18720816902
*/
int parse_monitor_sock_queue(char* recv_buffer, char* send_buffer, ParseMonitorType type)
{
int length;
@ -223,25 +205,6 @@ int parse_monitor_sock_queue(char* recv_buffer, char* send_buffer, ParseMonitorT
return 0;
}
/*
function name: parse_monitor_fd
description: Compare the string recv_buffer with "query fd:fd", the "fd"
after character ':' is an integer indicating file descriptor. If recv_buffer
accords with the format, the function will takes next action to see if
fd is 0, which represents stdin, so the function ends with returned value 0.
If fd isn't 0, compare the third argument type with ParseMonitorTypeSet,
if equal, then get a structure variable including socket descriptor
destined by the fd gotten from the first argument, if it's NULL, we can
write "fd:[%d], type:[normal fd]"(%d--fd) into send_buffer, else write
"fd:[%d], type:[%d]"(%d--fd,%d--sock_desc->m_fd_type).
arguments: The first argument is a pointer to a string indicating request infomation.
The second argument is a pointer to a string to store sent infomation.
The third argument tells the kind of socket request.
return value: 0 or 1.
note: none
date: 2022/8/5
contact tel: 18720816902
*/
int parse_monitor_fd(char* recv_buffer, char* send_buffer, ParseMonitorType type)
{
int length;

View File

@ -53,24 +53,6 @@ static void comm_wait_broadcast_end(SocketRequest** req_arr, int num);
* export function definition
************************************************************************************
*/
/*
function name: comm_proxy_socket
description: This function creates a socket file descriptor whose protocol family is
domain, protocol type is type, and protocol number is protocol. If the
function call is successful, it will return a file descriptor that identifies
the socket. If it fails, it will return - 1.
arguments: The first argument specifies the protocol family, it's used as domain to
set up network communication.
The second argument is used to set the type of socket communication.
The third argument is used to specify a specific type of a protocol, which
is a type in the second argument types' type.
return value: If the function call is successful, it will return a file descriptor that
identifies the socket. If it fails, it will return - 1.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_socket(int domain, int type, int protocol)
{
SocketRequest req;
@ -172,16 +154,6 @@ ssize_t comm_proxy_addr_recv(int sockfd, void *buf, size_t len, int flags)
return comm_proxy_recv(sockfd, buf, len, flags);
}
/*
function name: comm_proxy_close
description: The function is used to release the resources allocated
to the socket by the system.
arguments: The argument is the socket file descriptor to be closed.
return value: If the call is successful, return 0; otherwise, return - 1 and set errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_close(int fd)
{
SocketRequest req;
@ -233,18 +205,6 @@ int comm_proxy_close(int fd)
return result.s_ret;
}
/*
function name: comm_proxy_shutdown
description: The function is used to release the resources allocated
to the socket by the system.
arguments: The first argument is a descriptor used to identify a socket.
The second argument is used to describe which operations
are prohibited, which determines the behavior of the function.
return value: If the call is successful, return 0; otherwise, return - 1 and set errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_shutdown(int fd, int how)
{
SocketRequest req;
@ -299,21 +259,6 @@ int comm_proxy_shutdown(int fd, int how)
return result.s_ret;
}
/*
function name: comm_proxy_accept
description: This function extracts the first connection from the waiting connection queue of S, creates
a new socket interface similar to s and returns a handle.
arguments: The first argument is a socket descriptor, which listens for connection after comm_proxy_listen().
The second argument is a optional pointer pointing to a buffer where the address of the
connection entity known to the communication layer is received. The actual format of the
addr argument is determined by the address family generated when the socket is created.
The third argument is a optional pointer, used together with addr, pointing to the integer
number with the length of addr address.
return value: The return value is a new socket descriptor, which represents a new connection with the client.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen)
{
SocketRequest req;
@ -398,17 +343,6 @@ int comm_proxy_accept4(int sockfd, struct sockaddr* addr, socklen_t* addrlen, in
return comm_proxy_accept(sockfd, addr, addrlen);
}
/*
function name: comm_proxy_connect
description: This function is used to establish a connection with a specified socket.
arguments: The first argument is used to identify an unconnected socket.
The second argument is a pointer to the sockaddr structure to socket will be connected.
The third argument is byte length of sockaddr structure.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
SocketRequest req;
@ -460,17 +394,6 @@ int comm_proxy_connect(int sockfd, const struct sockaddr *addr, socklen_t addrle
return result.s_ret;
}
/*
function name: comm_proxy_bind
description: This function binds a local address with a set of interfaces.
arguments: The first argument indicates the socket descriptor that has been established.
The second argument is a pointer to the sockaddr structure to socket.
The third argument is byte length of sockaddr structure.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_bind(int sockfd, const struct sockaddr* ServerAddr, socklen_t addrlen)
{
SocketRequest req;
@ -498,16 +421,6 @@ int comm_proxy_bind(int sockfd, const struct sockaddr* ServerAddr, socklen_t add
return result.s_ret;
}
/*
function name: comm_proxy_listen
description: This function creates a socket interface and listens for the requested connection.
arguments: The first argument is a descriptor used to identify a bundled but unconnected socket.
The second argument indicates the maximum length of waiting for connection queue
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_listen(int sockfd, int backlog)
{
SocketRequest req;
@ -534,19 +447,6 @@ int comm_proxy_listen(int sockfd, int backlog)
return result.s_ret;
}
/*
function name: comm_proxy_setsockopt
description: The function is used to set option values for sockets of any type and any state.
arguments: The first argument is a descriptor that identifies a socket interface.
The second argument indicates the level defined by the option.
The third argument specifies the option to be set.
The fourth argument is a pointer to the buffer where the new value of the option to be set is stored.
The fifth argument indicates optval buffer length.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t optlen)
{
SocketRequest req;
@ -576,19 +476,6 @@ int comm_proxy_setsockopt(int sockfd, int level, int optname, const void* optval
return result.s_ret;
}
/*
function name: comm_proxy_getsockopt
description: The function is used to obtain the current value of the option of any type and any state socket, and store the result in optval.
arguments: The first argument is a descriptor that identifies a socket interface.
The second argument indicates the level defined by the option.
The third argument specifies the socket options to be obtained.
The fourth argument is a pointer to the buffer where the obtained option value is stored.
The fifth argument is a pointer to the length value of optval buffer.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen)
{
SocketRequest req;
@ -617,18 +504,6 @@ int comm_proxy_getsockopt(int sockfd, int level, int optname, void* optval, sock
return result.s_ret;
}
/*
function name: comm_proxy_getsockname
description: The function is used to get the name of a socket. It is used for a bundled or
connected socket, and the local address will be returned.
arguments: The first argument is a descriptor that identifies a socket interface.
The second argument indicates the address of the receiving socket.
The third argument specifies the length of the name buffer.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen)
{
SocketRequest req;
@ -655,17 +530,6 @@ int comm_proxy_getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen
return result.s_ret;
}
/*
function name: comm_proxy_getpeername
description: The function is used to obtain the foreign protocol address associated with a socket.
arguments: The first argument is a descriptor that identifies a socket interface.
The second argument indicates the name structure of the receiver address.
The third argument specifies the length of the name structure.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen)
{
SocketRequest req;
@ -692,19 +556,6 @@ int comm_proxy_getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen
return result.s_ret;
}
/*
function name: comm_proxy_fcntl
description: The function can change the nature of the opened file, it provides control over descriptors.
The argument sockfd is a descriptor operated by the argument cmd. For the value of cmd,
fcntl can accept the third argument arg, which is a variable argument.
arguments: The first argument is a descriptor that identifies a socket interface.
The second argument represents the instruction to be operated.
The third argument is a variable argument
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_fcntl(int sockfd, int cmd, ...)
{
SocketRequest req;
@ -771,17 +622,6 @@ int comm_proxy_fcntl(int sockfd, int cmd, ...)
return result.s_ret;
}
/*
function name: comm_proxy_poll
description: The function is used to hang the current file pointer to the waiting queue.
arguments: The first argument is an array of struct pollfd structure type, used to store the socket descriptor whose state needs to be detected.
The second argument is used to mark the total number of structural elements in the array fdarray;
The third argument is the blocking time of the comm_proxy_poll function call.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_poll(struct pollfd* fdarray, unsigned long nfds, int timeout)
{
CommWaitPollParam param;
@ -818,15 +658,6 @@ int comm_proxy_poll(struct pollfd* fdarray, unsigned long nfds, int timeout)
return param.s_ret;
}
/*
function name: comm_proxy_epoll_create
description: The function is used to create a handle to epoll.
arguments: The only argument size is used to tell the kernel how many listeners there are.
return value: Returns a file descriptor that points to the newly created epoll instance
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_epoll_create(int size)
{
/*
@ -856,21 +687,6 @@ int comm_proxy_epoll_create1(int flag)
return comm_proxy_epoll_create(1);
}
/*
function name: comm_proxy_epoll_ctl
description: This system call performs control operations on the epoll instance referenced
by the file descriptor epfd. It requires the operation op to execute the target
file descriptor fd. It's used as epoll's event registration function, it adds,
modifies, or deletes events of interest to the epoll object.
arguments: The first argument is a specific file descriptor for epoll generated by epoll_ create.
The second argument indicates the actions to be taken, such as registering events.
The third argument is associated file descriptor.
The fourth argument is a pointer of type struct epoll_event, used to tell the kernel what events and actions to listen for.
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_epoll_ctl(int epfd, int op, int fd, struct epoll_event* event)
{
SocketRequest req;
@ -1110,23 +926,6 @@ int comm_proxy_epoll_ctl(int epfd, int op, int fd, struct epoll_event* event)
return result.s_ret;
}
/*
function name: comm_proxy_epoll_wait
description: Wait for IO events on the specified epoll file descriptor.
arguments: The first argument is a specific file descriptor for epoll generated by epoll_ create.
The second argument is a pointer to type epoll_ event structure, but it is now used
as a container to get the collection of events from the kernel.
The third argument is used to tell how large the container is (number of event
array members), that is, the number of events that can be processed each time.
The fourth argument is the timeout value for waiting for IO events.
return value: When successful, comm_proxy_epoll_wait() returns the number of file descriptors
ready for the requested IO. Returns zero if no file descriptor is ready within the
requested timeout milliseconds. When an error occurs, comm_proxy_epoll_wait()
returns - 1 and sets errno correctly.
note: none
date: 2022/8/8
contact tel: 18720816902
*/
int comm_proxy_epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout)
{
CommWaitEpollWaitParam param;

View File

@ -81,27 +81,6 @@ void mc_tcp_set_keepalive(int fd)
mc_tcp_setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, (char*)&count, sizeof(count));
}
/*
function name: mc_tcp_get_peer_name
description: This function is used to obtain the host IP and port number of the host bound to the specific socket.
arguments: The first argument is a descriptor to a specified socket.
The second argument is used to store the host IP address bound to the socket determined by the first parameter, in dotted decimal.
The third parameter is used to store the port number bound to a specific socket, in the order of host bytes.
return value: Return 0 if the function runs successfully.
When the call to the getpeername() function fails
1Return EBADF if the socket argument is not a valid file descriptor.
2Return EINVAL if the socket has been shut down.
3Return ENOTCONN if the socket is not connected or otherwise has not had the peer pre-specified.
4Return ENOTSOCK if the socket argument does not refer to a socket.
5Return EOPNOTSUPP if the operation is not supported for the socket protocol.
6Return ENOBUFS if insufficient resources were available in the system to complete the call.
Return -2 when the host IP address belongs to IPv4 type, it fails to convert it to dotted decimal.
Return -3 when the host IP address belongs to IPv6 type, it fails to convert it to dotted decimal.
Return -4 when the error type is not any of the above.
note: Allocate a certain amount of memory space for the host and port pointers respectively in advance.
date: 2022/8/9
contact tel: 18720816902
*/
int mc_tcp_get_peer_name(int fd, char* host, int* port)
{
struct sockaddr peeraddr = {0};
@ -142,17 +121,6 @@ int mc_tcp_set_cloexec(int fd)
return set_socketopt(fd, 1, FD_CLOEXEC);
}
/*
function name: mc_tcp_accept
description: This function will block the process by default until a client connection is established and returns a new available socket.
arguments: The first argument is a socket descriptor to a specific socket.
The second argument is a result parameter, which is used to accept a return value that specifies the address of the client.
The third argument is also a result argument, which is used to accept the size of the sockaddr structure. It indicates the number of bytes occupied by the sockaddr structure.
return value: Return a value less than 0 if an error occurred when call the function accept4(), else return the new fd of socket.
note: none
date: 2022/8/9
contact tel: 18720816902
*/
int mc_tcp_accept(int fd, struct sockaddr* sa, socklen_t* salenptr)
{
int new_fd;
@ -179,17 +147,6 @@ again:
return (new_fd);
}
/*
function name: mc_tcp_bind
description: This function binds the specified socket to a specific IP address and port.
arguments: The first argument indicates the socket descriptor that has been established.
The second argument is a pointer to the sockaddr structure to socket.
The third argument is byte length of sockaddr structure.
return value: Return errno, the return value is 0 if succeed, else one of other error types is returned for failure.
note: none
date: 2022/8/9
contact tel: 18720816902
*/
int mc_tcp_bind(int fd, const struct sockaddr* sa, socklen_t salen)
{
int error = -1;
@ -234,20 +191,6 @@ static void mc_tcp_do_listen(int fd, int backlog)
}
}
/*
function name: mc_tcp_read_block
description: This function receives data from the other end of TCP in a blocking manner, the receiving
process will not end until the data of size byte length is successfully received or a real error occurs in the receiving process
arguments: The first argument indicates the specific socket that has been established.
The second argument is a pointer to memory area, we use it to store received data.
The third argument is byte length of the memory area pointed to by the data pointer.
The fourth argument specifies additional operations in addition to the read operation.
return value: If there is no error, it returns the byte length of the successfully read data. If an error
occurs, return - 1.
note: When the data is read successfully, the byte length of the data is greater than 0.
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_read_block(int fd, void* data, int size, int flags)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
@ -318,20 +261,6 @@ int mc_tcp_read_block(int fd, void* data, int size, int flags)
return (size_t)nbytes;
}
/*
function name: mc_tcp_read_nonblock
description: This function receives data from the other end of TCP in a non blocking manner,
the data receiving process is only performed once.
arguments: The first argument indicates the specific socket that has been established.
The second argument is a pointer to memory area, we use it to store received data.
The third argument is byte length of the memory area pointed to by the data pointer.
The fourth argument specifies additional operations in addition to the read operation.
return value: If the error type is one of the errors represented by EAGAIN, EWOULDBLOCK and EINTR, it returns 0;
other error types return - 1; if there is no error, it returns the byte length of the successfully read data.
note: When the data is read successfully, the byte length of the data is greater than 0.
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_read_nonblock(int fd, void* data, int size, int flags)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
@ -377,17 +306,6 @@ int mc_tcp_read_nonblock(int fd, void* data, int size, int flags)
return (size_t)nbytes;
}
/*
function name: mc_tcp_check_socket
description: This function binds the specified socket to a specific IP address and port.
arguments: The only argument indicates the specific socket that has been established.
return value: Return -1 if when the recv function wait for the protocol to receive data,
the other end of TCP closes the connection or a real error occurred while
reading data. In other cases, 0 is returned.
note: none
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_check_socket(int sock)
{
char temp_buf[IOV_DATA_SIZE] = {0};
@ -450,19 +368,6 @@ int mc_tcp_check_socket(int sock)
return 0;
}
/*
function name: mc_tcp_write_block
description: This function writes data to the specified socket in blocking mode, the sending process
will not end until all the data are successfully sent or a real error occurs during the sending process
arguments: The first argument indicates the specific socket that has been established.
The second argument is a pointer to memory area, we use it to store data to be sent.
The third argument is byte length of data to be sent.
return value: If there is no error, it returns the byte length of the successfully sent data. If an error
occurs, return - 1.
note: none
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_write_block(int fd, const void* data, int size)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
@ -521,21 +426,6 @@ int mc_tcp_write_block(int fd, const void* data, int size)
return (size_t)nSend;
}
/*
function name: mc_tcp_write_noblock
description: This function writes data to the specified socket in non blocking mode,
the data transmission process is only performed once.
arguments: The first argument indicates the specific socket that has been established.
The second argument is a pointer to memory area, we use it to store data to be sent.
The third argument is byte length of data to be sent.
return value: If the sending fails but the failure reason is one of the error types represented by EAGAIN
EWOULDBLOCKEINTR ENOBUFS, then 0 is returned; if the error type is other, then - 1
is returned; If the transmission is successful, the byte length of the successfully transmitted
data is returned
note: none
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_write_noblock(int fd, const void* data, int size)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
@ -629,17 +519,6 @@ int mc_tcp_addr_init(const char* host, int port, struct sockaddr_storage* ss, in
return (error == 1) ? 0 : error;
}
/*
function name: mc_tcp_connect_nonblock
description: This function is used to create a socket and establish a connection with the port of the specified host
in non blocking mode.
arguments: The first parameter specifies a specific host, and the second parameter specifies a specific port of the host.
return value: If the connection is successfully established, the file descriptor of the socket connected to the port of the
specified host is returned; otherwise, - 1 is returned.
note: none
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_connect_nonblock(const char* host, int port)
{
int sockfd, n;
@ -687,18 +566,6 @@ int mc_tcp_connect_nonblock(const char* host, int port)
return sockfd;
}
/*
function name: mc_tcp_connect
description: This function first obtains the ports of other hosts with the same domain name stored through
the ports of specific hosts, and creates a socket to establish a connection with an appropriate
one of these ports.
arguments: The first parameter specifies a specific host, and the second parameter specifies a specific port of the host.
return value: The key is to successfully establish a connection with a port in the linked list. If the connection is successful, the
socket file descriptor connected to it will be returned. Otherwise, it will return - 1.
note: We finally get the infomation of the ports of other hosts through a linked list.
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_connect(const char* host, int port)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
@ -789,18 +656,6 @@ retry:
return (sockfd);
}
/*
function name: mc_tcp_listen
description: This function first obtains the ports of other hosts with the same domain name stored through
the ports of specific hosts, and creates a socket to bind with an appropriate one of these ports.
arguments: The first parameter specifies a specific host, and the second parameter specifies a specific port of the host.
The third is used to store size of protocol address.
return value: The key lies in the successful binding with a port in the linked list. If the binding is successful, the socket file
descriptor connected to it will be returned. Otherwise, it will return - 1.
note: We finally get the infomation of the ports of other hosts through a linked list.
date: 2022/8/10
contact tel: 18720816902
*/
int mc_tcp_listen(const char* host, int port, socklen_t* addrlenp)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE

View File

@ -232,21 +232,6 @@ static int gs_tcp_write_noblock(int node_idx, int sock, const char* msg, int msg
return send_bytes;
}
/*
function name: libcomm_tcp_send
description: This function is used to send the message including message head and message body, to
a specific socket.
arguments: send_ info is a pointer of LibcommRecvInfo* type, pointing to the memory storing the data
waiting to be sent.
return value: Data will be sent twice in total. Before sending data, if it is found that the socket to receive
data is not matched with the specified socket, then - 1 will be returned; If the sending of
message head or message body fails, return - 1; If the function runs successfully, the byte
length of the message body sent successfully is returned.
note: none
date: 2022/8/11
contact tel: 18720816902
*/
static int libcomm_tcp_send(LibcommSendInfo* send_info)
{
int sock = send_info->socket;
@ -337,19 +322,6 @@ static int libcomm_tcp_send(LibcommSendInfo* send_info)
return send_bytes;
}
/*
function name: libcomm_tcp_recv_noidx
description: This function is used to store the message transmitted from the sender, specifically to obtain
the message from a specific socket.
arguments: recv_ info is a pointer of LibcommRecvInfo* type, pointing to the memory to store the data
received.
return value: If it fails to allocate memory for iov_ Item, return RECV_MEM_ERROR;
If it fails to obtain data, no matter it is a message header or a message body, from the specified socket in blocking mode, return RECV_NET_ERROR;
If the function runs successfully, the byte length of the read message body is returned.
note: none
date: 2022/8/11
contact tel: 18720816902
*/
static int libcomm_tcp_recv_noidx(LibcommRecvInfo* recv_info)
{
int sock = recv_info->socket;
@ -399,23 +371,6 @@ static int libcomm_tcp_recv_noidx(LibcommRecvInfo* recv_info)
return error;
}
/*
function name: libcomm_tcp_recv
description: This function is used to store the message transmitted from the sender, specifically to obtain
the message from a specific socket.
arguments: recv_ info is a pointer of LibcommRecvInfo* type, pointing to the memory to store the data
received.
return value: If the receiver has not been determined, call libcomm_tcp_recv_noidx() and take the return value
of (libcomm_tcp_recv_noidx (recv_info)); Return RECV_NET_ERROR if there is an error in the
process of reading the message heade or message body; If there is no data readable in the
receiving buffer of the specified socket at this time or the number of bytes of the data that
has been read is not enough, it returns RECV_NEED_RETRY; If iov_item is NULL, it returns
RECV_MEM_ERROR if it fails to allocate space for it; If the function runs successfully, then
the byte length of the read message head and message body is returned.
note: none
date: 2022/8/11
contact tel: 18720816902
*/
int libcomm_tcp_recv(LibcommRecvInfo* recv_info)
{
MsgHead* msg_head = NULL;

View File

@ -137,16 +137,6 @@ static int LibCommClientSSLDHVerifyCb(const SSL* s, const SSL_CTX* ctx,
return 1;
}
/*
function name: ssl_cipher_list2string
description: This function converts the two-dimensional character array storing the key into a one-dimensional character array.
arguments: The first argument represents the two-dimensional character array to be converted.
The second argument indicates the number of one-dimensional arrays contained in this two-dimensional array.
return value: Returns a pointer to the one-dimensional character array that has been successfully converted. If the conversion fails, NULL is returned.
note: none
date: 2022/8/12
contact tel: 18720816902
*/
static char* ssl_cipher_list2string(const char* ciphers[], const int num) {
int i;
int catlen = 0;
@ -237,20 +227,7 @@ char* LibCommErrMessage(void) {
return errBuf;
}
/*
function name: LibCommClientSSLPasswd
description: As a client, this function is used to detect whether there is a file with a valid key in the specified
directory and whether there is permission to operate it. If so, the password will be decrypted by
using the file.
arguments: The first parameter is a pointer of type (SSL *).
The second parameter is used to obtain the absolute path of the certificate file.
The third parameter represents the user name.
The fourth parameter is a pointer of type (libcommconn *), whose member variable contains the ciphertext to be decrypted.
return value: If the path is empty or does not have operation permission to the directory where the certificate file is located, a non-1 value is returned; otherwise, 0 is returned.
note: none
date: 2022/8/12
contact tel: 18720816902
*/
int LibCommClientSSLPasswd(SSL* pstContext, const char * path, const char * userName, LibCommConn * conn) {
char* CertFilesDir = NULL;
char CertFilesPath[MAXPATH] = {0};
@ -274,8 +251,8 @@ int LibCommClientSSLPasswd(SSL* pstContext, const char * path, const char * user
/*check whether the cipher and rand files begins with userName exist.
if exist, decrypt it.
if not,decrypt the default cipher and rand files begins with client.
Because,for every client user may own certification and private key*/
if not,decrypt the default cipher and rand files begins with client%.
Because,for every client user mayown certification and private key*/
if (NULL == userName) {
retval = LibCommClientCheckPermissionCipherFile(CertFilesDir, conn, NULL);
if (retval != 1)

View File

@ -33,16 +33,6 @@ inline int mc_lqueue_item_size(struct mc_lqueue_item* q_item)
return q_item->element.data->iov_len;
}
/*
function name: mc_lqueue_add
description: Add an element to a specific queue.
arguments: The first parameter is a pointer of type (mc_lqueue *), whose member variable list points to the target queue.
The second parameter points to the element to be added to the queue.
return value: Returns 1 if the element is successfully added to the queue, otherwise returns - 1.
note: none
date: 2022/8/13
contact tel: 18720816902
*/
int mc_lqueue_add(struct mc_lqueue* q, struct mc_lqueue_item* q_item)
{
if (q == NULL || q_item == NULL) {
@ -70,17 +60,6 @@ int mc_lqueue_add(struct mc_lqueue* q, struct mc_lqueue_item* q_item)
return 1;
}
/*
function name: mc_lqueue_remove
description: Remove the head element in a specific queue.
arguments: The first parameter is a pointer of type (mc_lqueue *), whose member variable list points to the target queue.
The second parameter points to the queue head element used to store the removal from the queue.
return value: Return NULL if an error occurs during the removal of the queue head element, otherwise a pointer to
the successfully removed queue head element is returned.
note: none
date: 2022/8/13
contact tel: 18720816902
*/
struct mc_lqueue_item* mc_lqueue_remove(struct mc_lqueue* q, struct mc_lqueue_item* q_item)
{
if (q == NULL) {
@ -110,18 +89,6 @@ struct mc_lqueue_item* mc_lqueue_remove(struct mc_lqueue* q, struct mc_lqueue_it
return q_item;
}
/*
function name: mc_lqueue_init
description: This function is used to open an area in the memory area. One part of the area is used to store a queue with
a certain specification, and the other part is used to store the information of the queue, such as the specification
and the number of elements. Finally, a pointer to the area is returned.
arguments: This parameter specifies that the maximum number of elements that the queue can hold is size, but this does
not mean that the size of the queue is so large at the beginning.
return value: If the function runs successfully, it returns a pointer to the opened memory area; otherwise, it returns NULL.
note: none
date: 2022/8/13
contact tel: 18720816902
*/
struct mc_lqueue* mc_lqueue_init(unsigned long size)
{
if (size == 0) {

View File

@ -222,7 +222,6 @@ NON_EXEC_STATIC void PercentileMain()
g_instance.stat_cxt.force_process = false;
sleep(SLEEP_INTERVAL);
}
elog(LOG, "instrumention percentile ended");
gs_thread_exit(0);
}

View File

@ -14,23 +14,11 @@ import os
from . import feature_mapping
from . import features
# To import file feature_mapping and features from parent folder
#function name: load_feature_lib
#description: Print the variable FEATURE_LIB in the file-- features
#return value: The value of FEATURE_LIB
#date: 2022/8/2
#contact: 1865997821
def load_feature_lib():
return features.FEATURE_LIB
#function name: get_feature_mapper
#description: Get the item and value of a dictionary type in the file-- feature_mapping and output it as a generator.
#return value: The item and value in _dict_ variable
#noteDictionary key-value pairs must start with C then the item and value will be return.
#date: 2022/8/2
#contact: 1865997821
def get_feature_mapper():
return {

View File

@ -11,27 +11,22 @@
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
import csv
#import csv packet
from collections import defaultdict
from typing import List
# To import defaultdict in the parent floder collections and List in the parent floder typing
import numpy as np
# import numpy packet as the name np
from ..analyzer import _euclid_distance as euclid_distance
from dbmind.common.utils import ExceptionCatch
#To import private function-- _euclid_distance as euclid_distance
#function name: calculate_weight
#description: This function will output feature_weight (= residual_vector / the sum of residual_vector)
#The data used for the calculation is from the features_labels_dict, and the key value pairs of the features_labels_dict are filtered
#arguments: np.ndarray and np.ndarray
#return value: weight_matrix
#date: 2022/8/2
#contact: 1865997821
def calculate_weight(features: np.ndarray, labels: np.ndarray) -> List:
"""
Calculate weight matrix based on feature set
:param features: feature set
:param labels: label set
:return: weight_matrix
"""
normalize_features, normalize_labels = [], []
features_labels_dict = defaultdict(list)
for i in range(len(labels)):
@ -61,16 +56,6 @@ def calculate_weight(features: np.ndarray, labels: np.ndarray) -> List:
return weight_matrix
# function name: build_model
# description: Create two variables-- features and labels.There are refer to two numpy array(all elements are zero)
# The features array's size is feature_number and dimension is feature_dimension
# This function will read the two arrays and write it as a matrix in a csv file(the save path is './features_new.npz')
# And then it will call the function calculate_weight to calculate the matrix
# arguments: feature_path, feature_number, feature_dimension
# return value: None
# noteA ExceptionCatch function modifier is used
# date: 2022/8/2
#contact: 1865997821
@ExceptionCatch(strategy='exit', name='FEATURE')
def build_model(feature_path: str, feature_number: int, feature_dimension: int,
save_path: str = './features_new.npz') -> None:

View File

@ -11,13 +11,6 @@
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
#function name: detect
#description: if the method is "bool" type, then call the functions sum_detect、avg_detect、ks_detect to diagnose errors
#These functions are in the parent slow_sql/significance_detection
#arguments: data1(array), data2(array), method
#return value: bool type
#date: 2022/8/2
#contact: 1865997821
def detect(data1, data2, method='bool', threshold=0.01, p_value=0.5):
if method == 'bool':

View File

@ -12,16 +12,17 @@
# See the Mulan PSL v2 for more details.
alpha = 1e-10
#Define a minimum number of errors
#function name: detect
#description: Calculate whether the data has abrupt changes based on the average value
#arguments: data1, data2, threshold,method
#return value: bool
#date: 2022/8/
#contact: 1865997821
def detect(data1, data2, threshold=0.5, method='bool'):
"""
Calculate whether the data has abrupt changes based on the average value
:param data1: input data array
:param data2: input data array
:param threshold: Mutation rate
:param method: The way to calculate the mutation
:return: bool
"""
if not isinstance(data1, list) or not isinstance(data2, list):
raise TypeError("The format of the input data is wrong.")
avg1 = sum(data1) / len(data1) if data1 else 0

View File

@ -13,14 +13,8 @@
import sys
from .cli import DBMindRun
#To import DBMindRun method from the parent file cli
#function name: main
#description: Get the system command parameters, pass to the DBMindRun and call this function,if an InterruptedError is reported, the program will exit( sys.exit(1)).
#arguments: None
#return value: None
#date: 2022/8/3
#contact: 1865997821
def main() -> None:
try:
DBMindRun(sys.argv[1:])

View File

@ -55,12 +55,7 @@ CONFIG_OPTIONS = {
'LOG-level': ['DEBUG', 'INFO', 'WARNING', 'ERROR']
}
#function name: check_config_validity
#description: Checks the validity of the passed parameter
#arguments: section, option, value
#return value: bool and string
#date: 2022/8/
#contact: 1865997821
def check_config_validity(section, option, value):
config_item = '%s-%s' % (section, option)
# exceptional cases:
@ -92,16 +87,6 @@ def check_config_validity(section, option, value):
return True, None
#function name: load_sys_configs
#description: Create and load the modification file
#arguments: The configuration to modify
#return value: a new configuration file
#noteTo facilitate the user to modify the configuration items through the
#configuration file easily, we add inline comments to the file, but we need to remove the inline comments while parsing.
#Otherwise, it will cause the read configuration items to be wrong.
#date: 2022/8/
#contact: 1865997821
def load_sys_configs(confile):
# Note: To facilitate the user to modify the configuration items through the
# configuration file easily, we add inline comments to the file, but we need
@ -111,8 +96,6 @@ def load_sys_configs(confile):
with open(file=confile, mode='r') as fp:
configs.read_file(fp)
# Define a class that encapsulates the modification item
class ConfigWrapper(object):
def __getattribute__(self, name):
try:
@ -139,7 +122,7 @@ def load_sys_configs(confile):
return ConfigWrapper()
# Defines a class that updates the encapsulated modification file
class ConfigUpdater:
def __init__(self, filepath):
self.config = ConfigParser(inline_comment_prefixes=None)
@ -187,7 +170,7 @@ class ConfigUpdater:
self.fp.flush()
self.fp.close()
# Defines a class that dynamically displays a modified item
class DynamicConfig:
@staticmethod
def get(*args, **kwargs):

View File

@ -43,7 +43,6 @@ except ImportError:
SKIP_LIST = ('COMMENT', 'LOG')
# The global variable acts as a switch that controls whether the program runs
dbmind_master_should_exit = False
@ -58,16 +57,8 @@ def _process_clean(force=False):
global_vars.worker.terminate(cancel_futures=force)
TimedTaskManager.stop()
#function name: signal_handler
#description: The function processes the received signal parameters, reassigns variable x according to different signals
#or calls other functions to complete the content indicated by signals
#arguments: signum, frame
#return value: bool (dbmind_master_should_exit)
#date: 2022/8/3
#contact: 1865997821
def signal_handler(signum, frame):
# The global variable dbmind_master_should_exit can be modified in this function to continue to play a control role
global dbmind_master_should_exit
if signum == signal.SIGINT or signum == signal.SIGHUP:
@ -157,12 +148,10 @@ class DBMindMain(Daemon):
time.sleep(1)
logging.info('DBMind will close.')
# Emptying the execution pool
def clean(self):
if os.path.exists(self.pid_file):
os.unlink(self.pid_file)
# Reload the execution pool and solve the error
def reload(self):
pid = read_dbmind_pid_file(self.pid_file)
if pid > 0:

View File

@ -27,17 +27,6 @@ def do_after(rt_result):
def do_exception(exception):
"""Nothing"""
#function name: around
#description: Preserve the function properties and prevent an error from terminating the program
#arguments: One or more functions
#return value: none
#note Decorators are implemented in such a way that the function being decorated is actually another function (the function name and other properties change).
#To avoid this, Python's FuncTools package provides a decorator called wraps to remove such side effects.
#When writing a decorator, it is a good idea to wrap FuncTools before implementing it.
#It preserves the name and properties of the original function
#date: 2022/8/4
#contact: 1865997821
def around(func, *args, **kw):
@wraps(func)
def wrapper():

View File

@ -15,11 +15,7 @@ from typing import Optional, Iterable, Union
from .root_cause import RootCause
from .enumerations import ALARM_TYPES, ALARM_LEVEL
#Define an Alarm class that takes the error parameters entered by the user and displays the error content and cause
#methodDisplay the error content and suggestions, and retrieve suggestions provided by the system. If there are no suggestions, return “ no suggestions”
#noteThe property decorator turns a method into a property call.(root_causes、suggestions)
#date2022/8/4
#contact18365997821
class Alarm:
def __init__(self,
host: Union[str],

View File

@ -12,11 +12,7 @@
# See the Mulan PSL v2 for more details.
from .root_cause import RootCause
#Define anSlowQuery class thatSlow query accepts user input commands and performs operations on the database
#methodDisplay the error content and suggestions, and retrieve suggestions provided by the system. If there are no suggestions, return “ no suggestions”
#noteThe property decorator turns a method into a property call.(root_causes、suggestions)
#date2022/8/4
#contact18365997821
class SlowQuery:
def __init__(self, db_host, db_port, db_name, schema_name, query, start_timestamp, duration_time,
hit_rate=None, fetch_rate=None, cpu_time=None, data_io_time=None, template_id=None, sort_count=None,

View File

@ -18,19 +18,13 @@ import psycopg2
from .execute_factory import ExecuteFactory
from .execute_factory import IndexInfo
#class name: DriverExecute Inherits from the parent class ExecuteFactory
#description: The SQL statement performs the operations associated with the call
#date: 2022/8/10
#contact: 1865997821
class DriverExecute(ExecuteFactory):
def __init__(self, *arg):
#Call the arguments of the parent class __init__ method
super(DriverExecute, self).__init__(*arg)
self.conn = None
self.cur = None
#Connecting to the database
def init_conn_handle(self):
self.conn = psycopg2.connect(dbname=self.dbname,
user=self.user,
@ -39,7 +33,6 @@ class DriverExecute(ExecuteFactory):
port=self.port)
self.cur = self.conn.cursor()
#If an error occurs after the SQL statement is executed, the error information is reported to the user
def execute(self, sql):
try:
self.cur.execute(sql)
@ -48,13 +41,11 @@ class DriverExecute(ExecuteFactory):
except Exception:
self.conn.commit()
#Disconnecting from the database
def close_conn(self):
if self.conn and self.cur:
self.cur.close()
self.conn.close()
#Check whether multiple nodes exist
def is_multi_node(self):
self.init_conn_handle()
try:

View File

@ -13,11 +13,6 @@
import re
#class name: IndexInfo
#description: Define information about table indexes
#methods: __init__
#date: 2022/8/10
#contact: 1865997821
class IndexInfo:
def __init__(self, schema, table, indexname, columns, indexdef):
@ -29,9 +24,7 @@ class IndexInfo:
self.primary_key = False
self.redundant_obj = []
#class name: ExecuteFactory
#date: 2022/8/10
#contact: 1865997821
class ExecuteFactory:
def __init__(self, dbname, user, password, host, port, schema, multi_node, max_index_storage):
self.dbname = dbname
@ -43,11 +36,11 @@ class ExecuteFactory:
self.max_index_storage = max_index_storage
self.multi_node = multi_node
# Record redundant indexes
@staticmethod
def record_redundant_indexes(cur_table_indexes, redundant_indexes):
cur_table_indexes = sorted(cur_table_indexes,
key=lambda index_obj: len(index_obj.columns.split(',')))
# record redundant indexes
for pos, index in enumerate(cur_table_indexes[:-1]):
is_redundant = False
for candidate_index in cur_table_indexes[pos + 1:]:
@ -59,7 +52,6 @@ class ExecuteFactory:
if is_redundant:
redundant_indexes.append(index)
#Match the name of the table against the index of the query
@staticmethod
def match_table_name(table_name, query_index_dict):
for elem in query_index_dict.keys():
@ -74,7 +66,6 @@ class ExecuteFactory:
return False, table_name
return True, table_name
#Retrieves a valid index based on the regular expression, adding the corresponding index and empty element if none exists
@staticmethod
def get_valid_indexes(record, hypoid_table_column, valid_indexes):
tokens = record.split(' ')
@ -97,7 +88,6 @@ class ExecuteFactory:
if columns not in valid_indexes[table_name]:
valid_indexes[table_name].append((columns, index_type))
#Record invalid SQL statements and returns the corresponding help information that matches the corresponding SQL statement
@staticmethod
def record_ineffective_negative_sql(candidate_index, obj, ind):
cur_table = candidate_index.table
@ -135,7 +125,6 @@ class ExecuteFactory:
candidate_index.ineffective_pos.append(ind)
candidate_index.total_sql_num += obj.frequency
#Returns the last input and the corresponding result
@staticmethod
def match_last_result(table_name, index_column, history_indexes, history_invalid_indexes):
for column in history_indexes.get(table_name, dict()):
@ -153,7 +142,6 @@ class ExecuteFactory:
if not history_indexes[table_name]:
del history_indexes[table_name]
#Correcting SQL statements
@staticmethod
def make_single_advisor_sql(ori_sql):
sql = 'select gs_index_advise(\''

View File

@ -23,16 +23,12 @@ from .execute_factory import IndexInfo
BASE_CMD = None
#class name: GSqlExecute
#description: Solve the optimization problem of GSQL statement execution
#date: 2022/8/11
#contact: 1865997821
class GSqlExecute(ExecuteFactory):
def __init__(self, *args):
super(GSqlExecute, self).__init__(*args)
def init_conn_handle(self):
#define a global variable BASE_CMD,it is a connection command statement
global BASE_CMD
BASE_CMD = 'gsql -p ' + str(self.port) + ' -d ' + self.dbname
if self.host:
@ -42,7 +38,6 @@ class GSqlExecute(ExecuteFactory):
if self.password:
BASE_CMD += ' -W ' + self.password
#Run the shell command in BASE_CMD
def run_shell_cmd(self, target_sql_list):
cmd = BASE_CMD + ' -c \"'
if self.schema:
@ -52,7 +47,6 @@ class GSqlExecute(ExecuteFactory):
cmd += '\"'
proc = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
#Read data from stdout and stderr,If an error message is displayed, an error message is displayed
(stdout, stderr) = proc.communicate()
stdout, stderr = stdout.decode(), stderr.decode()
if 'gsql: FATAL:' in stderr or 'failed to connect' in stderr:
@ -80,7 +74,6 @@ class GSqlExecute(ExecuteFactory):
print(e.output.decode(), file=sys.stderr)
return int(ret.decode().strip().split()[2]) > 0
#Parse the recommended result returned
@staticmethod
def parse_single_advisor_result(res, table_index_dict):
if len(res) > 2 and res[0:2] == ' (':
@ -190,7 +183,6 @@ class GSqlExecute(ExecuteFactory):
total_cost = 0
found_plan = False
hypo_index = False
# create hypo-indexes
for line in res:
if 'QUERY PLAN' in line:
found_plan = True
@ -230,7 +222,6 @@ class GSqlExecute(ExecuteFactory):
i += 1
return total_cost
#Production workflows consume report files
def estimate_workload_cost_file(self, workload, index_config=None, ori_indexes_name=None):
sql_file = str(time.time()) + '.sql'
is_computed = False
@ -273,7 +264,6 @@ class GSqlExecute(ExecuteFactory):
return total_cost
#Check for empty indexes and note them to optimize the table structure
def check_useless_index(self, history_indexes, history_invalid_indexes):
schemas = [elem.lower()
for elem in filter(None, self.schema.split(','))]

View File

@ -26,11 +26,9 @@ import logging
try:
from .dao.gsql_execute import GSqlExecute
from .dao.execute_factory import ExecuteFactory
from .mcts import MCTS
except ImportError:
from dao.gsql_execute import GSqlExecute
from dao.execute_factory import ExecuteFactory
from mcts import MCTS
ENABLE_MULTI_NODE = False
SAMPLE_NUM = 5
@ -194,12 +192,9 @@ class IndexAdvisor:
self.workload_used_index))
if DRIVER:
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,
candidate_indexes, self.index_cost_total[0])
opt_config = greedy_determine_opt_config(self.workload_info[0], atomic_config_total,
candidate_indexes, self.index_cost_total[0])
self.retain_lower_cost_index(candidate_indexes)
if len(opt_config) == 0:
print("No optimal indexes generated!")
@ -948,7 +943,7 @@ def check_parameter(args):
raise argparse.ArgumentTypeError("%s is an invalid positive int value" %
args.max_index_num)
if args.max_index_storage is not None and args.max_index_storage <= 0:
raise argparse.ArgumentTypeError("%s is an invalid positive float value" %
raise argparse.ArgumentTypeError("%s is an invalid positive int value" %
args.max_index_storage)
JSON_TYPE = args.json
MAX_INDEX_NUM = args.max_index_num
@ -976,7 +971,7 @@ def main(argv):
arg_parser.add_argument(
"--max_index_num", help="Maximum number of suggested indexes", type=int)
arg_parser.add_argument("--max_index_storage",
help="Maximum storage of suggested indexes/MB", type=float)
help="Maximum storage of suggested indexes/MB", type=int)
arg_parser.add_argument("--multi_iter_mode", action='store_true',
help="Whether to use multi-iteration algorithm", default=False)
arg_parser.add_argument("--multi_node", action='store_true',

View File

@ -1,397 +0,0 @@
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

View File

@ -539,14 +539,13 @@ class RnnModel():
keras.backend.clear_session()
set_session(self.session)
with self.graph.as_default():
# Judge whether the model needs to be initialized according to the changes of the model input and output dimensions.
feature, label, need_init = self.parse(filename)
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
epsilon = self.model_info.make_epsilon()
if need_init:# Cold start training
if need_init:
epoch_start = 0
self.model = self._build_model(epsilon)
else:# Incremental training
else:
epoch_start = int(self.model_info.last_epoch)
ratio_error = ratio_error_loss_wrapper(epsilon)
ratio_acc_2 = ratio_error_acc_wrapper(epsilon, 2)
@ -557,16 +556,12 @@ class RnnModel():
log_path = os.path.realpath(os.path.join(settings.PATH_LOG, self.model_info.model_name + '_log.json'))
if not os.path.exists(log_path):
os.mknod(log_path, mode=0o600)
# Training logging callback function
json_logging_callback = LossHistory(log_path, self.model_info.model_name, self.model_info.last_epoch)
# Data segmentation
X_train, X_val, y_train, y_val = \
train_test_split(feature, label, test_size=0.1)
# model training
self.model.fit(X_train, y_train, epochs=self.model_info.last_epoch,
batch_size=int(self.model_info.batch_size), validation_data=(X_val, y_val),
verbose=0, initial_epoch=epoch_start, callbacks=[json_logging_callback])
# save model
self.model.save(self.model_info.model_path)
val_pred = self.model.predict(X_val)
val_re = get_ratio_errors_general(val_pred, y_val, epsilon)

View File

@ -27,7 +27,6 @@ from . import AbstractModel
class TemplateModel(AbstractModel):
# Initialize algorithm parameters
def __init__(self, params):
super().__init__(params)
self.bias = 1e-5

View File

@ -173,16 +173,11 @@ def procedure_main(mode, db_info, config):
def rl_model(mode, env, config):
# Lazy loading. Because loading Tensorflow takes a long time.
from tuner.algorithms.rl_agent import RLAgent
# Start reinforcement learning agent class.
rl = RLAgent(env, alg=config['rl_algorithm'])
# The two modes of training and tuning correspond to different execution processes.
# The model needs to be trained before it can be used for tuning. The output of the training and tuning process is the list of parameters to be tuned. Because they share a set of models, it is required that the list of parameters to be tuned must be consistent in the two modes, otherwise exceptions with different output dimensions will be thrown.
if mode == 'train':
logging.warning('The list of tuned knobs in the training mode '
'based on the reinforcement learning algorithm must be the same as '
'that in the tuning mode. ')
# The key parameter is the maximum iteration round rl_ steps, theoretically, the longer the more accurate, but also more time-consuming.
# max_episode_steps is the maximum number of rounds in each round of reinforcement learning algorithm. In the implementation of x-tuner, this parameter is weakened, and it is generally default.
rl.fit(config['rl_steps'], nb_max_episode_steps=config['max_episode_steps'])
rl.save(config['rl_model_path'])
logging.info('Saved reinforcement learning model at %s.', config['rl_model_path'])
@ -205,7 +200,6 @@ def rl_model(mode, env, config):
def global_search(env, config):
method = config['gop_algorithm']
# Determine which algorithm to use.
if method == 'bayes':
from bayes_opt import BayesianOptimization
@ -213,13 +207,6 @@ def global_search(env, config):
pbound = {name: (0, 1) for name in env.db.ordered_knob_list}
def performance_function(**params):
"""
function name: performance_function
description: Define a black box function to adapt to the interface of the third-party library.
author: Li Xinran
date: 2022/8/4
contact: 19154068808
"""
if not len(params) == env.nb_actions:
raise AssertionError('Failed to check the input feature dimension.')
@ -235,21 +222,12 @@ def global_search(env, config):
pbounds=pbound
)
optimizer.maximize(
# The larger the maximum iteration round, the more accurate the result is, but it is also more time-consuming.
n_iter=config['max_iterations']
)
elif method == 'pso':
from tuner.algorithms.pso import Pso
def performance_function(v):
"""
function name: performance_function
description: Find the global minimum value.
note: Because the implementation of PSO algorithm is to find the global minimum value, take the opposite number here, so we need to change to take the global maximum value.
author: Li Xinran
date: 2022/8/4
contact: 19154068808
"""
s, r, d, _ = env.step(v)
return -r # Use -reward because PSO wishes to minimize.
@ -259,7 +237,6 @@ def global_search(env, config):
particle_nums=config['particle_nums'],
# max_iterations on the PSO indicates the maximum number of iterations per particle,
# so it must be divided by the number of particles to be consistent with Bayes.
# The larger the maximum iteration round is, the more accurate the result is, but also the more time-consuming.
max_iteration=config['max_iterations'] // config['particle_nums'],
x_min=0, x_max=1, max_vel=0.5
)

View File

@ -27,7 +27,6 @@ from collections.abc import Iterable
from collections import defaultdict
import index_advisor_workload as iaw
import mcts
def hash_any(obj):
@ -228,32 +227,6 @@ select * from student_range_part1 where credit=1;
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):
tables = 'table1 table2 table2 table3 table3 table3'.split()
columns = 'col1,col2 col2 col3 col1,col2 col2,col3 col2,col5'.split()

View File

@ -90,10 +90,7 @@ 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.")));
}
/*

View File

@ -1175,7 +1175,7 @@ void CreateExtension(CreateExtensionStmt* stmt)
FEATURE_NOT_PUBLIC_ERROR("EXTENSION is not yet supported.");
}
if (pg_strcasecmp(stmt->extname, "dolphin") == 0 && !DB_IS_CMPT(B_FORMAT)) {
if (pg_strcasecmp(stmt->extname, "b_sql_plugin") == 0 && !DB_IS_CMPT(B_FORMAT)) {
ereport(ERROR,
(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;
if (pg_strcasecmp(stmt->extname, "dolphin") == 0) {
u_sess->attr.attr_sql.dolphin = true;
if (pg_strcasecmp(stmt->extname, "b_sql_plugin") == 0) {
u_sess->attr.attr_sql.b_sql_plugin = true;
}
/*

View File

@ -44,7 +44,7 @@
#include "utils/array.h"
#include "utils/acl.h"
static bool ConnectPublisher(char* conninfo, char* slotname);
static void ConnectPublisher(char *conninfo, char* slotname);
static void CreateSlotInPublisher(char *slotname);
static void ValidateReplicationSlot(char *slotname, List *publications);
@ -56,7 +56,7 @@ static void ValidateReplicationSlot(char *slotname, List *publications);
* accommodate that.
*/
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 *binary_given, bool *binary)
bool *enabled, bool *slot_name_given, char **slot_name, char **synchronous_commit)
{
ListCell *lc;
@ -76,10 +76,6 @@ static void parse_subscription_options(const List *options, char **conninfo, Lis
if (synchronous_commit) {
*synchronous_commit = NULL;
}
if (binary) {
*binary_given = false;
*binary = false;
}
/* Parse options */
foreach (lc, options) {
@ -128,15 +124,6 @@ static void parse_subscription_options(const List *options, char **conninfo, Lis
/* 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,
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 {
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), errmsg("unrecognized subscription parameter: %s", defel->defname)));
@ -210,82 +197,26 @@ static Datum publicationListToArray(List *publist)
}
/*
* Parse the original connection string which is encrypted, poll all hosts and ports,
* and try to connect to the publisher.
* When checkRemoteMode is true, the remotemode must be normal or primary.
* Return true to indicate successful connection.
* connect publisher and create slot.
* the input conninfo should be encrypt, we will decrypt password inside
*/
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)
static void ConnectPublisher(char *conninfo, char *slotname)
{
/* Try to connect to the publisher. */
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
SpinLockAcquire(&walrcv->mutex);
walrcv->conn_target = REPCONNTARGET_PUBLICATION;
SpinLockRelease(&walrcv->mutex);
char* decryptConninfo = EncryptOrDecryptConninfo(conninfo, 'D');
char *decryptConninfo = DecryptConninfo(conninfo);
bool connectSuccess = (WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_connect(decryptConninfo, NULL, slotname, -1);
int rc = memset_s(decryptConninfo, strlen(decryptConninfo), 0, strlen(decryptConninfo));
securec_check(rc, "", "");
pfree_ext(decryptConninfo);
return connectSuccess;
if (!connectSuccess) {
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("could not connect to the publisher")));
}
}
/*
@ -362,10 +293,9 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
bool enabled_given = false;
bool enabled = true;
char *synchronous_commit;
char *conninfo;
char *slotname;
bool slotname_given;
bool binary;
bool binary_given;
char originname[NAMEDATALEN];
List *publications;
int rc;
@ -375,7 +305,7 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
* Connection and publication should not be specified here.
*/
parse_subscription_options(stmt->options, NULL, NULL, &enabled_given, &enabled, &slotname_given, &slotname,
&synchronous_commit, &binary_given, &binary);
&synchronous_commit);
/*
* Since creating a replication slot is not transactional, rolling back
@ -403,10 +333,11 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
synchronous_commit = "off";
}
conninfo = stmt->conninfo;
publications = stmt->publication;
/* Check the connection info string. */
libpqrcv_check_conninfo(stmt->conninfo);
libpqrcv_check_conninfo(conninfo);
/* Everything ok, form a new tuple. */
rc = memset_s(values, sizeof(values), 0, sizeof(values));
@ -418,12 +349,18 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
values[Anum_pg_subscription_subname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->subname));
values[Anum_pg_subscription_subowner - 1] = ObjectIdGetDatum(owner);
values[Anum_pg_subscription_subenabled - 1] = BoolGetDatum(enabled);
values[Anum_pg_subscription_subbinary - 1] = BoolGetDatum(binary);
/* encrypt conninfo */
char *encryptConninfo = EncryptOrDecryptConninfo(stmt->conninfo, 'E');
List *conninfoList = ConninfoToDefList(stmt->conninfo);
/* 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);
pfree_ext(conninfoList);
if (enabled) {
if (!slotname_given) {
slotname = stmt->subname;
@ -459,14 +396,11 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
*/
if (enabled) {
Assert(slotname);
if (!AttemptConnectPublisher(encryptConninfo, slotname, true)) {
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("Failed to connect to publisher.")));
}
ConnectPublisher(encryptConninfo, slotname);
CreateSlotInPublisher(slotname);
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
}
pfree_ext(encryptConninfo);
heap_close(rel, RowExclusiveLock);
rc = memset_s(stmt->conninfo, strlen(stmt->conninfo), 0, strlen(stmt->conninfo));
@ -505,8 +439,6 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
Oid subid;
bool enabled_given = false;
bool enabled;
bool binary_given;
bool binary;
char *synchronous_commit;
char *conninfo;
char *slot_name;
@ -541,7 +473,7 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
/* Parse options. */
parse_subscription_options(stmt->options, &conninfo, &publications, &enabled_given, &enabled, &slotname_given,
&slot_name, &synchronous_commit, &binary_given, &binary);
&slot_name, &synchronous_commit);
/* Form a new tuple. */
rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls));
@ -558,15 +490,23 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
if (conninfo) {
/* Check the connection info string. */
libpqrcv_check_conninfo(conninfo);
encryptConninfo = EncryptOrDecryptConninfo(conninfo, 'E');
rc = memset_s(conninfo, strlen(conninfo), 0, strlen(conninfo));
securec_check(rc, "\0", "\0");
values[Anum_pg_subscription_subconninfo - 1] = CStringGetTextDatum(encryptConninfo);
replaces[Anum_pg_subscription_subconninfo - 1] = true;
/* encrypt conninfo */
List *conninfoList = ConninfoToDefList(conninfo);
/* 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;
/* need to check whether new conninfo can be used to connect to new publisher */
values[Anum_pg_subscription_subconninfo - 1] = CStringGetTextDatum(encryptConninfo);
replaces[Anum_pg_subscription_subconninfo - 1] = true;
pfree_ext(conninfoList);
if (sub->enabled || (enabled_given && enabled)) {
/* we need to check whether new conninfo can be used to connect to new publisher */
checkConn = true;
}
}
@ -608,10 +548,6 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
values[Anum_pg_subscription_subsynccommit - 1] = CStringGetTextDatum(synchronous_commit);
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) {
values[Anum_pg_subscription_subpublications - 1] = publicationListToArray(publications);
replaces[Anum_pg_subscription_subpublications - 1] = true;
@ -634,18 +570,16 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
if (sub->enabled && !enabled) {
ereport(ERROR, (errmsg("If you want to deactivate this subscription, use DROP SUBSCRIPTION.")));
}
/* enabling subscription, but slot hasn't been created,
* then mark createSlot to true.
*/
if (!sub->enabled && enabled && (!sub->slotname || !*(sub->slotname))) {
/* enable subscription */
if (!sub->enabled && enabled) {
/* if slot hasn't been created, then create it */
if (!sub->slotname || !*(sub->slotname)) {
createSlot = true;
}
}
if (checkConn || createSlot || validateSlot) {
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.")));
}
ConnectPublisher(encryptConninfo, finalSlotName);
if (createSlot) {
CreateSlotInPublisher(finalSlotName);
@ -663,6 +597,12 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
if (needFreeConninfo) {
pfree_ext(encryptConninfo);
}
if (conninfo) {
rc = memset_s(conninfo, strlen(conninfo), 0, strlen(conninfo));
securec_check(rc, "", "");
}
return myself;
}
@ -813,11 +753,7 @@ void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
initStringInfo(&cmd);
appendStringInfo(&cmd, "DROP_REPLICATION_SLOT %s", quote_identifier(slotname));
if (!AttemptConnectPublisher(conninfo, slotname, true)) {
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg(
"could not connect to publisher.")));
}
ConnectPublisher(conninfo, slotname);
PG_TRY();
{
int sqlstate = 0;
@ -843,7 +779,6 @@ void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
pfree_ext(conninfo);
pfree(cmd.data);
heap_close(rel, NoLock);
}
@ -973,149 +908,3 @@ void RenameSubscription(List *oldname, const char *newname)
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;
}

View File

@ -23175,7 +23175,7 @@ static void checkValidationForExchangeTable(Relation partTableRel, Relation ordT
int2 bucketId = InvalidBktId;
// get right partition oid for the tuple
targetPartOid = heapTupleGetPartitionId(partTableRel, (HeapTuple)tuple, true);
targetPartOid = heapTupleGetPartitionId(partTableRel, (HeapTuple) tuple);
searchFakeReationForPartitionOid(
partRelHTAB, CurrentMemoryContext, partTableRel, targetPartOid, partRel, part, RowExclusiveLock);
@ -24797,8 +24797,7 @@ static Oid AddTemporaryPartitionForAlterPartitions(const AlterTableCmd* cmd, Rel
destPartOid = AddTemporaryHashPartitionForAlterPartitions(cmd, partTableRel, partSeq, renameTargetPart);
break;
}
case PART_TYPE_RANGE:
case PART_TYPE_INTERVAL: {
case PART_TYPE_RANGE: {
destPartOid = AddTemporaryRangePartitionForAlterPartitions(cmd, partTableRel, partSeq, renameTargetPart);
break;
}
@ -25099,11 +25098,11 @@ static void readTuplesAndInsertInternal(Relation tempTableRel, Relation partTabl
/* tableam_tops_copy_tuple is not ready so we add UStore hack path */
copyTuple = tableam_tops_copy_tuple(tuple);
targetPartOid = heapTupleGetPartitionId(partTableRel, (void *)tuple, true);
targetPartOid = heapTupleGetPartitionId(partTableRel, (void *)tuple);
searchFakeReationForPartitionOid(
partRelHTAB, CurrentMemoryContext, partTableRel, targetPartOid, partRel, part, RowExclusiveLock);
if (RelationIsSubPartitioned(partTableRel)) {
targetPartOid = heapTupleGetPartitionId(partRel, (void *)tuple, true);
targetPartOid = heapTupleGetPartitionId(partRel, (void *)tuple);
searchFakeReationForPartitionOid(partRelHTAB, CurrentMemoryContext, partRel, targetPartOid, subPartRel,
subPart, RowExclusiveLock);
partRel = subPartRel;

6
src/gausskernel/optimizer/commands/user.cpp Normal file → Executable file
View File

@ -5911,7 +5911,6 @@ Datum calculate_encrypted_combined_password(const char* password, const char* ro
errno_t rc = EOK;
/* For PG ecological compatibility, we stored both sha256 and md5 password. */
/* the encrypted method of sha256 */
if (!pg_sha256_encrypt(password,
salt_string,
strlen(salt_string),
@ -5922,7 +5921,7 @@ Datum calculate_encrypted_combined_password(const char* password, const char* ro
securec_check(rc, "\0", "\0");
ereport(ERROR, (errcode(ERRCODE_INVALID_PASSWORD), errmsg("first stage encryption password failed")));
}
/* the encrypted method of md5 */
if (!pg_md5_encrypt(password, rolname, strlen(rolname), encrypted_md5_password)) {
rc = memset_s(encrypted_md5_password, MD5_PASSWD_LEN + 1, 0, MD5_PASSWD_LEN + 1);
securec_check(rc, "\0", "\0");
@ -6053,7 +6052,6 @@ static Datum gs_calculate_encrypted_sm3_password(const char* password, const cha
Datum calculate_encrypted_password(bool is_encrypted, const char* password, const char* rolname,
const char* salt_string)
{
/* If the password is '\0' or not exist */
if (password == NULL || password[0] == '\0') {
ereport(ERROR, (errcode(ERRCODE_INVALID_PASSWORD), errmsg("The password could not be NULL.")));
}
@ -6061,7 +6059,6 @@ Datum calculate_encrypted_password(bool is_encrypted, const char* password, cons
char encrypted_md5_password[MD5_PASSWD_LEN + 1] = {0};
Datum datum_value;
/* If the password has encrypted */
if (!is_encrypted || isPWDENCRYPTED(password)) {
return CStringGetTextDatum(password);
}
@ -6071,7 +6068,6 @@ Datum calculate_encrypted_password(bool is_encrypted, const char* password, cons
* if Password_encryption_type is 0, the encrypted password is md5.
* if Password_encryption_type is 1, the encrypted password is sha256 + md5.
* if Password_encryption_type is 2, the encrypted password is sha256.
* if Password_encryption_type is 3, the encrypted password is SM3.
*/
if (u_sess->attr.attr_security.Password_encryption_type == 0) {
if (!pg_md5_encrypt(password, rolname, strlen(rolname), encrypted_md5_password)) {

View File

@ -1181,17 +1181,6 @@ static Node* pull_up_simple_subquery(PlannerInfo* root, Node* jtnode, RangeTblEn
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
* to upper query's. We have to fix the subquery's append_rel_list as

View File

@ -1263,7 +1263,7 @@ static void ckpt_pagewriter_main_thread_loop(void)
HandlePageWriterMainInterrupts();
candidate_num = get_curr_candidate_nums(false) + get_curr_candidate_nums(true);
if (candidate_num == 0 && !t_thrd.pagewriter_cxt.shutdown_requested) {
if (candidate_num == 0) {
/* wakeup sub thread scan the buffer pool, init the candidate list */
wakeup_sub_thread();
}

View File

@ -825,10 +825,10 @@ void client_read_ended(void)
#define INIT_PLUGIN_OBJECT "init_plugin_object"
void InitBSqlPluginHookIfNeeded()
{
const char* dolphin = "dolphin";
const char* b_sql_plugin = "b_sql_plugin";
CFunInfo tmpCF;
tmpCF = load_external_function(dolphin, INIT_PLUGIN_OBJECT, false, false);
tmpCF = load_external_function(b_sql_plugin, INIT_PLUGIN_OBJECT, false, false);
if (tmpCF.user_fn != NULL) {
((void* (*)(void))(tmpCF.user_fn))();
}
@ -862,11 +862,9 @@ List* pg_parse_query(const char* query_string, List** query_string_locationlist)
List* (*parser_hook)(const char*, List**) = 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 = (List* (*)(const char*, List**))g_instance.raw_parser_hook[id];
}
int id = GetCustomParserId();
if (id >= 0 && g_instance.raw_parser_hook[id] != NULL) {
parser_hook = (List* (*)(const char*, List**))g_instance.raw_parser_hook[id];
}
#endif
raw_parsetree_list = parser_hook(query_string, query_string_locationlist);
@ -6114,9 +6112,6 @@ void ProcessInterrupts(void)
/* The logical replication launcher can be stopped at any time. */
proc_exit(0);
} else if (IsLogicalWorker()) {
ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN),
errmsg("terminating logical replication worker due to administrator command")));
#endif
} else if (IsTxnSnapCapturerProcess()) {
ereport(FATAL,
@ -7574,7 +7569,7 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam
init_set_params_htab();
#ifndef ENABLE_MULTIPLE_NODES
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.dolphin) {
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.b_sql_plugin) {
InitBSqlPluginHookIfNeeded();
}
#endif

View File

@ -848,7 +848,7 @@ static bool InitSession(knl_session_context* session)
t_thrd.proc_cxt.PostInit->InitSession();
#ifndef ENABLE_MULTIPLE_NODES
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.dolphin) {
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.b_sql_plugin) {
InitBSqlPluginHookIfNeeded();
}
#endif

View File

@ -3,9 +3,9 @@
* execClusterResize.cpp
* MPPDB ClusterResizing relevant routines
*
* c 2020
* c 1996-2012PostgreSQL
* c 1994
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/gausskernel/runtime/executor/execClusterResize.cpp
@ -44,10 +44,10 @@
/*
* ---------------------------------------------------------------------------------
* /*
* *Local functions/variables declaration fields*
* ---------------------------------------------------------------------------------
*/
/*删除增量表定义 */
/* delete delta table definition */
#define Natts_pg_delete_delta 3
#define Anum_pg_delete_delta_xcnodeid_and_dntableoid 1
@ -120,12 +120,12 @@ static inline bool redis_ctid_retrive_function(const char* funcname, Oid rettype
/*
*pg_delete_delta表中
* -
* @rel/
* @tupleid
*-
*
* - Brief: Record the given tuple's tupleid into pg_delete_delta table
* - Parameter:
* @rel: target relation of UPDATE/DELETE operation
* @tupleid: tupleid that needs record
* - Return:
* no return value
*/
void RecordDeletedTuple(Oid relid, int2 bucketid, const ItemPointer tupleid, const Relation deldelta_rel)
{
@ -134,10 +134,10 @@ void RecordDeletedTuple(Oid relid, int2 bucketid, const ItemPointer tupleid, con
HeapTuple tup = NULL;
Assert(deldelta_rel);
/*在重新分发中,表 delete_delta 有 3 列或 2 列。 */
/* In redistribution, table delete_delta has 3 or 2 column. */
Assert(RelationGetDescr(deldelta_rel)->natts <= 3);
/*循环访问初始化空值和值的属性 */
/* Iterate through attributes initializing nulls and values */
for (int i = 0; i < Natts_pg_delete_delta; i++) {
nulls[i] = false;
values[i] = (Datum)0;
@ -149,7 +149,7 @@ void RecordDeletedTuple(Oid relid, int2 bucketid, const ItemPointer tupleid, con
if (BUCKET_NODE_IS_VALID(bucketid)) {
values[Anum_pg_delete_delta_tablebucketid_and_ctid - 1] |= ((uint64)bucketid << 48);
}
/* 记录增量 */
/* Record delta */
tup = heap_form_tuple(RelationGetDescr(deldelta_rel), values, nulls);
(void)simple_heap_insert(deldelta_rel, tup);
@ -157,18 +157,18 @@ void RecordDeletedTuple(Oid relid, int2 bucketid, const ItemPointer tupleid, con
}
/*
* -
* -
* @rel
* -
* @TRUE
* @FALSE:
* - Brief: Determine if the relation is under cluster resizing operation
* - Parameter:
* @rel: relation that needs to check
* - Return:
* @TRUE: relation is under cluster resizing
* @FALSE: relation is not under cluster resizing
*/
bool RelationInClusterResizing(const Relation rel)
{
Assert(rel != NULL);
/*检查关系的append_mode状态 */
/* Check relation's append_mode status */
if (!IsInitdb && RelationInRedistribute(rel))
return true;
@ -176,18 +176,18 @@ bool RelationInClusterResizing(const Relation rel)
}
/*
* - :
* - :
* @rel:
* - :
* @TRUE:
* @FALSE:
* - Brief: Determine if the relation is under cluster resizing read only operation
* - Parameter:
* @rel: relation that needs to check
* - Return:
* @TRUE: relation is under cluster resizing read only
* @FALSE: relation is not under cluster resizing read only
*/
bool RelationInClusterResizingReadOnly(const Relation rel)
{
Assert(rel != NULL);
/*检查关系的append_mode状态 */
/* Check relation's append_mode status */
if (!IsInitdb && RelationInRedistributeReadOnly(rel))
return true;
@ -195,18 +195,18 @@ bool RelationInClusterResizingReadOnly(const Relation rel)
}
/*
* - :
* - :
* @rel:
* - :
* @TRUE: endcatchup()
* @FALSE: endcatchup()
* - Brief: Determine if the relation is under cluster resizing read only operation
* - Parameter:
* @rel: relation that needs to check
* - Return:
* @TRUE: relation is under cluster resizing endcatchup(write error)
* @FALSE: relation is not under cluster resizing endcatchup(write error)
*/
bool RelationInClusterResizingEndCatchup(const Relation rel)
{
Assert(rel != NULL);
/* 检查关系的append_mode状态*/
/* Check relation's append_mode status */
if (!IsInitdb && RelationInRedistributeEndCatchup(rel))
return true;
@ -214,9 +214,9 @@ bool RelationInClusterResizingEndCatchup(const Relation rel)
}
/*
* @:
* @range_var:
* @:true
* @Description: check whether relation is in redistribution though range variable.
* @in range_var: range variable which stored relation info.
* @return: true for in redistribution.
*/
bool CheckRangeVarInRedistribution(const RangeVar* range_var)
{
@ -228,7 +228,7 @@ bool CheckRangeVarInRedistribution(const RangeVar* range_var)
if (OidIsValid(relid)) {
relation = relation_open(relid, NoLock);
/* 如果关系是索引,我们应该检查相关表是否在调整大小。*/
/* If the relation is index, we should check the related table is resizing or not. */
if (RelationIsIndex(relation)) {
Oid heapOid = IndexGetRelation(relid, false);
Relation heapRelation = relation_open(heapOid, AccessShareLock);
@ -245,12 +245,12 @@ bool CheckRangeVarInRedistribution(const RangeVar* range_var)
}
/*
* - :delete_delta table
* - :
* @relname:
* - :
* @TRUE: delete_delta表
* @FALSE: delete_delta表
* - Brief: Determine if the table name is delete_delta table.
* - Parameter:
* @relname: name of target table
* - Return:
* @TRUE: the table is delete_delta table
* @FALSE: the table is not delete_delta table
*/
bool RelationIsDeleteDeltaTable(char* delete_delta_name)
{
@ -292,10 +292,10 @@ bool RelationIsDeleteDeltaTable(char* delete_delta_name)
}
/*
* - :
* - :
* @TRUE:
* @FALSE:
* - Brief: Determine if the Progress is under cluster resizing status
* - Return:
* @TRUE: Progress is under cluster resizing
* @FALSE: Progress is not under cluster resizing
*/
bool ClusterResizingInProgress()
{
@ -329,27 +329,27 @@ bool ClusterResizingInProgress()
}
/*
* -:delete_delta表的名称
* - :
* @relname:
* @delta_delta_name: delete_delta表名的输出值
* @isMultiCatchup: delta
* - :
*
* - Brief: get the name of delete_delta table
* - Parameter:
* @relname: name of target table
* @delta_delta_name: output value for delete_delta table name
* @isMultiCatchup: multi catchup delta or not
* - Return:
* no return value
*/
static inline void RelationGetDeleteDeltaTableName(Relation rel, char* delete_delta_name, bool isMultiCatchup)
{
int rc = 0;
/* 检查输出参数是否没有从调用方palloc()-ed */
/* Check if output parameter it not palloc()-ed from caller side */
if (delete_delta_name == NULL || rel == NULL) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Invalid parameter in function '%s'", __FUNCTION__)));
}
/*
* Relation的关联以获得表的id
* delete_delta表的名称
* Look up Relation's reloptions to get table's cnoid to
* form the name of delete_delta table
*/
if (!IsInitdb) {
if (RelationInClusterResizing(rel) && !RelationInClusterResizingReadOnly(rel)) {
@ -381,12 +381,12 @@ static inline void RelationGetDeleteDeltaTableName(Relation rel, char* delete_de
}
/*
* - :delete_delta rel
* - :
* @rel: UPDATE/DELETE/TRUNCATE操作的目标关系
* @lockmode:
* @isMultiCatchup: delta
* - :
* - Brief: get and open delete_delta rel
* - Parameter:
* @rel: target relation of UPDATE/DELETE/TRUNCATE operation
* @lockmode: lock mode
* @isMultiCatchup: multi catchup delta or not
* - Return:
* delete_delta rel
*/
Relation GetAndOpenDeleteDeltaRel(const Relation rel, LOCKMODE lockmode, bool isMultiCatchup)
@ -403,22 +403,22 @@ Relation GetAndOpenDeleteDeltaRel(const Relation rel, LOCKMODE lockmode, bool is
RelationGetDeleteDeltaTableName(rel, (char*)delete_delta_tablename, isMultiCatchup);
data_redis_namespace = get_namespace_oid("data_redis", false);
/* 我们将在data_redis模式下获取delete delta关系。 */
/* We are going to fetch the delete delta relation under data_redis schema. */
deldelta_relid = get_relname_relid(delete_delta_tablename, data_redis_namespace);
if (!OidIsValid(deldelta_relid)) {
/*
* NULL We should not
* delta表是( Multi catchup delta table is)
*
* If multi catchup delta table is not there, just return NULL. We should not
* report error, because it is a valid case. Multi catchup delta table is
* dropped in each catchup iteration.
*/
if (isMultiCatchup) {
return NULL;
}
/*
* 2
* maxheapattributennumber的限制
*
* To support Update or Delete during extension, we need to add 2 more columns.
* more columns. Limited by MaxHeapAttributeNumber, if the table already contains too many columns,
* we don't allow update or delete anymore, but insert statement can still proceed.
*/
if (((rel->rd_att->natts > (MaxHeapAttributeNumber - (Natts_pg_delete_delta - 1))) &&
!RELATION_IS_PARTITIONED(rel)) ||
@ -429,7 +429,7 @@ Relation GetAndOpenDeleteDeltaRel(const Relation rel, LOCKMODE lockmode, bool is
RelationGetRelationName(rel)),
errdetail("Can not support online extension, if the table contains too many columns")));
}
/* 错误情况下,不应该出现在这里 */
/* ERROR case, should never come here */
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("delete delta table %s is not found when do cluster resizing table \"%s\"",
@ -446,11 +446,11 @@ Relation GetAndOpenDeleteDeltaRel(const Relation rel, LOCKMODE lockmode, bool is
}
/*
* - :线ddl
* - :
* @rel: DDL的解析树
* -:
*
* - Brief: Check the stmtment during online expansion, block unsupported ddl in cluster resizing.
* - Parameter:
* @rel: parsetree of DDL
* - Return:
* no return value
*/
void BlockUnsupportedDDL(const Node* parsetree)
{
@ -466,11 +466,11 @@ void BlockUnsupportedDDL(const Node* parsetree)
LOCKMODE lockmode_openrel = AccessShareLock;
/*
*
* relation.
* rel
* :syscache条目
*
* Check for shared-cache-inval messages before trying to access the
* relation. This is needed to cover the case where the name
* identifies a rel that has been dropped and recreated since the
* start of our transaction: if we don't flush the old syscache entry,
* then we'll latch onto that entry and suffer an error later.
*/
AcceptInvalidationMessages();
@ -501,13 +501,13 @@ void BlockUnsupportedDDL(const Node* parsetree)
return;
} break;
/* 在集群调整大小时阻塞游标 */
/* Block CURSOR for while table in cluster resizing */
case T_PlannedStmt: {
PlannedStmt* stmt = (PlannedStmt*)parsetree;
relidlist = stmt->relationOids;
} break;
/* 当表在集群中调整大小时块RENAME */
/* Block RENAME while table in cluster resizing */
case T_RenameStmt: {
RenameStmt* stmt = (RenameStmt*)parsetree;
@ -540,11 +540,11 @@ void BlockUnsupportedDDL(const Node* parsetree)
stmt->relation->relname)));
} break;
/* 当表在集群中调整大小时Block ALTER设置模式 */
/* Block ALTER set schema while table in cluster resizing */
case T_AlterObjectSchemaStmt: {
AlterObjectSchemaStmt* stmt = (AlterObjectSchemaStmt*)parsetree;
/* 在传输时禁用alter table set schema */
/* disable alter table set schema when transfer */
if (stmt->relation != NULL) {
Oid relOid = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
if (OidIsValid(relOid)) {
@ -567,7 +567,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
stmt->relation->relname)));
} break;
/* 当表在集群中调整大小时,阻塞创建索引(仅适用于行表) */
/* Block CREATE index while table in cluster resizing(for row table only) */
case T_IndexStmt: {
IndexStmt* stmt = (IndexStmt*)parsetree;
if (stmt->relation) {
@ -590,13 +590,13 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
} break;
/* 当表在集群中调整大小时块REINDEX(仅适用于行表) */
/* Block REINDEX while table in cluster resizing(for row table only) */
case T_ReindexStmt: {
ReindexStmt* stmt = (ReindexStmt*)parsetree;
if (stmt->relation) {
relid = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
if (OidIsValid(relid)) {
/* 在锁表之前释放索引锁以避免死锁 */
/* release index lock before lock table to avoid deadlock */
UnlockRelationOid(relid, AccessShareLock);
Relation relation = relation_open(relid, NoLock);
@ -622,7 +622,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
} break;
/* 当表在集群中调整大小时阻塞ALTER-Table */
/* Block ALTER-Table while table in cluster resizing */
case T_AlterTableStmt: {
AlterTableStmt* stmt = (AlterTableStmt*)parsetree;
AlterTableCmd* cmd = NULL;
@ -631,13 +631,13 @@ void BlockUnsupportedDDL(const Node* parsetree)
switch (cmd->subtype) {
case AT_TruncatePartition: {
/*
*
*线
* We do not allow truncate partition when the target is in read only
* mode during online expansion time.
*/
if (stmt->relation) {
relid = RangeVarGetRelid(stmt->relation, lockmode_getrelid, true);
if (OidIsValid(relid)) {
/* 禁止在传输过程中截断分区 */
/* disable alter table truncate partition during transfer */
if (CheckRangeVarInRedistribution(stmt->relation)) {
Oid nsOid = GetNamespaceIdbyRelId(relid);
TRANSFER_DISABLE_DDL(nsOid);
@ -704,12 +704,12 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
}
/* 如果rel选项包含append_mode则不检查。 */
/* If rel option contain append_mode, then not check. */
if (opt != NULL) {
break;
}
}
/* 失败 */
/* fall through */
default: {
if (stmt->relation && !u_sess->attr.attr_sql.enable_cluster_resize &&
CheckRangeVarInRedistribution(stmt->relation))
@ -725,7 +725,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
return;
} break;
/* 当集群中的目标表调整大小时阻塞CREATE-RULE语句 */
/* Block CREATE-RULE statements while target table in cluster resizing */
case T_RuleStmt: {
RuleStmt* stmt = (RuleStmt*)parsetree;
if (stmt->relation) {
@ -734,7 +734,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
} break;
/* 当所有者表在集群中调整大小时Block CREATE SEQUENCE设置模式 */
/* Block CREATE SEQUENCE set schema while owner table in cluster resizing */
case T_CreateSeqStmt: {
CreateSeqStmt* stmt = (CreateSeqStmt*)parsetree;
List* owned_by = NULL;
@ -761,7 +761,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
} break;
/* 当集群中的所有者表调整大小时阻塞ALTER SEQUENCE */
/* Block ALTER SEQUENCE while owner table in cluster resizing */
case T_AlterSeqStmt: {
AlterSeqStmt* stmt = (AlterSeqStmt*)parsetree;
List* owned_by = NIL;
@ -788,7 +788,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
} break;
/* 当表在集群中调整大小时阻塞集群 */
/* Block CLUSTER while table in cluster resizing */
case T_ClusterStmt: {
ClusterStmt* stmt = (ClusterStmt*)parsetree;
if (stmt->relation && CheckRangeVarInRedistribution(stmt->relation))
@ -799,7 +799,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
stmt->relation->relname)));
} break;
/* 当表在集群中调整大小时,块真空已满 */
/* Block VACUUM FULL while table in cluster resizing */
case T_VacuumStmt: {
VacuumStmt* stmt = (VacuumStmt*)parsetree;
if ((stmt->options & VACOPT_VACUUM) || (stmt->options & VACOPT_MERGE)) {
@ -822,7 +822,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
} break;
/* 在集群调整大小时当目标表为只读时块截断DDL */
/* Block truncate DDL when the target table is read only in cluster resizing */
case T_TruncateStmt: {
ListCell* cell = NULL;
TruncateStmt* stmt = (TruncateStmt*)parsetree;
@ -857,7 +857,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
DropStmt* stmt = (DropStmt*)parsetree;
switch (stmt->removeType) {
case OBJECT_TABLE: {
/* 在传输时禁用drop表 */
/* disable drop table when transfer */
ListCell* cell = NULL;
foreach (cell, stmt->objects) {
RangeVar* rel = makeRangeVarFromNameList((List*)lfirst(cell));
@ -871,7 +871,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
break;
}
case OBJECT_SCHEMA: {
/* 传输时禁用删除模式 */
/* disable drop schema when transfer */
ListCell* cell = NULL;
foreach (cell, stmt->objects) {
List* objname = (List*)lfirst(cell);
@ -887,7 +887,7 @@ void BlockUnsupportedDDL(const Node* parsetree)
} break;
case T_CreateStmt: {
/* 禁止传输时创建表 */
/* disable create table when transfer */
CreateStmt* stmt = (CreateStmt*)parsetree;
if (stmt->relation != NULL) {
Oid nsOid = RangeVarGetCreationNamespace(stmt->relation);
@ -916,14 +916,14 @@ void BlockUnsupportedDDL(const Node* parsetree)
}
/*
* - :线
* FQS评估时
* as STABLE
* - :
* @funcid: gs_redis范围内创建/Oid
* - :
* @true:
* @false:
* - Brief: For online expanions, the shippable function is evaluated here, the module
* will be invoked in optimizer when do FQS evaluation, we have to define function
* as STABLE
* - Parameter:
* @funcid: oid of user defined function which is createed/dropped in scope of gs_redis
* - Return:
* @true: shippable
* @false: unshippable
*/
bool redis_func_shippable(Oid funcid)
{
@ -937,11 +937,11 @@ bool redis_func_shippable(Oid funcid)
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function with OID %u does not exist", funcid)));
}
/* 获取函数签名 */
/* Fetch function signatures */
rettype = get_func_signature(funcid, &argstype, &nargs);
if (redis_tupleid_retrive_function(func_name, rettype, argstype, nargs)) {
/* Tupleid检索函数可以发布到数据节点 */
/* tupleid retrive functions is shippable to datanodes */
result = true;
} else if (redis_offset_retrive_function(func_name, rettype, argstype, nargs)) {
result = true;
@ -961,11 +961,11 @@ bool redis_func_shippable(Oid funcid)
}
/*
* - :
* - :
* @funcid: oid
* - :
* @result: true: false:
* - Brief: determine if given funcid reflects a dn-stable function
* - Parameter:
* @funcid: function oid that to evaluate
* - Return:
* @result: true:dnstable false: not-dnstable function
*/
bool redis_func_dnstable(Oid funcid)
{
@ -981,11 +981,11 @@ bool redis_func_dnstable(Oid funcid)
errmsg("function with OID %u does not exist when checking function dnstable", funcid)));
}
/* 获取函数签名 */
/* Fetch function signatures */
rettype = get_func_signature(funcid, &argstype, &nargs);
if (redis_tupleid_retrive_function(func_name, rettype, argstype, nargs)) {
/* 管状反射函数是不稳定的 */
/* tupleid retrive functions is dnstable */
result = true;
}
@ -993,23 +993,23 @@ bool redis_func_dnstable(Oid funcid)
}
/*
* - :ctid函数求值为const值以避免每次扫描
* seqscan中调用元组
* - :
* @rel:
* @original_quals: quals可能包含ctid_funcs
* @isRangeScanInRedis: redis范围扫描
* - :
* @new_quals: Quals将被const替换
* - Brief: evaluate ctid functions into a const value to avoid per-scanning
* tuple invokation in seqscan.
* - Parameter:
* @rel: the rel being redistributing
* @original_quals: the original quals possible contains ctid_funcs
* @isRangeScanInRedis: if is a redis range scan
* - Return:
* @new_quals: quals which func call be replaced by a const
*/
List* eval_ctid_funcs(Relation rel, List* original_quals, RangeScanInRedis *rangeScanInRedis)
{
StringInfo qual_str = makeStringInfo();
/*
* eval_dnstable_func_mutator的存在quals进行复制
*
*
* we have to make a copy of the original quals, since the eval_dnstable_func_mutator
* will modify the it. the original qual will be needed again and again in later
* to be re-eval in partition table scans.
*/
List* new_quals = (List*)copyObject((const void*)(original_quals));
@ -1033,16 +1033,16 @@ static int32 get_expr_const_val(Node *val){
}
/*
* - :eval_dnstable_func()const
* seqscan中调用每次扫描的元组
* - :
* @rel:
* @node:
* @qual_str:
* @isRangeScanInRedis: redis中的范围扫描
* @isRoot:
* - :
* @result: dn稳定函数const评估
* - Brief: working house for eval_dnstable_func() to evaluate dn stable function into a const
* value to avoid per-scanning tuple invocation in seqscan
* - Parameter:
* @rel: the rel being redistributing
* @node: expression node
* @qual_str: predicate pattern
* @isRangeScanInRedis: output to indicate if the predicate pattern is range scan in redis
* @isRoot: we want to compare the predicate pattern only once at root level
* - Return:
* @result: expression tree with dn stable function const-evaluated
*/
static Node* eval_dnstable_func_mutator(
Relation rel, Node* node, StringInfo qual_str, RangeScanInRedis *rangeScanInRedis, bool isRoot)
@ -1057,7 +1057,7 @@ static Node* eval_dnstable_func_mutator(
case T_FuncExpr: {
FuncExpr* expr = (FuncExpr*)node;
/* 将一个稳定函数扁平化为const值 */
/* flatten dn stable function into const value */
if (redis_func_dnstable(expr->funcid)) {
Node* new_const = NULL;
char* funcname = get_func_name(expr->funcid);
@ -1093,8 +1093,8 @@ static Node* eval_dnstable_func_mutator(
Node* new_expr = eval_dnstable_func_mutator(rel, expr, qual_str, rangeScanInRedis, false);
/*
* FuncExpr节点求值为T_Const值
*
* If a FuncExpr node is evalated into a T_Const value, we are hitting
* the point so replace it in qual list.
*/
if (expr && IsA(expr, FuncExpr) && new_expr && IsA(new_expr, Const)) {
l = list_delete_ptr(l, expr);
@ -1103,8 +1103,8 @@ static Node* eval_dnstable_func_mutator(
}
/*
* where ctid between pg_get_redis_rel_start_ctid('xx')
* pg_get_redis_rel_end_ctid('xx')"在DN上我们将在扫描节点下推谓词。
* If the predicate at root is something like "where ctid between pg_get_redis_rel_start_ctid('xx')
* and pg_get_redis_rel_end_ctid('xx')" on DN, we will pushdown the predicate at scan node.
*/
if (isRoot && pg_strcasecmp(qual_str->data, RANGE_SCAN_IN_REDIS) == 0) {
rangeScanInRedis->isRangeScanInRedis = true;
@ -1129,7 +1129,7 @@ static Node* eval_dnstable_func_mutator(
}
case T_Var: {
Var* var = (Var*)node;
/* 我们只期望谓词中有tid列 */
/* we only expect tid column in the predicate */
if (var->vartype == TIDOID) {
appendStringInfoString(qual_str, "tid");
appendStringInfoString(qual_str, "+");
@ -1147,10 +1147,10 @@ static Node* eval_dnstable_func_mutator(
}
/*
* - :new_table rel
* - :
* @rel: TRUNCATE操作的目标关系
* - :
* - Brief: get and open new_table rel
* - Parameter:
* @rel: target relation of TRUNCATE operation
* - Return:
* new_table rel
*/
Relation GetAndOpenNewTableRel(const Relation rel, LOCKMODE lockmode)
@ -1168,7 +1168,7 @@ Relation GetAndOpenNewTableRel(const Relation rel, LOCKMODE lockmode)
data_redis_namespace = get_namespace_oid("data_redis", false);
newtable_relid = get_relname_relid(new_tablename, data_redis_namespace);
if (!OidIsValid(newtable_relid)) {
/* 错误情况下,不应该出现在这里 */
/* ERROR case, should never come here */
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("new table %s is not found when do cluster resizing table \"%s\"",
@ -1185,18 +1185,18 @@ Relation GetAndOpenNewTableRel(const Relation rel, LOCKMODE lockmode)
}
/*
* - :
* - :
* @relname:
* @newtable_name:
* - :
*
* - Brief: get the name of new table
* - Parameter:
* @relname: name of target table
* @newtable_name: output value for new table name
* - Return:
* no return value
*/
void RelationGetNewTableName(Relation rel, char* newtable_name)
{
int rc = 0;
/* 检查输出参数是否没有从调用方palloc()-ed */
/* Check if output parameter it not palloc()-ed from caller side */
if (newtable_name == NULL || rel == NULL) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@ -1204,8 +1204,8 @@ void RelationGetNewTableName(Relation rel, char* newtable_name)
}
/*
*
*
* Look up relaion's reloptions to get table's cnoid to
* form the name of new table
*/
if (!IsInitdb) {
Oid rel_cn_oid = RelationGetRelCnOid(rel);
@ -1216,19 +1216,19 @@ void RelationGetNewTableName(Relation rel, char* newtable_name)
rc = snprintf_s(
newtable_name, NAMEDATALEN, NAMEDATALEN - 1, "data_redis_tmp_%s", RelationGetRelationName(rel));
}
/* 检查安全函数的返回值 */
/* check the return value of security function */
securec_check_ss(rc, "\0", "\0");
}
return;
}
/*
* - :
* - :
* @rel:
* - :
* @TRUE:
* @FALSE:
* - Brief: Determine if the relation is under cluster resizing write error mode
* - Parameter:
* @rel: relation that needs to check
* - Return:
* @TRUE: relation is under cluster resizing write error mode
* @FALSE: relation is not under cluster resizing write error mode
*/
bool RelationInClusterResizingWriteErrorMode(const Relation rel)
{

View File

@ -1,13 +1,13 @@
/* -------------------------------------------------------------------------
*
* execCurrent.c
* WHERE CURRENT OF游标执行程序支持WHERE CURRENT OF游标
* executor support for WHERE CURRENT OF cursor
*
*
* (c) 1996-2012, PostgreSQL全球发展集团
* (c) 1994
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/executor/execCurrent.c
*
* -------------------------------------------------------------------------
@ -38,12 +38,14 @@ static ScanState* search_plan_tree(PlanState *node, Oid table_oid);
/*
* execCurrentOf
*
* CURRENT OF表达式和表的OID
* CURRENT of的游标扫描
* TID为*current_tid
* Given a CURRENT OF expression and the OID of a table, determine which row
* of the table is currently being scanned by the cursor named by CURRENT OF,
* and return the row's TID into *current_tid.
*
* TRUEFALSE
* ()
* Returns TRUE if a row was identified. Returns FALSE if the cursor is valid
* for the table but is not currently scanning a row of the table (this is a
* legal situation in inheritance cases). Raises error if cursor is not a
* valid updatable scan of the specified table.
*/
bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relation, ItemPointer current_tid,
RelationPtr partitionOfCursor_tid)
@ -53,14 +55,14 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
QueryDesc *query_desc = NULL;
Oid table_oid = RelationGetRelid(relation);
/* 获取游标名称——可能需要查找参数引用 */
/* Get the cursor name --- may have to look up a parameter reference */
if (cexpr->cursor_name) {
cursor_name = cexpr->cursor_name;
} else {
cursor_name = fetch_cursor_param_value(econtext, cexpr->cursor_param);
}
/* 找到游标的入口 */
/* Find the cursor's portal */
portal = GetPortalByName(cursor_name);
if (!PortalIsValid(portal)) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_CURSOR),
@ -68,7 +70,8 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
}
/*
* select查询和持有的游标query_desc都可能为空
* We have to watch out for non-SELECT queries as well as held cursors,
* both of which may have null query_desc.
*/
if (portal->strategy != PORTAL_ONE_SELECT) {
ereport(ERROR,
@ -82,23 +85,26 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
}
/*
* 使
* /
* FOR UPDATE代码能够识别目标表FOR-UPDATE情况允许使用不敏感游标的when CURRENT of
* We have two different strategies depending on whether the cursor uses
* FOR UPDATE/SHARE or not. The reason for supporting both is that the
* FOR UPDATE code is able to identify a target table in many cases where
* the other code can't, while the non-FOR-UPDATE case allows use of WHERE
* CURRENT OF with an insensitive cursor.
*/
if (query_desc->estate->es_rowMarks) {
ExecRowMark *erm = NULL;
ListCell *lc = NULL;
/*
* FOR UPDATE/SHARE引用ctid信息
* Here, the query must have exactly one FOR UPDATE/SHARE reference to
* the target table, and we dig the ctid info out of that.
*/
erm = NULL;
foreach (lc, query_desc->estate->es_rowMarks) {
ExecRowMark *thiserm = (ExecRowMark *)lfirst(lc);
if (!RowMarkRequiresRowShareLock(thiserm->markType)) {
continue; /* 忽略非for UPDATE/SHARE项 */
continue; /* ignore non-FOR UPDATE/SHARE items */
}
if (RelationGetRelid(thiserm->relation) == table_oid) {
@ -118,14 +124,15 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
}
/*
* :SQL规范
* The cursor must have a current result row: per the SQL spec, it's
* an error if not.
*/
if (portal->atStart || portal->atEnd) {
ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_STATE),
errmsg("cursor \"%s\" is not positioned on a row when the cursor uses for UPDATE/SHARE", cursor_name)));
}
/* 返回当前扫描的TID(如果有) */
/* Return the currently scanned TID, if there is one */
if (ItemPointerIsValid(&(erm->curCtid))) {
*current_tid = erm->curCtid;
@ -137,7 +144,9 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
}
/*
* ;
* This table didn't produce the cursor's current row; some other
* inheritance child of the same parent must have. Signal caller to
* do nothing on this table.
*/
return false;
} else {
@ -147,7 +156,9 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
ItemPointer tuple_tid;
/*
* FOR UPDATE
* Without FOR UPDATE, we dig through the cursor's plan to find the
* scan node. Fail if it's not there or buried underneath
* aggregation.
*/
scanstate = search_plan_tree(query_desc->planstate, table_oid);
if (scanstate == NULL) {
@ -157,21 +168,23 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
}
/*
* :SQL规范
*
* OID是用于非活动扫描的false
* The cursor must have a current result row: per the SQL spec, it's
* an error if not. We test this at the top level, rather than at the
* scan node level, because in inheritance cases any one table scan
* could easily not be on a row. We want to return false, not raise
* error, if the passed-in table OID is for one of the inactive scans.
*/
if (portal->atStart || portal->atEnd) {
ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_STATE), errmsg(
"cursor \"%s\" is not positioned on a row when the cursor doesn't use for UPDATE/SHARE", cursor_name)));
}
/* 现在OK返回false如果我们发现一个非活动扫描 */
/* Now OK to return false if we found an inactive scan */
if (TupIsNull(scanstate->ss_ScanTupleSlot)) {
return false;
}
/* 使用slot_getattr捕获任何可能的错误 */
/* Use slot_getattr to catch any possible mistakes */
tuple_tableoid = DatumGetObjectId(tableam_tslot_getattr(scanstate->ss_ScanTupleSlot, TableOidAttributeNumber, &lisnull));
Assert(!lisnull);
tuple_tid = (ItemPointer)DatumGetPointer(
@ -193,7 +206,7 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
/*
* fetch_cursor_param_value
*
* REFCURSOR类型.
* Fetch the string value of a param, verifying it is of type REFCURSOR.
*/
static char *fetch_cursor_param_value(ExprContext *econtext, int paramId)
{
@ -202,20 +215,20 @@ static char *fetch_cursor_param_value(ExprContext *econtext, int paramId)
if (paramInfo && paramId > 0 && paramId <= paramInfo->numParams) {
ParamExternData *prm = &paramInfo->params[paramId - 1];
/* 如果参数是动态的,给钩子一个机会 */
/* give hook a chance in case parameter is dynamic */
if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL) {
(*paramInfo->paramFetch)(paramInfo, paramId);
}
if (OidIsValid(prm->ptype) && !prm->isnull) {
/* 安全检查,以防钩子发生意外 */
/* safety check in case hook did something unexpected */
if (prm->ptype != REFCURSOROID) {
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)", paramId,
format_type_be(prm->ptype), format_type_be(REFCURSOROID))));
}
/* 我们知道refcursor使用text的I/O例程 */
/* We know that refcursor uses text's I/O routines */
return TextDatumGetCString(prm->value);
}
}
@ -227,8 +240,8 @@ static char *fetch_cursor_param_value(ExprContext *econtext, int paramId)
/*
* search_plan_tree
*
* PlanState树中搜索指定表上的扫描节点
* NULL
* Search through a PlanState tree for a scan node on the specified table.
* Return NULL if not found or multiple candidates.
*/
#ifdef PGXC
ScanState* search_plan_tree(PlanState* node, Oid table_oid)
@ -249,7 +262,7 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
}
#endif
/*
*
* scan nodes can all be treated alike
*/
case T_SeqScanState:
case T_IndexScanState:
@ -271,7 +284,8 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
return result;
}
/*
* Append;(UNION ALL)
* For Append, we must look through the members; watch out for
* multiple matches (possible if it was from UNION ALL)
*/
case T_AppendState: {
AppendState *astate = (AppendState *)node;
@ -283,14 +297,14 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
if (elem == NULL)
continue;
if (result != NULL)
return NULL; /* 多个匹配 */
return NULL; /* multiple matches */
result = elem;
}
return result;
}
/*
* MergeAppend
* Similarly for MergeAppend
*/
case T_MergeAppendState: {
MergeAppendState *mstate = (MergeAppendState *)node;
@ -304,14 +318,15 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
continue;
}
if (result != NULL) {
return NULL; /* 多个匹配 */
return NULL; /* multiple matches */
}
result = elem;
}
return result;
}
/*
* Result和Limit可以依次下降()
* Result and Limit can be descended through (these are safe
* because they always return their input's current row)
*/
#ifdef PGXC
case T_MaterialState:
@ -322,13 +337,13 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
return search_plan_tree(node->lefttree, table_oid);
/*
* SubqueryScan也可以
* SubqueryScan too, but it keeps the child in a different place
*/
case T_SubqueryScanState:
return search_plan_tree(((SubqueryScanState *)node)->subplan, table_oid);
default:
/* 否则,假设我们不能从里面下去 */
/* Otherwise, assume we can't descend through it */
break;
}
return NULL;

View File

@ -1,13 +1,15 @@
/* -------------------------------------------------------------------------
*
* execGrouping.cpp
*
* executor utility routines for grouping, hashing, and aggregation
*
* :
* Note: we currently assume that equality and hashing functions are not
* collation-sensitive, so the code in this file has no support for passing
* collation settings through from callers. That may have to change someday.
*
*
* (c) 1996-2012, PostgreSQL全球发展集团
* (c) 1994
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
@ -29,21 +31,22 @@ static uint32 TupleHashTableHash(const void* key, Size keysize);
static int TupleHashTableMatch(const void* key1, const void* key2, Size keysize);
/*****************************************************************************
*
* Utility routines for grouping tuples together
*****************************************************************************/
/*
* execTuplesMatch
* true
* Return true if two tuples match in all the indicated fields.
*
* SQL的
* This actually implements SQL's notion of "not distinct". Two nulls
* match, a null and a not-null don't match.
*
* slot1, slot2:(!)
* numCols:
* matchColIdx:
* eqFunctions:使fmgr查找信息的数组
* evalContext:
* slot1, slot2: the tuples to compare (must have same columns!)
* numCols: the number of attributes to be examined
* matchColIdx: array of attribute column numbers
* eqFunctions: array of fmgr lookup info for the equality functions to use
* evalContext: short-term memory context for executing the functions
*
* NB: evalContext !
* NB: evalContext is reset each time!
*/
bool execTuplesMatch(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols, AttrNumber* matchColIdx,
FmgrInfo* eqfunctions, MemoryContext evalContext)
@ -52,15 +55,15 @@ bool execTuplesMatch(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols,
bool result = false;
int i;
/* 重置并切换到temp上下文。 */
/* Reset and switch into the temp context. */
MemoryContextReset(evalContext);
oldContext = MemoryContextSwitchTo(evalContext);
/*
*
*
* ()
*
* We cannot report a match without checking all the fields, but we can
* report a non-match as soon as we find unequal fields. So, start
* comparing at the last field (least significant sort key). That's the
* most likely to be different if we are dealing with sorted input.
*/
result = true;
@ -75,17 +78,17 @@ bool execTuplesMatch(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols,
attr2 = tableam_tslot_getattr(slot2, att, &isNull2);
if (isNull1 != isNull2) {
result = false; /* 一个null一个not;它们是不相等的 */
result = false; /* one null and one not; they aren't equal */
break;
}
if (isNull1) {
continue; /* 两者都为空,同等对待 */
continue; /* both are null, treat as equal */
}
/* 应用特定于类型的相等函数 */
/* Apply the type-specific equality function */
if (!DatumGetBool(FunctionCall2(&eqfunctions[i], attr1, attr2))) {
result = false; /* 它们是不相等的 */
result = false; /* they aren't equal */
break;
}
}
@ -97,11 +100,13 @@ bool execTuplesMatch(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols,
/*
* execTuplesUnequal
* true
* Return true if two tuples are definitely unequal in the indicated
* fields.
*
* null既不等于也不等于其他任何东西not-equal的非空字段时
* Nulls are neither equal nor unequal to anything else. A true result
* is obtained only if there are non-null fields that compare not-equal.
*
* execTuplesMatch相同
* Parameters are identical to execTuplesMatch.
*/
bool execTuplesUnequal(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols, AttrNumber* matchColIdx,
FmgrInfo* eqfunctions, MemoryContext evalContext)
@ -112,14 +117,15 @@ bool execTuplesUnequal(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols
Assert(slot1->tts_tupleDescriptor->tdTableAmType == slot2->tts_tupleDescriptor->tdTableAmType);
/* 重置并切换到temp上下文 */
/* Reset and switch into the temp context. */
MemoryContextReset(evalContext);
oldContext = MemoryContextSwitchTo(evalContext);
/*
*
* ()
*
* We cannot report a match without checking all the fields, but we can
* report a non-match as soon as we find unequal fields. So, start
* comparing at the last field (least significant sort key). That's the
* most likely to be different if we are dealing with sorted input.
*/
result = false;
@ -132,18 +138,18 @@ bool execTuplesUnequal(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols
attr1 = tableam_tslot_getattr(slot1, att, &isNull1);
if (isNull1) {
continue; /* 不能证明什么 */
continue; /* can't prove anything here */
}
attr2 = tableam_tslot_getattr(slot2, att, &isNull2);
if (isNull2) {
continue; /* 不能证明什么 */
continue; /* can't prove anything here */
}
/* 应用特定于类型的相等函数 */
/* Apply the type-specific equality function */
if (!DatumGetBool(FunctionCall2(&eqfunctions[i], attr1, attr2))) {
result = true; /* 它们是不相等的 */
result = true; /* they are unequal */
break;
}
}
@ -155,9 +161,10 @@ bool execTuplesUnequal(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols
/*
* execTuplesMatchPrepare
* oid数组execTuplesMatch或exectuplesinequality所需的相等函数
* Look up the equality functions needed for execTuplesMatch or
* execTuplesUnequal, given an array of equality operator OIDs.
*
* lolod数组
* The result is a palloc'd array.
*/
FmgrInfo* execTuplesMatchPrepare(int numCols, Oid* eqOperators)
{
@ -177,12 +184,13 @@ FmgrInfo* execTuplesMatchPrepare(int numCols, Oid* eqOperators)
/*
* execTuplesHashPrepare
* TupleHashTable所需的相等和散列函数
* Look up the equality and hashing functions needed for a TupleHashTable.
*
* execTuplesMatchPrepare
* *eqFunctions和*hashFunctions接收palloc结果数组
* This is similar to execTuplesMatchPrepare, but we also need to find the
* hash functions associated with the equality operators. *eqFunctions and
* *hashFunctions receive the palloc'd result arrays.
*
* :
* Note: we expect that the given operators are not cross-type comparisons.
*/
void execTuplesHashPrepare(int numCols, Oid* eqOperators, FmgrInfo** eqFunctions, FmgrInfo** hashFunctions)
{
@ -208,7 +216,7 @@ void execTuplesHashPrepare(int numCols, Oid* eqOperators, FmgrInfo** eqFunctions
i,
numCols)));
/* 我们不支持交叉类型的情况 */
/* We're not supporting cross-type cases here */
Assert(left_hash_function == right_hash_function);
fmgr_info(eq_function, &(*eqFunctions)[i]);
fmgr_info(right_hash_function, &(*hashFunctions)[i]);
@ -216,23 +224,29 @@ void execTuplesHashPrepare(int numCols, Oid* eqOperators, FmgrInfo** eqFunctions
}
/*****************************************************************************
*
* Utility routines for all-in-memory hash tables
*
* ()
*
* These routines build hash tables for grouping tuples together (eg, for
* hash aggregation). There is one entry for each not-distinct set of tuples
* presented.
*****************************************************************************/
/*
* TupleHashTable
* Construct an empty TupleHashTable
*
* numCols, keyColIdx:使eqfunctions:
* 使hashfunctions:使nbuckets:
* entrysize:(sizeof(TupleHashEntryData))
* tablext:tempcxt:
* numCols, keyColIdx: identify the tuple fields to use as lookup key
* eqfunctions: equality comparison functions to use
* hashfunctions: datatype-specific hashing functions to use
* nbuckets: initial estimate of hashtable size
* entrysize: size of each entry (at least sizeof(TupleHashEntryData))
* tablecxt: memory context in which to store table and table entries
* tempcxt: short-lived context for evaluation hash and comparison functions
*
* execTuplesHashPrepare()
*
* The function arrays may be made with execTuplesHashPrepare(). Note they
* are not cross-type functions, but expect to see the table datatype(s)
* on both sides.
*
* keyColIdxeqfunctions和hashfunctions必须分配到与散列表存在时间一样长的存储中
* Note that keyColIdx, eqfunctions, and hashfunctions must be allocated in
* storage that will live as long as the hashtable does.
*/
TupleHashTable BuildTupleHashTable(int numCols, AttrNumber* keyColIdx, FmgrInfo* eqfunctions, FmgrInfo* hashfunctions,
long nbuckets, Size entrysize, MemoryContext tablecxt, MemoryContext tempcxt, int workMem)
@ -243,7 +257,7 @@ TupleHashTable BuildTupleHashTable(int numCols, AttrNumber* keyColIdx, FmgrInfo*
Assert(nbuckets > 0);
Assert(entrysize >= sizeof(TupleHashEntryData));
/* 限制初始表大小请求不超过work_mem */
/* Limit initial table size request to not more than work_mem */
nbuckets = Min(nbuckets, (long)((workMem * 1024L) / entrysize));
if (u_sess->attr.attr_sql.hashagg_table_size != 0)
nbuckets = Min(nbuckets, u_sess->attr.attr_sql.hashagg_table_size);
@ -257,7 +271,7 @@ TupleHashTable BuildTupleHashTable(int numCols, AttrNumber* keyColIdx, FmgrInfo*
hashtable->tablecxt = tablecxt;
hashtable->tempcxt = tempcxt;
hashtable->entrysize = entrysize;
hashtable->tableslot = NULL; /* 将在第一次查找时进行 */
hashtable->tableslot = NULL; /* will be made on first lookup */
hashtable->inputslot = NULL;
hashtable->in_hash_funcs = NULL;
hashtable->cur_eq_funcs = NULL;
@ -279,16 +293,20 @@ TupleHashTable BuildTupleHashTable(int numCols, AttrNumber* keyColIdx, FmgrInfo*
}
/*
*
* Find or create a hashtable entry for the tuple group containing the
* given tuple. The tuple must be the same type as the hashtable entries.
*
* isnew为NULL;NULL
* If isnew is NULL, we do not create new entries; we return NULL if no
* match is found.
*
* isnew不为NULL
* *isnew为true
* false
* If isnew isn't NULL, then a new entry is created if no existing entry
* matches. On return, *isnew is true if the entry is newly created,
* false if it existed already. Any extra space in a new entry has been
* zeroed.
*
* isinserthashtbl为falseHASH_FINDHASH_ENTER
*
* If isinserthashtbl is false, the para of hash search is HASH_FIND
* instead of HASH_ENTER. This slot will be insert into temp file instead of
* hash table if it is new
*
*/
TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot* slot, bool* isnew, bool isinserthashtbl)
@ -299,27 +317,29 @@ TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot* sl
TupleHashEntryData dummy;
bool found = false;
/* 如果第一次通过,克隆输入槽来制作表槽 */
/* If first time through, clone the input slot to make table slot */
if (hashtable->tableslot == NULL) {
TupleDesc tupdesc;
oldContext = MemoryContextSwitchTo(hashtable->tablecxt);
/*
*
* We copy the input tuple descriptor just for safety --- we assume
* all input tuples will have equivalent descriptors.
*/
tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
hashtable->tableslot = MakeSingleTupleTableSlot(tupdesc);
MemoryContextSwitchTo(oldContext);
}
/* 需要在短期上下文中运行哈希函数 */
/* Need to run the hash functions in short-lived context */
oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
/*
*
* Set up data needed by hash and match functions
*
* u_sess-> exec_extCur_tuple_hash_table
* We save and restore u_sess->exec_cxt.cur_tuple_hash_table just in case someone manages to
* invoke this code re-entrantly.
*/
hashtable->inputslot = slot;
hashtable->in_hash_funcs = hashtable->tab_hash_funcs;
@ -328,33 +348,34 @@ TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot* sl
saveCurHT = u_sess->exec_cxt.cur_tuple_hash_table;
u_sess->exec_cxt.cur_tuple_hash_table = hashtable;
/* 搜索哈希表 */
dummy.firstTuple = NULL; /* 引用输入槽的标志 */
/* Search the hash table */
dummy.firstTuple = NULL; /* flag to reference inputslot */
if (isinserthashtbl) {
entry = (TupleHashEntry)hash_search(hashtable->hashtab, &dummy, isnew ? HASH_ENTER : HASH_FIND, &found);
} else {
/* 如果在哈希表中没有找到该槽位,则将其插入临时文件而不是哈希表中 */
/* this slot will be insert into temp file instead of hash table if it is not found in hash table */
entry = (TupleHashEntry)hash_search(hashtable->hashtab, &dummy, HASH_FIND, &found);
}
if (isnew != NULL) {
if (found) {
/* 发现已有条目 */
/* found pre-existing entry */
*isnew = false;
} else {
if (entry) {
Assert(isinserthashtbl);
/*
*
* created new entry
*
*
* (dynahash.c复制到新条目中)
* Zero any caller-requested space in the entry. (This zaps the
* "key data" dynahash.c copied into the new entry, but we don't
* care since we're about to overwrite it anyway.)
*/
errno_t errorno = memset_s(entry, hashtable->entrysize, 0, hashtable->entrysize);
securec_check(errorno, "\0", "\0");
/* 将第一个元组复制到表上下文中 */
/* Copy the first tuple into the table context */
MemoryContextSwitchTo(hashtable->tablecxt);
entry->firstTuple = ExecCopySlotMinimalTuple(slot);
if (hashtable->add_width)
@ -373,9 +394,13 @@ TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot* sl
}
/*
*
* LookupTupleHashEntry的非创建情况
*
* Search for a hashtable entry matching the given tuple. No entry is
* created if there's not a match. This is similar to the non-creating
* case of LookupTupleHashEntry, except that it supports cross-type
* comparisons, in which the given tuple is not of the same type as the
* table entries. The caller must provide the hash functions to use for
* the input tuple, as well as the equality functions, since these may be
* different from the table's internal functions.
*/
TupleHashEntry FindTupleHashEntry(
TupleHashTable hashtable, TupleTableSlot* slot, FmgrInfo* eqfunctions, FmgrInfo* hashfunctions)
@ -385,13 +410,14 @@ TupleHashEntry FindTupleHashEntry(
TupleHashTable saveCurHT;
TupleHashEntryData dummy;
/* 需要在短期上下文中运行哈希函数 */
/* Need to run the hash functions in short-lived context */
oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
/*
*
* Set up data needed by hash and match functions
*
* u_sess-> exec_extCur_tuple_hash_table
* We save and restore u_sess->exec_cxt.cur_tuple_hash_table just in case someone manages to
* invoke this code re-entrantly.
*/
hashtable->inputslot = slot;
hashtable->in_hash_funcs = hashfunctions;
@ -400,8 +426,8 @@ TupleHashEntry FindTupleHashEntry(
saveCurHT = u_sess->exec_cxt.cur_tuple_hash_table;
u_sess->exec_cxt.cur_tuple_hash_table = hashtable;
/* 搜索哈希表 */
dummy.firstTuple = NULL; /* 引用输入槽的标志 */
/* Search the hash table */
dummy.firstTuple = NULL; /* flag to reference inputslot */
entry = (TupleHashEntry)hash_search(hashtable->hashtab, &dummy, HASH_FIND, NULL);
u_sess->exec_cxt.cur_tuple_hash_table = saveCurHT;
@ -412,19 +438,20 @@ TupleHashEntry FindTupleHashEntry(
}
/*
*
* Compute the hash value for a tuple
*
* TupleHashEntryData的指针
* tuple字段指向一个元组(MinimalTuple格式中)
* LookupTupleHashEntry用一个NULL firstTuple字段
*
*
* The passed-in key is a pointer to TupleHashEntryData. In an actual hash
* table entry, the firstTuple field points to a tuple (in MinimalTuple
* format). LookupTupleHashEntry sets up a dummy TupleHashEntryData with a
* NULL firstTuple field --- that cues us to look at the inputslot instead.
* This convention avoids the need to materialize virtual input tuples unless
* they actually need to get copied into the table.
*
* u_sess - > exec_cxtcur_tuple_hash_table必须在调用它之前设置
* dynahash.c没有提供任何让我们以其他方式获取哈希表的API
* u_sess->exec_cxt.cur_tuple_hash_table must be set before calling this, since dynahash.c
* doesn't provide any API that would let us get at the hashtable otherwise.
*
*
* (dynahash.c不会改变CurrentMemoryContext)
* Also, the caller must select an appropriate memory context for running
* the hash functions. (dynahash.c doesn't change CurrentMemoryContext.)
*/
static uint32 TupleHashTableHash(const void* key, Size keysize)
{
@ -438,28 +465,28 @@ static uint32 TupleHashTableHash(const void* key, Size keysize)
int i;
if (tuple == NULL) {
/* 处理表的当前输入元组 */
/* Process the current input tuple for the table */
slot = hashtable->inputslot;
hashfunctions = hashtable->in_hash_funcs;
} else {
/* 处理已经存储在表中的元组 */
/* (这种情况在当前的dynahash.c代码中从未发生过) */
/* Process a tuple already stored in the table */
/* (this case never actually occurs in current dynahash.c code) */
slot = hashtable->tableslot;
ExecStoreMinimalTuple(tuple, slot, false);
hashfunctions = hashtable->tab_hash_funcs;
}
/* 获取表访问器方法*/
/* Get the Table Accessor Method*/
for (i = 0; i < numCols; i++) {
AttrNumber att = keyColIdx[i];
Datum attr;
bool isNull = false;
/* 每一步将哈希键向左旋转1位 */
/* rotate hashkey left 1 bit at each step */
hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0);
attr = tableam_tslot_getattr(slot, att, &isNull);
/* 将空值视为哈希键为0 */
/* treat nulls as having hash key 0 */
if (!isNull) {
uint32 hkey;
hkey = DatumGetUInt32(FunctionCall1(&hashfunctions[i], attr));
@ -473,13 +500,15 @@ static uint32 TupleHashTableHash(const void* key, Size keysize)
}
/*
* ()
* See whether two tuples (presumably of the same hash value) match
*
* TupleHashEntryData的指针
* As above, the passed pointers are pointers to TupleHashEntryData.
*
* u_sess - > exec_cxtcur_tuple_hash_table必须在调用它之前设置dynahash.c没有提供任何让我们以其他方式获取哈希表的API
* u_sess->exec_cxt.cur_tuple_hash_table must be set before calling this, since dynahash.c
* doesn't provide any API that would let us get at the hashtable otherwise.
*
* (dynahash.c不会改变CurrentMemoryContext)
* Also, the caller must select an appropriate memory context for running
* the compare functions. (dynahash.c doesn't change CurrentMemoryContext.)
*/
static int TupleHashTableMatch(const void* key1, const void* key2, Size keysize)
{
@ -493,9 +522,10 @@ static int TupleHashTableMatch(const void* key1, const void* key2, Size keysize)
TupleHashTable hashtable = u_sess->exec_cxt.cur_tuple_hash_table;
/*
* dynahash.c调用我们时
* LookupTupleHashEntry的假TupleHashEntryData
* dynahash.c使用
* We assume that dynahash.c will only ever call us with the first
* argument being an actual table entry, and the second argument being
* LookupTupleHashEntry's dummy TupleHashEntryData. The other direction
* could be supported too, but is not currently used by dynahash.c.
*/
Assert(tuple1 != NULL);
slot1 = hashtable->tableslot;
@ -503,7 +533,7 @@ static int TupleHashTableMatch(const void* key1, const void* key2, Size keysize)
Assert(tuple2 == NULL);
slot2 = hashtable->inputslot;
/* 对于交叉类型比较,输入槽必须是第一个 */
/* For crosstype comparisons, the inputslot must be first */
if (execTuplesMatch(
slot2, slot1, hashtable->numCols, hashtable->keyColIdx, hashtable->cur_eq_funcs, hashtable->tempcxt))
return 0;

View File

@ -1,12 +1,14 @@
/* -------------------------------------------------------------------------
* execJunk.cpp
* ...
*
* (c) 2020
* (c) 1996-2012PostgreSQL全球开发团队
* (c) 1994
* execJunk.cpp
* Junk attribute support stuff....
*
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/gausskernel/runtime/executor/execJunk.cpp
*
* -------------------------------------------------------------------------
@ -19,54 +21,84 @@
#include "pgxc/pgxc.h"
/* -------------------------------------------------------------------------
* XXX ExecProject() ProjectionInfo
* -cim 6/3/91
* "垃圾" "垃圾"
* "ctid"
* TargetEntry
* TargetEntry 'resjunk' true "垃圾"
* ExecInitJunkFilter
* resjunk
* ExecFindJunkAttribute/ExecGetJunkAttribute来检索我们感兴趣的垃圾属性的值 ExecFilterJunk
* "干净"
* XXX this stuff should be rewritten to take advantage
* of ExecProject() and the ProjectionInfo node.
* -cim 6/3/91
*
* An attribute of a tuple living inside the executor, can be
* either a normal attribute or a "junk" attribute. "junk" attributes
* never make it out of the executor, i.e. they are never printed,
* returned or stored on disk. Their only purpose in life is to
* store some information useful only to the executor, mainly the values
* of system attributes like "ctid", or sort key columns that are not to
* be output.
*
* The general idea is the following: A target list consists of a list of
* TargetEntry nodes containing expressions. Each TargetEntry has a field
* called 'resjunk'. If the value of this field is true then the
* corresponding attribute is a "junk" attribute.
*
* When we initialize a plan we call ExecInitJunkFilter to create a filter.
*
* We then execute the plan, treating the resjunk attributes like any others.
*
* Finally, when at the top level we get back a tuple, we can call
* ExecFindJunkAttribute/ExecGetJunkAttribute to retrieve the values of the
* junk attributes we are interested in, and ExecFilterJunk to remove all the
* junk attributes from a tuple. This new "clean" tuple is then printed,
* inserted, or updated.
*
* -------------------------------------------------------------------------
*/
/*
* ExecInitJunkFilter
* OID
* tlist
* OID
* resultSlot
*
* Initialize the Junk filter.
*
* The source targetlist is passed in. The output tuple descriptor is
* built from the non-junk tlist entries, plus the passed specification
* of whether to include room for an OID or not.
* An optional resultSlot can be passed as well.
*/
JunkFilter* ExecInitJunkFilter(List* targetList, bool hasoid, TupleTableSlot* slot, TableAmType tam)
{
JunkFilter* junkfilter = NULL;
TupleDesc cleanTupType;
int cleanLength;
AttrNumber* cleanMap = NULL;
ListCell* t = NULL;
AttrNumber cleanResno;
JunkFilter* junkfilter = NULL;
TupleDesc cleanTupType;
int cleanLength;
AttrNumber* cleanMap = NULL;
ListCell* t = NULL;
AttrNumber cleanResno;
// 计算清理后的元组描述符
cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, tam);
/*
* Compute the tuple descriptor for the cleaned tuple.
*/
cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, tam);
// 设置槽的描述符,如果给定了槽,则使用给定的槽,否则创建一个新槽
/*
* Use the given slot, or make a new slot if we weren't given one.
*/
if (slot != NULL)
ExecSetSlotDescriptor(slot, cleanTupType);
else
slot = MakeSingleTupleTableSlot(cleanTupType);
cleanLength = cleanTupType->natts;
/*
* Now calculate the mapping between the original tuple's attributes and
* the "clean" tuple's attributes.
*
* The "map" is an array of "cleanLength" attribute numbers, i.e. one
* entry for every attribute of the "clean" tuple. The value of this entry
* is the attribute number of the corresponding attribute of the
* "original" tuple. (Zero indicates a NULL output attribute, but we do
* not use that feature in this routine.)
*/
cleanLength = cleanTupType->natts;
if (cleanLength > 0) {
cleanMap = (AttrNumber*)palloc(cleanLength * sizeof(AttrNumber));
cleanMap = (AttrNumber*)palloc(cleanLength * sizeof(AttrNumber));
cleanResno = 1;
foreach (t, targetList) {
TargetEntry* tle = (TargetEntry*)lfirst(t);
// 如果不是 "junk" 属性,则建立属性映射关系
if (!tle->resjunk) {
cleanMap[cleanResno - 1] = tle->resno;
cleanResno++;
@ -76,10 +108,11 @@ JunkFilter* ExecInitJunkFilter(List* targetList, bool hasoid, TupleTableSlot* sl
cleanMap = NULL;
}
// 创建并初始化 JunkFilter 结构
/*
* Finally create and initialize the JunkFilter struct.
*/
junkfilter = makeNode(JunkFilter);
// 填充 JunkFilter 结构的字段
junkfilter->jf_targetList = targetList;
junkfilter->jf_cleanTupType = cleanTupType;
junkfilter->jf_cleanMap = cleanMap;
@ -91,64 +124,78 @@ JunkFilter* ExecInitJunkFilter(List* targetList, bool hasoid, TupleTableSlot* sl
/*
* ExecInitJunkFilterConversion
*
*
*
*
* Initialize a JunkFilter for rowtype conversions.
*
* Here, we are given the target "clean" tuple descriptor rather than
* inferring it from the targetlist. The target descriptor can contain
* deleted columns. It is assumed that the caller has checked that the
* non-deleted columns match up with the non-junk columns of the targetlist.
*/
JunkFilter* ExecInitJunkFilterConversion(List* targetList, TupleDesc cleanTupType, TupleTableSlot* slot)
{
JunkFilter* junkfilter = NULL;
int cleanLength;
AttrNumber* cleanMap = NULL;
ListCell* t = NULL;
int i;
JunkFilter* junkfilter = NULL;
int cleanLength;
AttrNumber* cleanMap = NULL;
ListCell* t = NULL;
int i;
// 检查是否给定了槽,如果给定则使用,否则创建一个新的槽
/*
* Use the given slot, or make a new slot if we weren't given one.
*/
if (slot != NULL)
ExecSetSlotDescriptor(slot, cleanTupType);
ExecSetSlotDescriptor(slot, cleanTupType);
else
slot = MakeSingleTupleTableSlot(cleanTupType);
slot = MakeSingleTupleTableSlot(cleanTupType);
cleanLength = cleanTupType->natts;
// 为属性映射数组分配内存,并初始化为 0
/*
* Calculate the mapping between the original tuple's attributes and the
* "clean" tuple's attributes.
*
* The "map" is an array of "cleanLength" attribute numbers, i.e. one
* entry for every attribute of the "clean" tuple. The value of this entry
* is the attribute number of the corresponding attribute of the
* "original" tuple. We store zero for any deleted attributes, marking
* that a NULL is needed in the output tuple.
*/
cleanLength = cleanTupType->natts;
if (cleanLength > 0) {
cleanMap = (AttrNumber*)palloc0(cleanLength * sizeof(AttrNumber));
t = list_head(targetList);
for (i = 0; i < cleanLength; i++) {
cleanMap = (AttrNumber*)palloc0(cleanLength * sizeof(AttrNumber));
t = list_head(targetList);
for (i = 0; i < cleanLength; i++) {
if (cleanTupType->attrs[i]->attisdropped)
continue; // 跳过已删除的属性
continue; /* map entry is already zero */
for (;;) {
TargetEntry* tle = (TargetEntry*)lfirst(t);
TargetEntry* tle = (TargetEntry*)lfirst(t);
t = lnext(t);
t = lnext(t);
if (!tle->resjunk) {
cleanMap[i] = tle->resno;
cleanMap[i] = tle->resno;
break;
}
}
}
} else {
cleanMap = NULL;
cleanMap = NULL;
}
// 创建并初始化 JunkFilter 结构
junkfilter = makeNode(JunkFilter);
/*
* Finally create and initialize the JunkFilter struct.
*/
junkfilter = makeNode(JunkFilter);
// 填充 JunkFilter 结构的各个字段
junkfilter->jf_targetList = targetList;
junkfilter->jf_cleanTupType = cleanTupType;
junkfilter->jf_cleanMap = cleanMap;
junkfilter->jf_resultSlot = slot;
junkfilter->jf_targetList = targetList;
junkfilter->jf_cleanTupType = cleanTupType;
junkfilter->jf_cleanMap = cleanMap;
junkfilter->jf_resultSlot = slot;
return junkfilter;
return junkfilter;
}
/*
* ExecFindJunkAttribute
*
* resno
* InvalidAttrNumber
* Locate the specified junk attribute in the junk filter's targetlist,
* and return its resno. Returns InvalidAttrNumber if not found.
*/
AttrNumber ExecFindJunkAttribute(JunkFilter* junkfilter, const char* attrName)
{
@ -158,7 +205,8 @@ AttrNumber ExecFindJunkAttribute(JunkFilter* junkfilter, const char* attrName)
/*
* ExecFindJunkPrimaryKeys
*
* xc_primary_key
* Locate the specified junk attribute in the junk filter's targetlist.
* Returns NIL if not found.
*/
List* ExecFindJunkPrimaryKeys(List* targetlist)
{
@ -180,7 +228,8 @@ List* ExecFindJunkPrimaryKeys(List* targetlist)
/*
* ExecFindJunkAttributeInTlist
*
*
* Find a junk attribute given a subplan's targetlist (not necessarily
* part of a JunkFilter).
*/
AttrNumber ExecFindJunkAttributeInTlist(List* targetlist, const char* attrName)
{
@ -201,7 +250,9 @@ AttrNumber ExecFindJunkAttributeInTlist(List* targetlist, const char* attrName)
/*
* ExecGetJunkAttribute
*
*
* Given a junk filter's input tuple (slot) and a junk attribute's number
* previously found by ExecFindJunkAttribute, extract & return the value and
* isNull flag of the attribute.
*/
Datum ExecGetJunkAttribute(TupleTableSlot* slot, AttrNumber attno, bool* isNull)
{
@ -214,7 +265,7 @@ Datum ExecGetJunkAttribute(TupleTableSlot* slot, AttrNumber attno, bool* isNull)
/*
* ExecFilterJunk
*
*
* Construct and return a slot with all the junk attributes removed.
*/
TupleTableSlot* ExecFilterJunk(JunkFilter* junkfilter, TupleTableSlot* slot)
{
@ -228,24 +279,34 @@ TupleTableSlot* ExecFilterJunk(JunkFilter* junkfilter, TupleTableSlot* slot)
Datum* old_values = NULL;
bool* old_isnull = NULL;
// 从原始元组中提取所有属性值
/*
* Extract all the values of the old tuple.
*/
/* Get the Table Accessor Method*/
Assert(slot != NULL && slot->tts_tupleDescriptor != NULL);
tableam_tslot_getallattrs(slot);
old_values = slot->tts_values;
old_isnull = slot->tts_isnull;
// 获取 JunkFilter 中的信息
/*
* get info from the junk filter
*/
cleanTupType = junkfilter->jf_cleanTupType;
cleanLength = cleanTupType->natts;
cleanMap = junkfilter->jf_cleanMap;
resultSlot = junkfilter->jf_resultSlot;
// 准备构建虚拟结果元组
/*
* Prepare to build a virtual result tuple.
*/
(void)ExecClearTuple(resultSlot);
values = resultSlot->tts_values;
isnull = resultSlot->tts_isnull;
// 转置数据到新元组的适当字段中
/*
* Transpose data into proper fields of the new tuple.
*/
for (i = 0; i < cleanLength; i++) {
int j = cleanMap[i];
@ -258,102 +319,95 @@ TupleTableSlot* ExecFilterJunk(JunkFilter* junkfilter, TupleTableSlot* slot)
}
}
// 返回过滤后的虚拟元组
/*
* And return the virtual tuple.
*/
return ExecStoreVirtualTuple(resultSlot);
}
/*
* BatchExecFilterJunk
*
*
* Construct and return a vector batch with all the junk attributes removed.
*/
VectorBatch* BatchExecFilterJunk(_in_ JunkFilter* junkfilter, __inout VectorBatch* batch)
{
AttrNumber* cleanMap = NULL; // 属性映射数组,将清理后的属性编号映射到原始属性编号
TupleDesc cleanTupType; // 清理后元组的描述符
int cleanLength; // 清理后元组的属性数量
int i; // 循环计数变量
ScalarVector* columns = NULL; // 存储列向量的数组
AttrNumber* cleanMap = NULL;
TupleDesc cleanTupType;
int cleanLength;
int i;
ScalarVector* columns = NULL;
// 获取 JunkFilter 中的信息
// Get info from the junk filter
//
cleanTupType = junkfilter->jf_cleanTupType;
cleanLength = cleanTupType->natts;
cleanMap = junkfilter->jf_cleanMap;
columns = batch->m_arr; // 获取列向量的数组
columns = batch->m_arr;
// 转置数据到新元组的适当字段中
// Transpose data into proper fields of the new tuple.
//
for (i = 0; i < cleanLength; i++) {
int j = cleanMap[i];
if (j == 0) {
for (int k = 0; k < columns[i].m_rows; k++) {
columns[i].SetNull(k); // 将该列向量的元素设置为 NULL
columns[i].SetNull(k);
}
} else {
columns[i] = columns[j - 1]; // 将原始属性的列向量复制到新属性列向量
columns[i] = columns[j - 1];
}
}
// 返回修改后的批处理数据,列数不变
// Return the modified batch without changing the column count
// as the column count is early decided at compile time.
//
return batch;
}
/*
*ExecSetjunkFilteDescriptor
*
*
* TupleDesc JunkFilter 便使
*/
void ExecSetjunkFilteDescriptor(JunkFilter* junkfilter, TupleDesc tupdesc)
{
TupleDesc resultslotTupType; // 结果槽的元组描述符
AttrNumber* cleanMap = NULL; // 属性映射数组,将清理后的属性编号映射到原始属性编号
int cleanLength; // 清理后元组的属性数量
int i; // 循环计数变量
TupleDesc resultslotTupType;
AttrNumber* cleanMap = NULL;
int cleanLength;
int i;
cleanLength = junkfilter->jf_cleanTupType->natts; // 获取清理后元组的属性数量
cleanMap = junkfilter->jf_cleanMap; // 获取属性映射数组
cleanLength = junkfilter->jf_cleanTupType->natts;
cleanMap = junkfilter->jf_cleanMap;
resultslotTupType = junkfilter->jf_resultSlot->tts_tupleDescriptor; // 获取结果槽的元组描述符
resultslotTupType = junkfilter->jf_resultSlot->tts_tupleDescriptor;
/*
* tupdesc
* Transpose tupdesc into proper fields of the new tupdesc.
*/
for (i = 0; i < cleanLength; i++) {
int j = cleanMap[i];
// 如果属性映射不为 0则将 tupdesc 的属性类型赋值给结果槽的元组描述符
if (j > 0)
resultslotTupType->attrs[i]->atttypid = tupdesc->attrs[j - 1]->atttypid;
}
}
/*BatchCheckNodeIdentifier
*
* `xc_node_id`
/*
* @Description: Check if junk attribute xc_node_id is the same as current node identifier
*
* @param[IN] junkfilter: junk attributes
* @param[IN] batch: vector batch
* @return: void
*/
void BatchCheckNodeIdentifier(JunkFilter* junkfilter, VectorBatch* batch)
{
ScalarVector* xc_node_id_col = NULL; // 用于存储 xc_node_id 的列向量
uint32 xc_node_id = 0; // 存储当前 xc_node_id
int counter = 0; // 循环计数变量
ScalarVector* xc_node_id_col = NULL;
uint32 xc_node_id = 0;
int counter = 0;
// 如果 xc_node_id 无效,则直接返回
if (InvalidAttrNumber == junkfilter->jf_xc_node_id) {
return;
}
// 获取 xc_node_id 列向量
xc_node_id_col = &(batch->m_arr[junkfilter->jf_xc_node_id - 1]);
// 遍历 xc_node_id 列向量中的值
for (counter = 0; counter < xc_node_id_col->m_rows; counter++) {
// 获取当前 xc_node_id 的值
xc_node_id = DatumGetUInt32(xc_node_id_col->m_vals[counter]);
// 检查当前 xc_node_id 是否与当前节点的标识不匹配,如果不匹配则抛出错误
if (u_sess->pgxc_cxt.PGXCNodeIdentifier != xc_node_id) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@ -364,4 +418,3 @@ void BatchCheckNodeIdentifier(JunkFilter* junkfilter, VectorBatch* batch)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,22 @@
/*
* (c) 2020
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss Mulan PSL v2
* Mulan PSL v2 使
* Mulan PSL v2
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* "原样"
*
* Mulan PSL v2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* execMerge.cpp
* MERGE Merge
* routines to handle Merge nodes relating to the MERGE command
*
*
* IDENTIFICATION
* src/gausskernel/runtime/executor/execMerge.cpp
*
* -------------------------------------------------------------------------
@ -38,13 +39,11 @@ static void ExecMergeNotMatched(ModifyTableState* mtstate, EState* estate, Tuple
static bool ExecMergeMatched(ModifyTableState* mtstate, EState* estate, TupleTableSlot* slot, JunkFilter* junkfilter,
ItemPointer tupleid, HeapTupleHeader oldtuple, Oid oldPartitionOid, int2 bucketid);
/*
* MERGE
* Perform MERGE.
*/
void ExecMerge(ModifyTableState* mtstate, EState* estate, TupleTableSlot* slot, JunkFilter* junkfilter,
ResultRelInfo* resultRelInfo)
{
// 获取执行上下文
ExprContext* econtext = mtstate->ps.ps_ExprContext;
ItemPointer tupleid;
ItemPointerData tuple_ctid;
@ -57,26 +56,34 @@ void ExecMerge(ModifyTableState* mtstate, EState* estate, TupleTableSlot* slot,
AttrNumber bucketIdNum;
int2 bucketid = InvalidBktId;
// 检查结果关系类型和垃圾过滤器
Assert(resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION ||
resultRelInfo->ri_RelationDesc->rd_rel->relkind == PARTTYPE_PARTITIONED_RELATION ||
junkfilter != NULL);
resultRelInfo->ri_RelationDesc->rd_rel->relkind == PARTTYPE_PARTITIONED_RELATION ||
junkfilter != NULL);
/*
*
* Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous cycle.
*/
ResetExprContext(econtext);
// 从槽中提取关于匹配情况的信息
/*
* We run a JOIN between the target relation and the source relation to
* find a set of candidate source rows that has matching row in the target
* table and a set of candidate source rows that does not have matching
* row in the target table. If the join returns us a tuple with target
* relation's tid set, that implies that the join found a matching row for
* the given source tuple. This case triggers the WHEN MATCHED clause of
* the MERGE. Whereas a NULL in the target relation's ctid column
* indicates a NOT MATCHED case.
*/
datum = ExecGetJunkAttribute(slot, junkfilter->jf_junkAttNo, &isNull);
if (!isNull) {
matched = true;
tupleid = (ItemPointer)DatumGetPointer(datum);
tuple_ctid = *tupleid;/* 确保我们不释放 ctid */
tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
tupleid = &tuple_ctid;
// 处理分区表和分桶表的情况
if (RELATION_IS_PARTITIONED(resultRelInfo->ri_RelationDesc) ||
RelationIsCUFormat(resultRelInfo->ri_RelationDesc)) {
Datum tableOiddatum;
@ -107,54 +114,67 @@ void ExecMerge(ModifyTableState* mtstate, EState* estate, TupleTableSlot* slot,
bucketid = DatumGetObjectId(bucketIddatum);
}
}
else {
} else {
matched = false;
tupleid = NULL; /* 对于 INSERT 操作,不需要这个信息 */
tupleid = NULL; /* we don't need it for INSERT actions */
}
/*
* WHEN MATCHED WHEN MATCHED AND
*
*
* WHEN NOT MATCHED WHEN NOT MATCHED
*
* WHEN MATCHED /
*
*
*
* 1. 使 WHEN MATCHED
*
* WHEN MATCHED WHEN MATCHED
*
* 2. 使
*
* WHEN NOT MATCHED
*
* WHEN MATCHED WHEN NOT MATCHED
*
* ExecMergeMatched WHEN MATCHED WHEN MATCHED
* ExecMergeNotMatched ExecMergeMatched
* ExecMergeNotMatched ExecMergeMatched
*/
// 根据匹配情况执行相应的动作
/*
* If we are dealing with a WHEN MATCHED case, we execute the first action
* for which the additional WHEN MATCHED AND quals pass. If an action
* without quals is found, that action is executed.
*
* Similarly, if we are dealing with WHEN NOT MATCHED case, we look at the
* given WHEN NOT MATCHED actions in sequence until one passes.
*
* Things get interesting in case of concurrent update/delete of the
* target tuple. Such concurrent update/delete is detected while we are
* executing a WHEN MATCHED action.
*
* A concurrent update can:
*
* 1. modify the target tuple so that it no longer satisfies the
* additional quals attached to the current WHEN MATCHED action OR
*
* In this case, we are still dealing with a WHEN MATCHED case, but
* we should recheck the list of WHEN MATCHED actions and choose the first
* one that satisfies the new target tuple.
*
* 2. modify the target tuple so that the join quals no longer pass and
* hence the source tuple no longer has a match.
*
* In the second case, the source tuple no longer matches the target tuple,
* so we now instead find a qualifying WHEN NOT MATCHED action to execute.
*
* A concurrent delete, changes a WHEN MATCHED case to WHEN NOT MATCHED.
*
* ExecMergeMatched takes care of following the update chain and
* re-finding the qualifying WHEN MATCHED action, as long as the updated
* target tuple still satisfies the join quals i.e. it still remains a
* WHEN MATCHED case. If the tuple gets deleted or the join quals fail, it
* returns and we try ExecMergeNotMatched. Given that ExecMergeMatched
* always make progress by following the update chain and we never switch
* from ExecMergeNotMatched to ExecMergeMatched, there is no risk of a
* livelock.
*/
if (matched)
matched = ExecMergeMatched(mtstate, estate, slot, junkfilter, tupleid, oldtuple, oldPartitionOid, bucketid);
// 如果没有匹配的情况,执行相应的 NOT MATCHED 操作
/*
* Either we were dealing with a NOT MATCHED tuple or ExecMergeNotMatched()
* returned "false", indicating the previously MATCHED tuple is no longer a
* matching tuple.
*/
if (!matched)
ExecMergeNotMatched(mtstate, estate, slot);
}
/*
*
* Extract tuple for checking constraints from plan slot
*/
static TupleTableSlot* ExtractConstraintTuple(
ModifyTableState* mtstate, CmdType commandType, TupleTableSlot* slot, TupleDesc tupDesc)
{
// 获取执行上下文
ExprContext* econtext = mtstate->ps.ps_ExprContext;
AutoContextSwitch memContext(econtext->ecxt_per_tuple_memory);
HeapTuple tempTuple = NULL;
@ -165,47 +185,40 @@ static TupleTableSlot* ExtractConstraintTuple(
int index = 0;
int i = 0;
// 根据命令类型提取约束元组的槽
switch (commandType) {
case CMD_UPDATE:
constrSlot = mtstate->mt_update_constr_slot;
for (i = 0; i < originTupleDesc->natts; i++) {
// 查找符合条件的属性并复制值和空标志
if (strstr(originTupleDesc->attrs[i]->attname.data, "action UPDATE target")) {
values[index] = slot->tts_values[i];
isnull[index] = slot->tts_isnull[i];
index++;
case CMD_UPDATE:
constrSlot = mtstate->mt_update_constr_slot;
for (i = 0; i < originTupleDesc->natts; i++) {
if (strstr(originTupleDesc->attrs[i]->attname.data, "action UPDATE target")) {
values[index] = slot->tts_values[i];
isnull[index] = slot->tts_isnull[i];
index++;
}
}
}
break;
case CMD_INSERT:
constrSlot = mtstate->mt_insert_constr_slot;
for (i = 0; i < originTupleDesc->natts; i++) {
// 查找符合条件的属性并复制值和空标志
if (strstr(originTupleDesc->attrs[i]->attname.data, "action INSERT target")) {
values[index] = slot->tts_values[i];
isnull[index] = slot->tts_isnull[i];
index++;
break;
case CMD_INSERT:
constrSlot = mtstate->mt_insert_constr_slot;
for (i = 0; i < originTupleDesc->natts; i++) {
if (strstr(originTupleDesc->attrs[i]->attname.data, "action INSERT target")) {
values[index] = slot->tts_values[i];
isnull[index] = slot->tts_isnull[i];
index++;
}
}
}
break;
default:
Assert(0);
break;
default:
Assert(0);
}
// 确保约束槽的表访问方法类型与原始元组描述一致
Assert(constrSlot->tts_tupleDescriptor->tdTableAmType == originTupleDesc->tdTableAmType);
// 使用 values 和 isnull 数组创建临时 HeapTuple并将其存储到约束槽中
tempTuple = (HeapTuple)tableam_tops_form_tuple(tupDesc, values, isnull, HEAP_TUPLE);
(void)ExecStoreTuple(tempTuple, constrSlot, InvalidBuffer, false);
return constrSlot;
}
/*
*
* Extract scan tuple for target table from plan slot
*/
TupleTableSlot* ExtractScanTuple(ModifyTableState* mtstate, TupleTableSlot* slot, TupleDesc tupDesc)
{
@ -221,18 +234,18 @@ TupleTableSlot* ExtractScanTuple(ModifyTableState* mtstate, TupleTableSlot* slot
int index = 0;
/*
* sourceTargetList
* sourceTargetList sourceTargetList
* resno sourceTargetList
* Find the right start index for target table. We should skip the sourceTargetList.
* First count the number of source targetlist. We add new columns to sourceTargetList
* but the resno is not continuous, so find the max continuous number to be the original
* length of sourceTargetList.
*/
foreach(lc, sourceTargetList) {
foreach (lc, sourceTargetList) {
TargetEntry* tle = (TargetEntry*)lfirst(lc);
if (tle->resno != startIdx + 1)
break;
startIdx++;
}
// 从原始槽中提取值和空标志,并构建一个临时 HeapTuple
for (index = 0; index < tupDesc->natts; index++) {
if (tupDesc->attrs[index]->attisdropped == true) {
isnull[index] = true;
@ -244,7 +257,6 @@ TupleTableSlot* ExtractScanTuple(ModifyTableState* mtstate, TupleTableSlot* slot
startIdx++;
}
// 使用 values 和 isnull 数组创建临时 HeapTuple并将其存储到扫描槽中
tempTuple = (HeapTuple)tableam_tops_form_tuple(tupDesc, values, isnull, HEAP_TUPLE);
(void)ExecStoreTuple(tempTuple, scanSlot, InvalidBuffer, false);
@ -252,15 +264,15 @@ TupleTableSlot* ExtractScanTuple(ModifyTableState* mtstate, TupleTableSlot* slot
}
/*
*
*
* @in mtstatemodifytable
* @in mergeMatchedActionStates
* @in econtext
* @in originSlot
* @in result_slot
* @in estate
*
* Description: projects and evaluates qual condition for update action.
* Parameters:
* @in mtstate: modifytable state.
* @in mergeMatchedActionStates: update action states.
* @in econtext: expression context.
* @in originSlot: slot to be projected.
* @in result_slot: slot to be returned.
* @in estate: working state for executor.
* Return: slot has been projected..
*/
TupleTableSlot* ExecMergeProjQual(ModifyTableState* mtstate, List* mergeMatchedActionStates, ExprContext* econtext,
TupleTableSlot* originSlot, TupleTableSlot* result_slot, EState* estate)
@ -273,51 +285,55 @@ TupleTableSlot* ExecMergeProjQual(ModifyTableState* mtstate, List* mergeMatchedA
Assert(CMD_UPDATE == action->commandType);
/*
*
*/
* get information on the (current) result relation
*/
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/*
* 使 ExecQual ExecProject
* scantuple
* UPDATE/DELETE
*/
* Make tuple and any needed join variables available to ExecQual and
* ExecProject. The target's existing tuple is installed in the scantuple.
* Again, this target relation's slot is required only in the case of a
* MATCHED tuple and UPDATE/DELETE actions.
*/
if (estate->es_result_update_remoterel == NULL) {
econtext->ecxt_scantuple = ExtractScanTuple(mtstate, originSlot, action->tupDesc);
econtext->ecxt_innertuple = originSlot;
econtext->ecxt_outertuple = NULL;
}
else {
} else {
econtext->ecxt_scantuple = originSlot;
econtext->ecxt_innertuple = NULL;
econtext->ecxt_outertuple = NULL;
}
/*
*
*
*
* ExecQual() true
*/
* Test condition, if any
*
* In the absence of a condition we perform the action unconditionally
* (no need to check separately since ExecQual() will return true if
* there are no conditions to evaluate).
*/
if (ExecQual((List*)action->whenqual, econtext, false)) {
if (estate->es_result_update_remoterel == NULL) {
/*
* ExecUpdate
*/
* We set up the projection earlier, so all we do here is
* Project, no need for any other tasks prior to the
* ExecUpdate.
*/
result_slot = ExecProject(action->proj, NULL);
}
else {
/* 在远程查询中我们不进行投影操作 */
} else {
/* we don't do projection in remote query */
}
/*
* ExecFilterJunk()使 UPDATE
* We don't call ExecFilterJunk() because the projected tuple
* using the UPDATE action's targetlist doesn't have a junk
* attribute.
*/
if (estate->es_result_update_remoterel) {
estate->es_result_remoterel = estate->es_result_update_remoterel;
/* 检查是否有约束条件 */
/* Check if has constraints */
if (resultRelationDesc->rd_att->constr) {
mtstate->mt_update_constr_slot =
ExtractConstraintTuple(mtstate, CMD_UPDATE, result_slot, action->tupDesc);
@ -331,20 +347,25 @@ TupleTableSlot* ExecMergeProjQual(ModifyTableState* mtstate, List* mergeMatchedA
}
/*
* MATCHED tupleid
* Check and execute the first qualifying MATCHED action. The current target
* tuple is identified by tupleid.
*
* WHEN MATCHED WHEN AND
* WHEN AND
* true
* We start from the first WHEN MATCHED action and check if the WHEN AND quals
* pass, if any. If the WHEN AND quals for the first action do not pass, we
* check the second, then the third and so on. If we reach to the end, no
* action is taken and we return true, indicating that no further action is
* required for this tuple.
*
*
* If we do find a qualifying action, then we attempt to execute the action.
*
* 使 EvalPlanQual
* MERGE EvalPlanQual
* EvalPlanQual
* false NOT MATCHED
* If the tuple is concurrently updated, EvalPlanQual is run with the updated
* tuple to recheck the join quals. Note that the additional quals associated
* with individual actions are evaluated separately by the MERGE code, while
* EvalPlanQual checks for the join quals. If EvalPlanQual tells us that the
* updated tuple still passes the join quals, then we restart from the first
* action to look for a qualifying action. Otherwise, we return false meaning
* that a NOT MATCHED action must now be executed for the current source tuple.
*/
static bool ExecMergeMatched(ModifyTableState* mtstate, EState* estate, TupleTableSlot* slot, JunkFilter* junkfilter,
ItemPointer tupleid, HeapTupleHeader oldtuple, Oid oldPartitionOid, int2 bucketid)
{
@ -357,13 +378,13 @@ static bool ExecMergeMatched(ModifyTableState* mtstate, EState* estate, TupleTab
bool partKeyUpdated = ((ModifyTable*)mtstate->ps.plan)->partKeyUpdated;
/*
*
* Save the current information and work with the correct result relation.
*/
saved_resultRelInfo = resultRelInfo;
estate->es_result_relation_info = resultRelInfo;
/*
*
* And get the correct action lists.
*/
mergeMatchedActionStates = resultRelInfo->ri_mergeState->matchedActionStates;
@ -374,33 +395,34 @@ static bool ExecMergeMatched(ModifyTableState* mtstate, EState* estate, TupleTab
if (slot != NULL) {
(void)ExecUpdate(tupleid,
oldPartitionOid,
bucketid,
oldtuple,
slot,
saved_slot,
epqstate,
mtstate,
mtstate->canSetTag,
partKeyUpdated);
oldPartitionOid,
bucketid,
oldtuple,
slot,
saved_slot,
epqstate,
mtstate,
mtstate->canSetTag,
partKeyUpdated);
}
if (action->commandType == CMD_UPDATE /* && tuple_updated*/)
InstrCountFiltered2(&mtstate->ps, 1);
/*
* WHEN
*/
* We've activated one of the WHEN clauses, so we don't search
* further. This is required behaviour, not an optimization.
*/
estate->es_result_relation_info = saved_resultRelInfo;
}
/*
*
* Successfully executed an action or no qualifying action was found.
*/
return true;
}
/*
* NOT MATCHED
* Execute the first qualifying NOT MATCHED action.
*/
static void ExecMergeNotMatched(ModifyTableState* mtstate, EState* estate, TupleTableSlot* slot)
{
@ -411,26 +433,31 @@ static void ExecMergeNotMatched(ModifyTableState* mtstate, EState* estate, Tuple
const int hi_options = 0;
/*
* NOT MATCHED MERGE使
*
* We are dealing with NOT MATCHED tuple. Since for MERGE, the partition
* tree is not expanded for the result relation, we continue to work with
* the currently active result relation, which corresponds to the root
* of the partition tree.
*/
resultRelInfo = mtstate->resultRelInfo;
/*
* INSERT INSERT WHEN
* 使
* For INSERT actions, root relation's merge action is OK since the
* INSERT's targetlist and the WHEN conditions can only refer to the
* source relation and hence it does not matter which result relation we
* work with.
*/
mergeNotMatchedActionStates = resultRelInfo->ri_mergeState->notMatchedActionStates;
/*
* 使 ExecQual ExecProject WHEN
* Make source tuple available to ExecQual and ExecProject. We don't need
* the target tuple since the WHEN quals and the targetlist can't refer to
* the target columns.
*/
if (estate->es_result_insert_remoterel == NULL) {
econtext->ecxt_scantuple = slot;
econtext->ecxt_innertuple = slot;
econtext->ecxt_outertuple = NULL;
}
else {
} else {
econtext->ecxt_scantuple = slot;
econtext->ecxt_innertuple = NULL;
econtext->ecxt_outertuple = NULL;
@ -444,33 +471,36 @@ static void ExecMergeNotMatched(ModifyTableState* mtstate, EState* estate, Tuple
Assert(CMD_INSERT == action->commandType);
/*
*
* get information on the (current) result relation
*/
resultRelationInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelationInfo->ri_RelationDesc;
/*
*
*
*
* ExecQual() true
*/
* Test condition, if any
*
* In the absence of a condition we perform the action unconditionally
* (no need to check separately since ExecQual() will return true if
* there are no conditions to evaluate).
*/
if (ExecQual((List*)action->whenqual, econtext, false)) {
/*
* ExecInsert
* We set up the projection earlier, so all we do here is
* Project, no need for any other tasks prior to the
* ExecInsert.
*/
if (estate->es_result_insert_remoterel == NULL) {
ExecProject(action->proj, NULL);
/*
* ExecPrepareTupleRouting action->slot
* ExecPrepareTupleRouting may modify the passed-in slot. Hence
* pass a local reference so that action->slot is not modified.
*/
myslot = mtstate->mt_mergeproj;
}
else {
/* 在 pgxc 中,我们在远程查询中进行投影操作 */
} else {
/* in pgxc we do projection in the remote query*/
myslot = slot;
/* 检查是否有约束条件 */
/* Check if has constraints */
if (resultRelationDesc->rd_att->constr) {
mtstate->mt_insert_constr_slot = ExtractConstraintTuple(mtstate, CMD_INSERT, slot, action->tupDesc);
}
@ -486,7 +516,7 @@ static void ExecMergeNotMatched(ModifyTableState* mtstate, EState* estate, Tuple
}
/*
* Merge
* Creates the run-time state information for the Merge node
*/
void ExecInitMerge(ModifyTableState* mtstate, EState* estate, ResultRelInfo* resultRelInfo)
{
@ -497,33 +527,32 @@ void ExecInitMerge(ModifyTableState* mtstate, EState* estate, ResultRelInfo* res
TupleDesc relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
ModifyTable* node = (ModifyTable*)mtstate->ps.plan;
// 如果 mergeActionList 为空,则直接返回
if (node->mergeActionList == NIL)
return;
mtstate->mt_merge_subcommands = 0;
// 分配表达式上下文,如果不存在的话
if (mtstate->ps.ps_ExprContext == NULL)
ExecAssignExprContext(estate, &mtstate->ps);
econtext = mtstate->ps.ps_ExprContext;
// 初始化扫描槽和约束槽
/* initialize scan slot and constraint slot */
mtstate->mt_scan_slot = NULL;
mtstate->mt_update_constr_slot = NULL;
mtstate->mt_insert_constr_slot = NULL;
// 初始化用于合并操作的投影槽
/* initialize slot for merge actions */
Assert(mtstate->mt_mergeproj == NULL);
mtstate->mt_mergeproj = ExecInitExtraTupleSlot(mtstate->ps.state);
ExecSetSlotDescriptor(mtstate->mt_mergeproj, relationDesc);
/*
* mergeActionList MergeActionState
*
* Create a MergeActionState for each action on the mergeActionList
* and add it to either a list of matched actions or not-matched
* actions.
*/
foreach(l, node->mergeActionList) {
foreach (l, node->mergeActionList) {
MergeAction* action = (MergeAction*)lfirst(l);
MergeActionState* action_state = makeNode(MergeActionState);
TupleDesc tupDesc;
@ -533,11 +562,10 @@ void ExecInitMerge(ModifyTableState* mtstate, EState* estate, ResultRelInfo* res
action_state->commandType = action->commandType;
action_state->whenqual = ExecInitExpr((Expr*)action->qual, &mtstate->ps);
/* 为此动作的投影创建目标槽 */
/* create target slot for this action's projection */
tupDesc = ExecTypeFromTL((List*)action->targetList, false, true, relationDesc->tdTableAmType);
action_state->tupDesc = tupDesc;
// 在特定情况下创建扫描槽和约束槽
if (IS_PGXC_DATANODE && CMD_UPDATE == action->commandType) {
mtstate->mt_scan_slot = MakeSingleTupleTableSlot(tupDesc);
}
@ -550,35 +578,34 @@ void ExecInitMerge(ModifyTableState* mtstate, EState* estate, ResultRelInfo* res
mtstate->mt_insert_constr_slot = MakeSingleTupleTableSlot(tupDesc);
}
/* 构建动作投影状态 */
/* build action projection state */
targetList = (List*)ExecInitExpr((Expr*)action->targetList, &mtstate->ps);
action_state->proj = ExecBuildProjectionInfo(targetList, econtext, mtstate->mt_mergeproj, relationDesc);
/*
* - WHEN MATCHED WHEN NOT MATCHED -
* MergeActionState
* We create two lists - one for WHEN MATCHED actions and one
* for WHEN NOT MATCHED actions - and stick the
* MergeActionState into the appropriate list.
*/
if (action_state->matched)
mergeMatchedActionStates = lappend(mergeMatchedActionStates, action_state);
else
mergeNotMatchedActionStates = lappend(mergeNotMatchedActionStates, action_state);
// 根据不同的操作类型设置子命令标志
switch (action->commandType) {
case CMD_INSERT:
ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, action->targetList);
mtstate->mt_merge_subcommands |= MERGE_INSERT;
break;
case CMD_UPDATE:
ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, action->targetList);
mtstate->mt_merge_subcommands |= MERGE_UPDATE;
break;
default:
Assert(0);
break;
case CMD_INSERT:
ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, action->targetList);
mtstate->mt_merge_subcommands |= MERGE_INSERT;
break;
case CMD_UPDATE:
ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, action->targetList);
mtstate->mt_merge_subcommands |= MERGE_UPDATE;
break;
default:
Assert(0);
break;
}
// 设置匹配和不匹配动作的状态列表
resultRelInfo->ri_mergeState->matchedActionStates = mergeMatchedActionStates;
resultRelInfo->ri_mergeState->notMatchedActionStates = mergeNotMatchedActionStates;
}

View File

@ -1,59 +1,80 @@
/* -------------------------------------------------------------------------
*
* execProcnode.cpp
* "初始化""获取元组" "清理"
* ExecInitNodeExecProcNode ExecEndNode
* contains dispatch functions which call the appropriate "initialize",
* "get a tuple", and "cleanup" routines for the given node type.
* If the node has children, then it will presumably call ExecInitNode,
* ExecProcNode, or ExecEndNode on its subnodes and do the appropriate
* processing.
*
* (c) 2020
* (c) 1996-2012 PostgreSQL
* (c) 1994
* (c) 2021 openGauss
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 2021, openGauss Contributors
*
*
*
* src/gausskernel/runtime/executor/execProcnode.cpp
* IDENTIFICATION
* src/gausskernel/runtime/executor/execProcnode.cpp
*
* -------------------------------------------------------------------------
*/
/*
*
* ExecInitNode -
* ExecProcNode -
* ExecEndNode -
* 便ExecInitNodeExecProcNode和ExecEndNode的同步
```
select DEPT.no_emps, EMP.age
where EMP.name = DEPT.mgr and
DEPT.name = "shoe"
```
```
Nest Loop (DEPT.mgr = EMP.name)
/ \
/ \
Seq Scan Seq Scan
DEPT EMP
(name = "shoe")
```
ExecutorStart()InitPlan()ExecInitNode()
* ExecInitNode() ExecInitNestLoop()ExecInitNode()ExecInitNode()
* ExecutorRun()ExecutePlan()ExecProcNode()ExecProcNode() ExecNestLoop()ExecProcNode()ExecSeqScan()ExecSeqScan() ExecNestLoop()
* ExecSeqScan() ExecutorEnd() ExecEndNode()ExecEndNestLoop()ExecEndNode()ExecEndSeqScan()
ExecInitNode()ExecProcNode() ExecEndNode()
* INTERFACE ROUTINES
* ExecInitNode - initialize a plan node and its subplans
* ExecProcNode - get a tuple by executing the plan node
* ExecEndNode - shut down a plan node and its subplans
*
* NOTES
* This used to be three files. It is now all combined into
* one file so that it is easier to keep ExecInitNode, ExecProcNode,
* and ExecEndNode in sync when new nodes are added.
*
* EXAMPLE
* Suppose we want the age of the manager of the shoe department and
* the number of employees in that department. So we have the query:
*
* select DEPT.no_emps, EMP.age
* where EMP.name = DEPT.mgr and
* DEPT.name = "shoe"
*
* Suppose the planner gives us the following plan:
*
* Nest Loop (DEPT.mgr = EMP.name)
* / \
* / \
* Seq Scan Seq Scan
* DEPT EMP
* (name = "shoe")
*
* ExecutorStart() is called first.
* It calls InitPlan() which calls ExecInitNode() on
* the root of the plan -- the nest loop node.
*
* * ExecInitNode() notices that it is looking at a nest loop and
* as the code below demonstrates, it calls ExecInitNestLoop().
* Eventually this calls ExecInitNode() on the right and left subplans
* and so forth until the entire plan is initialized. The result
* of ExecInitNode() is a plan state tree built with the same structure
* as the underlying plan tree.
*
* * Then when ExecutorRun() is called, it calls ExecutePlan() which calls
* ExecProcNode() repeatedly on the top node of the plan state tree.
* Each time this happens, ExecProcNode() will end up calling
* ExecNestLoop(), which calls ExecProcNode() on its subplans.
* Each of these subplans is a sequential scan so ExecSeqScan() is
* called. The slots returned by ExecSeqScan() may contain
* tuples which contain the attributes ExecNestLoop() uses to
* form the tuples it returns.
*
* * Eventually ExecSeqScan() stops returning tuples and the nest
* loop join ends. Lastly, ExecutorEnd() calls ExecEndNode() which
* calls ExecEndNestLoop() which in turn calls ExecEndNode() on
* its subplans which result in ExecEndSeqScan().
*
* This should show how the executor works by having
* ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch
* their work to the appopriate node support routines which may
* in turn call these routines themselves on their subplans.
*/
#include "postgres.h"
#include "knl/knl_variable.h"
@ -146,31 +167,30 @@ DEPT EMP
#define NODENAMELEN 64
/*NeedStubExecution
* PlanStub Execution
*
/*
* Function to determine a plannode should be processed in stub-routine when exec_nodes
* does not match current DN.
*
* "在存根中处理" ExecNodeInit()
* NodeInit工作在其lefttree/righttree上继续进行
* The term of "processed in stub" means we need let ExecNodeInit() bypass the actual
* initilaization work like open scanrel, instead allow NodeInit work to continue on its
* lefttree/righttree
*/
bool NeedStubExecution(Plan* plan)
{
#ifndef ENABLE_MULTIPLE_NODES
return false; // 如果不支持多节点模式,则直接返回不需要存根执行
return false;
#endif
// 如果计划节点位于递归联合操作之下,我们不考虑存根执行
/* If a plan node is under recursive union, we don't consider stub execution */
if (EXEC_IN_RECURSIVE_MODE(plan)) {
return false;
}
// 首先确定此计划步骤是否需要在当前数据库节点DN上执行
/* First, determine if this plan step needs excution on current dn */
if (NeedExecute(plan)) {
return false;
}
// 其次,确定此计划步骤是否需要进行存根处理
/* Second, determine if this plan step need stub processing */
switch (nodeTag(plan)) {
case T_ModifyTable:
case T_VecModifyTable:
@ -190,18 +210,15 @@ bool NeedStubExecution(Plan* plan)
case T_CStoreIndexHeapScan:
case T_SubqueryScan:
case T_FunctionScan:
return true; // 需要进行存根处理
return true;
default:
return false; // 其他情况不需要存根处理
return false;
}
}
/*
* NeedExecuteActiveSql
* SQL计划SQL
* not need execute active sql if the datanode don't run in multi-nodegroup.
*/
static bool NeedExecuteActiveSql(Plan* plan)
{
if ((!IS_PGXC_COORDINATOR) && (!IS_SINGLE_NODE) && false == NeedExecute(plan)) {
@ -211,50 +228,31 @@ static bool NeedExecuteActiveSql(Plan* plan)
return true;
}
/*
*
*/
static inline bool SeqScanNodeIsStub(SeqScanState* seq_scan)
{
return seq_scan->ss_currentScanDesc == NULL;
}
/*
*/
static inline bool IdxScanNodeIsStub(IndexScanState* index_scan)
{
return index_scan->iss_ScanDesc == NULL;
}
/*
*/
static inline bool IdxOnlyScanNodeIsStub(IndexOnlyScanState* index_only_scan)
{
return index_only_scan->ioss_ScanDesc == NULL;
}
/*
*/
static inline bool BmIdxOnlyScanNodeIsStub(BitmapIndexScanState* bm_index_scan)
{
return bm_index_scan->biss_ScanDesc == NULL;
}
/*
*/
static inline bool BmHeapScanNodeIsStub(BitmapHeapScanState* bm_heap_scan)
{
return bm_heap_scan->ss.ss_currentScanDesc == NULL;
}
/*
*/
PlanState* ExecInitNodeByType(Plan* node, EState* estate, int eflags)
{
switch (nodeTag(node)) {
@ -408,9 +406,6 @@ PlanState* ExecInitNodeByType(Plan* node, EState* estate, int eflags)
}
}
/*
PlanSubPlan,便
*/
void ExecInitNodeSubPlan(Plan* node, EState* estate, PlanState* result)
{
List* sub_ps = NIL;
@ -437,21 +432,17 @@ void ExecInitNodeSubPlan(Plan* node, EState* estate, PlanState* result)
/* ------------------------------------------------------------------------
* ExecInitNode
*
* 'node'
* Recursively initializes all the nodes in the plan tree rooted
* at 'node'.
*
* :
* 'node'
* 'estate'
* 'eflags'executor.h中描述的标志位的按位或
* Inputs:
* 'node' is the current node of the plan produced by the query planner
* 'estate' is the shared execution state for the plan tree
* 'eflags' is a bitwise OR of flag bits described in executor.h
*
* Plan节点相对应的PlanState节点
* Returns a PlanState node corresponding to the given Plan node.
* ------------------------------------------------------------------------
*/
/*
*/
PlanState* ExecInitNode(Plan* node, EState* estate, int e_flags)
{
PlanState* result = NULL;
@ -462,17 +453,14 @@ PlanState* ExecInitNode(Plan* node, EState* estate, int e_flags)
int rc = 0;
/*
*
* do nothing when we get to the end of a leaf on tree.
*/
if (node == NULL) {
return NULL;
}
// 进入性能跟踪
gstrace_entry(GS_TRC_ID_ExecInitNode);
// 根据节点类型和执行环境生成上下文名
if (!StreamTopConsumerAmI())
rc = snprintf_s(context_name,
NODENAMELEN,
@ -490,57 +478,135 @@ PlanState* ExecInitNode(Plan* node, EState* estate, int e_flags)
node->plan_node_id);
securec_check_ss(rc, "", "");
// 在此上下文中为表达式评估创建工作内存。
/*
* Create working memory for expression evaluation in this context.
*/
node_context = AllocSetContextCreate(estate->es_const_query_cxt,
context_name,
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
// 保存旧的查询上下文,并切换到新的节点上下文
query_context = estate->es_query_cxt;
// reassign the node context as we must run under this context.
estate->es_query_cxt = node_context;
// 切换到节点级内存上下文
/* Switch to Node Level Memory Context */
old_context = MemoryContextSwitchTo(node_context);
// 检查是否需要进行存根执行
/*
* Check whether this 'plan node' needs be processed in current DN exec_nodes,
* skip real initialization if it is not in exec-nodes
*
* Note: We only have to do such kind of specialy pocessing in some plan nodes
*/
if (unlikely(IS_PGXC_DATANODE && NeedStubExecution(node))) {
result = (PlanState*)ExecInitNodeStubNorm(node, estate, e_flags);
} else {
result = ExecInitNodeByType(node, estate, e_flags);
}
// 设置节点上下文
/* Set the nodeContext */
result->nodeContext = node_context;
// 初始化节点中的子计划
/*
* Initialize any initPlans present in this node. The planner put them in
* a separate list for us.
*/
/*
* We initialize subplan node on coordinator (for explain) or one dn thread
* that executes the subplan
*/
ExecInitNodeSubPlan(node, estate, result);
// 如果需要,为节点设置仪器(性能跟踪)
/* Set up instrumentation for this node if requested */
if (estate->es_instrument != INSTRUMENT_NONE) {
#ifdef ENABLE_MULTIPLE_NODES
// 为执行节点分配仪器槽位
// 注意:根据不同情况分配仪器槽位
/*
* "plan_node_id == 0" is special case, "with recursive + hdfs foreign table"
* will lead to plan_node_id of all plan node in subplan are zero.
* u_sess->instr_cxt.thread_instr->allocInstrSlot only return the instrArray->instr->instrPlanData
* which has allocated in threadinstrumentation.
*/
if (u_sess->instr_cxt.global_instr != NULL && u_sess->instr_cxt.thread_instr && node->plan_node_id > 0 &&
IS_PGXC_COORDINATOR && StreamTopConsumerAmI()) {
/* on compute pool */
result->instrument = u_sess->instr_cxt.thread_instr->allocInstrSlot(
node->plan_node_id, node->parent_node_id, result->plan, estate);
} else if (u_sess->instr_cxt.global_instr != NULL && u_sess->instr_cxt.thread_instr && node->plan_node_id > 0 &&
(IS_PGXC_DATANODE || (IS_PGXC_COORDINATOR && node->exec_type == EXEC_ON_COORDS))) {
/* plannode(exec on cn)or dn */
result->instrument = u_sess->instr_cxt.thread_instr->allocInstrSlot(
node->plan_node_id, node->parent_node_id, result->plan, estate);
} else {
/* on MPPDB CN */
result->instrument = InstrAlloc(1, estate->es_instrument);
}
#else
// 在非分布式环境下为执行节点分配仪器槽位
if (u_sess->instr_cxt.global_instr != NULL && u_sess->instr_cxt.thread_instr && node->plan_node_id > 0 &&
(!StreamTopConsumerAmI() ||
u_sess->instr_cxt.global_instr->get_planIdOffsetArray()[node->plan_node_id - 1] == 0)) {
result->instrument = u_sess->instr_cxt.thread_instr->allocInstrSlot(
node->plan_node_id, node->parent_node_id, result->plan, estate);
} else {
result->instrument = InstrAlloc(1, estate->es_instrument);
}
#endif
// 记录节点上下文以及其他性能统计信息
if (result->instrument) {
result->instrument->memoryinfo.nodeContext = node_context;
if (u_sess->attr.attr_resource.use_workload_manager &&
u_sess->attr.attr_resource.resource_track_level == RESOURCE_TRACK_OPERATOR &&
estate->es_can_realtime_statistics && u_sess->exec_cxt.need_track_resource &&
NeedExecuteActiveSql(node)) {
Qpid qid;
qid.plannodeid = node->plan_node_id;
qid.procId = u_sess->instr_cxt.gs_query_id->procId;
qid.queryId = u_sess->instr_cxt.gs_query_id->queryId;
int plan_dop = node->parallel_enabled ? u_sess->opt_cxt.query_dop : 1;
result->instrument->dop = plan_dop;
int64 plan_rows = e_rows_convert_to_int64(node->plan_rows);
if (nodeTag(node) == T_VecAgg &&
((Agg*)node)->aggstrategy == AGG_HASHED && ((VecAgg*)node)->is_sonichash) {
ExplainCreateDNodeInfoOnDN(&qid,
result->instrument,
node->exec_type == EXEC_ON_DATANODES,
"VectorSonicHashAgg",
plan_dop,
plan_rows);
} else if (nodeTag(node) == T_VecHashJoin && ((HashJoin*)node)->isSonicHash) {
ExplainCreateDNodeInfoOnDN(&qid,
result->instrument,
node->exec_type == EXEC_ON_DATANODES,
"VectorSonicHashJoin",
plan_dop,
plan_rows);
} else {
ExplainCreateDNodeInfoOnDN(&qid,
result->instrument,
node->exec_type == EXEC_ON_DATANODES,
nodeTagToString(nodeTag(node)),
plan_dop,
plan_rows);
}
}
}
}
// 切换回旧的内存上下文,恢复查询上下文
/* Switch to OldContext */
MemoryContextSwitchTo(old_context);
/* restore the per query context */
estate->es_query_cxt = query_context;
result->ps_rownum = 0;
// 退出性能跟踪
gstrace_exit(GS_TRC_ID_ExecInitNode);
return result;
}
/*
PlanState TupleTableSlot
*/
TupleTableSlot* ExecProcNodeByType(PlanState* node)
{
TupleTableSlot* result = NULL;
@ -593,12 +659,12 @@ TupleTableSlot* ExecProcNodeByType(PlanState* node)
return ExecHashJoin((HashJoinState*)node);
/*
*
* partition iterator node
*/
case T_PartIteratorState:
return ExecPartIterator((PartIteratorState*)node);
/*
*
/*
* materialization nodes
*/
case T_MaterialState:
return ExecMaterial((MaterialState*)node);
@ -644,35 +710,39 @@ TupleTableSlot* ExecProcNodeByType(PlanState* node)
return NULL;
}
}
/*
*/
void ExecProcNodeInstr(PlanState* node, TupleTableSlot* result)
{
switch (nodeTag(node)) {
case T_ModifyTableState:
case T_DistInsertSelectState:
// 计算第一个元组的处理时间
instr_time first_tuple;
INSTR_TIME_SET_ZERO(first_tuple);
INSTR_TIME_ACCUM_DIFF(
first_tuple, ((ModifyTableState*)node)->first_tuple_modified, node->instrument->starttime);
// 根据 es_last_processed 更新性能计数
/*
* If the value of es_last_processed is zero means the value of es_processed
* just come from current operator. If not means the value of es_processed
* come from current operator and other operator, es_processed minus
* es_last_processed is tuples processed of curent operator when modify
* the hdfs table, which may include modify the main table and modify the
* detla table, in this case, the value of es_processed will be set twice,
* resulting in error row value for modify operator in explain command.
*/
if (node->state->es_last_processed == 0) {
InstrStopNode(node->instrument, node->state->es_processed);
} else {
InstrStopNode(node->instrument, node->state->es_processed - node->state->es_last_processed);
}
// 更新 es_last_processed 并记录第一个元组的处理时间
node->state->es_last_processed = node->state->es_processed;
node->instrument->firsttuple = INSTR_TIME_GET_DOUBLE(first_tuple);
break;
case T_SeqScanState:
if (((SeqScanState*) node)->scanBatchMode) {
if (!TupIsNull(result)) {
// 在批处理模式下,根据处理的批次行数进行性能计数
/* Batch mode does not collect memory info as it takes too much CPU resources. */
InstrStopNode(node->instrument, ((SeqScanState*)node)->scanBatchState->scanBatch.rows, false);
} else {
InstrStopNode(node->instrument, 0.0);
@ -680,25 +750,17 @@ void ExecProcNodeInstr(PlanState* node, TupleTableSlot* result)
break;
}
default:
// 对于其他节点类型,根据是否返回了元组进行性能计数
InstrStopNode(node->instrument, TupIsNull(result) ? 0.0 : 1.0);
break;
}
// 更新节点的内存信息
node->instrument->memoryinfo.operatorMemory = SET_NODEMEM(node->plan->operatorMemKB[0], node->plan->dop);
// 如果未返回元组,将节点状态标记为 true表示节点执行完成
if (TupIsNull(result))
node->instrument->status = true;
}
typedef TupleTableSlot* (*ExecProcFuncType)(PlanState* node);
/*
*/
static inline TupleTableSlot *DefaultExecProc(PlanState *node)
{
ereport(ERROR,
@ -937,10 +999,9 @@ ExecProcFuncType g_execProcFuncTable[] = {
/* ----------------------------------------------------------------
* ExecProcNode
*
*
* Execute the given node to return a(nother) tuple.
* ----------------------------------------------------------------
*/
TupleTableSlot* ExecProcNode(PlanState* node)
{
TupleTableSlot* result = NULL;
@ -948,14 +1009,14 @@ TupleTableSlot* ExecProcNode(PlanState* node)
CHECK_FOR_INTERRUPTS();
MemoryContext old_context;
/* 响应停止或取消信号。 */
/* Response to stop or cancel signal. */
#ifdef ENABLE_MULTIPLE_NODES
if (unlikely(executorEarlyStop())) {
return NULL;
}
#endif
/* 切换到节点级内存上下文 */
/* Switch to Node Level Memory Context */
old_context = MemoryContextSwitchTo(node->nodeContext);
if (node->chgParam != NULL) { /* something changed */
@ -991,15 +1052,16 @@ TupleTableSlot* ExecProcNode(PlanState* node)
/* ----------------------------------------------------------------
* MultiExecProcNode
*
*
*
* Execute a node that doesn't return individual tuples
* (it might return a hashtable, bitmap, etc). Caller should
* check it got back the expected kind of Node.
*
* ExecProcNode
* InstrStartNode/InstrStopNode
*
* This has essentially the same responsibilities as ExecProcNode,
* but it does not do InstrStartNode/InstrStopNode (mainly because
* it can't tell how many returned tuples to count). Each per-node
* function must provide its own instrumentation support.
* ----------------------------------------------------------------
*/
Node* MultiExecProcNode(PlanState* node)
{
Node* result = NULL;
@ -1007,7 +1069,7 @@ Node* MultiExecProcNode(PlanState* node)
CHECK_FOR_INTERRUPTS();
/* 切换到节点级内存上下文 */
/* Switch to Node Level Memory Context */
old_context = MemoryContextSwitchTo(node->nodeContext);
if (node->chgParam != NULL) { /* something changed */
@ -1015,8 +1077,8 @@ Node* MultiExecProcNode(PlanState* node)
}
switch (nodeTag(node)) {
/*
*
/*
* Only node types that actually support multiexec will be listed
*/
case T_HashState:
result = MultiExecHash((HashState*)node);
@ -1043,7 +1105,7 @@ Node* MultiExecProcNode(PlanState* node)
break;
}
/* 打印哈希运算符的操作内存 */
/* Print Operator Memory for Hash operator */
if (node->instrument) {
node->instrument->memoryinfo.operatorMemory = node->plan->operatorMemKB[0];
}
@ -1053,38 +1115,29 @@ Node* MultiExecProcNode(PlanState* node)
return result;
}
/*
*/
void ExplainNodePending(PlanState* result_plan)
{
// 检查是否启用了工作负载管理以及资源跟踪级别是否是操作员级别,或者结果计划为 NULL。
if (!u_sess->attr.attr_resource.use_workload_manager ||
u_sess->attr.attr_resource.resource_track_level != RESOURCE_TRACK_OPERATOR || result_plan == NULL) {
return;
}
// 如果不是协调器或来自协调器的连接,并且不是单节点模式,直接返回。
if ((!IS_PGXC_COORDINATOR || IsConnFromCoord()) && !IS_SINGLE_NODE) {
return;
}
// 定义变量以存储查询标识符Qpid和返回值。
bool has_found = false;
Qpid qid;
int rc = 0;
// 从上下文中获取进程和查询标识符,并设置计划节点标识符。
qid.procId = u_sess->instr_cxt.gs_query_id->procId;
qid.queryId = u_sess->instr_cxt.gs_query_id->queryId;
qid.plannodeid = result_plan->plan->plan_node_id;
// 如果查询标识符无效,则直接返回。
if (IsQpidInvalid(&qid)) {
return;
}
uint32 hash_code = GetHashPlanCode(&qid, sizeof(Qpid));
LockOperHistHashPartition(hash_code, LW_EXCLUSIVE);
@ -1109,49 +1162,37 @@ void ExplainNodePending(PlanState* result_plan)
UnLockOperHistHashPartition(hash_code);
}
/*
*/
void ExplainNodeFinish(PlanState* result_plan, PlannedStmt *pstmt, TimestampTz current_time, bool is_pending)
{
// 检查是否启用了工作负载管理资源跟踪级别是否为操作员级别结果计划是否存在以及是否需要执行活动SQL。
if (!u_sess->attr.attr_resource.use_workload_manager ||
u_sess->attr.attr_resource.resource_track_level != RESOURCE_TRACK_OPERATOR || result_plan == NULL ||
!NeedExecuteActiveSql(result_plan->plan)) {
return;
}
// 如果结果计划的仪器信息不为空并且支持历史统计信息,则获取计划的并行度。
if (result_plan->instrument != NULL && result_plan->state->es_can_history_statistics) {
int plan_dop = result_plan->instrument->dop;
// 根据节点类型设置计划名称。
char *plan_name = NULL;
Plan* node = result_plan->plan;
if (nodeTag(node) == T_VecAgg && ((Agg*)node)->aggstrategy == AGG_HASHED && ((VecAgg*)node)->is_sonichash) {
plan_name = "VectorSonicHashAgg";
} else if (nodeTag(node) == T_VecHashJoin && ((HashJoin*)node)->isSonicHash) {
plan_name = "VectorSonicHashJoin";
} else {
plan_name = nodeTagToString(nodeTag(node));
}
// 如果不是挂起状态,则记录计划信息。
if (is_pending) {
ExplainNodePending(result_plan);
} else {
int64 plan_rows = e_rows_convert_to_int64(result_plan->plan->plan_rows);
OperatorPlanInfo* opt_plan_info = NULL;
Plan* node = result_plan->plan;
char *plan_name = NULL;
if (nodeTag(node) == T_VecAgg && ((Agg*)node)->aggstrategy == AGG_HASHED && ((VecAgg*)node)->is_sonichash) {
plan_name = "VectorSonicHashAgg";
} else if (nodeTag(node) == T_VecHashJoin && ((HashJoin*)node)->isSonicHash) {
plan_name = "VectorSonicHashJoin";
} else {
plan_name = nodeTagToString(nodeTag(node));
}
OperatorPlanInfo* opt_plan_info = NULL;
#ifndef ENABLE_MULTIPLE_NODES
// 提取操作员计划信息,如果是单节点模式。
if (pstmt != NULL)
opt_plan_info = ExtractOperatorPlanInfo(result_plan, pstmt);
#endif /* ENABLE_MULTIPLE_NODES */
// 设置计划的会话信息。
ExplainSetSessionInfo(result_plan->plan->plan_node_id,
result_plan->instrument,
result_plan->plan->exec_type == EXEC_ON_DATANODES,
@ -1163,11 +1204,9 @@ void ExplainNodeFinish(PlanState* result_plan, PlannedStmt *pstmt, TimestampTz c
}
}
// 根据节点类型执行递归操作。
switch (nodeTag(result_plan->plan)) {
case T_MergeAppend:
case T_VecMergeAppend: {
// 对于 MergeAppend 节点,递归调用 ExplainNodeFinish 函数。
MergeAppendState* ma = (MergeAppendState*)result_plan;
for (int i = 0; i < ma->ms_nplans; i++) {
PlanState* plan = ma->mergeplans[i];
@ -1176,14 +1215,42 @@ void ExplainNodeFinish(PlanState* result_plan, PlannedStmt *pstmt, TimestampTz c
} break;
case T_Append:
case T_VecAppend: {
// 对于 Append 节点,递归调用 ExplainNodeFinish 函数。
AppendState* append = (AppendState*)result_plan;
for (int i = 0; i < append->as_nplans; i++) {
PlanState* plan = append->appendplans[i];
ExplainNodeFinish(plan, pstmt, current_time, is_pending);
}
} break;
// 其他节点类型的类似递归调用,如 ModifyTable、SubqueryScan、BitmapAnd、BitmapOr 等。
case T_ModifyTable:
case T_VecModifyTable: {
ModifyTableState* mt = (ModifyTableState*)result_plan;
for (int i = 0; i < mt->mt_nplans; i++) {
PlanState* plan = mt->mt_plans[i];
ExplainNodeFinish(plan, pstmt, current_time, is_pending);
}
} break;
case T_SubqueryScan:
case T_VecSubqueryScan: {
SubqueryScanState* ss = (SubqueryScanState*)result_plan;
if (ss->subplan)
ExplainNodeFinish(ss->subplan, pstmt, current_time, is_pending);
} break;
case T_BitmapAnd:
case T_CStoreIndexAnd: {
BitmapAndState* ba = (BitmapAndState*)result_plan;
for (int i = 0; i < ba->nplans; i++) {
PlanState* plan = ba->bitmapplans[i];
ExplainNodeFinish(plan, pstmt, current_time, is_pending);
}
} break;
case T_BitmapOr:
case T_CStoreIndexOr: {
BitmapOrState* bo = (BitmapOrState*)result_plan;
for (int i = 0; i < bo->nplans; i++) {
PlanState* plan = bo->bitmapplans[i];
ExplainNodeFinish(plan, pstmt, current_time, is_pending);
}
} break;
default:
if (result_plan->lefttree)
ExplainNodeFinish(result_plan->lefttree, pstmt, current_time, is_pending);
@ -1192,7 +1259,6 @@ void ExplainNodeFinish(PlanState* result_plan, PlannedStmt *pstmt, TimestampTz c
break;
}
// 遍历 initPlan 和 subPlan 列表,递归调用 ExplainNodeFinish 函数。
ListCell* lst = NULL;
foreach (lst, result_plan->initPlan) {
SubPlanState* sps = (SubPlanState*)lfirst(lst);
@ -1213,42 +1279,35 @@ void ExplainNodeFinish(PlanState* result_plan, PlannedStmt *pstmt, TimestampTz c
}
}
/*
*/
* Target : clean up sensitive information used in encryption or decryption.
* Input : NA
* Output : NA
*/
void cleanup_sensitive_information()
{
// 外部变量声明:用于记录加密和解密操作的状态以及使用的向量和输入数据
/* used derive_keys and user_key in decryption. */
extern THR_LOCAL bool decryption_function_call;
extern THR_LOCAL unsigned char derive_vector_used[NUMBER_OF_SAVED_DERIVEKEYS][RANDOM_LEN];
extern THR_LOCAL unsigned char mac_vector_used[NUMBER_OF_SAVED_DERIVEKEYS][RANDOM_LEN];
extern THR_LOCAL unsigned char user_input_used[NUMBER_OF_SAVED_DERIVEKEYS][RANDOM_LEN];
/* used derive_keys and user_key in encryption. */
extern THR_LOCAL bool encryption_function_call;
extern THR_LOCAL unsigned char derive_vector_saved[RANDOM_LEN];
extern THR_LOCAL unsigned char mac_vector_saved[RANDOM_LEN];
extern THR_LOCAL unsigned char input_saved[RANDOM_LEN];
errno_t errorno = EOK;
// 清空加密信息
if (encryption_function_call == true) {
// 将保存的派生向量、输入数据和 MAC 向量的内容全部置为零
errorno = memset_s(derive_vector_saved, RANDOM_LEN, 0, RANDOM_LEN);
securec_check(errorno, "", "");
errorno = memset_s(input_saved, RANDOM_LEN, 0, RANDOM_LEN);
securec_check(errorno, "", "");
errorno = memset_s(mac_vector_saved, RANDOM_LEN, 0, RANDOM_LEN);
securec_check(errorno, "", "");
// 标记加密操作已完成
encryption_function_call = false;
}
// 清空解密信息
if (decryption_function_call == true) {
// 使用循环将每个保存的派生向量、用户输入数据和 MAC 向量的内容全部置为零
for (int i = 0; i < NUMBER_OF_SAVED_DERIVEKEYS; ++i) {
errorno = memset_s(derive_vector_used[i], RANDOM_LEN, 0, RANDOM_LEN);
securec_check(errorno, "", "");
@ -1257,8 +1316,6 @@ void cleanup_sensitive_information()
errorno = memset_s(mac_vector_used[i], RANDOM_LEN, 0, RANDOM_LEN);
securec_check(errorno, "", "");
}
// 标记解密操作已完成
decryption_function_call = false;
}
}
@ -1266,27 +1323,26 @@ void cleanup_sensitive_information()
/* ----------------------------------------------------------------
* ExecEndNodeByType
*
* 'node'
* Recursively cleans up all the nodes in the plan rooted
* at 'node'.
*
*
*
* After this operation, the query plan will not be able to be
* processed any further. This should be called only after
* the query plan has been fully executed.
* ----------------------------------------------------------------
*/
static void ExecEndNodeByType(PlanState* node)
{
/*
*
/*
* do nothing when we get to the end of a leaf on tree.
*/
/* clean up sensitive information used in encryption or decryption */
/* 清除在加密或解密中使用的敏感信息 */
/* 对于数据节点,我们应该在此函数中结束仪器,
*
/* As for data node, we should end instrument in this function,
* but in coordinator do in the explain function.
*/
/* 在计算池的协调器上 */
/* on the CN of the compute pool */
switch (nodeTag(node)) {
/*
* control nodes
@ -1324,10 +1380,9 @@ static void ExecEndNodeByType(PlanState* node)
ExecEndBitmapOr((BitmapOrState*)node);
break;
/*
*
*/
/*
* scan nodes
*/
case T_SeqScanState:
ExecEndSeqScan((SeqScanState*)node);
break;
@ -1418,10 +1473,9 @@ static void ExecEndNodeByType(PlanState* node)
ExecEndHashJoin((HashJoinState*)node);
break;
/*
*
*/
/*
* materialization nodes
*/
case T_MaterialState:
ExecEndMaterial((MaterialState*)node);
break;
@ -1576,51 +1630,30 @@ static void ExecEndNodeByType(PlanState* node)
break;
}
}
/*
*/
void ExecEndNode(PlanState* node)
{
// 如果节点为空,直接返回
if (node == NULL) {
return;
}
// 清理敏感信息
cleanup_sensitive_information();
// 释放变更参数集合
if (node->chgParam != NULL) {
bms_free_ext(node->chgParam);
node->chgParam = NULL;
}
// 结束仪器的测量循环
if (node->instrument != NULL) {
// 如果是分布式数据节点,结束测量循环
if (IS_PGXC_DATANODE) {
InstrEndLoop(node->instrument);
}
// 如果需要执行活动SQL操作移除相应的解释信息
if (NeedExecuteActiveSql(node->plan)) {
removeExplainInfo(node->plan->plan_node_id);
}
}
// 在协调器上执行的且是最终消费者的情况下,结束测量循环
if (node->instrument != NULL && IS_PGXC_COORDINATOR && StreamTopConsumerAmI()) {
InstrEndLoop(node->instrument);
}
// 如果需要对节点进行存根处理,执行相应的存根处理并返回
if (planstate_need_stub(node)) {
ExecEndNodeStub(node);
return;
}
// 执行特定类型节点的结束处理
ExecEndNodeByType(node);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,10 @@
/* -------------------------------------------------------------------------
*
* execReplication.cpp
*
* miscellaneous executor routines for logical replication
*
* (c) 1996-2021, PostgreSQL全球发展集团
* (c) 1994
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
@ -45,12 +45,13 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
TupleTableSlot *outslot, FakeRelationPartition *fakeRelPart);
/*
* relkeyScanKey
'rel'(** idxrel!)
* Setup a ScanKey for a search in the relation 'rel' for a tuple 'key' that
* is setup to match 'rel' (*NOT* idxrel!).
*
* null
* Returns whether any column contains NULLs.
*
* idxrel是一个rel的复制标识
* This is not generic routine, it expects the idxrel to be replication
* identity of a rel and meet all limitations associated with that.
*/
static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel, TupleTableSlot *searchslot)
{
@ -65,7 +66,7 @@ static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel
Assert(!isnull);
opclass = (oidvector *)DatumGetPointer(indclassDatum);
/*为索引中的每个属性构建scankey。 */
/* Build scankey for every attribute in the index. */
for (attoff = 0; attoff < IndexRelationGetNumberOfKeyAttributes(idxrel); attoff++) {
Oid op;
Oid opfamily;
@ -78,17 +79,10 @@ static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel
errmsg("index key attribute number %d exceeds number of columns %d",
mainattno, searchslot->tts_tupleDescriptor->natts)));
}
/* 这段代码片段是一个循环,它遍历索引的键属性。下面是它的功能细分:
1. attoff = 0IndexRelationGetNumberOfKeyAttributes(idxrel)
2. :
-
-
* /
/*
*
/*
* Load the operator info. We need this to get the equality operator
* function for the scan key.
*/
opfamily = get_opclass_family(opclass->values[attoff]);
@ -97,18 +91,12 @@ static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel
elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", BTEqualStrategyNumber, optype, optype, opfamily);
regop = get_opcode(op);
/*在给定的代码片段中,在错误检查之后执行以下步骤:
1. 使' get_opclass_family '' opclass->values[attoff] '
2. 使' get_opfamily_member '' opfamily ' ' optype ' ' optype '' BTEqualStrategyNumber '
3.使' OidIsValid '使' elog '
4. 使' get_opcode '' op '*/
/* 初始化扫描键。 */
/* Initialize the scankey. */
ScanKeyInit(&skey[attoff], pkattno, BTEqualStrategyNumber, regop, searchslot->tts_values[mainattno - 1]);
skey[attoff].sk_collation = idxrel->rd_indcollation[attoff];
/* 检查是否为空值。 */
/* Check for null value. */
if (searchslot->tts_isnull[mainattno - 1]) {
hasnulls = true;
skey[attoff].sk_flags |= SK_ISNULL;
@ -117,23 +105,15 @@ static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel
return hasnulls;
}
/*在给定的代码片段中,以下是代码的执行流程:
1. `searchslot->tts_isnull[mainattno - 1]`NULL
2. NULL`hasnulls``true`NULL值
3. `skey[attoff].sk_flags``SK_ISNULL`1NULL
4. `hasnulls`NULL值
NULL值 */
/* 检查tableam_tuple_lock结果如果需要重试则返回 */
/* Check tableam_tuple_lock result, and return if need to retry */
static bool inline CheckTupleLockRes(TM_Result res)
{
switch (res) {
case TM_Ok:
break;
case TM_Updated:
/* XXX:改进这里的操作 */
/* XXX: Improve handling here */
ereport(LOG, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("concurrent update, retrying")));
return true;
case TM_Invisible:
@ -145,21 +125,13 @@ static bool inline CheckTupleLockRes(TM_Result res)
}
return false;
}
/*这段代码定义了一个名为CheckTupleLockRes的静态内联函数其作用是检查给定的TM_Result结果并根据不同的结果进行相应的处理。以下是代码的执行流程
res结果进行switch语句的判断
TM_Ok
TM_Updatedtrue
TM_Invisible
heap_lock_tuple状态
false*/
/* 检查堆修改结果 */
/* Check heap modify result */
static void inline CheckTupleModifyRes(TM_Result res)
{
switch (res) {
case TM_SelfModified:
/* 元组已在当前命令中更新? */
/* Tuple was already updated in current command? */
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("tuple already updated by self")));
break;
case TM_Ok:
@ -182,32 +154,21 @@ static inline List* GetPartitionList(Relation rel, LOCKMODE lockmode)
return relationGetPartitionList(rel, lockmode);
}
}
/*CheckTupleModifyRes函数是一个静态内联函数用于检查给定的TM_Result结果并根据不同的结果输出相应的错误信息。执行流程如下
TM_SelfModified
TM_Ok
TM_Updated或TM_Deleted
GetPartitionList函数是一个内联函数
subpartitionedRelationGetSubPartitionList函数获取子分区列表
relationGetPartitionList函数获取分区列表
*/
static bool PartitionFindReplTupleByIndex(EState *estate, Relation rel, Relation idxrel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelInfo)
{
/* 必须是非GPI指数 */
/* must be non-GPI index */
Assert(!RelationIsGlobalIndex(idxrel));
fakeRelInfo->partList = GetPartitionList(rel, RowExclusiveLock);
/* 在分区列表中逐个搜索元组 */
/* search the tuple in partition list one by one */
ListCell *cell = NULL;
foreach (cell, fakeRelInfo->partList) {
Partition heapPart = (Partition)lfirst(cell);
Relation partionRel = RelationIsSubPartitioned(rel) ? SubPartitionGetRelation(rel, heapPart, NoLock) :
partitionGetRelation(rel, heapPart);
/* 获取此堆分区的索引分区 */
/* Get index partition of this heap partition */
Oid idxPartOid = getPartitionIndexOid(RelationGetRelid(idxrel), heapPart->pd_id);
Partition idxPart = partitionOpen(idxrel, idxPartOid, RowExclusiveLock);
Relation idxPartRel = RelationIsSubPartitioned(rel) ? SubPartitionGetRelation(idxrel, idxPart, NoLock) :
@ -218,28 +179,24 @@ static bool PartitionFindReplTupleByIndex(EState *estate, Relation rel, Relation
fakeRelInfo->partOid = heapPart->pd_id;
if (RelationFindReplTupleByIndex(estate, rel, idxPartRel, lockmode, searchslot, outslot, fakeRelInfo)) {
/* 命中,释放索引资源,堆分区需要以后使用,所以不要释放它 */
/* Hit, release index resource, heap partition need to be used later, so don't release it */
partitionClose(idxrel, idxPart, NoLock);
releaseDummyRelation(&idxPartRel);
/* 调用方应释放部件Rel */
/* caller shoud release partRel */
fakeRelInfo->needRleaseDummyRel = true;
return true;
}
/* 在当前分区中没有找到元组,释放虚拟关系并切换到下一个分区 */
/* didn't find tuple in current partition, release dummy relation and switch to next partition */
releaseDummyRelation(&fakeRelInfo->partRel);
partitionClose(idxrel, idxPart, NoLock);
releaseDummyRelation(&idxPartRel);
}
/* 没有找到元组在任何分区,关闭和返回 */
/* do not find tuple in any patition, close and return */
releasePartitionList(rel, &fakeRelInfo->partList, NoLock);
return false;
}
/* 这段代码是在分区表中根据索引查找元组的函数。首先它断言索引不是全局分区索引。然后它获取分区列表并使用foreach循环遍历每个分区。
ID获取索引分区的OIDfakeRelInfo结构中
RelationFindReplTupleByIndex函数来在当前分区的索引中查找匹配的元组
fakeRelInfo->needRleaseDummyRel设置为truetrue表示找到了匹配的元组*/
static bool PartitionFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelInfo)
@ -256,29 +213,25 @@ static bool PartitionFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
fakeRelInfo->partOid = heapPart->pd_id;
if (RelationFindReplTupleSeq(rel, lockmode, searchslot, outslot, fakeRelInfo)) {
/* 调用方应释放部件Rel */
/* caller shoud release partRel */
fakeRelInfo->needRleaseDummyRel = true;
return true;
}
releaseDummyRelation(&fakeRelInfo->partRel);
}
/* 没有找到元组在任何分区,关闭和返回 */
/* do not find tuple in any patition, close and return */
releasePartitionList(rel, &fakeRelInfo->partList, NoLock);
return false;
}
/* 这段代码是在分区表中按顺序查找元组的函数。它首先获取分区列表并使用foreach循环遍历每个分区。
fakeRelInfo结构中
RelationFindReplTupleSeq函数来在当前分区中按顺序查找匹配的元组fakeRelInfo->needRleaseDummyRel设置为truetrue表示找到了匹配的元组
false表示没有找到匹配的元组*/
/*
* 使'rel'
* Search the relation 'rel' for tuple using the index or seq scan.
*
* lockmode锁定它truefalse
* If a matching tuple is found, lock it with lockmode, fill the slot with its
* contents, and return true. Return false otherwise.
*
* fakeRelInfo->partList和fakeRelInfo-> parttrel
* Caller should check and release fakeRelInfo->partList and fakeRelInfo->partRel
*/
bool RelationFindReplTuple(EState *estate, Relation rel, Oid idxoid, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelInfo)
@ -287,7 +240,7 @@ bool RelationFindReplTuple(EState *estate, Relation rel, Oid idxoid, LockTupleMo
bool found = false;
Relation idxrel = NULL;
/* 清除假rel信息 */
/* clear fake rel info */
rc = memset_s(fakeRelInfo, sizeof(FakeRelationPartition), 0, sizeof(FakeRelationPartition));
securec_check(rc, "", "");
@ -295,7 +248,7 @@ bool RelationFindReplTuple(EState *estate, Relation rel, Oid idxoid, LockTupleMo
idxrel = index_open(idxoid, RowExclusiveLock);
}
/*对于非分区表或带有GPI的分区表使用父堆和索引进行扫描 */
/* for non partitioned table, or partitioned table with GPI, use parent heap and index to do the scan */
if (RelationIsNonpartitioned(rel) || (idxrel != NULL && RelationIsGlobalIndex(idxrel))) {
if (idxrel != NULL) {
found = RelationFindReplTupleByIndex(estate, rel, idxrel, lockmode, searchslot, outslot, fakeRelInfo);
@ -306,7 +259,7 @@ bool RelationFindReplTuple(EState *estate, Relation rel, Oid idxoid, LockTupleMo
}
}
/* 分区扫描 */
/* scan with partition */
if (idxrel != NULL) {
found = PartitionFindReplTupleByIndex(estate, rel, idxrel, lockmode, searchslot, outslot, fakeRelInfo);
index_close(idxrel, NoLock);
@ -317,9 +270,10 @@ bool RelationFindReplTuple(EState *estate, Relation rel, Oid idxoid, LockTupleMo
}
/*
* 使'rel'
* Search the relation 'rel' for tuple using the index.
*
* lockmode锁定它truefalse
* If a matching tuple is found, lock it with lockmode, fill the slot with its
* contents, and return true. Return false otherwise.
*/
static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation idxrel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelPart)
@ -334,7 +288,8 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
int rc;
bool isGpi = RelationIsGlobalIndex(idxrel);
/*
* GPI和非分区表使使
* For GPI and non-partition table, use parent heap relation to search the tuple,
* otherwise use partition relation
*/
if (isGpi || RelationIsNonpartitioned(rel)) {
targetRel = rel;
@ -342,21 +297,21 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
targetRel = fakeRelPart->partRel;
}
Assert(targetRel != NULL);
/* 启动索引扫描。 */
/* Start an index scan. */
InitDirtySnapshot(snap);
scan = scan_handler_idx_beginscan(targetRel, idxrel, &snap,
IndexRelationGetNumberOfKeyAttributes(idxrel), 0);
/* 参考check_violation如果我们想在UStore中使用脏快照我们需要设置isUpsert */
/* refer to check_violation, we need to set isUpsert if we want to use dirty snapshot in UStore */
scan->isUpsert = true;
/* 构建扫描键。 */
/* Build scan key. */
build_replindex_scan_key(skey, targetRel, idxrel, searchslot);
while (true) {
found = false;
scan_handler_idx_rescan(scan, skey, IndexRelationGetNumberOfKeyAttributes(idxrel), NULL, 0);
/* 试着找到这个元组 */
/* Try to find the tuple */
if (RelationIsUstoreFormat(targetRel)) {
found = IndexGetnextSlot(scan, ForwardScanDirection, outslot);
} else {
@ -366,10 +321,12 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
}
}
if (found) {
/* 找到元组,尝试在锁定模式下锁定它。 */
/* Found tuple, try to lock it in the lockmode. */
outslot->tts_tuple = ExecMaterializeSlot(outslot);
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
/*
*
* If the tuple is locked, wait for locking transaction to finish
* and retry.
*/
if (TransactionIdIsValid(xwait)) {
XactLockTableWait(xwait);
@ -389,21 +346,17 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
ItemPointer tid = tableam_tops_get_t_self(targetRel, outslot->tts_tuple);
if (RelationIsUstoreFormat(targetRel)) {
/* 将插槽物化,这样扫描结束后我们就可以访问它了 */
outslot->tts_tuple = UHeapMaterialize(outslot);
ItemPointerCopy(tid, &UHeaplocktup.ctid);
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
securec_check(rc, "\0", "\0");
UHeaplocktup.disk_tuple = &tbuf.hdr;
locktup = &UHeaplocktup;
} else {
/* 将插槽物化,这样扫描结束后我们就可以访问它了 */
outslot->tts_tuple = ExecMaterializeSlot(outslot);
ItemPointerCopy(tid, &heaplocktup.t_self);
locktup = &heaplocktup;
}
/* 获取目标元组的GPI分区 */
/* Get the target tuple's partition for GPI */
if (isGpi) {
GetFakeRelAndPart(estate, rel, outslot, fakeRelPart);
targetRel = fakeRelPart->partRel;
@ -412,20 +365,20 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
PushActiveSnapshot(GetLatestSnapshot());
res = tableam_tuple_lock(targetRel,
locktup, &buf, GetCurrentCommandId(false), lockmode, false, &hufd,
false, false, /* 不要关注更新 */
false, /* 评估 */
GetLatestSnapshot(), tid, /* 项目指针 */
false); /* 选择进行更新 */
/* 元组槽已固定缓冲区 */
false, false, /* don't follow updates */
false, /* eval */
GetLatestSnapshot(), tid, /* ItemPointer */
false); /* is select for update */
/* the tuple slot already has the buffer pinned */
ReleaseBuffer(buf);
PopActiveSnapshot();
if (CheckTupleLockRes(res)) {
/* 锁定元组失败,请重试 */
/* lock tuple failed, try again */
continue;
}
}
/* 我们结束了 */
/* we are done */
break;
}
@ -434,7 +387,7 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
}
/*
*
* Compare the tuple and slot and check if they have equal values.
*/
static bool tuple_equals_slot(TupleDesc desc, const Tuple tup, TupleTableSlot *slot, TypeCacheEntry **eq)
{
@ -445,21 +398,18 @@ static bool tuple_equals_slot(TupleDesc desc, const Tuple tup, TupleTableSlot *s
tableam_tops_deform_tuple(tup, desc, values, isnull);
/* 检查属性的相等性。 */
/* Check equality of the attributes. */
for (attrnum = 0; attrnum < desc->natts; attrnum++) {
TypeCacheEntry *typentry;
/* 跳过生成列跳过生成列 */
if (GetGeneratedCol(desc, attrnum)) {
continue;
}
/*
* NULLNULL
* If one value is NULL and other is not, then they are certainly not
* equal
*/
if (isnull[attrnum] != slot->tts_isnull[attrnum])
return false;
/*
* NULL
* If both are NULL, they can be considered equal.
*/
if (isnull[attrnum])
continue;
@ -485,13 +435,14 @@ static bool tuple_equals_slot(TupleDesc desc, const Tuple tup, TupleTableSlot *s
}
/*
* 使rel
* Search the relation 'rel' for tuple using the sequential scan.
*
* 使lockmode将其锁定truefalse
* If a matching tuple is found, lock it with lockmode, fill the slot with its
* contents, and return true. Return false otherwise.
*
*
* Note that this stops on the first matching tuple.
*
*
* This can obviously be quite slow on tables that have more than few rows.
*/
static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, TupleTableSlot *searchslot,
TupleTableSlot *outslot, FakeRelationPartition *fakeRelPart)
@ -510,7 +461,7 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
Assert(equalTupleDescs(desc, outslot->tts_tupleDescriptor));
eq = (TypeCacheEntry **)palloc0(sizeof(*eq) * outslot->tts_tupleDescriptor->natts);
/* 启动堆扫描。 */
/* Start a heap scan. */
InitDirtySnapshot(snap);
scan = scan_handler_tbl_beginscan(targetRel, &snap, 0, NULL, NULL);
@ -519,7 +470,7 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
found = false;
scan_handler_tbl_rescan(scan, NULL, targetRel);
/* 尝试查找元组 */
/* Try to find the tuple */
while ((scantuple = scan_handler_tbl_getnext(scan, ForwardScanDirection, targetRel)) != NULL) {
if (!tuple_equals_slot(desc, scantuple, searchslot, eq)) {
continue;
@ -527,10 +478,12 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
found = true;
ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
outslot->tts_tuple = ExecMaterializeSlot(outslot);
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
/*
*
* If the tuple is locked, wait for locking transaction to finish
* and retry.
*/
if (TransactionIdIsValid(xwait)) {
/* retry */
@ -544,7 +497,7 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
continue;
}
if (found) {
/* 找到元组,请尝试在锁定模式下锁定它。 */
/* Found tuple, try to lock it in the lockmode. */
Buffer buf;
TM_FailureData hufd;
TM_Result res;
@ -558,16 +511,12 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
ItemPointer tid = tableam_tops_get_t_self(rel, outslot->tts_tuple);
if (RelationIsUstoreFormat(targetRel)) {
/* 具体化插槽,这样我们就可以在扫描结束后访问它 */
outslot->tts_tuple = UHeapMaterialize(outslot);
ItemPointerCopy(tid, &UHeaplocktup.ctid);
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
securec_check(rc, "\0", "\0");
UHeaplocktup.disk_tuple = &tbuf.hdr;
locktup = &UHeaplocktup;
} else {
/* 具体化插槽,这样我们就可以在扫描结束后访问它 */
outslot->tts_tuple = ExecMaterializeSlot(outslot);
ItemPointerCopy(tid, &heaplocktup.t_self);
locktup = &heaplocktup;
}
@ -575,21 +524,21 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
PushActiveSnapshot(GetLatestSnapshot());
res = tableam_tuple_lock(targetRel, locktup, &buf, GetCurrentCommandId(false),
lockmode, false, &hufd, false,
false, /* 不关注更新 */
false, /* 评估 */
GetLatestSnapshot(), tid, /* 项目指针 */
false); /* 选择进行更新 */
false, /* don't follow updates */
false, /* eval */
GetLatestSnapshot(), tid, /* ItemPointer */
false); /* is select for update */
/* 元组槽已固定缓冲区 */
/* the tuple slot already has the buffer pinned */
ReleaseBuffer(buf);
PopActiveSnapshot();
if (CheckTupleLockRes(res)) {
/* 锁定元组失败,请重试 */
/* lock tuple failed, try again */
continue;
}
}
/* 我们结束了 */
/* we are done */
break;
}
@ -599,9 +548,10 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
}
/*
*
* Insert tuple represented in the slot to the relation, update the indexes,
* and execute any constraints and per-row triggers.
*
*
* Caller is responsible for opening the indexes.
*/
void ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot, FakeRelationPartition *relAndPart)
{
@ -610,12 +560,12 @@ void ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot, FakeRelation
Relation rel = resultRelInfo->ri_RelationDesc;
Relation targetRel = relAndPart->partRel == NULL ? rel : relAndPart->partRel;
/* 目前,我们只支持表格。 */
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
CheckCmdReplicaIdentity(rel, CMD_INSERT);
/* 在行之前插入触发器 */
/* BEFORE ROW INSERT Triggers */
if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_insert_before_row) {
slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
if (slot == NULL) {
@ -623,43 +573,40 @@ void ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot, FakeRelation
return;
}
}
/*这段代码是用于执行简单的关系表插入操作。它首先获取要插入的元组和目标关系表,
ExecBRInsertTriggers函数来执行这些触发器
*/
List *recheckIndexes = NIL;
/* 将槽具体化为一个元组,我们可以在上面乱涂乱画。 */
List *recheckIndexes = NIL;
/* Materialize slot into a tuple that we can scribble upon. */
tuple = tableam_tslot_get_tuple_from_slot(rel, slot);
tableam_tops_update_tuple_with_oid(targetRel, tuple, slot);
/* 计算存储的生成列 */
/* Compute stored generated columns */
if (rel->rd_att->constr && rel->rd_att->constr->has_generated_stored) {
ExecComputeStoredGenerated(resultRelInfo, estate, slot, tuple, CMD_INSERT);
tuple = slot->tts_tuple;
}
/* 检查元组的约束 */
/* Check the constraints of the tuple */
if (rel->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate);
/* 好的,存储元组并为其创建索引项 */
/* OK, store the tuple and create index entries for it */
(void)tableam_tuple_insert(targetRel, tuple, GetCurrentCommandId(true), 0, NULL);
if (resultRelInfo->ri_NumIndices > 0) {
ItemPointer pTSelf = tableam_tops_get_t_self(rel, tuple);
recheckIndexes =
ExecInsertIndexTuples(slot, pTSelf, estate, targetRel, relAndPart->part, InvalidBktId, NULL, NULL);
}
/* 在行后插入触发器 */
/* AFTER ROW INSERT Triggers */
ExecARInsertTriggers(estate, resultRelInfo, relAndPart->partOid, InvalidBktId, (HeapTuple)tuple, recheckIndexes);
list_free_ext(recheckIndexes);
}
/*
* searchslot元组使slot中的数据对其进行更新
* Find the searchslot tuple and update it with data in the slot,
* update the indexes, and execute any constraints and per-row triggers.
*
*
* Caller is responsible for opening the indexes.
*/
void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot *searchslot, TupleTableSlot *slot,
FakeRelationPartition *relAndPart)
@ -670,7 +617,7 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer searchSlotTid = tableam_tops_get_t_self(rel, searchslot->tts_tuple);
/* 目前,我们只支持表格。 */
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
CheckCmdReplicaIdentity(rel, CMD_UPDATE);
@ -680,7 +627,7 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
allowInplaceUpdate = false;
}
/* 排前更新触发器 */
/* BEFORE ROW UPDATE Triggers */
if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_update_before_row) {
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo, relAndPart->partOid, InvalidBktId, NULL,
searchSlotTid, slot);
@ -689,13 +636,8 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
return;
}
}
/* 这段代码是用于执行简单的关系表更新操作。它首先获取要更新的元组和目标关系表,然后检查关系表的复制标识以确保更新操作是合法的。
ExecBRUpdateTriggers函数来执行这些触发器
*/
/* 将槽具体化为一个元组,我们可以在上面乱涂乱画。 */
/* Materialize slot into a tuple that we can scribble upon. */
tuple = tableam_tslot_get_tuple_from_slot(rel, slot);
List *recheckIndexes = NIL;
Bitmapset *modifiedIdxAttrs = NULL;
@ -708,17 +650,18 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
Relation targetRelation = relAndPart->partRel == NULL ? rel : relAndPart->partRel;
Relation parentRelation = relAndPart->partRel == NULL ? NULL : rel;
/* 计算存储的生成列 */
/* Compute stored generated columns */
if (rel->rd_att->constr && rel->rd_att->constr->has_generated_stored) {
ExecComputeStoredGenerated(resultRelInfo, estate, slot, tuple, CMD_UPDATE);
tuple = slot->tts_tuple;
}
/* 检查元组的约束 */
/* Check the constraints of the tuple */
if (rel->rd_att->constr) {
ExecConstraints(resultRelInfo, slot, estate);
}
/* 检查分区表是否有行移动 */
/* check whether there is a row movement for partition table */
GetFakeRelAndPart(estate, rel, slot, &newTupleInfo);
if (newTupleInfo.partOid != InvalidOid && newTupleInfo.partOid != relAndPart->partOid) {
if (!rel->rd_rel->relrowmovement) {
@ -728,17 +671,11 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
}
rowMovement = true;
}
/* 这段代码是用于获取虚拟关系表和分区信息。
GetRelationPartitionOid函数来获取元组所属的分区OID
FakeRelationPartition信息
*/
tuple = slot->tts_tuple;
CommandId cid = GetCurrentCommandId(true);
/* 好的,更新它的元组和索引项 */
/* OK, update the tuple and index entries for it */
if (!rowMovement) {
res = tableam_tuple_update(targetRelation, parentRelation, searchSlotTid, tuple, cid,
res = tableam_tuple_update(targetRelation, parentRelation, searchSlotTid, slot->tts_tuple, cid,
InvalidSnapshot, estate->es_snapshot, true, &oldslot, &tmfd, &updateIndexes, &modifiedIdxAttrs,
false, allowInplaceUpdate);
CheckTupleModifyRes(res);
@ -753,7 +690,7 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
searchSlotTid, exec_index_tuples_state, InvalidBktId, modifiedIdxAttrs);
}
} else {
/* rowMovement,删除原始元组并插入新元组 */
/* rowMovement, delete origin tuple and insert new */
Assert(relAndPart->partRel != NULL);
Assert(newTupleInfo.partRel != NULL);
res = tableam_tuple_delete(relAndPart->partRel, searchSlotTid, cid, InvalidSnapshot,
@ -768,7 +705,7 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
tableam_tops_exec_delete_index_tuples(oldslot, relAndPart->partRel, NULL, searchSlotTid,
exec_index_tuples_state, modifiedIdxAttrs);
/* 插入新元组 */
/* Insert new tuple */
(void)tableam_tuple_insert(newTupleInfo.partRel, tuple, cid, 0, NULL);
if (resultRelInfo->ri_NumIndices > 0) {
ItemPointer pTSelf = tableam_tops_get_t_self(rel, tuple);
@ -780,12 +717,8 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
if (oldslot) {
ExecDropSingleTupleTableSlot(oldslot);
}
/* 这段代码是用于向关系表中插入新的元组。
tableam_tuple_insert函数将元组插入到目标关系表中
ExecInsertIndexTuples函数来为插入的元组创建索引
*/
/* 排后更新触发器 */
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo, relAndPart->partOid, InvalidBktId, relAndPart->partOid,
searchSlotTid, (HeapTuple)tuple, NULL, recheckIndexes);
@ -793,9 +726,10 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
}
/*
* .searchslot元组并将其删除
* Find the searchslot tuple and delete it, and execute any constraints
* and per-row triggers.
*
*
* Caller is responsible for opening the indexes.
*/
void ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, TupleTableSlot *searchslot,
FakeRelationPartition *relAndPart)
@ -805,12 +739,12 @@ void ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, TupleTableSlot
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer tid = tableam_tops_get_t_self(rel, searchslot->tts_tuple);
/* 目前,我们只支持表格。 */
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
CheckCmdReplicaIdentity(rel, CMD_DELETE);
/* 在行之前插入触发器 */
/* BEFORE ROW INSERT Triggers */
if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_delete_before_row) {
skip_tuple =
!ExecBRDeleteTriggers(estate, epqstate, resultRelInfo, relAndPart->partOid, InvalidBktId, NULL, tid);
@ -823,7 +757,7 @@ void ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, TupleTableSlot
Relation targetRel = relAndPart->partRel == NULL ? rel : relAndPart->partRel;
TM_FailureData tmfd;
/* 好,删除元组 */
/* OK, delete the tuple */
TM_Result res = tableam_tuple_delete(targetRel, tid, GetCurrentCommandId(true), InvalidSnapshot,
estate->es_snapshot, true, &oldslot, &tmfd);
CheckTupleModifyRes(res);
@ -839,29 +773,29 @@ void ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, TupleTableSlot
ExecDropSingleTupleTableSlot(oldslot);
}
/* 行删除触发器之后 */
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo, relAndPart->partOid, InvalidBktId, NULL, tid);
}
/*
* 使
* Check if command can be executed with current replica identity.
*/
void CheckCmdReplicaIdentity(Relation rel, CmdType cmd)
{
PublicationActions *pubactions;
/* 我们只需要检查UPDATE和DELETE。 */
/* We only need to do checks for UPDATE and DELETE. */
if (cmd != CMD_UPDATE && cmd != CMD_DELETE)
return;
/* 若关系具有复制身份,我们总是好的。 */
/* If relation has replica identity we are always good. */
if (RelationGetRelReplident(rel) == REPLICA_IDENTITY_FULL || OidIsValid(RelationGetReplicaIndex(rel)))
return;
/*
* UPDATE或DELETE
* This is either UPDATE OR DELETE and there is no replica identity.
*
* UPDATES或DELETES
* Check if the table publishes UPDATES or DELETES.
*/
pubactions = GetRelationPublicationActions(rel);
if (cmd == CMD_UPDATE && pubactions->pubupdate) {
@ -886,13 +820,8 @@ void GetFakeRelAndPart(EState *estate, Relation rel, TupleTableSlot *slot, FakeR
if (RelationIsNonpartitioned(rel)) {
return;
}
/* 此代码片段定义了一个名为GetFakeRelAndPart的函数
EState对象Relation对象TupleTableSlot对象和FakeRelationPartition对象作为输入参数
FakeRelationPartition对象的partRelpart和partOid属性分别初始化为NULL和InvalidOid
Relation对象是非分区的
*/
}
Relation partRelation = NULL;
Partition partition = NULL;
Oid partitionOid;
@ -932,13 +861,3 @@ void GetFakeRelAndPart(EState *estate, Relation rel, TupleTableSlot *slot, FakeR
break;
}
}
/* 此代码段继续实现“GetFakeElAndPart”函数。
partRelationpartitionpartitionOid
使tableam_tslot_get_tuple_from_slotTupleTableSlot
switch语句Relationparttype
switch语句
使heapTupleGetPartitionIdpartitionOid
searchFakeRetreationForPartitionOidpartitionOid
partRelationpartitionFakeRelationPartitionpartRelpartpartOid
使partitionOid使subPartOid
Relationparttype使ereport */

View File

@ -1,13 +1,15 @@
/* -------------------------------------------------------------------------
*
* execScan.cpp
* 广ExecScan被传递一个节点和一个指向函数的指针
*
* ExecScan然后做一些乏味的工作
* This code provides support for generalized relation scans. ExecScan
* is passed a node and a pointer to a function to "do the right thing"
* and return a tuple from the relation. ExecScan then does the tedious
* stuff - checking the qualification and projecting the tuple
* appropriately.
*
* c2020
* c1996-2012PostgreSQL
* c1994
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
@ -23,30 +25,21 @@
#include "utils/memutils.h"
/*
* ExecScanFetch --
* ExecScanFetch -- fetch next potential tuple
*
* EvalPlanQual复查中
* 访
* This routine is concerned with substituting a test tuple if we are
* inside an EvalPlanQual recheck. If we aren't, just execute
* the access method's next-tuple routine.
*/
static TupleTableSlot* ExecScanFetch(ScanState* node, ExecScanAccessMtd access_mtd, ExecScanRecheckMtd recheck_mtd)
/*
* ScanState对象
* access_mtd和recheck_mtd
* access_mtd是指向负责访问元组数据的函数的指针
* 访
* recheck_mtd是指向一个函数的指针
*
*
* TupleTableSlot对象NULL
*/
{
EState* estate = node->ps.state;
if (estate->es_epqTuple != NULL) {
/*
* EvalPlanQual复查
* 访
* We are inside an EvalPlanQual recheck. Return the test tuple if
* one is available, after rechecking any access-method-specific
* conditions.
*/
Index scan_rel_id = ((Scan*)node->ps.plan)->scanrelid;
@ -54,29 +47,29 @@ static TupleTableSlot* ExecScanFetch(ScanState* node, ExecScanAccessMtd access_m
if (estate->es_epqTupleSet[scan_rel_id - 1]) {
TupleTableSlot* slot = node->ss_ScanTupleSlot;
/* 如果我们已经返回了元组,则返回空槽 */
/* Return empty slot if we already returned a tuple */
if (estate->es_epqScanDone[scan_rel_id - 1])
return ExecClearTuple(slot);
/* 否则请记住,我们不应该再回来了 */
return ExecClearTuple(slot);
/* Else mark to remember that we shouldn't return more */
estate->es_epqScanDone[scan_rel_id - 1] = true;
/* 如果我们没有测试元组,则返回空槽 */
/* Return empty slot if we haven't got a test tuple */
if (estate->es_epqTuple[scan_rel_id - 1] == NULL)
return ExecClearTuple(slot);
/* 将测试元组存储在计划节点的扫描槽中 */
/* Store test tuple in the plan node's scan slot */
(void)ExecStoreTuple(estate->es_epqTuple[scan_rel_id - 1], slot, InvalidBuffer, false);
/* 检查是否符合访问方法条件 */
/* Check if it meets the access-method conditions */
if (!(*recheck_mtd)(node, slot))
(void)ExecClearTuple(slot); /* 不会通过扫描返回 */
(void)ExecClearTuple(slot); /* would not be returned by scan */
return slot;
}
}
/*
* 访
* Run the node-type-specific access method function to get the next tuple
*/
return (*access_mtd)(node);
}
@ -84,28 +77,27 @@ static TupleTableSlot* ExecScanFetch(ScanState* node, ExecScanAccessMtd access_m
/* ----------------------------------------------------------------
* ExecScan
*
* 使访ExecDirection中指定的方向返回下一个符合条件的元组
* access方法返回下一个元组execScanqual子句检查返回的元组
* Scans the relation using the 'access method' indicated and
* returns the next qualifying tuple in the direction specified
* in the global variable ExecDirection.
* The access method returns the next tuple and execScan() is
* responsible for checking the tuple returned against the qual-clause.
*
* 访qual条件检查关系的任意元组
* A 'recheck method' must also be provided that can check an
* arbitrary tuple of the relation against any qual conditions
* that are implemented internal to the access method.
*
* :
* -- AMI维护的
* Conditions:
* -- the "cursor" maintained by the AMI is positioned at the tuple
* returned previously.
*
* :
* -- 便
* Initial States:
* -- the relation indicated is opened for scanning so that the
* "cursor" is positioned before the first qualifying tuple.
* ----------------------------------------------------------------
*/
TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* 返回元组的函数 */
TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* function returning a tuple */
ExecScanRecheckMtd recheck_mtd)
/* 此函数负责扫描关系并返回下一个匹配的元组。它接受一个ScanState对象该对象包含有关扫描的信息以及两个函数指针access_mtd和recheck_mtd。
* access_mtd是指向负责访问元组数据的函数的指针
* 访
* recheck_mtd是指向一个函数的指针
*
* TupleTableSlot对象NULL
*/
{
ExprContext* econtext = NULL;
List* qual = NIL;
@ -117,14 +109,15 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* 返
return NULL;
/*
*
* Fetch data from node
*/
qual = node->ps.qual;
proj_info = node->ps.ps_ProjInfo;
econtext = node->ps.ps_ExprContext;
/*
* qual
* If we have neither a qual to check nor a projection to do, just skip
* all the overhead and return the raw scan tuple.
*/
if (qual == NULL && proj_info == NULL) {
ResetExprContext(econtext);
@ -132,36 +125,38 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* 返
}
/*
*
*
* u如果是
* Check to see if we're still projecting out tuples from a previous scan
* tuple (because there is a function-returning-set in the projection
* expressions). If so, try to project another one.
*/
if (node->ps.ps_TupFromTlist) {
Assert(proj_info); /* 如果不投影就不能到达这里 */
Assert(proj_info); /* can't get here if not projecting */
result_slot = ExecProject(proj_info, &is_done);
if (is_done == ExprMultipleResult)
return result_slot;
/* 已完成该源元组... */
/* Done with that source tuple... */
node->ps.ps_TupFromTlist = false;
}
/*
* @hdfs
* 使bu
* isscanfalse为true
* Optimize scan bu using informational constraint.
* if the is_scan_false is true, the iteration is over.
*/
if (node->is_scan_end) {
return NULL;
}
/*
*
*
* Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous tuple cycle. Note this can't happen
* until we're done projecting out tuples from a scan tuple.
*/
ResetExprContext(econtext);
/*
* access方法获取一个元组
* get a tuple from the access method. Loop until we obtain a tuple that
* passes the qualification.
*/
for (;;) {
TupleTableSlot* slot = NULL;
@ -169,11 +164,13 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* 返
CHECK_FOR_INTERRUPTS();
slot = ExecScanFetch(node, access_mtd, recheck_mtd);
/* 刷新qual每个循环 */
/* refresh qual every loop */
qual = node->ps.qual;
/*
* accessMtd返回的槽包含NULL西
* 使tupleDesc
* if the slot returned by the accessMtd contains NULL, then it means
* there is nothing more to scan so we just return an empty slot,
* being careful to use the projection result slot so it has correct
* tupleDesc.
*/
if (TupIsNull(slot) || unlikely(executorEarlyStop())) {
if (proj_info != NULL)
@ -183,28 +180,30 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* 返
}
/*
* expr上下文
* place the current tuple into the expr context
*/
econtext->ecxt_scantuple = slot;
/*
* qual子句
* check that the current tuple satisfies the qual-clause
*
* nil qualqual为nil时调用ExecQual...
* check for non-nil qual here to avoid a function call to ExecQual()
* when the qual is nil ... saves only a few cycles, but they add up
* ...
*/
if (qual == NULL || ExecQual(qual, econtext, false)) {
/*
*
* Found a satisfactory scan tuple.
*/
if (proj_info != NULL) {
/*
*
*
* Form a projection tuple, store it in the result tuple slot
* and return it --- unless we find we can project no tuples
* from this scan tuple, in which case continue scan.
*/
result_slot = ExecProject(proj_info, &is_done);
#ifdef PGXC
/* 复制xcnodeoid如果底层扫描的插槽有一个 */
/* Copy the xcnodeoid if underlying scanned slot has one */
result_slot->tts_xcnodeoid = slot->tts_xcnodeoid;
#endif /* PGXC */
if (is_done != ExprEndResult) {
@ -212,14 +211,15 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* 返
/*
* @hdfs
* 使
* Optimize foreign scan by using informational constraint.
*/
if (IsA(node->ps.plan, ForeignScan)) {
ForeignScan* foreign_scan = (ForeignScan*)(node->ps.plan);
if (foreign_scan->scan.scan_qual_optimized) {
/*
* set is_scan_end值为true
*
* If we find a suitable tuple, set is_scan_end value is true.
* It means that we do not find suitable tuple in the next iteration,
* the iteration is over.
*/
node->is_scan_end = true;
}
@ -228,20 +228,21 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* 返
}
} else {
/*
* 使
* Optimize foreign scan by using informational constraint.
*/
if (IsA(node->ps.plan, ForeignScan)) {
ForeignScan* foreign_scan = (ForeignScan*)(node->ps.plan);
if (foreign_scan->scan.scan_qual_optimized) {
/*
* set is_scan_end值为true
*
* If we find a suitable tuple, set is_scan_end value is true.
* It means that we do not find suitable tuple in the next iteration,
* the iteration is over.
*/
node->is_scan_end = true;
}
}
/*
*
* Here, we aren't projecting, so just return scan tuple.
*/
return slot;
}
@ -249,7 +250,7 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* 返
InstrCountFiltered1(node, 1);
/*
* qual
* Tuple fails qual, so free per-tuple memory and try again.
*/
ResetExprContext(econtext);
}
@ -257,21 +258,23 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* 返
/*
* ExecAssignScanProjectionInfo
*
* Set up projection info for a scan node, if necessary.
*
* tlist与底层元组类型完全匹配
* ps_ProjegInfo设置为NULL
* SELECT*FROM
* tlist
* We can avoid a projection step if the requested tlist exactly matches
* the underlying tuple type. If so, we just set ps_ProjInfo to NULL.
* Note that this case occurs not only for simple "SELECT * FROM ...", but
* also in most cases where there are joins or other processing nodes above
* the scan node, because the planner will preferentially generate a matching
* tlist.
*
* ExecAssignScanType
* ExecAssignScanType must have been called already.
*/
void ExecAssignScanProjectionInfo(ScanState* node)
{
Scan* scan = (Scan*)node->ps.plan;
Index var_no;
/* 仅索引扫描的tlist中的变量应为index_VAR */
/* Vars in an index-only scan's tlist should be INDEX_VAR */
if (IsA(scan, IndexOnlyScan))
var_no = INDEX_VAR;
else
@ -282,35 +285,15 @@ void ExecAssignScanProjectionInfo(ScanState* node)
else
ExecAssignProjectionInfo(&node->ps, node->ss_ScanTupleSlot->tts_tupleDescriptor);
}
/* 函数ExecAssignScanProjectionInfo负责将投影信息分配给ScanState节点。让我们分解代码
ScanState指针作为输入
使Scan*node->ps.plan将ScanState强制转换为Scan节点
var_no
使IsAscanIndexOnlyScanvar_no设置为index_var
var_no设置为scan->scanrelid
使tlist_matches_tupdesc函数检查扫描的目标列表是否与扫描元组槽的元组描述符匹配node->ps.ps_ProjInfo设置为NULL
ExecAssignProjectionInfo使ScanState节点
ScanState节点
*/
/*
* ExecAssignScanProjectionInfoWithVarno
* tlist中的Vars中指定varno
* ExecInitExtensiblePlan调用此函数来初始化投影信息
* ps_ProjegInfo设置为NULL来避免投影步骤SELECT*FROM
* As above, but caller can specify varno expected in Vars in the tlist.
* This function is called by ExecInitExtensiblePlan to initialize projection info.
* Usually the caller provides a targetlist describing the scan tuples, so we can
* avoid a projection step by setting ps_ProjInfo to NULL. Such as "SELECT * FROM ...".
*/
void ExecAssignScanProjectionInfoWithVarno(ScanState* node, Index var_no)
/* 函数ExecAssignScanProjectionInfoWithVarno将ScanState对象和Index变量号作为参数。它用于为具有特定变量编号的扫描节点分配投影信息。
ScanState对象node
var_no变量号
var_no是否有效并且是否在可用变量的范围内
var_no有效
ExecAssignScanProjectionInfoWithVarno负责为具有特定变量号的扫描节点分配投影信息使
*/
{
Scan* scan = (Scan*)node->ps.plan;
@ -327,42 +310,46 @@ bool tlist_matches_tupdesc(PlanState* ps, List* tlist, Index var_no, TupleDesc t
bool has_oid = false;
ListCell* tlist_item = list_head(tlist);
/* 检查tlist属性 */
/* Check the tlist attributes */
for (attr_no = 1; attr_no <= num_attrs; attr_no++) {
Form_pg_attribute att_tup = tup_desc->attrs[attr_no - 1];
Var* var = NULL;
if (tlist_item == NULL)
return false; /* tlist太短 */
return false; /* tlist too short */
var = (Var*)((TargetEntry*)lfirst(tlist_item))->expr;
if (var == NULL || !IsA(var, Var))
return false; /* tlist项不是Var */
/* 如果这些断言失败,计划者就会搞砸 */
return false; /* tlist item not a Var */
/* if these Asserts fail, planner messed up */
Assert(var->varno == var_no);
Assert(var->varlevelsup == 0);
if (var->varattno != attr_no)
return false; /* 发生故障 */
return false; /* out of order */
if (att_tup->attisdropped)
return false; /* 表包含删除的列 */
return false; /* table contains dropped columns */
/*
* Var的类型应该与元组完全匹配mod的列的并集的情况下
* Var可能来自并集之上mod-1Var仍然描述列
* tudesc那样准确
* typmod转换为typmod-1
* Note: usually the Var's type should match the tupdesc exactly, but
* in situations involving unions of columns that have different
* typmods, the Var may have come from above the union and hence have
* typmod -1. This is a legitimate situation since the Var still
* describes the column, just not as exactly as the tupdesc does. We
* could change the planner to prevent it, but it'd then insert
* projection steps just to convert from specific typmod to typmod -1,
* which is pretty silly.
*/
if (var->vartype != att_tup->atttypid || (var->vartypmod != att_tup->atttypmod && var->vartypmod != -1))
return false; /* 类型不匹配 */
return false; /* type mismatch */
tlist_item = lnext(tlist_item);
}
if (tlist_item != NULL)
return false; /* tlist 列表太长 */
return false; /* tlist too long */
/*
* hasoid设置
*
* If the plan context requires a particular hasoid setting, then that has
* to match, too.
*/
if (ExecContextForcesOids(ps, &has_oid) && has_oid != tup_desc->tdhasoid)
return false;
@ -373,16 +360,17 @@ bool tlist_matches_tupdesc(PlanState* ps, List* tlist, Index var_no, TupleDesc t
/*
* ExecScanReScan
*
* 使ExecScanReScan函数中调用
* This must be called within the ReScan function of any plan node type
* that uses ExecScan().
*/
void ExecScanReScan(ScanState* node)
{
EState* estate = node->ps.state;
/* 停止从目标列表中的SRF投影任何元组 */
/* Stop projecting any tuples from SRFs in the targetlist */
node->ps.ps_TupFromTlist = false;
/* 如果我们在EvalPlanQual复查中则重新扫描EvalPlanQual元组 */
/* Rescan EvalPlanQual tuple if we're inside an EvalPlanQual recheck */
if (estate->es_epqScanDone != NULL) {
Index scan_rel_id = ((Scan*)node->ps.plan)->scanrelid;
@ -391,14 +379,3 @@ void ExecScanReScan(ScanState* node)
estate->es_epqScanDone[scan_rel_id - 1] = false;
}
}
/* 函数ExecScanReScan将ScanState对象作为参数用于重置扫描操作的状态以便重新扫描数据。
ScanState对象node
ExecScanReScan功能提供了一种重置扫描操作状态的机制使
使
*/

View File

@ -1,17 +1,20 @@
/* -------------------------------------------------------------------------
*
* execTuples.cpp
* TupleTableSlots的例程
*
* 访使
* Routines dealing with TupleTableSlots. These are used for resource
* management associated with tuples (eg, releasing buffer pins for
* tuples in disk buffers, or freeing the memory occupied by transient
* tuples). Slots also provide access abstraction that lets us implement
* "virtual" tuples to reduce data-copying overhead.
*
*
* FormData_pg_attribute的数组
* getattributeformtuple等
* Routines dealing with the type information for tuples. Currently,
* the type information for a tuple is an array of FormData_pg_attribute.
* This information is needed by routines manipulating tuples
* (getattribute, formtuple, etc.).
*
* c2020
* c1996-2012PostgreSQL全球发展集团
* c1994
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
@ -21,27 +24,27 @@
* INTERFACE ROUTINES
*
* SLOT CREATION/DESTRUCTION
* MakeTupleTableSlot -
* ExecAllocTableSlot -
* ExecResetTupleTable -
* MakeSingleTupleTableSlot -
* ExecDropSingleTupleTableSlot -
* MakeTupleTableSlot - create an empty slot
* ExecAllocTableSlot - create a slot within a tuple table
* ExecResetTupleTable - clear and optionally delete a tuple table
* MakeSingleTupleTableSlot - make a standalone slot, set its descriptor
* ExecDropSingleTupleTableSlot - destroy a standalone slot
*
* SLOT ACCESSORS
* ExecSetSlotDescriptor -
* ExecStoreTuple -
* ExecStoreMinimalTuple -
* ExecClearTuple -
* ExecStoreVirtualTuple - slot标记为包含虚拟元组
* ExecCopySlotTuple -
* ExecCopySlotMinimalTuple -
* ExecMaterializeSlot -
* ExecCopySlot -
* ExecSetSlotDescriptor - set a slot's tuple descriptor
* ExecStoreTuple - store a physical tuple in the slot
* ExecStoreMinimalTuple - store a minimal physical tuple in the slot
* ExecClearTuple - clear contents of a slot
* ExecStoreVirtualTuple - mark slot as containing a virtual tuple
* ExecCopySlotTuple - build a physical tuple from a slot
* ExecCopySlotMinimalTuple - build a minimal physical tuple from a slot
* ExecMaterializeSlot - convert virtual to physical storage
* ExecCopySlot - copy one slot's contents to another
*
* CONVENIENCE INITIALIZATION ROUTINES (便)
* ExecInitResultTupleSlot \ convenience routines to initialize (便)
* ExecInitScanTupleSlot \ the various tuple slots for nodes ()
* ExecInitExtraTupleSlot / which store copies of tuples. ()
* CONVENIENCE INITIALIZATION ROUTINES
* ExecInitResultTupleSlot \ convenience routines to initialize
* ExecInitScanTupleSlot \ the various tuple slots for nodes
* ExecInitExtraTupleSlot / which store copies of tuples.
* ExecInitNullTupleSlot /
*
* Routines that probably belong somewhere else:
@ -100,7 +103,7 @@
static TupleDesc ExecTypeFromTLInternal(List* target_list, bool has_oid, bool skip_junk, bool mark_dropped = false, TableAmType tam = TAM_HEAP);
/* ----------------------------------------------------------------
* tuple table create/delete functions (/)
* tuple table create/delete functions
* ----------------------------------------------------------------
*/
/* --------------------------------
@ -110,10 +113,6 @@ static TupleDesc ExecTypeFromTLInternal(List* target_list, bool has_oid, bool sk
* --------------------------------
*/
TupleTableSlot* MakeTupleTableSlot(bool has_tuple_mcxt, TableAmType tupslotTableAm)
/* 它创建一个新的TupleTableSlot对象并返回一个指向它的指针。
has_tuple_mcxt
tupslotTableAm访TAM_HEAP
*/
{
TupleTableSlot* slot = makeNode(TupleTableSlot);
Assert(tupslotTableAm == TAM_HEAP || tupslotTableAm == TAM_USTORE);
@ -150,7 +149,7 @@ TupleTableSlot* MakeTupleTableSlot(bool has_tuple_mcxt, TableAmType tupslotTable
/* --------------------------------
* ExecAllocTableSlot
*
* Create a tuple table slot within a tuple table (which is just a List). //在元组表(它只是一个列表)中创建一个元组表槽。
* Create a tuple table slot within a tuple table (which is just a List).
* --------------------------------
*/
TupleTableSlot* ExecAllocTableSlot(List** tuple_table, TableAmType tupslotTableAm)
@ -165,37 +164,32 @@ TupleTableSlot* ExecAllocTableSlot(List** tuple_table, TableAmType tupslotTableA
return slot;
}
/*它将指向TupleTableSlot对象列表的指针和TableAmType参数作为输入。它返回一个指向新分配的TupleTableSlot对象的指针。
MakeTupleTableSlotTupleTableSlot对象slot
使lappendtuple_table
tts_tupslotTableAmtupslotTableAm
TupleTableSlot对象的指针
*/
/* --------------------------------
* ExecResetTupleTable
*
*
*
* EndPlan
* This releases any resources (buffer pins, tupdesc refcounts)
* held by the tuple table, and optionally releases the memory
* occupied by the tuple table data structure.
* It is expected that this routine be called by EndPlan().
* --------------------------------
*/
void ExecResetTupleTable(List* tuple_table, /* tuple table */
bool should_free) /* true ,如果我们应该释放内存 */
bool should_free) /* true if we should free memory */
{
ListCell* lc = NULL;
foreach (lc, tuple_table) {
TupleTableSlot* slot = (TupleTableSlot*)lfirst(lc);
/* 始终释放资源并将插槽重置为空*/
/* Always release resources and reset the slot to empty */
(void)ExecClearTuple(slot);
if (slot->tts_tupleDescriptor) {
ReleaseTupleDesc(slot->tts_tupleDescriptor);
slot->tts_tupleDescriptor = NULL;
}
/* 如果应该释放,释放插槽本身占用的内存 */
/* If shouldFree, release memory occupied by the slot itself */
if (should_free) {
if (slot->tts_values)
pfree_ext(slot->tts_values);
@ -208,33 +202,13 @@ void ExecResetTupleTable(List* tuple_table, /* tuple table */
}
}
/* 如果应该释放,则释放列表结构 */
/* If shouldFree, release the list structure */
if (should_free) {
list_free_ext(tuple_table);
}
}
/* 这是一个用于重置元组表的函数。
* tuple_tableshould_free
* TupleTableSlot类型的指针
* ExecClearTuple函数来释放资源并将槽slot
* slottts_tupleDescriptor字段不为空ReleaseTupleDesc函数释放该字段指向的TupleDesc结构体
* should_free为真slot
* tts_valuestts_isnull和tts_lobPointers字段指向的内存tts_per_tuple_mcxt字段指向的内存上下文
* slot
* should_free为真
*/
TupleTableSlot* ExecMakeTupleSlot(Tuple tuple, TableScanDesc tableScan, TupleTableSlot* slot, TableAmType tableAm)
/* 这是一个用于创建TupleTableSlot的函数。它接受一个Tuple类型的参数tuple一个TableScanDesc类型的参数tableScan
TupleTableSlot类型的参数slotTableAmType类型的参数tableAm
TupleTableSlot
TupleTableSlot对象slot
tuple赋值给slot的tts_tuple字段
tableScan赋值给slot的tts_tableScan字段
tableAm赋值给slot的tts_tableAm字段访
TupleTableSlot对象
*/
{
if (unlikely(RELATION_CREATE_BUCKET(tableScan->rs_rd))) {
tableScan = ((HBktTblScanDesc)tableScan)->currBktScan;
@ -251,24 +225,14 @@ TupleTableSlot* ExecMakeTupleSlot(Tuple tuple, TableScanDesc tableScan, TupleTab
return ExecClearTuple(slot);
}
/* 这段代码是一个用于创建TupleTableSlot的函数。它接受一个Tuple类型的参数tuple
TableScanDesc类型的参数tableScanTupleTableSlot类型的参数slotTableAmType类型的参数tableAm
tableScan指向当前桶扫描描述符
tuple是否为NULLNULL
tableScan不为NULL
tableAm赋值给slot的tts_tupslotTableAm字段访
ExecStoreTuple函数tuple存储到slot中使tableScan->rs_cbuf指定的缓冲区
TupleTableSlot对象
tuple为NULLExecClearTuple函数slot清空TupleTableSlot对象
*/
/* --------------------------------
* MakeSingleTupleTableSlot
*
* 便TupleTableSlot而不是从主执行器元组表中获得的操作
* 使
* This is a convenience routine for operations that need a
* standalone TupleTableSlot not gotten from the main executor
* tuple table. It makes a single slot and initializes it
* to use the given tuple descriptor.
* --------------------------------
*/
TupleTableSlot* MakeSingleTupleTableSlot(TupleDesc tup_desc, bool allocSlotCxt, TableAmType tupslotTableAm)
@ -281,13 +245,13 @@ TupleTableSlot* MakeSingleTupleTableSlot(TupleDesc tup_desc, bool allocSlotCxt,
/* --------------------------------
* ExecDropSingleTupleTableSlot
*
* Release a TupleTableSlot made with MakeSingleTupleTableSlot.(MakeSingleTupleTableSlot制作的TupleTableSlot)
* DON'T use this on a slot that's part of a tuple table list! (使)
* Release a TupleTableSlot made with MakeSingleTupleTableSlot.
* DON'T use this on a slot that's part of a tuple table list!
* --------------------------------
*/
void ExecDropSingleTupleTableSlot(TupleTableSlot* slot)
{
/* This should match ExecResetTupleTable's processing of one slot(这应该与ExecResetTupleTable对一个插槽的处理相匹配) */
/* This should match ExecResetTupleTable's processing of one slot */
(void)ExecClearTuple(slot);
if (slot->tts_tupleDescriptor != NULL) {
ReleaseTupleDesc(slot->tts_tupleDescriptor);
@ -308,16 +272,6 @@ void ExecDropSingleTupleTableSlot(TupleTableSlot* slot)
}
pfree_ext(slot);
}
/* 这段代码是用于释放一个单独的TupleTableSlot的资源。下面是对代码的逐行解释
(void)ExecClearTuple(slot);slot中的tuple数据
if (slot->tts_tupleDescriptor != NULL) { ReleaseTupleDesc(slot->tts_tupleDescriptor); }slot中的tuple描述符
if (slot->tts_values != NULL) { pfree_ext(slot->tts_values); }slot中的tuple值数组
if (slot->tts_isnull != NULL) { pfree_ext(slot->tts_isnull); }slot中的null标志数组
pfree_ext(slot->tts_lobPointers);slot中的LOB指针
if (slot->tts_per_tuple_mcxt != NULL) { MemoryContextDelete(slot->tts_per_tuple_mcxt); }slot中的内存上下文
pfree_ext(slot);slot本身的内存
TupleTableSlot所占用的资源
*/
/* ----------------------------------------------------------------
* tuple table slot accessor functions
@ -331,26 +285,23 @@ pfree_ext(slot);释放slot本身的内存。
* at least equal to the slot's. If it is a reference-counted descriptor
* then the reference count is incremented for as long as the slot holds
* a reference.
* (
* 寿寿
* )
* --------------------------------
*/
void ExecSetSlotDescriptor(TupleTableSlot* slot, /* 要更改的插槽 */
TupleDesc tup_desc) /* 新元组描述符 */
void ExecSetSlotDescriptor(TupleTableSlot* slot, /* slot to change */
TupleDesc tup_desc) /* new tuple descriptor */
{
/*为了安全起见,在更换插槽之前,请确保插槽为空*/
/* For safety, make sure slot is empty before changing it */
(void)ExecClearTuple(slot);
/*
* Datum/isull数组
*使
* Release any old descriptor. Also release old Datum/isnull arrays if
* present (we don't bother to check if they could be re-used).
*/
if (slot->tts_tupleDescriptor != NULL) {
ReleaseTupleDesc(slot->tts_tupleDescriptor);
}
#ifdef PGXC
/* XXX there in no routine to release AttInMetadata instance(XXX没有发布AttInMetadata实例的例程) */
/* XXX there in no routine to release AttInMetadata instance */
if (slot->tts_attinmeta != NULL) {
slot->tts_attinmeta = NULL;
}
@ -364,47 +315,35 @@ void ExecSetSlotDescriptor(TupleTableSlot* slot, /* 要更改的插槽 */
}
pfree_ext(slot->tts_lobPointers);
/*
*
* Install the new descriptor; if it's refcounted, bump its refcount.
*/
slot->tts_tupleDescriptor = tup_desc;
PinTupleDesc(tup_desc);
/*
*
* Allocate Datum/isnull arrays of the appropriate size. These must have
* the same lifetime as the slot, so allocate in the slot's own context.
*/
slot->tts_values = (Datum*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(Datum));
slot->tts_isnull = (bool*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(bool));
slot->tts_lobPointers = (Datum*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(Datum));
}
/*这段代码用于设置TupleTableSlot的描述符descriptor。下面是对代码的逐行解释
(void)ExecClearTuple(slot);slot中的tuple数据slot为空
if (slot->tts_tupleDescriptor != NULL) { ReleaseTupleDesc(slot->tts_tupleDescriptor); }slot中的旧的tuple描述符
#ifdef PGXC ... #endif这部分代码是针对特定的条件编译可能与特定的PostgreSQL扩展相关我们暂时不考虑它的作用。
if (slot->tts_values != NULL) { pfree_ext(slot->tts_values); }slot中的旧的tuple值数组
if (slot->tts_isnull != NULL) { pfree_ext(slot->tts_isnull); }slot中的旧的null标志数组
pfree_ext(slot->tts_lobPointers);slot中的旧的LOB指针
slot->tts_tupleDescriptor = tup_desc;tuple描述符赋值给slot
PinTupleDesc(tup_desc);tuple描述符的引用计数
slot->tts_values = (Datum*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(Datum));slot的内存上下文中分配新的tuple值数组
slot->tts_isnull = (bool*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(bool));slot的内存上下文中分配新的null标志数组
slot->tts_lobPointers = (Datum*)MemoryContextAlloc(slot->tts_mcxt, tup_desc->natts * sizeof(Datum));slot的内存上下文中分配新的LOB指针数组
TupleTableSlot的描述符slot
*/
/* --------------------------------
* ExecStoreTuple
*
* This function is used to store a physical tuple into a specified
* slot in the tuple table.()
* slot in the tuple table.
*
* tuple: tuple to store()
* slot: slot to store it in()
* buffer: disk buffer if tuple is in a disk page, else InvalidBuffer(InvalidBuffer)
* tuple: tuple to store
* slot: slot to store it in
* buffer: disk buffer if tuple is in a disk page, else InvalidBuffer
* shouldFree: true if ExecClearTuple should pfree_ext() the tuple
* when done with it ExecClearTuple在处理完元组后应该pfree_exttrue
* when done with it
*
* bufferInvalidBufferpin
* pin将一直保留到插槽被清除
* If 'buffer' is not InvalidBuffer, the tuple table code acquires a pin
* on the buffer which is held until the slot is cleared, so that the tuple
* won't go away on us.
*
* shouldFree is normally set 'true' for tuples constructed on-the-fly.
* It must always be 'false' for tuples that are stored in disk pages,
@ -431,7 +370,7 @@ void ExecSetSlotDescriptor(TupleTableSlot* slot, /* 要更改的插槽 */
TupleTableSlot* ExecStoreTuple(Tuple tuple, TupleTableSlot* slot, Buffer buffer, bool should_free)
{
/*
* sanity checks ()
* sanity checks
*/
Assert(tuple != NULL);
Assert(slot != NULL);
@ -448,72 +387,49 @@ TupleTableSlot* ExecStoreTuple(Tuple tuple, TupleTableSlot* slot, Buffer buffer,
return slot;
}
/*
Tuple存储到TupleTableSlot中
Assert(tuple != NULL);tuple不为空
Assert(slot != NULL);slot不为空
Assert(slot->tts_tupleDescriptor != NULL);slot的tuple描述符不为空
HeapTuple htup = (HeapTuple)tuple;tuple强制转换为HeapTuple类型htup
if (slot->tts_tupslotTableAm == TAM_USTORE && htup->tupTableType == HEAP_TUPLE)slot的存储类型是UStorehtup的表类型是Heap Tuple
tuple = (Tuple)HeapToUHeap(slot->tts_tupleDescriptor, (HeapTuple)tuple);Heap Tuple转换为UHeap Tuple
else if (slot->tts_tupslotTableAm == TAM_HEAP && htup->tupTableType == UHEAP_TUPLE)slot的存储类型是Heaphtup的表类型是UHeap Tuple
tuple = (Tuple)UHeapToHeap(slot->tts_tupleDescriptor, (UHeapTuple)tuple);UHeap Tuple转换为Heap Tuple
tableam_tslot_store_tuple(tuple, slot, buffer, should_free, false);tableam_tslot_store_tuple函数将tuple存储到slot中
return slot;tuple的slot
Tuple存储到TupleTableSlot中tuple的slot
*/
/* --------------------------------
* ExecStoreMinimalTuple
*
* Like ExecStoreTuple, but insert a "minimal" tuple into the slot. (ExecStoreTuple类似)
* Like ExecStoreTuple, but insert a "minimal" tuple into the slot.
*
* No 'buffer' parameter since minimal tuples are never stored in relations. (buffer)
* No 'buffer' parameter since minimal tuples are never stored in relations.
* --------------------------------
*/
TupleTableSlot* ExecStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot* slot, bool should_free)
{
/*
*
* sanity checks
*/
Assert(mtup != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
/*
*
* store the minimal tuple in the slot.
*/
tableam_tslot_store_minimal_tuple(mtup, slot, should_free);
return slot;
}
/*
MinimalTuple存储到TupleTableSlot中
Assert(mtup != NULL);MinimalTuple不为空
Assert(slot != NULL);slot不为空
Assert(slot->tts_tupleDescriptor != NULL);slot的tuple描述符不为空
tableam_tslot_store_minimal_tuple(mtup, slot, should_free);tableam_tslot_store_minimal_tuple函数将MinimalTuple存储到slot中
return slot;MinimalTuple的slot
MinimalTuple存储到TupleTableSlot中MinimalTuple的slot
*/
/* --------------------------------
* ExecClearTuple
*
* This function is used to clear out a slot in the tuple table.()
* This function is used to clear out a slot in the tuple table.
*
* NB: only the tuple is cleared, not the tuple descriptor (if any). ()
* NB: only the tuple is cleared, not the tuple descriptor (if any).
* --------------------------------
*/
TupleTableSlot* ExecClearTuple(TupleTableSlot* slot) /* returnslot-passed存储元组的slot*/
TupleTableSlot* ExecClearTuple(TupleTableSlot* slot) /* return: slot passed slot in which to store tuple */
{
/*
*
* sanity checks
*/
Assert(slot != NULL);
/*
* TableAm清除物理元组或最小元组
* clear the physical tuple or minimal tuple if present via TableAm.
*/
if (slot->tts_shouldFree || slot->tts_shouldFreeMin) {
Assert(slot->tts_tupleDescriptor != NULL);
@ -521,7 +437,7 @@ TupleTableSlot* ExecClearTuple(TupleTableSlot* slot) /* returnslot-passed存
}
/*
*tts_shouldFree为falsetts_tuple可能仍然有效
* tts_tuple may still be valid if tts_shouldFree is false, Original caller doesn't want this slot to free the tuple.
*/
slot->tts_tuple = NULL;
slot->tts_mintuple = NULL;
@ -539,7 +455,7 @@ TupleTableSlot* ExecClearTuple(TupleTableSlot* slot) /* returnslot-passed存
#endif
/*
*
* Drop the pin on the referenced buffer, if there is one.
*/
if (BufferIsValid(slot->tts_buffer)) {
ReleaseBuffer(slot->tts_buffer);
@ -547,42 +463,19 @@ TupleTableSlot* ExecClearTuple(TupleTableSlot* slot) /* returnslot-passed存
slot->tts_buffer = InvalidBuffer;
/*
*
* Mark it empty.
*/
slot->tts_isempty = true;
slot->tts_nvalid = 0;
//在某些情况下行解压缩使用slot->tts_per_tuple_mcxt
//因此我们需要重置内存上下文。此内存上下文由PGXC引入
// 仅在函数“slot_form_datarow”中使用。PGXC也在函数“FetchTuple”中进行重置。
// 所以它是安全的
//
// Row uncompression use slot->tts_per_tuple_mcxt in some case, So we need
// reset memory context. This memory context is introduced by PGXC and it only used
// in function 'slot_deform_datarow'. PGXC also do reset in function 'FetchTuple'.
// So it is safe
//
ResetSlotPerTupleContext(slot);
return slot;
}
/*
TupleTableSlot中的tuple
Assert(slot != NULL);slot不为空
if (slot->tts_shouldFree || slot->tts_shouldFreeMin)slot中的tuple需要释放
Assert(slot->tts_tupleDescriptor != NULL);slot的tuple描述符不为空
tableam_tslot_clear(slot);TableAm清除物理tuple或最小tuple
slot->tts_tuple = NULL;slot中的tuple置为NULL
slot->tts_mintuple = NULL;slot中的最小tuple置为NULL
slot->tts_shouldFree = false;slot的tts_shouldFree标志置为falsetuple
slot->tts_shouldFreeMin = false;slot的tts_shouldFreeMin标志置为falsetuple
if (slot->tts_shouldFreeRow) { pfree_ext(slot->tts_dataRow); }slot的tts_shouldFreeRow标志为trueslot中的数据行
slot->tts_shouldFreeRow = false;slot的tts_shouldFreeRow标志置为false
slot->tts_dataRow = NULL;slot中的数据行置为NULL
slot->tts_dataLen = -1;slot中的数据长度置为-1
slot->tts_xcnodeoid = 0;slot的tts_xcnodeoid置为0
if (BufferIsValid(slot->tts_buffer)) { ReleaseBuffer(slot->tts_buffer); }slot中的buffer有效buffer
slot->tts_buffer = InvalidBuffer;slot的buffer置为无效
slot->tts_isempty = true;slot的isempty标志置为trueslot为空
slot->tts_nvalid = 0;slot的nvalid置为0tuple数量为0
ResetSlotPerTupleContext(slot);slot的tts_per_tuple_mcxt内存上下文
return slot;tuple的slot
TupleTableSlot中的tupleslot重置为空tuple的slot
*/
/* --------------------------------
* ExecStoreVirtualTuple
@ -608,22 +501,13 @@ TupleTableSlot* ExecStoreVirtualTuple(TupleTableSlot* slot)
slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
if (slot->tts_tupslotTableAm != slot->tts_tupleDescriptor->tdTableAmType) {
// XXX: 如果tts_tupleDescriptor在更改其内容之前进行克隆
//则它可以直接引用RelationData中的rd_att。
// XXX: Should tts_tupleDescriptor be cloned before changing its contents
// as some time it can be direct reference to the rd_att in RelationData.
slot->tts_tupleDescriptor->tdTableAmType = slot->tts_tupslotTableAm;
}
return slot;
}
/*
ExecStoreVirtualTuple用于将虚拟元组存储在元组表槽中
TupleTableSlot指针作为输入
slot是否不为NULLslot->tts_tupleDescriptorslot中的元组的描述符NULLslot->tss_isempty是否为true
slot->tts_isempty设置为falseslot不再为空
slot->tts_nvalid设置为元组描述符slot->ttleStupleDescriptor->natts
slot->tts_tupslotTableAmslot的表访问方法slot->ttleStupleDescriptor->tdTableAmType访slot->tts_tupleDescriptor- >tdTableAmType以匹配slot->ts_tupslotTableAm
.
*/
/* --------------------------------
* ExecStoreAllNullTuple
@ -641,11 +525,11 @@ TupleTableSlot* ExecStoreAllNullTuple(TupleTableSlot* slot)
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
/* 清除所有旧内容 */
/* Clear any old contents */
(void)ExecClearTuple(slot);
/*
* null填充虚拟元组的所有列
* Fill all the columns of the virtual tuple with nulls
*/
errno_t rc = EOK;
@ -662,16 +546,6 @@ TupleTableSlot* ExecStoreAllNullTuple(TupleTableSlot* slot)
return ExecStoreVirtualTuple(slot);
}
/*
ExecStoreAllNullTuple用于将具有所有null值的虚拟元组存储在元组表槽中
TupleTableSlot指针作为输入
slot是否为NULLslot->tts_tupleDescriptorslot中的元组的描述符NULL
ExecClearTuple来清除插槽中的任何现有内容
使memset_s函数用null值填充插槽的tts_values数组tts_values数组设置为全零slot->tts_tupleDescriptor->natts*sizeofDatum
使memset_s用真值填充插槽的tts_isull数组nulltruenull
使ExecStoreVirtualTuple的结果
null值的虚拟元组
*/
/* --------------------------------
* ExecCopySlotTuple
@ -686,21 +560,13 @@ TupleTableSlot* ExecStoreAllNullTuple(TupleTableSlot* slot)
HeapTuple ExecCopySlotTuple(TupleTableSlot* slot)
{
/*
* sanity checks ()
* sanity checks
*/
Assert(slot != NULL);
Assert(!slot->tts_isempty);
return tableam_tslot_copy_heap_tuple(slot);
}
/*
ExecCopySlotTuple用于创建存储在元组表槽中的元组的副本
TupleTableSlot指针作为输入
slot是否不为NULLslot->tts_isempty是否为false
tableam_tslot_copy_heap_tuple来创建存储在插槽中的元组的副本
HeapTuple对象返回
*/
/* --------------------------------
* ExecCopySlotMinimalTuple
@ -719,13 +585,6 @@ MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot* slot, bool need_transform_
return tableam_tslot_copy_minimal_tuple(slot);
}
/*
ExecCopySlotMinimalTuple用于创建存储在元组表槽中的最小元组的副本
TupleTableSlot指针作为输入
slot是否不为NULLslot->tts_isempty是否为false
tableam_tslot_copy_minimaltuple来创建存储在插槽中的最小元组的副本
MinimalTuple对象返回
*/
/* --------------------------------
* ExecFetchSlotTuple
@ -752,13 +611,6 @@ HeapTuple ExecFetchSlotTuple(TupleTableSlot* slot)
return tableam_tslot_get_heap_tuple(slot);
}
/*
ExecFetchSlotTuple用于从元组表槽中检索堆元组
TupleTableSlot指针作为输入
slot是否不为NULLslot->tts_isempty是否为false
tableam_tslot_get_heap_tuple从插槽中检索堆元组
HeapTuple对象返回
*/
/* --------------------------------
* ExecFetchSlotMinimalTuple
@ -782,13 +634,7 @@ MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot* slot)
return tableam_tslot_get_minimal_tuple(slot);
}
/*
ExecFetchSlotMinimalTuple用于从元组表槽中检索最小元组
TupleTableSlot指针作为输入
slot是否不为NULL
tableam_tslot_get_minimaltuple从插槽中检索最小元组
MinimalTuple对象返回
*/
/* --------------------------------
* ExecFetchSlotTupleDatum
* Fetch the slot's tuple as a composite-type Datum.
@ -804,9 +650,9 @@ Datum ExecFetchSlotTupleDatum(TupleTableSlot* slot)
HeapTupleHeader td;
TupleDesc tup_desc;
/* Make sure we can scribble on the slot contents ... (确保我们可以在插槽内容上乱写) */
/* Make sure we can scribble on the slot contents ... */
tup = ExecMaterializeSlot(slot);
/* ... and set up the composite-Datum header fields, in case not done(并设置复合基准标题字段,如果未完成) */
/* ... and set up the composite-Datum header fields, in case not done */
td = tup->t_data;
tup_desc = slot->tts_tupleDescriptor;
HeapTupleHeaderSetDatumLength(td, tup->t_len);
@ -814,17 +660,6 @@ Datum ExecFetchSlotTupleDatum(TupleTableSlot* slot)
HeapTupleHeaderSetTypMod(td, tup_desc->tdtypmod);
return PointerGetDatum(td);
}
/*
ExecFetchSlotTupleDatum用于从元组表槽中检索表示元组的Datum
HeapTuple的变量tupHeapStupleHeader的变量td和类型为TupleDesc的变量tup_desc
ExecMaterializeSlot来实现槽Datum将是指向元组标头的指针访
tup->t_datatd变量
slot->tts_tupleDescriptortup_desc变量
使HeapTupleHeaderSetDatumLengthHeapTuppleHeaderSetTypeId和HeapTupleHeaderSetTypMod宏
tup_descID和类型修饰符td
PointerGetDatumtdtdDatum表示
*/
/* --------------------------------
* ExecMaterializeSlot
@ -849,14 +684,6 @@ HeapTuple ExecMaterializeSlot(TupleTableSlot* slot)
return tableam_tslot_materialize(slot);
}
/*
ExecMaterializeSlot用于实体化存储在元组表槽中的元组
TupleTableSlot指针作为输入
slot是否不为NULLslot->tts_isempty是否为false
tableam_tslot_materialize来实现槽中的元组
HeapTuple对象返回
tableam_tslot_materialize使访
*/
/* --------------------------------
* ExecCopySlot
@ -874,8 +701,9 @@ TupleTableSlot* ExecCopySlot(TupleTableSlot* dst_slot, TupleTableSlot* src_slot)
MemoryContext old_context;
/*
*
*
* There might be ways to optimize this when the source is virtual, but
* for now just always build a physical copy. Make sure it is in the
* right context.
*/
old_context = MemoryContextSwitchTo(dst_slot->tts_mcxt);
new_tuple = ExecCopySlotTuple(src_slot);
@ -883,17 +711,6 @@ TupleTableSlot* ExecCopySlot(TupleTableSlot* dst_slot, TupleTableSlot* src_slot)
return ExecStoreTuple(new_tuple, dst_slot, InvalidBuffer, true);
}
/*
ExecCopySlot用于创建存储在源元组表槽中的元组的副本
TupleTableSlot指针作为输入dst_slotsrc_slot
HeapTuple的变量new_tuple和类型为MemoryContext的变量old_context
使MemoryContext SwitchTo将内存上下文切换到目标插槽的内存上下文
ExecCopySlotTuple来创建存储在源槽中的元组的副本
使MemoryContext SwitchToold_context
ExecStoreTuple将复制的元组存储在目标槽中new_tupledst_slotInvalidBuffertrue
*/
/* ----------------------------------------------------------------
* convenience initialization routines
@ -924,17 +741,6 @@ void ExecInitScanTupleSlot(EState* estate, ScanState* scan_state, TableAmType ta
{
scan_state->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable, tam);
}
/*
ExecInitResultTupleSlot和ExecInitScanTupleSlot分别用于初始化用于存储结果元组和扫描元组的元组槽
ExecInitResultTupleSlot函数
estateplan_state和表示表访问方法类型的tam
使ExecAllocTableSlot函数将plan_state的结果元组槽分配给新分配的表槽使estate->es_tupleTable从estate的元组表中获得的
plan_state->ps_ResultTupleSlot
ExecInitScanTupleSlot函数
estatescan_state和表示表访问方法类型的tam
使ExecAllocTableSlot函数将scan_state的扫描元组槽分配给新分配的表槽使estate->es_tupleTable从estate的元组表中获得的
scan_state->ss_ScanTupleSlot
*/
/* ----------------
* ExecInitExtraTupleSlot
@ -961,13 +767,6 @@ TupleTableSlot* ExecInitNullTupleSlot(EState* estate, TupleDesc tup_type)
return ExecStoreAllNullTuple(slot);
}
/*
ExecInitNullTupleSlot用于使用null元组初始化元组表槽
estate和表示null元组的元组描述符的tup_type
ExecInitTextraTupleSlot来初始化一个额外的元组表槽
使ExecSetSlotDescriptor将元组描述符tup_type分配给插槽
使slot调用ExecStoreAllNullTuple的结果null来用null元组填充槽
*/
/* ----------------------------------------------------------------
* ExecTypeFromTL
@ -1030,20 +829,6 @@ static TupleDesc ExecTypeFromTLInternal(List* target_list, bool has_oid, bool sk
return type_info;
}
/*
ExecCleanTypeFromTL用于从目标列表生成干净的元组描述符
target_listhas_oidoid列的布尔值tam访
使ExecTypeFromTLInternal函数以生成元组描述符target_listhas_oidtruefalsetam访
ExecTypeFromTLInternal函数初始化一些变量type_infoTupleDesc对象len
使CreateTemplateTupleDesc创建模板元组描述符lenhas_oid和tamOID标志的空元组描述符
使foreach循环迭代目标列表中的每个目标条目
skip_junk为trueresjunk为true
使TupleDescInitEntry初始化元组描述符中的一个条目cur_resnoresname
exprTypeNode*ttle->exprtypmodexprTypmodNode*ttle->expr0
mark_dropped为true..pg.dropped.attitdropped设置为true
cur_resno将递增
*/
/*
* ExecTypeFromExprList - build a tuple descriptor from a list of Exprs
@ -1073,18 +858,6 @@ TupleDesc ExecTypeFromExprList(List* expr_list, List* names_list, TableAmType t
return type_info;
}
/*
ExecTypeFromExprList用于从表达式列表和相应的名称列表生成元组描述符
expr_listnames_listtam访
type_infoTupleDesc对象cur_resno
使Assert断言expr_list和names_list的长度相等
使CreateTemplateTupleDesc创建模板元组描述符expr_list的长度false表示元组描述符不应包括OID列tam表示表访问方法类型
使forboth循环并行迭代每个表达式和名称
使TupleDescInitEntry初始化元组描述符中的一个条目cur_resnonexprTypeetypmodexprTypmode0
使TupleDescInitEntryCollation和exprCollatione
cur_resno将递增
*/
/*
* BlessTupleDesc - make a completed tuple descriptor useful for SRFs
@ -1122,16 +895,6 @@ TupleTableSlot* TupleDescGetSlot(TupleDesc tup_desc)
/* Return the slot */
return slot;
}
/*
BlessTupleDesc用于祝福元组描述符TupleDescGetSlot用于根据提供的元组描述符初始化元组表槽
BlessTupleDesc将TupleDesc对象tup_desc作为输入
tup_desc的tdtypeid是RECORDOIDtdtypmod小于0assign_record_type_typmod为记录类型分配一个合适的typmod
tup_desc对象便
TupleDescGetSlot将TupleDesc对象tup_desc作为输入
BlessTupleDesc来祝福元组描述符使
MakeSingleTupleTableSlot
*/
/*
* TupleDescGetAttInMetadata - Build an AttInMetadata structure based on the
@ -1151,18 +914,18 @@ AttInMetadata* TupleDescGetAttInMetadata(TupleDesc tup_desc)
att_in_meta = (AttInMetadata*)palloc(sizeof(AttInMetadata));
/* "Bless" the tupledesc so that we can make rowtype datums with it(“Bless”元组这样我们就可以用它制作行型基准) */
/* "Bless" the tupledesc so that we can make rowtype datums with it */
att_in_meta->tupdesc = BlessTupleDesc(tup_desc);
/*
* Gather info needed later to call the "in" function for each attribute(in)
* Gather info needed later to call the "in" function for each attribute
*/
att_in_func_info = (FmgrInfo*)palloc0(natts * sizeof(FmgrInfo));
att_io_params = (Oid*)palloc0(natts * sizeof(Oid));
att_typ_mods = (int32*)palloc0(natts * sizeof(int32));
for (i = 0; i < natts; i++) {
/* Ignore dropped attributes(忽略丢弃的属性) */
/* Ignore dropped attributes */
if (!tup_desc->attrs[i]->attisdropped) {
att_type_id = tup_desc->attrs[i]->atttypid;
getTypeInputInfo(att_type_id, &att_in_func_id, &att_io_params[i]);
@ -1176,20 +939,6 @@ AttInMetadata* TupleDescGetAttInMetadata(TupleDesc tup_desc)
return att_in_meta;
}
/*
TupleDescGetAttInMetadata用于收集调用元组描述符中每个属性的in
TupleDesc对象tup_desc作为输入
nattsi
使palloc为AttInMetadata对象att_in_meta分配内存
BlessTupleDesc来使
att_in_func_infoatt_io_params和att_typ_mod
使
attidrepped为false使getTypeInputInfo收集诸如属性类型IDinID和IO参数等信息
使fmgr_info用inatt_in_func_info数组
IO参数和属性类型mod存储在相应的数组中
att_in_meta对象中的相应字段
att_in_meta对象
*/
/*
* BuildTupleFromCStrings - build a HeapTuple given user data in C string form.
@ -1237,19 +986,6 @@ HeapTuple BuildTupleFromCStrings(AttInMetadata* att_in_meta, char** values)
return tuple;
}
/*
BuildTupleFromCStrings用于从C样式字符串数组中构建HeapTuple
AttInMetadata对象att_in_meta和一个C样式字符串值数组作为输入
att_in_meta对象中提取TupleDesc对象tup_descnatt的数量
d_values和nullsnull标志
使palloc为d_values和null分配内存
使
attitdropped为false使InputFunctionCall为该属性调用in使C样式字符串值转换为基准null来设置null标志
NULLNULL标志设置为true
使tableam_tops_form_tuple形成一个HeapTuplenull标志创建一个新的HeapTuple
使pfree_ext释放为d_values和null分配的内存
HeapTuple
*/
/*
* Functions for sending tuples to the frontend (or other specified destination)
@ -1270,17 +1006,6 @@ TupOutputState* begin_tup_output_tupdesc(DestReceiver* dest, TupleDesc tup_desc)
return tstate;
}
/*
begin_tup_output_tupdesc用于初始化给定目标接收器和元组描述符的元组输出状态
desttup_desc
TupOutputState类型的变量tstate
使palloc为tstate对象分配内存
使tup_desc调用MakeSingleTupleTableSlot来初始化tstate的slot字段
dest参数指定给tstate的dest字段
使*tstate->dest->rStartuprStartup函数
rStartup函数是使用目标接收器CMD_SELECT
tstate对象
*/
/*
* write a single tuple
@ -1313,24 +1038,11 @@ void do_tup_output(TupOutputState* tstate, Datum* values, size_t values_len, con
/* clean up */
(void)ExecClearTuple(slot);
}
/*
do_tup_output用于使用提供的TupOutputState对象将元组输出到目标接收器
tstatevaluesDatum值的数组values_lenvalues数组的长度is_nullnull的布尔值的数组is_null_lenis _null数组的长度
使Assert断言值和is_null数组不为null
TupleTableSlot类型的变量slottstate对象的slot字段
tts_tupleDescriptor字段中获取属性natt的数量
使ExecClearTuple清除插槽
使memcpy_s将值数组复制到插槽的tts_values字段中
使memcpy_s将is_null数组复制到插槽的tts_isull字段中
使ExecStoreVirtualTuple将插槽标记为包含虚拟元组
receiveSlot函数slot和tstate->dest作为参数
使ExecClearTuple再次清除插槽以清除任何剩余数据
*/
/*
* write a chunk of text, breaking at newline characters()
* write a chunk of text, breaking at newline characters
*
* Should only be used with a single-TEXT-attribute tupdesc.(TEXT属性tupdesc一起使用)
* Should only be used with a single-TEXT-attribute tupdesc.
*/
int do_text_output_multiline(TupOutputState* tstate, char* text)
{
@ -1360,20 +1072,6 @@ int do_text_output_multiline(TupOutputState* tstate, char* text)
}
return tuple_count;
}
/*函数do_text_output_multiline用于使用提供的TupOutputState对象将多行文本作为元组输出到目标接收器。
tstatetext
1Datum类型的数组值
bool类型的数组is_null1falsenull
tuple_count0
使strchr搜索换行符'\n'eol指针以指向换行符之后的下一个字符eol设置为指向字符串的末尾
使cstring_to_text_with_len将文本行转换为基准[0]
do_tup_output函数来输出具有值数组is_null数组和提供的tstate的元组
tuple_count变量
使pfree释放为Datum值分配的内存
*/
void end_tup_output(TupOutputState* tstate)
{
@ -1451,28 +1149,3 @@ TupleTableSlot* ExecStoreDataRowTuple(char* msg, size_t len, Oid msgnode_oid, Tu
return slot;
}
#endif
/*
end_tup_output和ExecStoreDataRowTuple
end_tup_output
TupOutputState对象tstate作为输入
使*tstate->dest->rShutdownrShutdownfunction
使ExecDropSingleTupleTableSlot删除单元组表槽
使pfree_ext释放为tstate对象分配的内存
*/
/*
ExecStoreDataRowTuple
DataRow消息格式的缓冲区存储到元组表槽中
msgDataRow消息的缓冲区lenmsgnode_oidoidslotslotshould_free
dataRow
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2947,9 +2947,9 @@ void SetOneOfCompressOption(DefElem* defElem, TableCreateSupport* tableCreateSup
} else if (pg_strcasecmp(defname, "compress_level") == 0) {
tableCreateSupport->compressLevel = true;
} else if (pg_strcasecmp(defname, "compress_byte_convert") == 0) {
tableCreateSupport->compressByteConvert = defGetBoolean(defElem);
tableCreateSupport->compressByteConvert = true;
} else if (pg_strcasecmp(defname, "compress_diff_convert") == 0) {
tableCreateSupport->compressDiffConvert = defGetBoolean(defElem);
tableCreateSupport->compressDiffConvert = true;
}
}

View File

@ -8965,8 +8965,6 @@ void StartupXLOG(void)
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);
NotifyGscRecoveryStarted();
/*
* Initialize WAL insert status array and the flush index - lastWalStatusEntryFlushed.
*/

View File

@ -1818,47 +1818,3 @@ void UHeapSlotStoreUHeapTuple(UHeapTuple utuple, TupleTableSlot *slot, bool shou
/* Mark extracted state invalid */
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;
}

View File

@ -238,19 +238,15 @@ public:
return m_purpose;
}
GcEpochType GcStartInnerTxn()
void GcStartTxnMTtests()
{
m_gcEpoch = GetGlobalEpoch();
return m_gcEpoch;
if (m_gcEpoch != GetGlobalEpoch())
m_gcEpoch = GetGlobalEpoch();
}
void GcEndInnerTxn(bool clean_gc)
void GcEndTxnMTtests()
{
if (clean_gc) {
RunQuicese();
}
m_gcEpoch = 0;
RunQuicese();
}
void GcStartTxn()
@ -276,7 +272,6 @@ public:
RunQuicese();
m_managerLock.unlock();
}
m_gcEpoch = 0;
m_isTxnStarted = false;
}
@ -318,6 +313,7 @@ public:
{
if (m_performGcEpoch != g_gcActiveEpoch)
HardQuiesce(m_rcuFreeCount);
m_gcEpoch = 0;
}
/** @brief Clean all object at the end of the session */

View File

@ -258,15 +258,6 @@ bool Index::IndexInsert(Sentinel*& outputSentinel, const Key* key, uint32_t pid,
outputSentinel = IndexInsertImpl(key, sentinel, inserted, pid);
// sync between rollback/delete and insert
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
if (outputSentinel->RefCountUpdate(INC, pid) == RC_OK)
retryInsert = false;
@ -309,16 +300,8 @@ Sentinel* Index::IndexInsert(const Key* key, Row* row, uint32_t pid)
// no need to report to full error stack
SetLastError(MOT_ERROR_UNIQUE_VIOLATION, MOT_SEVERITY_NORMAL);
m_sentinelPool->Release<Sentinel>(sentinel);
sentinel = nullptr;
return nullptr;
} 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) {
sentinel->SetPrimaryIndex();
sentinel->SetNextPtr(row);
@ -360,6 +343,9 @@ Sentinel* Index::IndexReadHeader(const Key* key, uint32_t pid) const
Sentinel* Index::IndexRemove(const Key* key, uint32_t pid)
{
Sentinel* sentinel = IndexRemoveImpl(key, pid);
MOT_ASSERT(sentinel != nullptr);
MOT_ASSERT(sentinel->GetCounter() == 0);
return sentinel;
}

View File

@ -56,22 +56,12 @@ 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
leaf
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.
Unlocking the node and enter the key into the permutation will be done
later in finish_insert (done in lp.finish function). */
later in finish_insert (called from lp.finish). */
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());
bool found = lp.find_insert(*mtSessionThreadInfo);
// If the key is new (not previously existing) then we record the entry under
// that key

View File

@ -33,15 +33,15 @@ namespace Masstree {
template <typename P>
struct gc_layer_rcu_callback_ng : public P::threadinfo_type::mrcu_callback {
typedef typename P::threadinfo_type threadinfo;
node_base<P>** root_ref_;
node_base<P>* root_;
int len_;
size_t size_;
MOT::MasstreePrimaryIndex* index_;
char s_[0];
gc_layer_rcu_callback_ng(node_base<P>** root_ref, Str prefix, size_t size)
: root_ref_(root_ref), len_(prefix.length()), size_(size), index_(mtSessionThreadInfo->get_working_index())
gc_layer_rcu_callback_ng(node_base<P>* root, Str prefix, size_t size)
: root_(root), len_(prefix.length()), size_(size), index_(mtSessionThreadInfo->get_working_index())
{
errno_t erc = memcpy_s(s_, len_, prefix.data(), len_);
errno_t erc = memcpy_s(s_, size_, prefix.data(), len_);
securec_check(erc, "\0", "\0");
}
size_t operator()(bool drop_index);
@ -51,7 +51,7 @@ struct gc_layer_rcu_callback_ng : public P::threadinfo_type::mrcu_callback {
return size_;
}
static void make(node_base<P>** root_ref, Str prefix, threadinfo& ti);
static void make(node_base<P>* root, Str prefix, threadinfo& ti);
};
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
// more elements into GC)
if (drop_index == false) {
// GC layer remove might delete elements from tree and might create new gc layer removal requests and add them to GC.
// Index must be provided to allow access to the memory pools.
// GC layer remove might delete elements from tree and add them to the limbolist. Index must be provided to
// allow access to the memory pools.
mtSessionThreadInfo->set_working_index(index_);
(*this)(*mtSessionThreadInfo);
mtSessionThreadInfo->set_working_index(NULL);
@ -73,33 +73,30 @@ size_t gc_layer_rcu_callback_ng<P>::operator()(bool drop_index)
template <typename P>
void gc_layer_rcu_callback_ng<P>::operator()(threadinfo& ti)
{
masstree_invariant(root_ref_);
tcursor<P> node_cursor(root_ref_, s_, len_);
bool do_remove = node_cursor.gc_layer(ti);
if (!do_remove || !node_cursor.finish_remove(ti)) {
node_cursor.n_->unlock();
// root_ node while creating gc_layer_rcu_callback_ng might not be the current root. Find updated tree's root.
while (!root_->is_root()) {
root_ = root_->maybe_parent();
}
ti.add_nodes_to_gc();
}
template <typename P>
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>);
// As we are using slab allocator for allocation, sz is will be updated by ti.allocate with the real allocation
// size. We need this size for GC deallocation size report
void* data = ti.allocate(sz, memtag_masstree_gc, &sz /* IN/OUT PARAM */);
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);
// If root was already deleted, do nothing.
if (root_->deleted()) {
return;
}
gc_layer_rcu_callback_ng<P>* cb = new (data) gc_layer_rcu_callback_ng<P>(root_ref, prefix, sz);
tcursor<P> node_cursor(root_, s_, len_);
if (!node_cursor.gc_layer(ti) || !node_cursor.finish_remove(ti)) {
node_cursor.n_->unlock();
}
}
template <typename P>
void gc_layer_rcu_callback_ng<P>::make(node_base<P>* root, Str prefix, threadinfo& ti)
{
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
// size
void* data = ti.allocate(sz, memtag_masstree_gc, &sz /*OUT PARAM*/);
gc_layer_rcu_callback_ng<P>* cb = new (data) gc_layer_rcu_callback_ng<P>(root, prefix, sz);
ti.rcu_register(cb, sz);
}

View File

@ -69,17 +69,14 @@ Sentinel* MasstreePrimaryIndex::IndexInsertImpl(const Key* key, Sentinel* sentin
mtSessionThreadInfo->set_gc_session(
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);
mtSessionThreadInfo->set_gc_session(NULL);
mtSessionThreadInfo->set_working_index(NULL);
if (!inserted && existingItem) { // key mapping already exists in unique index
if (!inserted) { // key mapping already exists in unique index
result = reinterpret_cast<Sentinel*>(existingItem);
} // otherwise return null pointer (if !inserted && !existingItem, Key does not exist and insertation failed due to
// memory issue)
} // otherwise return null pointer
return result;
}
@ -111,8 +108,6 @@ Sentinel* MasstreePrimaryIndex::IndexRemoveImpl(const Key* key, uint32_t pid)
mtSessionThreadInfo->set_gc_session(
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);
mtSessionThreadInfo->set_gc_session(NULL);

View File

@ -37,7 +37,6 @@
#include "masstree/mot_masstree_struct.hpp"
#include "masstree/mot_masstree_iterator.hpp"
#include <cmath>
#include "mot_engine.h"
namespace MOT {
/**
@ -303,38 +302,11 @@ public:
/**
* @brief Print Masstree pools memory consumption details to log.
*/
virtual void PrintPoolsStats(LogLevel level = LogLevel::LL_DEBUG)
virtual void PrintPoolsStats()
{
m_leafsPool->Print("Leafs pool", level);
m_internodesPool->Print("Internode pool", level);
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();
m_leafsPool->Print("Leafs pool: ");
m_internodesPool->Print("Internode pool: ");
m_ksuffixSlab->Print("Ksuffix slab: ");
}
/**
@ -344,8 +316,6 @@ public:
{
m_initialized = false;
DestroyPools();
// remove masstree's root pointer (not valid anymore)
*(m_index.root_ref()) = nullptr;
return IndexInitImpl(NULL);
}
@ -362,7 +332,7 @@ public:
* @param tag Hint to determine which pool to use.
* @return Pointer to allocated memory.
*/
virtual void* AllocateMem(int& size, enum memtag tag)
void* AllocateMem(int& size, enum memtag tag)
{
switch (tag) {
case memtag_masstree_leaf:
@ -390,7 +360,7 @@ public:
* @param Pointer to allocated memory.
* @return True if deallocation succeeded.
*/
virtual bool DeallocateMem(void* ptr, int size, enum memtag tag)
bool DeallocateMem(void* ptr, int size, enum memtag tag)
{
switch (tag) {
case memtag_masstree_leaf:
@ -461,16 +431,10 @@ public:
{
// If dropIndex == true, all index's pools are going to be cleaned, so we skip the release here
mtSessionThreadInfo->set_gc_session(GetCurrentGcSession());
GcEpochType local_epoch =
GetSessionManager()->GetCurrentSessionContext()->GetTxnManager()->GetGcSession()->GcStartInnerTxn();
size_t allocationSize = (*static_cast<mrcu_callback*>(gcRemoveLayerFuncObjPtr))(dropIndex);
if (dropIndex == false) {
((SlabAllocator*)slab)->Release(gcRemoveLayerFuncObjPtr, allocationSize);
}
GetSessionManager()->GetCurrentSessionContext()->GetTxnManager()->GetGcSession()->GcEndInnerTxn(false);
mtSessionThreadInfo->set_gc_session(NULL);
return allocationSize;
}

View File

@ -582,7 +582,6 @@ Row* Table::RemoveKeyFromIndex(Row* row, Sentinel* sentinel, uint64_t tid, GcMan
#endif
currSentinel = ix->IndexRemove(&key, tid);
MOT_ASSERT(currSentinel == sentinel);
MOT_ASSERT(currSentinel->GetCounter() == 0);
if (likely(gc != nullptr)) {
if (ix->GetIndexOrder() == IndexOrder::INDEX_ORDER_PRIMARY) {
OutputRow = currSentinel->GetData();

View File

@ -413,8 +413,6 @@ RC TxnManager::RollbackInsert(Access* ac)
#endif
outputSen = index_->IndexRemove(&m_key, GetThdId());
MOT_ASSERT(outputSen != nullptr);
MOT_ASSERT(outputSen->GetCounter() == 0);
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,
// lets check if we can reclaim the deleted row
@ -822,8 +820,6 @@ void TxnInsertAction::CleanupOptimisticInsert(
MOT_ASSERT(pIndexInsertResult->GetCounter() == 0);
Sentinel* outputSen = index_->IndexRemove(currentItem->m_key, m_manager->GetThdId());
MOT_ASSERT(outputSen != nullptr);
MOT_ASSERT(outputSen->GetCounter() == 0);
m_manager->GcSessionRecordRcu(
index_->GetIndexId(), outputSen, nullptr, Index::SentinelDtor, SENTINEL_SIZE(index_));
m_manager->m_accessMgr->IncreaseTableStat(table);

Some files were not shown because too many files have changed in this diff Show More