forked from huawei/openGauss-server
Compare commits
1 Commits
master
...
undorecycl
| Author | SHA1 | Date |
|---|---|---|
|
|
0c4cf14576 |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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*
|
||||
Note:If 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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Note:none
|
||||
*/
|
||||
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
|
||||
Note:The 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;
|
||||
|
|
|
|||
|
|
@ -1229,12 +1229,6 @@ parse_next_sync_groups(char **pgroup, char *result)
|
|||
static int
|
||||
transform_az_name(char *config_value, char *allAZString, int allAZStringBufLen, const char *data_dir)
|
||||
{
|
||||
if (strcmp(config_value, "''") == 0) {
|
||||
errno_t rc = strncpy_s(allAZString, allAZStringBufLen, config_value, strlen(config_value));
|
||||
securec_check_c(rc, "\0", "\0");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
char *azString = NULL;
|
||||
char *buf = allAZString;
|
||||
int buflen = allAZStringBufLen;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ 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;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92607;
|
||||
|
||||
#ifdef DUMPSYSLOG
|
||||
char* syslogpath = NULL;
|
||||
|
|
@ -4454,7 +4454,16 @@ void getSubscriptions(Archive *fout)
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -10786,11 +10795,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 +21404,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,
|
||||
|
|
|
|||
|
|
@ -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, ¤t, nodeInfo, backup_conn, backup_replslots);
|
||||
pg_start_backup(label, smooth_checkpoint, ¤t, 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(¤t, 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -8641,7 +8641,7 @@
|
|||
),
|
||||
AddFuncGroup(
|
||||
"pg_stat_get_wal_senders", 1,
|
||||
AddBuiltinFunc(_0(3099), _1("pg_stat_get_wal_senders"), _2(0), _3(false), _4(true), _5(pg_stat_get_wal_senders), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(0), _21(21, 20, 23, 25, 25, 25, 25, 1184, 1184, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 23, 25, 25), _22(21, 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o'), _23(21, "pid", "sender_pid", "local_role", "peer_role", "peer_state", "state", "catchup_start", "catchup_end", "sender_sent_location", "sender_write_location", "sender_flush_location", "sender_replay_location", "receiver_received_location", "receiver_write_location", "receiver_flush_location", "receiver_replay_location", "sync_percent", "sync_state", "sync_priority", "sync_most_available", "channel"), _24(NULL), _25("pg_stat_get_wal_senders"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("statistics: information about currently active replication"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0))
|
||||
AddBuiltinFunc(_0(3099), _1("pg_stat_get_wal_senders"), _2(0), _3(false), _4(true), _5(pg_stat_get_wal_senders), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(0), _21(22, 20, 23, 25, 25, 25, 25, 1184, 1184, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 23, 23, 25, 25), _22(22, 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o'), _23(22, "pid", "sender_pid", "local_role", "peer_role", "peer_state", "state", "catchup_start", "catchup_end", "sender_sent_location", "sender_write_location", "sender_flush_location", "sender_replay_location", "receiver_received_location", "receiver_write_location", "receiver_flush_location", "receiver_replay_location", "sync_percent", "sync_state", "sync_group", "sync_priority", "sync_most_available", "channel"), _24(NULL), _25("pg_stat_get_wal_senders"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("statistics: information about currently active replication"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0))
|
||||
),
|
||||
AddFuncGroup(
|
||||
"pg_stat_get_wlm_ec_operator_info", 1,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -47,8 +47,7 @@ static void InternalAggIsSupported(const char *aggName)
|
|||
"json_agg",
|
||||
"json_object_agg",
|
||||
"st_summarystatsagg",
|
||||
"st_union",
|
||||
"wm_concat"
|
||||
"st_union"
|
||||
};
|
||||
|
||||
uint len = lengthof(supportList);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ Subscription *GetSubscription(Oid subid, bool missing_ok)
|
|||
|
||||
/* Get slotname */
|
||||
datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subslotname, &isnull);
|
||||
if (!isnull) {
|
||||
if (unlikely(isnull)) {
|
||||
sub->slotname = pstrdup(NameStr(*DatumGetName(datum)));
|
||||
} else {
|
||||
sub->slotname = NULL;
|
||||
|
|
@ -92,10 +92,10 @@ Subscription *GetSubscription(Oid subid, bool missing_ok)
|
|||
|
||||
datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subbinary, &isnull);
|
||||
if (unlikely(isnull)) {
|
||||
sub->binary = false;
|
||||
} else {
|
||||
sub->binary = DatumGetBool(datum);
|
||||
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
||||
errmsg("null binary for subscription %u", subid)));
|
||||
}
|
||||
sub->binary = DatumGetBool(datum);
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = 92607;
|
||||
|
||||
const uint32 PREDPUSH_SAME_LEVEL_VERSION_NUM = 92522;
|
||||
const uint32 UPSERT_WHERE_VERSION_NUM = 92514;
|
||||
|
|
@ -101,7 +101,7 @@ 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;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92607;
|
||||
|
||||
/* Version number of the guc parameter backend_version added in V500R001C20 */
|
||||
const uint32 V5R1C20_BACKEND_VERSION_NUM = 92305;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
note:The 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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
note:The 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.
|
||||
note:The 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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
1、Return EBADF if the socket argument is not a valid file descriptor.
|
||||
2、Return EINVAL if the socket has been shut down.
|
||||
3、Return ENOTCONN if the socket is not connected or otherwise has not had the peer pre-specified.
|
||||
4、Return ENOTSOCK if the socket argument does not refer to a socket.
|
||||
5、Return EOPNOTSUPP if the operation is not supported for the socket protocol.
|
||||
6、Return 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、
|
||||
EWOULDBLOCK、EINTR 和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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#note:Dictionary 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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
# note:A 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:
|
||||
|
|
|
|||
|
|
@ -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':
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:])
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#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 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):
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#method:Display the error content and suggestions, and retrieve suggestions provided by the system. If there are no suggestions, return “ no suggestions”
|
||||
#note:The property decorator turns a method into a property call.(root_causes、suggestions)
|
||||
#date:2022/8/4
|
||||
#contact:18365997821
|
||||
|
||||
class Alarm:
|
||||
def __init__(self,
|
||||
host: Union[str],
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#method:Display the error content and suggestions, and retrieve suggestions provided by the system. If there are no suggestions, return “ no suggestions”
|
||||
#note:The property decorator turns a method into a property call.(root_causes、suggestions)
|
||||
#date:2022/8/4
|
||||
#contact:18365997821
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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(\''
|
||||
|
|
|
|||
|
|
@ -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(','))]
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ from . import AbstractModel
|
|||
|
||||
|
||||
class TemplateModel(AbstractModel):
|
||||
# Initialize algorithm parameters
|
||||
def __init__(self, params):
|
||||
super().__init__(params)
|
||||
self.bias = 1e-5
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -49,35 +49,34 @@
|
|||
* is specified by a BASETYPE element in the parameters. Otherwise,
|
||||
* "args" defines the input type(s).
|
||||
*/
|
||||
// 定义聚合函数
|
||||
void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
||||
{
|
||||
char* aggName = NULL;// 聚合函数的名称
|
||||
Oid aggNamespace;// 聚合函数所在的命名空间
|
||||
char* aggName = NULL;
|
||||
Oid aggNamespace;
|
||||
AclResult aclresult;
|
||||
List* transfuncName = NIL;// 过渡函数的名称
|
||||
List* finalfuncName = NIL;// 最终函数的名称
|
||||
List* sortoperatorName = NIL;// 排序操作符的名称
|
||||
TypeName* baseType = NULL;// 输入类型的名称
|
||||
TypeName* transType = NULL;// 过渡类型的名称
|
||||
char* initval = NULL; // 初始化值
|
||||
List* transfuncName = NIL;
|
||||
List* finalfuncName = NIL;
|
||||
List* sortoperatorName = NIL;
|
||||
TypeName* baseType = NULL;
|
||||
TypeName* transType = NULL;
|
||||
char* initval = NULL;
|
||||
#ifdef PGXC
|
||||
List* collectfuncName = NIL;// 收集函数的名称
|
||||
char* initcollect = NULL;// 初始化收集值
|
||||
List* collectfuncName = NIL;
|
||||
char* initcollect = NULL;
|
||||
#endif
|
||||
Oid* aggArgTypes = NULL;// 聚合函数的参数类型列表
|
||||
Oid* aggArgTypes = NULL;
|
||||
int numArgs;
|
||||
Oid transTypeId; // 过渡类型的OID
|
||||
Oid transTypeId;
|
||||
ListCell* pl = NULL;
|
||||
|
||||
/* attribute for ordered set aggregate */
|
||||
char aggKind = AGGKIND_NORMAL; // 聚合函数的类型,默认为普通聚合函数
|
||||
char aggKind = AGGKIND_NORMAL;
|
||||
|
||||
/* Convert list of names to a name and namespace */
|
||||
aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName);// 从名称获取聚合函数所在的命名空间和名称
|
||||
aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName);
|
||||
|
||||
/* Check we have creation rights in target namespace */
|
||||
aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE);// 检查当前用户是否有在命名空间下创建聚合函数的权限
|
||||
aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(aggNamespace));
|
||||
if (u_sess->attr.attr_sql.enforce_a_behavior) {
|
||||
|
|
@ -100,7 +99,7 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
|||
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(aggNamespace));
|
||||
}
|
||||
}
|
||||
foreach (pl, parameters) {// 遍历参数列表,解析聚合函数的各个属性
|
||||
foreach (pl, parameters) {
|
||||
DefElem* defel = (DefElem*)lfirst(pl);
|
||||
|
||||
/*
|
||||
|
|
@ -139,15 +138,15 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
|||
/*
|
||||
* make sure we have our required definitions
|
||||
*/
|
||||
if (transType == NULL)// 检查过渡类型是否已经指定
|
||||
if (transType == NULL)
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate stype must be specified")));
|
||||
if (transfuncName == NIL)// 检查过渡函数是否已经指定
|
||||
if (transfuncName == NIL)
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate sfunc must be specified")));
|
||||
|
||||
/*
|
||||
* look up the aggregate's input datatype(s).
|
||||
*/
|
||||
if (oldstyle) {// 如果使用旧式语法定义聚合函数
|
||||
if (oldstyle) {
|
||||
/*
|
||||
* Old style: use basetype parameter. This supports aggregates of
|
||||
* zero or one input, with input type ANY meaning zero inputs.
|
||||
|
|
@ -206,9 +205,7 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
|||
* worse) by connecting up incompatible internal-using functions in an
|
||||
* aggregate.
|
||||
*/
|
||||
// 解析过渡类型的OID
|
||||
transTypeId = typenameTypeId(NULL, transType);
|
||||
// 检查过渡类型是否为伪类型,且不是多态类型
|
||||
if (get_typtype(transTypeId) == TYPTYPE_PSEUDO && !IsPolymorphicType(transTypeId)
|
||||
&& (transTypeId != INTERNALOID || !superuser())) {
|
||||
ereport(ERROR,
|
||||
|
|
@ -238,9 +235,9 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
|||
#endif
|
||||
}
|
||||
|
||||
void RenameAggregate(List* name, List* args, const char* newname)// 重命名聚合函数
|
||||
void RenameAggregate(List* name, List* args, const char* newname)
|
||||
{
|
||||
Oid procOid;// 聚合函数的OID
|
||||
Oid procOid;
|
||||
Oid namespaceOid;
|
||||
HeapTuple tup;
|
||||
Form_pg_proc procForm;
|
||||
|
|
@ -250,9 +247,9 @@ void RenameAggregate(List* name, List* args, const char* newname)// 重命名聚
|
|||
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
|
||||
|
||||
/* Look up function and make sure it's an aggregate */
|
||||
procOid = LookupAggNameTypeNames(name, args, false);// 获取聚合函数的OID
|
||||
procOid = LookupAggNameTypeNames(name, args, false);
|
||||
|
||||
tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));// 在系统表中查找聚合函数的元组
|
||||
tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));
|
||||
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", procOid)));
|
||||
procForm = (Form_pg_proc)GETSTRUCT(tup);
|
||||
|
|
@ -264,7 +261,7 @@ void RenameAggregate(List* name, List* args, const char* newname)// 重命名聚
|
|||
if (isNull) {
|
||||
packageoid = DatumGetObjectId(InvalidOid);
|
||||
}
|
||||
// 检查新名称是否与已存在的函数名称冲突
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
Datum allargtypes = ProcedureGetAllArgTypes(tup, &isNull);
|
||||
Datum argmodes = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_proargmodes, &isNull);
|
||||
|
|
@ -292,16 +289,16 @@ void RenameAggregate(List* name, List* args, const char* newname)// 重命名聚
|
|||
get_namespace_name(namespaceOid))));
|
||||
#endif
|
||||
/* must be owner */
|
||||
if (!pg_proc_ownercheck(procOid, GetUserId()))// 检查当前用户是否是函数的所有者
|
||||
if (!pg_proc_ownercheck(procOid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(name));
|
||||
|
||||
/* must have CREATE privilege on namespace */
|
||||
aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);// 检查命名空间的权限
|
||||
aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid));
|
||||
|
||||
/* rename */
|
||||
(void)namestrcpy(&(((Form_pg_proc)GETSTRUCT(tup))->proname), newname);// 更新函数名称并更新系统缓存
|
||||
(void)namestrcpy(&(((Form_pg_proc)GETSTRUCT(tup))->proname), newname);
|
||||
simple_heap_update(rel, &tup->t_self, tup);
|
||||
CatalogUpdateIndexes(rel, tup);
|
||||
|
||||
|
|
@ -312,7 +309,7 @@ void RenameAggregate(List* name, List* args, const char* newname)// 重命名聚
|
|||
/*
|
||||
* Change aggregate owner
|
||||
*/
|
||||
void AlterAggregateOwner(List* name, List* args, Oid newOwnerId)// 修改聚合函数的所有者
|
||||
void AlterAggregateOwner(List* name, List* args, Oid newOwnerId)
|
||||
{
|
||||
Oid procOid;
|
||||
|
||||
|
|
|
|||
|
|
@ -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.")));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ void geqo_copy(PlannerInfo* root, Chromosome* chromo1, Chromosome* chromo2, int
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < string_length; i++)// 使用循环遍历染色体的字符串,将chromo2的内容复制到chromo1中
|
||||
for (i = 0; i < string_length; i++)
|
||||
chromo1->string[i] = chromo2->string[i];
|
||||
|
||||
chromo1->worth = chromo2->worth;// 复制chromo2的worth值到chromo1
|
||||
chromo1->worth = chromo2->worth;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,19 +41,17 @@
|
|||
*
|
||||
* cycle crossover
|
||||
*/
|
||||
//这段代码实现了遗传算法中的一种交叉操作(Crossover),用于生成新的候选解。
|
||||
//函数的目标是从两个不同的父代查询计划,生成一个新的子代查询计划
|
||||
int cx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_gene, City* city_table)
|
||||
{
|
||||
int i, start_pos, curr_pos;
|
||||
int count = 0;
|
||||
int num_diffs = 0;
|
||||
int num_diffs = 0;
|
||||
|
||||
/* initialize city table */
|
||||
for (i = 1; i <= num_gene; i++) {
|
||||
city_table[i].used = 0;// 将节点表标记为未使用
|
||||
city_table[tour2[i - 1]].tour2_position = i - 1;// 记录tour2中每个基因在数组中的位置
|
||||
city_table[tour1[i - 1]].tour1_position = i - 1;// 记录tour1中每个基因在数组中的位置
|
||||
city_table[i].used = 0;
|
||||
city_table[tour2[i - 1]].tour2_position = i - 1;
|
||||
city_table[tour1[i - 1]].tour1_position = i - 1;
|
||||
}
|
||||
|
||||
/* choose random cycle starting position */
|
||||
|
|
@ -71,11 +69,10 @@ int cx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_gen
|
|||
/* cx main part */
|
||||
/* STEP 1 */
|
||||
while (tour2[curr_pos] != tour1[start_pos]) {
|
||||
city_table[(int)tour2[curr_pos]].used = 1;// 标记tour2中的节点为已使用
|
||||
curr_pos = city_table[(int)tour2[curr_pos]].tour1_position;// 切换到tour1中相应节点的位置
|
||||
offspring[curr_pos] = tour1[curr_pos];// 将对应的tour1节点添加到offspring中
|
||||
|
||||
count++;// 增加已添加节点的计数
|
||||
city_table[(int)tour2[curr_pos]].used = 1;
|
||||
curr_pos = city_table[(int)tour2[curr_pos]].tour1_position;
|
||||
offspring[curr_pos] = tour1[curr_pos];
|
||||
count++;
|
||||
}
|
||||
|
||||
/* STEP 2 */
|
||||
|
|
@ -84,7 +81,7 @@ int cx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_gen
|
|||
for (i = 1; i <= num_gene; i++) {
|
||||
if (!city_table[i].used) {
|
||||
offspring[city_table[i].tour2_position] = tour2[(int)city_table[i].tour2_position];
|
||||
count++;// 增加已添加节点的计数
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -95,10 +92,10 @@ int cx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_gen
|
|||
/* count the number of differences between mom and offspring */
|
||||
for (i = 0; i < num_gene; i++) {
|
||||
if (tour1[i] != offspring[i]) {
|
||||
num_diffs++;// 增加不同之处的计数
|
||||
num_diffs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return num_diffs;// 返回不同之处的数量
|
||||
return num_diffs;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,17 +33,10 @@
|
|||
#include "optimizer/geqo_recombination.h"
|
||||
#include "optimizer/geqo_random.h"
|
||||
|
||||
// 声明函数gimme_edge,它接受PlannerInfo结构体指针root,两个Gene节点gene1和gene2,
|
||||
// 以及Edge结构体指针edge_table作为参数,返回一个整数。
|
||||
static int gimme_edge(PlannerInfo* root, Gene gene1, Gene gene2, Edge* edge_table);
|
||||
// 声明函数remove_gene,它接受PlannerInfo结构体指针root,Gene gene,Edge edge,
|
||||
// 以及Edge结构体指针edge_table作为参数,不返回值。
|
||||
static void remove_gene(PlannerInfo* root, Gene gene, Edge edge, Edge* edge_table);
|
||||
// 声明函数gimme_gene,它接受PlannerInfo结构体指针root,Edge edge,
|
||||
// 以及Edge结构体指针edge_table作为参数,返回一个Gene。
|
||||
static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table);
|
||||
// 声明函数edge_failure,它接受PlannerInfo结构体指针root,Gene指针gene,整数index,
|
||||
// Edge结构体指针edge_table,以及整数num_gene作为参数,返回一个Gene。
|
||||
|
||||
static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_table, int num_gene);
|
||||
|
||||
/* alloc_edge_table
|
||||
|
|
@ -51,8 +44,6 @@ static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_ta
|
|||
* allocate memory for edge table
|
||||
*
|
||||
*/
|
||||
// 分配Edge结构体数组edge_table的内存,接受PlannerInfo结构体指针root和整数num_gene作为参数,
|
||||
// 返回一个Edge结构体指针。
|
||||
Edge* alloc_edge_table(PlannerInfo* root, int num_gene)
|
||||
{
|
||||
Edge* edge_table = NULL;
|
||||
|
|
@ -61,7 +52,7 @@ Edge* alloc_edge_table(PlannerInfo* root, int num_gene)
|
|||
* palloc one extra location so that nodes numbered 1..n can be indexed
|
||||
* directly; 0 will not be used
|
||||
*/
|
||||
edge_table = (Edge*)palloc((num_gene + 1) * sizeof(Edge));// 使用palloc函数为edge_table分配内存。
|
||||
edge_table = (Edge*)palloc((num_gene + 1) * sizeof(Edge));
|
||||
|
||||
return edge_table;
|
||||
}
|
||||
|
|
@ -71,8 +62,6 @@ Edge* alloc_edge_table(PlannerInfo* root, int num_gene)
|
|||
* deallocate memory of edge table
|
||||
*
|
||||
*/
|
||||
// 释放Edge结构体数组edge_table的内存,接受PlannerInfo结构体指针root和Edge结构体指针edge_table作为参数,
|
||||
// 不返回值。
|
||||
void free_edge_table(PlannerInfo* root, Edge* edge_table)
|
||||
{
|
||||
pfree_ext(edge_table);
|
||||
|
|
@ -91,22 +80,20 @@ void free_edge_table(PlannerInfo* root, Edge* edge_table)
|
|||
* where 2.0=homogeneous; 4.0=diverse
|
||||
*
|
||||
*/
|
||||
// 计算两个遗传算法个体(tour1和tour2)之间的边数,接受PlannerInfo结构体指针root,
|
||||
// 两个Gene指针tour1和tour2,整数num_gene,以及Edge结构体指针edge_table作为参数,返回一个浮点数。
|
||||
float gimme_edge_table(PlannerInfo* root, Gene* tour1, Gene* tour2, int num_gene, Edge* edge_table)
|
||||
{
|
||||
int i, index1, index2;
|
||||
int edge_total; /* total number of unique edges in two genes */
|
||||
|
||||
/* at first clear the edge table's old data */
|
||||
for (i = 1; i <= num_gene; i++) {// 初始化edge_table中各个表节点的边数信息。
|
||||
for (i = 1; i <= num_gene; i++) {
|
||||
edge_table[i].total_edges = 0;
|
||||
edge_table[i].unused_edges = 0;
|
||||
}
|
||||
|
||||
/* fill edge table with new data */
|
||||
edge_total = 0;
|
||||
// 计算两个遗传算法个体之间的边数,调用gimme_edge函数。
|
||||
|
||||
for (index1 = 0; index1 < num_gene; index1++) {
|
||||
/*
|
||||
* presume the tour is circular, i.e. 1->2, 2->3, 3->1 this operaton
|
||||
|
|
@ -143,8 +130,6 @@ float gimme_edge_table(PlannerInfo* root, Gene* tour1, Gene* tour2, int num_gene
|
|||
* returns 1 if edge was not already registered and was just added;
|
||||
* 0 if edge was already registered and edge_table is unchanged
|
||||
*/
|
||||
// 实现gimme_edge函数,接受PlannerInfo结构体指针root,两个Gene gene1和gene2,
|
||||
// 以及Edge结构体指针edge_table作为参数,返回一个整数。
|
||||
static int gimme_edge(PlannerInfo* root, Gene gene1, Gene gene2, Edge* edge_table)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -153,10 +138,10 @@ static int gimme_edge(PlannerInfo* root, Gene gene1, Gene gene2, Edge* edge_tabl
|
|||
int city2 = (int)gene2;
|
||||
|
||||
/* check whether edge city1->city2 already exists */
|
||||
edges = edge_table[city1].total_edges;// 获取表city1的边数。
|
||||
edges = edge_table[city1].total_edges;
|
||||
|
||||
for (i = 0; i < edges; i++) {// 遍历查询表city1的边列表,查找是否有查询表city2。
|
||||
if ((Gene)Abs(edge_table[city1].edge_list[i]) == city2) { // 如果找到,将对应边的标记置为负数表示已使用。
|
||||
for (i = 0; i < edges; i++) {
|
||||
if ((Gene)Abs(edge_table[city1].edge_list[i]) == city2) {
|
||||
/* mark shared edges as negative */
|
||||
edge_table[city1].edge_list[i] = 0 - city2;
|
||||
|
||||
|
|
@ -165,7 +150,7 @@ static int gimme_edge(PlannerInfo* root, Gene gene1, Gene gene2, Edge* edge_tabl
|
|||
}
|
||||
|
||||
/* add city1->city2; */
|
||||
edge_table[city1].edge_list[edges] = city2;// 如果没有找到表city2,将city2添加到city1的边列表中。
|
||||
edge_table[city1].edge_list[edges] = city2;
|
||||
|
||||
/* increment the number of edges from city1 */
|
||||
edge_table[city1].total_edges++;
|
||||
|
|
@ -182,11 +167,10 @@ static int gimme_edge(PlannerInfo* root, Gene gene1, Gene gene2, Edge* edge_tabl
|
|||
* in the edge table.)
|
||||
*
|
||||
*/
|
||||
|
||||
int gimme_tour(PlannerInfo* root, Edge* edge_table, Gene* new_gene, int num_gene)
|
||||
{
|
||||
int i;
|
||||
int edge_failures = 0;// 记录连接失败的数量。
|
||||
int edge_failures = 0;
|
||||
|
||||
/* choose int between 1 and num_gene */
|
||||
new_gene[0] = (Gene)geqo_randint(root, num_gene, 1);
|
||||
|
|
@ -196,22 +180,23 @@ int gimme_tour(PlannerInfo* root, Edge* edge_table, Gene* new_gene, int num_gene
|
|||
* as each point is entered into the tour, remove it from the edge
|
||||
* table
|
||||
*/
|
||||
remove_gene(root, new_gene[i - 1], edge_table[(int)new_gene[i - 1]], edge_table);// 从边表中移除前一个表和当前表之间的连接。
|
||||
remove_gene(root, new_gene[i - 1], edge_table[(int)new_gene[i - 1]], edge_table);
|
||||
|
||||
/* find destination for the newly entered point */
|
||||
if (edge_table[new_gene[i - 1]].unused_edges > 0)// 如果前一个表还有未使用的连接,就随机选择下一个表。
|
||||
if (edge_table[new_gene[i - 1]].unused_edges > 0)
|
||||
new_gene[i] = gimme_gene(root, edge_table[(int)new_gene[i - 1]], edge_table);
|
||||
|
||||
else { /* cope with fault */// 如果前一个表没有未使用的连接,则处理连接失败。
|
||||
else { /* cope with fault */
|
||||
edge_failures++;
|
||||
|
||||
new_gene[i] = edge_failure(root, new_gene, i - 1, edge_table, num_gene);
|
||||
}
|
||||
|
||||
/* mark this node as incorporated */
|
||||
edge_table[(int)new_gene[i - 1]].unused_edges = -1;// 将前一个表的未使用连接数设为-1,表示已经使用过。
|
||||
edge_table[(int)new_gene[i - 1]].unused_edges = -1;
|
||||
}
|
||||
|
||||
return edge_failures;// 返回连接失败的数量。
|
||||
return edge_failures;
|
||||
}
|
||||
|
||||
/* remove_gene
|
||||
|
|
@ -221,25 +206,23 @@ int gimme_tour(PlannerInfo* root, Edge* edge_table, Gene* new_gene, int num_gene
|
|||
* to identify deletion locations within edge table.
|
||||
*
|
||||
*/
|
||||
// 实现remove_gene函数,用于从边列表中移除gene,接受PlannerInfo结构体指针root,
|
||||
// Gene gene,Edge edge,以及Edge结构体指针edge_table作为参数,不返回值。
|
||||
static void remove_gene(PlannerInfo* root, Gene gene, Edge edge, Edge* edge_table)
|
||||
{
|
||||
int i, j;//循环计数器和内部循环计数器
|
||||
int possess_edge;//用于存储另一个查询表的标识
|
||||
int genes_remaining;//用于跟踪节点表的未使用边列表中剩余的未使用边的数量。
|
||||
int i, j;
|
||||
int possess_edge;
|
||||
int genes_remaining;
|
||||
|
||||
/*
|
||||
* do for every gene known to have an edge to input gene (i.e. in
|
||||
* edge_list for input edge)
|
||||
*/
|
||||
for (i = 0; i < edge.unused_edges; i++) { // 遍历城市gene的未使用边列表。
|
||||
for (i = 0; i < edge.unused_edges; i++) {
|
||||
possess_edge = (int)Abs(edge.edge_list[i]);
|
||||
genes_remaining = edge_table[possess_edge].unused_edges;
|
||||
|
||||
/* find the input gene in all edge_lists and delete it */
|
||||
for (j = 0; j < genes_remaining; j++) {
|
||||
if ((Gene)Abs(edge_table[possess_edge].edge_list[j]) == gene) {// 找到城市gene,将其从边列表中移除。
|
||||
if ((Gene)Abs(edge_table[possess_edge].edge_list[j]) == gene) {
|
||||
edge_table[possess_edge].unused_edges--;
|
||||
|
||||
edge_table[possess_edge].edge_list[j] = edge_table[possess_edge].edge_list[genes_remaining - 1];
|
||||
|
|
@ -256,25 +239,23 @@ static void remove_gene(PlannerInfo* root, Gene gene, Edge edge, Edge* edge_tabl
|
|||
* (i.e. edges which both genes possess)
|
||||
*
|
||||
*/
|
||||
// 实现gimme_gene函数,接受PlannerInfo结构体指针root,Edge edge,
|
||||
// 以及Edge结构体指针edge_table作为参数,返回一个Gene。
|
||||
static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table)
|
||||
{
|
||||
int i;// 循环计数器
|
||||
Gene frnd;// 用于存储目标节点的标识
|
||||
int minimum_edges;// 用于跟踪最小未使用边数
|
||||
int minimum_count = -1;// 用于跟踪最小未使用边数的节点表数量
|
||||
int rand_decision;// 随机决策的值
|
||||
const int EDGES_SIZE = 5;// 用于指定边的大小上限
|
||||
int i;
|
||||
Gene frnd;
|
||||
int minimum_edges;
|
||||
int minimum_count = -1;
|
||||
int rand_decision;
|
||||
const int EDGES_SIZE = 5;
|
||||
|
||||
/*
|
||||
* no point has edges to more than 4 other points thus, this contrived
|
||||
* minimum will be replaced
|
||||
*/
|
||||
minimum_edges = EDGES_SIZE;// 初始化minimum_edges为EDGES_SIZE。
|
||||
minimum_edges = EDGES_SIZE;
|
||||
|
||||
/* consider candidate destination points in edge list */
|
||||
for (i = 0; i < edge.unused_edges; i++) { /* 考虑边列表中的候选目标点 */
|
||||
for (i = 0; i < edge.unused_edges; i++) {
|
||||
frnd = (Gene)edge.edge_list[i];
|
||||
|
||||
/*
|
||||
|
|
@ -285,7 +266,6 @@ static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table)
|
|||
* converting to absolute values
|
||||
*/
|
||||
if (frnd < 0)
|
||||
// 如果目标节点标识小于0,返回其绝对值,表示已经访问过。
|
||||
return (Gene)Abs(frnd);
|
||||
|
||||
/*
|
||||
|
|
@ -301,11 +281,10 @@ static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table)
|
|||
* that the test will always succeed the first time around. If it can
|
||||
* fail then the code is in error
|
||||
*/
|
||||
if (edge_table[(int)frnd].unused_edges < minimum_edges) {// 如果目标节点的未使用边数少于minimum_edges,则更新minimum_edges和minimum_count。
|
||||
if (edge_table[(int)frnd].unused_edges < minimum_edges) {
|
||||
minimum_edges = edge_table[(int)frnd].unused_edges;
|
||||
minimum_count = 1;
|
||||
} else if (minimum_count == -1)
|
||||
// 如果minimum_count未设置,报告错误。
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("minimum_count not set")));
|
||||
else if (edge_table[(int)frnd].unused_edges == minimum_edges)
|
||||
|
|
@ -313,13 +292,13 @@ static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table)
|
|||
}
|
||||
|
||||
/* random decision of the possible candidates to use */
|
||||
rand_decision = geqo_randint(root, minimum_count - 1, 0);// 随机选择一个目标节点。
|
||||
rand_decision = geqo_randint(root, minimum_count - 1, 0);
|
||||
|
||||
for (i = 0; i < edge.unused_edges; i++) {
|
||||
frnd = (Gene)edge.edge_list[i];
|
||||
|
||||
/* return the chosen candidate point */
|
||||
if (edge_table[(int)frnd].unused_edges == minimum_edges) {// 如果目标节点的未使用边数等于minimum_edges,则减少minimum_count,并根据rand_decision返回目标城市。
|
||||
if (edge_table[(int)frnd].unused_edges == minimum_edges) {
|
||||
minimum_count--;
|
||||
|
||||
if (minimum_count == rand_decision)
|
||||
|
|
@ -328,7 +307,7 @@ static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table)
|
|||
}
|
||||
|
||||
/* ... should never be reached */
|
||||
ereport(ERROR,// 如果没有找到满足条件的目标节点,报告错误
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
||||
errmsg("neither shared nor minimum number nor random edge found")));
|
||||
|
|
@ -340,22 +319,19 @@ static Gene gimme_gene(PlannerInfo* root, Edge edge, Edge* edge_table)
|
|||
* routine for handling edge failure
|
||||
*
|
||||
*/
|
||||
// 实现edge_failure函数,接受PlannerInfo结构体指针root,Gene指针gene,整数index,
|
||||
// Edge结构体指针edge_table,以及整数num_gene作为参数,返回一个Gene。
|
||||
static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_table, int num_gene)
|
||||
{
|
||||
int i;// 循环计数器
|
||||
Gene fail_gene = gene[index];// 存储失败的基因
|
||||
int remaining_edges = 0;// 剩余的未使用边数
|
||||
int four_count = 0;// 拥有四条边的节点数量
|
||||
int rand_decision;// 随机决策的值
|
||||
const int TOTAL_EDGES_COUNT = 4;// 总边数的限制值
|
||||
int i;
|
||||
Gene fail_gene = gene[index];
|
||||
int remaining_edges = 0;
|
||||
int four_count = 0;
|
||||
int rand_decision;
|
||||
const int TOTAL_EDGES_COUNT = 4;
|
||||
|
||||
/*
|
||||
* how many edges remain? how many gene with four total (initial) edges
|
||||
* remain?
|
||||
*/
|
||||
// 计算剩余的未使用边数和total_edges等于TOTAL_EDGES_COUNT的节点数量。
|
||||
for (i = 1; i <= num_gene; i++) {
|
||||
if ((edge_table[i].unused_edges != -1) && (i != (int)fail_gene)) {
|
||||
remaining_edges++;
|
||||
|
|
@ -369,7 +345,7 @@ static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_ta
|
|||
* random decision of the gene with remaining edges and whose total_edges
|
||||
* == 4
|
||||
*/
|
||||
if (four_count != 0) {// 如果有total_edges等于TOTAL_EDGES_COUNT的节点,随机选择一个返回。
|
||||
if (four_count != 0) {
|
||||
rand_decision = geqo_randint(root, four_count - 1, 0);
|
||||
for (i = 1; i <= num_gene; i++) {
|
||||
if ((Gene)i != fail_gene && edge_table[i].unused_edges != -1 && edge_table[i].total_edges == TOTAL_EDGES_COUNT) {
|
||||
|
|
@ -379,7 +355,7 @@ static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_ta
|
|||
return (Gene)i;
|
||||
}
|
||||
}
|
||||
// 如果没有找到满足条件的节点,记录日志。
|
||||
|
||||
elog(LOG, "no edge found via random decision and total_edges == 4");
|
||||
} else if (remaining_edges != 0) {
|
||||
/* random decision of the gene with remaining edges */
|
||||
|
|
@ -394,9 +370,9 @@ static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_ta
|
|||
}
|
||||
}
|
||||
}
|
||||
// 如果没有找到满足条件的节点,记录日志。
|
||||
|
||||
elog(LOG, "no edge found via random decision with remaining edges");
|
||||
} else { // 如果没有剩余的未使用边,返回最后一个未使用点
|
||||
} else {
|
||||
/*
|
||||
* edge table seems to be empty; this happens sometimes on the last point
|
||||
* due to the fact that the first point is removed from the table even
|
||||
|
|
@ -413,6 +389,6 @@ static Gene edge_failure(PlannerInfo* root, Gene* gene, int index, Edge* edge_ta
|
|||
}
|
||||
|
||||
/* ... should never be reached */
|
||||
ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("no edge found")));// 如果没有找到满足条件的城市,报告错误。
|
||||
ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("no edge found")));
|
||||
return 0; /* to keep the compiler quiet */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,33 +34,26 @@
|
|||
|
||||
/* A "clump" of already-joined relations within gimme_tree */
|
||||
typedef struct {
|
||||
// 关联的关系信息
|
||||
RelOptInfo* joinrel; /* joinrel for the set of relations */
|
||||
// 关联的大小
|
||||
int size; /* number of input relations in clump */
|
||||
} Clump;
|
||||
|
||||
static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool force);// 静态函数——合并关联的 Clump 结构体列表
|
||||
static bool desirable_join(PlannerInfo* root, RelOptInfo* outer_rel, RelOptInfo* inner_rel);// 静态函数——确定连接两个关系是否是合适的
|
||||
static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool force);
|
||||
static bool desirable_join(PlannerInfo* root, RelOptInfo* outer_rel, RelOptInfo* inner_rel);
|
||||
|
||||
/*
|
||||
* geqo_eval
|
||||
*
|
||||
* Returns cost of a query tree as an individual of the population.
|
||||
*/
|
||||
|
||||
/*
|
||||
函数接收一个查询规划器(PlannerInfo)和一个遗传算法的代表性基因序列(Gene* tour),
|
||||
并尝试通过优化连接关系表的顺序来找到查询的最佳执行计划,以最小化总成本
|
||||
*/
|
||||
Cost geqo_eval(PlannerInfo* root, Gene* tour, int num_gene)
|
||||
{
|
||||
MemoryContext mycontext;// 创建一个内存上下文,用于存储 GEQO 相关数据
|
||||
MemoryContext oldcxt;// 用于保存当前内存上下文
|
||||
RelOptInfo* joinrel = NULL;// 初始化关联的关系信息
|
||||
Cost fitness;// 存储查询执行计划的适应度(成本)
|
||||
int savelength;// 保存当前关联的关系表列表的长度
|
||||
struct HTAB* savehash;// 保存当前的关系表哈希表
|
||||
MemoryContext mycontext;
|
||||
MemoryContext oldcxt;
|
||||
RelOptInfo* joinrel = NULL;
|
||||
Cost fitness;
|
||||
int savelength;
|
||||
struct HTAB* savehash;
|
||||
|
||||
/*
|
||||
* Create a private memory context that will hold all temp storage
|
||||
|
|
@ -72,9 +65,8 @@ Cost geqo_eval(PlannerInfo* root, Gene* tour, int num_gene)
|
|||
* be freed even if we abort via ereport(ERROR).
|
||||
*/
|
||||
mycontext = AllocSetContextCreate(
|
||||
// 创建一个内存上下文,用于存储 GEQO 相关数据
|
||||
CurrentMemoryContext, "GEQO", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
||||
oldcxt = MemoryContextSwitchTo(mycontext); // 切换到新的内存上下文
|
||||
oldcxt = MemoryContextSwitchTo(mycontext);
|
||||
|
||||
/*
|
||||
* gimme_tree will add entries to root->join_rel_list, which may or may
|
||||
|
|
@ -91,14 +83,14 @@ Cost geqo_eval(PlannerInfo* root, Gene* tour, int num_gene)
|
|||
*
|
||||
* join_rel_level[] shouldn't be in use, so just Assert it isn't.
|
||||
*/
|
||||
savelength = list_length(root->join_rel_list);// 获取当前关联的关系表列表的长度
|
||||
savehash = root->join_rel_hash;// 保存当前的关系表哈希表
|
||||
AssertEreport(root->join_rel_level == NULL, MOD_OPT, "");// 使用断言确保当前的关联关系级别为空
|
||||
savelength = list_length(root->join_rel_list);
|
||||
savehash = root->join_rel_hash;
|
||||
AssertEreport(root->join_rel_level == NULL, MOD_OPT, "");
|
||||
|
||||
root->join_rel_hash = NULL;// 清空当前关系表哈希表
|
||||
root->join_rel_hash = NULL;
|
||||
|
||||
/* construct the best path for the given combination of relations */
|
||||
joinrel = gimme_tree(root, tour, num_gene);// 调用 gimme_tree 函数获取关联的关系树
|
||||
joinrel = gimme_tree(root, tour, num_gene);
|
||||
|
||||
/*
|
||||
* compute fitness
|
||||
|
|
@ -106,20 +98,20 @@ Cost geqo_eval(PlannerInfo* root, Gene* tour, int num_gene)
|
|||
* XXX geqo does not currently support optimization for partial result
|
||||
* retrieval --- how to fix?
|
||||
*/
|
||||
fitness = ((Path*)linitial(joinrel->cheapest_total_path))->total_cost;// 获取最便宜的路径的总代价
|
||||
fitness = ((Path*)linitial(joinrel->cheapest_total_path))->total_cost;
|
||||
|
||||
/*
|
||||
* Restore join_rel_list to its former state, and put back original
|
||||
* hashtable if any.
|
||||
*/
|
||||
root->join_rel_list = list_truncate(root->join_rel_list, savelength);// 恢复关系表列表的长度
|
||||
root->join_rel_hash = savehash;// 恢复关系表哈希表
|
||||
root->join_rel_list = list_truncate(root->join_rel_list, savelength);
|
||||
root->join_rel_hash = savehash;
|
||||
|
||||
/* release all the memory acquired within gimme_tree */
|
||||
(void)MemoryContextSwitchTo(oldcxt);// 切换回原始的内存上下文
|
||||
MemoryContextDelete(mycontext);// 删除新创建的内存上下文
|
||||
(void)MemoryContextSwitchTo(oldcxt);
|
||||
MemoryContextDelete(mycontext);
|
||||
|
||||
return fitness;// 返回计算的适应度值
|
||||
return fitness;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -146,12 +138,9 @@ Cost geqo_eval(PlannerInfo* root, Gene* tour, int num_gene)
|
|||
* generated plans.
|
||||
*/
|
||||
RelOptInfo* gimme_tree(PlannerInfo* root, Gene* tour, int num_gene)
|
||||
/*
|
||||
函数的功能是根据基因序列和初始关系表信息生成一个最优的关系表连接树,以便在查询优化中使用
|
||||
*/
|
||||
{
|
||||
GeqoPrivateData* priv = (GeqoPrivateData*)root->join_search_private;// 获取 GEQO 的私有数据
|
||||
List* clumps = NIL;// 创建一个关联的关系表列表
|
||||
GeqoPrivateData* priv = (GeqoPrivateData*)root->join_search_private;
|
||||
List* clumps = NIL;
|
||||
int rel_count;
|
||||
|
||||
/*
|
||||
|
|
@ -166,7 +155,7 @@ RelOptInfo* gimme_tree(PlannerInfo* root, Gene* tour, int num_gene)
|
|||
* joins might still fail due to semantics, but we should always be able
|
||||
* to find some join order that works.
|
||||
*/
|
||||
clumps = NIL;// 初始化关系表列表
|
||||
clumps = NIL;
|
||||
|
||||
for (rel_count = 0; rel_count < num_gene; rel_count++) {
|
||||
int cur_rel_index;
|
||||
|
|
@ -174,16 +163,16 @@ RelOptInfo* gimme_tree(PlannerInfo* root, Gene* tour, int num_gene)
|
|||
Clump* cur_clump = NULL;
|
||||
|
||||
/* Get the next input relation */
|
||||
cur_rel_index = (int)tour[rel_count];// 获取当前关联的关系索引
|
||||
cur_rel = (RelOptInfo*)list_nth(priv->initial_rels, cur_rel_index - 1);// 根据索引获取当前关系的信息
|
||||
cur_rel_index = (int)tour[rel_count];
|
||||
cur_rel = (RelOptInfo*)list_nth(priv->initial_rels, cur_rel_index - 1);
|
||||
|
||||
/* Make it into a single-rel clump */
|
||||
cur_clump = (Clump*)palloc(sizeof(Clump));// 分配内存以存储当前的关联关系信息
|
||||
cur_clump->joinrel = cur_rel;// 设置当前关联的关系信息
|
||||
cur_clump->size = 1;// 设置关系大小为1
|
||||
cur_clump = (Clump*)palloc(sizeof(Clump));
|
||||
cur_clump->joinrel = cur_rel;
|
||||
cur_clump->size = 1;
|
||||
|
||||
/* Merge it into the clumps list, using only desirable joins */
|
||||
clumps = merge_clump(root, clumps, cur_clump, false);// 调用 merge_clump 函数将当前关联关系合并到列表中
|
||||
clumps = merge_clump(root, clumps, cur_clump, false);
|
||||
}
|
||||
|
||||
if (list_length(clumps) > 1) {
|
||||
|
|
@ -195,7 +184,7 @@ RelOptInfo* gimme_tree(PlannerInfo* root, Gene* tour, int num_gene)
|
|||
Clump* clump = (Clump*)lfirst(lc);
|
||||
|
||||
fclumps = merge_clump(root, fclumps, clump, true);
|
||||
}// 强制合并关联关系列表
|
||||
}
|
||||
clumps = fclumps;
|
||||
}
|
||||
|
||||
|
|
@ -204,9 +193,9 @@ RelOptInfo* gimme_tree(PlannerInfo* root, Gene* tour, int num_gene)
|
|||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
||||
errmsg("failed to join all relations together"))); // 如果无法将所有关联关系连接在一起,则报告错误
|
||||
errmsg("failed to join all relations together")));
|
||||
|
||||
return ((Clump*)linitial(clumps))->joinrel;// 返回最终的关联的关系表
|
||||
return ((Clump*)linitial(clumps))->joinrel;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -221,25 +210,17 @@ RelOptInfo* gimme_tree(PlannerInfo* root, Gene* tour, int num_gene)
|
|||
* a cartesian join to be performed. When force is false, do only
|
||||
* "desirable" joins.
|
||||
*/
|
||||
|
||||
/*
|
||||
这段代码的功能是实现一个查询计划中的Clump合并操作。Clump是一种数据结构,用于表示查询计划中的关联关系,
|
||||
merge_clump函数接受一个PlannerInfo结构体(查询规划信息)、一个Clump列表clumps、一个新的Clump new_
|
||||
clump以及一个布尔标志force。它会尝试合并新的Clump与已有的Clump,以优化查询计划。函数首先迭代遍历clumps
|
||||
列表,尝试找到可合并的旧Clump,如果找到可合并的关联关系,就将它们合并,并递归调用自身以处理可能的进
|
||||
一步合并。如果无法合并,它会根据一定的规则将新Clump插入到clumps列表中,并返回最终的合并结果。
|
||||
*/
|
||||
static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool force)
|
||||
{
|
||||
ListCell* prev = NULL;// 用于追踪前一个列表元素的指针
|
||||
ListCell* lc = NULL;// 用于迭代遍历clumps列表的指针
|
||||
ListCell* prev = NULL;
|
||||
ListCell* lc = NULL;
|
||||
|
||||
/* Look for a clump that new_clump can join to */
|
||||
foreach (lc, clumps) {// 对clumps列表中的每个Clump元素进行迭代处理
|
||||
Clump* old_clump = (Clump*)lfirst(lc);// 获取当前迭代元素的指针
|
||||
foreach (lc, clumps) {
|
||||
Clump* old_clump = (Clump*)lfirst(lc);
|
||||
|
||||
if (force || desirable_join(root, old_clump->joinrel, new_clump->joinrel)) {// 如果force标志为true,或者新旧Clump之间存在可合并的关联关系
|
||||
RelOptInfo* joinrel = NULL;// 用于存储合并后的关系信息
|
||||
if (force || desirable_join(root, old_clump->joinrel, new_clump->joinrel)) {
|
||||
RelOptInfo* joinrel = NULL;
|
||||
|
||||
/*
|
||||
* Construct a RelOptInfo representing the join of these two input
|
||||
|
|
@ -249,17 +230,17 @@ static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool
|
|||
*/
|
||||
joinrel = make_join_rel(root, old_clump->joinrel, new_clump->joinrel);
|
||||
/* Keep searching if join order is not valid */
|
||||
if (joinrel != NULL) {// 如果成功创建了新的关联关系
|
||||
if (joinrel != NULL) {
|
||||
/* Find and save the cheapest paths for this joinrel */
|
||||
set_cheapest(joinrel);// 标记joinrel为最便宜的查询计划
|
||||
set_cheapest(joinrel);
|
||||
|
||||
/* Absorb new clump into old */
|
||||
old_clump->joinrel = joinrel; // 更新旧Clump的关联关系信息
|
||||
old_clump->size += new_clump->size;// 更新旧Clump的大小
|
||||
pfree_ext(new_clump);// 释放新Clump的内存
|
||||
old_clump->joinrel = joinrel;
|
||||
old_clump->size += new_clump->size;
|
||||
pfree_ext(new_clump);
|
||||
|
||||
/* Remove old_clump from list */
|
||||
clumps = list_delete_cell(clumps, lc, prev);// 从clumps列表中删除旧Clump
|
||||
clumps = list_delete_cell(clumps, lc, prev);
|
||||
|
||||
/*
|
||||
* Recursively try to merge the enlarged old_clump with
|
||||
|
|
@ -269,7 +250,7 @@ static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool
|
|||
return merge_clump(root, clumps, old_clump, force);
|
||||
}
|
||||
}
|
||||
prev = lc;// 更新prev指针以继续迭代
|
||||
prev = lc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -277,14 +258,13 @@ static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool
|
|||
* proper order according to size. We can be fast for the common case
|
||||
* where it has size 1 --- it should always go at the end.
|
||||
*/
|
||||
if (clumps == NIL || new_clump->size == 1) // 如果clumps为空或新Clump的大小为1,将新Clump添加到clumps列表末尾
|
||||
|
||||
if (clumps == NIL || new_clump->size == 1)
|
||||
return lappend(clumps, new_clump);
|
||||
|
||||
/* Check if it belongs at the front */
|
||||
lc = list_head(clumps);// 获取clumps列表的头元素指针
|
||||
lc = list_head(clumps);
|
||||
if (new_clump->size > ((Clump*)lfirst(lc))->size)
|
||||
return lcons(new_clump, clumps);// 如果新Clump的大小大于第一个Clump的大小,将新Clump插入列表头部
|
||||
return lcons(new_clump, clumps);
|
||||
|
||||
/* Else search for the place to insert it */
|
||||
for (;;) {
|
||||
|
|
@ -292,10 +272,9 @@ static List* merge_clump(PlannerInfo* root, List* clumps, Clump* new_clump, bool
|
|||
|
||||
if (nxt == NULL || new_clump->size > ((Clump*)lfirst(nxt))->size)
|
||||
break; /* it belongs after 'lc', before 'nxt' */
|
||||
// 如果新Clump的大小大于下一个Clump的大小,退出循环,确定插入位置
|
||||
lc = nxt;
|
||||
}
|
||||
(void)lappend_cell(clumps, lc, new_clump);// 在lc之后插入新Clump
|
||||
(void)lappend_cell(clumps, lc, new_clump);
|
||||
|
||||
return clumps;
|
||||
}
|
||||
|
|
@ -309,7 +288,6 @@ static bool desirable_join(PlannerInfo* root, RelOptInfo* outer_rel, RelOptInfo*
|
|||
* Join if there is an applicable join clause, or if there is a join order
|
||||
* restriction forcing these rels to be joined.
|
||||
*/
|
||||
// 判断是否有相关的连接条件或连接顺序限制
|
||||
if (have_relevant_joinclause(root, outer_rel, inner_rel) || have_join_order_restriction(root, outer_rel, inner_rel))
|
||||
return true;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,28 +32,21 @@
|
|||
#include "optimizer/geqo_random.h"
|
||||
#include "optimizer/geqo_selection.h"
|
||||
|
||||
static int gimme_pool_size(int nr_rel);//根据关系数(nr_rel)计算池大小
|
||||
static int gimme_number_generations(int pool_size);//根据池大小计算生成的代数数量
|
||||
static int gimme_pool_size(int nr_rel);
|
||||
static int gimme_number_generations(int pool_size);
|
||||
|
||||
/* define edge recombination crossover [ERX] per default */
|
||||
#if !defined(ERX) && !defined(PMX) && !defined(CX) && !defined(PX) && !defined(OX1) && !defined(OX2)
|
||||
#define ERX
|
||||
#endif
|
||||
|
||||
/*
|
||||
该部分代码在实现了遗传查询优化算法,其中查询计划的一组候选解(染色体)在多代之间演化,以找到给定关系集的最佳查询计划
|
||||
特定的交叉和变异方法可以在编译时基于宏选择,算法旨在通过评估和演化候选查询计划来找到最佳的查询计划
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* geqo
|
||||
* solution of the query optimization problem
|
||||
* similar to a constrained Traveling Salesman Problem (TSP)
|
||||
*/
|
||||
RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels)// GEQO(遗传查询优化)算法主函数
|
||||
RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels)
|
||||
{
|
||||
// 定义GEQO的私有数据结构和变量
|
||||
GeqoPrivateData priv;
|
||||
int generation;
|
||||
Chromosome* momma = NULL;
|
||||
|
|
@ -62,7 +55,7 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels)// GE
|
|||
Pool* pool = NULL;
|
||||
int pool_size, number_generations;
|
||||
|
||||
#ifdef GEQO_DEBUG// 根据宏定义,为特定的交叉方法分配额外的数据结构
|
||||
#ifdef GEQO_DEBUG
|
||||
int status_interval;
|
||||
#endif
|
||||
Gene* best_tour = NULL;
|
||||
|
|
@ -81,19 +74,19 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels)// GE
|
|||
#endif
|
||||
|
||||
/* set up private information */
|
||||
root->join_search_private = (void*)&priv;// 设置PlannerInfo中的私有数据
|
||||
root->join_search_private = (void*)&priv;
|
||||
priv.initial_rels = initial_rels;
|
||||
|
||||
/* initialize private number generator */
|
||||
geqo_set_seed(root, u_sess->attr.attr_sql.Geqo_seed);// 设置GEQO的随机种子
|
||||
geqo_set_seed(root, u_sess->attr.attr_sql.Geqo_seed);
|
||||
|
||||
/* set GA parameters */
|
||||
pool_size = gimme_pool_size(number_of_rels);// 根据输入计算池大小和生成代数数量
|
||||
pool_size = gimme_pool_size(number_of_rels);
|
||||
number_generations = gimme_number_generations(pool_size);
|
||||
#ifdef GEQO_DEBUG
|
||||
status_interval = 10;
|
||||
#endif
|
||||
// 为GEQO池分配内存并初始化
|
||||
|
||||
/* allocate genetic pool memory */
|
||||
pool = alloc_pool(root, pool_size, number_of_rels);
|
||||
|
||||
|
|
@ -104,7 +97,7 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels)// GE
|
|||
sort_pool(root, pool); /* we have to do it only one time, since all
|
||||
* kids replace the worst individuals in
|
||||
* future (-> geqo_pool.c:spread_chromo ) */
|
||||
#ifdef GEQO_DEBUG// 在调试模式下记录池统计信息
|
||||
#ifdef GEQO_DEBUG
|
||||
elog(DEBUG1,
|
||||
"GEQO selected %d pool entries, best %.2f, worst %.2f",
|
||||
pool_size,
|
||||
|
|
@ -113,10 +106,9 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels)// GE
|
|||
#endif
|
||||
|
||||
/* allocate chromosome momma and daddy memory */
|
||||
momma = alloc_chromo(root, pool->string_length);// 为父代染色体(momma和daddy)分配内存
|
||||
momma = alloc_chromo(root, pool->string_length);
|
||||
daddy = alloc_chromo(root, pool->string_length);
|
||||
|
||||
// 根据所选的交叉方法,为额外的数据结构分配内存
|
||||
#if defined(ERX)
|
||||
#ifdef GEQO_DEBUG
|
||||
elog(DEBUG2, "using edge recombination crossover [ERX]");
|
||||
|
|
@ -161,9 +153,9 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels)// GE
|
|||
|
||||
/* my pain main part: */
|
||||
/* iterative optimization */
|
||||
for (generation = 0; generation < number_generations; generation++) {// GEQO算法的主循环,用于代数迭代
|
||||
for (generation = 0; generation < number_generations; generation++) {
|
||||
/* SELECTION: using linear bias function */
|
||||
geqo_selection(root, momma, daddy, pool, u_sess->attr.attr_sql.Geqo_selection_bias);// 执行父代选择和基于所选方法的交叉操作
|
||||
geqo_selection(root, momma, daddy, pool, u_sess->attr.attr_sql.Geqo_selection_bias);
|
||||
|
||||
#if defined(ERX)
|
||||
/* EDGE RECOMBINATION CROSSOVER */
|
||||
|
|
@ -196,10 +188,10 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels)// GE
|
|||
#endif
|
||||
|
||||
/* EVALUATE FITNESS */
|
||||
kid->worth = geqo_eval(root, kid->string, pool->string_length);// 评估子代染色体的价值
|
||||
kid->worth = geqo_eval(root, kid->string, pool->string_length);
|
||||
|
||||
/* push the kid into the wilderness of life according to its worth */
|
||||
spread_chromo(root, kid, pool);// 将子代染色体传播到池中以用于下一代
|
||||
spread_chromo(root, kid, pool);
|
||||
|
||||
#ifdef GEQO_DEBUG
|
||||
if (status_interval && !(generation % status_interval))
|
||||
|
|
@ -207,7 +199,7 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels)// GE
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(ERX) && defined(GEQO_DEBUG)// 在调试模式下打印池的状态
|
||||
#if defined(ERX) && defined(GEQO_DEBUG)
|
||||
if (edge_failures != 0)
|
||||
elog(LOG, "[GEQO] failures: %d, average: %d", edge_failures, number_generations / edge_failures);
|
||||
else
|
||||
|
|
@ -278,14 +270,14 @@ RelOptInfo* geqo(PlannerInfo* root, int number_of_rels, List* initial_rels)// GE
|
|||
* The default is based on query size (no. of relations) = 2^(QS+1),
|
||||
* but constrained to a range based on the effort value.
|
||||
*/
|
||||
static int gimme_pool_size(int nr_rel)// 用于根据关系数计算池大小的函数
|
||||
static int gimme_pool_size(int nr_rel)
|
||||
{
|
||||
double size;
|
||||
int minsize;
|
||||
int maxsize;
|
||||
|
||||
/* Legal pool size *must* be at least 2, so ignore attempt to select 1 */
|
||||
if (u_sess->attr.attr_sql.Geqo_pool_size >= 2)// 检查配置中是否设置了特定的池大小
|
||||
if (u_sess->attr.attr_sql.Geqo_pool_size >= 2)
|
||||
return u_sess->attr.attr_sql.Geqo_pool_size;
|
||||
|
||||
size = pow(2.0, nr_rel + 1.0);
|
||||
|
|
@ -310,11 +302,10 @@ static int gimme_pool_size(int nr_rel)// 用于根据关系数计算池大小的
|
|||
* sure that less-fit individuals get pushed out of the breeding
|
||||
* population before the run finishes.
|
||||
*/
|
||||
static int gimme_number_generations(int pool_size)// 根据池大小计算生成代数数量的函数
|
||||
static int gimme_number_generations(int pool_size)
|
||||
{
|
||||
// 检查配置中是否设置了特定的代数数量
|
||||
if (u_sess->attr.attr_sql.Geqo_generations > 0)
|
||||
return u_sess->attr.attr_sql.Geqo_generations;
|
||||
|
||||
return pool_size;// 使用池大小作为默认的代数数量
|
||||
return pool_size;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,12 +29,7 @@
|
|||
/*
|
||||
* avg_pool
|
||||
*/
|
||||
|
||||
/*
|
||||
这段代码是关于查询优化器中的一些辅助函数,用于汇总和打印池(pool)和边缘表(edge table)的信息
|
||||
*/
|
||||
|
||||
static double avg_pool(Pool* pool)//这个函数计算池中数据元素的平均值,先检查池的大小是否为零,然后遍历每个元素,将其值累加到cumulative中,最后返回平均值
|
||||
static double avg_pool(Pool* pool)
|
||||
{
|
||||
int i;
|
||||
double cumulative = 0.0;
|
||||
|
|
@ -49,37 +44,37 @@ static double avg_pool(Pool* pool)//这个函数计算池中数据元素的平
|
|||
* little in speed and accuracy, but this routine is only used for debug
|
||||
* printouts, so we don't care that much.
|
||||
*/
|
||||
for (i = 0; i < pool->size; i++)// 遍历池中的数据,累加每个元素的值
|
||||
for (i = 0; i < pool->size; i++)
|
||||
cumulative += pool->data[i].worth / pool->size;
|
||||
|
||||
return cumulative;// 返回平均值
|
||||
return cumulative;
|
||||
}
|
||||
|
||||
/* print_pool
|
||||
*/
|
||||
void print_pool(FILE* fp, Pool* pool, int start, int stop)//这个函数用于打印池中的数据元素到指定文件,可以指定起始和结束位置,如果它们超出了池的边界,会重新设置它们
|
||||
void print_pool(FILE* fp, Pool* pool, int start, int stop)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* be extra careful that start and stop are valid inputs */
|
||||
if (start < 0)// 如果start小于0,将其设置为0
|
||||
if (start < 0)
|
||||
start = 0;
|
||||
if (stop > pool->size)// 如果stop大于池的大小,将其设置为池的大小
|
||||
if (stop > pool->size)
|
||||
stop = pool->size;
|
||||
|
||||
if (start + stop > pool->size) {// 如果start和stop的和大于池的大小,重新设置它们
|
||||
if (start + stop > pool->size) {
|
||||
start = 0;
|
||||
stop = pool->size;
|
||||
}
|
||||
|
||||
for (i = start; i < stop; i++) {// 打印池中的数据元素到指定文件
|
||||
for (i = start; i < stop; i++) {
|
||||
fprintf(fp, "%d)\t", i);
|
||||
for (j = 0; j < pool->string_length; j++)
|
||||
fprintf(fp, "%d ", pool->data[i].string[j]);
|
||||
fprintf(fp, "%g\n", pool->data[i].worth);
|
||||
}
|
||||
|
||||
fflush(fp);// 刷新文件缓冲区
|
||||
fflush(fp);
|
||||
}
|
||||
|
||||
/* print_gen
|
||||
|
|
@ -94,7 +89,7 @@ void print_gen(FILE* fp, Pool* pool, int generation)
|
|||
/* Use 2nd to last since last is buffer. */
|
||||
lowest = pool->size > 1 ? pool->size - 2 : 0;
|
||||
|
||||
fprintf(fp,// 打印池的<E6B1A0><E79A84><EFBFBD>些统计信息,包括最佳、最差、平均和平均池值
|
||||
fprintf(fp,
|
||||
"%5d | Best: %g Worst: %g Mean: %g Avg: %g\n",
|
||||
generation,
|
||||
pool->data[0].worth,
|
||||
|
|
@ -109,7 +104,7 @@ void print_edge_table(FILE* fp, Edge* edge_table, int num_gene)
|
|||
{
|
||||
int i, j;
|
||||
|
||||
fprintf(fp, "\nEDGE TABLE\n");// 打印边缘表的内容
|
||||
fprintf(fp, "\nEDGE TABLE\n");
|
||||
|
||||
for (i = 1; i <= num_gene; i++) {
|
||||
fprintf(fp, "%d :", i);
|
||||
|
|
|
|||
|
|
@ -37,29 +37,22 @@
|
|||
|
||||
void geqo_mutation(PlannerInfo* root, Gene* tour, int num_gene)
|
||||
{
|
||||
// 定义两个整数变量来表示要交换的基因的索引
|
||||
int swap1;
|
||||
int swap2;
|
||||
// 计算要执行的交换次数,这里使用随机数,num_gene/3表示最大交换次数
|
||||
int num_swaps = geqo_randint(root, num_gene / 3, 0);
|
||||
Gene temp;// 临时变量,用于存储基因交换时的中间结果
|
||||
Gene temp;
|
||||
|
||||
while (num_swaps > 0) {// 当还有交换次数时循环执行交换
|
||||
swap1 = geqo_randint(root, num_gene - 1, 0);// 随机选择两个不同的基因索引
|
||||
while (num_swaps > 0) {
|
||||
swap1 = geqo_randint(root, num_gene - 1, 0);
|
||||
swap2 = geqo_randint(root, num_gene - 1, 0);
|
||||
|
||||
while (swap1 == swap2)// 确保选到的两个索引不相同
|
||||
while (swap1 == swap2)
|
||||
swap2 = geqo_randint(root, num_gene - 1, 0);
|
||||
|
||||
temp = tour[swap1];// 执行基因交换,将选中的两个基因进行互换
|
||||
temp = tour[swap1];
|
||||
tour[swap1] = tour[swap2];
|
||||
tour[swap2] = temp;
|
||||
|
||||
num_swaps -= 1;// 减少剩余的交换次数
|
||||
num_swaps -= 1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
这段代码的功能是在给定基因序列(tour)中执行基因交换操作。
|
||||
它首先确定要执行的交换次数,然后随机选择两个不同的基因位置进行交换。
|
||||
通过这种方式,它可以引入随机性,有助于在搜索空间中找到更多可能的解决方案,以提高查询优化的效果
|
||||
*/
|
||||
|
|
@ -46,44 +46,34 @@ void ox1(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g
|
|||
int left, right, k, p, temp;
|
||||
|
||||
/* initialize city table */
|
||||
for (k = 1; k <= num_gene; k++)// 初始化节点表中的标志位,用于记录城市是否已经在后代中出现
|
||||
for (k = 1; k <= num_gene; k++)
|
||||
city_table[k].used = 0;
|
||||
|
||||
/* select portion to copy from tour1 */
|
||||
// 随机选择要从tour1中复制到后代中的基因片段
|
||||
left = geqo_randint(root, num_gene - 1, 0);
|
||||
right = geqo_randint(root, num_gene - 1, 0);
|
||||
if (left > right) {// 确保left小于right
|
||||
if (left > right) {
|
||||
temp = left;
|
||||
left = right;
|
||||
right = temp;
|
||||
}
|
||||
|
||||
/* copy portion from tour1 to offspring */
|
||||
for (k = left; k <= right; k++) {// 复制tour1中选定的基因片段到后代
|
||||
for (k = left; k <= right; k++) {
|
||||
offspring[k] = tour1[k];
|
||||
city_table[(int)tour1[k]].used = 1;
|
||||
}
|
||||
|
||||
// 初始化k和p,用于迭代地从tour2中选择未出现在后代中的基因
|
||||
k = (right + 1) % num_gene; /* index into offspring */
|
||||
p = k; /* index into tour2 */
|
||||
|
||||
/* copy stuff from tour2 to offspring */
|
||||
while (k != left) {// 开始迭代
|
||||
// 如果tour2中的基因没有在后代中出现过,将其添加到后代中
|
||||
while (k != left) {
|
||||
if (!city_table[(int)tour2[p]].used) {
|
||||
offspring[k] = tour2[p];
|
||||
k = (k + 1) % num_gene;
|
||||
city_table[(int)tour2[p]].used = 1;
|
||||
}
|
||||
p = (p + 1) % num_gene; /* increment tour2-index */// 增加tour2的索引
|
||||
p = (p + 1) % num_gene; /* increment tour2-index */
|
||||
}
|
||||
}
|
||||
/*
|
||||
这段代码实现了一种遗传算法的交叉操作,称为OX1 (Order Crossover 1)。
|
||||
它从两个父代基因序列 (tour1 和 tour2) 中选取一个随机的基因片段,
|
||||
然后将该片段复制到后代基因序列 (offspring) 中。
|
||||
接着,它从第二个父代基因序列 (tour2) 中选择未在后代中出现的基因,并按照原始顺序添加到后代中,以保持基因的顺序。
|
||||
这有助于保留父代的一些特性,并引入一些随机性,以生成新的个体
|
||||
*/
|
||||
|
|
@ -45,27 +45,26 @@ void ox2(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g
|
|||
int k, j, count, pos, select, num_positions;
|
||||
|
||||
/* initialize city table */
|
||||
for (k = 1; k <= num_gene; k++) {// 随机生成要选择的位置数目
|
||||
city_table[k].used = 0;// 标记节点尚未在后代中出现过
|
||||
city_table[k - 1].select_list = -1;// 初始化选择列表为-1
|
||||
for (k = 1; k <= num_gene; k++) {
|
||||
city_table[k].used = 0;
|
||||
city_table[k - 1].select_list = -1;
|
||||
}
|
||||
|
||||
/* determine the number of positions to be inherited from tour1 */
|
||||
num_positions = geqo_randint(root, 2 * num_gene / 3, num_gene / 3);
|
||||
// 随机生成要选择的位置数目
|
||||
|
||||
/* make a list of selected cities */
|
||||
for (k = 0; k < num_positions; k++) {// 随机选择位置并将对应的节点标记为已使用
|
||||
pos = geqo_randint(root, num_gene - 1, 0);// 随机选择一个位置
|
||||
city_table[pos].select_list = (int)tour1[pos];// 记录tour1中选中位置的基因值
|
||||
city_table[(int)tour1[pos]].used = 1; /* mark used */ // 标记节点已在后代中使用
|
||||
for (k = 0; k < num_positions; k++) {
|
||||
pos = geqo_randint(root, num_gene - 1, 0);
|
||||
city_table[pos].select_list = (int)tour1[pos];
|
||||
city_table[(int)tour1[pos]].used = 1; /* mark used */
|
||||
}
|
||||
|
||||
count = 0;
|
||||
k = 0;
|
||||
|
||||
/* consolidate the select list to adjacent positions */
|
||||
while (count < num_positions) {// 创建选择列表,用于在后代中选择未出现的节点
|
||||
while (count < num_positions) {
|
||||
if (city_table[k].select_list == -1) {
|
||||
j = k + 1;
|
||||
while ((city_table[j].select_list == -1) && (j < num_gene))
|
||||
|
|
@ -80,7 +79,7 @@ void ox2(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g
|
|||
}
|
||||
|
||||
select = 0;
|
||||
// 根据选择列表构建后代基因序列
|
||||
|
||||
for (k = 0; k < num_gene; k++) {
|
||||
if (city_table[(int)tour2[k]].used) {
|
||||
offspring[k] = (Gene)city_table[select].select_list;
|
||||
|
|
@ -90,10 +89,3 @@ void ox2(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g
|
|||
offspring[k] = tour2[k];
|
||||
}
|
||||
}
|
||||
/*
|
||||
这段代码实现了另一种遗传算法的交叉操作,称为OX2 (Order Crossover 2)。
|
||||
它的目标是生成后代基因序列 (offspring),将两个父代基因序列 (tour1 和 tour2) 结合起来。
|
||||
首先,它随机选择一些位置,并将相应的节点标记为已使用。
|
||||
接着,它构建一个选择列表,用于在后代中选择未出现的节点。
|
||||
最后,它根据选择列表构建后代基因序列,保持了一部分父代的顺序特性,同时引入了一些随机性。这有助于维持多样性并生成更多的解决方案
|
||||
*/
|
||||
|
|
@ -43,7 +43,6 @@
|
|||
*/
|
||||
void pmx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_gene)
|
||||
{
|
||||
// 为辅助数组分配内存
|
||||
int* failed = (int*)palloc((num_gene + 1) * sizeof(int));
|
||||
int* from = (int*)palloc((num_gene + 1) * sizeof(int));
|
||||
int* indx = (int*)palloc((num_gene + 1) * sizeof(int));
|
||||
|
|
@ -54,33 +53,33 @@ void pmx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g
|
|||
|
||||
/* no mutation so start up the pmx replacement algorithm */
|
||||
/* initialize failed[], from[], check_list[] */
|
||||
for (k = 0; k < num_gene; k++) {// 初始化辅助数组和检查列表
|
||||
failed[k] = -1;// 用于记录失败的映射关系
|
||||
from[k] = -1; // 用于记录映射的来源 (DAD or MOM)
|
||||
check_list[k + 1] = 0; // 检查列表用于记录节点的出现次数
|
||||
for (k = 0; k < num_gene; k++) {
|
||||
failed[k] = -1;
|
||||
from[k] = -1;
|
||||
check_list[k + 1] = 0;
|
||||
}
|
||||
// 随机选择要进行映射的基因片段
|
||||
|
||||
/* locate crossover points */
|
||||
left = geqo_randint(root, num_gene - 1, 0);
|
||||
right = geqo_randint(root, num_gene - 1, 0);
|
||||
if (left > right) {// 确保left小于right
|
||||
if (left > right) {
|
||||
temp = left;
|
||||
left = right;
|
||||
right = temp;
|
||||
}
|
||||
|
||||
/* copy tour2 into offspring */
|
||||
for (k = 0; k < num_gene; k++) { // 复制tour2中的基因到后代,并初始化相关数据结构
|
||||
for (k = 0; k < num_gene; k++) {
|
||||
offspring[k] = tour2[k];
|
||||
from[k] = DAD;// 从父代DAD中继承的基因
|
||||
check_list[tour2[k]]++;// 记录节点的出现次数
|
||||
from[k] = DAD;
|
||||
check_list[tour2[k]]++;
|
||||
}
|
||||
|
||||
/* copy tour1 into offspring */
|
||||
for (k = left; k <= right; k++) {// 执行部分映射操作,更新后代和相关数据结构
|
||||
for (k = left; k <= right; k++) {
|
||||
check_list[offspring[k]]--;
|
||||
offspring[k] = tour1[k];
|
||||
from[k] = MOM; // 从父代MOM中继承的基因
|
||||
from[k] = MOM;
|
||||
check_list[tour1[k]]++;
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +87,6 @@ void pmx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g
|
|||
mx_fail = 0;
|
||||
|
||||
/* STEP 1 */
|
||||
// 处理没有成功映射的情况
|
||||
for (k = left; k <= right; k++) { /* for all elements in the tour1-2 */
|
||||
if (tour1[k] == tour2[k])
|
||||
found = 1; /* find match in tour2 */
|
||||
|
|
@ -118,7 +116,7 @@ void pmx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g
|
|||
|
||||
/* STEP 2 */
|
||||
/* see if any genes could not be replaced */
|
||||
if (mx_fail > 0) {// 处理多次映射的情况
|
||||
if (mx_fail > 0) {
|
||||
mx_hold = mx_fail;
|
||||
|
||||
for (k = 0; k < mx_hold; k++) {
|
||||
|
|
@ -142,7 +140,6 @@ void pmx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g
|
|||
} /* ... if */
|
||||
/* STEP 3 */
|
||||
for (k = 1; k <= num_gene; k++) {
|
||||
// 处理重复节点的情况,确保每个基因只出现一次
|
||||
|
||||
if (check_list[k] > 1) {
|
||||
i = 0;
|
||||
|
|
@ -167,15 +164,9 @@ void pmx(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_g
|
|||
} /* end while */
|
||||
}
|
||||
} /* ... for */
|
||||
// 释放内存
|
||||
|
||||
pfree_ext(failed);
|
||||
pfree_ext(from);
|
||||
pfree_ext(indx);
|
||||
pfree_ext(check_list);
|
||||
}
|
||||
/*
|
||||
这段代码实现了PMX (Partially Mapped Crossover) 运算,用于生成后代基因序列。
|
||||
它首先随机选择一个基因片段来映射,然后根据映射关系进行替换。
|
||||
在映射操作中,它处理了映射失败和多次映射的情况,并最终确保后代中每个基因只出现一次。
|
||||
这有助于保持基因的顺序性和多样性。
|
||||
*/
|
||||
|
|
@ -30,24 +30,24 @@
|
|||
#include "optimizer/geqo_copy.h"
|
||||
#include "optimizer/geqo_pool.h"
|
||||
#include "optimizer/geqo_recombination.h"
|
||||
// 定义比较函数,用于排序
|
||||
|
||||
static int compare(const void* arg1, const void* arg2);
|
||||
|
||||
/*
|
||||
* alloc_pool
|
||||
* allocates memory for GA pool
|
||||
*/
|
||||
Pool* alloc_pool(PlannerInfo* root, int pool_size, int string_length)// 分配内存并初始化一个新的池
|
||||
Pool* alloc_pool(PlannerInfo* root, int pool_size, int string_length)
|
||||
{
|
||||
Pool* new_pool = NULL;
|
||||
Chromosome* chromo = NULL;
|
||||
int i;
|
||||
// 分配内存以存储新池
|
||||
|
||||
/* pool */
|
||||
new_pool = (Pool*)palloc(sizeof(Pool));
|
||||
new_pool->size = (int)pool_size;
|
||||
new_pool->string_length = (int)string_length;
|
||||
// 分配内存以存储染色体数据
|
||||
|
||||
/* all chromosome */
|
||||
new_pool->data = (Chromosome*)palloc(pool_size * sizeof(Chromosome));
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ Pool* alloc_pool(PlannerInfo* root, int pool_size, int string_length)// 分配
|
|||
* free_pool
|
||||
* deallocates memory for GA pool
|
||||
*/
|
||||
void free_pool(PlannerInfo* root, Pool* pool)// 释放池及其相关的内存
|
||||
void free_pool(PlannerInfo* root, Pool* pool)
|
||||
{
|
||||
Chromosome* chromo = NULL;
|
||||
int i;
|
||||
|
|
@ -74,19 +74,18 @@ void free_pool(PlannerInfo* root, Pool* pool)// 释放池及其相关的内存
|
|||
pfree_ext(chromo[i].string);
|
||||
|
||||
/* all chromosome */
|
||||
pfree_ext(pool->data);// 释放染色体数据
|
||||
pfree_ext(pool->data);
|
||||
|
||||
/* pool */
|
||||
pfree_ext(pool);
|
||||
// 释放池本身
|
||||
}
|
||||
|
||||
void random_init_pool(PlannerInfo* root, Pool* pool)// 随机初始化池中的染色体
|
||||
void random_init_pool(PlannerInfo* root, Pool* pool)
|
||||
{
|
||||
Chromosome* chromo = (Chromosome*)pool->data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pool->size; i++) {// 初始化池中的染色体
|
||||
for (i = 0; i < pool->size; i++) {
|
||||
init_tour(root, chromo[i].string, pool->string_length);
|
||||
pool->data[i].worth = geqo_eval(root, chromo[i].string, pool->string_length);
|
||||
}
|
||||
|
|
@ -98,7 +97,7 @@ void random_init_pool(PlannerInfo* root, Pool* pool)// 随机初始化池中的
|
|||
*
|
||||
* maybe you have to change compare() for different ordering ...
|
||||
*/
|
||||
void sort_pool(PlannerInfo* root, Pool* pool)// 对池中的染色体按照适应度进行排序
|
||||
void sort_pool(PlannerInfo* root, Pool* pool)
|
||||
{
|
||||
qsort(pool->data, pool->size, sizeof(Chromosome), compare);
|
||||
}
|
||||
|
|
@ -111,7 +110,7 @@ static int compare(const void* arg1, const void* arg2)
|
|||
{
|
||||
const Chromosome* chromo1 = (const Chromosome*)arg1;
|
||||
const Chromosome* chromo2 = (const Chromosome*)arg2;
|
||||
// 根据染色体的适应度比较它们的顺序
|
||||
|
||||
if (chromo1->worth - chromo2->worth == 0)
|
||||
return 0;
|
||||
else if (chromo1->worth > chromo2->worth)
|
||||
|
|
@ -123,10 +122,10 @@ static int compare(const void* arg1, const void* arg2)
|
|||
/* alloc_chromo
|
||||
* allocates a chromosome and string space
|
||||
*/
|
||||
Chromosome* alloc_chromo(PlannerInfo* root, int string_length)// 分配内存并初始化一个新的染色体
|
||||
Chromosome* alloc_chromo(PlannerInfo* root, int string_length)
|
||||
{
|
||||
Chromosome* chromo = NULL;
|
||||
// 分配内存以存储染色体
|
||||
|
||||
chromo = (Chromosome*)palloc(sizeof(Chromosome));
|
||||
chromo->string = (Gene*)palloc((string_length + 1) * sizeof(Gene));
|
||||
|
||||
|
|
@ -137,24 +136,23 @@ Chromosome* alloc_chromo(PlannerInfo* root, int string_length)// 分配内存并
|
|||
* deallocates a chromosome and string space
|
||||
*/
|
||||
void free_chromo(PlannerInfo* root, Chromosome* chromo)
|
||||
// 释放染色体及其相关的内存
|
||||
{
|
||||
pfree_ext(chromo->string);// 释放染色体数据
|
||||
pfree_ext(chromo);// 释放染色体本身
|
||||
pfree_ext(chromo->string);
|
||||
pfree_ext(chromo);
|
||||
}
|
||||
|
||||
/* spread_chromo
|
||||
* inserts a new chromosome into the pool, displacing worst gene in pool
|
||||
* assumes best->worst = smallest->largest
|
||||
*/
|
||||
void spread_chromo(PlannerInfo* root, Chromosome* chromo, Pool* pool)// 将染色体插入到池中并维护池的排序顺序
|
||||
void spread_chromo(PlannerInfo* root, Chromosome* chromo, Pool* pool)
|
||||
{
|
||||
int top, mid, bot;
|
||||
int i, index;
|
||||
Chromosome swap_chromo, tmp_chromo;
|
||||
|
||||
/* new chromo is so bad we can't use it */
|
||||
if (chromo->worth > pool->data[pool->size - 1].worth)// 如果染色体的适应度高于池中的最差染色体,则不插入
|
||||
if (chromo->worth > pool->data[pool->size - 1].worth)
|
||||
return;
|
||||
|
||||
/* do a binary search to find the index of the new chromo */
|
||||
|
|
@ -162,7 +160,7 @@ void spread_chromo(PlannerInfo* root, Chromosome* chromo, Pool* pool)// 将染
|
|||
mid = pool->size / 2;
|
||||
bot = pool->size - 1;
|
||||
index = -1;
|
||||
// 在池中查找插入位置
|
||||
|
||||
while (index == -1) {
|
||||
/* these 4 cases find a new location */
|
||||
if (chromo->worth <= pool->data[top].worth) {
|
||||
|
|
@ -193,7 +191,7 @@ void spread_chromo(PlannerInfo* root, Chromosome* chromo, Pool* pool)// 将染
|
|||
/*
|
||||
* copy new gene into pool storage; always replace worst gene in pool
|
||||
*/
|
||||
geqo_copy(root, &pool->data[pool->size - 1], chromo, pool->string_length);// 复制染色体并维护池的排序顺序
|
||||
geqo_copy(root, &pool->data[pool->size - 1], chromo, pool->string_length);
|
||||
|
||||
swap_chromo.string = pool->data[pool->size - 1].string;
|
||||
swap_chromo.worth = pool->data[pool->size - 1].worth;
|
||||
|
|
@ -209,9 +207,3 @@ void spread_chromo(PlannerInfo* root, Chromosome* chromo, Pool* pool)// 将染
|
|||
swap_chromo.worth = tmp_chromo.worth;
|
||||
}
|
||||
}
|
||||
/*
|
||||
这些代码涉及到遗传算法中的池管理和排序操作。
|
||||
它们用于分配和释放内存、随机初始化染色体、对池中的染色体按适应度进行排序以及插入新染色体并维护排序顺序。
|
||||
compare 函数用于比较两个染色体的适应度,以便在排序时使用。
|
||||
这些操作是遗传算法中的关键步骤,用于生成和维护染色体集合。
|
||||
*/
|
||||
|
|
@ -41,30 +41,20 @@
|
|||
*
|
||||
* position crossover
|
||||
*/
|
||||
// 实现部分顺序交叉算子
|
||||
/*
|
||||
//PlannerInfo* root 查询优化器的信息结构
|
||||
//Gene* tour1 第一个染色体
|
||||
//Gene* tour2 第二个染色体
|
||||
//Gene* offspring 生成的子代染色体
|
||||
//int num_gene 基因的数量
|
||||
//City* city_table 城市信息表,用于标记城市是否已被使用
|
||||
*/
|
||||
|
||||
void px(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_gene, City* city_table)
|
||||
{
|
||||
int num_positions; // 用于存储要交叉的位置数量
|
||||
int i, pos, tour2_index, offspring_index;// 循环变量和临时位置变量和用于追踪tour2和offspring中的位置
|
||||
int num_positions;
|
||||
int i, pos, tour2_index, offspring_index;
|
||||
|
||||
/* initialize city table */
|
||||
for (i = 1; i <= num_gene; i++)// 初始化城市使用标志,标记城市是否已被使用
|
||||
for (i = 1; i <= num_gene; i++)
|
||||
city_table[i].used = 0;
|
||||
|
||||
/* choose random positions that will be inherited directly from parent */
|
||||
num_positions = geqo_randint(root, 2 * num_gene / 3, num_gene / 3);// 随机确定要交叉的位置数量
|
||||
num_positions = geqo_randint(root, 2 * num_gene / 3, num_gene / 3);
|
||||
|
||||
/* choose random position */
|
||||
for (i = 0; i < num_positions; i++) {// 随机选择并复制部分基因片段从tour1到offspring,并标记城市为已使用
|
||||
for (i = 0; i < num_positions; i++) {
|
||||
pos = geqo_randint(root, num_gene - 1, 0);
|
||||
|
||||
offspring[pos] = tour1[pos]; /* transfer cities to child */
|
||||
|
|
@ -75,7 +65,7 @@ void px(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_ge
|
|||
offspring_index = 0;
|
||||
|
||||
/* px main part */
|
||||
while (offspring_index < num_gene) {// 从tour2中选择未被标记为已使用的城市,将其添加到offspring中
|
||||
while (offspring_index < num_gene) {
|
||||
/* next position in offspring filled */
|
||||
if (!city_table[(int)tour1[offspring_index]].used) {
|
||||
/* next city in tour1 not used */
|
||||
|
|
@ -92,8 +82,3 @@ void px(PlannerInfo* root, Gene* tour1, Gene* tour2, Gene* offspring, int num_ge
|
|||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
这段代码实现了部分顺序交叉(PX)算子,用于在遗传算法中交叉两个染色体(tour1和tour2)以生成一个新的染色体(offspring)。
|
||||
在PX算子中,首先随机选择一定数量的基因位置来交叉,然后将这些位置上的基因从tour1复制到offspring,并将相应的城市标记为已使用。
|
||||
接着,从tour2中选择未被标记为已使用的城市,并将它们添加到offspring中,以确保生成的染色体包含tour1和tour2的信息。
|
||||
*/
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include "optimizer/geqo_random.h"
|
||||
|
||||
void geqo_set_seed(PlannerInfo* root, double seed)// 设置遗传查询优化的随机种子
|
||||
void geqo_set_seed(PlannerInfo* root, double seed)
|
||||
{
|
||||
GeqoPrivateData* priv = (GeqoPrivateData*)root->join_search_private;
|
||||
|
||||
|
|
@ -26,19 +26,15 @@ void geqo_set_seed(PlannerInfo* root, double seed)// 设置遗传查询优化的
|
|||
* critical to do so.
|
||||
*/
|
||||
errno_t rc = EOK;
|
||||
rc = memset_s(priv->random_state, sizeof(priv->random_state), 0, sizeof(priv->random_state));// 使用memset_s将随机状态数组初始化为0
|
||||
rc = memset_s(priv->random_state, sizeof(priv->random_state), 0, sizeof(priv->random_state));
|
||||
securec_check(rc, "\0", "\0");
|
||||
// 使用memcpy_s将给定的种子复制到随机状态数组中
|
||||
rc = memcpy_s(priv->random_state, sizeof(priv->random_state), &seed, Min(sizeof(priv->random_state), sizeof(seed)));
|
||||
securec_check(rc, "\0", "\0");
|
||||
}
|
||||
// 生成随机数(0到1之间)用于遗传查询优化
|
||||
|
||||
double geqo_rand(PlannerInfo* root)
|
||||
{
|
||||
GeqoPrivateData* priv = (GeqoPrivateData*)root->join_search_private;
|
||||
// 使用erand48函数生成随机数,基于随机状态数组
|
||||
|
||||
return erand48(priv->random_state);
|
||||
}
|
||||
/*
|
||||
第一个函数用于设置随机种子,第二个函数用于生成遗传查询优化中的随机数。
|
||||
*/
|
||||
|
|
@ -32,21 +32,21 @@
|
|||
* this array. When a city is chosen, the array is shortened
|
||||
* and the procedure repeated.
|
||||
*/
|
||||
void init_tour(PlannerInfo* root, Gene* tour, int num_gene)// 初始化一个遍历染色体(tour)的函数
|
||||
void init_tour(PlannerInfo* root, Gene* tour, int num_gene)
|
||||
{
|
||||
Gene* tmp = NULL;// 临时数组,用于初始化基因
|
||||
int remainder;// 剩余未处理的基因数量
|
||||
int next, i;// 下一个基因的位置和循环变量
|
||||
Gene* tmp = NULL;
|
||||
int remainder;
|
||||
int next, i;
|
||||
|
||||
/* Fill a temp array with the IDs of all not-yet-visited cities */
|
||||
tmp = (Gene*)palloc(num_gene * sizeof(Gene));// 分配临时数组以初始化基因
|
||||
tmp = (Gene*)palloc(num_gene * sizeof(Gene));
|
||||
|
||||
for (i = 0; i < num_gene; i++)
|
||||
tmp[i] = (Gene)(i + 1);
|
||||
|
||||
remainder = num_gene - 1;
|
||||
|
||||
for (i = 0; i < num_gene; i++) {// 随机生成遍历顺序,初始化染色体
|
||||
for (i = 0; i < num_gene; i++) {
|
||||
/* choose value between 0 and remainder inclusive */
|
||||
next = geqo_randint(root, remainder, 0);
|
||||
/* output that element of the tmp array */
|
||||
|
|
@ -56,7 +56,6 @@ void init_tour(PlannerInfo* root, Gene* tour, int num_gene)// 初始化一个遍
|
|||
remainder--;
|
||||
}
|
||||
|
||||
// 释放临时数组内存
|
||||
pfree_ext(tmp);
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +63,7 @@ void init_tour(PlannerInfo* root, Gene* tour, int num_gene)// 初始化一个遍
|
|||
*
|
||||
* allocate memory for city table
|
||||
*/
|
||||
City* alloc_city_table(PlannerInfo* root, int num_gene)// 分配城市信息表的内存
|
||||
City* alloc_city_table(PlannerInfo* root, int num_gene)
|
||||
{
|
||||
City* city_table = NULL;
|
||||
|
||||
|
|
@ -81,10 +80,7 @@ City* alloc_city_table(PlannerInfo* root, int num_gene)// 分配城市信息表
|
|||
*
|
||||
* deallocate memory of city table
|
||||
*/
|
||||
void free_city_table(PlannerInfo* root, City* city_table)// 释放城市信息表的内存
|
||||
void free_city_table(PlannerInfo* root, City* city_table)
|
||||
{
|
||||
pfree_ext(city_table);
|
||||
}
|
||||
/*
|
||||
这些代码包括了初始化遍历染色体的函数、分配城市信息表内存的函数以及释放城市信息表内存的函数
|
||||
*/
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
#include "optimizer/geqo_copy.h"
|
||||
#include "optimizer/geqo_random.h"
|
||||
#include "optimizer/geqo_selection.h"
|
||||
// 线性随机数生成函数,用于选择遗传算法中的父代
|
||||
|
||||
static int linear_rand(PlannerInfo* root, int max, double bias);
|
||||
|
||||
/*
|
||||
|
|
@ -49,13 +49,11 @@ static int linear_rand(PlannerInfo* root, int max, double bias);
|
|||
* according to bias described by input parameters,
|
||||
* first and second genes are selected from the pool
|
||||
*/
|
||||
void geqo_selection(PlannerInfo* root, Chromosome* momma, Chromosome* daddy, Pool* pool, double bias)// 遗传算法中的选择操作,选择两个染色体作为父代
|
||||
|
||||
void geqo_selection(PlannerInfo* root, Chromosome* momma, Chromosome* daddy, Pool* pool, double bias)
|
||||
{
|
||||
int first, second;
|
||||
// 使用linear_rand函数随机选择第一个父代
|
||||
|
||||
first = linear_rand(root, pool->size, bias);
|
||||
// 使用linear_rand函数随机选择第二个父代
|
||||
second = linear_rand(root, pool->size, bias);
|
||||
|
||||
/*
|
||||
|
|
@ -66,11 +64,11 @@ void geqo_selection(PlannerInfo* root, Chromosome* momma, Chromosome* daddy, Poo
|
|||
* platform's implementation of erand48() was broken. We now always use
|
||||
* our own version.
|
||||
*/
|
||||
if (pool->size > 1) {// 确保选择的两个父代不相同(如果池中有多于一个染色体)
|
||||
if (pool->size > 1) {
|
||||
while (first == second)
|
||||
second = linear_rand(root, pool->size, bias);
|
||||
}
|
||||
// 复制选定的父代到momma和daddy中
|
||||
|
||||
geqo_copy(root, momma, &pool->data[first], pool->string_length);
|
||||
geqo_copy(root, daddy, &pool->data[second], pool->string_length);
|
||||
}
|
||||
|
|
@ -85,10 +83,10 @@ void geqo_selection(PlannerInfo* root, Chromosome* momma, Chromosome* daddy, Poo
|
|||
* probability distribution function is: f(x) = bias - 2(bias - 1)x
|
||||
* bias = (prob of first rule) / (prob of middle rule)
|
||||
*/
|
||||
static int linear_rand(PlannerInfo* root, int pool_size, double bias)// 线性随机数生成函数,用于选择遗传算法中的父代
|
||||
static int linear_rand(PlannerInfo* root, int pool_size, double bias)
|
||||
{
|
||||
double index; /* index between 0 and pop_size */// 生成的随机索引值
|
||||
double max = (double)pool_size;// 池的最大大小
|
||||
double index; /* index between 0 and pop_size */
|
||||
double max = (double)pool_size;
|
||||
|
||||
/*
|
||||
* If geqo_rand() returns exactly 1.0 then we will get exactly max from
|
||||
|
|
@ -98,8 +96,8 @@ static int linear_rand(PlannerInfo* root, int pool_size, double bias)// 线性
|
|||
* sqrt(). If we get a bad value just try again.
|
||||
*/
|
||||
do {
|
||||
double sqrtval; // 平方根值
|
||||
// 根据bias和随机数生成index值
|
||||
double sqrtval;
|
||||
|
||||
sqrtval = (bias * bias) - 4.0 * (bias - 1.0) * geqo_rand(root);
|
||||
if (sqrtval > 0.0)
|
||||
sqrtval = sqrt(sqrtval);
|
||||
|
|
|
|||
|
|
@ -129,22 +129,27 @@ static void set_varratio_for_rqclause(
|
|||
Selectivity clauselist_selectivity(
|
||||
PlannerInfo* root, List* clauses, int varRelid, JoinType jointype, SpecialJoinInfo* sjinfo, bool varratio_cached)
|
||||
{
|
||||
Selectivity s1 = 1.0;// 初始化选择性为1.0,这是最初的选择性估计值
|
||||
RangeQueryClause* rqlist = NULL;// 用于存储范围查询子句的链表
|
||||
ListCell* l = NULL; // 遍历用的链表指针
|
||||
List* varlist = NIL; // 用于存储涉及的变量列表
|
||||
List* clauselist = clauses;// 初始化待处理的子句列表
|
||||
ES_SELECTIVITY* es = NULL;// 扩展统计信息,用于某些连接类型的选择性估算
|
||||
MemoryContext ExtendedStat = NULL; // 用于存储扩展统计信息的内存上下文
|
||||
Selectivity s1 = 1.0;
|
||||
RangeQueryClause* rqlist = NULL;
|
||||
ListCell* l = NULL;
|
||||
List* varlist = NIL;
|
||||
List* clauselist = clauses;
|
||||
ES_SELECTIVITY* es = NULL;
|
||||
MemoryContext ExtendedStat = NULL;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
if (list_length(clauses) == 1) // 如果只有一个子句,直接调用 clause_selectivity 估算选择性并返回
|
||||
/*
|
||||
* If there's exactly one clause, then no use in trying to match up pairs,
|
||||
* so just go directly to clause_selectivity().
|
||||
*/
|
||||
if (list_length(clauses) == 1)
|
||||
return clause_selectivity(root, (Node*)linitial(clauses), varRelid, jointype, sjinfo, varratio_cached);
|
||||
|
||||
/* initialize es_selectivity class, list_length(clauses) can be 0 when called by set_baserel_size_estimates */
|
||||
if (list_length(clauses) >= 2 &&
|
||||
(jointype == JOIN_INNER || jointype == JOIN_FULL || jointype == JOIN_LEFT || jointype == JOIN_ANTI ||
|
||||
jointype == JOIN_SEMI || jointype == JOIN_LEFT_ANTI_FULL)) {// 对于一些连接类型,需要进行扩展统计信息的计算
|
||||
ExtendedStat = AllocSetContextCreate(CurrentMemoryContext,// 创建内存上下文用于扩展统计信息
|
||||
jointype == JOIN_SEMI || jointype == JOIN_LEFT_ANTI_FULL)) {
|
||||
ExtendedStat = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"ExtendedStat",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
|
|
@ -152,22 +157,33 @@ Selectivity clauselist_selectivity(
|
|||
oldcontext = MemoryContextSwitchTo(ExtendedStat);
|
||||
es = New(ExtendedStat) ES_SELECTIVITY();
|
||||
Assert(root != NULL);
|
||||
s1 = es->calculate_selectivity(root, clauses, sjinfo, jointype, NULL, ES_EQJOINSEL); // 计算选择性并更新待处理的子句列表
|
||||
s1 = es->calculate_selectivity(root, clauses, sjinfo, jointype, NULL, ES_EQJOINSEL);
|
||||
clauselist = es->unmatched_clause_group;
|
||||
(void)MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
|
||||
// 遍历待处理的子句列表
|
||||
/*
|
||||
* Initial scan over clauses. Anything that doesn't look like a potential
|
||||
* rangequery clause gets multiplied into s1 and forgotten. Anything that
|
||||
* does gets inserted into an rqlist entry.
|
||||
*/
|
||||
foreach (l, clauselist) {
|
||||
Node* clause = (Node*)lfirst(l);
|
||||
RestrictInfo* rinfo = NULL;
|
||||
Selectivity s2;
|
||||
|
||||
s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo, varratio_cached, true);// 计算单个子句的选择性
|
||||
/* Always compute the selectivity using clause_selectivity */
|
||||
s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo, varratio_cached, true);
|
||||
|
||||
/*
|
||||
* Check for being passed a RestrictInfo.
|
||||
*
|
||||
* If it's a pseudoconstant RestrictInfo, then s2 is either 1.0 or
|
||||
* 0.0; just use that rather than looking for range pairs.
|
||||
*/
|
||||
if (IsA(clause, RestrictInfo)) {
|
||||
rinfo = (RestrictInfo*)clause;
|
||||
if (rinfo->pseudoconstant) {// 如果子句中包含伪常量,更新选择性并继续下一个子句
|
||||
if (rinfo->pseudoconstant) {
|
||||
s1 = s1 * s2;
|
||||
rinfo->clause->selec = s2;
|
||||
continue;
|
||||
|
|
@ -176,9 +192,18 @@ Selectivity clauselist_selectivity(
|
|||
} else
|
||||
rinfo = NULL;
|
||||
|
||||
/*
|
||||
* if the clause is range query like 'between and',
|
||||
* we should scan the pair of rangequery and compute final selectivity.
|
||||
*/
|
||||
OpExpr* expr = (OpExpr*)clause;
|
||||
bool varonleft = true;
|
||||
if (is_rangequery_clause(clause, rinfo, &varonleft)) {// 检查是否为范围查询子句,根据不同的操作符类型进行处理
|
||||
if (is_rangequery_clause(clause, rinfo, &varonleft)) {
|
||||
/*
|
||||
* If it's not a "<" or ">" operator, just merge the
|
||||
* selectivity in generically. But if it's the right oprrest,
|
||||
* add the clause to rqlist for later processing.
|
||||
*/
|
||||
switch (get_oprrest(expr->opno)) {
|
||||
case F_SCALARLTSEL:
|
||||
addRangeClause(&rqlist, clause, varonleft, true, s2);
|
||||
|
|
@ -186,7 +211,8 @@ Selectivity clauselist_selectivity(
|
|||
case F_SCALARGTSEL:
|
||||
addRangeClause(&rqlist, clause, varonleft, false, s2);
|
||||
break;
|
||||
default:// 根据不同的参数选择性计算策略更新选择性
|
||||
default:
|
||||
/* Just merge the selectivity in generically */
|
||||
if ((uint32)u_sess->attr.attr_sql.cost_param & COST_ALTERNATIVE_CONJUNCT) {
|
||||
s1 = MIN(s1, s2);
|
||||
expr->xpr.selec = s1;
|
||||
|
|
@ -198,7 +224,9 @@ Selectivity clauselist_selectivity(
|
|||
}
|
||||
continue;
|
||||
}
|
||||
if ((uint32)u_sess->attr.attr_sql.cost_param & COST_ALTERNATIVE_CONJUNCT) {// 对于非范围查询子句,跟据不同的参数选择性计算策略更新选择性
|
||||
|
||||
/* Not the right form, so treat it generically. */
|
||||
if ((uint32)u_sess->attr.attr_sql.cost_param & COST_ALTERNATIVE_CONJUNCT) {
|
||||
s1 = MIN(s1, s2);
|
||||
expr->xpr.selec = s1;
|
||||
} else {
|
||||
|
|
@ -206,31 +234,59 @@ Selectivity clauselist_selectivity(
|
|||
expr->xpr.selec = s2;
|
||||
}
|
||||
}
|
||||
while (rqlist != NULL) {// 处理范围查询子句列表中的每个子句
|
||||
|
||||
/*
|
||||
* Now scan the rangequery pair list.
|
||||
*/
|
||||
while (rqlist != NULL) {
|
||||
RangeQueryClause* rqnext = NULL;
|
||||
|
||||
if (rqlist->have_lobound && rqlist->have_hibound) {
|
||||
/* Successfully matched a pair of range clauses */
|
||||
Selectivity s2;
|
||||
|
||||
if (rqlist->hibound == DEFAULT_INEQ_SEL || rqlist->lobound == DEFAULT_INEQ_SEL) {//如果上下限为默认值,那么选择性为默认值
|
||||
/*
|
||||
* Exact equality to the default value probably means the
|
||||
* selectivity function punted. This is not airtight but should
|
||||
* be good enough.
|
||||
*/
|
||||
if (rqlist->hibound == DEFAULT_INEQ_SEL || rqlist->lobound == DEFAULT_INEQ_SEL) {
|
||||
s2 = DEFAULT_RANGE_INEQ_SEL;
|
||||
} else {
|
||||
s2 = rqlist->hibound + rqlist->lobound - 1.0;//否则进行计算
|
||||
s2 = rqlist->hibound + rqlist->lobound - 1.0;
|
||||
|
||||
/* Adjust for double-exclusion of NULLs */
|
||||
s2 += nulltestsel(root, IS_NULL, rqlist->var, varRelid, jointype, sjinfo);
|
||||
|
||||
/*
|
||||
* A zero or slightly negative s2 should be converted into a
|
||||
* small positive value; we probably are dealing with a very
|
||||
* tight range and got a bogus result due to roundoff errors.
|
||||
* However, if s2 is very negative, then we probably have
|
||||
* default selectivity estimates on one or both sides of the
|
||||
* range that we failed to recognize above for some reason.
|
||||
*/
|
||||
if (s2 <= 0.0) {
|
||||
if (s2 < -0.01) {//如果选择性非常小,选择默认值
|
||||
if (s2 < -0.01) {
|
||||
/*
|
||||
* No data available --- use a default estimate that
|
||||
* is small, but not real small.
|
||||
*/
|
||||
s2 = DEFAULT_RANGE_INEQ_SEL;
|
||||
} else {//否则,取一个非常小的正数
|
||||
} else {
|
||||
/*
|
||||
* It's just roundoff error; use a small positive
|
||||
* value
|
||||
*/
|
||||
s2 = 1.0e-10;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Merge in the selectivity of the pair of clauses */
|
||||
s1 *= s2;
|
||||
rqlist->clause->selec = s2;
|
||||
} else {
|
||||
/* Only found one of a pair, merge it in generically */
|
||||
if (rqlist->have_lobound) {
|
||||
s1 *= rqlist->lobound;
|
||||
rqlist->clause->selec = rqlist->lobound;
|
||||
|
|
@ -245,16 +301,21 @@ Selectivity clauselist_selectivity(
|
|||
pfree_ext(rqlist);
|
||||
rqlist = rqnext;
|
||||
}
|
||||
if (varratio_cached && varlist != NIL)// 如果启用了变量比率缓存且存在变量列表,则设置变量比率
|
||||
|
||||
/* we should cache the range query's var ratio if can do and there are range query's vars. */
|
||||
if (varratio_cached && varlist != NIL)
|
||||
set_varratio_for_rqclause(root, varlist, varRelid, s1, sjinfo);
|
||||
|
||||
list_free_ext(varlist);
|
||||
if (es != NULL) {// 清理扩展统计信息相关内存
|
||||
|
||||
/* free space used by extended statistic */
|
||||
if (es != NULL) {
|
||||
clauselist = NIL;
|
||||
list_free_ext(es->unmatched_clause_group);
|
||||
delete es;
|
||||
MemoryContextDelete(ExtendedStat);
|
||||
}
|
||||
|
||||
return s1;
|
||||
}
|
||||
|
||||
|
|
@ -265,28 +326,27 @@ Selectivity clauselist_selectivity(
|
|||
*/
|
||||
static void addRangeClause(RangeQueryClause** rqlist, Node* clause, bool varonleft, bool isLTsel, Selectivity s2)
|
||||
{
|
||||
RangeQueryClause* rqelem = NULL;// 用于表示范围查询子句的数据结构
|
||||
Node* var = NULL; // 用于表示子句中的变量
|
||||
bool is_lobound = false; // 标识是否是下限子句
|
||||
RangeQueryClause* rqelem = NULL;
|
||||
Node* var = NULL;
|
||||
bool is_lobound = false;
|
||||
|
||||
|
||||
if (varonleft) {// 如果子句的变量在左侧,获取左操作数
|
||||
if (varonleft) {
|
||||
var = get_leftop((Expr*)clause);
|
||||
is_lobound = !isLTsel; /* x < something is high bound */
|
||||
} else {// 如果子句的变量在右侧,获取右操作数
|
||||
} else {
|
||||
var = get_rightop((Expr*)clause);
|
||||
is_lobound = isLTsel; /* something < x is low bound */
|
||||
}
|
||||
|
||||
for (rqelem = *rqlist; rqelem; rqelem = rqelem->next) {// 遍历已有的范围查询子句列表
|
||||
for (rqelem = *rqlist; rqelem; rqelem = rqelem->next) {
|
||||
/*
|
||||
* We use full equal() here because the "var" might be a function of
|
||||
* one or more attributes of the same relation...
|
||||
*/
|
||||
if (!equal(var, rqelem->var)) // 如果当前子句是下限子句且范围查询子句中没有下限子句,设置下限子句信息
|
||||
if (!equal(var, rqelem->var))
|
||||
continue;
|
||||
/* Found the right group to put this clause in */
|
||||
if (is_lobound) {// 如果已经有下限子句,比较并保留选择性较小的下限子句
|
||||
if (is_lobound) {
|
||||
if (!rqelem->have_lobound) {
|
||||
rqelem->have_lobound = true;
|
||||
rqelem->lobound = s2;
|
||||
|
|
@ -320,7 +380,7 @@ static void addRangeClause(RangeQueryClause** rqlist, Node* clause, bool varonle
|
|||
rqelem->clause = (Expr*)clause;
|
||||
return;
|
||||
}
|
||||
// 如果在范围查询子句列表中没有找到匹配的变量,创建一个新的范围查询子句并添加到列表
|
||||
|
||||
/* No matching var found, so make a new clause-pair data structure */
|
||||
rqelem = (RangeQueryClause*)palloc(sizeof(RangeQueryClause));
|
||||
rqelem->var = var;
|
||||
|
|
@ -344,15 +404,15 @@ static void addRangeClause(RangeQueryClause** rqlist, Node* clause, bool varonle
|
|||
* Decide whether an operator clause is to be handled by the
|
||||
* restriction or join estimator. Subroutine for clause_selectivity().
|
||||
*/
|
||||
bool treat_as_join_clause(Node* clause, RestrictInfo* rinfo, int varRelid, SpecialJoinInfo* sjinfo)//判断给定的子句是否应该被视为连接子句
|
||||
bool treat_as_join_clause(Node* clause, RestrictInfo* rinfo, int varRelid, SpecialJoinInfo* sjinfo)
|
||||
{
|
||||
if (varRelid != 0) {// 如果变量关联标识不为0,表示这个子句与一个特定的关系变量相关,不被视为连接子句
|
||||
if (varRelid != 0) {
|
||||
/*
|
||||
* Caller is forcing restriction mode (eg, because we are examining an
|
||||
* inner indexscan qual).
|
||||
*/
|
||||
return false;
|
||||
} else if (sjinfo == NULL) {// 如果特殊连接信息为空,也不被视为连接子句
|
||||
} else if (sjinfo == NULL) {
|
||||
/*
|
||||
* It must be a restriction clause, since it's being evaluated at a
|
||||
* scan node.
|
||||
|
|
@ -369,9 +429,9 @@ bool treat_as_join_clause(Node* clause, RestrictInfo* rinfo, int varRelid, Speci
|
|||
* anyway, it seems likely that we ought to account for the
|
||||
* probability of injected nulls somehow.
|
||||
*/
|
||||
if (rinfo != NULL)// 如果有 RestrictInfo,检查 RestrictInfo 中的关系变量是否属于多重集合(可能与多个表关联)
|
||||
if (rinfo != NULL)
|
||||
return (bms_membership(rinfo->clause_relids) == BMS_MULTIPLE);
|
||||
else// 如果没有 RestrictInfo,检查子句中的关系变量数量是否大于1(可能与多个表关联)
|
||||
else
|
||||
return (NumRelids(clause) > 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -422,9 +482,9 @@ Selectivity clause_selectivity(PlannerInfo* root, Node* clause, int varRelid, Jo
|
|||
SpecialJoinInfo* sjinfo, bool varratio_cached, bool check_scalarop)
|
||||
{
|
||||
Selectivity s1 = 0.5; /* default for any unhandled clause type */
|
||||
RestrictInfo* rinfo = NULL; // 用于存储约束信息的指针
|
||||
bool cacheable = false;// 是否可以缓存选择性值
|
||||
RatioType ratiotype = RatioType_Filter;// 选择性的类型,默认为过滤选择性
|
||||
RestrictInfo* rinfo = NULL;
|
||||
bool cacheable = false;
|
||||
RatioType ratiotype = RatioType_Filter;
|
||||
|
||||
if (clause == NULL) /* can this still happen? */
|
||||
return s1;
|
||||
|
|
@ -442,14 +502,14 @@ Selectivity clause_selectivity(PlannerInfo* root, Node* clause, int varRelid, Jo
|
|||
*/
|
||||
if (rinfo->pseudoconstant) {
|
||||
if (!IsA(rinfo->clause, Const))
|
||||
return (Selectivity)1.0;// 如果约束是伪常量但不是常量,则返回选择性1.0
|
||||
return (Selectivity)1.0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the clause is marked redundant, always return 1.0.
|
||||
*/
|
||||
if (rinfo->norm_selec > 1)
|
||||
return (Selectivity)1.0;// 如果约束的规范选择性大于1.0,则返回选择性1.0
|
||||
return (Selectivity)1.0;
|
||||
|
||||
/*
|
||||
* If possible, cache the result of the selectivity calculation for
|
||||
|
|
@ -474,7 +534,7 @@ Selectivity clause_selectivity(PlannerInfo* root, Node* clause, int varRelid, Jo
|
|||
else
|
||||
clause = (Node*)rinfo->clause;
|
||||
}
|
||||
// 处理不同类型的查询子句
|
||||
|
||||
if (IsA(clause, Var)) {
|
||||
Var* var = (Var*)clause;
|
||||
|
||||
|
|
@ -566,7 +626,7 @@ Selectivity clause_selectivity(PlannerInfo* root, Node* clause, int varRelid, Jo
|
|||
* This estimation method doesn't give the right behavior for nulls,
|
||||
* but it's better than doing nothing.
|
||||
*/
|
||||
if (IsA(clause, DistinctExpr))// 如果是去重表达式,计算去重后的选择性
|
||||
if (IsA(clause, DistinctExpr))
|
||||
s1 = 1.0 - s1;
|
||||
} else if (is_funcclause(clause)) {
|
||||
/*
|
||||
|
|
@ -622,7 +682,7 @@ Selectivity clause_selectivity(PlannerInfo* root, Node* clause, int varRelid, Jo
|
|||
if (jointype == JOIN_INNER)
|
||||
rinfo->norm_selec = s1;
|
||||
else
|
||||
rinfo->outer_selec = s1;// 如果约束可缓存,根据联接类型缓存选择性信息
|
||||
rinfo->outer_selec = s1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -643,15 +703,15 @@ Selectivity clause_selectivity(PlannerInfo* root, Node* clause, int varRelid, Jo
|
|||
/* Produce arg list, const convert to expr type */
|
||||
static List* switch_arg_items(Node* funExpr, Const* cnst, Oid* eqlOprOid, Oid* inputcollid, bool isequal)
|
||||
{
|
||||
List* argList = NULL;// 存储参数列表
|
||||
Node* arg = NULL;// 函数参数
|
||||
Const* cnp = NULL;// 常量参数
|
||||
Oid argType = InvalidOid;// 参数类型
|
||||
List* argList = NULL;
|
||||
Node* arg = NULL;
|
||||
Const* cnp = NULL;
|
||||
Oid argType = InvalidOid;
|
||||
|
||||
if (IsA(funExpr, FuncExpr) && ((FuncExpr*)funExpr)->funcformat == COERCE_IMPLICIT_CAST) { // 检查函数表达式是否为隐式强制类型转换函数,以及它是否具有正确的格式
|
||||
if (IsA(funExpr, FuncExpr) && ((FuncExpr*)funExpr)->funcformat == COERCE_IMPLICIT_CAST) {
|
||||
FuncExpr* fun_expr = (FuncExpr*)funExpr;
|
||||
arg = (Node*)linitial(fun_expr->args);
|
||||
argType = exprType(arg);// 获取参数的数据类型
|
||||
argType = exprType(arg);
|
||||
HeapTuple typeTuple;
|
||||
Oid funcId = 0;
|
||||
Oid constType = exprType((Node*)cnst);
|
||||
|
|
@ -662,145 +722,125 @@ static List* switch_arg_items(Node* funExpr, Const* cnst, Oid* eqlOprOid, Oid* i
|
|||
return NIL;
|
||||
}
|
||||
|
||||
// 查找强制类型转换的路径,获取pathtype(强制转换路径的类型)、funcId(强制转换函数的ID)
|
||||
CoercionPathType pathtype = find_coercion_pathway(argType, constType, COERCION_IMPLICIT, &funcId);
|
||||
CoercionPathType pathtype = find_coercion_pathway(argType, constType, COERCION_IMPLICIT, &funcId);
|
||||
|
||||
// 如果存在强制转换路径
|
||||
if (pathtype != COERCION_PATH_NONE) {
|
||||
// 保存当前内存上下文
|
||||
MemoryContext current_context = CurrentMemoryContext;
|
||||
bool outer_is_stream = false;
|
||||
bool outer_is_stream_support = false;
|
||||
// 保存当前资源拥有者
|
||||
ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner;
|
||||
// 创建临时资源拥有者
|
||||
ResourceOwner tempOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, "SwitchArgItems",
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER));
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = tempOwner;
|
||||
if (pathtype != COERCION_PATH_NONE) {
|
||||
MemoryContext current_context = CurrentMemoryContext;
|
||||
bool outer_is_stream = false;
|
||||
bool outer_is_stream_support = false;
|
||||
ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner;
|
||||
ResourceOwner tempOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, "SwitchArgItems",
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER));
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = tempOwner;
|
||||
|
||||
// 如果当前运行在PGXC协调器上
|
||||
if (IS_PGXC_COORDINATOR) {
|
||||
// 保存原始的流式查询标志和流式查询支持标志
|
||||
outer_is_stream = u_sess->opt_cxt.is_stream;
|
||||
outer_is_stream_support = u_sess->opt_cxt.is_stream_support;
|
||||
}
|
||||
if (IS_PGXC_COORDINATOR) {
|
||||
outer_is_stream = u_sess->opt_cxt.is_stream;
|
||||
outer_is_stream_support = u_sess->opt_cxt.is_stream_support;
|
||||
}
|
||||
|
||||
// 在PG_TRY块中执行强制转换函数,捕获可能的异常
|
||||
PG_TRY();
|
||||
{
|
||||
// 调用强制转换函数,将常量值转换为目标类型
|
||||
constValue = OidFunctionCall1(funcId, ((Const*)cnst)->constvalue);
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
// 切换回原来的内存上下文
|
||||
MemoryContextSwitchTo(current_context);
|
||||
FlushErrorState();
|
||||
PG_TRY();
|
||||
{
|
||||
constValue = OidFunctionCall1(funcId, ((Const*)cnst)->constvalue);
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
MemoryContextSwitchTo(current_context);
|
||||
FlushErrorState();
|
||||
|
||||
// 在PGXC协调器上,恢复流式查询标志和流式查询支持标志
|
||||
if (IS_PGXC_COORDINATOR) {
|
||||
u_sess->opt_cxt.is_stream = outer_is_stream;
|
||||
u_sess->opt_cxt.is_stream_support = outer_is_stream_support;
|
||||
/* in case they are not set back */
|
||||
if (IS_PGXC_COORDINATOR) {
|
||||
u_sess->opt_cxt.is_stream = outer_is_stream;
|
||||
u_sess->opt_cxt.is_stream_support = outer_is_stream_support;
|
||||
}
|
||||
|
||||
/* release resource applied in OidFunctionCall1 of the PG_TRY. */
|
||||
ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false);
|
||||
ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_LOCKS, false, false);
|
||||
ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, false);
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = currentOwner;
|
||||
ResourceOwnerDelete(tempOwner);
|
||||
|
||||
return NIL;
|
||||
}
|
||||
PG_END_TRY();
|
||||
|
||||
/* release resource applied in standard_planner of the PG_TRY. */
|
||||
ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false);
|
||||
ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_LOCKS, false, false);
|
||||
ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, false);
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = currentOwner;
|
||||
ResourceOwnerDelete(tempOwner);
|
||||
|
||||
if (IS_PGXC_COORDINATOR) {
|
||||
u_sess->opt_cxt.is_stream = outer_is_stream;
|
||||
u_sess->opt_cxt.is_stream_support = outer_is_stream_support;
|
||||
}
|
||||
}
|
||||
|
||||
// 释放在PG_TRY中申请的资源
|
||||
ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false);
|
||||
ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_LOCKS, false, false);
|
||||
ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, false);
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = currentOwner;
|
||||
ResourceOwnerDelete(tempOwner);
|
||||
|
||||
// 返回一个空列表(NIL)
|
||||
return NIL;
|
||||
if (constValue) {
|
||||
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argType));
|
||||
if (!HeapTupleIsValid(typeTuple)) {
|
||||
return NIL;
|
||||
}
|
||||
Form_pg_type type = (Form_pg_type)GETSTRUCT(typeTuple);
|
||||
cnp = makeConst(
|
||||
argType, exprTypmod(arg), type->typcollation, type->typlen, constValue, false, type->typbyval);
|
||||
ReleaseSysCache(typeTuple);
|
||||
}
|
||||
}
|
||||
PG_END_TRY();
|
||||
if (cnp != NULL) {
|
||||
argList = lappend(argList, arg);
|
||||
argList = lappend(argList, cnp);
|
||||
|
||||
// 释放在PG_TRY块中申请的资源
|
||||
ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false);
|
||||
ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_LOCKS, false, false);
|
||||
ResourceOwnerRelease(tempOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, false);
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = currentOwner;
|
||||
ResourceOwnerDelete(tempOwner);
|
||||
if (argType == VARCHAROID) {
|
||||
argType = TEXTOID;
|
||||
}
|
||||
|
||||
// 如果在PGXC协调器上,恢复流式查询标志和流式查询支持标志
|
||||
if (IS_PGXC_COORDINATOR) {
|
||||
u_sess->opt_cxt.is_stream = outer_is_stream;
|
||||
u_sess->opt_cxt.is_stream_support = outer_is_stream_support;
|
||||
HeapTuple opertup;
|
||||
opertup = SearchSysCache4(OPERNAMENSP,
|
||||
CStringGetDatum(isequal ? "=" : "<>"),
|
||||
ObjectIdGetDatum(argType),
|
||||
ObjectIdGetDatum(argType),
|
||||
ObjectIdGetDatum(PG_CATALOG_NAMESPACE));
|
||||
if (!HeapTupleIsValid(opertup)) {
|
||||
return NIL;
|
||||
}
|
||||
|
||||
*eqlOprOid = HeapTupleGetOid(opertup);
|
||||
|
||||
ReleaseSysCache(opertup);
|
||||
|
||||
*inputcollid = exprCollation(arg);
|
||||
}
|
||||
|
||||
return argList;
|
||||
}
|
||||
|
||||
// 如果成功执行强制类型转换,constValue将不为NULL
|
||||
if (constValue) {
|
||||
// 在系统缓存中查找目标类型的元组
|
||||
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argType));
|
||||
// 如果找不到目标类型的元组,返回一个空列表(NIL)
|
||||
if (!HeapTupleIsValid(typeTuple)) {
|
||||
return NIL;
|
||||
}
|
||||
// 获取目标类型的元组数据
|
||||
Form_pg_type type = (Form_pg_type)GETSTRUCT(typeTuple);
|
||||
// 创建一个新的常量节点,表示转换后的常量
|
||||
cnp = makeConst(
|
||||
argType, exprTypmod(arg), type->typcollation, type->typlen, constValue, false, type->typbyval);
|
||||
// 释放系统缓存中的目标类型元组
|
||||
ReleaseSysCache(typeTuple);
|
||||
}
|
||||
|
||||
// 如果cnp不为空,表示成功创建了新的常量节点
|
||||
if (cnp != NULL) {
|
||||
// 将原始参数和新的常量节点添加到参数列表中
|
||||
argList = lappend(argList, arg);
|
||||
argList = lappend(argList, cnp);
|
||||
|
||||
// 如果目标类型是VARCHAROID,则将argType更改为TEXTOID
|
||||
if (argType == VARCHAROID) {
|
||||
argType = TEXTOID;
|
||||
}
|
||||
|
||||
HeapTuple opertup;
|
||||
// 在系统缓存中查找等于(=)或不等于(<>)操作符的元组
|
||||
opertup = SearchSysCache4(OPERNAMENSP,
|
||||
CStringGetDatum(isequal ? "=" : "<>"),
|
||||
ObjectIdGetDatum(argType),
|
||||
ObjectIdGetDatum(argType),
|
||||
ObjectIdGetDatum(PG_CATALOG_NAMESPACE));
|
||||
// 如果找不到操作符的元组,返回一个空列表(NIL)
|
||||
if (!HeapTupleIsValid(opertup)) {
|
||||
return NIL;
|
||||
}
|
||||
|
||||
// 获取操作符的OID并存储在eqlOprOid指针中
|
||||
*eqlOprOid = HeapTupleGetOid(opertup);
|
||||
|
||||
// 释放系统缓存中的操作符元组
|
||||
ReleaseSysCache(opertup);
|
||||
|
||||
// 获取表达式的排序规则(collation)
|
||||
*inputcollid = exprCollation(arg);
|
||||
}
|
||||
|
||||
// 返回参数列表
|
||||
return argList;
|
||||
|
||||
static List* do_restrictinfo_conversion(List* args, Oid* eqlOprOid, Oid* inputcollid, bool isequal)
|
||||
{
|
||||
AssertEreport(list_length(args) == 2, MOD_OPT, "");// 使用 AssertEreport 断言确保传入的参数列表 args 包含且仅包含两个参数,否则引发错误
|
||||
bool lIsConst = false;//用于表示左参数和右参数是否为常量
|
||||
AssertEreport(list_length(args) == 2, MOD_OPT, "");
|
||||
|
||||
bool lIsConst = false;
|
||||
bool rIsConst = false;
|
||||
Node* lNode = (Node*)linitial(args);// 从参数列表 args 中提取左参数 lNode 和右参数 rNode。
|
||||
Node* lNode = (Node*)linitial(args);
|
||||
Node* rNode = (Node*)list_nth(args, 1);
|
||||
|
||||
List* argsList = NULL;
|
||||
|
||||
if (IsA(lNode, Const)) {
|
||||
lIsConst = true;
|
||||
}
|
||||
|
||||
if (IsA(rNode, Const)) {
|
||||
rIsConst = true;
|
||||
}
|
||||
if (lIsConst == true && rIsConst == false) {// 调用 switch_arg_items 函数,将右参数 rNode 视为常量,将左参数 lNode 视为函数参数,并将结果存储在 argsList 中
|
||||
|
||||
if (lIsConst == true && rIsConst == false) {
|
||||
argsList = switch_arg_items(rNode, (Const*)lNode, eqlOprOid, inputcollid, isequal);
|
||||
} else if (lIsConst == false && rIsConst == true) {
|
||||
argsList = switch_arg_items(lNode, (Const*)rNode, eqlOprOid, inputcollid, isequal);
|
||||
}
|
||||
|
||||
return argsList;
|
||||
}
|
||||
|
||||
|
|
@ -818,22 +858,19 @@ static List* do_restrictinfo_conversion(List* args, Oid* eqlOprOid, Oid* inputco
|
|||
*
|
||||
* Returns: void
|
||||
*/
|
||||
// 定义一个静态函数get_vardata_for_filter_or_semijoin,接受多个参数
|
||||
static void get_vardata_for_filter_or_semijoin(
|
||||
PlannerInfo* root, Node* clause, int varRelid, Selectivity selec, SpecialJoinInfo* sjinfo, RatioType type)
|
||||
{
|
||||
// 创建get_vardata_for_filter_or_semijoin_context结构体变量context,用于存储上下文信息
|
||||
get_vardata_for_filter_or_semijoin_context context;
|
||||
bool vardataIsValid = false;
|
||||
|
||||
/* 构建上下文成员 */
|
||||
context.root = root; // 存储PlannerInfo指针
|
||||
context.varRelid = varRelid; // 存储关联变量的Relid
|
||||
context.ratiotype = type; // 存储比率类型
|
||||
context.sjinfo = sjinfo; // 存储特殊连接信息
|
||||
/* construct context members. */
|
||||
context.root = root;
|
||||
context.varRelid = varRelid;
|
||||
context.ratiotype = type;
|
||||
context.sjinfo = sjinfo;
|
||||
errno_t rc = EOK;
|
||||
|
||||
// 初始化context中的VariableStatData结构体成员为0
|
||||
rc = memset_s(&context.filter_vardata, sizeof(VariableStatData), 0, sizeof(VariableStatData));
|
||||
securec_check(rc, "\0", "\0");
|
||||
rc = memset_s(&context.semijoin_vardata1, sizeof(VariableStatData), 0, sizeof(VariableStatData));
|
||||
|
|
@ -841,43 +878,36 @@ static void get_vardata_for_filter_or_semijoin(
|
|||
rc = memset_s(&context.semijoin_vardata2, sizeof(VariableStatData), 0, sizeof(VariableStatData));
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
||||
/* 获取clause中的变量数据 */
|
||||
// 调用get_vardata_for_filter_or_semijoin_walker函数,获取变量数据
|
||||
/* get vardata walker for clause. */
|
||||
vardataIsValid = get_vardata_for_filter_or_semijoin_walker(clause, &context);
|
||||
|
||||
/* 如果变量数据无效,不需要设置变量比率,直接返回 */
|
||||
/* we don't need set var ratio if vardata is invalid. */
|
||||
if (!vardataIsValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* 设置过滤器或半连接/反半连接的变量比率 */
|
||||
/* set var ratio for filter or semi/anti join. */
|
||||
if (RatioType_Filter == type) {
|
||||
set_varratio_after_calc_selectivity(&context.filter_vardata, RatioType_Filter, selec, NULL);
|
||||
// 释放VariableStatData资源
|
||||
ReleaseVariableStats(context.filter_vardata);
|
||||
} else {
|
||||
set_varratio_after_calc_selectivity(&context.semijoin_vardata1, RatioType_Join, selec, sjinfo);
|
||||
set_varratio_after_calc_selectivity(&context.semijoin_vardata2, RatioType_Join, selec, sjinfo);
|
||||
|
||||
// 释放VariableStatData资源
|
||||
ReleaseVariableStats(context.semijoin_vardata1);
|
||||
ReleaseVariableStats(context.semijoin_vardata2);
|
||||
}
|
||||
}
|
||||
|
||||
// 定义一个函数getVardataFromScalarArray,用于从标量数组中获取变量数据
|
||||
void getVardataFromScalarArray(Node* node, get_vardata_for_filter_or_semijoin_context* context)
|
||||
{
|
||||
Node* left = NULL;
|
||||
// 如果比率类型是Join
|
||||
if (RatioType_Join == context->ratiotype) {
|
||||
bool join_is_reversed = false;
|
||||
// 调用get_join_variables函数,获取连接中的变量数据
|
||||
get_join_variables(context->root, ((ScalarArrayOpExpr*)node)->args, context->sjinfo,
|
||||
&context->semijoin_vardata1, &context->semijoin_vardata2, &join_is_reversed);
|
||||
} else {
|
||||
left = (Node*)linitial(((ScalarArrayOpExpr*)node)->args);
|
||||
// 调用examine_variable函数,检查变量数据
|
||||
examine_variable(context->root, left, context->varRelid, &context->filter_vardata);
|
||||
}
|
||||
}
|
||||
|
|
@ -891,41 +921,39 @@ void getVardataFromScalarArray(Node* node, get_vardata_for_filter_or_semijoin_co
|
|||
*
|
||||
* Returns: bool(true:vardata is valid)
|
||||
*/
|
||||
// 定义静态函数get_vardata_for_filter_or_semijoin_walker,用于获取变量数据
|
||||
static bool get_vardata_for_filter_or_semijoin_walker(Node* node, get_vardata_for_filter_or_semijoin_context* context)
|
||||
{
|
||||
List* args = NIL; // 用于存储参数列表
|
||||
Node* other = NULL; // 用于存储另一个节点
|
||||
Node* left = NULL; // 用于存储左节点
|
||||
Node* clause = NULL; // 用于存储子节点
|
||||
bool varonleft = false; // 标志变量是否在左侧
|
||||
List* args = NIL;
|
||||
Node* other = NULL;
|
||||
Node* left = NULL;
|
||||
Node* clause = NULL;
|
||||
bool varonleft = false;
|
||||
|
||||
if (node == NULL)
|
||||
return false;
|
||||
|
||||
/* 从不同子节点中获取变量数据 */
|
||||
if (IsA(node, Var)) { // 如果节点是一个变量
|
||||
/* get vardata info from different clause's args. */
|
||||
if (IsA(node, Var)) {
|
||||
Var* var = (Var*)node;
|
||||
|
||||
/*
|
||||
* 我们可能不应该在这里看到uplevel Var,但如果出现,返回默认选择度...
|
||||
* We probably shouldn't ever see an uplevel Var here, but if we do,
|
||||
* return the default selectivity...
|
||||
*/
|
||||
if (var->varlevelsup == 0 && (context->varRelid == 0 || context->varRelid == (int)var->varno)) {
|
||||
// 检查变量数据
|
||||
examine_variable(context->root, (Node*)var, context->varRelid, &context->filter_vardata);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if (not_clause(node)) { // 如果节点是NOT表达式
|
||||
} else if (not_clause(node)) {
|
||||
clause = (Node*)get_notclausearg((Expr*)node);
|
||||
} else if (is_opclause(node)) { // 如果节点是操作符表达式
|
||||
} else if (is_opclause(node)) {
|
||||
OpExpr* opclause = (OpExpr*)node;
|
||||
Oid opno = opclause->opno;
|
||||
|
||||
if (RatioType_Join == context->ratiotype) { // 如果是连接比率类型
|
||||
if (RatioType_Join == context->ratiotype) {
|
||||
bool join_is_reversed = false;
|
||||
// 获取连接中的变量数据
|
||||
get_join_variables(context->root, opclause->args, context->sjinfo, &context->semijoin_vardata1,
|
||||
&context->semijoin_vardata2, &join_is_reversed);
|
||||
return true;
|
||||
|
|
@ -933,9 +961,8 @@ static bool get_vardata_for_filter_or_semijoin_walker(Node* node, get_vardata_fo
|
|||
Oid eqlOprOid = 0;
|
||||
List* argList = NULL;
|
||||
Oid inputcollid = 0;
|
||||
/* 只处理=或<>操作符 */
|
||||
/* only handle = or <> operator */
|
||||
if (get_oprrest(opno) == EQSELRETURNOID || get_oprrest(opno) == NEQSELRETURNOID) {
|
||||
// 进行操作符参数的转换
|
||||
argList = do_restrictinfo_conversion(
|
||||
opclause->args, &eqlOprOid, &inputcollid, get_oprrest(opno) == EQSELRETURNOID);
|
||||
}
|
||||
|
|
@ -945,36 +972,30 @@ static bool get_vardata_for_filter_or_semijoin_walker(Node* node, get_vardata_fo
|
|||
else
|
||||
args = opclause->args;
|
||||
|
||||
// 获取限制条件中的变量数据
|
||||
return get_restriction_variable(
|
||||
context->root, args, context->varRelid, &context->filter_vardata, &other, &varonleft);
|
||||
}
|
||||
} else if (IsA(node, ScalarArrayOpExpr)) { // 如果节点是标量数组操作符表达式
|
||||
// 从标量数组中获取变量数据
|
||||
} else if (IsA(node, ScalarArrayOpExpr)) {
|
||||
getVardataFromScalarArray(node, context);
|
||||
return true;
|
||||
} else if (IsA(node, RowCompareExpr)) { // 如果节点是行比较表达式
|
||||
} else if (IsA(node, RowCompareExpr)) {
|
||||
args = list_make2(linitial(((RowCompareExpr*)node)->largs), linitial(((RowCompareExpr*)node)->rargs));
|
||||
// 获取限制条件中的变量数据
|
||||
return get_restriction_variable(
|
||||
context->root, args, context->varRelid, &context->filter_vardata, &other, &varonleft);
|
||||
} else if (IsA(node, NullTest)) { // 如果节点是Null测试表达式
|
||||
} else if (IsA(node, NullTest)) {
|
||||
left = (Node*)((NullTest*)node)->arg;
|
||||
// 检查变量数据
|
||||
examine_variable(context->root, left, context->varRelid, &context->filter_vardata);
|
||||
return true;
|
||||
} else if (IsA(node, BooleanTest)) { // 如果节点是布尔测试表达式
|
||||
} else if (IsA(node, BooleanTest)) {
|
||||
left = (Node*)((BooleanTest*)node)->arg;
|
||||
// 检查变量数据
|
||||
examine_variable(context->root, left, context->varRelid, &context->filter_vardata);
|
||||
return true;
|
||||
} else if (IsA(node, RelabelType)) { // 如果节点是重新标记类型表达式
|
||||
} else if (IsA(node, RelabelType)) {
|
||||
clause = (Node*)((RelabelType*)node)->arg;
|
||||
} else if (IsA(node, CoerceToDomain)) { // 如果节点是强制转换为域类型表达式
|
||||
} else if (IsA(node, CoerceToDomain)) {
|
||||
clause = (Node*)((CoerceToDomain*)node)->arg;
|
||||
}
|
||||
|
||||
// 递归处理子节点
|
||||
return get_vardata_for_filter_or_semijoin_walker(clause, context);
|
||||
}
|
||||
|
||||
|
|
@ -989,10 +1010,8 @@ static bool get_vardata_for_filter_or_semijoin_walker(Node* node, get_vardata_fo
|
|||
* Returns: bool(true:the clause is range query)
|
||||
*/
|
||||
static bool is_rangequery_clause(Node* clause, RestrictInfo* rinfo, bool* varonleft)
|
||||
// 定义静态函数is_rangequery_clause,用于检测是否是范围查询子句
|
||||
{
|
||||
bool isrqclause = false;
|
||||
// 初始化isrqclause为假
|
||||
|
||||
/*
|
||||
* See if it looks like a restriction clause with a pseudoconstant on
|
||||
|
|
@ -1000,29 +1019,24 @@ static bool is_rangequery_clause(Node* clause, RestrictInfo* rinfo, bool* varonl
|
|||
* the simple way we are expecting.) Most of the tests here can be
|
||||
* done more efficiently with rinfo than without.
|
||||
*/
|
||||
// 如果是操作符表达式并且参数个数为2
|
||||
if (is_opclause(clause) && list_length(((OpExpr*)clause)->args) == 2) {
|
||||
OpExpr* expr = (OpExpr*)clause; // 强制转换为操作符表达式类型
|
||||
OpExpr* expr = (OpExpr*)clause;
|
||||
|
||||
// 如果有rinfo信息
|
||||
if (rinfo != NULL) {
|
||||
// 检查rinfo的clause_relids成员是否只包含一个集合成员
|
||||
// 同时检查表达式的左侧或右侧是否为伪常量子句
|
||||
isrqclause = (bms_membership(rinfo->clause_relids) == BMS_SINGLETON) &&
|
||||
(is_pseudo_constant_clause_relids((Node*)lsecond(expr->args), rinfo->right_relids) ||
|
||||
(*varonleft = false,
|
||||
is_pseudo_constant_clause_relids((Node*)linitial(expr->args), rinfo->left_relids)));
|
||||
} else { // 如果没有rinfo信息
|
||||
// 检查clause的NumRelids是否为1
|
||||
// 同时检查表达式的左侧或右侧是否为伪常量子句
|
||||
} else {
|
||||
isrqclause = (NumRelids(clause) == 1) &&
|
||||
(is_pseudo_constant_clause((Node*)lsecond(expr->args)) ||
|
||||
(*varonleft = false, is_pseudo_constant_clause((Node*)linitial(expr->args))));
|
||||
}
|
||||
}
|
||||
|
||||
return isrqclause; // 返回是否是范围查询子句的结果
|
||||
return isrqclause;
|
||||
}
|
||||
|
||||
/*
|
||||
* is_rangequery_contain_scalarop: if the range query contain scalar operator or not.
|
||||
*
|
||||
|
|
@ -1032,24 +1046,20 @@ static bool is_rangequery_clause(Node* clause, RestrictInfo* rinfo, bool* varonl
|
|||
*
|
||||
* Returns: bool(true:the range query contain scalar operator)
|
||||
*/
|
||||
// 定义静态函数is_rangequery_contain_scalarop,用于检测范围查询子句是否包含标量操作符
|
||||
static bool is_rangequery_contain_scalarop(Node* clause, RestrictInfo* rinfo)
|
||||
{
|
||||
bool varonleft = false; // 初始化变量varonleft为假
|
||||
bool varonleft = false;
|
||||
|
||||
// 调用is_rangequery_clause函数,检查子句是否符合范围查询条件,并获取varonleft值
|
||||
if (is_rangequery_clause(clause, rinfo, &varonleft)) {
|
||||
OpExpr* expr = (OpExpr*)clause; // 强制转换为操作符表达式类型
|
||||
OpExpr* expr = (OpExpr*)clause;
|
||||
|
||||
// 检查操作符的返回值是否为F_SCALARLTSEL或F_SCALARGTSEL
|
||||
if ((F_SCALARLTSEL == get_oprrest(expr->opno)) || (F_SCALARGTSEL == get_oprrest(expr->opno)))
|
||||
return true; // 如果满足条件,返回真
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // 如果不满足条件,返回假
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* set_varratio_for_rqclause: set var ratio for range query clause.
|
||||
*
|
||||
|
|
@ -1063,27 +1073,22 @@ static bool is_rangequery_contain_scalarop(Node* clause, RestrictInfo* rinfo)
|
|||
*
|
||||
* Returns: void
|
||||
*/
|
||||
static void set_varratio_for_rqclause(// 定义函数set_varratio_for_rqclause,用于为范围查询子句设置变量比率
|
||||
static void set_varratio_for_rqclause(
|
||||
PlannerInfo* root, List* varlist, int varRelid, double ratio, SpecialJoinInfo* sjinfo)
|
||||
{
|
||||
ListCell* lc = NULL; // 定义链表迭代器
|
||||
ListCell* lc = NULL;
|
||||
|
||||
// 遍历变量列表
|
||||
foreach (lc, varlist) {
|
||||
VariableStatData vardata; // 定义变量统计数据结构体
|
||||
Node* node = (Node*)lfirst(lc); // 获取链表元素并强制转换为Node类型
|
||||
VariableStatData vardata;
|
||||
Node* node = (Node*)lfirst(lc);
|
||||
|
||||
// 调用examine_variable函数,检查变量数据
|
||||
examine_variable(root, node, varRelid, &vardata);
|
||||
|
||||
// 根据sjinfo是否为空,调用不同的函数设置变量比率
|
||||
if (sjinfo == NULL)
|
||||
set_varratio_after_calc_selectivity(&vardata, RatioType_Filter, ratio, NULL);
|
||||
else
|
||||
set_varratio_after_calc_selectivity(&vardata, RatioType_Join, ratio, sjinfo);
|
||||
|
||||
ReleaseVariableStats(vardata); // 释放变量统计数据资源
|
||||
ReleaseVariableStats(vardata);
|
||||
}
|
||||
}
|
||||
|
||||
//这两个函数分别用于检测范围查询子句是否包含标量操作符,并为范围查询子句中的变量设置比率。第一个函数返回一个布尔值,指示是否包含标量操作符,第二个函数遍历变量列表,并根据条件设置变量比率。
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -71,40 +71,35 @@ static PathKey* makePathKey(EquivalenceClass* eclass, Oid opfamily, int strategy
|
|||
static PathKey* make_canonical_pathkey(
|
||||
PlannerInfo* root, EquivalenceClass* eclass, Oid opfamily, int strategy, bool nulls_first)
|
||||
{
|
||||
PathKey* pk = NULL; // 声明一个指向PathKey的指针,并将其初始化为NULL。
|
||||
ListCell* lc = NULL; // 声明一个指向ListCell的指针,并将其初始化为NULL。
|
||||
MemoryContext oldcontext; // 声明一个MemoryContext变量,命名为oldcontext。
|
||||
PathKey* pk = NULL;
|
||||
ListCell* lc = NULL;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
// 遍历等价类(EquivalenceClass)的层次结构,直到达到顶层的等价类。
|
||||
/* The passed eclass might be non-canonical, so chase up to the top */
|
||||
while (eclass->ec_merged)
|
||||
eclass = eclass->ec_merged;
|
||||
|
||||
// 遍历PlannerInfo中的规范路径键(canonical pathkeys)列表。
|
||||
foreach (lc, root->canon_pathkeys) {
|
||||
// 获取列表中的下一个PathKey。
|
||||
/* Here need ensure ec_group_set be also equal. */
|
||||
pk = (PathKey*)lfirst(lc);
|
||||
|
||||
// 检查当前PathKey是否与给定的等价类(eclass)、操作族(opfamily)、策略(strategy)和nulls_first相匹配。
|
||||
if (eclass == pk->pk_eclass && eclass->ec_group_set == pk->pk_eclass->ec_group_set &&
|
||||
OpFamilyEquals(opfamily, pk->pk_opfamily) && strategy == pk->pk_strategy &&
|
||||
nulls_first == pk->pk_nulls_first)
|
||||
return pk; // 如果找到匹配的PathKey,则返回该PathKey。
|
||||
return pk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Be sure canonical pathkeys are allocated in the main planning context.
|
||||
* Not an issue in normal planning, but it is for GEQO.
|
||||
*/
|
||||
// 切换到另一个内存上下文以进行分配。
|
||||
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
|
||||
|
||||
// 使用提供的参数创建新的PathKey,并将其添加到规范路径键列表中。
|
||||
pk = makePathKey(eclass, opfamily, strategy, nulls_first);
|
||||
root->canon_pathkeys = lappend(root->canon_pathkeys, pk);
|
||||
|
||||
// 切换回原始内存上下文。
|
||||
(void)MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
return pk; // 返回创建的或现有的PathKey。
|
||||
return pk;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -139,52 +134,50 @@ static PathKey* make_canonical_pathkey(
|
|||
*/
|
||||
static bool pathkey_is_redundant(PathKey* new_pathkey, List* pathkeys, bool predpush)
|
||||
{
|
||||
EquivalenceClass* new_ec = new_pathkey->pk_eclass; // 获取新路径键的等价类。
|
||||
EquivalenceClass* new_ec = new_pathkey->pk_eclass;
|
||||
ListCell* lc = NULL;
|
||||
|
||||
// 使用断言确保新等价类未合并。
|
||||
/* Assert we've been given canonical pathkeys */
|
||||
Assert(!new_ec->ec_merged);
|
||||
|
||||
if (predpush) { // 如果是谓词推送(predicate pushdown):
|
||||
|
||||
bool have_const = false; // 初始化一个标志,表示是否存在常量表达式。
|
||||
|
||||
if (EC_MUST_BE_REDUNDANT(new_ec)) { // 检查等价类是否必须是冗余的。
|
||||
|
||||
/* Check for EC containing a constant --- unconditionally redundant */
|
||||
if (predpush) {
|
||||
/* skip the Param */
|
||||
bool have_const = false;
|
||||
if (EC_MUST_BE_REDUNDANT(new_ec))
|
||||
{
|
||||
lc = NULL;
|
||||
foreach (lc, new_ec->ec_members) { // 遍历等价类的成员。
|
||||
foreach (lc, new_ec->ec_members) {
|
||||
EquivalenceMember *mem = (EquivalenceMember *)lfirst(lc);
|
||||
if (mem->em_is_const && !check_param_clause((Node *)mem->em_expr)) {
|
||||
have_const = true; // 如果存在常量表达式且不是参数子句,将标志设置为true。
|
||||
have_const = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果存在常量且不在外连接下,且没有等价类组合,则认为是冗余的。
|
||||
if ((have_const && !new_ec->ec_below_outer_join) && !new_ec->ec_group_set)
|
||||
return true;
|
||||
|
||||
} else { // 如果不是谓词推送:
|
||||
|
||||
// 如果等价类必须是冗余的且没有等价类组合,则认为是冗余的。
|
||||
} else {
|
||||
if (EC_MUST_BE_REDUNDANT(new_ec) && !new_ec->ec_group_set)
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (lc, pathkeys) { // 遍历路径键列表。
|
||||
PathKey* old_pathkey = (PathKey*)lfirst(lc); // 获取旧路径键。
|
||||
/* If same EC already used in list, then redundant */
|
||||
foreach (lc, pathkeys) {
|
||||
PathKey* old_pathkey = (PathKey*)lfirst(lc);
|
||||
|
||||
// 使用断言确保旧路径键的等价类未合并。
|
||||
/* Assert we've been given canonical pathkeys */
|
||||
Assert(!old_pathkey->pk_eclass->ec_merged);
|
||||
|
||||
if (new_ec == old_pathkey->pk_eclass) // 如果新等价类与旧路径键的等价类相同,则认为是冗余的。
|
||||
if (new_ec == old_pathkey->pk_eclass)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // 如果没有冗余,返回false。
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* canonicalize_pathkeys
|
||||
* Convert a not-necessarily-canonical pathkeys list to canonical form.
|
||||
|
|
@ -235,31 +228,33 @@ List* canonicalize_pathkeys(PlannerInfo* root, List* pathkeys)
|
|||
*/
|
||||
List* remove_param_pathkeys(PlannerInfo* root, List* pathkeys)
|
||||
{
|
||||
List* new_pathkeys = NIL; // 声明一个新的路径键列表,并初始化为空列表。
|
||||
ListCell* l = NULL; // 声明一个指向ListCell的指针,并初始化为空。
|
||||
List* new_pathkeys = NIL;
|
||||
ListCell* l = NULL;
|
||||
|
||||
if (pathkeys == NULL) // 如果输入的路径键列表为空,直接返回空列表。
|
||||
if (pathkeys == NULL)
|
||||
return NULL;
|
||||
|
||||
foreach (l, pathkeys) { // 遍历输入的路径键列表。
|
||||
PathKey* pathkey = (PathKey*)lfirst(l); // 获取当前路径键。
|
||||
foreach (l, pathkeys) {
|
||||
PathKey* pathkey = (PathKey*)lfirst(l);
|
||||
EquivalenceClass* eclass = NULL;
|
||||
|
||||
EquivalenceClass* eclass = NULL; // 声明一个指向等价类的指针,并初始化为空。
|
||||
/* Find the canonical (merged) EquivalenceClass */
|
||||
eclass = pathkey->pk_eclass;
|
||||
Assert(eclass->ec_merged == NULL);
|
||||
|
||||
eclass = pathkey->pk_eclass; // 获取路径键的等价类。
|
||||
Assert(eclass->ec_merged == NULL); // 使用断言确保等价类未合并。
|
||||
|
||||
// 如果等价类必须是冗余的且没有等价类组合,则跳过当前路径键。
|
||||
/*
|
||||
* If we can tell it's redundant just from the EC, skip.
|
||||
* pathkey_is_redundant would notice that, but we needn't even bother
|
||||
* constructing the node...
|
||||
*/
|
||||
if (EC_MUST_BE_REDUNDANT(eclass) && !eclass->ec_group_set)
|
||||
continue;
|
||||
|
||||
new_pathkeys = lappend(new_pathkeys, pathkey); // 否则,将路径键添加到新的路径键列表中。
|
||||
new_pathkeys = lappend(new_pathkeys, pathkey);
|
||||
}
|
||||
|
||||
return new_pathkeys; // 返回新的路径键列表。
|
||||
return new_pathkeys;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* make_pathkey_from_sortinfo
|
||||
* Given an expression and sort-order information, create a PathKey.
|
||||
|
|
@ -284,12 +279,11 @@ List* remove_param_pathkeys(PlannerInfo* root, List* pathkeys)
|
|||
static PathKey* make_pathkey_from_sortinfo(PlannerInfo* root, Expr* expr, Oid opfamily, Oid opcintype, Oid collation,
|
||||
bool reverse_sort, bool nulls_first, Index sortref, bool groupSet, Relids rel, bool create_it, bool canonicalize)
|
||||
{
|
||||
int16 strategy; // 声明一个int16类型的变量strategy,用于表示排序策略。
|
||||
Oid equality_op; // 声明一个Oid类型的变量equality_op,用于表示相等比较运算符。
|
||||
List* opfamilies = NIL; // 声明一个List类型的变量opfamilies,用于保存操作族列表。
|
||||
EquivalenceClass* eclass = NULL; // 声明一个EquivalenceClass类型的指针eclass,初始化为NULL。
|
||||
int16 strategy;
|
||||
Oid equality_op;
|
||||
List* opfamilies = NIL;
|
||||
EquivalenceClass* eclass = NULL;
|
||||
|
||||
// 根据是否逆序设置排序策略。
|
||||
strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber;
|
||||
|
||||
/*
|
||||
|
|
@ -298,36 +292,28 @@ static PathKey* make_pathkey_from_sortinfo(PlannerInfo* root, Expr* expr, Oid op
|
|||
* more than one opfamily. So we have to look up the opfamily's equality
|
||||
* operator and get its membership.
|
||||
*/
|
||||
// 获取操作族中的相等比较运算符。
|
||||
equality_op = get_opfamily_member(opfamily, opcintype, opcintype, BTEqualStrategyNumber);
|
||||
|
||||
// 如果没有找到相等比较运算符,报告错误。
|
||||
if (!OidIsValid(equality_op))
|
||||
if (!OidIsValid(equality_op)) /* shouldn't happen */
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
||||
(errmsg(
|
||||
"could not find equality operator for opfamily %u when make pathkey from sortinfo", opfamily))));
|
||||
|
||||
// 获取相等比较运算符对应的操作族列表。
|
||||
opfamilies = get_mergejoin_opfamilies(equality_op);
|
||||
|
||||
// 如果没有找到操作族列表,报告错误。
|
||||
if (opfamilies == NIL)
|
||||
if (opfamilies == NIL) /* certainly should find some */
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
||||
(errmsg("could not find opfamilies for equality operator %u when make pathkey from sortinfo",
|
||||
equality_op))));
|
||||
|
||||
// 为排序表达式获取等价类。
|
||||
/* Now find or (optionally) create a matching EquivalenceClass */
|
||||
eclass = get_eclass_for_sort_expr(root, expr, opfamilies, opcintype, collation, sortref, groupSet, rel, create_it);
|
||||
|
||||
// 如果未找到等价类,返回NULL。
|
||||
/* Fail if no EC and !create_it */
|
||||
if (eclass == NULL)
|
||||
return NULL;
|
||||
|
||||
// 如果要规范化路径键,调用make_canonical_pathkey函数,否则调用makePathKey函数。
|
||||
/* And finally we can find or create a PathKey node */
|
||||
if (canonicalize)
|
||||
return make_canonical_pathkey(root, eclass, opfamily, strategy, nulls_first);
|
||||
else
|
||||
|
|
@ -347,17 +333,16 @@ static PathKey* make_pathkey_from_sortop(PlannerInfo* root, Expr* expr, Oid orde
|
|||
Oid opfamily, opcintype, collation;
|
||||
int16 strategy;
|
||||
|
||||
/* 通过在pg_amop中查找操作符,获取操作族、操作数类型和排序策略 */
|
||||
/* Find the operator in pg_amop --- failure shouldn't happen */
|
||||
if (!get_ordering_op_properties(ordering_op, &opfamily, &opcintype, &strategy))
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
||||
(errmsg("operator %u is not a valid ordering operator when make pathkey from sortinfo", ordering_op))));
|
||||
|
||||
/* 由于SortGroupClause不携带排序规则,需查看表达式获取排序规则 */
|
||||
/* Because SortGroupClause doesn't carry collation, consult the expr */
|
||||
collation = exprCollation((Node*)expr);
|
||||
|
||||
// 调用make_pathkey_from_sortinfo函数来创建路径键,并返回结果。
|
||||
return make_pathkey_from_sortinfo(root,
|
||||
expr,
|
||||
opfamily,
|
||||
|
|
@ -371,6 +356,7 @@ static PathKey* make_pathkey_from_sortop(PlannerInfo* root, Expr* expr, Oid orde
|
|||
create_it,
|
||||
canonicalize);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* PATHKEY COMPARISONS
|
||||
****************************************************************************/
|
||||
|
|
@ -406,32 +392,25 @@ PathKeysComparison compare_pathkeys(List* keys1, List* keys2)
|
|||
* but PlannerInfo not accessible here...
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
// 使用断言确保pathkey1在root->canon_pathkeys中。
|
||||
AssertEreport(list_member_ptr(root->canon_pathkeys, pathkey1), MOD_OPT, "pathkey1 is not a member in pathkeys");
|
||||
AssertEreport(list_member_ptr(root->canon_pathkeys, pathkey1), MOD_OPT, "pathky1 is not a member in pathkeys");
|
||||
|
||||
// 使用断言确保pathkey2在root->canon_pathkeys中。
|
||||
AssertEreport(list_member_ptr(root->canon_pathkeys, pathkey2), MOD_OPT, "pathkey2 is not a member in pathkeys");
|
||||
AssertEreport(list_member_ptr(root->canon_pathkeys, pathkey2), MOD_OPT, "pathky2 is not a member in pathkeys");
|
||||
#endif
|
||||
|
||||
// 如果pathkey1等于pathkey2,继续循环。
|
||||
if (pathkey1 == pathkey2) {
|
||||
continue;
|
||||
if (pathkey1 == pathkey2) {
|
||||
continue;
|
||||
}
|
||||
if (pathkey1 == NULL && pathkey2 != NULL) {
|
||||
return PATHKEYS_DIFFERENT; /* no need to keep looking */
|
||||
}
|
||||
if (pathkey1 != NULL && pathkey2 == NULL) {
|
||||
return PATHKEYS_DIFFERENT; /* no need to keep looking */
|
||||
}
|
||||
if (pathkey1->type != pathkey2->type || !OpFamilyEquals(pathkey1->pk_opfamily, pathkey2->pk_opfamily) ||
|
||||
pathkey1->pk_eclass != pathkey2->pk_eclass || pathkey1->pk_strategy != pathkey2->pk_strategy ||
|
||||
pathkey1->pk_nulls_first != pathkey2->pk_nulls_first) {
|
||||
return PATHKEYS_DIFFERENT; /* no need to keep looking */
|
||||
}
|
||||
}
|
||||
// 如果pathkey1为NULL而pathkey2不为NULL,则返回PATHKEYS_DIFFERENT,不再继续查找。
|
||||
if (pathkey1 == NULL && pathkey2 != NULL) {
|
||||
return PATHKEYS_DIFFERENT;
|
||||
}
|
||||
// 如果pathkey1不为NULL而pathkey2为NULL,则返回PATHKEYS_DIFFERENT,不再继续查找。
|
||||
if (pathkey1 != NULL && pathkey2 == NULL) {
|
||||
return PATHKEYS_DIFFERENT;
|
||||
}
|
||||
// 如果pathkey1和pathkey2的类型、操作族、等价类、策略和nulls_first都不相等,则返回PATHKEYS_DIFFERENT,不再继续查找。
|
||||
if (pathkey1->type != pathkey2->type || !OpFamilyEquals(pathkey1->pk_opfamily, pathkey2->pk_opfamily) ||
|
||||
pathkey1->pk_eclass != pathkey2->pk_eclass || pathkey1->pk_strategy != pathkey2->pk_strategy ||
|
||||
pathkey1->pk_nulls_first != pathkey2->pk_nulls_first) {
|
||||
return PATHKEYS_DIFFERENT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we reached the end of only one list, the other is longer and
|
||||
|
|
@ -553,34 +532,32 @@ Path* get_cheapest_fractional_path_for_pathkeys(List* paths, List* pathkeys, Rel
|
|||
*/
|
||||
List* build_index_pathkeys(PlannerInfo* root, IndexOptInfo* index, ScanDirection scandir)
|
||||
{
|
||||
List* retval = NIL; // 声明一个结果列表,并初始化为空。
|
||||
ListCell* lc = NULL; // 声明一个指向ListCell的指针,并初始化为空。
|
||||
List* retval = NIL;
|
||||
ListCell* lc = NULL;
|
||||
int i;
|
||||
|
||||
// 如果索引没有排序操作符族(sortopfamily),返回一个空列表,表示索引不支持有序扫描。
|
||||
if (index->sortopfamily == NULL)
|
||||
return NIL;
|
||||
return NIL; /* non-orderable index */
|
||||
|
||||
i = 0;
|
||||
foreach (lc, index->indextlist) { // 遍历索引列列表。
|
||||
TargetEntry* indextle = (TargetEntry*)lfirst(lc); // 获取索引目标条目。
|
||||
Expr* indexkey = NULL; // 声明一个表达式指针,并初始化为空。
|
||||
bool reverse_sort = false; // 是否逆序排序的标志,默认为false。
|
||||
bool nulls_first = false; // NULL值排在前面的标志,默认为false。
|
||||
PathKey* cpathkey = NULL; // 声明一个PathKey指针,并初始化为空。
|
||||
foreach (lc, index->indextlist) {
|
||||
TargetEntry* indextle = (TargetEntry*)lfirst(lc);
|
||||
Expr* indexkey = NULL;
|
||||
bool reverse_sort = false;
|
||||
bool nulls_first = false;
|
||||
PathKey* cpathkey = NULL;
|
||||
|
||||
/*
|
||||
* INCLUDE列存储在索引中无序,因此它们不支持有序索引扫描。
|
||||
* 如果i大于或等于索引的关键列数,跳出循环。
|
||||
* INCLUDE columns are stored in index unordered, so they don't
|
||||
* support ordered index scan.
|
||||
*/
|
||||
if (i >= index->nkeycolumns) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* 假设我们不需要复制tlist项目 */
|
||||
indexkey = indextle->expr; // 获取索引键的表达式。
|
||||
/* We assume we don't need to make a copy of the tlist item */
|
||||
indexkey = indextle->expr;
|
||||
|
||||
// 根据扫描方向设置逆序排序和NULL值排在前面的标志。
|
||||
if (ScanDirectionIsBackward(scandir)) {
|
||||
reverse_sort = !index->reverse_sort[i];
|
||||
nulls_first = !index->nulls_first[i];
|
||||
|
|
@ -589,7 +566,7 @@ List* build_index_pathkeys(PlannerInfo* root, IndexOptInfo* index, ScanDirection
|
|||
nulls_first = index->nulls_first[i];
|
||||
}
|
||||
|
||||
/* 尝试为这个排序键创建一个规范路径键 */
|
||||
/* OK, try to make a canonical pathkey for this sort key */
|
||||
cpathkey = make_pathkey_from_sortinfo(root,
|
||||
indexkey,
|
||||
index->sortopfamily[i],
|
||||
|
|
@ -604,23 +581,23 @@ List* build_index_pathkeys(PlannerInfo* root, IndexOptInfo* index, ScanDirection
|
|||
true);
|
||||
|
||||
/*
|
||||
* 如果排序键尚未存在于任何等价类中,则它对于此查询不是有趣的排序顺序。
|
||||
* 因此,我们可以停止遍历 --- 较低阶的排序键也不有用。
|
||||
* If the sort key isn't already present in any EquivalenceClass, then
|
||||
* it's not an interesting sort order for this query. So we can stop
|
||||
* now --- lower-order sort keys aren't useful either.
|
||||
*/
|
||||
if (cpathkey == NULL)
|
||||
break;
|
||||
|
||||
/* 将路径键添加到列表,除非它是冗余的 */
|
||||
/* Add to list unless redundant */
|
||||
if (!pathkey_is_redundant(cpathkey, retval))
|
||||
retval = lappend(retval, cpathkey);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return retval; // 返回结果列表。
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* convert_subquery_pathkeys
|
||||
* Build a pathkeys list that describes the ordering of a subquery's
|
||||
|
|
@ -636,55 +613,43 @@ List* build_index_pathkeys(PlannerInfo* root, IndexOptInfo* index, ScanDirection
|
|||
*/
|
||||
List* convert_subquery_pathkeys(PlannerInfo* root, RelOptInfo* rel, List* subquery_pathkeys)
|
||||
{
|
||||
List* retval = NIL; // 声明一个结果列表,并初始化为空。
|
||||
int retvallen = 0; // 初始化结果列表的长度为0。
|
||||
int outer_query_keys = list_length(root->query_pathkeys); // 获取外部查询的路径键数。
|
||||
List* sub_tlist = rel->subplan->targetlist; // 获取子查询的目标列表。
|
||||
ListCell* i = NULL; // 声明一个指向ListCell的指针,并初始化为空。
|
||||
List* retval = NIL;
|
||||
int retvallen = 0;
|
||||
int outer_query_keys = list_length(root->query_pathkeys);
|
||||
List* sub_tlist = rel->subplan->targetlist;
|
||||
ListCell* i = NULL;
|
||||
|
||||
// 遍历子查询的路径键列表。
|
||||
foreach (i, subquery_pathkeys) {
|
||||
PathKey* sub_pathkey = (PathKey*)lfirst(i); // 获取子查询路径键。
|
||||
EquivalenceClass* sub_eclass = sub_pathkey->pk_eclass; // 获取子查询等价类。
|
||||
PathKey* best_pathkey = NULL; // 声明一个最佳路径键指针,并初始化为空。
|
||||
PathKey* sub_pathkey = (PathKey*)lfirst(i);
|
||||
EquivalenceClass* sub_eclass = sub_pathkey->pk_eclass;
|
||||
PathKey* best_pathkey = NULL;
|
||||
|
||||
if (sub_eclass->ec_has_volatile) {
|
||||
/*
|
||||
* 如果子查询路径键的等价类是易失性的,那么它必须来自于ORDER BY子句,
|
||||
* 我们必须将它与相同的目标列表条目匹配。
|
||||
* If the sub_pathkey's EquivalenceClass is volatile, then it must
|
||||
* have come from an ORDER BY clause, and we have to match it to
|
||||
* that same targetlist entry.
|
||||
*/
|
||||
TargetEntry* tle = NULL;
|
||||
|
||||
// 如果子查询路径键的等价类没有排序引用(sortref),则报告错误。
|
||||
if (sub_eclass->ec_sortref == 0)
|
||||
if (sub_eclass->ec_sortref == 0) /* can't happen */
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
||||
(errmsg("volatile EquivalenceClass has no sortref when convert subquery pathkeys"))));
|
||||
|
||||
// 获取与子查询路径键等价类对应的目标列表条目。
|
||||
tle = get_sortgroupref_tle(sub_eclass->ec_sortref, sub_tlist);
|
||||
|
||||
// 使用断言确保目标列表条目不为空。
|
||||
AssertEreport(tle != NULL, MOD_OPT, "tle is NULL");
|
||||
|
||||
/* resjunk 项对外部查询不可见 */
|
||||
/* resjunk items aren't visible to outer query */
|
||||
if (!tle->resjunk) {
|
||||
/* 我们可以表示这个子查询路径键 */
|
||||
/* We can represent this sub_pathkey */
|
||||
EquivalenceMember* sub_member = NULL;
|
||||
Expr* outer_expr = NULL;
|
||||
EquivalenceClass* outer_ec = NULL;
|
||||
|
||||
// 使用断言确保等价类成员列表长度为1。
|
||||
AssertEreport(list_length(sub_eclass->ec_members) == 1, MOD_OPT, "ec member number is not 1");
|
||||
|
||||
// 获取等价类的成员。
|
||||
sub_member = (EquivalenceMember*)linitial(sub_eclass->ec_members);
|
||||
|
||||
// 创建外部查询的表达式,以引用子查询的目标列表条目。
|
||||
outer_expr = (Expr*)makeVarFromTargetEntry(rel->relid, tle);
|
||||
|
||||
|
||||
/*
|
||||
* Note: it might look funny to be setting sortref = 0 for a
|
||||
* reference to a volatile sub_eclass. However, the
|
||||
|
|
@ -731,47 +696,44 @@ List* convert_subquery_pathkeys(PlannerInfo* root, RelOptInfo* rel, List* subque
|
|||
* query_pathkeys). This is the most likely to be useful in the
|
||||
* outer query.
|
||||
*/
|
||||
int best_score = -1; // 初始化最佳分数为-1。
|
||||
ListCell* j = NULL; // 声明一个指向ListCell的指针,并初始化为空。
|
||||
int best_score = -1;
|
||||
ListCell* j = NULL;
|
||||
|
||||
foreach (j, sub_eclass->ec_members) {
|
||||
EquivalenceMember* sub_member = (EquivalenceMember*)lfirst(j); // 获取等价类成员。
|
||||
Expr* sub_expr = sub_member->em_expr; // 获取等价类成员的表达式。
|
||||
Oid sub_expr_type = sub_member->em_datatype; // 获取等价类成员的数据类型。
|
||||
Oid sub_expr_coll = sub_eclass->ec_collation; // 获取等价类成员的排序规则。
|
||||
ListCell* k = NULL;
|
||||
int seq = 0;
|
||||
foreach (j, sub_eclass->ec_members) {
|
||||
EquivalenceMember* sub_member = (EquivalenceMember*)lfirst(j);
|
||||
Expr* sub_expr = sub_member->em_expr;
|
||||
Oid sub_expr_type = sub_member->em_datatype;
|
||||
Oid sub_expr_coll = sub_eclass->ec_collation;
|
||||
ListCell* k = NULL;
|
||||
int seq = 0;
|
||||
|
||||
if (sub_member->em_is_child)
|
||||
continue; /* 忽略子查询的子查询等价类 */
|
||||
if (sub_member->em_is_child)
|
||||
continue; /* ignore children here */
|
||||
|
||||
foreach (k, sub_tlist) {
|
||||
TargetEntry* tle = (TargetEntry*)lfirst(k); // 获取子查询目标列表的目标条目。
|
||||
Expr* tle_expr = NULL;
|
||||
Expr* outer_expr = NULL;
|
||||
EquivalenceClass* outer_ec = NULL;
|
||||
PathKey* outer_pk = NULL;
|
||||
int score;
|
||||
ListCell* lc = NULL;
|
||||
foreach (k, sub_tlist) {
|
||||
TargetEntry* tle = (TargetEntry*)lfirst(k);
|
||||
Expr* tle_expr = NULL;
|
||||
Expr* outer_expr = NULL;
|
||||
EquivalenceClass* outer_ec = NULL;
|
||||
PathKey* outer_pk = NULL;
|
||||
int score;
|
||||
ListCell* lc = NULL;
|
||||
|
||||
seq++;
|
||||
seq++;
|
||||
|
||||
/* resjunk 项对外部查询不可见 */
|
||||
if (tle->resjunk)
|
||||
continue;
|
||||
/* resjunk items aren't visible to outer query */
|
||||
if (tle->resjunk)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* 检查目标条目是否存在于最终的子查询目标列表中。
|
||||
* 如果不在子查询目标列表中,则继续下一条目。
|
||||
*/
|
||||
foreach (lc, rel->reltargetlist) {
|
||||
Node* n = (Node*)lfirst(lc);
|
||||
if (IsA(n, Var) && ((Var*)n)->varattno == seq)
|
||||
break;
|
||||
}
|
||||
/* check if targetentry exists in final subquery targetlist */
|
||||
foreach (lc, rel->reltargetlist) {
|
||||
Node* n = (Node*)lfirst(lc);
|
||||
if (IsA(n, Var) && ((Var*)n)->varattno == seq)
|
||||
break;
|
||||
}
|
||||
|
||||
if (lc == NULL)
|
||||
continue;
|
||||
if (lc == NULL)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The targetlist entry is considered to match if it
|
||||
|
|
@ -899,22 +861,16 @@ List* build_join_pathkeys(PlannerInfo* root, RelOptInfo* joinrel, JoinType joint
|
|||
*/
|
||||
List* make_pathkeys_for_sortclauses(PlannerInfo* root, List* sortclauses, List* tlist, bool canonicalize)
|
||||
{
|
||||
List* pathkeys = NIL; // 声明一个路径键列表,并初始化为空。
|
||||
ListCell* l = NULL; // 声明一个指向ListCell的指针,并初始化为空。
|
||||
List* pathkeys = NIL;
|
||||
ListCell* l = NULL;
|
||||
|
||||
// 遍历排序子句列表。
|
||||
foreach (l, sortclauses) {
|
||||
SortGroupClause* sortcl = (SortGroupClause*)lfirst(l); // 获取排序子句。
|
||||
Expr* sortkey = NULL; // 声明一个排序键表达式指针,并初始化为空。
|
||||
PathKey* pathkey = NULL; // 声明一个路径键指针,并初始化为空。
|
||||
SortGroupClause* sortcl = (SortGroupClause*)lfirst(l);
|
||||
Expr* sortkey = NULL;
|
||||
PathKey* pathkey = NULL;
|
||||
|
||||
// 获取排序子句的排序键表达式。
|
||||
sortkey = (Expr*)get_sortgroupclause_expr(sortcl, tlist);
|
||||
|
||||
// 使用断言确保排序子句的排序操作符是有效的。
|
||||
AssertEreport(OidIsValid(sortcl->sortop), MOD_OPT, "ordering operator is invalid");
|
||||
|
||||
// 调用make_pathkey_from_sortop函数创建路径键。
|
||||
pathkey = make_pathkey_from_sortop(root,
|
||||
sortkey,
|
||||
sortcl->sortop,
|
||||
|
|
@ -924,19 +880,16 @@ List* make_pathkeys_for_sortclauses(PlannerInfo* root, List* sortclauses, List*
|
|||
true,
|
||||
canonicalize);
|
||||
|
||||
/* 规范形式消除冗余的排序键 */
|
||||
/* Canonical form eliminates redundant ordering keys */
|
||||
if (canonicalize) {
|
||||
// 如果路径键不是冗余的,则将其添加到路径键列表中。
|
||||
if (!pathkey_is_redundant(pathkey, pathkeys, ENABLE_PRED_PUSH_ALL(root)))
|
||||
pathkeys = lappend(pathkeys, pathkey);
|
||||
} else
|
||||
pathkeys = lappend(pathkeys, pathkey); // 将路径键添加到路径键列表中。
|
||||
pathkeys = lappend(pathkeys, pathkey);
|
||||
}
|
||||
|
||||
return pathkeys; // 返回路径键列表。
|
||||
return pathkeys;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* PATHKEYS AND MERGECLAUSES
|
||||
****************************************************************************/
|
||||
|
|
@ -961,19 +914,17 @@ List* make_pathkeys_for_sortclauses(PlannerInfo* root, List* sortclauses, List*
|
|||
*/
|
||||
void initialize_mergeclause_eclasses(PlannerInfo* root, RestrictInfo* restrictinfo)
|
||||
{
|
||||
Expr* clause = restrictinfo->clause;// 获取限制信息中的表达式。
|
||||
Expr* clause = restrictinfo->clause;
|
||||
Oid lefttype, righttype;
|
||||
|
||||
/* Should be a mergeclause ... */
|
||||
AssertEreport(restrictinfo->mergeopfamilies != NIL, MOD_OPT, "clause is not mergejoinable");
|
||||
//检查限制信息是否表示一个合并条件(mergeclause)
|
||||
/* ... with links not yet set */
|
||||
AssertEreport(restrictinfo->left_ec == NULL, MOD_OPT, "lefthand mergeclause processing is set");
|
||||
AssertEreport(restrictinfo->right_ec == NULL, MOD_OPT, "righthand mergeclause processing is set");
|
||||
//检查左侧和右侧等价类是否尚未设置链接
|
||||
|
||||
/* Need the declared input types of the operator */
|
||||
op_input_types(((OpExpr*)clause)->opno, &lefttype, &righttype);// 获取操作符的输入类型
|
||||
op_input_types(((OpExpr*)clause)->opno, &lefttype, &righttype);
|
||||
|
||||
/* Find or create a matching EquivalenceClass for each side */
|
||||
restrictinfo->left_ec = get_eclass_for_sort_expr(root,
|
||||
|
|
@ -1041,22 +992,21 @@ void update_mergeclause_eclasses(PlannerInfo* root, RestrictInfo* restrictinfo)
|
|||
*/
|
||||
List* find_mergeclauses_for_outer_pathkeys(PlannerInfo* root, List* pathkeys, List* restrictinfos)
|
||||
{
|
||||
List* mergeclauses = NIL; // 初始化合并条件列表为空。
|
||||
ListCell* i = NULL; // 声明一个指向ListCell的指针,并初始化为空。
|
||||
List* mergeclauses = NIL;
|
||||
ListCell* i = NULL;
|
||||
|
||||
/* make sure we have eclasses cached in the clauses */
|
||||
// 遍历限制信息列表。
|
||||
foreach (i, restrictinfos) {
|
||||
RestrictInfo* rinfo = (RestrictInfo*)lfirst(i); // 获取限制信息。
|
||||
RestrictInfo* rinfo = (RestrictInfo*)lfirst(i);
|
||||
|
||||
update_mergeclause_eclasses(root, rinfo); // 更新合并条件的等价类。
|
||||
update_mergeclause_eclasses(root, rinfo);
|
||||
}
|
||||
|
||||
foreach (i, pathkeys) {
|
||||
PathKey* pathkey = (PathKey*)lfirst(i); // 获取路径键。
|
||||
EquivalenceClass* pathkey_ec = pathkey->pk_eclass; // 获取路径键的等价类。
|
||||
List* matched_restrictinfos = NIL; // 初始化匹配的限制信息列表为空。
|
||||
ListCell* j = NULL; // 声明一个指向ListCell的指针,并初始化为空。
|
||||
PathKey* pathkey = (PathKey*)lfirst(i);
|
||||
EquivalenceClass* pathkey_ec = pathkey->pk_eclass;
|
||||
List* matched_restrictinfos = NIL;
|
||||
ListCell* j = NULL;
|
||||
|
||||
/* ----------
|
||||
* A mergejoin clause matches a pathkey if it has the same EC.
|
||||
|
|
@ -1298,44 +1248,43 @@ List* select_outer_pathkeys_for_merge(PlannerInfo* root, List* mergeclauses, Rel
|
|||
*/
|
||||
List* make_inner_pathkeys_for_merge(PlannerInfo* root, List* mergeclauses, List* outer_pathkeys)
|
||||
{
|
||||
List* pathkeys = NIL; // 初始化内部路径键列表为空。
|
||||
EquivalenceClass* lastoeclass = NULL; // 上一个外部等价类。
|
||||
PathKey* opathkey = NULL; // 外部路径键。
|
||||
ListCell* lc = NULL; // 声明一个指向ListCell的指针。
|
||||
ListCell* lop = NULL; // 声明一个指向ListCell的指针。
|
||||
List* pathkeys = NIL;
|
||||
EquivalenceClass* lastoeclass = NULL;
|
||||
PathKey* opathkey = NULL;
|
||||
ListCell* lc = NULL;
|
||||
ListCell* lop = NULL;
|
||||
|
||||
lastoeclass = NULL; // 初始化上一个外部等价类为空。
|
||||
opathkey = NULL; // 初始化外部路径键为空。
|
||||
lop = list_head(outer_pathkeys); // 初始化外部路径键列表的头指针。
|
||||
lastoeclass = NULL;
|
||||
opathkey = NULL;
|
||||
lop = list_head(outer_pathkeys);
|
||||
|
||||
// 遍历合并条件列表。
|
||||
foreach (lc, mergeclauses) {
|
||||
RestrictInfo* rinfo = (RestrictInfo*)lfirst(lc); // 获取限制信息。
|
||||
EquivalenceClass* oeclass = NULL; // 外部等价类。
|
||||
EquivalenceClass* ieclass = NULL; // 内部等价类。
|
||||
PathKey* pathkey = NULL; // 路径键。
|
||||
RestrictInfo* rinfo = (RestrictInfo*)lfirst(lc);
|
||||
EquivalenceClass* oeclass = NULL;
|
||||
EquivalenceClass* ieclass = NULL;
|
||||
PathKey* pathkey = NULL;
|
||||
|
||||
update_mergeclause_eclasses(root, rinfo); // 更新合并条件的等价类。
|
||||
update_mergeclause_eclasses(root, rinfo);
|
||||
|
||||
if (rinfo->outer_is_left) {
|
||||
oeclass = rinfo->left_ec; // 左侧是外部等价类。
|
||||
ieclass = rinfo->right_ec; // 右侧是内部等价类。
|
||||
oeclass = rinfo->left_ec;
|
||||
ieclass = rinfo->right_ec;
|
||||
} else {
|
||||
oeclass = rinfo->right_ec; // 右侧是外部等价类。
|
||||
ieclass = rinfo->left_ec; // 左侧是内部等价类。
|
||||
oeclass = rinfo->right_ec;
|
||||
ieclass = rinfo->left_ec;
|
||||
}
|
||||
|
||||
/* outer eclass should match current or next pathkeys */
|
||||
/* we check this carefully for debugging reasons */
|
||||
// 如果外部等价类不同于上一个外部等价类。
|
||||
if (oeclass != lastoeclass) {
|
||||
if (lop == NULL)
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
||||
(errmsg("too few pathkeys for mergeclauses when make inner pathkeys for merge"))));
|
||||
opathkey = (PathKey*)lfirst(lop); // 获取下一个外部路径键。
|
||||
lop = lnext(lop); // 移动到下一个外部路径键。
|
||||
lastoeclass = opathkey->pk_eclass; // 更新上一个外部等价类。
|
||||
opathkey = (PathKey*)lfirst(lop);
|
||||
lop = lnext(lop);
|
||||
lastoeclass = opathkey->pk_eclass;
|
||||
if (oeclass != lastoeclass)
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
|
|
@ -1348,7 +1297,6 @@ List* make_inner_pathkeys_for_merge(PlannerInfo* root, List* mergeclauses, List*
|
|||
* pathkey is also canonical for the inner side, and we can skip a
|
||||
* useless search.
|
||||
*/
|
||||
// 如果内部等价类等于外部等价类,则使用外部路径键。
|
||||
if (ieclass == oeclass)
|
||||
pathkey = opathkey;
|
||||
else
|
||||
|
|
@ -1364,12 +1312,11 @@ List* make_inner_pathkeys_for_merge(PlannerInfo* root, List* mergeclauses, List*
|
|||
* reason, it certainly wouldn't match any available sort order for
|
||||
* the input relation.
|
||||
*/
|
||||
// 如果路径键不是多余的,则添加到内部路径键列表。
|
||||
if (!pathkey_is_redundant(pathkey, pathkeys))
|
||||
pathkeys = lappend(pathkeys, pathkey);
|
||||
}
|
||||
|
||||
return pathkeys;// 返回内部路径键列表。
|
||||
return pathkeys;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1541,15 +1488,13 @@ static int pathkeys_useful_for_merging(PlannerInfo* root, RelOptInfo* rel, List*
|
|||
*/
|
||||
static bool right_merge_direction(PlannerInfo* root, PathKey* pathkey)
|
||||
{
|
||||
ListCell* l = NULL; // 声明一个指向ListCell的指针。
|
||||
ListCell* l = NULL;
|
||||
|
||||
// 遍历查询路径键列表。
|
||||
foreach (l, root->query_pathkeys) {
|
||||
PathKey* query_pathkey = (PathKey*)lfirst(l); // 获取查询路径键。
|
||||
PathKey* query_pathkey = (PathKey*)lfirst(l);
|
||||
|
||||
// 如果路径键的等价类和操作符族相同。
|
||||
if (pathkey->pk_eclass == query_pathkey->pk_eclass &&
|
||||
OpFamilyEquals(pathkey->pk_opfamily, query_pathkey->pk_opfamily)) {
|
||||
OpFamilyEquals(pathkey->pk_opfamily, query_pathkey->pk_opfamily)) {
|
||||
/*
|
||||
* Found a matching query sort column. Prefer this pathkey's
|
||||
* direction iff it matches. Note that we ignore pk_nulls_first,
|
||||
|
|
@ -1557,12 +1502,12 @@ static bool right_merge_direction(PlannerInfo* root, PathKey* pathkey)
|
|||
* want to prefer only one of the two possible directions, and we
|
||||
* might as well use this one.
|
||||
*/
|
||||
return (pathkey->pk_strategy == query_pathkey->pk_strategy); // 返回策略是否相同。
|
||||
return (pathkey->pk_strategy == query_pathkey->pk_strategy);
|
||||
}
|
||||
}
|
||||
|
||||
/* If no matching ORDER BY request, prefer the ASC direction */
|
||||
return (pathkey->pk_strategy == BTLessStrategyNumber);// 如果没有匹配的查询路径键,默认返回 false。
|
||||
return (pathkey->pk_strategy == BTLessStrategyNumber);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1599,13 +1544,8 @@ List* truncate_useless_pathkeys(PlannerInfo* root, RelOptInfo* rel, List* pathke
|
|||
int nuseful;
|
||||
int nuseful2;
|
||||
|
||||
// 计算路径键对于合并连接操作的有用性。
|
||||
nuseful = pathkeys_useful_for_merging(root, rel, pathkeys);
|
||||
|
||||
// 计算路径键对于排序操作的有用性。
|
||||
nuseful2 = pathkeys_useful_for_ordering(root, pathkeys);
|
||||
|
||||
// 如果排序操作的有用性更高,则使用排序操作的有用性。
|
||||
if (nuseful2 > nuseful) {
|
||||
nuseful = nuseful2;
|
||||
}
|
||||
|
|
@ -1614,13 +1554,10 @@ List* truncate_useless_pathkeys(PlannerInfo* root, RelOptInfo* rel, List* pathke
|
|||
* Note: not safe to modify input list destructively, but we can avoid
|
||||
* copying the list if we're not actually going to change it
|
||||
*/
|
||||
// 如果没有有用的路径键,返回空列表。
|
||||
if (nuseful == 0)
|
||||
return NIL;
|
||||
// 如果所有路径键都有用,返回原始路径键列表。
|
||||
else if (nuseful == list_length(pathkeys))
|
||||
return pathkeys;
|
||||
// 否则,截断路径键列表,只保留有用的部分。
|
||||
else
|
||||
return list_truncate(list_copy(pathkeys), nuseful);
|
||||
}
|
||||
|
|
@ -1667,24 +1604,23 @@ construct_pathkeys(PlannerInfo *root, List *tlist, List *activeWindows,
|
|||
*/
|
||||
|
||||
/* To groupingSet, we need build it's groupPathKey according to it's lower levels sort clause.*/
|
||||
// 设置 group_pathkeys,如果 groupClause 存在并且可排序。
|
||||
if (groupClause && grouping_is_sortable(groupClause)) {
|
||||
root->group_pathkeys = make_pathkeys_for_sortclauses(root, groupClause, tlist, canonical);
|
||||
} else {
|
||||
root->group_pathkeys = NIL; // 否则,设置为空列表。
|
||||
}
|
||||
if (groupClause && grouping_is_sortable(groupClause)) {
|
||||
root->group_pathkeys = make_pathkeys_for_sortclauses(root, groupClause, tlist, canonical);
|
||||
} else {
|
||||
root->group_pathkeys = NIL;
|
||||
}
|
||||
|
||||
/* We consider only the first (bottom) window in pathkeys logic */
|
||||
if (activeWindows != NIL) {
|
||||
WindowClause* wc = NULL;
|
||||
// 获取活动窗口中的第一个窗口子句。
|
||||
|
||||
wc = (WindowClause*)linitial(activeWindows);// 设置 window_pathkeys,如果窗口子句存在并且可排序。
|
||||
wc = (WindowClause*)linitial(activeWindows);
|
||||
|
||||
root->window_pathkeys = make_pathkeys_for_window(root, wc, tlist, canonical);
|
||||
} else {
|
||||
root->window_pathkeys = NIL;// 否则,设置为空列表。
|
||||
root->window_pathkeys = NIL;
|
||||
}
|
||||
// 设置 distinct_pathkeys,如果 distinctClause 存在并且可排序。
|
||||
|
||||
if (parse->distinctClause && grouping_is_sortable(parse->distinctClause)) {
|
||||
|
||||
root->distinct_pathkeys = make_pathkeys_for_sortclauses(root,
|
||||
|
|
@ -1692,11 +1628,10 @@ if (groupClause && grouping_is_sortable(groupClause)) {
|
|||
} else {
|
||||
root->distinct_pathkeys = NIL;
|
||||
}
|
||||
// 设置 sort_pathkeys。
|
||||
|
||||
root->sort_pathkeys = make_pathkeys_for_sortclauses(root, parse->sortClause, tlist, canonical);
|
||||
|
||||
/* Remove the PARAM EC */
|
||||
// 如果 canonical 为真,移除参数路径键(不包含参数的路径键)。
|
||||
if (canonical) {
|
||||
root->group_pathkeys = remove_param_pathkeys(root, root->group_pathkeys);
|
||||
root->window_pathkeys = remove_param_pathkeys(root, root->window_pathkeys);
|
||||
|
|
@ -1722,68 +1657,52 @@ if (groupClause && grouping_is_sortable(groupClause)) {
|
|||
* versus ORDER BY is much easier, since we know that the parser
|
||||
* ensured that one is a superset of the other.
|
||||
*/
|
||||
// 如果存在 group_pathkeys,则将 query_pathkeys 设置为 group_pathkeys。
|
||||
if (root->group_pathkeys) {
|
||||
root->query_pathkeys = root->group_pathkeys;
|
||||
}
|
||||
// 如果存在 window_pathkeys 且没有 group_pathkeys,则将 query_pathkeys 设置为 window_pathkeys。
|
||||
else if (root->window_pathkeys) {
|
||||
root->query_pathkeys = root->window_pathkeys;
|
||||
}
|
||||
// 如果 distinct_pathkeys 的长度大于 sort_pathkeys 的长度,且没有 group_pathkeys 或 window_pathkeys,则将 query_pathkeys 设置为 distinct_pathkeys。
|
||||
else if (list_length(root->distinct_pathkeys) > list_length(root->sort_pathkeys)) {
|
||||
root->query_pathkeys = root->distinct_pathkeys;
|
||||
}
|
||||
// 如果存在 sort_pathkeys 且没有 group_pathkeys、window_pathkeys 或 distinct_pathkeys,则将 query_pathkeys 设置为 sort_pathkeys。
|
||||
else if (root->sort_pathkeys) {
|
||||
root->query_pathkeys = root->sort_pathkeys;
|
||||
}
|
||||
// 如果没有任何路径键,则将 query_pathkeys 设置为空列表。
|
||||
else {
|
||||
root->query_pathkeys = NIL;
|
||||
}
|
||||
|
||||
return;
|
||||
if (root->group_pathkeys)
|
||||
root->query_pathkeys = root->group_pathkeys;
|
||||
else if (root->window_pathkeys)
|
||||
root->query_pathkeys = root->window_pathkeys;
|
||||
else if (list_length(root->distinct_pathkeys) > list_length(root->sort_pathkeys))
|
||||
root->query_pathkeys = root->distinct_pathkeys;
|
||||
else if (root->sort_pathkeys)
|
||||
root->query_pathkeys = root->sort_pathkeys;
|
||||
else
|
||||
root->query_pathkeys = NIL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init the standard_qp_extra
|
||||
*/
|
||||
// 初始化查询计划的路径键,可以选择启用或禁用规范化路径键的功能。
|
||||
void standard_qp_init(PlannerInfo *root, void *extra, List *tlist,
|
||||
void
|
||||
standard_qp_init(PlannerInfo *root, void *extra, List *tlist,
|
||||
List *activeWindows, List *groupClause)
|
||||
{
|
||||
// 如果启用了 SQL BETA 功能中的规范化路径键功能。
|
||||
if (ENABLE_SQL_BETA_FEATURE(CANONICAL_PATHKEY)) {
|
||||
Assert(extra != NULL); // 断言 extra 不为空。
|
||||
Assert (extra != NULL);
|
||||
standard_qp_extra *qp_extra = (standard_qp_extra *)extra;
|
||||
qp_extra->tlist = tlist; // 将 tlist 设置为额外数据的成员。
|
||||
qp_extra->activeWindows = activeWindows; // 将 activeWindows 设置为额外数据的成员。
|
||||
qp_extra->groupClause = groupClause; // 将 groupClause 设置为额外数据的成员。
|
||||
qp_extra->tlist = tlist;
|
||||
qp_extra->activeWindows = activeWindows;
|
||||
qp_extra->groupClause = groupClause;
|
||||
} else {
|
||||
// 如果未启用规范化路径键功能,则构建路径键。
|
||||
construct_pathkeys(root, tlist, activeWindows, groupClause, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compute query_pathkeys and other pathkeys during plan generation
|
||||
*/
|
||||
// 查询计划的回调函数,用于执行规范化路径键操作。
|
||||
void standard_qp_callback(PlannerInfo *root, void *extra)
|
||||
void
|
||||
standard_qp_callback(PlannerInfo *root, void *extra)
|
||||
{
|
||||
// 如果启用了 SQL BETA 功能中的规范化路径键功能。
|
||||
if (ENABLE_SQL_BETA_FEATURE(CANONICAL_PATHKEY)) {
|
||||
Assert(extra != NULL); // 断言 extra 不为空。
|
||||
Assert (extra != NULL);
|
||||
standard_qp_extra *qp_extra = (standard_qp_extra *)extra;
|
||||
construct_pathkeys(root, qp_extra->tlist, qp_extra->activeWindows,
|
||||
qp_extra->groupClause, true); // 执行构建路径键操作,规范化路径键。
|
||||
qp_extra->groupClause, true);
|
||||
} else {
|
||||
// 如果未启用规范化路径键功能,则对各种路径键进行规范化处理。
|
||||
root->group_pathkeys = canonicalize_pathkeys(root, root->group_pathkeys);
|
||||
root->window_pathkeys = canonicalize_pathkeys(root, root->window_pathkeys);
|
||||
root->distinct_pathkeys = canonicalize_pathkeys(root, root->distinct_pathkeys);
|
||||
|
|
|
|||
|
|
@ -26,11 +26,9 @@
|
|||
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
|
||||
static RemoteQueryPath* pgxc_find_remotequery_path(RelOptInfo* rel);// 定义一个静态函数pgxc_find_remotequery_path,该函数返回RemoteQueryPath指针,并接受RelOptInfo类型的参数rel。
|
||||
static RemoteQueryPath* pgxc_find_remotequery_path(RelOptInfo* rel);
|
||||
static RemoteQueryPath* create_remotequery_path(PlannerInfo* root, RelOptInfo* rel, ExecNodes* exec_nodes,
|
||||
// 定义一个静态函数create_remotequery_path,该函数返回RemoteQueryPath指针,并接受多个参数,包括PlannerInfo、RelOptInfo、ExecNodes等。
|
||||
RemoteQueryPath* leftpath, RemoteQueryPath* rightpath, JoinType jointype, List* join_restrictlist);
|
||||
|
||||
/*
|
||||
* create_remotequery_path
|
||||
* Creates a path for given RelOptInfo (for base rel or a join rel) so that
|
||||
|
|
@ -45,66 +43,51 @@ static RemoteQueryPath* create_remotequery_path(PlannerInfo* root, RelOptInfo* r
|
|||
* If any of the relations involved in this path is a temporary relation,
|
||||
* record that fact.
|
||||
*/
|
||||
// 实现create_remotequery_path函数
|
||||
static RemoteQueryPath* create_remotequery_path(PlannerInfo* root, RelOptInfo* rel, ExecNodes* exec_nodes,
|
||||
RemoteQueryPath* leftpath, RemoteQueryPath* rightpath, JoinType jointype, List* join_restrictlist)
|
||||
{
|
||||
// 创建RemoteQueryPath结构体指针rqpath并初始化为新节点
|
||||
RemoteQueryPath* rqpath = makeNode(RemoteQueryPath);
|
||||
// 初始化一个布尔值unshippable_quals为false
|
||||
bool unshippable_quals = false;
|
||||
|
||||
|
||||
// 如果rel的reloptkind属性为RELOPT_JOINREL,并且leftpath或rightpath为空,则抛出错误。
|
||||
if (rel->reloptkind == RELOPT_JOINREL && (!leftpath || !rightpath))
|
||||
elog(ERROR, "a join rel requires both the left path and right path");
|
||||
|
||||
// 设置rqpath的各个属性值
|
||||
rqpath->path.pathtype = T_RemoteQuery; // 设置pathtype属性为T_RemoteQuery
|
||||
rqpath->path.parent = rel; // 设置parent属性为传入的rel
|
||||
rqpath->path.pathtype = T_RemoteQuery;
|
||||
rqpath->path.parent = rel;
|
||||
/* PGXC_TODO: do we want to care about it */
|
||||
rqpath->path.param_info = NULL; // 设置param_info属性为NULL
|
||||
rqpath->path.pathkeys = NIL; // 设置pathkeys属性为空列表(结果总是无序的)
|
||||
rqpath->rqpath_en = exec_nodes; // 设置rqpath_en属性为传入的exec_nodes
|
||||
rqpath->leftpath = leftpath; // 设置leftpath属性为传入的leftpath
|
||||
rqpath->rightpath = rightpath; // 设置rightpath属性为传入的rightpath
|
||||
rqpath->jointype = jointype; // 设置jointype属性为传入的jointype
|
||||
rqpath->join_restrictlist = join_restrictlist; // 设置join_restrictlist属性为传入的join_restrictlist
|
||||
rqpath->path.param_info = NULL;
|
||||
rqpath->path.pathkeys = NIL; /* result is always unordered */
|
||||
rqpath->rqpath_en = exec_nodes;
|
||||
rqpath->leftpath = leftpath;
|
||||
rqpath->rightpath = rightpath;
|
||||
rqpath->jointype = jointype;
|
||||
rqpath->join_restrictlist = join_restrictlist;
|
||||
|
||||
// 根据rel的reloptkind属性不同,执行不同的分支
|
||||
switch (rel->reloptkind) {
|
||||
case RELOPT_BASEREL:
|
||||
case RELOPT_OTHER_MEMBER_REL: {
|
||||
// 获取关联的RangeTblEntry
|
||||
RangeTblEntry* rte = rt_fetch(rel->relid, root->parse->rtable);
|
||||
// 如果rte的rtekind不是RTE_RELATION,则抛出错误
|
||||
if (rte->rtekind != RTE_RELATION)
|
||||
elog(ERROR, "can not create remote path for ranges of type %d", rte->rtekind);
|
||||
// 检查关联的表是否为临时表
|
||||
rqpath->rqhas_temp_rel = IsTempTable(rte->relid);
|
||||
// 检查不可运输的限制条件(unshippable_quals)
|
||||
unshippable_quals =
|
||||
!pgxc_is_expr_shippable((Expr*)extract_actual_clauses(rel->baserestrictinfo, false), NULL);
|
||||
} break;
|
||||
|
||||
case RELOPT_JOINREL: {
|
||||
// 检查是否有临时关系
|
||||
rqpath->rqhas_temp_rel = leftpath->rqhas_temp_rel || rightpath->rqhas_temp_rel;
|
||||
// 检查不可运输的连接限制条件(join_restrictlist)
|
||||
unshippable_quals = !pgxc_is_expr_shippable((Expr*)extract_actual_clauses(join_restrictlist, false), NULL);
|
||||
} break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "can not create remote path for relation of type %d", rel->reloptkind);
|
||||
}
|
||||
// 设置rqhas_unshippable_qual属性为unshippable_quals
|
||||
rqpath->rqhas_unshippable_qual = unshippable_quals;
|
||||
// 检查不可运输的目标列表
|
||||
rqpath->rqhas_unshippable_tlist = !pgxc_is_expr_shippable((Expr*)rel->reltargetlist, NULL);
|
||||
|
||||
// 计算远程查询的成本
|
||||
/* set cost properly */
|
||||
cost_remotequery(rqpath, root, rel);
|
||||
|
||||
// 返回rqpath
|
||||
return rqpath;
|
||||
}
|
||||
|
||||
|
|
@ -116,36 +99,34 @@ static RemoteQueryPath* create_remotequery_path(PlannerInfo* root, RelOptInfo* r
|
|||
* The caller can decide whether to add the scan paths depending upon the return
|
||||
* value.
|
||||
*/
|
||||
// 定义一个外部函数create_plainrel_rqpath,该函数返回布尔值,并接受多个参数,包括PlannerInfo、RelOptInfo和RangeTblEntry。
|
||||
extern bool create_plainrel_rqpath(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* rte)
|
||||
{
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
// 如果不启用多节点支持,则返回false
|
||||
return false;
|
||||
#else
|
||||
List* quals = NIL;
|
||||
ExecNodes* exec_nodes = NULL;
|
||||
// 如果不是PGXC协调器、连接来自协调器或是本地查询,则返回false
|
||||
|
||||
/*
|
||||
* If we are on the Coordinator, we always want to use
|
||||
* the remote query path unless relation is local to coordinator or the
|
||||
* query is to entirely executed on coordinator.
|
||||
*/
|
||||
if (!IS_PGXC_COORDINATOR || IsConnFromCoord() || root->parse->is_local)
|
||||
return false;
|
||||
|
||||
// 提取实际限制条件(quals)
|
||||
quals = extract_actual_clauses(rel->baserestrictinfo, false);
|
||||
// 根据限制条件获取执行节点信息
|
||||
exec_nodes = GetRelationNodesByQuals(rte->relid, rel->relid, (Node*)quals, RELATION_ACCESS_READ);
|
||||
// 如果没有找到执行节点,则返回false
|
||||
if (!exec_nodes)
|
||||
return false;
|
||||
|
||||
// 如果执行节点是按值分布的,则设置en_dist_vars属性
|
||||
if (IsExecNodesDistributedByValue(exec_nodes)) {
|
||||
Var* dist_var = pgxc_get_dist_var(rel->relid, rte, rel->reltargetlist);
|
||||
exec_nodes->en_dist_vars = list_make1(dist_var);
|
||||
}
|
||||
|
||||
// 调用pgxc_find_remotequery_path函数查找RemoteQueryPath
|
||||
pgxc_find_remotequery_path(rel);
|
||||
// 返回true表示成功创建了RemoteQueryPath
|
||||
/* We don't have subpaths for a plain base relation */
|
||||
add_path(rel, (Path*)create_remotequery_path(root, rel, exec_nodes, NULL, NULL, 0, NULL));
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -156,19 +137,15 @@ extern bool create_plainrel_rqpath(PlannerInfo* root, RelOptInfo* rel, RangeTblE
|
|||
* if one found, NULL otherwise. There should be only one RemoteQuery path for
|
||||
* each rel, but we don't check for this.
|
||||
*/
|
||||
// 定义一个函数pgxc_find_remotequery_path,该函数返回RemoteQueryPath指针,并接受RelOptInfo类型的参数rel。
|
||||
static RemoteQueryPath* pgxc_find_remotequery_path(RelOptInfo* rel)
|
||||
{
|
||||
ListCell* cell = NULL;
|
||||
|
||||
// 遍历关系的路径列表pathlist
|
||||
foreach (cell, rel->pathlist) {
|
||||
Path* path = (Path*)lfirst(cell); // 获取当前路径
|
||||
// 如果当前路径是RemoteQueryPath类型,则返回该路径
|
||||
Path* path = (Path*)lfirst(cell);
|
||||
if (IsA(path, RemoteQueryPath))
|
||||
return (RemoteQueryPath*)path;
|
||||
}
|
||||
// 如果没有找到RemoteQueryPath,返回NULL
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -178,12 +155,10 @@ static RemoteQueryPath* pgxc_find_remotequery_path(RelOptInfo* rel)
|
|||
* is shippable to the datanodes, and if so, create a remotequery path for this
|
||||
* JOIN.
|
||||
*/
|
||||
// 定义一个外部函数create_joinrel_rqpath,该函数用于创建连接关系的远程查询路径。
|
||||
extern void create_joinrel_rqpath(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel,
|
||||
List* restrictlist, JoinType jointype, SpecialJoinInfo* sjinfo)
|
||||
{
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
// 如果不启用多节点支持,则直接返回
|
||||
return;
|
||||
#else
|
||||
RemoteQueryPath* innerpath = NULL;
|
||||
|
|
@ -195,11 +170,9 @@ extern void create_joinrel_rqpath(PlannerInfo* root, RelOptInfo* joinrel, RelOpt
|
|||
List* other_quals = NIL;
|
||||
|
||||
/* If GUC does not allow remote join optimization, so be it */
|
||||
// 如果GUC不允许远程连接优化,则直接返回
|
||||
if (!enable_remotejoin)
|
||||
return;
|
||||
|
||||
// 通过pgxc_find_remotequery_path函数查找内部关系和外部关系的远程查询路径
|
||||
innerpath = pgxc_find_remotequery_path(innerrel);
|
||||
outerpath = pgxc_find_remotequery_path(outerrel);
|
||||
/*
|
||||
|
|
@ -211,18 +184,14 @@ extern void create_joinrel_rqpath(PlannerInfo* root, RelOptInfo* joinrel, RelOpt
|
|||
* attaching the unshippable qual to the join itself, and thus shipping join
|
||||
* but evaluating the qual on join result. But we don't attempt it for now
|
||||
*/
|
||||
// 如果没有找到内部路径、外部路径,或者这些路径具有不可运输的限制条件,则直接返回
|
||||
if (!innerpath || !outerpath || innerpath->rqhas_unshippable_qual || outerpath->rqhas_unshippable_qual)
|
||||
return;
|
||||
|
||||
// 获取内部路径和外部路径的执行节点信息
|
||||
inner_en = innerpath->rqpath_en;
|
||||
outer_en = outerpath->rqpath_en;
|
||||
|
||||
// 如果内部执行节点或外部执行节点为空,则抛出错误
|
||||
if (!inner_en || !outer_en)
|
||||
elog(ERROR, "No node list provided for remote query path");
|
||||
|
||||
/*
|
||||
* Collect quals from restrictions so as to check the shippability of a JOIN
|
||||
* between distributed relations.
|
||||
|
|
@ -247,20 +216,15 @@ extern void create_joinrel_rqpath(PlannerInfo* root, RelOptInfo* joinrel, RelOpt
|
|||
* If the nodelists on both the sides of JOIN can be merged, the JOIN is
|
||||
* shippable.
|
||||
*/
|
||||
// 调用pgxc_is_join_shippable函数来检查连接是否可以进行远程运行,并获取适用于连接的执行节点信息
|
||||
join_en = pgxc_is_join_shippable(inner_en,
|
||||
outer_en,
|
||||
innerpath->rqhas_unshippable_tlist,
|
||||
outerpath->rqhas_unshippable_tlist,
|
||||
jointype,
|
||||
(Node*)join_quals);
|
||||
|
||||
// 如果join_en不为空(表示连接可以进行远程运行),则创建连接关系的远程查询路径并将其添加到joinrel的路径列表中
|
||||
if (join_en)
|
||||
add_path(joinrel,
|
||||
(Path*)create_remotequery_path(root, joinrel, join_en, outerpath, innerpath, jointype, restrictlist));
|
||||
|
||||
// 函数结束
|
||||
return;
|
||||
join_en = pgxc_is_join_shippable(inner_en,
|
||||
outer_en,
|
||||
innerpath->rqhas_unshippable_tlist,
|
||||
outerpath->rqhas_unshippable_tlist,
|
||||
jointype,
|
||||
(Node*)join_quals);
|
||||
if (join_en)
|
||||
add_path(joinrel,
|
||||
(Path*)create_remotequery_path(root, joinrel, join_en, outerpath, innerpath, jointype, restrictlist));
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,29 +62,22 @@
|
|||
* @param[IN] src: the source stream info pair.
|
||||
* @return void
|
||||
*/
|
||||
// 复制 StreamInfoPair 结构体的内容,从源 (src) 到目标 (dst)。
|
||||
void copy_stream_info_pair(StreamInfoPair* dst, StreamInfoPair* src)
|
||||
{
|
||||
// 检查目标和源是否为 NULL,如果是则直接返回。
|
||||
if (dst == NULL || src == NULL)
|
||||
return;
|
||||
|
||||
// 使用 errno_t 变量 rc 来存储 memcpy_s 函数的返回值。
|
||||
errno_t rc = EOK;
|
||||
|
||||
// 使用 memcpy_s 函数将 src 的 inner_info 成员的内容复制到 dst 的 inner_info 成员。
|
||||
rc = memcpy_s(&dst->inner_info, sizeof(StreamInfo), &src->inner_info, sizeof(StreamInfo));
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
||||
// 使用 memcpy_s 函数将 src 的 outer_info 成员的内容复制到 dst 的 outer_info 成员。
|
||||
rc = memcpy_s(&dst->outer_info, sizeof(StreamInfo), &src->outer_info, sizeof(StreamInfo));
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
||||
// 将 dst 的 skew_optimize 成员设置为 SKEW_RES_NONE。
|
||||
dst->skew_optimize = SKEW_RES_NONE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @Description: construnctor for PathGen.
|
||||
*
|
||||
|
|
@ -130,11 +123,9 @@ void PathGen::addPath(Path* new_path)
|
|||
* @param[IN] required_outer: the set of required outer rels.
|
||||
*/
|
||||
JoinPathGenBase::JoinPathGenBase(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype,
|
||||
// JoinPathGenBase 类的构造函数,用于初始化关联的属性和成员变量。
|
||||
SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, List* joinclauses, List* restrictinfo,
|
||||
Path* outer_path, Path* inner_path, Relids required_outer)
|
||||
: PathGen(root, joinrel),// 调用基类 PathGen 的构造函数初始化基类成员。
|
||||
// 初始化各种成员变量,这些成员变量在类定义中声明。
|
||||
: PathGen(root, joinrel),
|
||||
m_jointype(jointype),
|
||||
m_saveJointype(save_jointype),
|
||||
m_workspace(NULL),
|
||||
|
|
@ -170,33 +161,23 @@ JoinPathGenBase::JoinPathGenBase(PlannerInfo* root, RelOptInfo* joinrel, JoinTyp
|
|||
m_redistributeOuter(false),
|
||||
m_canBroadcastInner(false),
|
||||
m_canBroadcastOuter(false)
|
||||
{ // 调用 init() 函数来完成进一步的初始化。
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: decontructor function for join path generation.
|
||||
*/
|
||||
// 析构函数,用于释放资源和清理成员变量。
|
||||
JoinPathGenBase::~JoinPathGenBase()
|
||||
{
|
||||
// 检查 m_resourceOwner 是否为 NULL。
|
||||
if (m_resourceOwner != NULL) {
|
||||
// 释放资源:在锁之前的阶段释放。
|
||||
ResourceOwnerRelease(m_resourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false);
|
||||
// 释放资源:锁定阶段释放。
|
||||
ResourceOwnerRelease(m_resourceOwner, RESOURCE_RELEASE_LOCKS, false, false);
|
||||
// 释放资源:在锁之后的阶段释放。
|
||||
ResourceOwnerRelease(m_resourceOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, false);
|
||||
|
||||
// 删除 m_resourceOwner 对象。
|
||||
ResourceOwnerDelete(m_resourceOwner);
|
||||
|
||||
// 将 m_resourceOwner 设置为 NULL,表示资源已被释放。
|
||||
m_resourceOwner = NULL;
|
||||
}
|
||||
|
||||
// 重置以下成员变量,将它们设置为初始状态或 NULL。
|
||||
m_distributeKeysInner = NIL;
|
||||
m_distributeKeysOuter = NIL;
|
||||
m_innerPath = NULL;
|
||||
|
|
@ -225,92 +206,63 @@ JoinPathGenBase::~JoinPathGenBase()
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
// 初始化方法,用于设置成员变量的初始状态。
|
||||
void JoinPathGenBase::init()
|
||||
{
|
||||
// 设置连接方法为哈希连接。
|
||||
m_joinmethod = T_HashJoin;
|
||||
|
||||
// 初始化工作区为 NULL。
|
||||
m_workspace = NULL;
|
||||
|
||||
// 初始化路径键列表为空。
|
||||
m_pathkeys = NIL;
|
||||
|
||||
// 初始化目标分发方式为空。
|
||||
m_targetDistribution = NULL;
|
||||
|
||||
// 初始化外部流路径和内部流路径为空。
|
||||
m_outerStreamPath = NULL;
|
||||
m_innerStreamPath = NULL;
|
||||
|
||||
// 获取内部和外部关系。
|
||||
m_innerRel = m_innerPath->parent;
|
||||
m_outerRel = m_outerPath->parent;
|
||||
|
||||
// 初始化 RRInfo(Range Table),将其置为空列表。
|
||||
m_rrinfoInner = NIL;
|
||||
m_rrinfoOuter = NIL;
|
||||
|
||||
// 获取内部和外部路径的分发键列表。
|
||||
m_distributeKeysInner = m_innerPath->distribute_keys;
|
||||
m_distributeKeysOuter = m_outerPath->distribute_keys;
|
||||
|
||||
// 初始化复制标志。
|
||||
/* Init replicate flag. */
|
||||
m_replicateInner = is_replicated_path(m_innerPath);
|
||||
m_replicateOuter = is_replicated_path(m_outerPath);
|
||||
|
||||
// 根据连接类型等信息初始化广播标志。
|
||||
/* Init broadcast flag base on join type etc. */
|
||||
m_canBroadcastInner =
|
||||
can_broadcast_inner(m_jointype, m_saveJointype, m_replicateOuter, m_distributeKeysOuter, m_outerPath);
|
||||
m_canBroadcastOuter =
|
||||
can_broadcast_outer(m_jointype, m_saveJointype, m_replicateInner, m_distributeKeysInner, m_innerPath);
|
||||
|
||||
// 初始化流信息列表为空列表。
|
||||
m_streamInfoList = NIL;
|
||||
|
||||
// 初始化流信息对为空。
|
||||
m_streamInfoPair = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a resource owner to keep track of resources
|
||||
* in order to release resources when catch the exception.
|
||||
*/
|
||||
// 创建资源所有者,用于管理资源的释放。
|
||||
m_resourceOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, "join_path_gen",
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER));
|
||||
m_resourceOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, "join_path_gen",
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER));
|
||||
|
||||
// 初始化 DOP(Degree of Parallelism),设置为 0。
|
||||
m_dop = 0;
|
||||
|
||||
// 初始化内部和外部的多重度,都设置为 1.0。
|
||||
m_multipleInner = 1.0;
|
||||
m_multipleOuter = 1.0;
|
||||
|
||||
// 初始化内部和外部是否需要重新分发数据的标志,都设置为 false。
|
||||
m_redistributeInner = false;
|
||||
m_redistributeOuter = false;
|
||||
m_dop = 0;
|
||||
m_multipleInner = 1.0;
|
||||
m_multipleOuter = 1.0;
|
||||
|
||||
m_redistributeInner = false;
|
||||
m_redistributeOuter = false;
|
||||
}
|
||||
|
||||
// 检查外部路径是否分布在分片上(range/list分布方式)
|
||||
m_rangelistOuter = IsLocatorDistributedBySlice(m_outerPath->locator_type);
|
||||
|
||||
// 检查内部路径是否分布在分片上(range/list分布方式)
|
||||
m_rangelistInner = IsLocatorDistributedBySlice(m_innerPath->locator_type);
|
||||
|
||||
// 初始化标志以指示外部和内部路径是否具有相同的边界
|
||||
m_sameBoundary = false;
|
||||
|
||||
// 如果外部或内部路径分布在分片上,则检查它们是否具有相同的边界
|
||||
if (m_rangelistOuter || m_rangelistInner) {
|
||||
m_sameBoundary = IsSliceInfoEqualByOid(m_outerPath->rangelistOid, m_innerPath->rangelistOid);
|
||||
|
||||
// 如果具有相同的边界
|
||||
if (m_sameBoundary) {
|
||||
if (m_redistributeOuter || m_redistributeInner) {
|
||||
void JoinPathGenBase::initRangeListDistribution()
|
||||
{
|
||||
m_rangelistOuter = IsLocatorDistributedBySlice(m_outerPath->locator_type);
|
||||
m_rangelistInner = IsLocatorDistributedBySlice(m_innerPath->locator_type);
|
||||
m_sameBoundary = false;
|
||||
|
||||
if (m_rangelistOuter || m_rangelistInner) {
|
||||
m_sameBoundary = IsSliceInfoEqualByOid(m_outerPath->rangelistOid, m_innerPath->rangelistOid);
|
||||
if (m_sameBoundary) {
|
||||
if (m_redistributeOuter || m_redistributeInner) {
|
||||
/* if one side needs to redistribtue, the other side should redistribute too */
|
||||
m_redistributeOuter = true;
|
||||
m_redistributeInner = true;
|
||||
|
|
@ -333,14 +285,11 @@ if (m_rangelistOuter || m_rangelistInner) {
|
|||
m_redistributeInner = true;
|
||||
}
|
||||
}
|
||||
// 如果外部路径使用范围/列表分布并需要重新分发,则清除外部分布键
|
||||
if (m_rangelistOuter && m_redistributeOuter) {
|
||||
m_distributeKeysOuter = NIL;
|
||||
}
|
||||
|
||||
// 如果内部路径使用范围/列表分布并需要重新分发,则清除内部分布键
|
||||
if (m_rangelistInner && m_redistributeInner) {
|
||||
m_distributeKeysInner = NIL;
|
||||
if (m_rangelistOuter && m_redistributeOuter) {
|
||||
m_distributeKeysOuter = NIL;
|
||||
}
|
||||
if (m_rangelistInner && m_redistributeInner) {
|
||||
m_distributeKeysInner = NIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -402,7 +351,6 @@ const bool JoinPathGenBase::isParallelEnable()
|
|||
*/
|
||||
List* JoinPathGenBase::getOthersideKey(bool stream_outer)
|
||||
{
|
||||
// 根据是否是外部流,选择相应的约束信息和关联的关系信息
|
||||
List* rinfo = stream_outer ? m_rrinfoInner : m_rrinfoOuter;
|
||||
RelOptInfo* otherside_rel = stream_outer ? m_outerRel : m_innerRel;
|
||||
double* multiple = stream_outer ? &m_multipleOuter : &m_multipleInner;
|
||||
|
|
@ -413,13 +361,11 @@ List* JoinPathGenBase::getOthersideKey(bool stream_outer)
|
|||
Node* match_var = NULL;
|
||||
ListCell* cell = NULL;
|
||||
|
||||
// 遍历约束信息
|
||||
foreach (cell, rinfo) {
|
||||
EquivalenceClass* oeclass = NULL;
|
||||
RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(cell);
|
||||
match_var = NULL;
|
||||
|
||||
// 根据约束信息的左右侧关系判断 EquivalenceClass
|
||||
if (bms_is_subset(restrictinfo->left_relids, otherside_rel->relids)) {
|
||||
oeclass = restrictinfo->left_ec;
|
||||
} else {
|
||||
|
|
@ -429,7 +375,6 @@ List* JoinPathGenBase::getOthersideKey(bool stream_outer)
|
|||
|
||||
Assert(restrictinfo->orclause == NULL);
|
||||
|
||||
// 遍历 EquivalenceClass 的成员
|
||||
foreach (lc1, oeclass->ec_members) {
|
||||
EquivalenceMember* em = (EquivalenceMember*)lfirst(lc1);
|
||||
Node* nem = (Node*)em->em_expr;
|
||||
|
|
@ -437,14 +382,10 @@ List* JoinPathGenBase::getOthersideKey(bool stream_outer)
|
|||
List* vars = NIL;
|
||||
Relids relIds;
|
||||
|
||||
// 如果数据类型无效或者不可分布,则跳过
|
||||
if (!OidIsValid(datatype) || !IsTypeDistributable(datatype))
|
||||
continue;
|
||||
|
||||
// 提取表达式中的关系 ID
|
||||
relIds = pull_varnos(nem);
|
||||
|
||||
// 如果关系 ID 为空或不是目标关系的子集,则跳过
|
||||
if (bms_is_empty(relIds) || !bms_is_subset(relIds, otherside_rel->relids)) {
|
||||
bms_free(relIds);
|
||||
continue;
|
||||
|
|
@ -487,7 +428,7 @@ List* JoinPathGenBase::getOthersideKey(bool stream_outer)
|
|||
|
||||
/* Calculate skew multiple of the distribute keys. */
|
||||
*multiple = get_multiple_by_distkey(m_root, key_list, otherside_rel->rows);
|
||||
// 检查分布键是否有效
|
||||
|
||||
if (!ng_is_distribute_key_valid(m_root, key_list, targetlist)) {
|
||||
list_free(key_list);
|
||||
key_list = NIL;
|
||||
|
|
@ -506,7 +447,6 @@ List* JoinPathGenBase::getOthersideKey(bool stream_outer)
|
|||
* @return void.
|
||||
*/
|
||||
void JoinPathGenBase::getDistributeKeys(
|
||||
// 调用 get_distribute_keys 函数以获取连接路径的分布键信息
|
||||
List** distribute_keys_outer, List** distribute_keys_inner, List* desired_keys, bool exact_match)
|
||||
{
|
||||
get_distribute_keys(m_root,
|
||||
|
|
@ -532,43 +472,31 @@ void JoinPathGenBase::getDistributeKeys(
|
|||
*/
|
||||
bool JoinPathGenBase::checkJoinMethodAlternative(bool* try_eq_related_indirectly)
|
||||
{
|
||||
// 初始化变量 hasalternative 为 false
|
||||
bool hasalternative = false;
|
||||
ListCell* l = NULL;
|
||||
|
||||
// 遍历连接限制信息列表 m_joinRestrictinfo
|
||||
foreach (l, m_joinRestrictinfo) {
|
||||
RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(l);
|
||||
|
||||
// 检查限制条件是否可以使用哈希连接或合并连接,并且连接的两个表匹配
|
||||
/* Check if clause is a hashable or mergeable operator clause */
|
||||
if (restrictinfo->can_join && clause_sides_match_join(restrictinfo, m_outerRel, m_innerRel)) {
|
||||
// 如果启用了哈希连接并且限制条件包含哈希连接操作符,则设置 hasalternative 为 true
|
||||
if (u_sess->attr.attr_sql.enable_hashjoin && restrictinfo->hashjoinoperator != InvalidOid)
|
||||
hasalternative = true;
|
||||
|
||||
// 如果启用了合并连接并且限制条件包含合并连接的操作符族,则设置 hasalternative 为 true
|
||||
if (u_sess->attr.attr_sql.enable_mergejoin && restrictinfo->mergeopfamilies != NIL)
|
||||
hasalternative = true;
|
||||
|
||||
// 如果启用了哈希连接或合并连接,将 try_eq_related_indirectly 设置为 true
|
||||
if (u_sess->attr.attr_sql.enable_hashjoin || u_sess->attr.attr_sql.enable_mergejoin)
|
||||
*try_eq_related_indirectly = true;
|
||||
}
|
||||
|
||||
// 如果已经找到了可行的连接方法,则退出循环
|
||||
if (hasalternative)
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果启用了嵌套循环连接,并且连接类型不是全外连接,则设置 hasalternative 为 true
|
||||
if (u_sess->attr.attr_sql.enable_nestloop && m_jointype != JOIN_FULL)
|
||||
hasalternative = true;
|
||||
|
||||
// 返回是否找到可行的连接方法
|
||||
return hasalternative;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @Description: check to see if this path is a nestloop index params path
|
||||
*
|
||||
|
|
@ -643,31 +571,23 @@ bool JoinPathGenBase::isReplicateJoinCanRedistribute()
|
|||
* redistribute; 2.Outer is hash and inner is replicate: LHS join or probing side execute on CN, and build side need
|
||||
* redistribute or is param path;
|
||||
*/
|
||||
// 如果外部表为复制表而内部表不是
|
||||
if (m_replicateOuter && !m_replicateInner) {
|
||||
// 如果连接类型是右连接且内部表需要重新分布,则不能重新分布
|
||||
if (RHS_join(m_saveJointype) && m_redistributeInner)
|
||||
can_redistribute = false;
|
||||
}
|
||||
// 如果内部表为复制表而外部表不是
|
||||
else if (!m_replicateOuter && m_replicateInner) {
|
||||
// 如果连接类型是左连接且外部表需要重新分布或者是参数路径,则不能重新分布
|
||||
} else if (!m_replicateOuter && m_replicateInner) {
|
||||
if (LHS_join(m_saveJointype) && (m_redistributeOuter || is_param_path()))
|
||||
can_redistribute = false;
|
||||
}
|
||||
// 如果外部表和内部表都不是复制表
|
||||
else {
|
||||
} else {
|
||||
can_redistribute = false;
|
||||
}
|
||||
|
||||
// 如果可以重新分布,则从 m_streamInfoList 中删除 m_streamInfoPair,并释放内存
|
||||
/* Need hash filter for replicate table, so delete this path. */
|
||||
if (can_redistribute) {
|
||||
m_streamInfoList = list_delete(m_streamInfoList, m_streamInfoPair);
|
||||
pfree_ext(m_streamInfoPair);
|
||||
m_streamInfoPair = NULL;
|
||||
}
|
||||
|
||||
// 返回是否可以重新分布
|
||||
return can_redistribute;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,19 +34,12 @@
|
|||
#include "utils/lsyscache.h"
|
||||
|
||||
/* local functions */
|
||||
// 检查特殊连接是否可移除
|
||||
static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo);
|
||||
// 从查询中移除关系
|
||||
static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelids);
|
||||
// 从连接列表中移除关系
|
||||
static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved);
|
||||
// 检查关系是否支持去重
|
||||
static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel);
|
||||
// 检查关系是否对指定子句去重
|
||||
static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause_list);
|
||||
// 检查列的唯一性
|
||||
static bool check_column_uniqueness(List* groupClause, List* targetList, List* colnos, List* opids);
|
||||
// 查找具有唯一性的列
|
||||
static Oid distinct_col_search(int colno, List* colnos, List* opids);
|
||||
|
||||
/*
|
||||
|
|
@ -57,25 +50,23 @@ static Oid distinct_col_search(int colno, List* colnos, List* opids);
|
|||
* We are passed the current joinlist and return the updated list. Other
|
||||
* data structures that have to be updated are accessible via "root".
|
||||
*/
|
||||
List* remove_useless_joins(PlannerInfo* root, List* joinlist)// 移除无用的连接
|
||||
List* remove_useless_joins(PlannerInfo* root, List* joinlist)
|
||||
{
|
||||
ListCell* lc = NULL;// 用于遍历join_info_list的指针
|
||||
ListCell* pnext = NULL;// 用于保存下一个元素的指针
|
||||
ListCell* lc = NULL;
|
||||
ListCell* pnext = NULL;
|
||||
|
||||
/*
|
||||
* We are only interested in relations that are left-joined to, so we can
|
||||
* scan the join_info_list to find them easily.
|
||||
*/
|
||||
restart:
|
||||
// 遍历特殊连接信息列表 join_info_list
|
||||
for (lc = list_head(root->join_info_list); lc != NULL; lc = pnext) {
|
||||
SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(lc);// 获取特殊连接信息
|
||||
pnext = lnext(lc); // 保存下一个元素的指针
|
||||
int innerrelid;// 内部关系的标识符
|
||||
int nremoved;// 移除的关系数量
|
||||
SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(lc);
|
||||
pnext = lnext(lc);
|
||||
int innerrelid;
|
||||
int nremoved;
|
||||
|
||||
/* Skip if not removable */
|
||||
// 检查特殊连接是否可移除,如果不可移除则继续下一个连接
|
||||
if (!join_is_removable(root, sjinfo))
|
||||
continue;
|
||||
|
||||
|
|
@ -84,14 +75,14 @@ restart:
|
|||
* righthand is a single baserel. Remove that rel from the query and
|
||||
* joinlist.
|
||||
*/
|
||||
innerrelid = bms_singleton_member(sjinfo->min_righthand);// 获取内部关系的标识符
|
||||
innerrelid = bms_singleton_member(sjinfo->min_righthand);
|
||||
|
||||
remove_rel_from_query(root, innerrelid, bms_union(sjinfo->min_lefthand, sjinfo->min_righthand));// 从查询中移除内部关系
|
||||
remove_rel_from_query(root, innerrelid, bms_union(sjinfo->min_lefthand, sjinfo->min_righthand));
|
||||
|
||||
/* We verify that exactly one reference gets removed from joinlist */
|
||||
nremoved = 0;
|
||||
joinlist = remove_rel_from_joinlist(joinlist, innerrelid, &nremoved);// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量
|
||||
if (nremoved != 1)// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量
|
||||
joinlist = remove_rel_from_joinlist(joinlist, innerrelid, &nremoved);
|
||||
if (nremoved != 1)
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
||||
|
|
@ -101,7 +92,7 @@ restart:
|
|||
* We can delete this SpecialJoinInfo from the list too, since it's no
|
||||
* longer of interest.
|
||||
*/
|
||||
root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo);// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量
|
||||
root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo);
|
||||
|
||||
/*
|
||||
* Restart the scan. This is necessary to ensure we find all
|
||||
|
|
@ -111,10 +102,10 @@ restart:
|
|||
* current list cell, we'd have to have some kluge to continue the
|
||||
* list scan anyway.
|
||||
*/
|
||||
goto restart;// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量
|
||||
goto restart;
|
||||
}
|
||||
|
||||
return joinlist;// 从连接列表 joinlist 中移除内部关系,并记录移除的关系数量
|
||||
return joinlist;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -127,21 +118,18 @@ restart:
|
|||
* rather than mixing outer and inner vars on either side. If it matches,
|
||||
* we set the transient flag outer_is_left to identify which side is which.
|
||||
*/
|
||||
static inline bool clause_sides_match_join(RestrictInfo* rinfo, Relids outerrelids, Relids innerrelids)// 内联函数,检查子句的两侧是否匹配连接
|
||||
|
||||
static inline bool clause_sides_match_join(RestrictInfo* rinfo, Relids outerrelids, Relids innerrelids)
|
||||
{
|
||||
// 检查约束信息的左侧关系是否是外部关系的子集,右侧关系是否是内部关系的子集
|
||||
if (bms_is_subset(rinfo->left_relids, outerrelids) && bms_is_subset(rinfo->right_relids, innerrelids)) {
|
||||
/* lefthand side is outer */
|
||||
rinfo->outer_is_left = true;// 设置 outer_is_left 为 true,表示左侧关系在连接中是外部关系
|
||||
return true;// 返回 true,表示约束信息的关系符合连接
|
||||
rinfo->outer_is_left = true;
|
||||
return true;
|
||||
} else if (bms_is_subset(rinfo->left_relids, innerrelids) && bms_is_subset(rinfo->right_relids, outerrelids)) {
|
||||
/* righthand side is outer */
|
||||
rinfo->outer_is_left = false;// 设置 outer_is_left 为 false,表示左侧关系在连接中是内部关系
|
||||
return true;// 返回 true,表示约束信息的关系符合连接
|
||||
rinfo->outer_is_left = false;
|
||||
return true;
|
||||
}
|
||||
return false; /* no good for these input relations */
|
||||
// 如果左右关系都不符合连接,返回 false
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -155,7 +143,7 @@ static inline bool clause_sides_match_join(RestrictInfo* rinfo, Relids outerreli
|
|||
* have to check that the inner side doesn't generate any variables needed
|
||||
* above the join.
|
||||
*/
|
||||
static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)// 检查连接是否可移除
|
||||
static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)
|
||||
{
|
||||
int innerrelid;
|
||||
RelOptInfo* innerrel = NULL;
|
||||
|
|
@ -168,7 +156,6 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)// 检
|
|||
* Must be a non-delaying left join to a single baserel, else we aren't
|
||||
* going to be able to do anything with it.
|
||||
*/
|
||||
// 仅适用于左连接和左反连接,且右侧关系为单一关系
|
||||
if ((sjinfo->jointype != JOIN_LEFT && sjinfo->jointype != JOIN_LEFT_ANTI_FULL) || sjinfo->delay_upper_joins ||
|
||||
bms_membership(sjinfo->min_righthand) != BMS_SINGLETON)
|
||||
return false;
|
||||
|
|
@ -181,11 +168,11 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)// 检
|
|||
* are needed above the join, make a quick check to eliminate cases in
|
||||
* which we will surely be unable to prove uniqueness of the innerrel.
|
||||
*/
|
||||
if (!rel_supports_distinctness(root, innerrel))// 检查右侧关系是否支持去重
|
||||
if (!rel_supports_distinctness(root, innerrel))
|
||||
return false;
|
||||
|
||||
/* Compute the relid set for the join we are considering */
|
||||
joinrelids = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand);// 计算连接关系的集合
|
||||
joinrelids = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand);
|
||||
|
||||
/*
|
||||
* We can't remove the join if any inner-rel attributes are used above the
|
||||
|
|
@ -200,7 +187,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)// 检
|
|||
* theory that the system attributes are somewhat less likely to be wanted
|
||||
* and should be tested last.
|
||||
*/
|
||||
for (attroff = innerrel->max_attr - innerrel->min_attr; attroff >= 0; attroff--) {// 检查左侧关系所需的属性是否完全包含在右侧关系中
|
||||
for (attroff = innerrel->max_attr - innerrel->min_attr; attroff >= 0; attroff--) {
|
||||
if (!bms_is_subset(innerrel->attr_needed[attroff], joinrelids))
|
||||
return false;
|
||||
}
|
||||
|
|
@ -212,7 +199,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)// 检
|
|||
* for that is relatively expensive, so we first check against ph_eval_at,
|
||||
* which must mention the inner rel if the PHV uses any inner-rel attrs.
|
||||
*/
|
||||
foreach (l, root->placeholder_list) {// 检查占位符的需要属性是否完全包含在右侧关系中
|
||||
foreach (l, root->placeholder_list) {
|
||||
PlaceHolderInfo* phinfo = (PlaceHolderInfo*)lfirst(l);
|
||||
|
||||
if (bms_is_subset(phinfo->ph_needed, joinrelids))
|
||||
|
|
@ -230,7 +217,7 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)// 检
|
|||
* it's what we want. The mergejoinability test also eliminates clauses
|
||||
* containing volatile functions, which we couldn't depend on.
|
||||
*/
|
||||
foreach (l, innerrel->joininfo) {// 遍历右侧关系的连接信息,检查是否符合移除条件
|
||||
foreach (l, innerrel->joininfo) {
|
||||
RestrictInfo* restrictinfo = (RestrictInfo*)lfirst(l);
|
||||
|
||||
/*
|
||||
|
|
@ -239,7 +226,6 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)// 检
|
|||
* above the outer join, even if it references no other rels (it might
|
||||
* be from WHERE, for example).
|
||||
*/
|
||||
// 如果约束已下推或所需关系不符合连接关系,则跳过
|
||||
if (restrictinfo->is_pushed_down || !bms_equal(restrictinfo->required_relids, joinrelids)) {
|
||||
/*
|
||||
* If such a clause actually references the inner rel then join
|
||||
|
|
@ -260,11 +246,11 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)// 检
|
|||
* Check if clause has the form "outer op inner" or "inner op outer",
|
||||
* and if so mark which side is inner.
|
||||
*/
|
||||
if (!clause_sides_match_join(restrictinfo, sjinfo->min_lefthand, innerrel->relids))// 检查约束的两侧是否匹配连接
|
||||
if (!clause_sides_match_join(restrictinfo, sjinfo->min_lefthand, innerrel->relids))
|
||||
continue; /* no good for these input relations */
|
||||
|
||||
/* OK, add to list */
|
||||
clause_list = lappend(clause_list, restrictinfo); // 将符合条件的约束加入列表
|
||||
clause_list = lappend(clause_list, restrictinfo);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -291,42 +277,38 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo)// 检
|
|||
* Also, join quals involving the rel have to be removed from the joininfo
|
||||
* lists, but only if they belong to the outer join identified by joinrelids.
|
||||
*/
|
||||
// 静态函数,用于从查询计划中移除与给定 relid 关联的关系
|
||||
static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelids)
|
||||
{
|
||||
RelOptInfo* rel = find_base_rel(root, relid);// 查找与给定 relid 相关的 RelOptInfo 结构体
|
||||
List* joininfos = NIL;// 用于存储连接信息的列表
|
||||
Index rti;// 用于遍历 RelOptInfo 结构体数组的索引
|
||||
ListCell* l = NULL;// 用于循环迭代的列表元素指针
|
||||
RelOptInfo* rel = find_base_rel(root, relid);
|
||||
List* joininfos = NIL;
|
||||
Index rti;
|
||||
ListCell* l = NULL;
|
||||
ListCell* nextl = NULL;
|
||||
|
||||
/*
|
||||
* Mark the rel as "dead" to show it is no longer part of the join tree.
|
||||
* (Removing it from the baserel array altogether seems too risky.)
|
||||
*/
|
||||
rel->reloptkind = RELOPT_DEADREL;// 将 rel 标记为已删除状态
|
||||
rel->reloptkind = RELOPT_DEADREL;
|
||||
|
||||
/*
|
||||
* Remove references to the rel from other baserels' attr_needed arrays.
|
||||
*/
|
||||
for (rti = 1; rti < (unsigned int)root->simple_rel_array_size; rti++) {// 遍历 simple_rel_array 数组中的 RelOptInfo 结构体
|
||||
RelOptInfo* otherrel = root->simple_rel_array[rti]; // 获取当前的 RelOptInfo 结构体
|
||||
for (rti = 1; rti < (unsigned int)root->simple_rel_array_size; rti++) {
|
||||
RelOptInfo* otherrel = root->simple_rel_array[rti];
|
||||
int attroff;
|
||||
|
||||
/* there may be empty slots corresponding to non-baserel RTEs */
|
||||
|
||||
// 如果 RelOptInfo 结构体为空,则继续下一轮循环
|
||||
if (otherrel == NULL)
|
||||
continue;
|
||||
|
||||
// 断言确保 RelOptInfo 的索引正确
|
||||
AssertEreport(otherrel->relid == rti, MOD_OPT, "RelOptInfo Index Incorrect.");
|
||||
|
||||
/* no point in processing target rel itself */
|
||||
if (otherrel == rel)// 如果当前的 RelOptInfo 与目标 rel 相同,则继续下一轮循环
|
||||
if (otherrel == rel)
|
||||
continue;
|
||||
|
||||
for (attroff = otherrel->max_attr - otherrel->min_attr; attroff >= 0; attroff--) {// 遍历属性列表,从属性的 attr_needed 中删除 relid
|
||||
for (attroff = otherrel->max_attr - otherrel->min_attr; attroff >= 0; attroff--) {
|
||||
otherrel->attr_needed[attroff] = bms_del_member(otherrel->attr_needed[attroff], relid);
|
||||
}
|
||||
}
|
||||
|
|
@ -339,7 +321,7 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid
|
|||
* RHS of the target outer join will be made empty here, but that's OK
|
||||
* since caller will delete that SpecialJoinInfo entirely.
|
||||
*/
|
||||
foreach (l, root->join_info_list) {// 遍历 join_info_list 列表,更新连接信息中的左右关系
|
||||
foreach (l, root->join_info_list) {
|
||||
SpecialJoinInfo* sjinfo = (SpecialJoinInfo*)lfirst(l);
|
||||
|
||||
sjinfo->min_lefthand = bms_del_member(sjinfo->min_lefthand, relid);
|
||||
|
|
@ -356,12 +338,11 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid
|
|||
* included in any lateral_lhs set. (It probably can't be, since that
|
||||
* should have precluded deciding to remove it; but let's cope anyway.)
|
||||
*/
|
||||
for (l = list_head(root->lateral_info_list); l != NULL; l = nextl)// 遍历 lateral_info_list 列表,更新 lateral 关系信息
|
||||
for (l = list_head(root->lateral_info_list); l != NULL; l = nextl)
|
||||
{
|
||||
LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l);
|
||||
|
||||
nextl = lnext(l);
|
||||
// 如果 lateral_rhs 与给定的 relid 相同,则从列表中删除该项
|
||||
if (ljinfo->lateral_rhs == (Index)relid)
|
||||
root->lateral_info_list = list_delete_ptr(root->lateral_info_list,
|
||||
ljinfo);
|
||||
|
|
@ -377,14 +358,14 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid
|
|||
* set. An empty eval_at set would confuse later processing since it
|
||||
* would match every possible eval placement.
|
||||
*/
|
||||
foreach (l, root->placeholder_list) {// 遍历 placeholder_list 列表,更新占位符信息
|
||||
foreach (l, root->placeholder_list) {
|
||||
PlaceHolderInfo* phinfo = (PlaceHolderInfo*)lfirst(l);
|
||||
|
||||
phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at, relid);// 从 ph_eval_at 中删除 relid
|
||||
if (bms_is_empty(phinfo->ph_eval_at)) /* oops, belay that */// 如果 ph_eval_at 变为空集,将 relid 添加回去
|
||||
phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at, relid);
|
||||
if (bms_is_empty(phinfo->ph_eval_at)) /* oops, belay that */
|
||||
phinfo->ph_eval_at = bms_add_member(phinfo->ph_eval_at, relid);
|
||||
|
||||
phinfo->ph_needed = bms_del_member(phinfo->ph_needed, relid);// 从 ph_needed 中删除 relid
|
||||
phinfo->ph_needed = bms_del_member(phinfo->ph_needed, relid);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -401,22 +382,22 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid
|
|||
* loop, because otherwise remove_join_clause_from_rels would destroy the
|
||||
* list while we're scanning it.
|
||||
*/
|
||||
joininfos = list_copy(rel->joininfo);// 复制 rel 的 joininfo 列表,并遍历处理
|
||||
joininfos = list_copy(rel->joininfo);
|
||||
foreach (l, joininfos) {
|
||||
RestrictInfo* rinfo = (RestrictInfo*)lfirst(l);
|
||||
|
||||
remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);// 从连接信息中移除与 relid 相关的子句
|
||||
remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);
|
||||
|
||||
if (rinfo->is_pushed_down || !bms_equal(rinfo->required_relids, joinrelids)) {// 如果子句已被推送下来或者不等于 joinrelids,则进行处理
|
||||
if (rinfo->is_pushed_down || !bms_equal(rinfo->required_relids, joinrelids)) {
|
||||
/* Recheck that qual doesn't actually reference the target rel */
|
||||
AssertEreport(!bms_is_member(relid, rinfo->clause_relids), MOD_OPT, "");
|
||||
/*
|
||||
* The required_relids probably aren't shared with anything else,
|
||||
* but let's copy them just to be sure.
|
||||
*/
|
||||
rinfo->required_relids = bms_copy(rinfo->required_relids);// 复制 required_relids,然后从中删除 relid
|
||||
rinfo->required_relids = bms_copy(rinfo->required_relids);
|
||||
rinfo->required_relids = bms_del_member(rinfo->required_relids, relid);
|
||||
distribute_restrictinfo_to_rels(root, rinfo);// 将处理后的信息重新分发到相关的 RelOptInfo 结构体中
|
||||
distribute_restrictinfo_to_rels(root, rinfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -430,23 +411,22 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid
|
|||
* *nremoved is incremented by the number of occurrences removed (there
|
||||
* should be exactly one, but the caller checks that).
|
||||
*/
|
||||
// 静态函数,从连接列表中移除与给定 relid 相关的关系
|
||||
static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved)
|
||||
{
|
||||
List* result = NIL;// 用于存储结果的列表
|
||||
ListCell* jl = NULL;// 用于遍历连接列表的列表元素指针
|
||||
List* result = NIL;
|
||||
ListCell* jl = NULL;
|
||||
|
||||
foreach (jl, joinlist) {// 遍历连接列表
|
||||
Node* jlnode = (Node*)lfirst(jl);// 获取当前列表元素
|
||||
foreach (jl, joinlist) {
|
||||
Node* jlnode = (Node*)lfirst(jl);
|
||||
|
||||
if (IsA(jlnode, RangeTblRef)) {// 如果当前元素是 RangeTblRef
|
||||
if (IsA(jlnode, RangeTblRef)) {
|
||||
int varno = ((RangeTblRef*)jlnode)->rtindex;
|
||||
|
||||
if (varno == relid)// 如果 varno 等于 relid,则增加 nremoved 计数
|
||||
if (varno == relid)
|
||||
(*nremoved)++;
|
||||
else// 否则将当前元素添加到结果列表中
|
||||
else
|
||||
result = lappend(result, jlnode);
|
||||
} else if (IsA(jlnode, List)) {// 如果当前元素是 List,则递归调用 remove_rel_from_joinlist 处理子列表
|
||||
} else if (IsA(jlnode, List)) {
|
||||
/* Recurse to handle subproblem */
|
||||
List* sublist = NIL;
|
||||
|
||||
|
|
@ -454,7 +434,7 @@ static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved)
|
|||
/* Avoid including empty sub-lists in the result */
|
||||
if (sublist != NIL)
|
||||
result = lappend(result, sublist);
|
||||
} else {// 如果是其他类型的节点,则报错
|
||||
} else {
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_UNEXPECTED_NODE_STATE),
|
||||
|
|
@ -463,7 +443,7 @@ static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved)
|
|||
}
|
||||
}
|
||||
|
||||
return result;// 返回处理后的结果列表
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -477,7 +457,7 @@ static List* remove_rel_from_joinlist(List* joinlist, int relid, int* nremoved)
|
|||
* rel_is_distinct_for()'s argument lists if the call could not possibly
|
||||
* succeed.
|
||||
*/
|
||||
static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel)// 静态函数,判断 rel 是否支持 DISTINCT 操作
|
||||
static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel)
|
||||
{
|
||||
/*
|
||||
* We only handle two cases here:
|
||||
|
|
@ -485,9 +465,9 @@ static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel)// 静
|
|||
* 2. subquery with aggreations
|
||||
* We can improve later when we can propagate uniqueness
|
||||
*/
|
||||
if (rel->reloptkind != RELOPT_BASEREL)// 如果 rel 不是基本关系,则不支持 DISTINCT
|
||||
if (rel->reloptkind != RELOPT_BASEREL)
|
||||
return false;
|
||||
if (rel->rtekind == RTE_RELATION) {// 如果 rel 是关系表(RTE_RELATION)
|
||||
if (rel->rtekind == RTE_RELATION) {
|
||||
/*
|
||||
* For a plain relation, we only know how to prove uniqueness by
|
||||
* reference to unique indexes. Make sure there's at least one
|
||||
|
|
@ -495,18 +475,17 @@ static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel)// 静
|
|||
* it's a partial index, it must match the query. (Keep these
|
||||
* conditions in sync with relation_has_unique_index_for!)
|
||||
*/
|
||||
// 遍历索引列表,查找唯一且立即可用的索引
|
||||
ListCell* lc = NULL;
|
||||
|
||||
foreach (lc, rel->indexlist) {
|
||||
IndexOptInfo* ind = (IndexOptInfo*)lfirst(lc);
|
||||
// 如果索引是唯一的、立即可用的,并且没有过滤条件,则支持 DISTINCT
|
||||
|
||||
if (ind->unique && ind->immediate && (ind->indpred == NIL || ind->predOK))
|
||||
return true;
|
||||
}
|
||||
} else if (rel->rtekind == RTE_SUBQUERY) {
|
||||
Query* subquery = root->simple_rte_array[rel->relid]->subquery;
|
||||
// 如果 rel 是子查询,则判断子查询是否支持 DISTINCT
|
||||
|
||||
/* Check if the subquery has any qualities that support distinctness */
|
||||
if (query_supports_distinctness(subquery))
|
||||
return true;
|
||||
|
|
@ -533,25 +512,24 @@ static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel)// 静
|
|||
* is OK for current uses, because the clause_list is built by the caller for
|
||||
* the sole purpose of passing to this function.
|
||||
*/
|
||||
static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause_list)// 静态函数,判断 rel 是否对给定的列列表支持 DISTINCT 操作
|
||||
static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause_list)
|
||||
{
|
||||
/*
|
||||
* We could skip a couple of tests here if we assume all callers checked
|
||||
* rel_supports_distinctness first, but it doesn't seem worth taking any
|
||||
* risk for.
|
||||
*/
|
||||
if (rel->reloptkind != RELOPT_BASEREL)// 如果 rel 不是基本关系,则不支持 DISTINCT
|
||||
if (rel->reloptkind != RELOPT_BASEREL)
|
||||
return false;
|
||||
if (rel->rtekind == RTE_RELATION) {// 如果 rel 是关系表(RTE_RELATION)
|
||||
|
||||
if (rel->rtekind == RTE_RELATION) {
|
||||
/*
|
||||
* Examine the indexes to see if we have a matching unique index.
|
||||
* relation_has_unique_index_for automatically adds any usable
|
||||
* restriction clauses for the rel, so we needn't do that here.
|
||||
*/
|
||||
if (relation_has_unique_index_for(root, rel, clause_list, NIL, NIL))// 调用 relation_has_unique_index_for 函数判断是否有唯一索引支持 DISTINCT
|
||||
if (relation_has_unique_index_for(root, rel, clause_list, NIL, NIL))
|
||||
return true;
|
||||
} else if (rel->rtekind == RTE_SUBQUERY) {// 如果 rel 是子查询,则判断子查询是否支持 DISTINCT
|
||||
} else if (rel->rtekind == RTE_SUBQUERY) {
|
||||
Index relid = rel->relid;
|
||||
Query* subquery = root->simple_rte_array[relid]->subquery;
|
||||
List* colnos = NIL;
|
||||
|
|
@ -567,7 +545,7 @@ static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause
|
|||
* (XXX we are not considering restriction clauses attached to the
|
||||
* subquery; is that worth doing?)
|
||||
*/
|
||||
foreach (l, clause_list) { // 遍历子句列表,获取列号和操作符号
|
||||
foreach (l, clause_list) {
|
||||
RestrictInfo* rinfo = (RestrictInfo*)lfirst(l);
|
||||
Oid op;
|
||||
Var* var = NULL;
|
||||
|
|
@ -589,7 +567,7 @@ static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause
|
|||
else
|
||||
var = (Var*)get_leftop(rinfo->clause);
|
||||
|
||||
if (var != NULL) { // 尝试找到兼容的 Var
|
||||
if (var != NULL) {
|
||||
/* try to find compatible var */
|
||||
var = locate_distribute_var((Expr*)var);
|
||||
}
|
||||
|
|
@ -606,7 +584,7 @@ static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause
|
|||
opids = lappend_oid(opids, op);
|
||||
}
|
||||
|
||||
if (query_is_distinct_for(subquery, colnos, opids))// 调用 query_is_distinct_for 函数判断子查询是否支持 DISTINCT
|
||||
if (query_is_distinct_for(subquery, colnos, opids))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -623,7 +601,7 @@ static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause
|
|||
* query_is_distinct_for()'s argument lists if the call could not possibly
|
||||
* succeed.
|
||||
*/
|
||||
bool query_supports_distinctness(Query* query)// 判断查询是否支持 DISTINCT 操作
|
||||
bool query_supports_distinctness(Query* query)
|
||||
{
|
||||
if (query->distinctClause != NIL || query->groupClause != NIL || query->hasAggs || query->havingQual ||
|
||||
query->setOperations)
|
||||
|
|
@ -650,12 +628,11 @@ bool query_supports_distinctness(Query* query)// 判断查询是否支持 DISTIN
|
|||
* should give trustworthy answers for all operators that we might need
|
||||
* to deal with here.)
|
||||
*/
|
||||
bool query_is_distinct_for(Query* query, List* colnos, List* opids)// 判断查询是否对给定列列表 colnos 和操作符列表 opids 支持 DISTINCT 操作
|
||||
bool query_is_distinct_for(Query* query, List* colnos, List* opids)
|
||||
{
|
||||
ListCell* l = NULL;
|
||||
Oid opid;
|
||||
|
||||
// 断言:列列表和操作符列表长度必须相等
|
||||
Assert(list_length(colnos) == list_length(opids));
|
||||
|
||||
/*
|
||||
|
|
@ -666,7 +643,7 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)// 判断查
|
|||
* specified columns, since those must be evaluated before de-duplication;
|
||||
* but it doesn't presently seem worth the complication to check that.)
|
||||
*/
|
||||
if (expression_returns_set((Node*)query->targetList))// 如果查询的目标列表返回集合,则不支持 DISTINCT
|
||||
if (expression_returns_set((Node*)query->targetList))
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
|
@ -674,9 +651,8 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)// 判断查
|
|||
* columns in the DISTINCT clause appear in colnos and operator semantics
|
||||
* match.
|
||||
*/
|
||||
// 如果查询中存在 DISTINCT 子句
|
||||
if (query->distinctClause != NIL) {
|
||||
if (check_column_uniqueness(query->distinctClause, query->targetList, colnos, opids))// 调用 check_column_uniqueness 函数检查列是否唯一
|
||||
if (check_column_uniqueness(query->distinctClause, query->targetList, colnos, opids))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -684,15 +660,15 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)// 判断查
|
|||
* Similarly, GROUP BY guarantees uniqueness if all the grouped columns
|
||||
* appear in colnos and operator semantics match.
|
||||
*/
|
||||
if (query->groupClause != NIL && query->groupingSets == NIL) {// 如果查询中存在 GROUP BY 子句且没有 GROUPING SETS
|
||||
if (check_column_uniqueness(query->groupClause, query->targetList, colnos, opids))// 调用 check_column_uniqueness 函数检查列是否唯一
|
||||
if (query->groupClause != NIL && query->groupingSets == NIL) {
|
||||
if (check_column_uniqueness(query->groupClause, query->targetList, colnos, opids))
|
||||
return true;
|
||||
} else if (query->groupingSets != NIL) {
|
||||
/*
|
||||
* If we have grouping sets with expressions, we probably don't have
|
||||
* uniqueness and analysis would be hard. Punt.
|
||||
*/
|
||||
if (query->groupClause != NIL) // 如果存在 GROUPING SETS
|
||||
if (query->groupClause != NIL)
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
|
@ -701,13 +677,13 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)// 判断查
|
|||
* then we're returning only one row and are certainly unique. But
|
||||
* otherwise, we know we're certainly not unique.
|
||||
*/
|
||||
bool isTrue = list_length(query->groupingSets) == 1 &&// 如果 GROUPING SETS 只包含一个空集合
|
||||
bool isTrue = list_length(query->groupingSets) == 1 &&
|
||||
((GroupingSet*)linitial(query->groupingSets))->kind == GROUPING_SET_EMPTY;
|
||||
if (isTrue)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
} else {// 如果查询中存在聚合函数或 HAVING 子句,则支持 DISTINCT
|
||||
} else {
|
||||
/*
|
||||
* If we have no GROUP BY, but do have aggregates or HAVING, then the
|
||||
* result is at most one row so it's surely unique, for any operators.
|
||||
|
|
@ -720,19 +696,18 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)// 判断查
|
|||
* UNION, INTERSECT, EXCEPT guarantee uniqueness of the whole output row,
|
||||
* except with ALL.
|
||||
*/
|
||||
// 如果查询中存在集合操作
|
||||
if (query->setOperations != NULL) {
|
||||
SetOperationStmt* topop = (SetOperationStmt*)query->setOperations;
|
||||
|
||||
Assert(IsA(topop, SetOperationStmt));
|
||||
Assert(topop->op != SETOP_NONE);
|
||||
|
||||
if (!topop->all) {// 如果不是 UNION ALL 操作
|
||||
if (!topop->all) {
|
||||
ListCell* lg = NULL;
|
||||
|
||||
/* We're good if all the nonjunk output columns are in colnos */
|
||||
lg = list_head(topop->groupClauses);
|
||||
foreach (l, query->targetList) {// 遍历查询的目标列表
|
||||
foreach (l, query->targetList) {
|
||||
TargetEntry* tle = (TargetEntry*)lfirst(l);
|
||||
SortGroupClause* sgc = NULL;
|
||||
|
||||
|
|
@ -743,13 +718,12 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)// 判断查
|
|||
Assert(lg != NULL);
|
||||
sgc = (SortGroupClause*)lfirst(lg);
|
||||
lg = lnext(lg);
|
||||
// 调用 distinct_col_search 函数查找列的操作符
|
||||
|
||||
opid = distinct_col_search(tle->resno, colnos, opids);
|
||||
if (!OidIsValid(opid) || !equality_ops_are_compatible(opid, sgc->eqop))
|
||||
break; /* exit early if no match */
|
||||
}
|
||||
if (l == NULL) /* had matches for all? */
|
||||
// 如果成功遍历了所有目标列,说明支持 DISTINCT
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -770,7 +744,7 @@ bool query_is_distinct_for(Query* query, List* colnos, List* opids)// 判断查
|
|||
* Given group clause and targetlist, find if all the aggregated columns
|
||||
* in colnos, return true if so, else false.
|
||||
*/
|
||||
static bool check_column_uniqueness(List* groupClause, List* targetList, List* colnos, List* opids)// 检查列是否唯一
|
||||
static bool check_column_uniqueness(List* groupClause, List* targetList, List* colnos, List* opids)
|
||||
{
|
||||
ListCell* l = NULL;
|
||||
Oid opid;
|
||||
|
|
@ -780,11 +754,9 @@ static bool check_column_uniqueness(List* groupClause, List* targetList, List* c
|
|||
TargetEntry* tle = get_sortgroupclause_tle(sgc, targetList);
|
||||
|
||||
opid = distinct_col_search(tle->resno, colnos, opids);
|
||||
// 如果操作符无效或不兼容,则中断循环
|
||||
if (!OidIsValid(opid) || !equality_ops_are_compatible(opid, sgc->eqop))
|
||||
break; /* exit early if no match */
|
||||
}
|
||||
// 如果成功遍历了所有列,说明支持 DISTINCT
|
||||
if (l == NULL) /* had matches for all? */
|
||||
return true;
|
||||
|
||||
|
|
@ -798,15 +770,15 @@ static bool check_column_uniqueness(List* groupClause, List* targetList, List* c
|
|||
* else return InvalidOid. (Ordinarily colnos would not contain duplicates,
|
||||
* but if it does, we arbitrarily select the first match.)
|
||||
*/
|
||||
static Oid distinct_col_search(int colno, List* colnos, List* opids)// 在列列表 colnos 中查找列的操作符
|
||||
static Oid distinct_col_search(int colno, List* colnos, List* opids)
|
||||
{
|
||||
ListCell* lc1 = NULL;
|
||||
ListCell* lc2 = NULL;
|
||||
|
||||
forboth(lc1, colnos, lc2, opids) // 同时遍历列列表和操作符列表
|
||||
forboth(lc1, colnos, lc2, opids)
|
||||
{
|
||||
if (colno == lfirst_int(lc1))
|
||||
return lfirst_oid(lc2);
|
||||
}
|
||||
return InvalidOid;// 如果未找到,返回无效的操作符标识
|
||||
return InvalidOid;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,18 +252,18 @@ FORCE_INLINE bool CanTransferInJoin(JoinType jointype)
|
|||
*
|
||||
* @return: void
|
||||
*/
|
||||
void set_plan_rows(Plan* plan, double globalRows, double multiple)// 设置计划节点的行数估算值
|
||||
void set_plan_rows(Plan* plan, double globalRows, double multiple)
|
||||
{
|
||||
plan->multiple = multiple;// 设置计划节点的多倍数
|
||||
plan->multiple = multiple;
|
||||
|
||||
/*
|
||||
* for global stats, We should reset global rows as localRows*u_sess->pgxc_cxt.NumDataNodes for replication except
|
||||
* RemoteQuery, because the local rows is equal to global rows.
|
||||
*/
|
||||
if (is_replicated_plan(plan) && is_execute_on_datanodes(plan)) {// 如果计划是复制计划并且在数据节点上执行
|
||||
plan->plan_rows = get_global_rows(globalRows, multiple, ng_get_dest_num_data_nodes(plan));// 调用 get_global_rows 函数获取全局行数估算值
|
||||
if (is_replicated_plan(plan) && is_execute_on_datanodes(plan)) {
|
||||
plan->plan_rows = get_global_rows(globalRows, multiple, ng_get_dest_num_data_nodes(plan));
|
||||
} else {
|
||||
plan->plan_rows = globalRows;// 否则,使用传入的全局行数估算值
|
||||
plan->plan_rows = globalRows;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -280,10 +280,10 @@ void set_plan_rows(Plan* plan, double globalRows, double multiple)// 设置计
|
|||
*
|
||||
* @return: void
|
||||
*/
|
||||
void set_plan_rows_from_plan(Plan* plan, double localRows, double multiple)// 从另一个计划节点获取行数估算值,并设置多倍数
|
||||
void set_plan_rows_from_plan(Plan* plan, double localRows, double multiple)
|
||||
{
|
||||
plan->multiple = multiple;// 设置计划节点的多倍数
|
||||
plan->plan_rows = get_global_rows(localRows, plan->multiple, ng_get_dest_num_data_nodes(plan));// 调用 get_global_rows 函数获取全局行数估算值
|
||||
plan->multiple = multiple;
|
||||
plan->plan_rows = get_global_rows(localRows, plan->multiple, ng_get_dest_num_data_nodes(plan));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -301,24 +301,23 @@ void set_plan_rows_from_plan(Plan* plan, double localRows, double multiple)//
|
|||
*
|
||||
* Returns a Plan tree.
|
||||
*/
|
||||
Plan* create_plan(PlannerInfo* root, Path* best_path)// 创建查询计划节点
|
||||
Plan* create_plan(PlannerInfo* root, Path* best_path)
|
||||
{
|
||||
Plan* plan = NULL;
|
||||
|
||||
/* plan_params should not be in use in current query level */
|
||||
Assert(root->plan_params == NIL);// 断言:查询计划参数列表为空
|
||||
Assert(root->plan_params == NIL);
|
||||
|
||||
/* Initialize this module's private workspace in PlannerInfo */
|
||||
// 清空当前外部关系和参数
|
||||
root->curOuterRels = NULL;
|
||||
root->curOuterParams = NIL;
|
||||
u_sess->opt_cxt.is_under_append_plan = false;
|
||||
|
||||
/* Recursively process the path tree */
|
||||
plan = create_plan_recurse(root, best_path);// 递归创建查询计划
|
||||
plan = create_plan_recurse(root, best_path);
|
||||
|
||||
/* Check we successfully assigned all NestLoopParams to plan nodes */
|
||||
if (root->curOuterParams != NIL)// 检查是否成功为所有 NestLoopParams 分配了计划节点
|
||||
if (root->curOuterParams != NIL)
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
||||
|
|
@ -330,7 +329,6 @@ Plan* create_plan(PlannerInfo* root, Path* best_path)// 创建查询计划节点
|
|||
root->plan_params = NIL;
|
||||
|
||||
/* Try to find the changed vars in chosed inlist2join path */
|
||||
// 如果在 QRW_INLIST2JOIN_CBO 模式下且存在变量映射,则查找 inlist2join 路径
|
||||
if (u_sess->opt_cxt.qrw_inlist2join_optmode == QRW_INLIST2JOIN_CBO && root->var_mappings != NIL) {
|
||||
find_inlist2join_path(root, best_path);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,22 +26,21 @@
|
|||
#include "optimizer/streamplan.h"
|
||||
void InitDynamicSmp()
|
||||
{
|
||||
DISTRIBUTED_FEATURE_NOT_SUPPORTED();// 调用 DISTRIBUTED_FEATURE_NOT_SUPPORTED 宏,表示不支持该功能
|
||||
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
|
||||
}
|
||||
|
||||
void ChooseStartQueryDop(int hashTableCount)
|
||||
{
|
||||
DISTRIBUTED_FEATURE_NOT_SUPPORTED();// 调用 DISTRIBUTED_FEATURE_NOT_SUPPORTED 宏,表示不支持该功能
|
||||
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
|
||||
}
|
||||
|
||||
void OptimizePlanDop(PlannedStmt* plannedStmt)
|
||||
{
|
||||
DISTRIBUTED_FEATURE_NOT_SUPPORTED(); // 调用 DISTRIBUTED_FEATURE_NOT_SUPPORTED 宏,表示不支持该功能
|
||||
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
|
||||
}
|
||||
|
||||
bool IsDynamicSmpEnabled()
|
||||
{
|
||||
// 使用一系列条件来检查是否启用了动态并行查询执行
|
||||
return IS_STREAM_PLAN && u_sess->opt_cxt.max_query_dop >= 0 && !u_sess->attr.attr_common.IsInplaceUpgrade &&
|
||||
!IsInitdb;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue