forked from Cachuela/openGauss-server
Compare commits
4 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
c6cf59f29f | |
|
|
a5410b7f86 | |
|
|
c711c7dc5f | |
|
|
3bea35e8c1 |
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,6 @@ char* all_data_nodename_list = NULL;
|
|||
const uint32 USTORE_UPGRADE_VERSION = 92368;
|
||||
const uint32 PACKAGE_ENHANCEMENT = 92444;
|
||||
const uint32 SUBSCRIPTION_VERSION = 92580;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
|
||||
|
||||
#ifdef DUMPSYSLOG
|
||||
char* syslogpath = NULL;
|
||||
|
|
@ -4445,16 +4444,23 @@ void getSubscriptions(Archive *fout)
|
|||
int i_subslotname;
|
||||
int i_subsynccommit;
|
||||
int i_subpublications;
|
||||
int i_subbinary;
|
||||
int i;
|
||||
int ntups;
|
||||
int i, ntups;
|
||||
|
||||
if (no_subscriptions || GetVersionNum(fout) < SUBSCRIPTION_VERSION) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isExecUserSuperRole(fout)) {
|
||||
write_msg(NULL, "WARNING: subscriptions not dumped because current user is not a superuser\n");
|
||||
res = ExecuteSqlQuery(fout,
|
||||
"SELECT count(*) FROM pg_subscription "
|
||||
"WHERE subdbid = (SELECT oid FROM pg_catalog.pg_database"
|
||||
" WHERE datname = current_database())",
|
||||
PGRES_TUPLES_OK);
|
||||
uint64 n = (res != NULL) ? strtoul(PQgetvalue(res, 0, 0), NULL, 10) : 0;
|
||||
if (n > 0) {
|
||||
write_msg(NULL, "WARNING: subscriptions not dumped because current user is not a superuser\n");
|
||||
}
|
||||
PQclear(res);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -4463,20 +4469,14 @@ void getSubscriptions(Archive *fout)
|
|||
resetPQExpBuffer(query);
|
||||
|
||||
/* Get the subscriptions in current database. */
|
||||
appendPQExpBuffer(query, "SELECT s.tableoid, s.oid, s.subname,"
|
||||
"(%s s.subowner) AS rolname, s.subconninfo, s.subslotname, "
|
||||
"s.subsynccommit, s.subpublications, \n", username_subquery);
|
||||
|
||||
if (GetVersionNum(fout) >= SUBSCRIPTION_BINARY_VERSION_NUM) {
|
||||
appendPQExpBuffer(query, " s.subbinary\n");
|
||||
} else {
|
||||
appendPQExpBuffer(query, " false AS subbinary\n");
|
||||
}
|
||||
|
||||
appendPQExpBuffer(query, "FROM pg_catalog.pg_subscription s "
|
||||
appendPQExpBuffer(query,
|
||||
"SELECT s.tableoid, s.oid, s.subname,"
|
||||
"(%s s.subowner) AS rolname, "
|
||||
" s.subconninfo, s.subslotname, s.subsynccommit, s.subpublications "
|
||||
"FROM pg_catalog.pg_subscription s "
|
||||
"WHERE s.subdbid = (SELECT oid FROM pg_catalog.pg_database"
|
||||
" WHERE datname = current_database())");
|
||||
|
||||
" WHERE datname = current_database())",
|
||||
username_subquery);
|
||||
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
|
||||
|
||||
ntups = PQntuples(res);
|
||||
|
|
@ -4494,7 +4494,6 @@ void getSubscriptions(Archive *fout)
|
|||
i_subslotname = PQfnumber(res, "subslotname");
|
||||
i_subsynccommit = PQfnumber(res, "subsynccommit");
|
||||
i_subpublications = PQfnumber(res, "subpublications");
|
||||
i_subbinary = PQfnumber(res, "subbinary");
|
||||
|
||||
subinfo = (SubscriptionInfo *)pg_malloc(ntups * sizeof(SubscriptionInfo));
|
||||
|
||||
|
|
@ -4513,7 +4512,6 @@ void getSubscriptions(Archive *fout)
|
|||
}
|
||||
subinfo[i].subsynccommit = gs_strdup(PQgetvalue(res, i, i_subsynccommit));
|
||||
subinfo[i].subpublications = gs_strdup(PQgetvalue(res, i, i_subpublications));
|
||||
subinfo[i].subbinary = gs_strdup(PQgetvalue(res, i, i_subbinary));
|
||||
|
||||
if (strlen(subinfo[i].rolname) == 0) {
|
||||
write_msg(NULL, "WARNING: owner of subscription \"%s\" appears to be invalid\n", subinfo[i].dobj.name);
|
||||
|
|
@ -4580,10 +4578,6 @@ static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
|
|||
appendPQExpBufferStr(query, "NONE");
|
||||
}
|
||||
|
||||
if (strcmp(subinfo->subbinary, "t") == 0) {
|
||||
appendPQExpBuffer(query, ", binary = true");
|
||||
}
|
||||
|
||||
if (strcmp(subinfo->subsynccommit, "off") != 0) {
|
||||
appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
|
||||
}
|
||||
|
|
@ -10786,11 +10780,6 @@ static void dumpDirectory(Archive* fout)
|
|||
char* dirpath = NULL;
|
||||
char* diracl = NULL;
|
||||
|
||||
if (!isExecUserSuperRole(fout)) {
|
||||
write_msg(NULL, "WARNING: directory not dumped because current user is not a superuser\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make sure we are in proper schema */
|
||||
selectSourceSchema(fout, "pg_catalog");
|
||||
|
||||
|
|
@ -21400,11 +21389,6 @@ static void dumpSynonym(Archive* fout)
|
|||
PQExpBuffer q;
|
||||
PQExpBuffer delq;
|
||||
|
||||
if (!isExecUserSuperRole(fout)) {
|
||||
write_msg(NULL, "WARNING: synonym not dumped because current user is not a superuser\n");
|
||||
return;
|
||||
}
|
||||
|
||||
selectSourceSchema(fout, "pg_catalog");
|
||||
query = createPQExpBuffer();
|
||||
printfPQExpBuffer(query,
|
||||
|
|
|
|||
|
|
@ -498,7 +498,6 @@ typedef struct _SubscriptionInfo {
|
|||
char *subslotname;
|
||||
char *subsynccommit;
|
||||
char *subpublications;
|
||||
char *subbinary;
|
||||
} SubscriptionInfo;
|
||||
|
||||
/* global decls */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "replication/worker_internal.h"
|
||||
|
||||
static List *textarray_to_stringlist(ArrayType *textarray);
|
||||
|
||||
|
|
@ -90,13 +91,6 @@ Subscription *GetSubscription(Oid subid, bool missing_ok)
|
|||
}
|
||||
sub->publications = textarray_to_stringlist(DatumGetArrayTypeP(datum));
|
||||
|
||||
datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subbinary, &isnull);
|
||||
if (unlikely(isnull)) {
|
||||
sub->binary = false;
|
||||
} else {
|
||||
sub->binary = DatumGetBool(datum);
|
||||
}
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
return sub;
|
||||
|
|
@ -189,7 +183,7 @@ char *get_subscription_name(Oid subid, bool missing_ok)
|
|||
}
|
||||
|
||||
/* Clear the list content, only deal with DefElem and string content */
|
||||
void ClearListContent(List *list)
|
||||
static void ClearListContent(List *list)
|
||||
{
|
||||
ListCell *cell = NULL;
|
||||
foreach(cell, list) {
|
||||
|
|
@ -209,6 +203,25 @@ void ClearListContent(List *list)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Decrypt conninfo for subscription.
|
||||
* IMPORTANT: caller should clear and free the memory after using it immediately
|
||||
*/
|
||||
char *DecryptConninfo(char *encryptConninfo)
|
||||
{
|
||||
const char* sensitiveOptionsArray[] = {"password"};
|
||||
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
|
||||
List *defList = ConninfoToDefList(encryptConninfo);
|
||||
DecryptOptions(defList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
||||
char *decryptConninfo = DefListToString(defList);
|
||||
|
||||
/* defList has plain content, clear it before free */
|
||||
ClearListContent(defList);
|
||||
list_free_ext(defList);
|
||||
/* IMPORTANT: caller should clear and free the memory after using it immediately */
|
||||
return decryptConninfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert text array to list of strings.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 = 92605;
|
||||
|
||||
const uint32 PREDPUSH_SAME_LEVEL_VERSION_NUM = 92522;
|
||||
const uint32 UPSERT_WHERE_VERSION_NUM = 92514;
|
||||
|
|
@ -101,7 +101,6 @@ const uint32 PRIVS_DIRECTORY_VERSION_NUM = 92460;
|
|||
const uint32 COMMENT_RECORD_PARAM_VERSION_NUM = 92484;
|
||||
const uint32 SCAN_BATCH_MODE_VERSION_NUM = 92568;
|
||||
const uint32 PUBLICATION_VERSION_NUM = 92580;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
|
||||
|
||||
/* Version number of the guc parameter backend_version added in V500R001C20 */
|
||||
const uint32 V5R1C20_BACKEND_VERSION_NUM = 92305;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
#include "utils/array.h"
|
||||
#include "utils/acl.h"
|
||||
|
||||
static bool ConnectPublisher(char* conninfo, char* slotname);
|
||||
static void ConnectPublisher(char *conninfo, char* slotname);
|
||||
static void CreateSlotInPublisher(char *slotname);
|
||||
static void ValidateReplicationSlot(char *slotname, List *publications);
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ static void ValidateReplicationSlot(char *slotname, List *publications);
|
|||
* accommodate that.
|
||||
*/
|
||||
static void parse_subscription_options(const List *options, char **conninfo, List **publications, bool *enabled_given,
|
||||
bool *enabled, bool *slot_name_given, char **slot_name, char **synchronous_commit, bool *binary_given, bool *binary)
|
||||
bool *enabled, bool *slot_name_given, char **slot_name, char **synchronous_commit)
|
||||
{
|
||||
ListCell *lc;
|
||||
|
||||
|
|
@ -76,10 +76,6 @@ static void parse_subscription_options(const List *options, char **conninfo, Lis
|
|||
if (synchronous_commit) {
|
||||
*synchronous_commit = NULL;
|
||||
}
|
||||
if (binary) {
|
||||
*binary_given = false;
|
||||
*binary = false;
|
||||
}
|
||||
|
||||
/* Parse options */
|
||||
foreach (lc, options) {
|
||||
|
|
@ -128,15 +124,6 @@ static void parse_subscription_options(const List *options, char **conninfo, Lis
|
|||
/* Test if the given value is valid for synchronous_commit GUC. */
|
||||
(void)set_config_option("synchronous_commit", *synchronous_commit, PGC_BACKEND, PGC_S_TEST, GUC_ACTION_SET,
|
||||
false, 0, false);
|
||||
} else if (strcmp(defel->defname, "binary") == 0 && binary) {
|
||||
if (*binary_given) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting or redundant options")));
|
||||
}
|
||||
|
||||
*binary_given = true;
|
||||
*binary = defGetBoolean(defel);
|
||||
} else {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR), errmsg("unrecognized subscription parameter: %s", defel->defname)));
|
||||
|
|
@ -210,82 +197,26 @@ static Datum publicationListToArray(List *publist)
|
|||
}
|
||||
|
||||
/*
|
||||
* Parse the original connection string which is encrypted, poll all hosts and ports,
|
||||
* and try to connect to the publisher.
|
||||
* When checkRemoteMode is true, the remotemode must be normal or primary.
|
||||
* Return true to indicate successful connection.
|
||||
* connect publisher and create slot.
|
||||
* the input conninfo should be encrypt, we will decrypt password inside
|
||||
*/
|
||||
bool AttemptConnectPublisher(const char *conninfoOriginal, char* slotname, bool checkRemoteMode)
|
||||
{
|
||||
size_t conninfoLen = strlen(conninfoOriginal) + 1;
|
||||
|
||||
char* conninfo = NULL;
|
||||
StringInfoData conninfoWithoutHostport;
|
||||
initStringInfo(&conninfoWithoutHostport);
|
||||
HostPort* hostPortList[MAX_REPLNODE_NUM] = {NULL};
|
||||
ParseConninfo(conninfoOriginal, &conninfoWithoutHostport, hostPortList);
|
||||
if (hostPortList[0] == NULL) {
|
||||
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg(
|
||||
"invalid connection string syntax, missing host and port")));
|
||||
}
|
||||
bool connectSuccess = false;
|
||||
conninfo = (char*)palloc(conninfoLen * sizeof(char));
|
||||
for (int i = 0; i < MAX_REPLNODE_NUM; ++i) {
|
||||
if (hostPortList[i] == NULL) {
|
||||
break;
|
||||
}
|
||||
int ret = snprintf_s(conninfo, conninfoLen, conninfoLen - 1,
|
||||
"%s host=%s port=%s", conninfoWithoutHostport.data,
|
||||
hostPortList[i]->host, hostPortList[i]->port);
|
||||
securec_check_ss(ret, "\0", "\0");
|
||||
|
||||
connectSuccess = ConnectPublisher(conninfo, slotname);
|
||||
if (!connectSuccess) {
|
||||
/* try next host */
|
||||
continue;
|
||||
}
|
||||
if (!checkRemoteMode) {
|
||||
break;
|
||||
}
|
||||
ServerMode publisherServerMde = IdentifyRemoteMode();
|
||||
if (publisherServerMde == NORMAL_MODE || publisherServerMde == PRIMARY_MODE) {
|
||||
break;
|
||||
}
|
||||
/* it's a standby, try next host */
|
||||
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
|
||||
connectSuccess = false;
|
||||
}
|
||||
pfree_ext(conninfo);
|
||||
|
||||
/* clean up */
|
||||
FreeStringInfo(&conninfoWithoutHostport);
|
||||
for (int i = 0; i < MAX_REPLNODE_NUM; ++i) {
|
||||
if (hostPortList[i] == NULL) {
|
||||
break;
|
||||
}
|
||||
pfree_ext(hostPortList[i]->host);
|
||||
pfree_ext(hostPortList[i]->port);
|
||||
pfree_ext(hostPortList[i]);
|
||||
}
|
||||
return connectSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* connect to publisher with conninfo
|
||||
*/
|
||||
static bool ConnectPublisher(char* conninfo, char* slotname)
|
||||
static void ConnectPublisher(char *conninfo, char *slotname)
|
||||
{
|
||||
/* Try to connect to the publisher. */
|
||||
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
|
||||
SpinLockAcquire(&walrcv->mutex);
|
||||
walrcv->conn_target = REPCONNTARGET_PUBLICATION;
|
||||
SpinLockRelease(&walrcv->mutex);
|
||||
char* decryptConninfo = EncryptOrDecryptConninfo(conninfo, 'D');
|
||||
|
||||
char *decryptConninfo = DecryptConninfo(conninfo);
|
||||
bool connectSuccess = (WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_connect(decryptConninfo, NULL, slotname, -1);
|
||||
int rc = memset_s(decryptConninfo, strlen(decryptConninfo), 0, strlen(decryptConninfo));
|
||||
securec_check(rc, "", "");
|
||||
pfree_ext(decryptConninfo);
|
||||
return connectSuccess;
|
||||
|
||||
if (!connectSuccess) {
|
||||
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("could not connect to the publisher")));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -362,10 +293,9 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
|||
bool enabled_given = false;
|
||||
bool enabled = true;
|
||||
char *synchronous_commit;
|
||||
char *conninfo;
|
||||
char *slotname;
|
||||
bool slotname_given;
|
||||
bool binary;
|
||||
bool binary_given;
|
||||
char originname[NAMEDATALEN];
|
||||
List *publications;
|
||||
int rc;
|
||||
|
|
@ -375,7 +305,7 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
|||
* Connection and publication should not be specified here.
|
||||
*/
|
||||
parse_subscription_options(stmt->options, NULL, NULL, &enabled_given, &enabled, &slotname_given, &slotname,
|
||||
&synchronous_commit, &binary_given, &binary);
|
||||
&synchronous_commit);
|
||||
|
||||
/*
|
||||
* Since creating a replication slot is not transactional, rolling back
|
||||
|
|
@ -403,10 +333,11 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
|||
synchronous_commit = "off";
|
||||
}
|
||||
|
||||
conninfo = stmt->conninfo;
|
||||
publications = stmt->publication;
|
||||
|
||||
/* Check the connection info string. */
|
||||
libpqrcv_check_conninfo(stmt->conninfo);
|
||||
libpqrcv_check_conninfo(conninfo);
|
||||
|
||||
/* Everything ok, form a new tuple. */
|
||||
rc = memset_s(values, sizeof(values), 0, sizeof(values));
|
||||
|
|
@ -418,12 +349,18 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
|||
values[Anum_pg_subscription_subname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->subname));
|
||||
values[Anum_pg_subscription_subowner - 1] = ObjectIdGetDatum(owner);
|
||||
values[Anum_pg_subscription_subenabled - 1] = BoolGetDatum(enabled);
|
||||
values[Anum_pg_subscription_subbinary - 1] = BoolGetDatum(binary);
|
||||
|
||||
/* encrypt conninfo */
|
||||
char *encryptConninfo = EncryptOrDecryptConninfo(stmt->conninfo, 'E');
|
||||
List *conninfoList = ConninfoToDefList(stmt->conninfo);
|
||||
/* Sensitive options for subscription, will be encrypted when saved to catalog. */
|
||||
const char* sensitiveOptionsArray[] = {"password"};
|
||||
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
|
||||
EncryptGenericOptions(conninfoList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
||||
char *encryptConninfo = DefListToString(conninfoList);
|
||||
|
||||
values[Anum_pg_subscription_subconninfo - 1] = CStringGetTextDatum(encryptConninfo);
|
||||
|
||||
pfree_ext(conninfoList);
|
||||
if (enabled) {
|
||||
if (!slotname_given) {
|
||||
slotname = stmt->subname;
|
||||
|
|
@ -459,14 +396,11 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
|||
*/
|
||||
if (enabled) {
|
||||
Assert(slotname);
|
||||
|
||||
if (!AttemptConnectPublisher(encryptConninfo, slotname, true)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("Failed to connect to publisher.")));
|
||||
}
|
||||
|
||||
ConnectPublisher(encryptConninfo, slotname);
|
||||
CreateSlotInPublisher(slotname);
|
||||
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
|
||||
}
|
||||
|
||||
pfree_ext(encryptConninfo);
|
||||
heap_close(rel, RowExclusiveLock);
|
||||
rc = memset_s(stmt->conninfo, strlen(stmt->conninfo), 0, strlen(stmt->conninfo));
|
||||
|
|
@ -505,8 +439,6 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
|||
Oid subid;
|
||||
bool enabled_given = false;
|
||||
bool enabled;
|
||||
bool binary_given;
|
||||
bool binary;
|
||||
char *synchronous_commit;
|
||||
char *conninfo;
|
||||
char *slot_name;
|
||||
|
|
@ -541,7 +473,7 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
|||
|
||||
/* Parse options. */
|
||||
parse_subscription_options(stmt->options, &conninfo, &publications, &enabled_given, &enabled, &slotname_given,
|
||||
&slot_name, &synchronous_commit, &binary_given, &binary);
|
||||
&slot_name, &synchronous_commit);
|
||||
|
||||
/* Form a new tuple. */
|
||||
rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls));
|
||||
|
|
@ -558,15 +490,23 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
|||
if (conninfo) {
|
||||
/* Check the connection info string. */
|
||||
libpqrcv_check_conninfo(conninfo);
|
||||
encryptConninfo = EncryptOrDecryptConninfo(conninfo, 'E');
|
||||
rc = memset_s(conninfo, strlen(conninfo), 0, strlen(conninfo));
|
||||
securec_check(rc, "\0", "\0");
|
||||
values[Anum_pg_subscription_subconninfo - 1] = CStringGetTextDatum(encryptConninfo);
|
||||
replaces[Anum_pg_subscription_subconninfo - 1] = true;
|
||||
|
||||
/* encrypt conninfo */
|
||||
List *conninfoList = ConninfoToDefList(conninfo);
|
||||
/* Sensitive options for subscription, will be encrypted when saved to catalog. */
|
||||
const char* sensitiveOptionsArray[] = {"password"};
|
||||
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
|
||||
EncryptGenericOptions(conninfoList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
||||
encryptConninfo = DefListToString(conninfoList);
|
||||
needFreeConninfo = true;
|
||||
|
||||
/* need to check whether new conninfo can be used to connect to new publisher */
|
||||
values[Anum_pg_subscription_subconninfo - 1] = CStringGetTextDatum(encryptConninfo);
|
||||
replaces[Anum_pg_subscription_subconninfo - 1] = true;
|
||||
|
||||
pfree_ext(conninfoList);
|
||||
|
||||
if (sub->enabled || (enabled_given && enabled)) {
|
||||
/* we need to check whether new conninfo can be used to connect to new publisher */
|
||||
checkConn = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -608,10 +548,6 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
|||
values[Anum_pg_subscription_subsynccommit - 1] = CStringGetTextDatum(synchronous_commit);
|
||||
replaces[Anum_pg_subscription_subsynccommit - 1] = true;
|
||||
}
|
||||
if (binary_given) {
|
||||
values[Anum_pg_subscription_subbinary - 1] = BoolGetDatum(binary);
|
||||
replaces[Anum_pg_subscription_subbinary - 1] = true;
|
||||
}
|
||||
if (publications != NIL) {
|
||||
values[Anum_pg_subscription_subpublications - 1] = publicationListToArray(publications);
|
||||
replaces[Anum_pg_subscription_subpublications - 1] = true;
|
||||
|
|
@ -634,18 +570,16 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
|||
if (sub->enabled && !enabled) {
|
||||
ereport(ERROR, (errmsg("If you want to deactivate this subscription, use DROP SUBSCRIPTION.")));
|
||||
}
|
||||
/* enabling subscription, but slot hasn't been created,
|
||||
* then mark createSlot to true.
|
||||
*/
|
||||
if (!sub->enabled && enabled && (!sub->slotname || !*(sub->slotname))) {
|
||||
/* enable subscription */
|
||||
if (!sub->enabled && enabled) {
|
||||
/* if slot hasn't been created, then create it */
|
||||
if (!sub->slotname || !*(sub->slotname)) {
|
||||
createSlot = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkConn || createSlot || validateSlot) {
|
||||
if (!AttemptConnectPublisher(encryptConninfo, finalSlotName, true)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg(
|
||||
checkConn ? "The new conninfo cannot connect to new publisher." : "Failed to connect to publisher.")));
|
||||
}
|
||||
ConnectPublisher(encryptConninfo, finalSlotName);
|
||||
|
||||
if (createSlot) {
|
||||
CreateSlotInPublisher(finalSlotName);
|
||||
|
|
@ -663,6 +597,12 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
|||
if (needFreeConninfo) {
|
||||
pfree_ext(encryptConninfo);
|
||||
}
|
||||
|
||||
if (conninfo) {
|
||||
rc = memset_s(conninfo, strlen(conninfo), 0, strlen(conninfo));
|
||||
securec_check(rc, "", "");
|
||||
}
|
||||
|
||||
return myself;
|
||||
}
|
||||
|
||||
|
|
@ -813,11 +753,7 @@ void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
|
|||
initStringInfo(&cmd);
|
||||
appendStringInfo(&cmd, "DROP_REPLICATION_SLOT %s", quote_identifier(slotname));
|
||||
|
||||
if (!AttemptConnectPublisher(conninfo, slotname, true)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg(
|
||||
"could not connect to publisher.")));
|
||||
}
|
||||
|
||||
ConnectPublisher(conninfo, slotname);
|
||||
PG_TRY();
|
||||
{
|
||||
int sqlstate = 0;
|
||||
|
|
@ -843,7 +779,6 @@ void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
|
|||
|
||||
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
|
||||
|
||||
pfree_ext(conninfo);
|
||||
pfree(cmd.data);
|
||||
heap_close(rel, NoLock);
|
||||
}
|
||||
|
|
@ -973,149 +908,3 @@ void RenameSubscription(List *oldname, const char *newname)
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the host or port string into a string array,
|
||||
* where host and port are separated by ",".
|
||||
* input: conn --- host or port string separated by ","
|
||||
* output: connArray --- host or port string array
|
||||
* return: the length of connArray
|
||||
* for example:
|
||||
* (1):
|
||||
* conn = 1.1.1.1,2.2.2.2,...,9.9.9.9
|
||||
* connArray = {
|
||||
* 1,.1.1.1,
|
||||
* 2.2.2.2,
|
||||
* ...,
|
||||
* 9.9.9.9
|
||||
* }
|
||||
* return 9
|
||||
* (2):
|
||||
* conn = 1,2,...,9
|
||||
* connArray = {1,2,...,9}
|
||||
* return 9
|
||||
*/
|
||||
static int HostsPortsToArray(const char* conn, char** connArray)
|
||||
{
|
||||
if (conn == NULL) {
|
||||
return 0;
|
||||
}
|
||||
char* cp = NULL;
|
||||
char* cur = NULL;
|
||||
char *buf = pstrdup(conn);
|
||||
|
||||
cp = buf;
|
||||
int i = 0;
|
||||
while (*cp) {
|
||||
cur = cp;
|
||||
while (*cp && *cp != ',') {
|
||||
++cp;
|
||||
}
|
||||
if (*cp == ',') {
|
||||
*cp = '\0';
|
||||
++cp;
|
||||
}
|
||||
if (i >= MAX_REPLNODE_NUM) {
|
||||
ereport(ERROR, (errmsg("Currently, a maximum of %d servers are "
|
||||
"supported.", MAX_REPLNODE_NUM)));
|
||||
}
|
||||
connArray[i++] = pstrdup(cur);
|
||||
|
||||
if (*cp == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
pfree(buf);
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse host and port
|
||||
*/
|
||||
static void ParseHostPort(char* hoststr, char* portstr, HostPort** hostPortList)
|
||||
{
|
||||
char* hosts[MAX_REPLNODE_NUM] = {NULL};
|
||||
char* ports[MAX_REPLNODE_NUM] = {NULL};
|
||||
int hostNum = HostsPortsToArray(hoststr, hosts);
|
||||
int portNum = HostsPortsToArray(portstr, ports);
|
||||
if (hostNum != portNum) {
|
||||
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("The number of host and port are inconsistent.")));
|
||||
}
|
||||
|
||||
for (int i = 0; i < hostNum; ++i) {
|
||||
hostPortList[i] = (HostPort*)palloc(sizeof(HostPort));
|
||||
hostPortList[i]->host = hosts[i];
|
||||
hostPortList[i]->port = ports[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse conninfo
|
||||
* conninfo format:
|
||||
* 'dbname=abc user=username password=xxxx host=ip1,ip2,...,ip9 port=p1,p2,...,p9'
|
||||
* after parsing:
|
||||
* conninfoWithoutHostPort:
|
||||
* 'dbname=abc user=username password=xxxx'
|
||||
* hostPortList:
|
||||
* {
|
||||
* {host=ip1, port=p1},
|
||||
* {host=ip2, port=p2},
|
||||
* ...
|
||||
* {host=ip9, port=p9}
|
||||
* }
|
||||
*/
|
||||
void ParseConninfo(const char* conninfo, StringInfoData* conninfoWithoutHostPort, HostPort** hostPortList)
|
||||
{
|
||||
List* conninfoList = ConninfoToDefList(conninfo);
|
||||
ListCell* l = NULL;
|
||||
|
||||
char* hostStr = NULL;
|
||||
char* portStr = NULL;
|
||||
foreach (l, conninfoList) {
|
||||
DefElem* defel = (DefElem*)lfirst(l);
|
||||
if (pg_strcasecmp(defel->defname, "host") == 0) {
|
||||
hostStr = defGetString(defel);
|
||||
} else if (pg_strcasecmp(defel->defname, "port") == 0) {
|
||||
portStr = defGetString(defel);
|
||||
} else {
|
||||
appendStringInfo(conninfoWithoutHostPort, "%s=%s ", defel->defname, defGetString(defel));
|
||||
}
|
||||
}
|
||||
if (hostPortList != NULL) {
|
||||
ParseHostPort(hostStr, portStr, hostPortList);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* encrypt conninfo when action = 'E'
|
||||
* decrypt conninfo when action = 'D'
|
||||
* conninfoNew: encrypted or decrypted conninfo
|
||||
*/
|
||||
char* EncryptOrDecryptConninfo(const char* conninfo, const char action)
|
||||
{
|
||||
/* parse conninfo to list */
|
||||
List *conninfoList = ConninfoToDefList(conninfo);
|
||||
/* Sensitive options for subscription */
|
||||
const char* sensitiveOptionsArray[] = {"password"};
|
||||
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
|
||||
switch (action) {
|
||||
/* Encrypt */
|
||||
case 'E':
|
||||
EncryptGenericOptions(conninfoList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
||||
break;
|
||||
|
||||
/* Decrypt */
|
||||
case 'D':
|
||||
DecryptOptions(conninfoList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
char* conninfoNew = DefListToString(conninfoList);
|
||||
ClearListContent(conninfoList);
|
||||
list_free_ext(conninfoList);
|
||||
|
||||
return conninfoNew;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1181,17 +1181,6 @@ static Node* pull_up_simple_subquery(PlannerInfo* root, Node* jtnode, RangeTblEn
|
|||
return jtnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* We must flatten any join alias Vars in the subquery's targetlist,
|
||||
* because pulling up the subquery's subqueries might have changed their
|
||||
* expansions into arbitrary expressions, which could affect
|
||||
* pullup_replace_vars' decisions about whether PlaceHolderVar wrappers
|
||||
* are needed for tlist entries. (Likely it'd be better to do
|
||||
* flatten_join_alias_vars on the whole query tree at some earlier stage,
|
||||
* maybe even in the rewriter; but for now let's just fix this case here.)
|
||||
*/
|
||||
subquery->targetList = (List *) flatten_join_alias_vars(subroot, (Node *) subquery->targetList);
|
||||
|
||||
/*
|
||||
* Adjust level-0 varnos in subquery so that we can append its rangetable
|
||||
* to upper query's. We have to fix the subquery's append_rel_list as
|
||||
|
|
|
|||
|
|
@ -65,25 +65,14 @@ static void setRuleCheckAsUser_Query(Query* qry, Oid userid);
|
|||
* takes the arguments and inserts them as a row into the system
|
||||
* relation "pg_rewrite"
|
||||
*/
|
||||
static Oid InsertRule(char* rulname, // 规则名称
|
||||
int evtype, // 事件类型(触发器类型)
|
||||
Oid eventrel_oid, // 触发器所属关系的OID
|
||||
AttrNumber evslot_index, // 触发器事件索引号
|
||||
bool evinstead, // 是否是INSTEAD 触发器
|
||||
Node* event_qual, // 触发器事件限定条件
|
||||
List* action, // 触发器的操作列表(动作)
|
||||
bool replace) // 是否替换已存在的同名规则
|
||||
static Oid InsertRule(char* rulname, int evtype, Oid eventrel_oid, AttrNumber evslot_index, bool evinstead,
|
||||
Node* event_qual, List* action, bool replace)
|
||||
{
|
||||
// 将触发器事件限定条件和操作列表转换为字符串形式
|
||||
char* evqual = nodeToString(event_qual);
|
||||
char* actiontree = nodeToString((Node*)action);
|
||||
|
||||
// 定义数据和标志数组以保存规则的属性值和状态
|
||||
Datum values[Natts_pg_rewrite];
|
||||
bool nulls[Natts_pg_rewrite];
|
||||
bool replaces[Natts_pg_rewrite];
|
||||
|
||||
// 定义规则名称、pg_rewrite关系的描述符、新的和旧的HeapTuple以及新的规则对象OID
|
||||
NameData rname;
|
||||
Relation pg_rewrite_desc;
|
||||
HeapTuple tup, oldtup;
|
||||
|
|
@ -95,14 +84,10 @@ static Oid InsertRule(char* rulname, // 规则名称
|
|||
/*
|
||||
* Set up *nulls and *values arrays
|
||||
*/
|
||||
|
||||
|
||||
rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls));
|
||||
securec_check(rc, "", "");
|
||||
|
||||
(void)namestrcpy(&rname, rulname);
|
||||
|
||||
// 使用提供的参数初始化values数组
|
||||
values[Anum_pg_rewrite_rulename - 1] = NameGetDatum(&rname);
|
||||
values[Anum_pg_rewrite_ev_class - 1] = ObjectIdGetDatum(eventrel_oid);
|
||||
values[Anum_pg_rewrite_ev_attr - 1] = Int16GetDatum(evslot_index);
|
||||
|
|
@ -123,7 +108,6 @@ static Oid InsertRule(char* rulname, // 规则名称
|
|||
oldtup = SearchSysCache2(RULERELNAME, ObjectIdGetDatum(eventrel_oid), PointerGetDatum(rulname));
|
||||
|
||||
if (HeapTupleIsValid(oldtup)) {
|
||||
// 如果不允许替换,抛出错误,因为已经存在同名规则
|
||||
if (!replace)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
|
|
@ -140,19 +124,15 @@ static Oid InsertRule(char* rulname, // 规则名称
|
|||
replaces[Anum_pg_rewrite_ev_qual - 1] = true;
|
||||
replaces[Anum_pg_rewrite_ev_action - 1] = true;
|
||||
|
||||
// 通过将新的属性值与旧的元组结合,创建一个新的元组
|
||||
tup = (HeapTuple) tableam_tops_modify_tuple(oldtup, RelationGetDescr(pg_rewrite_desc), values, nulls, replaces);
|
||||
|
||||
// 更新pg_rewrite关系中的元组
|
||||
simple_heap_update(pg_rewrite_desc, &tup->t_self, tup);
|
||||
|
||||
// 释放旧元组的系统缓存引用
|
||||
ReleaseSysCache(oldtup);
|
||||
|
||||
rewriteObjectId = HeapTupleGetOid(tup);
|
||||
is_update = true;
|
||||
} else {
|
||||
// 如果没有找到同名的规则,将一个新的元组插入pg_rewrite中
|
||||
tup = heap_form_tuple(pg_rewrite_desc->rd_att, values, nulls);
|
||||
|
||||
rewriteObjectId = simple_heap_insert(pg_rewrite_desc, tup);
|
||||
|
|
@ -181,7 +161,6 @@ static Oid InsertRule(char* rulname, // 规则名称
|
|||
referenced.objectId = eventrel_oid;
|
||||
referenced.objectSubId = 0;
|
||||
|
||||
// 记录依赖关系
|
||||
recordDependencyOn(&myself, &referenced, (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
|
||||
|
||||
/*
|
||||
|
|
@ -194,8 +173,6 @@ static Oid InsertRule(char* rulname, // 规则名称
|
|||
Query* qry = (Query*)linitial(action);
|
||||
|
||||
qry = getInsertSelectQuery(qry, NULL);
|
||||
|
||||
// 在event_qual上创建引用对象的依赖关系
|
||||
recordDependencyOnExpr(&myself, event_qual, qry->rtable, DEPENDENCY_NORMAL);
|
||||
}
|
||||
|
||||
|
|
@ -213,9 +190,9 @@ static Oid InsertRule(char* rulname, // 规则名称
|
|||
*/
|
||||
void DefineRule(RuleStmt* stmt, const char* queryString)
|
||||
{
|
||||
List* actions = NIL;//规则的操作列表
|
||||
Node* whereClause = NULL; // 规则的WHERE子句
|
||||
Oid relId;// 关系的OID(对象标识符)
|
||||
List* actions = NIL;
|
||||
Node* whereClause = NULL;
|
||||
Oid relId;
|
||||
|
||||
/* Parse analysis. */
|
||||
transformRuleStmt(stmt, queryString, &actions, &whereClause);
|
||||
|
|
@ -240,15 +217,15 @@ void DefineRule(RuleStmt* stmt, const char* queryString)
|
|||
void DefineQueryRewrite(
|
||||
char* rulename, Oid event_relid, Node* event_qual, CmdType event_type, bool is_instead, bool replace, List* action)
|
||||
{
|
||||
Relation event_relation;// 触发规则的关系
|
||||
int event_attno; // 触发规则的属性编号
|
||||
ListCell* l = NULL;// 遍历操作列表的指针
|
||||
Query* query = NULL;// 规则操作的查询对象
|
||||
bool RelisBecomingView = false;// 标记关系是否变成视图
|
||||
Datum values[Natts_pg_class];// 用于修改pg_class元组的字段值数组
|
||||
bool nulls[Natts_pg_class]; // 用于修改pg_class元组的空值标志数组
|
||||
bool replaces[Natts_pg_class];// 用于修改pg_class元组的字段替换标志数组
|
||||
errno_t rc;// 用于内存安全检查的错误码
|
||||
Relation event_relation;
|
||||
int event_attno;
|
||||
ListCell* l = NULL;
|
||||
Query* query = NULL;
|
||||
bool RelisBecomingView = false;
|
||||
Datum values[Natts_pg_class];
|
||||
bool nulls[Natts_pg_class];
|
||||
bool replaces[Natts_pg_class];
|
||||
errno_t rc;
|
||||
|
||||
/*
|
||||
* If we are installing an ON SELECT rule, we had better grab
|
||||
|
|
@ -260,7 +237,7 @@ void DefineQueryRewrite(
|
|||
*
|
||||
* Note that this lock level should match the one used in DefineRule.
|
||||
*/
|
||||
event_relation = heap_open(event_relid, AccessExclusiveLock);//获取事件关系的 AccessExclusiveLock,确保在定义规则时对关系进行独占访问
|
||||
event_relation = heap_open(event_relid, AccessExclusiveLock);
|
||||
|
||||
/*
|
||||
* Verify relation is of a type that rules can sensibly be applied to.
|
||||
|
|
@ -306,7 +283,7 @@ void DefineQueryRewrite(
|
|||
errmsg("rule actions on NEW are not implemented"),
|
||||
errhint("Use triggers instead.")));
|
||||
}
|
||||
//处理 CMD_UTILITY 事件(实用命令)
|
||||
|
||||
if (event_type == CMD_UTILITY) {
|
||||
bool is_copy = false;
|
||||
if (list_length(action) == 1) {
|
||||
|
|
@ -321,7 +298,7 @@ void DefineQueryRewrite(
|
|||
}
|
||||
}
|
||||
}
|
||||
//处理 CMD_SELEC 事件(实用命令)
|
||||
|
||||
if (event_type == CMD_SELECT) {
|
||||
/*
|
||||
* Rules ON SELECT are restricted to view definitions
|
||||
|
|
@ -379,12 +356,12 @@ void DefineQueryRewrite(
|
|||
*/
|
||||
if (!replace && event_relation->rd_rules != NULL) {
|
||||
int i;
|
||||
// 遍历事件关系中的每个规则
|
||||
|
||||
for (i = 0; i < event_relation->rd_rules->numLocks; i++) {
|
||||
RewriteRule* rule = NULL;
|
||||
// 获取当前规则
|
||||
|
||||
rule = event_relation->rd_rules->rules[i];
|
||||
if (rule->event == CMD_SELECT)// 如果当前规则是一个 SELECT 规则,表示事件关系已经是一个视图,触发错误
|
||||
if (rule->event == CMD_SELECT)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("\"%s\" is already a view", RelationGetRelationName(event_relation))));
|
||||
|
|
@ -489,27 +466,24 @@ void DefineQueryRewrite(
|
|||
*/
|
||||
bool haveReturning = false;
|
||||
|
||||
foreach (l, action) {// 遍历规则中的每个动作
|
||||
foreach (l, action) {
|
||||
query = (Query*)lfirst(l);
|
||||
// 如果当前动作没有 RETURNING 子句,继续下一个动作
|
||||
|
||||
if (!query->returningList)
|
||||
continue;
|
||||
// 如果已经有 RETURNING 子句出现,触发错误,不允许多个 RETURNING 子句
|
||||
if (haveReturning)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot have multiple RETURNING lists in a rule")));
|
||||
haveReturning = true;
|
||||
// 如果存在条件限制(event_qual 不为空),不支持 RETURNING 子句,触发错误
|
||||
if (event_qual != NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("RETURNING lists are not supported in conditional rules")));
|
||||
// 如果不是 INSTEAD 规则,不支持 RETURNING 子句,触发错误
|
||||
if (!is_instead)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("RETURNING lists are not supported in non-INSTEAD rules")));
|
||||
checkRuleResultList(query->returningList, RelationGetDescr(event_relation), false); // 检查 RETURNING 子句的结果列表是否与事件关系匹配
|
||||
checkRuleResultList(query->returningList, RelationGetDescr(event_relation), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -548,19 +522,14 @@ void DefineQueryRewrite(
|
|||
* ---------------------------------------------------------------------
|
||||
*/
|
||||
if (RelisBecomingView) {
|
||||
Relation relationRelation; // 用于访问 pg_class 表的关系对象
|
||||
Oid toastrelid;// toast 表的 OID
|
||||
HeapTuple classTup;// 关系在 pg_class 表中的元组
|
||||
HeapTuple nctup;// 新的关系元组,用于更新 pg_class 表
|
||||
Form_pg_class classForm; // 指向关系元组的结构体,用于访问关系属性
|
||||
Oid nspid;// 关系所属的命名空间的 OID
|
||||
Relation relationRelation;
|
||||
Oid toastrelid;
|
||||
HeapTuple classTup;
|
||||
HeapTuple nctup;
|
||||
Form_pg_class classForm;
|
||||
Oid nspid;
|
||||
|
||||
// 打开 pg_class 表,用于获取和修改关系元数据
|
||||
// 使用 RowExclusiveLock 锁定,以确保独占访问
|
||||
relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
|
||||
// 获取事件关系的 toast 表的 OID
|
||||
// toast 表用于存储超过一页大小的大对象数据
|
||||
toastrelid = event_relation->rd_rel->reltoastrelid;
|
||||
|
||||
/* drop storage while table still looks like a table */
|
||||
|
|
@ -602,7 +571,7 @@ void DefineQueryRewrite(
|
|||
* the correct relkind and removal of reltoastrelid/reltoastidxid of
|
||||
* the toast table we potentially removed above.
|
||||
*/
|
||||
classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(event_relid));// 在 pg_class 表中搜索指定 OID 的关系元组的副本
|
||||
classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(event_relid));
|
||||
if (!HeapTupleIsValid(classTup))
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT_REWRITE),
|
||||
|
|
@ -611,8 +580,6 @@ void DefineQueryRewrite(
|
|||
classForm = (Form_pg_class)GETSTRUCT(classTup);
|
||||
|
||||
nspid = classForm->relnamespace;
|
||||
|
||||
// 对关系属性进行修改,将其设置为视图的属性
|
||||
classForm->reltablespace = InvalidOid;
|
||||
classForm->relpages = 0;
|
||||
classForm->reltuples = 0;
|
||||
|
|
@ -637,11 +604,10 @@ void DefineQueryRewrite(
|
|||
replaces[Anum_pg_class_relfrozenxid64 - 1] = true;
|
||||
values[Anum_pg_class_relfrozenxid64 - 1] = TransactionIdGetDatum(InvalidTransactionId);
|
||||
|
||||
// 修改关系元组副本的内容,用于更新 pg_class 表
|
||||
nctup = (HeapTuple) tableam_tops_modify_tuple(classTup, RelationGetDescr(relationRelation), values, nulls, replaces);
|
||||
|
||||
simple_heap_update(relationRelation, &nctup->t_self, nctup);// 在 pg_class 表中进行简单的元组更新操作
|
||||
CatalogUpdateIndexes(relationRelation, nctup);// 更新索引以反映元组更新的更改
|
||||
simple_heap_update(relationRelation, &nctup->t_self, nctup);
|
||||
CatalogUpdateIndexes(relationRelation, nctup);
|
||||
|
||||
tableam_tops_free_tuple(nctup);
|
||||
tableam_tops_free_tuple(classTup);
|
||||
|
|
@ -651,14 +617,13 @@ void DefineQueryRewrite(
|
|||
RemovePgxcClass(event_relid);
|
||||
RemovePgxcSlice(event_relid);
|
||||
deleteDependencyRecordsFor(PgxcClassRelationId, event_relid, false);
|
||||
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {// 在协调器节点上执行 DROP TABLE 操作以删除关系
|
||||
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
|
||||
StringInfoData dropbuf;
|
||||
char* nspname = get_namespace_name(nspid);
|
||||
char* relname = get_rel_name(event_relid);
|
||||
const char* quoteNsp = quote_identifier(nspname);
|
||||
const char* quoteRel = quote_identifier(relname);
|
||||
|
||||
// 构造 DROP TABLE 命令字符串
|
||||
initStringInfo(&dropbuf);
|
||||
appendStringInfo(&dropbuf, "DROP TABLE %s.%s;", quoteNsp, quoteRel);
|
||||
ExecUtilityStmtOnNodes(dropbuf.data, NULL, false, false, EXEC_ON_DATANODES, false);
|
||||
|
|
@ -678,16 +643,16 @@ void DefineQueryRewrite(
|
|||
* isSelect tells which. (This is mostly used for choosing error messages,
|
||||
* but also we don't enforce column name matching for RETURNING.)
|
||||
*/
|
||||
static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isSelect)//函数用于验证目标列表是否与元组描述兼容,主要检查 SELECT 或 RETURNING 列表的条目是否与元组描述中的列匹配
|
||||
static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isSelect)
|
||||
{
|
||||
ListCell* tllist = NULL;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
foreach (tllist, targetList) {
|
||||
TargetEntry* tle = (TargetEntry*)lfirst(tllist);//获取目标条目的指针,并转换为 TargetEntry 类型
|
||||
TargetEntry* tle = (TargetEntry*)lfirst(tllist);
|
||||
int32 tletypmod;
|
||||
Form_pg_attribute attr;//用于存储目标条目关联的属性描述信息
|
||||
Form_pg_attribute attr;
|
||||
char* attname = NULL;
|
||||
|
||||
/* resjunk entries may be ignored */
|
||||
|
|
@ -716,12 +681,12 @@ static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isS
|
|||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot convert relation containing dropped columns to view")));
|
||||
|
||||
if (isSelect && strcmp(tle->resname, attname) != 0)//检查目标列表的条目名是否与元组描述的列名匹配(仅用于 SELECT 规则)
|
||||
if (isSelect && strcmp(tle->resname, attname) != 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("SELECT rule's target entry %d has different column name from \"%s\"", i, attname)));
|
||||
|
||||
if (attr->atttypid != exprType((Node*)tle->expr))//检查目标列表的表达式类型是否与列类型匹配
|
||||
if (attr->atttypid != exprType((Node*)tle->expr))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
isSelect ? errmsg("SELECT rule's target entry %d has different type from column \"%s\"", i, attname)
|
||||
|
|
@ -741,7 +706,7 @@ static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isS
|
|||
: errmsg("RETURNING list's entry %d has different size from column \"%s\"", i, attname)));
|
||||
}
|
||||
|
||||
if (i != resultDesc->natts)//检查目标列表的条目数量是否与元组描述的列数一致
|
||||
if (i != resultDesc->natts)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
isSelect ? errmsg("SELECT rule's target list has too few entries")
|
||||
|
|
@ -760,7 +725,7 @@ static void checkRuleResultList(List* targetList, TupleDesc resultDesc, bool isS
|
|||
* it's important to set these fields to match the rule owner. So we just set
|
||||
* them always.
|
||||
*/
|
||||
void setRuleCheckAsUser(Node* node, Oid userid)//函数递归地设置查询或表达式树中的 rtable 条目的 checkAsUser 字段为给定的用户 ID,用于在规则执行时模拟特定用户的权限
|
||||
void setRuleCheckAsUser(Node* node, Oid userid)
|
||||
{
|
||||
(void)setRuleCheckAsUser_walker(node, &userid);
|
||||
}
|
||||
|
|
@ -806,12 +771,12 @@ static void setRuleCheckAsUser_Query(Query* qry, Oid userid)
|
|||
/*
|
||||
* Change the firing semantics of an existing rule.
|
||||
*/
|
||||
void EnableDisableRule(Relation rel, const char* rulename, char fires_when)//函数用于启用或禁用规则的触发语义,可以更改规则的触发条件
|
||||
void EnableDisableRule(Relation rel, const char* rulename, char fires_when)
|
||||
{
|
||||
Relation pg_rewrite_desc;//用于存储 pg_rewrite 表的关系描述符
|
||||
Oid owningRel = RelationGetRelid(rel);//获得传入关系(表)的 Oid
|
||||
Oid eventRelationOid;//用于存储规则关联的事件关系的 Oid
|
||||
HeapTuple ruletup;//用于存储查询 pg_rewrite 表后获取的规则元组
|
||||
Relation pg_rewrite_desc;
|
||||
Oid owningRel = RelationGetRelid(rel);
|
||||
Oid eventRelationOid;
|
||||
HeapTuple ruletup;
|
||||
bool changed = false;
|
||||
|
||||
/*
|
||||
|
|
@ -863,14 +828,13 @@ void EnableDisableRule(Relation rel, const char* rulename, char fires_when)//函
|
|||
* This is unused code at the moment. Note that it lacks a permissions check.
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
void RenameRewriteRule(Oid owningRel, const char* oldName, const char* newName)//函数用于重命名已存在的重写规则
|
||||
void RenameRewriteRule(Oid owningRel, const char* oldName, const char* newName)
|
||||
{
|
||||
Relation pg_rewrite_desc;// 用于表示 pg_rewrite 表的关系描述符
|
||||
HeapTuple ruletup;// 用于存储查找到的重写规则元组
|
||||
Relation pg_rewrite_desc;
|
||||
HeapTuple ruletup;
|
||||
|
||||
pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock);
|
||||
|
||||
//在系统缓存中查找并复制指定规则的元组
|
||||
ruletup = SearchSysCacheCopy2(RULERELNAME, ObjectIdGetDatum(owningRel), PointerGetDatum(oldName));
|
||||
if (!HeapTupleIsValid(ruletup))
|
||||
ereport(ERROR,
|
||||
|
|
@ -883,7 +847,6 @@ void RenameRewriteRule(Oid owningRel, const char* oldName, const char* newName)/
|
|||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
errmsg("rule \"%s\" for relation \"%s\" already exists", newName, get_rel_name(owningRel))));
|
||||
|
||||
//更新规则元组的 rulename 字段为新名称
|
||||
(void)namestrcpy(&(((Form_pg_rewrite)GETSTRUCT(ruletup))->rulename), newName);
|
||||
|
||||
simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup);
|
||||
|
|
|
|||
|
|
@ -81,10 +81,10 @@ static Query* fireRIRrules(Query* parsetree, List* activeRIRs, bool forUpdatePus
|
|||
|
||||
#ifdef PGXC
|
||||
typedef struct pull_qual_vars_context {
|
||||
List* varlist;// 一个指向List结构的指针,用于存储变量列表
|
||||
int sublevels_up;// 表示查询所在的嵌套层级
|
||||
int resultRelation;// 表示查询的结果关系(表)
|
||||
bool noRepeat; // 一个布尔值,用于标识是否避免重复
|
||||
List* varlist;
|
||||
int sublevels_up;
|
||||
int resultRelation;
|
||||
bool noRepeat;
|
||||
} pull_qual_vars_context;
|
||||
static bool pull_qual_vars_walker(Node* node, pull_qual_vars_context* context);
|
||||
#endif
|
||||
|
|
@ -125,7 +125,7 @@ static bool pull_qual_vars_walker(Node* node, pull_qual_vars_context* context);
|
|||
* That approach had horrible performance unfortunately; in particular
|
||||
* construction of a nested join was O(N^2) in the nesting depth.)
|
||||
*/
|
||||
void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown)//在查询重写过程中获取必要的锁,以确保在查询处理过程中的数据访问的正确性和一致性
|
||||
void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown)
|
||||
{
|
||||
ListCell* l = NULL;
|
||||
int rt_index;
|
||||
|
|
@ -145,7 +145,7 @@ void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown)//在查询
|
|||
|
||||
++rt_index;
|
||||
switch (rte->rtekind) {
|
||||
case RTE_RELATION://根据是否为查询结果关系和是否需要更新锁等情况,选择适当的锁类型,并更新关系的元数据
|
||||
case RTE_RELATION:
|
||||
|
||||
/*
|
||||
* Grab the appropriate lock type for the relation, and do not
|
||||
|
|
@ -177,7 +177,7 @@ void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown)//在查询
|
|||
heap_close(rel, NoLock);
|
||||
break;
|
||||
|
||||
case RTE_JOIN://检查别名变量列表,如果有列被删除,则将其替换为 NULL 常量
|
||||
case RTE_JOIN:
|
||||
|
||||
/*
|
||||
* Scan the join's alias var list to see if any columns have
|
||||
|
|
@ -230,7 +230,7 @@ void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown)//在查询
|
|||
rte->joinaliasvars = newaliasvars;
|
||||
break;
|
||||
|
||||
case RTE_SUBQUERY://递归地调用 AcquireRewriteLocks 处理代表的子查询
|
||||
case RTE_SUBQUERY:
|
||||
|
||||
/*
|
||||
* The subquery RTE itself is all right, but we have to
|
||||
|
|
@ -247,7 +247,7 @@ void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown)//在查询
|
|||
}
|
||||
|
||||
/* Recurse into subqueries in WITH */
|
||||
foreach (l, parsetree->cteList) {//处理 WITH 子句中的子查询时,递归地对每个子查询调用 AcquireRewriteLocks
|
||||
foreach (l, parsetree->cteList) {
|
||||
CommonTableExpr* cte = (CommonTableExpr*)lfirst(l);
|
||||
|
||||
AcquireRewriteLocks((Query*)cte->ctequery, false);
|
||||
|
|
@ -257,18 +257,18 @@ void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown)//在查询
|
|||
* Recurse into sublink subqueries, too. But we already did the ones in
|
||||
* the rtable and cteList.
|
||||
*/
|
||||
if (parsetree->hasSubLinks)//对于包含子链接的情况,使用 query_tree_walker 函数递归地处理子链接中的子查询
|
||||
if (parsetree->hasSubLinks)
|
||||
(void)query_tree_walker(parsetree, (bool (*)())acquireLocksOnSubLinks, NULL, QTW_IGNORE_RC_SUBQUERIES);
|
||||
}
|
||||
|
||||
/*
|
||||
* Walker to find sublink subqueries for AcquireRewriteLocks
|
||||
*/
|
||||
static bool acquireLocksOnSubLinks(Node* node, void* context)//函数用于在查询重写过程中获取子查询中的锁
|
||||
static bool acquireLocksOnSubLinks(Node* node, void* context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, SubLink)) {// 如果当前节点是 SubLink 类型,则处理子查询
|
||||
if (IsA(node, SubLink)) {
|
||||
SubLink* sub = (SubLink*)node;
|
||||
|
||||
/* Do what we came for */
|
||||
|
|
@ -300,18 +300,13 @@ static bool acquireLocksOnSubLinks(Node* node, void* context)//函数用于在
|
|||
* Return value:
|
||||
* rewritten form of rule_action
|
||||
*/
|
||||
static Query* rewriteRuleAction(//使用适当的限定词(从触发查询中获取)来重写规则操作
|
||||
Query* parsetree,//原始查询
|
||||
Query* rule_action, //规则的一个操作(查询)
|
||||
Node* rule_qual, // 规则的 WHERE 条件,如果无条件则为 NULL
|
||||
int rt_index, //原始查询中结果关系的 RT 索引
|
||||
CmdType event, // 规则事件的类型
|
||||
bool* returning_flag)//如果重写了规则动作中的 RETURNING 子句,则设置为 TRUE
|
||||
static Query* rewriteRuleAction(
|
||||
Query* parsetree, Query* rule_action, Node* rule_qual, int rt_index, CmdType event, bool* returning_flag)
|
||||
{
|
||||
int current_varno, new_varno;// 当前查询的关系变量序号和用于重写的新关系变量序号
|
||||
int rt_length;// 原始查询的关系表达式长度
|
||||
Query* sub_action = NULL; // 子查询的规则动作
|
||||
Query** sub_action_ptr;// 指向子查询规则动作的指针
|
||||
int current_varno, new_varno;
|
||||
int rt_length;
|
||||
Query* sub_action = NULL;
|
||||
Query** sub_action_ptr;
|
||||
|
||||
/*
|
||||
* Make modifiable copies of rule action and qual (what we're passed are
|
||||
|
|
@ -340,7 +335,6 @@ static Query* rewriteRuleAction(//使用适当的限定词(从触发查询中
|
|||
*/
|
||||
sub_action = getInsertSelectQuery(rule_action, &sub_action_ptr);
|
||||
|
||||
|
||||
OffsetVarNodes((Node*)sub_action, rt_length, 0);
|
||||
OffsetVarNodes(rule_qual, rt_length, 0);
|
||||
/* but references to OLD should point at original rt_index */
|
||||
|
|
@ -385,21 +379,18 @@ static Query* rewriteRuleAction(//使用适当的限定词(从触发查询中
|
|||
ListCell* lc = NULL;
|
||||
|
||||
foreach (lc, parsetree->rtable) {
|
||||
RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc);// // 获取当前迭代的 RangeTblEntry(关系表达式条目)
|
||||
RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc);
|
||||
|
||||
switch (rte->rtekind) {
|
||||
case RTE_RELATION:
|
||||
sub_action->hasSubLinks = checkExprHasSubLink((Node*)rte->tablesample)
|
||||
|| checkExprHasSubLink((Node*)rte->timecapsule);
|
||||
// 检查 tablesample 和 timecapsule 子表达式是否包含子链接
|
||||
break;
|
||||
case RTE_FUNCTION:
|
||||
sub_action->hasSubLinks = checkExprHasSubLink(rte->funcexpr);
|
||||
// 检查 funcexpr 子表达式是否包含子链接
|
||||
break;
|
||||
case RTE_VALUES:
|
||||
sub_action->hasSubLinks = checkExprHasSubLink((Node*)rte->values_lists);
|
||||
// 检查 values_lists 子表达式是否包含子链接
|
||||
break;
|
||||
default:
|
||||
/* other RTE types don't contain bare expressions */
|
||||
|
|
@ -543,9 +534,8 @@ static Query* rewriteRuleAction(//使用适当的限定词(从触发查询中
|
|||
if (*returning_flag)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot have RETURNING lists in multiple rules")));
|
||||
// // 报告错误:不支持在多个规则中使用 RETURNING 子句
|
||||
*returning_flag = true;// 设置返回标志为真,表示规则动作已经有 RETURNING 子句
|
||||
rule_action->returningList = (List*)ResolveNew((Node*)parsetree->returningList, // 解析并替换规则动作的 RETURNING 子句,保留原始查询的语义
|
||||
*returning_flag = true;
|
||||
rule_action->returningList = (List*)ResolveNew((Node*)parsetree->returningList,
|
||||
parsetree->resultRelation,
|
||||
0,
|
||||
rt_fetch(parsetree->resultRelation, parsetree->rtable),
|
||||
|
|
|
|||
|
|
@ -50,53 +50,59 @@ static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
|
|||
|
||||
/*
|
||||
* checkExprHasAggs -
|
||||
* 检查表达式是否包含当前Query级别的聚合函数调用。
|
||||
* Check if an expression contains an aggregate function call of the
|
||||
* current query level.
|
||||
*/
|
||||
bool checkExprHasAggs(Node* node)
|
||||
{
|
||||
return contain_aggs_of_level(node, 0);//调用contain_aggs_of_level函数并将级别设为0
|
||||
return contain_aggs_of_level(node, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* contain_aggs_of_level -
|
||||
* 检查表达式是否包含指定Query级别的聚合函数调用。
|
||||
* Check if an expression contains an aggregate function call of a
|
||||
* specified query level.
|
||||
*
|
||||
* 这个例程的目的是检测是否存在属于给定Query级别的聚合。属于子查询或外部查询
|
||||
* 的聚合不会产生真结果。我们必须递归到子查询中,以检测逻辑上属于指定查询级别的外部引用聚合。
|
||||
* The objective of this routine is to detect whether there are aggregates
|
||||
* belonging to the given query level. Aggregates belonging to subqueries
|
||||
* or outer queries do NOT cause a true result. We must recurse into
|
||||
* subqueries to detect outer-reference aggregates that logically belong to
|
||||
* the specified query level.
|
||||
*/
|
||||
bool contain_aggs_of_level(Node* node, int levelsup)
|
||||
{
|
||||
contain_aggs_of_level_context context;//定义记录级别的结构体
|
||||
contain_aggs_of_level_context context;
|
||||
|
||||
context.sublevels_up = levelsup;//传入希望查询的聚合的级别
|
||||
context.sublevels_up = levelsup;
|
||||
|
||||
/*
|
||||
* 必须准备好从Query或裸表达式树开始;如果它是一个Query,我们不想增加sublevels_up
|
||||
* Must be prepared to start with a Query or a bare expression tree; if
|
||||
* it's a Query, we don't want to increment sublevels_up.
|
||||
*/
|
||||
return query_or_expression_tree_walker(node, (bool (*)())contain_aggs_of_level_walker, (void*)&context, 0);
|
||||
}
|
||||
|
||||
static bool contain_aggs_of_level_walker(Node* node, contain_aggs_of_level_context* context)//传入节点及其级别
|
||||
static bool contain_aggs_of_level_walker(Node* node, contain_aggs_of_level_context* context)
|
||||
{
|
||||
if (node == NULL)//为空
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Aggref)) {//节点为Aggref节点
|
||||
if (((Aggref*)node)->agglevelsup == (Index)context->sublevels_up)//级别相等
|
||||
return true; /* 中止树遍历并返回true */
|
||||
/* 否则就通过检验论证 */
|
||||
if (IsA(node, Aggref)) {
|
||||
if (((Aggref*)node)->agglevelsup == (Index)context->sublevels_up)
|
||||
return true; /* abort the tree traversal and return true */
|
||||
/* else fall through to examine argument */
|
||||
}
|
||||
if (IsA(node, GroupingFunc)) {//节点为GroupingFunc
|
||||
if (IsA(node, GroupingFunc)) {
|
||||
if (((GroupingFunc*)node)->agglevelsup == (Index)context->sublevels_up)
|
||||
return true;
|
||||
/* 否则就通过检验论证 */
|
||||
/* else fall through to examine argument */
|
||||
}
|
||||
if (IsA(node, Query)) {//节点为Query
|
||||
/* 递归为子选择 */
|
||||
if (IsA(node, Query)) {
|
||||
/* Recurse into subselects */
|
||||
bool result = false;
|
||||
|
||||
context->sublevels_up++;
|
||||
result = query_tree_walker((Query*)node, (bool (*)())contain_aggs_of_level_walker, (void*)context, 0);
|
||||
context->sublevels_up--;//还原sublevels_up
|
||||
context->sublevels_up--;
|
||||
return result;
|
||||
}
|
||||
return expression_tree_walker(node, (bool (*)())contain_aggs_of_level_walker, (void*)context);
|
||||
|
|
@ -104,13 +110,14 @@ static bool contain_aggs_of_level_walker(Node* node, contain_aggs_of_level_conte
|
|||
|
||||
/*
|
||||
* contain_aggs_of_level_or_above -
|
||||
* 检查表达式是否包含指定Query级别或更高级别的聚合函数调用。
|
||||
* Check if an expression contains an aggregate function call of a
|
||||
* specified query level or level above.
|
||||
*
|
||||
* 如果找到任何这样的聚合函数,返回ture。
|
||||
* Return ture if any such aggregate function found.
|
||||
*/
|
||||
bool contain_aggs_of_level_or_above(Node* node, int levelsup)//未定义记录级别的结构体
|
||||
bool contain_aggs_of_level_or_above(Node* node, int levelsup)
|
||||
{
|
||||
int sublevels_up = levelsup;//传入希望查询的聚合的级别
|
||||
int sublevels_up = levelsup;
|
||||
|
||||
return query_or_expression_tree_walker(
|
||||
node, (bool (*)())contain_aggs_of_level_or_above_walker, (void*)&sublevels_up, 0);
|
||||
|
|
@ -118,27 +125,27 @@ bool contain_aggs_of_level_or_above(Node* node, int levelsup)//未定义记录
|
|||
|
||||
static bool contain_aggs_of_level_or_above_walker(Node* node, int* sublevels_up)
|
||||
{
|
||||
if (node == NULL)//为空
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Aggref)) {//节点为Aggref节点
|
||||
if (((Aggref*)node)->agglevelsup >= (Index)*sublevels_up) {//级别相等或大于
|
||||
if (IsA(node, Aggref)) {
|
||||
if (((Aggref*)node)->agglevelsup >= (Index)*sublevels_up) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (IsA(node, GroupingFunc)) {//节点为GroupingFunc
|
||||
if (((GroupingFunc*)node)->agglevelsup >= (Index)*sublevels_up) {//级别相等或大于
|
||||
if (IsA(node, GroupingFunc)) {
|
||||
if (((GroupingFunc*)node)->agglevelsup >= (Index)*sublevels_up) {
|
||||
return true;
|
||||
}
|
||||
/* 否则就通过检验论证 */
|
||||
/* else fall through to examine argument */
|
||||
}
|
||||
if (IsA(node, Query)) {//节点为Query
|
||||
/* 递归为子选择 */
|
||||
if (IsA(node, Query)) {
|
||||
/* Recurse into subselects */
|
||||
bool result = false;
|
||||
|
||||
(*sublevels_up)++;
|
||||
result =
|
||||
query_tree_walker((Query*)node, (bool (*)())contain_aggs_of_level_or_above_walker, (void*)sublevels_up, 0);
|
||||
(*sublevels_up)--;//还原sublevels_up
|
||||
(*sublevels_up)--;
|
||||
return result;
|
||||
}
|
||||
return expression_tree_walker(node, (bool (*)())contain_aggs_of_level_or_above_walker, (void*)sublevels_up);
|
||||
|
|
@ -146,50 +153,53 @@ static bool contain_aggs_of_level_or_above_walker(Node* node, int* sublevels_up)
|
|||
|
||||
/*
|
||||
* locate_agg_of_level -
|
||||
* 查找指定Query级别的任何聚合的解析位置。
|
||||
* Find the parse location of any aggregate of the specified query level.
|
||||
*
|
||||
* 如果查询树中没有这样的聚合,或者它们都有未知的解析位置,则返回-1。
|
||||
* (前一种情况可能是调用者错误,但我们不必费心将其与后一种情况区分开来。)
|
||||
* Returns -1 if no such agg is in the querytree, or if they all have
|
||||
* unknown parse location. (The former case is probably caller error,
|
||||
* but we don't bother to distinguish it from the latter case.)
|
||||
*
|
||||
* Note: 将此功能合并到包含级别的聚合中似乎是合适的,但这会使该函数的API复杂化。
|
||||
* 目前,该函数的唯一用途是错误报告,因此剃除周期可能不是很重要。
|
||||
* Note: it might seem appropriate to merge this functionality into
|
||||
* contain_aggs_of_level, but that would complicate that function's API.
|
||||
* Currently, the only uses of this function are for error reporting,
|
||||
* and so shaving cycles probably isn't very important.
|
||||
*/
|
||||
int locate_agg_of_level(Node* node, int levelsup)//传入希望查找的聚合级别
|
||||
int locate_agg_of_level(Node* node, int levelsup)
|
||||
{
|
||||
locate_agg_of_level_context context;//定义包含位置的结构体
|
||||
locate_agg_of_level_context context;
|
||||
|
||||
context.agg_location = -1; /* 以防我们一无所获 */
|
||||
context.agg_location = -1; /* in case we find nothing */
|
||||
context.sublevels_up = levelsup;
|
||||
|
||||
/*
|
||||
* 必须准备以查询或裸表达式树开始; 如果它是一个Query,我们不希望增加子级别。
|
||||
* Must be prepared to start with a Query or a bare expression tree; if
|
||||
* it's a Query, we don't want to increment sublevels_up.
|
||||
*/
|
||||
(void)query_or_expression_tree_walker(node, (bool (*)())locate_agg_of_level_walker, (void*)&context, 0);
|
||||
//调用query_or_expression_tree_walker
|
||||
|
||||
return context.agg_location;
|
||||
}
|
||||
|
||||
static bool locate_agg_of_level_walker(Node* node, locate_agg_of_level_context* context)
|
||||
{
|
||||
if (node == NULL)//为空
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Aggref)) {
|
||||
if (((Aggref*)node)->agglevelsup == (Index)(context->sublevels_up) && ((Aggref*)node)->location >= 0) {//并且查找到的地址不为-1
|
||||
if (((Aggref*)node)->agglevelsup == (Index)(context->sublevels_up) && ((Aggref*)node)->location >= 0) {
|
||||
context->agg_location = ((Aggref*)node)->location;
|
||||
return true; /* 中止树遍历并返回true */
|
||||
return true; /* abort the tree traversal and return true */
|
||||
}
|
||||
/* 否则就通过检验论证 */
|
||||
/* else fall through to examine argument */
|
||||
}
|
||||
if (IsA(node, GroupingFunc)) {
|
||||
if (((GroupingFunc*)node)->agglevelsup == (Index)context->sublevels_up &&
|
||||
((GroupingFunc*)node)->location >= 0) {//并且查找到的地址不为-1
|
||||
((GroupingFunc*)node)->location >= 0) {
|
||||
context->agg_location = ((GroupingFunc*)node)->location;
|
||||
return true; /* 中止树遍历并返回true */
|
||||
return true; /* abort the tree traversal and return true */
|
||||
}
|
||||
}
|
||||
if (IsA(node, Query)) {
|
||||
/* 递归为子选择 */
|
||||
/* Recurse into subselects */
|
||||
bool result = false;
|
||||
|
||||
context->sublevels_up++;
|
||||
|
|
|
|||
|
|
@ -35,29 +35,28 @@
|
|||
/*
|
||||
* Guts of rule deletion.
|
||||
*/
|
||||
void RemoveRewriteRuleById(Oid ruleOid)//函数主要作用为从pg_rewrite表中删除指定的规则元组,并发出通知,确保其他后端在需要时更新相关的relcache条目,并在处理过程中使用适当的锁,以避免并发冲突
|
||||
void RemoveRewriteRuleById(Oid ruleOid)
|
||||
{
|
||||
Relation RewriteRelation;// pg_rewrite表的关系对象
|
||||
ScanKeyData skey[1];// 用于扫描pg_rewrite表的扫描键
|
||||
SysScanDesc rcscan;// 用于pg_rewrite表的扫描描述符
|
||||
Relation event_relation; // 触发规则的事件对象的关系对象
|
||||
HeapTuple tuple;// 用于保存查询到的pg_rewrite表的元组
|
||||
Oid eventRelationOid;// 触发规则的事件对象的OID
|
||||
Relation RewriteRelation;
|
||||
ScanKeyData skey[1];
|
||||
SysScanDesc rcscan;
|
||||
Relation event_relation;
|
||||
HeapTuple tuple;
|
||||
Oid eventRelationOid;
|
||||
|
||||
/*
|
||||
* Open the pg_rewrite relation.
|
||||
*/
|
||||
RewriteRelation = heap_open(RewriteRelationId, RowExclusiveLock);//打开pg_rewrite表,使用RowExclusiveLock锁,防止其他事务同时修改表
|
||||
RewriteRelation = heap_open(RewriteRelationId, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* Find the tuple for the target rule.
|
||||
*/
|
||||
ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(ruleOid));
|
||||
//创建一个扫描键,用于根据ruleOid在pg_rewrite表中查找匹配的规则元组
|
||||
|
||||
rcscan = systable_beginscan(RewriteRelation, RewriteOidIndexId, true, NULL, 1, skey);//开始使用扫描键在pg_rewrite表中进行扫描
|
||||
rcscan = systable_beginscan(RewriteRelation, RewriteOidIndexId, true, NULL, 1, skey);
|
||||
|
||||
tuple = systable_getnext(rcscan);//获取匹配的规则元组,如果找不到,则报错
|
||||
tuple = systable_getnext(rcscan);
|
||||
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("could not find tuple for rule %u", ruleOid)));
|
||||
|
|
@ -67,24 +66,24 @@ void RemoveRewriteRuleById(Oid ruleOid)//函数主要作用为从pg_rewrite表
|
|||
* going on that might depend on this rule. (Note: a weaker lock would
|
||||
* suffice if it's not an ON SELECT rule.)
|
||||
*/
|
||||
eventRelationOid = ((Form_pg_rewrite)GETSTRUCT(tuple))->ev_class;//获取触发规则的事件对象的OID
|
||||
event_relation = heap_open(eventRelationOid, AccessExclusiveLock);//打开触发规则的事件对象,并使用AccessExclusiveLock锁,以防止其他查询依赖于该规则
|
||||
eventRelationOid = ((Form_pg_rewrite)GETSTRUCT(tuple))->ev_class;
|
||||
event_relation = heap_open(eventRelationOid, AccessExclusiveLock);
|
||||
|
||||
/*
|
||||
* Now delete the pg_rewrite tuple for the rule
|
||||
*/
|
||||
simple_heap_delete(RewriteRelation, &tuple->t_self);//在pg_rewrite表中删除规则元组
|
||||
simple_heap_delete(RewriteRelation, &tuple->t_self);
|
||||
|
||||
systable_endscan(rcscan);//结束对pg_rewrite表的扫描
|
||||
systable_endscan(rcscan);
|
||||
|
||||
heap_close(RewriteRelation, RowExclusiveLock);//关闭pg_rewrite表
|
||||
heap_close(RewriteRelation, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* Issue shared-inval notice to force all backends (including me!) to
|
||||
* update relcache entries with the new rule set.
|
||||
*/
|
||||
CacheInvalidateRelcache(event_relation);//发出共享失效通知,强制所有后端(包括当前进程)更新relcache条目,以使用新的规则集
|
||||
CacheInvalidateRelcache(event_relation);
|
||||
|
||||
/* Close rel, but keep lock till commit... */
|
||||
heap_close(event_relation, NoLock);//关闭触发规则的事件对象,但保持锁,直到事务提交
|
||||
heap_close(event_relation, NoLock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,19 +56,26 @@ static void AddRlsUsingQuals(
|
|||
* @param (in) roleid: Role Oid
|
||||
* @return: This Row-Level-Security policy apply to role or not.
|
||||
*/
|
||||
static bool CheckRoleForRlsPolicy(const RlsPolicy* policy, Oid roleid)//policy表示行级别安全策略,roleid表示用户角色的标识符
|
||||
static bool CheckRoleForRlsPolicy(const RlsPolicy* policy, Oid roleid)
|
||||
{
|
||||
Oid* roles = (Oid*)ARR_DATA_PTR(policy->roles);//roles用来访问传入行级别安全策略policy中的有权限的每个角色标识符
|
||||
int roleNums = ARR_DIMS(policy->roles)[0];//roleNums用于存储行级安全策略中包含的角色数量
|
||||
//ACL_ID_PUBLIC为所有用户均具备的基础权限
|
||||
if (roles[0] == ACL_ID_PUBLIC) {//优先判断,提高时间的效率
|
||||
Oid* roles = (Oid*)ARR_DATA_PTR(policy->roles);
|
||||
int roleNums = ARR_DIMS(policy->roles)[0];
|
||||
|
||||
/*
|
||||
* ACL_ID_PUBLIC means this policy applies to all users,
|
||||
* and ACL_ID_PUBLIC is the only applied user for this policy.
|
||||
*/
|
||||
if (roles[0] == ACL_ID_PUBLIC) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < roleNums; i++) {
|
||||
if (has_privs_of_role(roleid, roles[i])) {//遍历roles中所有拥有权限的角色是否为roleid
|
||||
/* Check this user has the privilege for this policy */
|
||||
if (has_privs_of_role(roleid, roles[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* This policy does not apply to current user */
|
||||
return false;
|
||||
}
|
||||
|
|
@ -88,23 +95,18 @@ static bool CheckRoleForRlsPolicy(const RlsPolicy* policy, Oid roleid)//policy
|
|||
*/
|
||||
static void PullRlsPoliciesForRel(CmdType cmd, Oid roleid, const List* relRlsPolicies, List** permissivePolicies,
|
||||
List** restrictivePolicies, bool& hasSubLink)
|
||||
//cmd 为SQL查询命令的类型
|
||||
//roleid 为当前用户的角色
|
||||
//relRlsPolicies 为行级安全策略表,其中存储多个 RlsPolicy 型行级安全策略
|
||||
//permissivePolicies 存储传递容许型策略,restrictivePolicies 存储传递限制型策略
|
||||
//hasSubLink 标记是否含有子链接
|
||||
{
|
||||
ListCell* item = NULL;//用于遍历 relRlsPolicies 的临时变量
|
||||
RlsPolicy* policy = NULL;//用于存储 item 的值的临时变量
|
||||
bool roleForPolicy = false;//用于表示当前用户是否适用于该策略
|
||||
bool cmdMatch = false;//用于表示当前策略是否与给定的 SQL 查询命令类型匹配
|
||||
ListCell* item = NULL;
|
||||
RlsPolicy* policy = NULL;
|
||||
bool roleForPolicy = false;
|
||||
bool cmdMatch = false;
|
||||
foreach (item, relRlsPolicies) {
|
||||
policy = (RlsPolicy*)lfirst(item);
|
||||
/* Check this R.L.S policy affect this user, if not just skip */
|
||||
roleForPolicy = CheckRoleForRlsPolicy(policy, roleid);//判断该用户是否适用于该策略
|
||||
roleForPolicy = CheckRoleForRlsPolicy(policy, roleid);
|
||||
if (roleForPolicy) {
|
||||
cmdMatch = false;
|
||||
switch (cmd) {//依据不同的SQL查询命令cmd与当前策略policy相比较,判断是否相匹配
|
||||
switch (cmd) {
|
||||
case CMD_SELECT:
|
||||
if ((policy->cmdName == ACL_SELECT_CHR) || (policy->cmdName == RLS_CMD_ALL_CHR))
|
||||
cmdMatch = true;
|
||||
|
|
@ -121,7 +123,7 @@ static void PullRlsPoliciesForRel(CmdType cmd, Oid roleid, const List* relRlsPol
|
|||
if ((policy->cmdName == ACL_UPDATE_CHR) || (policy->cmdName == RLS_CMD_ALL_CHR))
|
||||
cmdMatch = true;
|
||||
break;
|
||||
default://不支持的命令类型,抛出错误
|
||||
default:
|
||||
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("unsupported command type: %d.", cmd)));
|
||||
break;
|
||||
}
|
||||
|
|
@ -165,10 +167,10 @@ static void AddRlsUsingQuals(
|
|||
foreach (item, restrictivePolicies) {
|
||||
policy = (RlsPolicy*)lfirst(item);
|
||||
|
||||
if (policy->usingExpr != NULL) {//如果策略中包含 USING 子句
|
||||
rlsExpr = (Expr*)copyObject(policy->usingExpr);//rlsExpr暂时存储 USING 子句
|
||||
ChangeVarNodes((Node*)rlsExpr, 1, rtIndex, 0);//修改 rlsExpr 中的变量节点,使其适应当前查询的表索引
|
||||
*rlsUsingQuals = list_append_unique(*rlsUsingQuals, rlsExpr);//将 rlsExpr 添加到 rlsUsingQuals 列表中
|
||||
if (policy->usingExpr != NULL) {
|
||||
rlsExpr = (Expr*)copyObject(policy->usingExpr);
|
||||
ChangeVarNodes((Node*)rlsExpr, 1, rtIndex, 0);
|
||||
*rlsUsingQuals = list_append_unique(*rlsUsingQuals, rlsExpr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,7 +178,7 @@ static void AddRlsUsingQuals(
|
|||
foreach (item, permissivePolicies) {
|
||||
policy = (RlsPolicy*)lfirst(item);
|
||||
|
||||
if (policy->usingExpr != NULL) {//包含USING子句则放入‘permissiveQuals’列表
|
||||
if (policy->usingExpr != NULL) {
|
||||
permissiveQuals = lappend(permissiveQuals, copyObject(policy->usingExpr));
|
||||
}
|
||||
}
|
||||
|
|
@ -187,12 +189,10 @@ static void AddRlsUsingQuals(
|
|||
* openGauss (PG will generate one-time False filter when no permissive
|
||||
* policies exist).
|
||||
*/
|
||||
|
||||
//根据 permissiveQuals 列表中表达式的数量,构造一个合并所有容许型策略的 USING 子句的表达式 'rlsExpr'
|
||||
rlsExpr = NULL;
|
||||
if (list_length(permissiveQuals) == 1) {//只含1个表达式则直接使用
|
||||
if (list_length(permissiveQuals) == 1) {
|
||||
rlsExpr = (Expr*)linitial(permissiveQuals);
|
||||
} else if (list_length(permissiveQuals) > 1) {//大于1个使用 makeBoolExpr 函数生成一个使用 OR 连接的表达式
|
||||
} else if (list_length(permissiveQuals) > 1) {
|
||||
rlsExpr = makeBoolExpr(OR_EXPR, permissiveQuals, -1);
|
||||
}
|
||||
ChangeVarNodes((Node*)rlsExpr, 1, rtIndex, 0);
|
||||
|
|
@ -222,10 +222,10 @@ void GetRlsPolicies(const Query* query, const RangeTblEntry* rte, const Relation
|
|||
/* Check whether enabled Row-Level-Security for this relation */
|
||||
EnableRlsFeature rlsStatus = CheckEnableRlsPolicies(relation, roleid);
|
||||
/* relation did not enable row level security */
|
||||
if (rlsStatus == RLS_DISABLED) { // 关系的 RLS 已禁用,因此无需应用安全检查
|
||||
if (rlsStatus == RLS_DISABLED) {
|
||||
hasRowSecurity = false;
|
||||
return;
|
||||
} else if (rlsStatus == RLS_DEPEND) {// 由于存在依赖策略,关系启用了 RLS
|
||||
} else if (rlsStatus == RLS_DEPEND) {
|
||||
/*
|
||||
* relation enable row level security, but current user can bypass it.
|
||||
* hasRowSecurity is marked as true to force a re-plan when the environment
|
||||
|
|
@ -249,7 +249,6 @@ void GetRlsPolicies(const Query* query, const RangeTblEntry* rte, const Relation
|
|||
* policies and t2's SELECT policies.
|
||||
*/
|
||||
CmdType cmdType = (rtIndex == query->resultRelation) ? query->commandType : CMD_SELECT;
|
||||
// 确定正在执行的 SQL 命令类型(INSERT、UPDATE、DELETE 或 SELECT)
|
||||
List* rlsPermissivePolicies = NULL;
|
||||
List* rlsRestrictivePolicies = NULL;
|
||||
|
||||
|
|
@ -282,8 +281,6 @@ void GetRlsPolicies(const Query* query, const RangeTblEntry* rte, const Relation
|
|||
hasSubLink);
|
||||
|
||||
AddRlsUsingQuals(CMD_UPDATE, rtIndex, updateMermissivePolicies, updateRestrictivePolicies, rlsQuals);
|
||||
//通过调用 AddRlsUsingQuals 函数将获取的 RLS 策略转换为适当的条件,并将这些条件添加到查询的 rlsQuals 列表中
|
||||
//这些条件将在执行查询时应用于 WHERE 子句,从而强制执行行级安全策略
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -36,8 +36,6 @@
|
|||
bool IsDefinedRewriteRule(Oid owningRel, const char* ruleName)
|
||||
{
|
||||
return SearchSysCacheExists2(RULERELNAME, ObjectIdGetDatum(owningRel), PointerGetDatum(ruleName));
|
||||
//SearchSysCacheExists2是一个系统缓存搜索函数,用于检查是否存在满足指定条件的元组
|
||||
//RULERELNAME参数指定按照规则名进行检索
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -54,8 +52,6 @@ bool IsDefinedRewriteRule(Oid owningRel, const char* ruleName)
|
|||
* row.
|
||||
*/
|
||||
void SetRelationRuleStatus(Oid relationId, bool relHasRules, bool relIsBecomingView)
|
||||
//该函数用于设置给定表(relationId)的规则状态,即是否具有规则(relHasRules),以及是否将该表转换为视图(relIsBecomingView)
|
||||
//并且会在修改关系的规则状态时发送失效消息以更新缓存
|
||||
{
|
||||
Relation relationRelation;
|
||||
HeapTuple tuple;
|
||||
|
|
@ -65,7 +61,6 @@ void SetRelationRuleStatus(Oid relationId, bool relHasRules, bool relIsBecomingV
|
|||
* Find the tuple to update in pg_class, using syscache for the lookup.
|
||||
*/
|
||||
relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
//通过打开relationRelation(关系的系统目录表)来获取表的元组
|
||||
tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
ereport(ERROR,
|
||||
|
|
@ -75,9 +70,7 @@ void SetRelationRuleStatus(Oid relationId, bool relHasRules, bool relIsBecomingV
|
|||
classForm = (Form_pg_class)GETSTRUCT(tuple);
|
||||
|
||||
if (classForm->relhasrules != relHasRules || (relIsBecomingView && classForm->relkind != RELKIND_VIEW
|
||||
&& classForm->relkind != RELKIND_CONTQUERY))
|
||||
//根据传入的参数更新表的relhasrules和relkind字段
|
||||
{
|
||||
&& classForm->relkind != RELKIND_CONTQUERY)) {
|
||||
/* Do the update */
|
||||
classForm->relhasrules = relHasRules;
|
||||
if (relIsBecomingView)
|
||||
|
|
@ -86,10 +79,10 @@ void SetRelationRuleStatus(Oid relationId, bool relHasRules, bool relIsBecomingV
|
|||
simple_heap_update(relationRelation, &tuple->t_self, tuple);
|
||||
|
||||
/* Keep the catalog indexes up to date */
|
||||
CatalogUpdateIndexes(relationRelation, tuple);//通过CatalogUpdateIndexes更新系统目录索引
|
||||
CatalogUpdateIndexes(relationRelation, tuple);
|
||||
} else {
|
||||
/* no need to change tuple, but force relcache rebuild anyway */
|
||||
CacheInvalidateRelcacheByTuple(tuple);//通过CacheInvalidateRelcacheByTuple更新缓存
|
||||
CacheInvalidateRelcacheByTuple(tuple);
|
||||
}
|
||||
|
||||
tableam_tops_free_tuple(tuple);
|
||||
|
|
@ -103,14 +96,12 @@ void SetRelationRuleStatus(Oid relationId, bool relHasRules, bool relIsBecomingV
|
|||
* true, just return InvalidOid.
|
||||
*/
|
||||
Oid get_rewrite_oid(Oid relid, const char* rulename, bool missing_ok)
|
||||
//用于获取给定表(relid)上指定规则(rulename)的OID(对象标识符)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
Oid ruleoid;
|
||||
|
||||
/* Find the rule's pg_rewrite tuple, get its OID */
|
||||
tuple = SearchSysCache2(RULERELNAME, ObjectIdGetDatum(relid), PointerGetDatum(rulename));
|
||||
//使用SearchSysCache2来搜索符合规则名和表ID条件的规则元组,然后从元组中获取OID并返回
|
||||
if (!HeapTupleIsValid(tuple)) {
|
||||
if (missing_ok)
|
||||
return InvalidOid;
|
||||
|
|
@ -125,7 +116,6 @@ Oid get_rewrite_oid(Oid relid, const char* rulename, bool missing_ok)
|
|||
}
|
||||
|
||||
char* get_rewrite_rulename(Oid ruleid, bool missing_ok)
|
||||
//用于获取给定规则OID(ruleid)对应的规则名
|
||||
{
|
||||
ScanKeyData entry;
|
||||
SysScanDesc scan;
|
||||
|
|
@ -145,7 +135,7 @@ char* get_rewrite_rulename(Oid ruleid, bool missing_ok)
|
|||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("rule \"%u\" does not exist", ruleid)));
|
||||
}
|
||||
Form_pg_rewrite pg_rewrite = (Form_pg_rewrite)GETSTRUCT(rewrite_tup);//通过在pg_rewrite表上扫描匹配规则OID的元组来获取规则名
|
||||
Form_pg_rewrite pg_rewrite = (Form_pg_rewrite)GETSTRUCT(rewrite_tup);
|
||||
rulename = (char*)palloc0(NAMEDATALEN);
|
||||
rc = strncpy_s(rulename, NAMEDATALEN, NameStr(pg_rewrite->rulename), NAMEDATALEN - 1);
|
||||
securec_check_c(rc, "\0", "\0");
|
||||
|
|
@ -161,7 +151,6 @@ char* get_rewrite_rulename(Oid ruleid, bool missing_ok)
|
|||
* ev_type is CmdType, transfer it to char beacuse it is char in system catalog pg_rewrite
|
||||
*/
|
||||
bool rel_has_rule(Oid relid, char ev_type)
|
||||
//用于检查给定表(relid)上是否存在指定事件类型(ev_type)的规则
|
||||
{
|
||||
bool has_rule = false;
|
||||
ScanKeyData entry;
|
||||
|
|
@ -171,7 +160,6 @@ bool rel_has_rule(Oid relid, char ev_type)
|
|||
ScanKeyInit(&entry, Anum_pg_rewrite_ev_class, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid));
|
||||
scan = systable_beginscan(rewrite_rel, RewriteRelRulenameIndexId, true, NULL, 1, &entry);
|
||||
while (HeapTupleIsValid((rewrite_tup = systable_getnext(scan)))) {
|
||||
//在pg_rewrite表上进行扫描,查找满足表ID和事件类型的规则,如果找到了就返回true,否则返回false
|
||||
Form_pg_rewrite pg_rewrite = (Form_pg_rewrite)GETSTRUCT(rewrite_tup);
|
||||
if (pg_rewrite->ev_type == ev_type) {
|
||||
has_rule = true;
|
||||
|
|
@ -192,7 +180,6 @@ bool rel_has_rule(Oid relid, char ev_type)
|
|||
* were unique across the entire database.
|
||||
*/
|
||||
Oid get_rewrite_oid_without_relid(const char* rulename, Oid* reloid, bool missing_ok)
|
||||
//在没有关系ID的情况下,获取给定规则名(rulename)的OID
|
||||
{
|
||||
Relation RewriteRelation;
|
||||
TableScanDesc scanDesc;
|
||||
|
|
@ -202,12 +189,12 @@ Oid get_rewrite_oid_without_relid(const char* rulename, Oid* reloid, bool missin
|
|||
|
||||
/* Search pg_rewrite for such a rule */
|
||||
ScanKeyInit(&scanKeyData, Anum_pg_rewrite_rulename, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(rulename));
|
||||
|
||||
|
||||
RewriteRelation = heap_open(RewriteRelationId, AccessShareLock);
|
||||
scanDesc = tableam_scan_begin(RewriteRelation, SnapshotNow, 1, &scanKeyData);
|
||||
|
||||
htup = (HeapTuple) tableam_scan_getnexttuple(scanDesc, ForwardScanDirection);
|
||||
if (!HeapTupleIsValid(htup)) {//在pg_rewrite表上扫描,查找满足规则名的规则,如果找到了,则返回OID,如果没有找到并且missing_ok参数为false,则会报错
|
||||
if (!HeapTupleIsValid(htup)) {
|
||||
if (!missing_ok)
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%s\" does not exist", rulename)));
|
||||
ruleoid = InvalidOid;
|
||||
|
|
|
|||
|
|
@ -1263,7 +1263,7 @@ static void ckpt_pagewriter_main_thread_loop(void)
|
|||
HandlePageWriterMainInterrupts();
|
||||
|
||||
candidate_num = get_curr_candidate_nums(false) + get_curr_candidate_nums(true);
|
||||
if (candidate_num == 0 && !t_thrd.pagewriter_cxt.shutdown_requested) {
|
||||
if (candidate_num == 0) {
|
||||
/* wakeup sub thread scan the buffer pool, init the candidate list */
|
||||
wakeup_sub_thread();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -825,10 +825,10 @@ void client_read_ended(void)
|
|||
#define INIT_PLUGIN_OBJECT "init_plugin_object"
|
||||
void InitBSqlPluginHookIfNeeded()
|
||||
{
|
||||
const char* dolphin = "dolphin";
|
||||
const char* b_sql_plugin = "b_sql_plugin";
|
||||
CFunInfo tmpCF;
|
||||
|
||||
tmpCF = load_external_function(dolphin, INIT_PLUGIN_OBJECT, false, false);
|
||||
tmpCF = load_external_function(b_sql_plugin, INIT_PLUGIN_OBJECT, false, false);
|
||||
if (tmpCF.user_fn != NULL) {
|
||||
((void* (*)(void))(tmpCF.user_fn))();
|
||||
}
|
||||
|
|
@ -862,11 +862,9 @@ List* pg_parse_query(const char* query_string, List** query_string_locationlist)
|
|||
|
||||
List* (*parser_hook)(const char*, List**) = raw_parser;
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (u_sess->attr.attr_sql.dolphin) {
|
||||
int id = GetCustomParserId();
|
||||
if (id >= 0 && g_instance.raw_parser_hook[id] != NULL) {
|
||||
parser_hook = (List* (*)(const char*, List**))g_instance.raw_parser_hook[id];
|
||||
}
|
||||
int id = GetCustomParserId();
|
||||
if (id >= 0 && g_instance.raw_parser_hook[id] != NULL) {
|
||||
parser_hook = (List* (*)(const char*, List**))g_instance.raw_parser_hook[id];
|
||||
}
|
||||
#endif
|
||||
raw_parsetree_list = parser_hook(query_string, query_string_locationlist);
|
||||
|
|
@ -6114,9 +6112,6 @@ void ProcessInterrupts(void)
|
|||
|
||||
/* The logical replication launcher can be stopped at any time. */
|
||||
proc_exit(0);
|
||||
} else if (IsLogicalWorker()) {
|
||||
ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN),
|
||||
errmsg("terminating logical replication worker due to administrator command")));
|
||||
#endif
|
||||
} else if (IsTxnSnapCapturerProcess()) {
|
||||
ereport(FATAL,
|
||||
|
|
@ -7574,7 +7569,7 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam
|
|||
init_set_params_htab();
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.dolphin) {
|
||||
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.b_sql_plugin) {
|
||||
InitBSqlPluginHookIfNeeded();
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -848,7 +848,7 @@ static bool InitSession(knl_session_context* session)
|
|||
t_thrd.proc_cxt.PostInit->InitSession();
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.dolphin) {
|
||||
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.b_sql_plugin) {
|
||||
InitBSqlPluginHookIfNeeded();
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -720,6 +720,7 @@ static Datum ExecEvalWholeRowVar(
|
|||
{
|
||||
Var* variable = (Var*)wrvstate->xprstate.expr;
|
||||
TupleTableSlot* slot = NULL;
|
||||
TupleDesc slot_tupdesc;
|
||||
bool needslow = false;
|
||||
|
||||
if (isDone != NULL)
|
||||
|
|
@ -801,13 +802,21 @@ static Datum ExecEvalWholeRowVar(
|
|||
if (wrvstate->wrv_junkFilter != NULL)
|
||||
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
|
||||
|
||||
slot_tupdesc = slot->tts_tupleDescriptor;
|
||||
|
||||
/*
|
||||
* If it's a RECORD Var, we'll use the slot's type ID info. It's likely
|
||||
* that the slot's type is also RECORD; if so, make sure it's been
|
||||
* "blessed", so that the Datum can be interpreted later.
|
||||
*
|
||||
* If the Var identifies a named composite type, we must check that the
|
||||
* actual tuple type is compatible with it.
|
||||
*/
|
||||
if (variable->vartype != RECORDOID) {
|
||||
if (variable->vartype == RECORDOID) {
|
||||
if (slot_tupdesc->tdtypeid == RECORDOID && slot_tupdesc->tdtypmod < 0)
|
||||
assign_record_type_typmod(slot_tupdesc);
|
||||
} else {
|
||||
TupleDesc var_tupdesc;
|
||||
TupleDesc slot_tupdesc;
|
||||
int i;
|
||||
|
||||
/*
|
||||
|
|
@ -824,8 +833,6 @@ static Datum ExecEvalWholeRowVar(
|
|||
*/
|
||||
var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
|
||||
|
||||
slot_tupdesc = slot->tts_tupleDescriptor;
|
||||
|
||||
if (var_tupdesc->natts != slot_tupdesc->natts)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
|
|
@ -879,7 +886,6 @@ static Datum ExecEvalWholeRowFast(
|
|||
{
|
||||
Var* variable = (Var*)wrvstate->xprstate.expr;
|
||||
TupleTableSlot* slot = NULL;
|
||||
TupleDesc slot_tupdesc;
|
||||
HeapTuple tuple;
|
||||
TupleDesc tupleDesc;
|
||||
HeapTupleHeader dtuple;
|
||||
|
|
@ -909,17 +915,6 @@ static Datum ExecEvalWholeRowFast(
|
|||
if (wrvstate->wrv_junkFilter != NULL)
|
||||
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
|
||||
|
||||
/*
|
||||
* If it's a RECORD Var, we'll use the slot's type ID info. It's likely
|
||||
* that the slot's type is also RECORD; if so, make sure it's been
|
||||
* "blessed", so that the Datum can be interpreted later.
|
||||
*/
|
||||
slot_tupdesc = slot->tts_tupleDescriptor;
|
||||
if (variable->vartype == RECORDOID) {
|
||||
if (slot_tupdesc->tdtypeid == RECORDOID && slot_tupdesc->tdtypmod < 0)
|
||||
assign_record_type_typmod(slot_tupdesc);
|
||||
}
|
||||
|
||||
tuple = ExecFetchSlotTuple(slot);
|
||||
tupleDesc = slot->tts_tupleDescriptor;
|
||||
|
||||
|
|
@ -1901,13 +1896,6 @@ static void set_result_for_plpgsql_language_function_with_outparam(FuncExprState
|
|||
* @bool has_cursor_return - need store out-args cursor info.
|
||||
* @bool has_refcursor - need store in-args cursor info.
|
||||
* @bool isSetReturnFunc - indicate function returns a set.
|
||||
*The execution process of the ExecMakeFunctionResult function is as follows.
|
||||
* (1) Check whether funcResultStore exists, if so, get the result and return it
|
||||
(2) The calculated parameter values are stored in fcinfo.
|
||||
(3) Pass the parameter into the expression function to calculate the expression,
|
||||
first determine whether the parameter args exists null, and then determine the return mode of the function that returns the set,
|
||||
SFRM_ValuePerCall mode is to return a value each time the call, The SFRM_Materialize schema is the result set instantiated in Tuplestore.
|
||||
(4) Calculate and return results according to different modes.
|
||||
*/
|
||||
template <bool has_refcursor, bool has_cursor_return, bool isSetReturnFunc>
|
||||
static Datum ExecMakeFunctionResult(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
|
||||
|
|
@ -3089,10 +3077,6 @@ no_function_result:
|
|||
/* ----------------------------------------------------------------
|
||||
* ExecEvalFunc
|
||||
* ----------------------------------------------------------------
|
||||
*The execution process of the ExecEvalFunc function is as follows.
|
||||
(1) Initialize the FuncExprState node by init_fcache function, including initialization parameters, memory management, etc.
|
||||
(2) Judge whether the returned result is of set type according to the data in the FuncExprState function,
|
||||
and call the corresponding function to calculate the result.
|
||||
*/
|
||||
static Datum ExecEvalFunc(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
|
||||
{
|
||||
|
|
@ -3537,10 +3521,6 @@ static Datum ExecEvalNot(BoolExprState* notclause, ExprContext* econtext, bool*
|
|||
/* ----------------------------------------------------------------
|
||||
* ExecEvalOr
|
||||
* ----------------------------------------------------------------
|
||||
*The main execution process of ExecEvalOr function is as follows.
|
||||
(1) Traverse child expression clauses.
|
||||
(2) Use the function ExecEvalExpr to call the expression calculation function in clause and calculate the result.
|
||||
(3) To judge the results, if there is a result in the or expression that meets the conditions, it will jump out of the loop and return directly.
|
||||
*/
|
||||
static Datum ExecEvalOr(BoolExprState* orExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
|
||||
{
|
||||
|
|
@ -5179,11 +5159,6 @@ Datum ExecEvalExprSwitchContext(ExprState* expression, ExprContext* econtext, bo
|
|||
* 'parent' may be NULL if we are preparing an expression that is not
|
||||
* associated with a plan tree. (If so, it can't have aggs or subplans.)
|
||||
* This case should usually come through ExecPrepareExpr, not directly here.
|
||||
*The execution process of the ExecInitExpr function is as follows.
|
||||
(1) Determine whether the input node is empty. If it is empty,return NULL directly, indicating that there is no restriction for expression.
|
||||
(2) According to the type of node input,Initialize variable evalfunc which is the execution function corresponding to node,
|
||||
If the node has parameters or expressions, the function ExecInitExpr will be recursively called and ExprState tree will be generated.
|
||||
(3) Return ExprState tree, and execute the expression recursively according to ExprState tree.
|
||||
*/
|
||||
ExprState* ExecInitExpr(Expr* node, PlanState* parent)
|
||||
{
|
||||
|
|
@ -6143,10 +6118,6 @@ Datum fetch_lob_value_from_tuple(varatt_lob_pointer* lob_pointer, Oid update_oid
|
|||
* of *isDone = ExprMultipleResult signifies a set element, and a return
|
||||
* of *isDone = ExprEndResult signifies end of the set of tuple.
|
||||
* We assume that *isDone has been initialized to ExprSingleResult by caller.
|
||||
* The execution process of the ExecTargetList function is as follows.
|
||||
(1) Iterate over the expressions in targetlist.
|
||||
(2) Calculation of expression results.
|
||||
(3) Judge the itemIsDone[resind] parameter in the results and generate the final tuple.
|
||||
*/
|
||||
static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* values, bool* isnull,
|
||||
ExprDoneCond* itemIsDone, ExprDoneCond* isDone)
|
||||
|
|
|
|||
|
|
@ -322,6 +322,7 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
|
|||
}
|
||||
if (found) {
|
||||
/* Found tuple, try to lock it in the lockmode. */
|
||||
outslot->tts_tuple = ExecMaterializeSlot(outslot);
|
||||
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
|
||||
/*
|
||||
* If the tuple is locked, wait for locking transaction to finish
|
||||
|
|
@ -345,16 +346,12 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
|
|||
ItemPointer tid = tableam_tops_get_t_self(targetRel, outslot->tts_tuple);
|
||||
|
||||
if (RelationIsUstoreFormat(targetRel)) {
|
||||
/* materialize the slot, so we can visit it after the scan is end */
|
||||
outslot->tts_tuple = UHeapMaterialize(outslot);
|
||||
ItemPointerCopy(tid, &UHeaplocktup.ctid);
|
||||
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
|
||||
securec_check(rc, "\0", "\0");
|
||||
UHeaplocktup.disk_tuple = &tbuf.hdr;
|
||||
locktup = &UHeaplocktup;
|
||||
} else {
|
||||
/* materialize the slot, so we can visit it after the scan is end */
|
||||
outslot->tts_tuple = ExecMaterializeSlot(outslot);
|
||||
ItemPointerCopy(tid, &heaplocktup.t_self);
|
||||
locktup = &heaplocktup;
|
||||
}
|
||||
|
|
@ -404,10 +401,6 @@ static bool tuple_equals_slot(TupleDesc desc, const Tuple tup, TupleTableSlot *s
|
|||
/* Check equality of the attributes. */
|
||||
for (attrnum = 0; attrnum < desc->natts; attrnum++) {
|
||||
TypeCacheEntry *typentry;
|
||||
/* skip generate column */
|
||||
if (GetGeneratedCol(desc, attrnum)) {
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* If one value is NULL and other is not, then they are certainly not
|
||||
* equal
|
||||
|
|
@ -485,6 +478,7 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
|
|||
|
||||
found = true;
|
||||
ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
|
||||
outslot->tts_tuple = ExecMaterializeSlot(outslot);
|
||||
|
||||
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
|
||||
/*
|
||||
|
|
@ -517,16 +511,12 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
|
|||
ItemPointer tid = tableam_tops_get_t_self(rel, outslot->tts_tuple);
|
||||
|
||||
if (RelationIsUstoreFormat(targetRel)) {
|
||||
/* materialize the slot, so we can visit it after the scan is end */
|
||||
outslot->tts_tuple = UHeapMaterialize(outslot);
|
||||
ItemPointerCopy(tid, &UHeaplocktup.ctid);
|
||||
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
|
||||
securec_check(rc, "\0", "\0");
|
||||
UHeaplocktup.disk_tuple = &tbuf.hdr;
|
||||
locktup = &UHeaplocktup;
|
||||
} else {
|
||||
/* materialize the slot, so we can visit it after the scan is end */
|
||||
outslot->tts_tuple = ExecMaterializeSlot(outslot);
|
||||
ItemPointerCopy(tid, &heaplocktup.t_self);
|
||||
locktup = &heaplocktup;
|
||||
}
|
||||
|
|
@ -663,6 +653,7 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
|
|||
/* Compute stored generated columns */
|
||||
if (rel->rd_att->constr && rel->rd_att->constr->has_generated_stored) {
|
||||
ExecComputeStoredGenerated(resultRelInfo, estate, slot, tuple, CMD_UPDATE);
|
||||
tuple = slot->tts_tuple;
|
||||
}
|
||||
|
||||
/* Check the constraints of the tuple */
|
||||
|
|
@ -681,11 +672,10 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
|
|||
rowMovement = true;
|
||||
}
|
||||
|
||||
tuple = slot->tts_tuple;
|
||||
CommandId cid = GetCurrentCommandId(true);
|
||||
/* OK, update the tuple and index entries for it */
|
||||
if (!rowMovement) {
|
||||
res = tableam_tuple_update(targetRelation, parentRelation, searchSlotTid, tuple, cid,
|
||||
res = tableam_tuple_update(targetRelation, parentRelation, searchSlotTid, slot->tts_tuple, cid,
|
||||
InvalidSnapshot, estate->es_snapshot, true, &oldslot, &tmfd, &updateIndexes, &modifiedIdxAttrs,
|
||||
false, allowInplaceUpdate);
|
||||
CheckTupleModifyRes(res);
|
||||
|
|
|
|||
|
|
@ -41,10 +41,8 @@
|
|||
*
|
||||
* Note: globalhash is generated by operate info and previous globalhash using md5.
|
||||
*/
|
||||
// 生成全局链的哈希值
|
||||
bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist, const hash32_t *prev_hash)
|
||||
{
|
||||
// 错误代码处理变量
|
||||
errno_t rc = EOK;
|
||||
int comb_strlen;
|
||||
char *comb_string = NULL;
|
||||
|
|
@ -54,9 +52,7 @@ bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist,
|
|||
* generation. If previous block exists, we will use previous global
|
||||
* hash as combine string to calculate globalhash.
|
||||
*/
|
||||
// 如果前一个块不存在,则使用全局系统表名作为生成哈希值的组合字符串
|
||||
if (!exist) {
|
||||
// 生成创世块的全局哈希
|
||||
/* generate genesis block globalhash */
|
||||
comb_strlen = strlen(GCHAIN_NAME) + strlen(info_string) + 1;
|
||||
comb_string = (char *)palloc0(comb_strlen);
|
||||
|
|
@ -64,7 +60,6 @@ bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist,
|
|||
securec_check_ss(rc, "", "");
|
||||
} else {
|
||||
/* use previous globalhash and current block info to calculate globalhash. */
|
||||
// 使用前一个全局哈希和当前块信息生成全局哈希
|
||||
char *pre_hash_str = DatumGetCString(DirectFunctionCall1(hash32out, HASH32GetDatum(prev_hash)));
|
||||
comb_strlen = strlen(pre_hash_str) + strlen(info_string) + 1;
|
||||
comb_string = (char *)palloc0(comb_strlen);
|
||||
|
|
@ -72,7 +67,7 @@ bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist,
|
|||
securec_check_ss(rc, "", "");
|
||||
pfree_ext(pre_hash_str);
|
||||
}
|
||||
// 使用 md5 函数生成哈希值
|
||||
|
||||
if (!pg_md5_binary(comb_string, comb_strlen - 1, hash_buffer->data)) {
|
||||
pfree(comb_string);
|
||||
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Failed to generate globalhash, out of memory")));
|
||||
|
|
@ -92,18 +87,14 @@ bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist,
|
|||
* cmd_text: the command query which modified user table.
|
||||
* rel_hash: rel_hash of current block.
|
||||
*/
|
||||
// 组合块信息
|
||||
char *set_gchain_comb_string(const char *db_name, const char *user_name,
|
||||
const char *nsp_name, const char *rel_name, const char *cmd_text, uint64 rel_hash)
|
||||
{
|
||||
// 如果命令文本为空,则设置为空字符串
|
||||
if (cmd_text == NULL) {
|
||||
cmd_text = "";
|
||||
}
|
||||
// 计算组合字符串长度
|
||||
int comb_len = strlen(db_name) + strlen(user_name) + strlen(nsp_name) +
|
||||
strlen(rel_name) + strlen(cmd_text) + PREVIOUS_HASH_LEN + 1;
|
||||
// 分配内存并填充组合字符串
|
||||
char *comb_str = (char *)palloc0(sizeof(char) * comb_len);
|
||||
errno_t rc = snprintf_s(comb_str, comb_len, comb_len - 1, "%s%s%s%s%s%lu",
|
||||
db_name, user_name, nsp_name, rel_name, cmd_text, rel_hash);
|
||||
|
|
@ -122,10 +113,8 @@ char *set_gchain_comb_string(const char *db_name, const char *user_name,
|
|||
* into gchain cache for next block. Thus, previous global hash is
|
||||
* come from cache directly.
|
||||
*/
|
||||
// 向全局链追加块记录的函数
|
||||
void ledger_gchain_append(Oid relid, const char *query_string, uint64 cn_hash)
|
||||
{
|
||||
// 定义变量
|
||||
Datum current_time;
|
||||
Datum values[Natts_gs_global_chain] = {0};
|
||||
bool nulls[Natts_gs_global_chain] = {false};
|
||||
|
|
@ -139,7 +128,6 @@ void ledger_gchain_append(Oid relid, const char *query_string, uint64 cn_hash)
|
|||
GlobalPrevBlock current_block;
|
||||
|
||||
/* get basic informations. */
|
||||
// 获取基本信息
|
||||
db_name = get_database_name(u_sess->proc_cxt.MyDatabaseId);
|
||||
user_name = GetUserNameFromId(GetCurrentUserId());
|
||||
current_time = TimestampTzGetDatum(GetCurrentTimestamp());
|
||||
|
|
@ -147,17 +135,15 @@ void ledger_gchain_append(Oid relid, const char *query_string, uint64 cn_hash)
|
|||
rel_name = get_rel_name(relid);
|
||||
|
||||
/* Make combine string of current record: rel_name + nsp_name + query_string + rel_hash */
|
||||
// 创建当前记录的组合字符串
|
||||
combine_string = set_gchain_comb_string(db_name, user_name, nsp_name, rel_name, query_string, cn_hash);
|
||||
|
||||
/*
|
||||
* rel_hash: sum of hash in DN which generated by this query_string.
|
||||
* globalhash: hash for last record of gs_global_chain, it means blockchain prevhash.
|
||||
*/
|
||||
// 计算当前块的全局哈希
|
||||
current_block.blocknum = get_next_g_blocknum();
|
||||
gen_global_hash(¤t_block.globalhash, combine_string, false, NULL);
|
||||
// 填充插入记录的值
|
||||
|
||||
values[Anum_gs_global_chain_blocknum - 1] = UInt64GetDatum(current_block.blocknum);
|
||||
values[Anum_gs_global_chain_dbname - 1] = DirectFunctionCall1(namein, CStringGetDatum(db_name));
|
||||
values[Anum_gs_global_chain_username - 1] = DirectFunctionCall1(namein, CStringGetDatum(user_name));
|
||||
|
|
@ -168,7 +154,7 @@ void ledger_gchain_append(Oid relid, const char *query_string, uint64 cn_hash)
|
|||
values[Anum_gs_global_chain_relhash - 1] = UInt64GetDatum(cn_hash);
|
||||
values[Anum_gs_global_chain_globalhash - 1] = HASH32GetDatum(¤t_block.globalhash);
|
||||
values[Anum_gs_global_chain_txcommand - 1] = CStringGetTextDatum(query_string);
|
||||
// 打开全局链表并插入记录
|
||||
|
||||
rel_gchain = heap_open(GsGlobalChainRelationId, RowExclusiveLock);
|
||||
tup = heap_form_tuple(rel_gchain->rd_att, values, nulls);
|
||||
|
||||
|
|
@ -176,7 +162,6 @@ void ledger_gchain_append(Oid relid, const char *query_string, uint64 cn_hash)
|
|||
heap_freetuple(tup);
|
||||
|
||||
/* set latest previous global chain block */
|
||||
// 设置最新的全局链块
|
||||
heap_close(rel_gchain, RowExclusiveLock);
|
||||
pfree(combine_string);
|
||||
}
|
||||
|
|
@ -188,14 +173,12 @@ void ledger_gchain_append(Oid relid, const char *query_string, uint64 cn_hash)
|
|||
* operation: command operation.
|
||||
* hash: the hash that prepare to append.
|
||||
*/
|
||||
// 向响应标签追加哈希值的函数
|
||||
static void ledger_output_append_hash(char *resp_tag, CmdType operation, uint64 hash)
|
||||
{
|
||||
// 断言确保响应标签不为空
|
||||
Assert(resp_tag != NULL);
|
||||
size_t len = strlen(resp_tag);
|
||||
errno_t ret = EOK;
|
||||
// 根据命令类型追加哈希值
|
||||
|
||||
switch (operation) {
|
||||
case CMD_INSERT:
|
||||
case CMD_UPDATE:
|
||||
|
|
@ -223,14 +206,11 @@ static void ledger_ExecutorEnd(QueryDesc *query_desc)
|
|||
{
|
||||
uint64 hashsum;
|
||||
bool has_remote_hash = query_desc->estate->es_modifiedRowHash != NIL;
|
||||
// 计算 es_modifiedRowHash 中所有哈希的组合哈希值
|
||||
hashsum = hash_combiner(query_desc->estate->es_modifiedRowHash);
|
||||
// 如果当前节点是协调器或单节点,并且存在远程哈希,则继续执行以下操作
|
||||
if ((IS_PGXC_COORDINATOR || g_instance.role == VSINGLENODE) && has_remote_hash) {
|
||||
Oid relid = InvalidOid;
|
||||
Relation rel = NULL;
|
||||
int relnum = query_desc->estate->es_num_result_relations;
|
||||
// 如果存在结果关系,则获取结果关系描述
|
||||
if (relnum > 0) {
|
||||
rel = query_desc->estate->es_result_relations->ri_RelationDesc;
|
||||
/* gs_global_chain only records following actions */
|
||||
|
|
@ -239,7 +219,6 @@ static void ledger_ExecutorEnd(QueryDesc *query_desc)
|
|||
case CMD_DELETE:
|
||||
case CMD_UPDATE:
|
||||
relid = RelationGetRelid(rel);
|
||||
// 如果关系是区块链表,则将块追加到 gs_global_chain
|
||||
if (rel->rd_isblockchain) {
|
||||
ledger_gchain_append(relid, query_desc->sourceText, hashsum);
|
||||
}
|
||||
|
|
@ -249,12 +228,11 @@ static void ledger_ExecutorEnd(QueryDesc *query_desc)
|
|||
}
|
||||
}
|
||||
}
|
||||
// 如果存在要返回的响应标签,并且存在远程哈希,并且连接不是来自应用程序,则将哈希追加到响应标签
|
||||
|
||||
if (u_sess->ledger_cxt.resp_tag != NULL && has_remote_hash && !IsConnFromApp()) {
|
||||
ledger_output_append_hash(u_sess->ledger_cxt.resp_tag, query_desc->operation, hashsum);
|
||||
u_sess->ledger_cxt.resp_tag = NULL;
|
||||
}
|
||||
// 如果存在前一个 ExecutorEnd 钩子函数,则执行它,否则执行标准的 ExecutorEnd 函数
|
||||
if (t_thrd.security_ledger_cxt.prev_ExecutorEnd) {
|
||||
((ExecutorEnd_hook_type)t_thrd.security_ledger_cxt.prev_ExecutorEnd)(query_desc);
|
||||
} else {
|
||||
|
|
@ -273,19 +251,16 @@ static void ledger_ExecutorEnd(QueryDesc *query_desc)
|
|||
*/
|
||||
void light_ledger_ExecutorEnd(Query *query, uint64 relhash)
|
||||
{
|
||||
// 检查当前节点是否为协调器或者单节点(VSINGLENODE),如果不是,则直接返回,不执行后续操作。
|
||||
if (!IS_PGXC_COORDINATOR && g_instance.role != VSINGLENODE) {
|
||||
return;
|
||||
}
|
||||
Oid relid = InvalidOid;
|
||||
// 根据查询类型(命令类型)执行不同的操作。
|
||||
|
||||
switch (query->commandType) {
|
||||
case CMD_INSERT:
|
||||
case CMD_DELETE:
|
||||
case CMD_UPDATE:
|
||||
// 获取目标查询中关系的 OID。
|
||||
relid = get_target_query_relid(query->rtable, query->resultRelation);
|
||||
// 检查该关系是否为区块链用户表,如果是,则将块追加到 gs_global_chain。
|
||||
if (is_ledger_usertable(relid)) {
|
||||
ledger_gchain_append(relid, query->sql_statement, relhash);
|
||||
}
|
||||
|
|
@ -295,9 +270,7 @@ void light_ledger_ExecutorEnd(Query *query, uint64 relhash)
|
|||
break;
|
||||
}
|
||||
}
|
||||
//ight_ledger_ExecutorEnd,用于在轻量级代理中记录块到 gs_global_chain。它接收一个查询对象 query 和一个关系哈希值 relhash 作为参数。
|
||||
//如果当前节点不是协调器且不是单节点(VSINGLENODE),则直接返回,不执行后续操作。否则,根据查询的类型(命令类型),执行以下操作:
|
||||
//如果是插入(CMD_INSERT)、删除(CMD_DELETE)或更新(CMD_UPDATE)操作,则获取查询中目标关系的 OID,并检查是否是区块链用户表,如果是,则将块追加到 gs_global_chain。
|
||||
|
||||
/*
|
||||
* light_ledger_ExecutorEnd -- record block to gchain in opfusion.
|
||||
*
|
||||
|
|
@ -308,16 +281,14 @@ void light_ledger_ExecutorEnd(Query *query, uint64 relhash)
|
|||
*/
|
||||
void opfusion_ledger_ExecutorEnd(FusionType fusiontype, Oid relid, const char *query, uint64 relhash)
|
||||
{
|
||||
// 如果当前节点是数据节点(VDATANODE)或者关系不是区块链用户表,则直接返回,不执行后续操作。
|
||||
if (g_instance.role == VDATANODE || !is_ledger_usertable(relid)) {
|
||||
return;
|
||||
}
|
||||
// 根据操作类型(融合类型)执行不同的操作。
|
||||
|
||||
switch (fusiontype) {
|
||||
case INSERT_FUSION:
|
||||
case UPDATE_FUSION:
|
||||
case DELETE_FUSION:
|
||||
// 如果关系是区块链用户表,则将块追加到 gs_global_chain。
|
||||
if (is_ledger_usertable(relid)) {
|
||||
ledger_gchain_append(relid, query, relhash);
|
||||
}
|
||||
|
|
@ -327,23 +298,16 @@ void opfusion_ledger_ExecutorEnd(FusionType fusiontype, Oid relid, const char *q
|
|||
break;
|
||||
}
|
||||
}
|
||||
//opfusion_ledger_ExecutorEnd,用于在操作融合(opfusion)中记录块到 gs_global_chain。它接收操作类型 fusiontype、用户表的 OID relid、修改用户表的原始查询 query 和源文本生成的关系哈希值 relhash 作为参数。
|
||||
//如果当前节点是数据节点(VDATANODE)或关系不是区块链用户表,则直接返回,不执行后续操作。否则,根据融合操作类型执行以下操作:
|
||||
// 如果是插入融合(INSERT_FUSION)、更新融合(UPDATE_FUSION)或删除融合(DELETE_FUSION)操作,并且关系是区块链用户表,则将块追加到 gs_global_chain。
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ledger_hook_init -- install of gchain block record hook.
|
||||
*/
|
||||
void ledger_hook_init(void)
|
||||
{
|
||||
// 保存先前的 ExecutorEnd_hook 函数到 prev_ExecutorEnd 中。
|
||||
t_thrd.security_ledger_cxt.prev_ExecutorEnd = (void *)ExecutorEnd_hook;
|
||||
// 将 ExecutorEnd_hook 设置为 ledger_ExecutorEnd 函数,以便在查询执行结束时记录块到 gs_global_chain。
|
||||
ExecutorEnd_hook = ledger_ExecutorEnd;
|
||||
}
|
||||
//ledger_hook_init,用于初始化 gs_global_chain 块记录的挂钩函数。首先,它保存先前的 ExecutorEnd_hook 函数到 prev_ExecutorEnd 中。然后,将 ExecutorEnd_hook 设置为 ledger_ExecutorEnd 函数,以便在查询执行结束时记录块到 gs_global_chain。
|
||||
|
||||
/*
|
||||
* ledger_hook_fini -- uninstall of gchain block record hook.
|
||||
*/
|
||||
|
|
@ -351,5 +315,3 @@ void ledger_hook_fini(void)
|
|||
{
|
||||
ExecutorEnd_hook = (ExecutorEnd_hook_type)t_thrd.security_ledger_cxt.prev_ExecutorEnd;
|
||||
}
|
||||
//ledger_hook_fini,用于卸载 gs_global_chain 块记录的挂钩函数。它将 ExecutorEnd_hook 恢复为先前保存的 prev_ExecutorEnd 函数,以取消块记录的挂钩功能。
|
||||
//总结函数功能:这一系列函数用于在不同的执行上下文中记录块到 gs_global_chain。具体地,它们根据查询类型和节点角色来确定是否记录块,并将块的相关信息追加到 gs_global_chain 中。函数 ledger_hook_init 和 ledger_hook_fini 用于初始化和卸载块记录的挂钩函数。
|
||||
|
|
@ -55,23 +55,18 @@
|
|||
*/
|
||||
static void prepare_histback_dir(void)
|
||||
{
|
||||
// 准备历史归档目录
|
||||
char ledger_histback_dir[MAXPGPATH] = {0};
|
||||
// 声明一个存储目录路径的字符数组。
|
||||
int rc = snprintf_s(ledger_histback_dir, MAXPGPATH, MAXPGPATH - 1,
|
||||
"%s/hist_bak", g_instance.attr.attr_security.Audit_directory);
|
||||
securec_check_ss(rc, "\0", "\0");
|
||||
// 使用 snprintf_s 函数构建目录路径,并检查是否出现错误。
|
||||
|
||||
/*
|
||||
* Create histback directory if not present; ignore errors
|
||||
*/
|
||||
(void)pg_mkdir_p(g_instance.attr.attr_security.Audit_directory, S_IRWXU);
|
||||
(void)pg_mkdir_p(ledger_histback_dir, S_IRWXU);
|
||||
// 创建目录,这里使用了 PostgreSQL 提供的创建目录的函数 pg_mkdir_p。
|
||||
}
|
||||
//prepare_histback_dir 函数:功能为准备历史归档目录;
|
||||
//具体步骤:1构建历史归档目录的路径。2使用 pg_mkdir_p 函数创建历史归档目录。
|
||||
//作用:确保历史数据归档的目录已经存在,如果不存在则创建。
|
||||
|
||||
/*
|
||||
* ledger_copytable -- copy rows of hist table.
|
||||
*
|
||||
|
|
@ -83,7 +78,6 @@ static void prepare_histback_dir(void)
|
|||
*/
|
||||
static uint64 ledger_copytable(CopyState cstate)
|
||||
{
|
||||
// 复制表中的数据
|
||||
Relation cur_rel;
|
||||
TupleDesc tuple_desc;
|
||||
Form_pg_attribute *attr = NULL;
|
||||
|
|
@ -91,16 +85,13 @@ static uint64 ledger_copytable(CopyState cstate)
|
|||
int num_phys_attrs;
|
||||
uint64 processed = 0;
|
||||
bool is_gchain;
|
||||
// 声明一些变量用于存储表的信息和处理数据。
|
||||
|
||||
cur_rel = cstate->curPartionRel;
|
||||
is_gchain = RelationGetRelid(cur_rel) == GsGlobalChainRelationId;
|
||||
// 获取当前处理的表,并检查是否为全局链表。
|
||||
tuple_desc = RelationGetDescr(cur_rel);
|
||||
attr = tuple_desc->attrs;
|
||||
num_phys_attrs = tuple_desc->natts;
|
||||
cstate->null_print_client = cstate->null_print;
|
||||
// 获取表的描述信息,包括属性和属性数量,并设置一些用于打印 NULL 值的变量。
|
||||
|
||||
/* We use fe_msgbuf as a per-row buffer regardless of copy_dest */
|
||||
if (cstate->fe_msgbuf == NULL) {
|
||||
|
|
@ -108,7 +99,7 @@ static uint64 ledger_copytable(CopyState cstate)
|
|||
if (IS_PGXC_COORDINATOR || g_instance.role == VSINGLENODE)
|
||||
ProcessFileHeader(cstate);
|
||||
}
|
||||
// 如果消息缓冲区为空,则创建一个,并在特定条件下处理文件头。
|
||||
|
||||
/* For each column type, get its out function. */
|
||||
cstate->out_functions = (FmgrInfo*)palloc(num_phys_attrs * sizeof(FmgrInfo));
|
||||
foreach (cur, cstate->attnumlist) {
|
||||
|
|
@ -118,7 +109,6 @@ static uint64 ledger_copytable(CopyState cstate)
|
|||
getTypeOutputInfo(attr[attnum - 1]->atttypid, &out_func_oid, &isvarlena);
|
||||
fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]);
|
||||
}
|
||||
// 为输出函数分配内存并填充函数信息,这将用于将数据从内部格式转换为文本格式。
|
||||
|
||||
/*
|
||||
* Create a temporary memory context that we can reset once per row to
|
||||
|
|
@ -128,7 +118,7 @@ static uint64 ledger_copytable(CopyState cstate)
|
|||
*/
|
||||
cstate->rowcontext = AllocSetContextCreate(
|
||||
CurrentMemoryContext, "COPY TO", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
||||
// 创建一个内存上下文,用于存储行数据。
|
||||
|
||||
/*
|
||||
* For non-binary copy, we need to convert null_print to file
|
||||
* encoding, because it will be sent directly with CopySendString.
|
||||
|
|
@ -136,7 +126,6 @@ static uint64 ledger_copytable(CopyState cstate)
|
|||
if (cstate->need_transcoding) {
|
||||
cstate->null_print_client = pg_server_to_any(cstate->null_print, cstate->null_print_len, cstate->file_encoding);
|
||||
}
|
||||
// 如果需要字符编码转换,将 NULL 值的打印格式转换为客户端编码。
|
||||
|
||||
Tuple tuple;
|
||||
TableScanDesc scan_desc;
|
||||
|
|
@ -147,17 +136,14 @@ static uint64 ledger_copytable(CopyState cstate)
|
|||
|
||||
values = (Datum*)palloc0(num_phys_attrs * sizeof(Datum));
|
||||
nulls = (bool*)palloc0(num_phys_attrs * sizeof(bool));
|
||||
// 分配内存来存储行的数据和 NULL 值的标志。
|
||||
|
||||
scan_desc = scan_handler_tbl_beginscan(cur_rel, GetActiveSnapshot(), 0, NULL);
|
||||
// 开始扫描表,获取一个用于扫描的描述符。
|
||||
|
||||
/* For each row, we will recalculate previous hash. */
|
||||
while ((tuple = scan_handler_tbl_getnext(scan_desc, ForwardScanDirection, cur_rel)) != NULL) {
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
// 检查是否有中断请求。
|
||||
/* Deconstruct the tuple ... faster than repeated heap_getattr */
|
||||
tableam_tops_deform_tuple2(tuple, tuple_desc, values, nulls, GetTableScanDesc(scan_desc, cur_rel)->rs_cbuf);
|
||||
// 解析元组数据并填充到 values 和 nulls 数组中。
|
||||
if (!is_gchain) {
|
||||
char comb_str[NAMEDATALEN] = {0};
|
||||
uint64 t_ins = nulls[USERCHAIN_COLUMN_HASH_INS] ? 0 : DatumGetUInt64(values[USERCHAIN_COLUMN_HASH_INS]);
|
||||
|
|
@ -182,22 +168,19 @@ static uint64 ledger_copytable(CopyState cstate)
|
|||
}
|
||||
/* Format and send the data */
|
||||
CopyOneRowTo(cstate, HeapTupleGetOid((HeapTuple)tuple), values, nulls);
|
||||
// ... 以下代码对解析后的数据进行处理,包括计算哈希值和将数据发送到输出。
|
||||
rec_num++;
|
||||
processed++;
|
||||
}
|
||||
|
||||
scan_handler_tbl_endscan(scan_desc);
|
||||
// 结束表的扫描。
|
||||
|
||||
pfree_ext(values);
|
||||
pfree_ext(nulls);
|
||||
MemoryContextDelete(cstate->rowcontext);
|
||||
// 释放分配的内存和上下文。
|
||||
|
||||
return processed;
|
||||
}
|
||||
//ledger_copytable 函数: 功能为复制表中的数据。
|
||||
//具体步骤:1获取要复制的表的描述信息,包括属性和属性数量。2设置输出函数,用于将内部数据转换为文本格式。3 创建用于存储行数据的内存上下文。4如果需要字符编码转换,将 NULL 值的打印格式转换为客户端编码。5扫描表中的每一行数据,解析并处理每一行的数据。6计算哈希值并将数据发送到输出。6结束表的扫描,释放分配的内存和上下文。
|
||||
//作用:将表中的数据复制到输出,同时进行一些数据处理,如哈希计算和字符编码转换。
|
||||
|
||||
/*
|
||||
* ledger_docopy -- the copy process of hist table
|
||||
*
|
||||
|
|
@ -219,48 +202,33 @@ static uint64 ledger_docopy(CopyStmt *stmt, const char *queryString)
|
|||
|
||||
/* Open and lock the relation, using the appropriate lock type. */
|
||||
rel = heap_openrv(stmt->relation, AccessShareLock);
|
||||
// 使用给定的表名打开表,并锁定以防止其他事务的写入。
|
||||
rte = makeNode(RangeTblEntry);
|
||||
rte->rtekind = RTE_RELATION;
|
||||
rte->relid = RelationGetRelid(rel);
|
||||
rte->relkind = rel->rd_rel->relkind;
|
||||
rte->requiredPerms = ACL_SELECT;
|
||||
// 创建一个表示关系的 RangeTblEntry 结构,并设置相关信息,包括关系ID、类型和所需的权限。
|
||||
|
||||
tup_desc = RelationGetDescr(rel);
|
||||
// 获取关系的元组描述。
|
||||
|
||||
attnum = (rte->relid == GsGlobalChainRelationId) ? Natts_gs_global_chain : USERCHAIN_COLUMN_NUM;
|
||||
// 确定要选择的列数,根据关系的类型。
|
||||
|
||||
/* add columns that need select permission. */
|
||||
for (int i = 1; i <= attnum; ++i) {
|
||||
int attno = i - FirstLowInvalidHeapAttributeNumber;
|
||||
rte->selectedCols = bms_add_member(rte->selectedCols, attno);
|
||||
}
|
||||
// 循环遍历列,将需要 SELECT 权限的列添加到 selectedCols 集合中。
|
||||
(void)ExecCheckRTPerms(list_make1(rte), true);
|
||||
// 使用 list_make1 创建 RangeTblEntry 的列表,并检查权限。
|
||||
|
||||
cstate = BeginCopyTo(rel, query, queryString, stmt->filename, stmt->attlist, stmt->options);
|
||||
// 初始化用于复制数据的 CopyState 结构。
|
||||
cstate->range_table = list_make1(rte);
|
||||
cstate->curPartionRel = cstate->rel;
|
||||
// 设置 range_table 和 curPartionRel。
|
||||
processed = ledger_copytable(cstate);
|
||||
// 调用 ledger_copytable 函数执行数据复制操作。
|
||||
EndCopyTo(cstate);
|
||||
// 结束数据复制操作。
|
||||
|
||||
if (rel != NULL) {
|
||||
heap_close(rel, AccessShareLock);
|
||||
}
|
||||
// 如果关系仍然打开,关闭关系。
|
||||
|
||||
return processed;
|
||||
}
|
||||
//ledger_docopy 函数:功能:执行数据复制操作。
|
||||
//具体步骤:1打开和锁定指定的数据库表。2 创建并配置 RangeTblEntry,用于表示表的相关信息和权限。3初始化 CopyState 结构,用于进行数据复制操作。4配置相关参数,如输出文件名、选定的列和其他选项。5调用 ledger_copytable 函数执行数据复制操作。6结束数据复制操作。
|
||||
//作用:执行复制数据的操作,包括表的锁定、权限检查、数据复制和结束操作。
|
||||
|
||||
/*
|
||||
* get_current_timestamp_text -- generate time text for name appending
|
||||
|
|
@ -272,7 +240,6 @@ static uint64 ledger_docopy(CopyStmt *stmt, const char *queryString)
|
|||
static void get_current_timestamp_text(char *time_str)
|
||||
{
|
||||
const char *now = timestamptz_to_str(GetCurrentTimestamp());
|
||||
// 获取当前时间戳的文本表示。
|
||||
size_t time_len = strlen(now);
|
||||
size_t pos = 0;
|
||||
for (size_t i = 0; i < time_len; ++i) {
|
||||
|
|
@ -283,11 +250,8 @@ static void get_current_timestamp_text(char *time_str)
|
|||
}
|
||||
}
|
||||
time_str[pos] = '\0';
|
||||
// 遍历时间戳文本,仅保留数字字符,去除其他字符,生成最终的时间字符串。
|
||||
}
|
||||
//get_current_timestamp_text 函数功能:获取当前时间戳的文本表示,并去除非数字字符。
|
||||
//具体步骤:获取当前时间戳的文本表示。遍历时间戳文本,仅保留数字字符,去除其他字符。
|
||||
//作用:生成当前时间的文本表示,用于文件名。
|
||||
|
||||
/*
|
||||
* copy_local_hist_table -- copy history table to hist_back dir.
|
||||
*
|
||||
|
|
@ -303,7 +267,6 @@ static void copy_local_hist_table(Oid relid, char *histname, const char *time)
|
|||
initStringInfo(&buf);
|
||||
CopyStmt *stmt = makeNode(CopyStmt);
|
||||
RangeVar *relation = makeRangeVar("blockchain", histname, -1);
|
||||
// 创建一个表示表的 RangeVar 结构。
|
||||
if (!is_absolute_path(g_instance.attr.attr_security.Audit_directory)) {
|
||||
rc = snprintf_s(path, MAXPGPATH, MAXPGPATH - 1, "%s/%s/hist_bak/%s_%u_%s.hist",
|
||||
t_thrd.proc_cxt.DataDir, g_instance.attr.attr_security.Audit_directory, histname, relid, time);
|
||||
|
|
@ -312,18 +275,13 @@ static void copy_local_hist_table(Oid relid, char *histname, const char *time)
|
|||
g_instance.attr.attr_security.Audit_directory, histname, relid, time);
|
||||
}
|
||||
securec_check_ss(rc, "", "");
|
||||
// 构建历史表的文件路径。
|
||||
appendStringInfo(&buf, "COPY blockchain.%s to \'%s\'", histname, path);
|
||||
// 构建 COPY 命令的字符串。
|
||||
stmt->relation = relation;
|
||||
stmt->is_from = false;
|
||||
stmt->filename = path;
|
||||
// 配置 CopyStmt 结构。
|
||||
ledger_docopy((CopyStmt *)stmt, buf.data);
|
||||
// 调用 ledger_docopy 函数执行数据复制操作。
|
||||
}
|
||||
//copy_local_hist_table 函数功能:复制本地历史表的数据到指定文件。
|
||||
//具体步骤:构建历史表的文件路径。创建 CopyStmt 结构,表示复制操作。配置 CopyStmt 包括源表、目标文件和其他选项。调用 ledger_docopy 函数执行数据复制操作。
|
||||
|
||||
/*
|
||||
* open_histback_dir -- open hist_back dir.
|
||||
*
|
||||
|
|
@ -343,14 +301,10 @@ static DIR *open_histback_dir(char *dir_path)
|
|||
g_instance.attr.attr_security.Audit_directory);
|
||||
}
|
||||
securec_check_ss(rc, "", "");
|
||||
// 构建历史归档目录的路径。
|
||||
dir = AllocateDir(dir_path);
|
||||
// 使用 AllocateDir 打开目录。
|
||||
return dir;
|
||||
}
|
||||
//open_histback_dir 函数功能:打开历史归档目录并返回目录句柄。
|
||||
//具体步骤:构建历史归档目录的路径。使用 AllocateDir 函数打开目录。
|
||||
//作用:打开历史归档目录以便后续的文件操作。
|
||||
|
||||
/*
|
||||
* get_histback_dir_filesize -- count all file size of hist_back dir.
|
||||
*/
|
||||
|
|
@ -362,7 +316,6 @@ static uint64 get_histback_dir_filesize()
|
|||
errno_t rc = EOK;
|
||||
uint64 size = 0;
|
||||
dir = open_histback_dir(dir_path);
|
||||
// 打开历史归档目录。
|
||||
if (dir == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -381,12 +334,9 @@ static uint64 get_histback_dir_filesize()
|
|||
}
|
||||
}
|
||||
FreeDir(dir);
|
||||
// 遍历目录中的文件,计算它们的大小并累加。
|
||||
return size;
|
||||
}
|
||||
//get_histback_dir_filesize 函数功能:计算历史归档目录中文件的总大小。
|
||||
//具体步骤:打开历史归档目录。遍历目录中的文件,计算它们的大小并累加。关闭目录句柄。
|
||||
//作用:计算历史归档目录中文件的总大小。
|
||||
|
||||
/*
|
||||
* remove_oldest_histback_file -- remove oldest file in hist_back
|
||||
*
|
||||
|
|
@ -405,7 +355,6 @@ static uint64 remove_oldest_histback_file()
|
|||
struct stat stat_buf;
|
||||
|
||||
dir = open_histback_dir(dir_path);
|
||||
// 打开历史归档目录。
|
||||
if (dir == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -428,17 +377,12 @@ static uint64 remove_oldest_histback_file()
|
|||
}
|
||||
}
|
||||
FreeDir(dir);
|
||||
// 遍历目录中的文件,找到最旧的文件并记录其路径和大小。
|
||||
|
||||
if (unlink(del_file) < 0) {
|
||||
ereport(WARNING, (errmsg("could not remove histbak file: %s", del_file)));
|
||||
}
|
||||
// 移除最旧的文件。
|
||||
return filesize;
|
||||
}
|
||||
//remove_oldest_histback_file 函数功能:移除历史归档目录中最旧的文件。
|
||||
//具体步骤:打开历史归档目录。遍历目录中的文件,找到最旧的文件并记录其路径和大小。移除最旧的文件。关闭目录句柄。
|
||||
//作用:移除历史归档目录中最旧的文件,以释放空间。
|
||||
|
||||
/*
|
||||
* ledger_hist_archive -- interface for history table archive
|
||||
*
|
||||
|
|
@ -450,7 +394,6 @@ static uint64 remove_oldest_histback_file()
|
|||
*/
|
||||
Datum ledger_hist_archive(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// 检查用户权限,只有超级用户或审计管理员才能执行此操作。
|
||||
if (!isRelSuperuser() && !isAuditadmin(GetUserId())) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
|
||||
}
|
||||
|
|
@ -469,19 +412,15 @@ Datum ledger_hist_archive(PG_FUNCTION_ARGS)
|
|||
table_name = text_to_cstring(rel_name);
|
||||
Oid nspoid = get_namespace_oid(table_nsp, false);
|
||||
relid = get_relname_relid(table_name, nspoid);
|
||||
// 检查用户是否具有表的权限。
|
||||
ledger_usertable_check(relid, nspoid, table_name, table_nsp);
|
||||
// 生成历史表的名称。
|
||||
|
||||
get_hist_name(relid, table_name, hist_name, nspoid, table_nsp);
|
||||
// 获取当前时间的文本表示。
|
||||
get_current_timestamp_text(current_time);
|
||||
// 如果当前角色不是协调者,执行历史归档操作。
|
||||
if (g_instance.role != VCOORDINATOR) {
|
||||
/*
|
||||
* Step 1. Copy user history table.
|
||||
*/
|
||||
uint64 total_histback_size = get_histback_dir_filesize();
|
||||
// 当历史归档目录大小超过限制时,删除最旧的历史文件以释放空间。
|
||||
while (total_histback_size >= (uint64)(u_sess->attr.attr_security.Audit_SpaceLimit * 1024L)) {
|
||||
total_histback_size -= remove_oldest_histback_file();
|
||||
}
|
||||
|
|
@ -500,10 +439,8 @@ Datum ledger_hist_archive(PG_FUNCTION_ARGS)
|
|||
uint64 cur_rec_num = 0;
|
||||
TableScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
// 锁定历史哈希缓存。
|
||||
/* sum all hash_ins and hash_del for unification. */
|
||||
lock_hist_hash_cache(LW_EXCLUSIVE);
|
||||
// 打开历史表以获取数据。
|
||||
Relation histRel = heap_open(get_relname_relid(hist_name, PG_BLOCKCHAIN_NAMESPACE), AccessExclusiveLock);
|
||||
scan = heap_beginscan(histRel, SnapshotNow, 0, NULL);
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {
|
||||
|
|
@ -538,7 +475,6 @@ Datum ledger_hist_archive(PG_FUNCTION_ARGS)
|
|||
values[USERCHAIN_COLUMN_HASH_DEL] = UInt64GetDatum(hash_del);
|
||||
nulls[USERCHAIN_COLUMN_REC_NUM] = false;
|
||||
nulls[USERCHAIN_COLUMN_PREVHASH] = false;
|
||||
// 创建历史记录元组并插入历史表。
|
||||
tuple = heap_form_tuple(RelationGetDescr(histRel), values, nulls);
|
||||
|
||||
/* Do real truncate. */
|
||||
|
|
@ -558,7 +494,7 @@ Datum ledger_hist_archive(PG_FUNCTION_ARGS)
|
|||
}
|
||||
return BoolGetDatum(res);
|
||||
}
|
||||
//它们的主要功能是执行历史表和全局链表的归档操作,包括权限检查、历史文件的管理、数据复制等。这些操作用于审计和维护数据库的历史数据。
|
||||
|
||||
/*
|
||||
* ledger_gchain_archive -- archive gs_global_chain and unify each user rel
|
||||
*
|
||||
|
|
@ -568,7 +504,6 @@ Datum ledger_hist_archive(PG_FUNCTION_ARGS)
|
|||
*/
|
||||
Datum ledger_gchain_archive(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// 检查用户权限,只有超级用户或审计管理员才能执行此操作。
|
||||
if (!isRelSuperuser() && !isAuditadmin(GetUserId())) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
|
||||
}
|
||||
|
|
@ -580,9 +515,8 @@ Datum ledger_gchain_archive(PG_FUNCTION_ARGS)
|
|||
if (g_instance.role != VCOORDINATOR && g_instance.role != VSINGLENODE) {
|
||||
return BoolGetDatum(res);
|
||||
}
|
||||
// 获取历史备份目录的当前大小。
|
||||
|
||||
uint64 total_histback_size = get_histback_dir_filesize();
|
||||
// 当历史备份目录大小超过限制时,删除最旧的历史文件以释放空间。
|
||||
while (total_histback_size >= (uint64)(u_sess->attr.attr_security.Audit_SpaceLimit * 1024L)) {
|
||||
total_histback_size -= remove_oldest_histback_file();
|
||||
}
|
||||
|
|
@ -612,7 +546,6 @@ Datum ledger_gchain_archive(PG_FUNCTION_ARGS)
|
|||
stmt->relation = relation;
|
||||
stmt->is_from = false;
|
||||
stmt->filename = path;
|
||||
// 调用 ledger_docopy 函数执行数据复制。
|
||||
ledger_docopy((CopyStmt *)stmt, buf.data);
|
||||
|
||||
/*
|
||||
|
|
@ -719,4 +652,3 @@ Datum ledger_gchain_archive(PG_FUNCTION_ARGS)
|
|||
|
||||
return BoolGetDatum(res);
|
||||
}
|
||||
//执行全局链表的归档操作,包括权限检查、历史文件的管理、数据复制、哈希表操作等。该操作用于维护数据库的全局链表数据,并确保数据一致性和完整性。
|
||||
|
|
@ -53,34 +53,34 @@
|
|||
*/
|
||||
static uint64 gen_usertable_hash_sum(Relation rel)
|
||||
{
|
||||
uint64 rel_hash = 0;//初始化关系哈希和为零
|
||||
bool is_null = false;//初始化标志位为假
|
||||
int hash_natt = user_hash_attrno(rel->rd_att);//获取用户关系的哈希属性编号
|
||||
uint64 rel_hash = 0;
|
||||
bool is_null = false;
|
||||
int hash_natt = user_hash_attrno(rel->rd_att);
|
||||
Assert(hash_natt >= 0);
|
||||
HeapTuple tuple;//用于存储关系的元组
|
||||
TupleDesc desc = rel->rd_att;//获取关系的元组描述
|
||||
Snapshot snapshot = GetActiveSnapshot();//活动快照
|
||||
TableScanDesc scan;//表扫描器
|
||||
if (RELATION_CREATE_BUCKET(rel)) {//如果关系是哈希分区表啊
|
||||
Relation bucket_rel = NULL;//初始化分区关系
|
||||
oidvector *bucket_list = searchHashBucketByOid(rel->rd_bucketoid);//获取哈希分区列表
|
||||
for (int i = 0; i < bucket_list->dim1; i++) {//遍历哈希分区列表
|
||||
bucket_rel = bucketGetRelation(rel, NULL, bucket_list->values[i]);//获取哈希分区关系
|
||||
scan = heap_beginscan(bucket_rel, snapshot, 0, NULL);//开始扫描分区表
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {//遍历分区表中的元组
|
||||
HeapTuple tuple;
|
||||
TupleDesc desc = rel->rd_att;
|
||||
Snapshot snapshot = GetActiveSnapshot();
|
||||
TableScanDesc scan;
|
||||
if (RELATION_CREATE_BUCKET(rel)) {
|
||||
Relation bucket_rel = NULL;
|
||||
oidvector *bucket_list = searchHashBucketByOid(rel->rd_bucketoid);
|
||||
for (int i = 0; i < bucket_list->dim1; i++) {
|
||||
bucket_rel = bucketGetRelation(rel, NULL, bucket_list->values[i]);
|
||||
scan = heap_beginscan(bucket_rel, snapshot, 0, NULL);
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {
|
||||
rel_hash += DatumGetUInt64(heap_getattr(tuple, hash_natt + 1, desc, &is_null));
|
||||
}//获取哈希值累加到关系哈希和中
|
||||
heap_endscan(scan);//结束扫描
|
||||
}
|
||||
heap_endscan(scan);
|
||||
bucketCloseRelation(bucket_rel);
|
||||
}
|
||||
} else {//如果关系不是哈希分区表
|
||||
scan = heap_beginscan(rel, snapshot, 0, NULL);//开始扫描关系
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {//遍历关系中的元组
|
||||
} else {
|
||||
scan = heap_beginscan(rel, snapshot, 0, NULL);
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {
|
||||
rel_hash += DatumGetUInt64(heap_getattr(tuple, hash_natt + 1, desc, &is_null));
|
||||
}
|
||||
heap_endscan(scan);//结束扫描
|
||||
heap_endscan(scan);
|
||||
}
|
||||
return rel_hash;//返回关系哈希和
|
||||
return rel_hash;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -92,27 +92,27 @@ static uint64 gen_usertable_hash_sum(Relation rel)
|
|||
*/
|
||||
static uint64 get_usertable_hash_sum(Oid relid)
|
||||
{
|
||||
uint64 rel_hash = 0;//初始化关系哈希和为零
|
||||
uint64 rel_hash = 0;
|
||||
Relation rel = NULL;
|
||||
rel = heap_open(relid, AccessShareLock);//打开用户表关系并获取共享锁
|
||||
if (!RelationIsPartitioned(rel)) {//如果用户表不是分区表
|
||||
rel_hash = gen_usertable_hash_sum(rel);//调用函数
|
||||
} else {//如果是分区表
|
||||
List *partition_list = NIL;//初始化
|
||||
rel = heap_open(relid, AccessShareLock);
|
||||
if (!RelationIsPartitioned(rel)) {
|
||||
rel_hash = gen_usertable_hash_sum(rel);
|
||||
} else {
|
||||
List *partition_list = NIL;
|
||||
ListCell *lc = NULL;
|
||||
Partition part;//分区
|
||||
Relation fake_rel;//假分区关系
|
||||
partition_list = relationGetPartitionList(rel, AccessShareLock);//获取分区列表
|
||||
foreach (lc, partition_list) {//遍历分区列表
|
||||
part = (Partition)lfirst(lc);//获取分区
|
||||
Partition part;
|
||||
Relation fake_rel;
|
||||
partition_list = relationGetPartitionList(rel, AccessShareLock);
|
||||
foreach (lc, partition_list) {
|
||||
part = (Partition)lfirst(lc);
|
||||
fake_rel = partitionGetRelation(rel, part);
|
||||
rel_hash += gen_usertable_hash_sum(fake_rel);//调用函数计算分区关系哈希和并累加
|
||||
releaseDummyRelation(&fake_rel);//释放分区关系
|
||||
rel_hash += gen_usertable_hash_sum(fake_rel);
|
||||
releaseDummyRelation(&fake_rel);
|
||||
}
|
||||
releasePartitionList(rel, &partition_list, AccessShareLock);// 释放分区列表
|
||||
releasePartitionList(rel, &partition_list, AccessShareLock);
|
||||
}
|
||||
heap_close(rel, AccessShareLock);// 关闭用户表关系并释放共享锁
|
||||
return rel_hash;// 返回关系哈希和
|
||||
heap_close(rel, AccessShareLock);
|
||||
return rel_hash;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -122,28 +122,28 @@ static uint64 get_usertable_hash_sum(Oid relid)
|
|||
*/
|
||||
static uint64 get_histtable_hash_sum(Oid hist_oid)
|
||||
{
|
||||
uint64 rel_hash = 0;// 初始化哈希差值为零
|
||||
bool is_null = false;// 初始化标志位为假
|
||||
Relation hist_rel;// 初始化历史表关系
|
||||
uint64 rel_hash = 0;
|
||||
bool is_null = false;
|
||||
Relation hist_rel;
|
||||
TableScanDesc scan;
|
||||
HeapTuple tuple; // 用于存储历史表中的元组
|
||||
HeapTuple tuple;
|
||||
Snapshot snapshot = GetActiveSnapshot();
|
||||
|
||||
hist_rel = heap_open(hist_oid, AccessShareLock);// 打开历史表关系并获取共享锁
|
||||
scan = heap_beginscan(hist_rel, snapshot, 0, NULL); // 开始扫描历史表
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { // 遍历历史表中的元组
|
||||
hist_rel = heap_open(hist_oid, AccessShareLock);
|
||||
scan = heap_beginscan(hist_rel, snapshot, 0, NULL);
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {
|
||||
Datum value = heap_getattr(tuple, USERCHAIN_COLUMN_HASH_INS + 1, hist_rel->rd_att, &is_null);
|
||||
if (!is_null) {// 如果不是空值
|
||||
rel_hash += DatumGetUInt64(value);// 累加到哈希差值中
|
||||
if (!is_null) {
|
||||
rel_hash += DatumGetUInt64(value);
|
||||
}
|
||||
|
||||
value = heap_getattr(tuple, USERCHAIN_COLUMN_HASH_DEL + 1, hist_rel->rd_att, &is_null);
|
||||
if (!is_null) {// 如果不是空值
|
||||
rel_hash -= DatumGetUInt64(value); // 从哈希差值中减去
|
||||
if (!is_null) {
|
||||
rel_hash -= DatumGetUInt64(value);
|
||||
}
|
||||
}
|
||||
heap_endscan(scan);// 结束历史表扫描
|
||||
heap_close(hist_rel, AccessShareLock); // 关闭历史表关系并释放共享锁
|
||||
heap_endscan(scan);
|
||||
heap_close(hist_rel, AccessShareLock);
|
||||
return rel_hash;
|
||||
}
|
||||
|
||||
|
|
@ -171,23 +171,23 @@ static bool has_ledger_consistent_privilege(Oid relid, Oid namespaceId)
|
|||
*/
|
||||
bool is_hist_hash_identity(Oid relid, uint64 *res_hash)
|
||||
{
|
||||
uint64 user_hash_sum;// 用户表的哈希和
|
||||
uint64 hist_hash_sum;// 历史表的哈希和
|
||||
uint64 user_hash_sum;
|
||||
uint64 hist_hash_sum;
|
||||
char hist_name[NAMEDATALEN];
|
||||
char *rel_name = get_rel_name(relid);// 获取用户表的名称
|
||||
if (!get_hist_name(relid, rel_name, hist_name)) {// 获取历史表的名称
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("get hist table name failed."))); // 如果获取失败,则<EFBC8C><E58899><EFBFBD><EFBFBD><EFBFBD>错
|
||||
char *rel_name = get_rel_name(relid);
|
||||
if (!get_hist_name(relid, rel_name, hist_name)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("get hist table name failed.")));
|
||||
}
|
||||
Oid histoid = get_relname_relid(hist_name, PG_BLOCKCHAIN_NAMESPACE);// 获取历史表的 OID
|
||||
if (!OidIsValid(histoid)) { // 如果 OID 无效
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find hist table of \"%s\".", rel_name)));// 报错,找不到历史表
|
||||
Oid histoid = get_relname_relid(hist_name, PG_BLOCKCHAIN_NAMESPACE);
|
||||
if (!OidIsValid(histoid)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find hist table of \"%s\".", rel_name)));
|
||||
}
|
||||
|
||||
user_hash_sum = get_usertable_hash_sum(relid); // 获取用户表的哈希总和
|
||||
hist_hash_sum = get_histtable_hash_sum(histoid); // 获取历史表的哈希总和
|
||||
user_hash_sum = get_usertable_hash_sum(relid);
|
||||
hist_hash_sum = get_histtable_hash_sum(histoid);
|
||||
|
||||
*res_hash = hist_hash_sum; // 返回历史表的哈希和
|
||||
return user_hash_sum == hist_hash_sum; // 返回用户表哈希和和历史表哈希和是否相等的比较结果
|
||||
*res_hash = hist_hash_sum;
|
||||
return user_hash_sum == hist_hash_sum;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
|
|
@ -201,28 +201,28 @@ bool is_hist_hash_identity(Oid relid, uint64 *res_hash)
|
|||
*/
|
||||
static void StrategyFuncAnd(ParallelFunctionState* state)
|
||||
{
|
||||
TupleTableSlot* slot = NULL;// 创建 TupleTableSlot 用于存储结果
|
||||
bool result = true;// 初始化结果为true
|
||||
TupleTableSlot* slot = NULL;
|
||||
bool result = true;
|
||||
|
||||
Assert(state); // 断言 state 不为空
|
||||
Assert(state->tupstore);// 断言 tupstore 不为空
|
||||
Assert(state->tupdesc);// 断言 tupdesc 不为空
|
||||
slot = MakeSingleTupleTableSlot(state->tupdesc);// 创建单个 TupleTableSlot 用于存储数据
|
||||
Assert(state);
|
||||
Assert(state->tupstore);
|
||||
Assert(state->tupdesc);
|
||||
slot = MakeSingleTupleTableSlot(state->tupdesc);
|
||||
|
||||
while (true) {
|
||||
bool isnull = false;
|
||||
|
||||
if (!tuplestore_gettupleslot(state->tupstore, true, false, slot)) // 从 tupstore 获取下一个结果,如果没有更多结果则退出循环
|
||||
if (!tuplestore_gettupleslot(state->tupstore, true, false, slot))
|
||||
break;
|
||||
|
||||
if (!DatumGetBool(tableam_tslot_getattr(slot, 1, &isnull))) {// 从 TupleTableSlot 中获取属性值,如果为 false 则将结果设置为 false 并退出循环
|
||||
if (!DatumGetBool(tableam_tslot_getattr(slot, 1, &isnull))) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
(void)ExecClearTuple(slot); // 清空 TupleTableSlot
|
||||
(void)ExecClearTuple(slot);
|
||||
}
|
||||
|
||||
state->result = result;// 将结果存储在并行函数状态中
|
||||
state->result = result;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -235,30 +235,31 @@ static void StrategyFuncAnd(ParallelFunctionState* state)
|
|||
*/
|
||||
static void StrategyFuncUInt64Sum(ParallelFunctionState* state)
|
||||
{
|
||||
TupleTableSlot* slot = NULL;// 创建 TupleTableSlot 用于存储结果
|
||||
int64 result = 0;// 初始化结果为 0
|
||||
TupleTableSlot* slot = NULL;
|
||||
int64 result = 0;
|
||||
|
||||
Assert(state && state->tupstore && state->tupdesc);// 断言 state、tupstore 和 tupdesc 不为空
|
||||
slot = MakeSingleTupleTableSlot(state->tupdesc);// 创建单个 TupleTableSlot 用于存储数据
|
||||
Assert(state && state->tupstore && state->tupdesc);
|
||||
slot = MakeSingleTupleTableSlot(state->tupdesc);
|
||||
|
||||
while (true) {
|
||||
bool isnull = false;
|
||||
|
||||
if (!tuplestore_gettupleslot(state->tupstore, true, false, slot))// 从 tupstore 获取下一个结果,如果没有更多结果则退出循环
|
||||
if (!tuplestore_gettupleslot(state->tupstore, true, false, slot))
|
||||
break;
|
||||
|
||||
result += DatumGetUInt64(tableam_tslot_getattr(slot, 1, &isnull));// 从 TupleTableSlot 中获取属性值并累加到结果中
|
||||
ExecClearTuple(slot);// 清空 TupleTableSlot
|
||||
result += DatumGetUInt64(tableam_tslot_getattr(slot, 1, &isnull));
|
||||
ExecClearTuple(slot);
|
||||
}
|
||||
|
||||
state->result = result;// 将结果存储在并行函数状态中
|
||||
state->result = result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ledger_hist_check -- 检查用户表哈希和历史表哈希是否相等
|
||||
* ledger_hist_check -- check whether user table hash and history table hash are equal
|
||||
*
|
||||
* parameter1: 用户表名 [类型: text]
|
||||
* parameter2: 用户表的命名空间 [类型: text]
|
||||
* parameter1: user table name [type: text]
|
||||
* parameter2: namespace of user table [type: text]
|
||||
*/
|
||||
Datum ledger_hist_check(PG_FUNCTION_ARGS)
|
||||
{
|
||||
|
|
@ -268,32 +269,30 @@ Datum ledger_hist_check(PG_FUNCTION_ARGS)
|
|||
bool res = false;
|
||||
char *table_name;
|
||||
char *table_nsp;
|
||||
text *rel_nsp = PG_GETARG_TEXT_PP(0); // 获取第一个参数,用户表的命名空间
|
||||
text *rel_name = PG_GETARG_TEXT_PP(1); // 获取第二个参数,用户表名
|
||||
text *rel_nsp = PG_GETARG_TEXT_PP(0);
|
||||
text *rel_name = PG_GETARG_TEXT_PP(1);
|
||||
|
||||
table_nsp = text_to_cstring(rel_nsp);
|
||||
table_name = text_to_cstring(rel_name);
|
||||
nsp_oid = get_namespace_oid(table_nsp, false);
|
||||
relid = get_relname_relid(table_name, nsp_oid);
|
||||
ledger_usertable_check(relid, nsp_oid, table_name, table_nsp);
|
||||
|
||||
|
||||
table_nsp = text_to_cstring(rel_nsp);// 将文本参数转换为 C 字符串
|
||||
table_name = text_to_cstring(rel_name);// 将文本参数转换为 C 字符串
|
||||
nsp_oid = get_namespace_oid(table_nsp, false);// 获取命名空间的 OID
|
||||
relid = get_relname_relid(table_name, nsp_oid);// 获取用户表的 OID
|
||||
ledger_usertable_check(relid, nsp_oid, table_name, table_nsp);// 检查用户表是否存在
|
||||
|
||||
if (!has_ledger_consistent_privilege(relid, nsp_oid)) { // 检查权限
|
||||
if (!has_ledger_consistent_privilege(relid, nsp_oid)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
|
||||
}
|
||||
res = is_hist_hash_identity(relid, &res_hash);// 检查用户表哈希和历史表哈希是否相等
|
||||
res = is_hist_hash_identity(relid, &res_hash);
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
if (!IsConnFromCoord()) {// 如果不是从协调器节点调用
|
||||
StringInfoData buf;// 创建一个字符串缓冲区
|
||||
ParallelFunctionState* state = NULL;// 创建并行函数状态
|
||||
initStringInfo(&buf);// 初始化字符串缓冲区
|
||||
if (!IsConnFromCoord()) {
|
||||
StringInfoData buf;
|
||||
ParallelFunctionState* state = NULL;
|
||||
initStringInfo(&buf);
|
||||
appendStringInfo(&buf, "SELECT pg_catalog.ledger_hist_check('%s', '%s')", table_nsp, table_name);
|
||||
/* Get all hash diffs from DNs in distribute scenairo. */
|
||||
state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncAnd);
|
||||
res &= state->result;// 更新结果
|
||||
FreeParallelFunctionState(state);// 释放并行函数状态
|
||||
res &= state->result;
|
||||
FreeParallelFunctionState(state);
|
||||
}
|
||||
#endif
|
||||
return BoolGetDatum(res);
|
||||
|
|
@ -310,19 +309,19 @@ static uint64 get_gchain_relhash_sum(Oid relid)
|
|||
HeapTuple tuple = NULL;
|
||||
|
||||
/* scan the gs_global_chain catalog by relid */
|
||||
Relation gchain_rel = heap_open(GsGlobalChainRelationId, AccessShareLock);// 打开 gs_global_chain 关系
|
||||
Relation gchain_rel = heap_open(GsGlobalChainRelationId, AccessShareLock);
|
||||
Form_gs_global_chain rdata = NULL;
|
||||
TableScanDesc scan = heap_beginscan(gchain_rel, SnapshotNow, 0, NULL);// 创建表扫描描述符
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {// 循环遍历结果集
|
||||
rdata = (Form_gs_global_chain)GETSTRUCT(tuple);// 获取结果的数据结构
|
||||
if (rdata == NULL || rdata->relid != relid) {// 如果数据为空或者 OID 不匹配,则继续下一轮循环
|
||||
TableScanDesc scan = heap_beginscan(gchain_rel, SnapshotNow, 0, NULL);
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) {
|
||||
rdata = (Form_gs_global_chain)GETSTRUCT(tuple);
|
||||
if (rdata == NULL || rdata->relid != relid) {
|
||||
continue;
|
||||
}
|
||||
relhash += rdata->relhash;// 累加关系哈希值
|
||||
relhash += rdata->relhash;
|
||||
}
|
||||
heap_endscan(scan);// 结束扫描
|
||||
heap_close(gchain_rel, AccessShareLock);// 关闭关系
|
||||
return relhash;// 返回关系哈希和
|
||||
heap_endscan(scan);
|
||||
heap_close(gchain_rel, AccessShareLock);
|
||||
return relhash;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -337,39 +336,39 @@ static uint64 get_gchain_relhash_sum(Oid relid)
|
|||
Datum get_dn_hist_relhash(PG_FUNCTION_ARGS)
|
||||
{
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
DISTRIBUTED_FEATURE_NOT_SUPPORTED(); // 不支持分布式特性
|
||||
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
|
||||
return UInt64GetDatum(0);
|
||||
#else
|
||||
if (!IsConnFromCoord()) {// 如果不是从协调器节点调用
|
||||
if (!IsConnFromCoord()) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
|
||||
return UInt64GetDatum(0);
|
||||
}
|
||||
Oid user_relid;
|
||||
Oid nsp_oid;
|
||||
uint64 res_hash;
|
||||
text *rel_nsp = PG_GETARG_TEXT_PP(0);// 获取第一个参数,用户表的命名空间
|
||||
text *rel_name = PG_GETARG_TEXT_PP(1);// 获取第二个参数,用户表名
|
||||
text *rel_nsp = PG_GETARG_TEXT_PP(0);
|
||||
text *rel_name = PG_GETARG_TEXT_PP(1);
|
||||
char *table_name;
|
||||
char *table_nsp;
|
||||
|
||||
table_nsp = text_to_cstring(rel_nsp);// 将文本参数转换为 C 字符串
|
||||
table_name = text_to_cstring(rel_name);// 将文本参数转换为 C 字符串
|
||||
nsp_oid = get_namespace_oid(table_nsp, false);// 获取命名空间的 OID
|
||||
user_relid = get_relname_relid(table_name, nsp_oid);// 获取用户表的 OID
|
||||
ledger_usertable_check(user_relid, nsp_oid, table_name, table_nsp);// 检查用户表是否存在
|
||||
table_nsp = text_to_cstring(rel_nsp);
|
||||
table_name = text_to_cstring(rel_name);
|
||||
nsp_oid = get_namespace_oid(table_nsp, false);
|
||||
user_relid = get_relname_relid(table_name, nsp_oid);
|
||||
ledger_usertable_check(user_relid, nsp_oid, table_name, table_nsp);
|
||||
|
||||
if (!has_ledger_consistent_privilege(user_relid, nsp_oid)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
|
||||
}
|
||||
if (IS_PGXC_DATANODE) {// 如果当前节点是数据节点
|
||||
if (!is_hist_hash_identity(user_relid, &res_hash)) {// 检查用户表哈希和历史表哈希是否相等
|
||||
if (IS_PGXC_DATANODE) {
|
||||
if (!is_hist_hash_identity(user_relid, &res_hash)) {
|
||||
res_hash = 0;
|
||||
}
|
||||
} else {// 如果当前节点是协调器节点
|
||||
} else {
|
||||
res_hash = get_gchain_relhash_sum(user_relid);
|
||||
}
|
||||
|
||||
return UInt64GetDatum(res_hash);//返回结果
|
||||
return UInt64GetDatum(res_hash);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -382,7 +381,7 @@ Datum get_dn_hist_relhash(PG_FUNCTION_ARGS)
|
|||
Datum ledger_gchain_check(PG_FUNCTION_ARGS)
|
||||
{
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
if (!IS_PGXC_COORDINATOR || IsConnFromCoord()) {// 如果不是协调器节点或者是从协调器节点调用
|
||||
if (!IS_PGXC_COORDINATOR || IsConnFromCoord()) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
|
||||
}
|
||||
#endif
|
||||
|
|
@ -400,17 +399,16 @@ Datum ledger_gchain_check(PG_FUNCTION_ARGS)
|
|||
table_name = text_to_cstring(rel_name);
|
||||
nsp_oid = get_namespace_oid(table_nsp, false);
|
||||
user_relid = get_relname_relid(table_name, nsp_oid);
|
||||
//检查用户的一致性
|
||||
ledger_usertable_check(user_relid, nsp_oid, table_name, table_nsp);
|
||||
|
||||
if (!has_ledger_consistent_privilege(user_relid, nsp_oid)) {// 如果用户没有足够的权限,则报告权限不足的错误
|
||||
if (!has_ledger_consistent_privilege(user_relid, nsp_oid)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
|
||||
}
|
||||
// 检查历史表和用户表的哈希是否一致,并获取历史表的哈希值
|
||||
|
||||
res = is_hist_hash_identity(user_relid, &dn_hash);
|
||||
if (!res) {
|
||||
return BoolGetDatum(res);
|
||||
} // 获取全局链表中用户表的哈希值
|
||||
}
|
||||
cn_hash = get_gchain_relhash_sum(user_relid);
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
ParallelFunctionState* state = NULL;
|
||||
|
|
@ -430,8 +428,7 @@ Datum ledger_gchain_check(PG_FUNCTION_ARGS)
|
|||
#endif
|
||||
return BoolGetDatum(dn_hash == cn_hash);
|
||||
}
|
||||
//函数名为 ledger_gchain_check,用于检查用户表和历史表的哈希一致性,并返回一个表示一致性的布尔值。在多节点环境下(#ifdef ENABLE_MULTIPLE_NODES),首先检查当前节点是否为协调器节点,以及是否从协调器节点调用此函数。如果不是,报告权限不足的错误。然后,从函数参数中获取用户表的命名空间和名称,并将它们转换为 C 字符串,获取用户表的 OID。接着,调用 ledger_usertable_check 函数检查用户表的一致性,确保用户表存在且合法。如果用户没有足够的权限,再次报告权限不足的错误。使用 is_hist_hash_identity 函数检查历史表和用户表的哈希是否一致,并获取历史表的哈希值。
|
||||
//如果历史表和用户表的哈希不一致,函数会立即返回一个布尔值表示不一致。如果哈希一致,函数继续获取全局链表中用户表的哈希值,并在多节点环境下,从数据节点和协调器节点获取并累积哈希值。最后,函数返回一个布尔值,表示数据节点哈希与协调器哈希是否一致。
|
||||
|
||||
/*
|
||||
* repaire_hist_table_internal -- compare hash and repair hist table
|
||||
*
|
||||
|
|
@ -444,21 +441,21 @@ Datum ledger_gchain_check(PG_FUNCTION_ARGS)
|
|||
*/
|
||||
static uint64 repaire_hist_table_internal(Oid relid, char *rel_name, Oid nspoid, bool option)
|
||||
{
|
||||
uint64 rel_hash;// 声明用户表的哈希值
|
||||
uint64 hash_diff;// 声明哈希差值
|
||||
char histname[NAMEDATALEN];// 声明历史表的名称
|
||||
get_hist_name(relid, rel_name, histname, nspoid);// 获取历史表的名称
|
||||
uint64 rel_hash;
|
||||
uint64 hash_diff;
|
||||
char histname[NAMEDATALEN];
|
||||
get_hist_name(relid, rel_name, histname, nspoid);
|
||||
Oid histoid = get_relname_relid(histname, PG_BLOCKCHAIN_NAMESPACE);
|
||||
if (!OidIsValid(histoid)) {// 检查历史表是否有效
|
||||
if (!OidIsValid(histoid)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("The hist table of \"%s\" is not exist.", rel_name)));
|
||||
} // 如果历史表无效,则报错
|
||||
rel_hash = get_usertable_hash_sum(relid); // 获取用户表的哈希值
|
||||
hash_diff = rel_hash - get_histtable_hash_sum(histoid);// 计算哈希差值(用户表哈希值减去历史表哈希值)
|
||||
if (hash_diff != 0) {// 如果哈希差值不为零
|
||||
/* Do hist table repair. */
|
||||
hist_table_record_internal(histoid, &hash_diff, NULL); // 调用函数修复历史表
|
||||
}
|
||||
return option ? rel_hash : hash_diff; // 根据选项返回用户表哈希值或哈希差值
|
||||
rel_hash = get_usertable_hash_sum(relid);
|
||||
hash_diff = rel_hash - get_histtable_hash_sum(histoid);
|
||||
if (hash_diff != 0) {
|
||||
/* Do hist table repair. */
|
||||
hist_table_record_internal(histoid, &hash_diff, NULL);
|
||||
}
|
||||
return option ? rel_hash : hash_diff;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -467,14 +464,11 @@ static uint64 repaire_hist_table_internal(Oid relid, char *rel_name, Oid nspoid,
|
|||
* parameter1: user table name [type: text]
|
||||
* parameter2: namespace of user table [type: text]
|
||||
*/
|
||||
// PostgreSQL 函数,用于修复用户表的历史记录和全局链表中的哈希差异。
|
||||
// 在执行任何操作之前,首先检查当前用户是否具有足够的权限。
|
||||
Datum ledger_hist_repair(PG_FUNCTION_ARGS)
|
||||
{// 如果当前用户既不是超级用户也不是审计管理员,则拒绝执行。
|
||||
{
|
||||
if (!isRelSuperuser() && !isAuditadmin(GetUserId())) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
|
||||
}
|
||||
// 从函数参数中获取用户表的命名空间和名称。
|
||||
text *rel_nsp = PG_GETARG_TEXT_PP(0);
|
||||
text *rel_name = PG_GETARG_TEXT_PP(1);
|
||||
char *table_name;
|
||||
|
|
@ -482,47 +476,38 @@ Datum ledger_hist_repair(PG_FUNCTION_ARGS)
|
|||
Oid relid;
|
||||
Oid nspoid;
|
||||
uint64 delta = 0;
|
||||
// 将用户表的命名空间和名称转换为 C 字符串。
|
||||
|
||||
table_nsp = text_to_cstring(rel_nsp);
|
||||
table_name = text_to_cstring(rel_name);
|
||||
// 获取用户表的命名空间的 OID。
|
||||
nspoid = get_namespace_oid(table_nsp, false);
|
||||
// 获取用户表的 OID。
|
||||
relid = get_relname_relid(table_name, nspoid);
|
||||
// 检查用户表的一致性。
|
||||
ledger_usertable_check(relid, nspoid, table_name, table_nsp);
|
||||
|
||||
/*
|
||||
* Repair hist table of current datanode. Get hash sum of hist
|
||||
* table and rel_hash of usertable, append the difference to hist table.
|
||||
*/
|
||||
// 如果当前节点是数据节点或单节点
|
||||
if (g_instance.role == VDATANODE || g_instance.role == VSINGLENODE) { // 调用 repaire_hist_table_internal 函数以获取并追加哈希差异。
|
||||
if (g_instance.role == VDATANODE || g_instance.role == VSINGLENODE) {
|
||||
delta = repaire_hist_table_internal(relid, table_name, nspoid, false);
|
||||
}
|
||||
|
||||
// 如果当前节点是协调器或单节点
|
||||
if (g_instance.role == VCOORDINATOR || g_instance.role == VSINGLENODE) {
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
ParallelFunctionState* state = NULL;
|
||||
StringInfoData buf;
|
||||
initStringInfo(&buf);
|
||||
// 构建一个 SQL 查询字符串,以获取哈希差异。
|
||||
appendStringInfo(&buf, "SELECT pg_catalog.ledger_hist_repair('%s', '%s')", table_nsp, table_name);
|
||||
/* Get all hash diffs from DNs in distribute scenairo. */
|
||||
// 调用 RemoteFunctionResultHandler 函数以获取哈希差异。
|
||||
state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncUInt64Sum);
|
||||
// 将哈希差异累积到 delta 变量中。
|
||||
delta += state->result;
|
||||
// 释放并清理远程函数状态。
|
||||
|
||||
FreeParallelFunctionState(state);
|
||||
#endif
|
||||
// 如果 delta 不为零,向全局链表追加修复信息。
|
||||
if (delta != 0) {
|
||||
ledger_gchain_append(relid, "HIST REPAIR.", delta);
|
||||
}
|
||||
}
|
||||
// 返回 delta,表示哈希差异或修复结果。
|
||||
|
||||
return UInt64GetDatum(delta);
|
||||
}
|
||||
|
||||
|
|
@ -532,14 +517,11 @@ Datum ledger_hist_repair(PG_FUNCTION_ARGS)
|
|||
* parameter1: user table name [type: text]
|
||||
* parameter2: namespace of user table [type: text]
|
||||
*/
|
||||
// 在执行任何操作之前,首先检查当前用户是否具有足够的权限。
|
||||
Datum ledger_gchain_repair(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// 如果当前用户既不是超级用户也不是审计管理员,则拒绝执行。
|
||||
if (!isRelSuperuser() && !isAuditadmin(GetUserId())) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
|
||||
}
|
||||
// 从函数参数中获取用户表的命名空间和名称。
|
||||
text *rel_nsp = PG_GETARG_TEXT_PP(0);
|
||||
text *rel_name = PG_GETARG_TEXT_PP(1);
|
||||
char *table_name;
|
||||
|
|
@ -547,70 +529,54 @@ Datum ledger_gchain_repair(PG_FUNCTION_ARGS)
|
|||
Oid relid;
|
||||
Oid nspoid;
|
||||
uint64 dn_hash = 0;
|
||||
// 将用户表的命名空间和名称转换为 C 字符串。
|
||||
|
||||
table_nsp = text_to_cstring(rel_nsp);
|
||||
table_name = text_to_cstring(rel_name);
|
||||
// 获取用户表的命名空间的 OID。
|
||||
nspoid = get_namespace_oid(table_nsp, false);
|
||||
// 获取用户表的 OID。
|
||||
relid = get_relname_relid(table_name, nspoid);
|
||||
// 检查用户表的一致性。
|
||||
ledger_usertable_check(relid, nspoid, table_name, table_nsp);
|
||||
|
||||
/*
|
||||
* Repair hist table of current datanode. Get hash sum of hist
|
||||
* table and rel_hash of usertable, append the difference to hist table.
|
||||
*/
|
||||
// 如果当前节点是数据节点或单节点
|
||||
if (g_instance.role == VDATANODE || g_instance.role == VSINGLENODE) {//调用 repaire_hist_table_internal 函数来获取并追加哈希差异
|
||||
if (g_instance.role == VDATANODE || g_instance.role == VSINGLENODE) {
|
||||
dn_hash = repaire_hist_table_internal(relid, table_name, nspoid, true);
|
||||
}
|
||||
|
||||
uint64 rel_hash = dn_hash;
|
||||
uint64 cn_hash = 0;
|
||||
// 如果当前节点是协调器或单节点
|
||||
if (g_instance.role == VCOORDINATOR || g_instance.role == VSINGLENODE) {
|
||||
uint64 delta = 0;
|
||||
cn_hash = get_gchain_relhash_sum(relid);
|
||||
// 如果不是从协调器节点发起的连接
|
||||
if (!IsConnFromCoord()) {
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
/*
|
||||
* CN accumulate all gchain cn_hash from all CNs, get all dn_hash from all DNs.
|
||||
* Then compare cn_hash and dn_hash, and fill up delta hash to gchain for repairing.
|
||||
*/
|
||||
arallelFunctionState* state = NULL;
|
||||
ParallelFunctionState* state = NULL;
|
||||
StringInfoData buf;
|
||||
initStringInfo(&buf);
|
||||
// 构建一个 SQL 查询字符串,用于获取哈希差异
|
||||
appendStringInfo(&buf, "SELECT pg_catalog.ledger_gchain_repair('%s', '%s')", table_nsp, table_name);
|
||||
/* Get and accumulate all dn_hash from all DNs. */
|
||||
state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncUInt64Sum);
|
||||
// 将数据节点哈希值累积到 dn_hash 变量中。
|
||||
dn_hash += state->result;
|
||||
// 释放并清理远程函数状态
|
||||
FreeParallelFunctionState(state);
|
||||
// 如果存在其他协调器节点
|
||||
if (GetAllCoordNodes() != NIL) {
|
||||
/* Get and accumulate all cn_hash from all CNs. */
|
||||
// // 调用远程函数 RemoteFunctionResultHandler 来获取哈希差异
|
||||
state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncUInt64Sum, true, EXEC_ON_COORDS);// 将协调器节点哈希值累积到 cn_hash 变量中。
|
||||
cn_hash += state->result; // 释放并清理远程函数状态
|
||||
state = RemoteFunctionResultHandler(buf.data, NULL, StrategyFuncUInt64Sum, true, EXEC_ON_COORDS);
|
||||
cn_hash += state->result;
|
||||
FreeParallelFunctionState(state);
|
||||
}
|
||||
#endif
|
||||
// 计算哈希差异
|
||||
delta = dn_hash - cn_hash;// 如果 delta 不为零,向全局链表追加修复信息
|
||||
delta = dn_hash - cn_hash;
|
||||
if (delta != 0) {
|
||||
ledger_gchain_append(relid, "GCHAIN REPAIR.", delta);
|
||||
}
|
||||
}
|
||||
// 将修复后的哈希值设置为协调器哈希值。
|
||||
rel_hash = cn_hash;
|
||||
}
|
||||
// 返回 delta,表示哈希差异或修复结果
|
||||
|
||||
return UInt64GetDatum(rel_hash);
|
||||
}
|
||||
//这两个函数用于修复用户表和历史表之间的哈希差异,以及修复全局链表中的哈希差异。在函数开头,首先检查当前用户是否是超级用户或审计管理员,如果不是,报告权限不足的错误。然后,从函数参数中获取用户表的命名空间和名称,并将它们转换为 C 字符串,获取用户表的 OID。调用 ledger_usertable_check 函数来确保用户表存在且一致性。
|
||||
//接下来,根据当前节点的角色,选择是否修复历史表。在数据节点或单节点上,调用 repaire_hist_table_internal 函数来获取并追加哈希差异。如果当前节点是协调器或单节点,会进行更多操作。在多节点环境下,会调用远程函数来获取数据节点的哈希差异,并将其累积到 delta 变量中。如果 delta 不为零,会向全局链表追加修复信息。
|
||||
//最后,根据修复的结果,函数返回一个表示哈希差异或修复结果的 UInt64 数据类型。
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include "gs_ledger/ledger_utils.h"
|
||||
#include "catalog/gs_global_chain.h"
|
||||
// 定义全局变量
|
||||
|
||||
static pg_atomic_uint64 g_blocknum = 0;
|
||||
static HTAB *g_recnum_cache = NULL;
|
||||
|
||||
|
|
@ -33,7 +33,6 @@ static HTAB *g_recnum_cache = NULL;
|
|||
*
|
||||
* Note:If gchain is empty, next blocknum will start from 0.
|
||||
*/
|
||||
// 重新加载下一个 g_blocknum,从 gchain 中加载
|
||||
static uint32 reload_next_g_blocknum()
|
||||
{
|
||||
Relation gchain_rel = NULL;
|
||||
|
|
@ -42,11 +41,9 @@ static uint32 reload_next_g_blocknum()
|
|||
uint32 blocknum;
|
||||
uint32 max_num = 0;
|
||||
bool isnull = false;
|
||||
// 打开 gs_global_chain 表
|
||||
|
||||
gchain_rel = heap_open(GsGlobalChainRelationId, RowExclusiveLock);
|
||||
// 创建表扫描器
|
||||
scan = heap_beginscan(gchain_rel, SnapshotAny, 0, NULL);
|
||||
// 遍历表格,查找最大的 blocknum
|
||||
while ((tup = heap_getnext(scan, BackwardScanDirection)) != NULL) {
|
||||
blocknum = DatumGetUInt32(heap_getattr(tup, Anum_gs_global_chain_blocknum,
|
||||
RelationGetDescr(gchain_rel), &isnull));
|
||||
|
|
@ -60,20 +57,18 @@ static uint32 reload_next_g_blocknum()
|
|||
heap_close(gchain_rel, RowExclusiveLock);
|
||||
return max_num;
|
||||
}
|
||||
//函数static uint32 reload_next_g_blocknum():重新加载下一个 g_blocknum,从 gs_global_chain 表中加载。打开 gs_global_chain 表,查找最大的 blocknum 值。
|
||||
|
||||
/*
|
||||
* get_next_g_blocknum -- get next blocknum for gchain record.
|
||||
*
|
||||
* Note:provide next blocknum and auto increment itself.
|
||||
*/
|
||||
// 获取下一个 g_blocknum
|
||||
uint64 get_next_g_blocknum()
|
||||
{
|
||||
uint64 res = 0;
|
||||
if (g_blocknum == 0) {
|
||||
LWLockAcquire(GlobalPrevHashLock, LW_EXCLUSIVE);
|
||||
if (g_blocknum == 0) {
|
||||
// 原子操作:增加 g_blocknum 的值
|
||||
pg_atomic_fetch_add_u64(&g_blocknum, 1);
|
||||
int cur_num = reload_next_g_blocknum();
|
||||
pg_atomic_fetch_add_u64(&g_blocknum, cur_num);
|
||||
|
|
@ -85,13 +80,12 @@ uint64 get_next_g_blocknum()
|
|||
LWLockRelease(GlobalPrevHashLock);
|
||||
return res;
|
||||
}
|
||||
//函数uint64 get_next_g_blocknum()功能:获取下一个 g_blocknum。 如果 g_blocknum 为零,获取锁以确保只有一个线程执行加载和更新。使用原子操作增加 g_blocknum 的值,并获取最大的 blocknum 值。释放锁并返回下一个 g_blocknum 值。
|
||||
// 重置 g_blocknum
|
||||
|
||||
void reset_g_blocknum()
|
||||
{
|
||||
g_blocknum = 0;
|
||||
}
|
||||
//重置 g_blocknum 为零,用于重新开始计数。
|
||||
|
||||
/*
|
||||
* reload_g_rec_num -- load next rec_num from hist table.
|
||||
*
|
||||
|
|
@ -99,7 +93,6 @@ void reset_g_blocknum()
|
|||
*
|
||||
* Note:return next rec_num and auto increment.
|
||||
*/
|
||||
// 重新加载下一个 rec_num,从 hist 表中加载
|
||||
uint64 reload_g_rec_num(Oid histoid)
|
||||
{
|
||||
if (!OidIsValid(histoid)) {
|
||||
|
|
@ -113,10 +106,9 @@ uint64 reload_g_rec_num(Oid histoid)
|
|||
bool hist_empty = true;
|
||||
bool isnull = false;
|
||||
bool found;
|
||||
// 打开 hist 表
|
||||
|
||||
histRelation = heap_open(histoid, AccessShareLock);
|
||||
scan = heap_beginscan(histRelation, SnapshotNow, 0, NULL);
|
||||
// 遍历表格,查找最大的 rec_num
|
||||
while ((tup = heap_getnext(scan, BackwardScanDirection)) != NULL) {
|
||||
rec_num = DatumGetUInt64(heap_getattr(tup, 1, RelationGetDescr(histRelation), &isnull));
|
||||
if (rec_num >= max_rec_num) {
|
||||
|
|
@ -135,13 +127,12 @@ uint64 reload_g_rec_num(Oid histoid)
|
|||
item->rec_num = rec_num + 1;
|
||||
return rec_num;
|
||||
}
|
||||
//重新加载下一个记录号 (rec_num),从历史表 (hist) 中加载。打开历史表,查找最大的 rec_num 值。
|
||||
|
||||
/*
|
||||
* get_next_recnum -- provide next rec_num.
|
||||
*
|
||||
* histoid: hist table oid.
|
||||
*/
|
||||
// 获取下一个 rec_num
|
||||
uint64 get_next_recnum(Oid histoid)
|
||||
{
|
||||
if (g_recnum_cache == NULL) {
|
||||
|
|
@ -179,8 +170,7 @@ uint64 get_next_recnum(Oid histoid)
|
|||
LWLockRelease(BlockchainVersionLock);
|
||||
return res;
|
||||
}
|
||||
//函数uint64 get_next_recnum(Oid histoid)功能:获取下一个记录号 (rec_num)。如果记录号缓存 (g_recnum_cache) 不存在,创建并初始化哈希表。使用原子操作获取下一个 rec_num 值,如果在缓存中找到则增加并返回。
|
||||
// 移除 hist recnum 缓存
|
||||
|
||||
bool remove_hist_recnum_cache(Oid histoid)
|
||||
{
|
||||
if (!OidIsValid(histoid) || g_recnum_cache == NULL) {
|
||||
|
|
@ -190,23 +180,22 @@ bool remove_hist_recnum_cache(Oid histoid)
|
|||
|
||||
return true;
|
||||
}
|
||||
//从历史记录号缓存中移除指定历史表的记录号缓存。
|
||||
|
||||
/*
|
||||
* lock_gchain_cache -- lock g_blocknum cache with lock mode.
|
||||
*
|
||||
* mode: lockmode
|
||||
*/
|
||||
// 锁定 g_blocknum 缓存
|
||||
void lock_gchain_cache(LWLockMode mode)
|
||||
{
|
||||
LWLockAcquire(GlobalPrevHashLock, mode);
|
||||
}
|
||||
//锁定 g_blocknum 缓存以提供不同的锁模式。
|
||||
|
||||
/*
|
||||
* release_gchain_cache -- release g_blocknum cache.
|
||||
*/
|
||||
void release_gchain_cache()
|
||||
{// 释放全局链缓存的轻量级锁
|
||||
{
|
||||
LWLockRelease(GlobalPrevHashLock);
|
||||
}
|
||||
|
||||
|
|
@ -214,7 +203,7 @@ void release_gchain_cache()
|
|||
* lock_hist_hash_cache -- load hist cache.
|
||||
*/
|
||||
void lock_hist_hash_cache(LWLockMode mode)
|
||||
{ // 获取 BlockchainVersionLock 的轻量级锁,使用指定的锁模式
|
||||
{
|
||||
LWLockAcquire(BlockchainVersionLock, mode);
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +211,7 @@ void lock_hist_hash_cache(LWLockMode mode)
|
|||
* release_hist_hash_cache -- release hist cache.
|
||||
*/
|
||||
void release_hist_hash_cache()
|
||||
{ // 释放 BlockchainVersionLock 的轻量级锁
|
||||
{
|
||||
LWLockRelease(BlockchainVersionLock);
|
||||
}
|
||||
|
||||
|
|
@ -237,10 +226,8 @@ Oid get_target_query_relid(List* rte_list, int resultRelation)
|
|||
Oid relid = InvalidOid;
|
||||
|
||||
if (resultRelation > 0) {
|
||||
// 从 rte_list 中获取目标关系的范围表条目
|
||||
RangeTblEntry *rte = (RangeTblEntry *)list_nth(rte_list, resultRelation - 1);
|
||||
if (rte->relkind == RELKIND_RELATION) {
|
||||
// 如果范围表条目表示一个表,获取其 OID
|
||||
relid = rte->relid;
|
||||
}
|
||||
}
|
||||
|
|
@ -263,13 +250,11 @@ bool is_ledger_usertable(Oid relid)
|
|||
|
||||
Oid nspid = get_rel_namespace(relid);
|
||||
char relkind = get_rel_relkind(relid);
|
||||
// 只有表才有其用户链表
|
||||
/* only table has its user chain table */
|
||||
if (relkind != RELKIND_RELATION) {
|
||||
return false;
|
||||
}
|
||||
/* check table belong to blockchain schema */
|
||||
// 检查表是否属于区块链模式
|
||||
return IsLedgerNameSpace(nspid);
|
||||
}
|
||||
|
||||
|
|
@ -285,7 +270,7 @@ uint64 hash_combiner(List *relhash_list)
|
|||
if (relhash_list == NIL) {
|
||||
return relhash_sum;
|
||||
}
|
||||
// 遍历 relhash_list 中的每个哈希值,将它们相加
|
||||
|
||||
foreach (lc, relhash_list) {
|
||||
Datum *value = (Datum *)lfirst(lc);
|
||||
relhash_sum += DatumGetUInt64(value);
|
||||
|
|
@ -308,7 +293,6 @@ bool is_ledger_hist_table(Oid relid)
|
|||
Oid relnsp = get_rel_namespace(relid);
|
||||
char relkind = get_rel_relkind(relid);
|
||||
/* check namespace oid of relation to verify hist table. */
|
||||
// 检查关系的命名空间 OID 以验证是否为历史表
|
||||
return relnsp == PG_BLOCKCHAIN_NAMESPACE && relkind == RELKIND_RELATION;
|
||||
}
|
||||
|
||||
|
|
@ -335,11 +319,9 @@ bool is_ledger_related_rel(Relation rel)
|
|||
bool ledger_usertable_check(Oid relid, Oid nspoid, const char *tablename, const char *tablensp)
|
||||
{
|
||||
if (!OidIsValid(relid)) {
|
||||
// 报告错误,指定的表不存在
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("table %s.%s not exists.", tablensp, tablename)));
|
||||
}
|
||||
if (!IsLedgerNameSpace(nspoid)) {
|
||||
// 报告错误,指定的表不是账本用户表
|
||||
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("table %s.%s is not ledger user table.", tablensp, tablename)));
|
||||
}
|
||||
|
|
@ -353,30 +335,30 @@ bool ledger_usertable_check(Oid relid, Oid nspoid, const char *tablename, const
|
|||
*/
|
||||
List *namespace_get_depended_relid(Oid nspid)
|
||||
{
|
||||
List *relid_list = NIL;// 创建一个空列表用于存储关系的OID。
|
||||
Relation pg_class_rel = NULL;// 创建一个指向系统表 pg_class 的关系
|
||||
ScanKeyData skey[1];// 创建扫描键的结构体数组,用于扫描 pg_class 表。
|
||||
SysScanDesc sysscan; // 创建系统扫描描述符。
|
||||
HeapTuple tuple;// 创建一个堆元组变量,用于存储扫描结果。
|
||||
Oid tupid = InvalidOid;// 初始化一个无效的 OID 用于后续赋值。
|
||||
// 初始化扫描键,用于检索具有特定命名空间 OID 的关系。
|
||||
List *relid_list = NIL;
|
||||
Relation pg_class_rel = NULL;
|
||||
ScanKeyData skey[1];
|
||||
SysScanDesc sysscan;
|
||||
HeapTuple tuple;
|
||||
Oid tupid = InvalidOid;
|
||||
|
||||
ScanKeyInit(&skey[0], Anum_pg_class_relnamespace, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(nspid));
|
||||
pg_class_rel = heap_open(RelationRelationId, AccessShareLock);
|
||||
sysscan = systable_beginscan(pg_class_rel, ClassNameNspIndexId, true, SnapshotNow, 1, skey);
|
||||
// 遍历扫描结果,将每个关系的 OID 添加到 relid_list 列表中。
|
||||
|
||||
while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) {
|
||||
Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tuple); // 获取堆元组的数据结构。
|
||||
if (reltup->relkind == RELKIND_RELATION) {// 如果关系的类型是 RELKIND_RELATION(表)。
|
||||
tupid = HeapTupleGetOid(tuple);// 获取关系的 OID。
|
||||
relid_list = lappend_oid(relid_list, tupid);// 将关系的 OID 添加到列表中。
|
||||
Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tuple);
|
||||
if (reltup->relkind == RELKIND_RELATION) {
|
||||
tupid = HeapTupleGetOid(tuple);
|
||||
relid_list = lappend_oid(relid_list, tupid);
|
||||
}
|
||||
}
|
||||
|
||||
systable_endscan(sysscan);// 结束系统表扫描。
|
||||
heap_close(pg_class_rel, AccessShareLock); // 关闭 pg_class 表,释放共享锁。
|
||||
return relid_list; // 返回包含关系 OID 的列表。
|
||||
systable_endscan(sysscan);
|
||||
heap_close(pg_class_rel, AccessShareLock);
|
||||
return relid_list;
|
||||
}
|
||||
//用于检索指定命名空间下的所有关系的 OID,然后将它们存储在一个列表中并返回。
|
||||
|
||||
/*
|
||||
* get_ledger_msg_hash -- extract relhash from response message.
|
||||
*
|
||||
|
|
@ -397,7 +379,6 @@ bool get_ledger_msg_hash(char *message, uint64 *hash, int *msg_len)
|
|||
return false;
|
||||
}
|
||||
/* match space times. */
|
||||
// 匹配空格次数
|
||||
for (size_t i = 0; i < lengthof(hash_offset_map); i++) {
|
||||
if (strncmp(message, hash_offset_map[i].name, 6) == 0) { /* 6: string length of INSERT/UPDATE/DELETE */
|
||||
hash_offset = hash_offset_map[i].hash_offset;
|
||||
|
|
@ -418,11 +399,8 @@ bool get_ledger_msg_hash(char *message, uint64 *hash, int *msg_len)
|
|||
if (hash_offset == 0) {
|
||||
size_t remain_len = len - pos;
|
||||
if (remain_len > 0) {
|
||||
// 将哈希值从 message 中提取出来
|
||||
*hash = strtoul(message + pos, NULL, 10); /* 10: Decimal */
|
||||
// 移除附加的哈希字符串
|
||||
message[pos - 1] = '\0'; /* remove appended hash string. */
|
||||
// 更新消息的长度
|
||||
*msg_len = *msg_len - remain_len - 1;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -448,26 +426,23 @@ bool get_ledger_msg_hash(char *message, uint64 *hash, int *msg_len)
|
|||
*/
|
||||
bool get_hist_name(Oid relid, const char *rel_name, char *hist_name, Oid nsp_oid, const char *nsp_name)
|
||||
{
|
||||
errno_t rc;// 用于存储 snprintf_s 函数的返回值
|
||||
|
||||
errno_t rc;
|
||||
if (!OidIsValid(relid) || rel_name == NULL) {
|
||||
return false;// 如果表的 OID 无效或表名为空,则返回 false
|
||||
return false;
|
||||
}
|
||||
nsp_oid = OidIsValid(nsp_oid) ? nsp_oid : get_rel_namespace(relid);// 如果模式 OID 有效,则使用给定值;否则获取表的模式 OID
|
||||
nsp_name = (nsp_name == NULL) ? get_namespace_name(nsp_oid) : nsp_name;// 如果模式名为空,则获取模式名
|
||||
int part_hist_name_len = strlen(rel_name) + strlen(nsp_name) + 1;// 计算部分历史表名的长度(表名 + 模式名 + 下划线)
|
||||
nsp_oid = OidIsValid(nsp_oid) ? nsp_oid : get_rel_namespace(relid);
|
||||
nsp_name = (nsp_name == NULL) ? get_namespace_name(nsp_oid) : nsp_name;
|
||||
int part_hist_name_len = strlen(rel_name) + strlen(nsp_name) + 1;
|
||||
if (part_hist_name_len + strlen("_hist") >= NAMEDATALEN) {
|
||||
// 如果完整历史表名(部分历史表名 + "_hist")的长度超过了 NAMEDATALEN,就使用 nsp_oid 和 relid 创建历史表名
|
||||
rc = snprintf_s(hist_name, NAMEDATALEN, NAMEDATALEN - 1, "%d_%d_hist", nsp_oid, relid);
|
||||
securec_check_ss(rc, "", "");// 安全地检查 snprintf_s 的返回值
|
||||
securec_check_ss(rc, "", "");
|
||||
} else {
|
||||
// 否则,使用 nsp_name 和 rel_name 创建历史表名
|
||||
rc = snprintf_s(hist_name, NAMEDATALEN, NAMEDATALEN - 1, "%s_%s_hist", nsp_name, rel_name);
|
||||
securec_check_ss(rc, "", "");// 安全地检查 snprintf_s 的返回值
|
||||
securec_check_ss(rc, "", "");
|
||||
}
|
||||
return true; // 返回 true 表示成功生成历史表名
|
||||
return true;
|
||||
}
|
||||
//此函数的目的是生成历史表的名称。如果给定了表的 OID、表名、模式 OID、模式名,它将使用这些信息来创建历史表的名称。如果未提供模式 OID 或模式名,它将根据表的 OID 获取模式信息。在生成历史表名时,它会检查表名、模式名的长度以及是否需要在历史表名中使用表的 OID 和模式 OID。最后,它将生成的历史表名存储在 hist_name 参数中,并返回 true 表示成功。
|
||||
|
||||
/*
|
||||
* querydesc_contains_ledger_usertable -- check querydesc result relation.
|
||||
*
|
||||
|
|
@ -476,39 +451,32 @@ bool get_hist_name(Oid relid, const char *rel_name, char *hist_name, Oid nsp_oid
|
|||
bool querydesc_contains_ledger_usertable(QueryDesc *query_desc)
|
||||
{
|
||||
if (query_desc == NULL || query_desc->estate == NULL) {
|
||||
// 如果传入的 QueryDesc 为空或者其 estate 为空,返回 false
|
||||
return false;
|
||||
}
|
||||
EState *estate = query_desc->estate;
|
||||
int relnum = estate->es_num_result_relations;
|
||||
if (relnum == 0 || estate->es_result_relations == NULL) {
|
||||
// 如果结果关系数为 0 或者结果关系数组为空,返回 false
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < relnum; ++i) {
|
||||
// 遍历结果关系数组
|
||||
if (estate->es_result_relations[i].ri_RelationDesc->rd_isblockchain) {
|
||||
// 如果结果关系的描述符中包含 rd_isblockchain 为真,表示是账本用户表,返回 true
|
||||
return true;
|
||||
}
|
||||
} // 没有找到账本用户表,返回 false
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//功能:检查查询描述中是否包含账本用户表。
|
||||
//描述:该函数接受一个查询描述结构体指针,检查其内部的执行状态是否包含账本用户表。如果包含,则返回 true,否则返回 false
|
||||
|
||||
/*
|
||||
* ledger_check_switch_schema -- check two schema has same blockchain option.
|
||||
*/
|
||||
void ledger_check_switch_schema(Oid old_nsp, Oid new_nsp)
|
||||
{
|
||||
if (IsLedgerNameSpace(old_nsp) != IsLedgerNameSpace(new_nsp)) {
|
||||
// 如果旧模式和新模式的区块链选项不一致,报错
|
||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("Unsupport to switch schema of a table between ledger schema and normal schema.")));
|
||||
}
|
||||
}
|
||||
// 功能:检查模式切换是否允许。
|
||||
//描述:该函数接受两个模式的 OID(旧模式和新模式),然后检查它们是否都属于账本模式或都不属于账本模式。如果不一致,抛出错误。
|
||||
|
||||
/*
|
||||
* is_ledger_rowstore -- check withOpt of CreateStmt.
|
||||
*
|
||||
|
|
@ -524,15 +492,12 @@ bool is_ledger_rowstore(List *defList)
|
|||
DefElem* def = (DefElem*)lfirst(lc);
|
||||
if (pg_strcasecmp(def->defname, "orientation") == 0 &&
|
||||
pg_strcasecmp(defGetString(def), ORIENTATION_ROW) != 0) {
|
||||
// 如果 ORIENTATION 选项存在且值不为 ROW,返回 false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 未找到 ORIENTATION 选项或其值为 ROW,返回 true
|
||||
return true;
|
||||
}
|
||||
//功能:检查表的存储选项是否为 ROW。
|
||||
//描述:该函数接受一个表的定义选项列表,检查其中是否包含存储选项 "orientation",并且其值是否为 "ROW"。如果是,返回 true,否则返回 false。
|
||||
|
||||
bool is_ledger_hashbucketstore(List *defList)
|
||||
{
|
||||
ListCell *lc = NULL;
|
||||
|
|
@ -542,15 +507,11 @@ bool is_ledger_hashbucketstore(List *defList)
|
|||
if (pg_strcasecmp(def->defname, "bucketcnt") == 0 ||
|
||||
(pg_strcasecmp(def->defname, "hashbucket") == 0 &&
|
||||
defGetBoolean(def))) {
|
||||
// 如果选项中包含 bucketcnt 或者 hashbucket 为真,表示使用哈希分桶存储,返回 true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 未找到哈希分桶存储的选项,返回 false
|
||||
return false;
|
||||
}
|
||||
//功能:检查表的存储选项是否为哈希分桶存储。
|
||||
//描述:该函数接受一个表的定义选项列表,检查其中是否包含 "bucketcnt" 或 "hashbucket" 存储选项,并且 "hashbucket" 的值为真。如果是哈希分桶存储,返回 true,否则返回 false。
|
||||
/*
|
||||
* check_ledger_attrs_support -- check attrs is ledger supported.
|
||||
*
|
||||
|
|
@ -562,7 +523,6 @@ bool is_ledger_hashbucketstore(List *defList)
|
|||
void check_ledger_attrs_support(List *attrs)
|
||||
{
|
||||
if (attrs == NIL) {
|
||||
// 如果属性列表为空,直接返回
|
||||
return;
|
||||
}
|
||||
ListCell *lc = NULL;
|
||||
|
|
@ -574,7 +534,6 @@ void check_ledger_attrs_support(List *attrs)
|
|||
}
|
||||
Oid typid = colDef->typname->typeOid;
|
||||
if (!OidIsValid(typid)) {
|
||||
// 如果列的类型无效,尝试获取类型信息
|
||||
Type ctype = typenameType(NULL, colDef->typname, NULL);
|
||||
if (ctype != NULL) {
|
||||
typid = typeTypeId(ctype);
|
||||
|
|
@ -582,7 +541,6 @@ void check_ledger_attrs_support(List *attrs)
|
|||
}
|
||||
}
|
||||
switch (typid) {
|
||||
// 支持的列类型
|
||||
case INT8OID:
|
||||
case INT1OID:
|
||||
case INT2OID:
|
||||
|
|
@ -617,7 +575,6 @@ void check_ledger_attrs_support(List *attrs)
|
|||
case NUMERICOID:
|
||||
case UUIDOID:
|
||||
break;
|
||||
// 不支持的列类型,报错
|
||||
default:
|
||||
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("Unsupport column type \"%s\" of ledger user table.",
|
||||
|
|
@ -625,5 +582,3 @@ void check_ledger_attrs_support(List *attrs)
|
|||
}
|
||||
}
|
||||
}
|
||||
//功能:检查表的列是否支持。
|
||||
// 描述:该函数接受一个表的列定义列表,遍历每个列,检查其数据类型是否属于支持的类型。如果遇到不支持的类型,将抛出错误。此函数还会跳过名为 "hash" 的列,因为它通常是用于存储哈希值的列。
|
||||
|
|
@ -36,9 +36,9 @@
|
|||
#include "gs_policy/curl_utils.h"
|
||||
#include "utils/elog.h"
|
||||
|
||||
// 用于多线程访问的互斥锁
|
||||
|
||||
static std::mutex g_i_mutex;
|
||||
// CurlUtils 类的构造函数
|
||||
|
||||
CurlUtils::CurlUtils() : m_withSSL(false),
|
||||
m_certificate(""),
|
||||
m_user(""),
|
||||
|
|
@ -46,12 +46,12 @@ CurlUtils::CurlUtils() : m_withSSL(false),
|
|||
m_curlForPost(NULL)
|
||||
{
|
||||
}
|
||||
// CurlUtils 类的析构函数
|
||||
|
||||
CurlUtils::~CurlUtils()
|
||||
{ // 清理 Curl 对象
|
||||
{
|
||||
curl_easy_cleanup(m_curlForPost);
|
||||
}
|
||||
// 初始化 CurlUtils 类的成员变量
|
||||
|
||||
void CurlUtils::initialize(bool withSSL, const std::string certificate, const std::string user,
|
||||
const std::string password)
|
||||
{
|
||||
|
|
@ -65,20 +65,16 @@ void CurlUtils::initialize(bool withSSL, const std::string certificate, const st
|
|||
/*
|
||||
* Send file to remote web server as rest interface
|
||||
*/
|
||||
// 发送 HTTP POST 请求,上传文件
|
||||
bool CurlUtils::http_post_file_request(const std::string url, const std::string fileName, bool connection_testing)
|
||||
{// 记录日志
|
||||
{
|
||||
ereport(INFO, (errmsg("Url = %s, fileName = %s", url.c_str(), fileName.c_str())));
|
||||
// 从文件中读取文件内容
|
||||
std::ifstream t(fileName);
|
||||
std::string str((std::istreambuf_iterator<char>(t)),
|
||||
std::istreambuf_iterator<char>());
|
||||
|
||||
if (m_curlForPost != NULL) {
|
||||
// 设置请求的 Content-Type
|
||||
struct curl_slist *slist1 = NULL;
|
||||
slist1 = curl_slist_append(slist1, "Content-Type: application/json");
|
||||
// 设置 Curl 选项
|
||||
(void)curl_easy_setopt(m_curlForPost, CURLOPT_URL, url.c_str());
|
||||
(void)curl_easy_setopt(m_curlForPost, CURLOPT_NOPROGRESS, 1L);
|
||||
(void)curl_easy_setopt(m_curlForPost, CURLOPT_POSTFIELDS, str.c_str());
|
||||
|
|
@ -96,13 +92,11 @@ bool CurlUtils::http_post_file_request(const std::string url, const std::string
|
|||
(void)curl_easy_setopt(m_curlForPost, CURLOPT_TCP_KEEPALIVE, 1L);
|
||||
|
||||
/* a simply connection test to server, just verify the connection without any data transfer */
|
||||
// 用于连接测试,只验证连接而不传输数据
|
||||
if (connection_testing) {
|
||||
(void)curl_easy_setopt(m_curlForPost, CURLOPT_CONNECT_ONLY, 1L);
|
||||
}
|
||||
|
||||
/* perform a file transfer */
|
||||
// 执行文件传输
|
||||
CURLcode res = curl_easy_perform(m_curlForPost);
|
||||
if (res != CURLE_OK) {
|
||||
/*
|
||||
|
|
@ -114,17 +108,15 @@ bool CurlUtils::http_post_file_request(const std::string url, const std::string
|
|||
(errmsg("make sure connection to elastic_search_ip_addr, error info: %s\n",
|
||||
curl_easy_strerror(res))));
|
||||
}
|
||||
// 清理资源并重置 Curl 对象
|
||||
|
||||
curl_slist_free_all(slist1);
|
||||
curl_easy_reset(m_curlForPost);
|
||||
ereport(WARNING, (errmsg("Connection issue happended, post file error: %s\n", curl_easy_strerror(res))));
|
||||
return false;
|
||||
}
|
||||
// 清理资源并重置 Curl 对象
|
||||
curl_slist_free_all(slist1);
|
||||
curl_easy_reset(m_curlForPost);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//是一个使用 libcurl 库发送 HTTP POST 请求的示例。它包含了初始化 Curl 对象、发送文件请求、处理连接问题等功能。此代码适用于在 C++ 程序中使用 libcurl 库进行网络通信。
|
||||
|
|
@ -48,13 +48,13 @@
|
|||
#include "pgaudit.h"
|
||||
#include "utils/snapmgr.h"
|
||||
|
||||
// 这些变量用于存储不同的钩子函数,用于加载和验证数据遮蔽策略。
|
||||
|
||||
LoadPoliciesPtr load_masking_policies_hook = NULL;
|
||||
LoadPolicyAccessPtr load_masking_policy_actions_hook = NULL;
|
||||
LoadPolicyFilterPtr load_masking_policy_filter_hook = NULL;
|
||||
ValidateBehaviourPtr validate_masking_behaviour_hook = NULL;
|
||||
VerifyLabelsByPolicy gs_verify_labels_by_policy_hook = NULL;
|
||||
// 这是一个字符串数组,包含了不同的数据遮蔽函数名称。
|
||||
|
||||
static const char* g_maskFunctions[] = {
|
||||
"creditcardmasking",
|
||||
"basicemailmasking",
|
||||
|
|
@ -65,14 +65,13 @@ static const char* g_maskFunctions[] = {
|
|||
"regexpmasking",
|
||||
NULL
|
||||
};
|
||||
// 这个函数用于重新加载数据遮蔽策略。
|
||||
|
||||
static void reload_masking_policies()
|
||||
{
|
||||
// 如果当前数据库无效,直接返回。
|
||||
if (!OidIsValid(u_sess->proc_cxt.MyDatabaseId)) {
|
||||
return;
|
||||
}
|
||||
// 如果有加载数据遮蔽策略的钩子函数,就调用它们。
|
||||
|
||||
if (load_masking_policies_hook != NULL) {
|
||||
load_masking_policies_hook(false);
|
||||
}
|
||||
|
|
@ -80,14 +79,11 @@ static void reload_masking_policies()
|
|||
load_masking_policy_actions_hook(false);
|
||||
}
|
||||
/* load filters must be last */
|
||||
// 加载过滤器应该在最后加载。
|
||||
if (load_masking_policy_filter_hook != NULL) {
|
||||
load_masking_policy_filter_hook(false);
|
||||
}
|
||||
}
|
||||
//功能:重新加载数据遮蔽策略。描述:该函数检查当前数据库是否有效,然后调用不同的钩子函数来加载数据遮蔽策略、策略动作以及策略过滤器。策略过滤器的加载应该在最后进行。
|
||||
// 下面是一系列关于数据遮蔽策略的结构体操作函数,用于比较和排序。
|
||||
// 这个函数用于比较两个 PgPolicyMaskingActionStruct 是否相等。
|
||||
|
||||
#define BUFFSIZE 512
|
||||
bool PgPolicyMaskingActionStruct::operator == (const PgPolicyMaskingActionStruct &arg) const
|
||||
{
|
||||
|
|
@ -99,8 +95,7 @@ bool PgPolicyMaskingActionStruct::operator == (const PgPolicyMaskingActionStruct
|
|||
return true;
|
||||
}
|
||||
}
|
||||
//功能:比较两个数据遮蔽策略动作是否相等。描述:该函数用于比较两个数据遮蔽策略动作对象是否相等。
|
||||
// 这个函数用于比较两个 PgPolicyMaskingActionStruct 的大小关系。
|
||||
|
||||
bool PgPolicyMaskingActionStruct::operator < (const PgPolicyMaskingActionStruct &arg) const
|
||||
{
|
||||
int res = strcasecmp(m_type.c_str(), arg.m_type.c_str());
|
||||
|
|
@ -112,7 +107,6 @@ bool PgPolicyMaskingActionStruct::operator < (const PgPolicyMaskingActionStruct
|
|||
return m_policy_oid < arg.m_policy_oid;
|
||||
}
|
||||
|
||||
// 这个函数用于比较两个 PgPolicyMaskingActionStruct 的大小关系,通常用于排序。
|
||||
int PgPolicyMaskingActionStruct::operator - (const PgPolicyMaskingActionStruct &arg) const
|
||||
{
|
||||
if (*this < arg) {
|
||||
|
|
@ -131,7 +125,6 @@ typedef gs_stl::gs_map<gs_stl::gs_string, gs_stl::gs_vector<PgPolicyMaskingActio
|
|||
*
|
||||
* load all labels of policyOid in catalog gs_masking_policy_actions into policy_labels
|
||||
*/
|
||||
// 这个函数用于加载已存在的数据遮蔽标签。
|
||||
void load_existing_masking_labels(policy_labelname_set *policy_labels, Oid policyOid)
|
||||
{
|
||||
HeapTuple maskingPolicyTuple = NULL;
|
||||
|
|
@ -170,7 +163,6 @@ void load_existing_masking_labels(policy_labelname_set *policy_labels, Oid polic
|
|||
* load all action items of policyOid in catalog gs_masking_policy_actions actions
|
||||
* load all action items of policyOid in to masking label map
|
||||
*/
|
||||
// 这个函数用于加载已存在的数据遮蔽动作。
|
||||
void load_existing_masking_actions(masking_actions_set* actions,
|
||||
masking_label_to_actions_map* labels_to_actions, long long policy_oid = 0)
|
||||
{
|
||||
|
|
@ -206,7 +198,7 @@ void load_existing_masking_actions(masking_actions_set* actions,
|
|||
|
||||
heap_close(relation, RowExclusiveLock);
|
||||
}
|
||||
// 这个函数用于检查数据遮蔽属性的类型是否允许遮蔽。
|
||||
|
||||
static bool check_masking_attrtype(Oid atttypeid)
|
||||
{
|
||||
if (!OidIsValid(atttypeid)) {
|
||||
|
|
@ -242,8 +234,7 @@ static bool check_masking_attrtype(Oid atttypeid)
|
|||
}
|
||||
return result;
|
||||
}
|
||||
//功能:检查数据遮蔽属性的数据类型是否支持遮蔽。描述:该函数根据给定的数据类型 OID,检查该数据类型是否支持进行数据遮蔽。支持的数据类型包括布尔型、日期时间类型、文本类型等。
|
||||
// 这个函数用于检查列是否允许进行数据遮蔽。
|
||||
|
||||
static bool column_allow_to_masking(Oid relid, const char *column)
|
||||
{
|
||||
if (!OidIsValid(relid)) {
|
||||
|
|
@ -261,22 +252,20 @@ static bool column_allow_to_masking(Oid relid, const char *column)
|
|||
ReleaseSysCache(attr_tuple);
|
||||
return check_masking_attrtype(column_typeid);
|
||||
}
|
||||
//功能:检查列是否允许进行数据遮蔽。描述:该函数根据给定的表 OID 和列名,检查该列是否存在于表中且允许进行数据遮蔽。如果列不存在或者数据类型不支持遮蔽,将返回 false。
|
||||
|
||||
/*
|
||||
* add_labels_to_masking_action
|
||||
*
|
||||
* insert row of catalog gs_masking_policy_actions
|
||||
*/
|
||||
// 这个函数用于向数据遮蔽策略动作中添加标签信息
|
||||
static inline void add_labels_to_masking_action(const gs_stl::gs_string action_type,
|
||||
const gs_stl::gs_string action_params, const gs_stl::gs_string label_name,
|
||||
Relation relation, Oid policyOid, Datum curtime)
|
||||
{ // 创建一个用于标记是否为空的数组和一个数据数组
|
||||
{
|
||||
bool policy_actions_nulls[Natts_gs_masking_policy_actions] = {false};
|
||||
Datum policy_actions_values[Natts_gs_masking_policy_actions] = {0};
|
||||
// 检查是否有验证标签的挂钩函数
|
||||
|
||||
if (verify_label_hook) {
|
||||
// 如果有验证标签的挂钩函数,检查标签是否有效
|
||||
if (!verify_label_hook(label_name.c_str())) {
|
||||
heap_close(relation, RowExclusiveLock);
|
||||
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("[%s] no such label found", label_name.c_str())));
|
||||
|
|
@ -286,12 +275,9 @@ static inline void add_labels_to_masking_action(const gs_stl::gs_string action_t
|
|||
/* for now mask on table/view is not allowed */
|
||||
Relation label_rel = NULL;
|
||||
policy_labels_map existing_labels;
|
||||
// 打开策略标签的关系表
|
||||
label_rel = heap_open(GsPolicyLabelRelationId, RowExclusiveLock);
|
||||
// 加载现有的标签数据到existing_labels映射中
|
||||
load_existing_labels(label_rel, &existing_labels);
|
||||
heap_close(label_rel, RowExclusiveLock);
|
||||
// 在existing_labels映射中查找指定<E68C87><E5AE9A><EFBFBD>标签
|
||||
policy_labels_map::iterator lbit = existing_labels.find(label_name);
|
||||
if (lbit != existing_labels.end()) {
|
||||
policy_labels_set::iterator lit = (lbit->second)->begin();
|
||||
|
|
@ -299,7 +285,6 @@ static inline void add_labels_to_masking_action(const gs_stl::gs_string action_t
|
|||
for (; lit != eit; ++lit) {
|
||||
Oid fqdnOid = lit->m_data_value_fqdn.m_value_object;
|
||||
/* masking only allowed operator on column */
|
||||
// 检查标签的数据类型是否为"column"
|
||||
if (strcmp(lit->m_data_type.c_str(), "column") != 0) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
|
|
@ -307,16 +292,13 @@ static inline void add_labels_to_masking_action(const gs_stl::gs_string action_t
|
|||
}
|
||||
/* masked column should belong to an ordinary table */
|
||||
if (OidIsValid(fqdnOid)) {
|
||||
// 获取关系对象的类型
|
||||
char relkind = get_rel_relkind(fqdnOid);
|
||||
// 检查关系对象是否是普通表
|
||||
if (relkind != RELKIND_RELATION || get_rel_persistence(fqdnOid) != 'p') {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("Masking policy can only operate on column of ordinary table.")));
|
||||
}
|
||||
const char *column_name = lit->m_data_value_fqdn.m_value_object_attrib.c_str();
|
||||
// 检查列是否允许进行数据遮蔽
|
||||
if (!column_allow_to_masking(fqdnOid, column_name)) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
|
|
@ -325,19 +307,17 @@ static inline void add_labels_to_masking_action(const gs_stl::gs_string action_t
|
|||
}
|
||||
}
|
||||
}
|
||||
// 将数据添加到policy_actions_values数组中
|
||||
|
||||
policy_actions_values[Anum_gs_masking_policy_act_action_type - 1] = DirectFunctionCall1(namein, CStringGetDatum(action_type.c_str()));
|
||||
policy_actions_values[Anum_gs_masking_policy_act_action_params - 1] = DirectFunctionCall1(namein, CStringGetDatum(action_params.c_str()));
|
||||
policy_actions_values[Anum_gs_masking_policy_act_label_name - 1] = DirectFunctionCall1(namein, CStringGetDatum(label_name.c_str()));
|
||||
policy_actions_values[Anum_gs_masking_policy_act_policy_oid - 1] = ObjectIdGetDatum(policyOid);
|
||||
policy_actions_values[Anum_gs_masking_policy_act_modify_date - 1] = curtime;
|
||||
// 创建一个用于插入的堆元组
|
||||
|
||||
HeapTuple policy_htup = heap_form_tuple(relation->rd_att, policy_actions_values, policy_actions_nulls);
|
||||
// 将堆元组插入关系表
|
||||
|
||||
simple_heap_insert(relation, policy_htup);
|
||||
// 更新关系表的索引
|
||||
CatalogUpdateIndexes(relation, policy_htup);
|
||||
// 释放堆元组的内存
|
||||
heap_freetuple(policy_htup);
|
||||
}
|
||||
|
||||
|
|
@ -345,7 +325,6 @@ static inline void add_labels_to_masking_action(const gs_stl::gs_string action_t
|
|||
* add_masking_filters
|
||||
* insert a set of filters into gs_masking_policy_filters
|
||||
*/
|
||||
// 这个函数用于向数据遮蔽策略过滤器中添加策略过滤条件
|
||||
static inline void add_masking_filters(const filters_set* filters_to_add, Relation relation)
|
||||
{
|
||||
Datum curtime;
|
||||
|
|
@ -355,12 +334,10 @@ static inline void add_masking_filters(const filters_set* filters_to_add, Relati
|
|||
errno_t rc = EOK;
|
||||
|
||||
/* Get current timestamp */
|
||||
// 获取当前时间
|
||||
curtime = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp());
|
||||
// 遍历要添加的策略过滤器集合
|
||||
|
||||
for (filters_set::const_iterator it = filters_to_add->begin(); it != filters_to_add->end(); ++it) {
|
||||
/* restore values and nulls for insert new node group record */
|
||||
// 清空policy_filters_values和policy_filters_nulls数组
|
||||
rc = memset_s(policy_filters_values, sizeof(policy_filters_values), 0, sizeof(policy_filters_values));
|
||||
securec_check(rc, "\0", "\0");
|
||||
rc = memset_s(policy_filters_nulls, sizeof(policy_filters_nulls), false, sizeof(policy_filters_nulls));
|
||||
|
|
@ -375,11 +352,9 @@ static inline void add_masking_filters(const filters_set* filters_to_add, Relati
|
|||
policy_filters_htup = heap_form_tuple(relation->rd_att, policy_filters_values, policy_filters_nulls);
|
||||
|
||||
/* Do the insertion */
|
||||
// 将HeapTuple插入到关系中
|
||||
simple_heap_insert(relation, policy_filters_htup);
|
||||
// 更新关系的索引信息
|
||||
|
||||
CatalogUpdateIndexes(relation, policy_filters_htup);
|
||||
// 释放HeapTuple占用的内存
|
||||
heap_freetuple(policy_filters_htup);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,26 +54,24 @@
|
|||
#ifdef ENABLE_UT
|
||||
#define static
|
||||
#endif
|
||||
// 定义用于管理事件的钩子函数
|
||||
|
||||
GsSaveManagementEvent gs_save_mng_event_hook = NULL;
|
||||
GsSendManagementEvent gs_send_mng_event_hook = NULL;
|
||||
//GsSaveManagementEvent 和 GsSendManagementEvent 就是用于注册管理事件钩子的函数。
|
||||
//这些钩子函数允许用户在特定事件发生时执行自定义的管理操作。钩子函数通常用于插件系统、事件驱动编程和扩展应用程序的功能。
|
||||
// 保存管理消息
|
||||
|
||||
void save_manage_message(const char* message)
|
||||
{
|
||||
if (gs_save_mng_event_hook != NULL) {
|
||||
gs_save_mng_event_hook(message);
|
||||
}
|
||||
}
|
||||
// 发送管理消息
|
||||
|
||||
void send_manage_message(AuditResult result_type)
|
||||
{
|
||||
if (gs_send_mng_event_hook != NULL) {
|
||||
gs_send_mng_event_hook(result_type);
|
||||
}
|
||||
}
|
||||
// 以下是一系列重载操作符的定义,用于比较 GsPolicyStruct、PgPolicyFiltersStruct 和 PgPolicyPrivilegesAccessStruct 对象。
|
||||
|
||||
bool GsPolicyStruct::operator == (const GsPolicyStruct &arg) const
|
||||
{
|
||||
return strcasecmp(m_name.c_str(), arg.m_name.c_str()) == 0;
|
||||
|
|
@ -167,7 +165,6 @@ int PgPolicyPrivilegesAccessStruct::operator - (const PgPolicyPrivilegesAccessSt
|
|||
}
|
||||
|
||||
/* Process new filters from parser tree and tranform them into string */
|
||||
// 处理来自解析器树的新筛选器,并将它们转化为字符串
|
||||
bool process_new_filters(const List *policy_filters, gs_stl::gs_string *flat_tree)
|
||||
{
|
||||
if (!policy_filters)
|
||||
|
|
@ -219,47 +216,42 @@ bool process_new_filters(const List *policy_filters, gs_stl::gs_string *flat_tre
|
|||
}
|
||||
|
||||
bool scan_to_delete_from_relation(long long row_id, Relation relation, unsigned int index_id)
|
||||
{// 如果关系为空,返回false
|
||||
{
|
||||
if (relation == NULL) {
|
||||
return false;
|
||||
}
|
||||
ScanKeyData skey;
|
||||
/* Find the row to delete. */
|
||||
// 创建用于扫描的键值对结构体skey,这里的目的是找到要删除的行。
|
||||
ScanKeyInit(&skey,
|
||||
ObjectIdAttributeNumber,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(row_id));
|
||||
// 使用systable_beginscan函数开始对关系的扫描,使用指定的索引(index_id)。
|
||||
|
||||
SysScanDesc tgscan = systable_beginscan(relation, index_id, true, NULL, 1, &skey);
|
||||
// 获取扫描到的下一个堆元组。
|
||||
|
||||
HeapTuple tup = systable_getnext(tgscan);
|
||||
// 如果没有有效的堆元组,结束扫描并返回false。
|
||||
if (!HeapTupleIsValid(tup)) {
|
||||
systable_endscan(tgscan);
|
||||
return false;
|
||||
}
|
||||
/* Delete the label tuple */
|
||||
// 删除找到的堆元组。
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
// 结束扫描。
|
||||
|
||||
systable_endscan(tgscan);
|
||||
|
||||
return true;
|
||||
}
|
||||
// 这个函数用于构建资源名称。
|
||||
|
||||
void construct_resource_name(const RangeVar *rel, gs_stl::gs_string *target_name_s)
|
||||
{// 如果有目录名,将其添加到目标名称中,以"."分隔。
|
||||
{
|
||||
if (rel->catalogname) {
|
||||
(void)target_name_s->append((const char *)rel->catalogname);
|
||||
target_name_s->push_back('.');
|
||||
}
|
||||
// 如果有模式名,将其添加到目标名称中,以"."分隔。
|
||||
if (rel->schemaname) {
|
||||
(void)target_name_s->append((const char *)rel->schemaname);
|
||||
target_name_s->push_back('.');
|
||||
}
|
||||
// 添加表名到目标名称中。
|
||||
if (rel->relname) {
|
||||
(void)target_name_s->append(rel->relname);
|
||||
}
|
||||
|
|
@ -268,22 +260,20 @@ void construct_resource_name(const RangeVar *rel, gs_stl::gs_string *target_name
|
|||
/**
|
||||
* Check if current app is valid or not.
|
||||
*/
|
||||
// 这个函数用于验证应用程序过滤器是否有效。
|
||||
bool verify_app_filter(const char* obj_value)
|
||||
{// 如果应用程序值为空,生成错误报告并返回false。
|
||||
{
|
||||
if (strlen(obj_value) == 0) {
|
||||
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("app: [%s] is invalid", obj_value)));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The first character id numbers or dollar */
|
||||
// 检查应用程序值的首字符是否是数字或"$",如果是,生成错误报告并返回false。
|
||||
char c = obj_value[0];
|
||||
if ((c >= '0' && c <= '9') || c == '$') {
|
||||
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("app: [%s] is invalid", obj_value)));
|
||||
return false;
|
||||
}
|
||||
// 遍历应用程序值的每个字符,检查是否包含无效字符。
|
||||
|
||||
int len = strlen(obj_value);
|
||||
for (int i = 0; i < len; i++) {
|
||||
c = obj_value[i];
|
||||
|
|
@ -304,12 +294,10 @@ bool verify_app_filter(const char* obj_value)
|
|||
* @ obj_value : the actual filter information.
|
||||
* @ return_value : record about the filter information.
|
||||
*/
|
||||
// 这个函数用于验证过滤器信息的有效性,包括IP、角色和应用程序。
|
||||
bool verify_ip_role_app(const char* obj_type, const char* obj_value, gs_stl::gs_string *return_value)
|
||||
{
|
||||
if (!strcasecmp(obj_type, "ip")) {
|
||||
const char* check_value = obj_value;
|
||||
// 验证IP范围是否有效。
|
||||
if (!IPRange::is_range_valid(check_value)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("ip range: [%s] is invalid, please identify", obj_value)));
|
||||
return false;
|
||||
|
|
@ -317,20 +305,17 @@ bool verify_ip_role_app(const char* obj_type, const char* obj_value, gs_stl::gs_
|
|||
(void)return_value->append(check_value);
|
||||
return true;
|
||||
} else if (!strcasecmp(obj_type, "roles")) {
|
||||
// 获取角色的OID并验证其有效性。
|
||||
Oid uid = get_role_oid(obj_value, true);
|
||||
if (!OidIsValid(uid)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("role: [%s] is invalid", obj_value)));
|
||||
return false;
|
||||
}
|
||||
char buffer[64]; /* buffer to store the oid int as string. 64 is the max length of oid. */
|
||||
// 用于将OID转换为字符串的缓冲区。
|
||||
int nRet = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%d", uid);
|
||||
securec_check_ss(nRet, "\0", "\0");
|
||||
(void)return_value->append(buffer);
|
||||
return true;
|
||||
} else if (!strcasecmp(obj_type, "app")) {
|
||||
// 验证应用程序过滤器是否有效。
|
||||
bool is_valid_app = verify_app_filter(obj_value);
|
||||
if (!is_valid_app) {
|
||||
return false;
|
||||
|
|
@ -339,7 +324,7 @@ bool verify_ip_role_app(const char* obj_type, const char* obj_value, gs_stl::gs_
|
|||
(void)return_value->append(obj_value);
|
||||
return true;
|
||||
}
|
||||
// 这个函数用于添加权限和访问信息。
|
||||
|
||||
static bool add_privileges_access(const char *action_type, const char *label_name,
|
||||
privileges_access_set *actions, const policy_labels_map *existing_labels, const GsPolicyStruct *policy,
|
||||
gs_stl::gs_string *err_msg)
|
||||
|
|
@ -349,7 +334,6 @@ static bool add_privileges_access(const char *action_type, const char *label_nam
|
|||
item.m_label_name = label_name;
|
||||
item.m_policy_oid = policy->m_id;
|
||||
/* validate that such label exists */
|
||||
// 验证是否存在该标签,如果不存在,生成错误报告并返回false。
|
||||
if (existing_labels->find(label_name) == existing_labels->end()) {
|
||||
err_msg->clear();
|
||||
(void)err_msg->append("Trying to add/remove privilege/access [");
|
||||
|
|
@ -362,7 +346,7 @@ static bool add_privileges_access(const char *action_type, const char *label_nam
|
|||
(void)actions->insert(item);
|
||||
return true;
|
||||
}
|
||||
// 这个函数用于处理目标,包括权限和访问。
|
||||
|
||||
bool handle_target(ListCell *target,
|
||||
int opt_type,
|
||||
bool is_add,
|
||||
|
|
@ -406,7 +390,7 @@ bool handle_target(ListCell *target,
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
// 这个函数用于解析逗号分隔的值,并验证它们的有效性。
|
||||
|
||||
static bool parse_values(const gs_stl::gs_string logical_expr_str, int *offset, const char* obj_type)
|
||||
{
|
||||
std::size_t found = gs_stl::gs_string::npos;
|
||||
|
|
@ -417,14 +401,12 @@ static bool parse_values(const gs_stl::gs_string logical_expr_str, int *offset,
|
|||
bool filter_valid = false;
|
||||
int nRet;
|
||||
/* not finding last ']' means error */
|
||||
// 如果在偏移位置找不到最后的 ']',则表示出错。
|
||||
if (limit_pos == gs_stl::gs_string::npos) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("filter: [%s] is invalid", logical_expr_str.c_str())));
|
||||
return false;
|
||||
}
|
||||
// 在逗号之间循环解析值,并验证它们的有效性。
|
||||
while ((found = logical_expr_str.find(',', *offset)) != gs_stl::gs_string::npos && found < limit_pos) {
|
||||
nRet = snprintf_s(buff, sizeof(buff), sizeof(buff) - 1, "%.*s", (int)(found - *offset),
|
||||
logical_expr_str.c_str() + *offset);
|
||||
|
|
@ -433,7 +415,7 @@ static bool parse_values(const gs_stl::gs_string logical_expr_str, int *offset,
|
|||
parsed = parsed && filter_valid;
|
||||
*offset = found + 1;
|
||||
}
|
||||
// 处理最后一个值。
|
||||
|
||||
if (*offset < (int)limit_pos) {
|
||||
nRet = snprintf_s(buff, sizeof(buff), sizeof(buff) - 1, "%.*s", (int)(limit_pos - *offset),
|
||||
logical_expr_str.c_str() + *offset);
|
||||
|
|
@ -449,16 +431,14 @@ static bool parse_values(const gs_stl::gs_string logical_expr_str, int *offset,
|
|||
}
|
||||
|
||||
/* Parses & validates (recursively) polish-notation format string into logical tree */
|
||||
// 这个函数用于验证逻辑表达式字符串是否有效。
|
||||
bool validate_logical_expression(const gs_stl::gs_string logical_expr_str, int *offset)
|
||||
{
|
||||
int logical_expr_len = logical_expr_str.size();
|
||||
|
||||
while (*offset < logical_expr_len) {
|
||||
/* AND/OR node */// AND/OR 节点
|
||||
/* AND/OR node */
|
||||
if ((logical_expr_str[*offset] == '*') || (logical_expr_str[*offset] == '+')) {
|
||||
(*offset)++;
|
||||
// 递归验证左右子树
|
||||
return (validate_logical_expression(logical_expr_str, offset) /* go left */
|
||||
&& validate_logical_expression(logical_expr_str, offset)); /* go right */
|
||||
} else if (logical_expr_str[*offset] == '!') { /* NOT operator */
|
||||
|
|
@ -477,7 +457,7 @@ bool validate_logical_expression(const gs_stl::gs_string logical_expr_str, int *
|
|||
|
||||
return false;
|
||||
}
|
||||
// 这个函数用于获取当前会话的IP地址。
|
||||
|
||||
void get_session_ip(char *session_ip, int len)
|
||||
{
|
||||
if (len < MAX_IP_LEN) {
|
||||
|
|
@ -495,7 +475,7 @@ void get_session_ip(char *session_ip, int len)
|
|||
get_client_ip(remote_addr, session_ip);
|
||||
}
|
||||
}
|
||||
// 这个函数用于解析客户端的IP地址。
|
||||
|
||||
void get_client_ip(const struct sockaddr* remote_addr, char *ip_str)
|
||||
{
|
||||
/* parse the remote ip address */
|
||||
|
|
@ -505,7 +485,7 @@ void get_client_ip(const struct sockaddr* remote_addr, char *ip_str)
|
|||
(void)inet_ntop(AF_INET, &((struct sockaddr_in*)remote_addr)->sin_addr, ip_str, MAX_IP_LEN - 1);
|
||||
}
|
||||
}
|
||||
// 这个函数用于检查数据库是否有效。
|
||||
|
||||
bool is_database_valid(const char* dbname)
|
||||
{
|
||||
if (dbname == NULL) {
|
||||
|
|
@ -518,7 +498,7 @@ bool is_database_valid(const char* dbname)
|
|||
|
||||
return false;
|
||||
}
|
||||
// 这个函数用于创建临时资源所有者。
|
||||
|
||||
ResourceOwnerData* create_temp_resourceowner()
|
||||
{
|
||||
ResourceOwner tmpOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner,
|
||||
|
|
@ -527,7 +507,7 @@ ResourceOwnerData* create_temp_resourceowner()
|
|||
t_thrd.utils_cxt.CurrentResourceOwner = tmpOwner;
|
||||
return currentOwner;
|
||||
}
|
||||
// 这个函数用于释放临时资源所有者。
|
||||
|
||||
void release_temp_resourceowner(ResourceOwnerData* resource_owner)
|
||||
{
|
||||
ResourceOwner tmpOwner = t_thrd.utils_cxt.CurrentResourceOwner;
|
||||
|
|
|
|||
|
|
@ -2947,9 +2947,9 @@ void SetOneOfCompressOption(DefElem* defElem, TableCreateSupport* tableCreateSup
|
|||
} else if (pg_strcasecmp(defname, "compress_level") == 0) {
|
||||
tableCreateSupport->compressLevel = true;
|
||||
} else if (pg_strcasecmp(defname, "compress_byte_convert") == 0) {
|
||||
tableCreateSupport->compressByteConvert = defGetBoolean(defElem);
|
||||
tableCreateSupport->compressByteConvert = true;
|
||||
} else if (pg_strcasecmp(defname, "compress_diff_convert") == 0) {
|
||||
tableCreateSupport->compressDiffConvert = defGetBoolean(defElem);
|
||||
tableCreateSupport->compressDiffConvert = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8965,8 +8965,6 @@ void StartupXLOG(void)
|
|||
g_instance.comm_cxt.predo_cxt.redoPf.redo_done_time = 0;
|
||||
pg_atomic_write_u32(&(g_instance.comm_cxt.localinfo_cxt.is_finish_redo), 0);
|
||||
|
||||
NotifyGscRecoveryStarted();
|
||||
|
||||
/*
|
||||
* Initialize WAL insert status array and the flush index - lastWalStatusEntryFlushed.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1818,47 +1818,3 @@ void UHeapSlotStoreUHeapTuple(UHeapTuple utuple, TupleTableSlot *slot, bool shou
|
|||
/* Mark extracted state invalid */
|
||||
slot->tts_nvalid = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make the contents of the uheap table's slot contents solely depend on the slot(make them a local copy),
|
||||
* and not on underlying external resources like another memory context, buffers etc.
|
||||
*
|
||||
* @pram slot: slot to be materialized.
|
||||
*/
|
||||
Tuple UHeapMaterialize(TupleTableSlot *slot)
|
||||
{
|
||||
Assert(!slot->tts_isempty);
|
||||
Assert(slot->tts_tupslotTableAm == TAM_USTORE);
|
||||
Assert(slot->tts_tupleDescriptor != NULL);
|
||||
/*
|
||||
* If we have a regular physical tuple, and it's locally palloc'd, we have
|
||||
* nothing to do.
|
||||
*/
|
||||
if (slot->tts_tuple && slot->tts_shouldFree) {
|
||||
return slot->tts_tuple;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise, copy or build a physical tuple, and store it into the slot.
|
||||
*
|
||||
* We may be called in a context that is shorter-lived than the tuple
|
||||
* slot, but we have to ensure that the materialized tuple will survive
|
||||
* anyway.
|
||||
*/
|
||||
MemoryContext old_context = MemoryContextSwitchTo(slot->tts_mcxt);
|
||||
if (slot->tts_tuple != NULL) {
|
||||
slot->tts_tuple = UHeapCopyTuple((UHeapTuple)slot->tts_tuple);
|
||||
} else {
|
||||
slot->tts_tuple = UHeapFormTuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull);
|
||||
}
|
||||
slot->tts_shouldFree = true;
|
||||
MemoryContextSwitchTo(old_context);
|
||||
|
||||
/*
|
||||
* Have to deform from scratch, otherwise tts_values[] entries could point
|
||||
* into the non-materialized tuple (which might be gone when accessed).
|
||||
*/
|
||||
slot->tts_nvalid = 0;
|
||||
return slot->tts_tuple;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -238,19 +238,15 @@ public:
|
|||
return m_purpose;
|
||||
}
|
||||
|
||||
GcEpochType GcStartInnerTxn()
|
||||
void GcStartTxnMTtests()
|
||||
{
|
||||
m_gcEpoch = GetGlobalEpoch();
|
||||
|
||||
return m_gcEpoch;
|
||||
if (m_gcEpoch != GetGlobalEpoch())
|
||||
m_gcEpoch = GetGlobalEpoch();
|
||||
}
|
||||
|
||||
void GcEndInnerTxn(bool clean_gc)
|
||||
void GcEndTxnMTtests()
|
||||
{
|
||||
if (clean_gc) {
|
||||
RunQuicese();
|
||||
}
|
||||
m_gcEpoch = 0;
|
||||
RunQuicese();
|
||||
}
|
||||
|
||||
void GcStartTxn()
|
||||
|
|
@ -276,7 +272,6 @@ public:
|
|||
RunQuicese();
|
||||
m_managerLock.unlock();
|
||||
}
|
||||
m_gcEpoch = 0;
|
||||
m_isTxnStarted = false;
|
||||
}
|
||||
|
||||
|
|
@ -318,6 +313,7 @@ public:
|
|||
{
|
||||
if (m_performGcEpoch != g_gcActiveEpoch)
|
||||
HardQuiesce(m_rcuFreeCount);
|
||||
m_gcEpoch = 0;
|
||||
}
|
||||
|
||||
/** @brief Clean all object at the end of the session */
|
||||
|
|
|
|||
|
|
@ -258,15 +258,6 @@ bool Index::IndexInsert(Sentinel*& outputSentinel, const Key* key, uint32_t pid,
|
|||
outputSentinel = IndexInsertImpl(key, sentinel, inserted, pid);
|
||||
// sync between rollback/delete and insert
|
||||
if (inserted == false) {
|
||||
if (unlikely(outputSentinel == nullptr)) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_OOM, "Index Insert", "Failed to insert sentinel to index %s", m_name.c_str());
|
||||
rc = RC_MEMORY_ALLOCATION_ERROR;
|
||||
m_sentinelPool->Release<Sentinel>(sentinel);
|
||||
sentinel = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Spin if the counter is 0 - aborting in parallel or sentinel is marks for commit
|
||||
if (outputSentinel->RefCountUpdate(INC, pid) == RC_OK)
|
||||
retryInsert = false;
|
||||
|
|
@ -309,16 +300,8 @@ Sentinel* Index::IndexInsert(const Key* key, Row* row, uint32_t pid)
|
|||
// no need to report to full error stack
|
||||
SetLastError(MOT_ERROR_UNIQUE_VIOLATION, MOT_SEVERITY_NORMAL);
|
||||
m_sentinelPool->Release<Sentinel>(sentinel);
|
||||
sentinel = nullptr;
|
||||
return nullptr;
|
||||
} else {
|
||||
if (inserted == false) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Index Insert", "Failed to insert sentinel to index %s", m_name.c_str());
|
||||
m_sentinelPool->Release<Sentinel>(sentinel);
|
||||
sentinel = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (GetIndexOrder() == IndexOrder::INDEX_ORDER_PRIMARY) {
|
||||
sentinel->SetPrimaryIndex();
|
||||
sentinel->SetNextPtr(row);
|
||||
|
|
@ -360,6 +343,9 @@ Sentinel* Index::IndexReadHeader(const Key* key, uint32_t pid) const
|
|||
Sentinel* Index::IndexRemove(const Key* key, uint32_t pid)
|
||||
{
|
||||
Sentinel* sentinel = IndexRemoveImpl(key, pid);
|
||||
|
||||
MOT_ASSERT(sentinel != nullptr);
|
||||
MOT_ASSERT(sentinel->GetCounter() == 0);
|
||||
return sentinel;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,22 +56,12 @@ void* basic_table<P>::insert(MOT::Key const* const& key, void* const& entry, boo
|
|||
5. Update the the key slice, keylen, key suffix and key's value in the
|
||||
leaf
|
||||
6. Add the key's location in permutation's back (key is not visible for
|
||||
readers yet) as key's location is not part of the permutation yet, the key
|
||||
readers yet) As key's location is not part of the permutation yet, the key
|
||||
is not reachable (aka not present). In addition, the leaf is still locked.
|
||||
Unlocking the node and enter the key into the permutation will be done
|
||||
later in finish_insert (done in lp.finish function). */
|
||||
later in finish_insert (called from lp.finish). */
|
||||
|
||||
bool found = false;
|
||||
if (!lp.find_insert(*mtSessionThreadInfo, found)) {
|
||||
// Failed to insert key due to memory allocation failure.
|
||||
MOT_ASSERT(!mtSessionThreadInfo->non_disruptive_error());
|
||||
MOT_ASSERT(found == false);
|
||||
lp.finish(0, *mtSessionThreadInfo);
|
||||
result = false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOT_ASSERT(mtSessionThreadInfo->non_disruptive_error());
|
||||
bool found = lp.find_insert(*mtSessionThreadInfo);
|
||||
|
||||
// If the key is new (not previously existing) then we record the entry under
|
||||
// that key
|
||||
|
|
|
|||
|
|
@ -33,15 +33,15 @@ namespace Masstree {
|
|||
template <typename P>
|
||||
struct gc_layer_rcu_callback_ng : public P::threadinfo_type::mrcu_callback {
|
||||
typedef typename P::threadinfo_type threadinfo;
|
||||
node_base<P>** root_ref_;
|
||||
node_base<P>* root_;
|
||||
int len_;
|
||||
size_t size_;
|
||||
MOT::MasstreePrimaryIndex* index_;
|
||||
char s_[0];
|
||||
gc_layer_rcu_callback_ng(node_base<P>** root_ref, Str prefix, size_t size)
|
||||
: root_ref_(root_ref), len_(prefix.length()), size_(size), index_(mtSessionThreadInfo->get_working_index())
|
||||
gc_layer_rcu_callback_ng(node_base<P>* root, Str prefix, size_t size)
|
||||
: root_(root), len_(prefix.length()), size_(size), index_(mtSessionThreadInfo->get_working_index())
|
||||
{
|
||||
errno_t erc = memcpy_s(s_, len_, prefix.data(), len_);
|
||||
errno_t erc = memcpy_s(s_, size_, prefix.data(), len_);
|
||||
securec_check(erc, "\0", "\0");
|
||||
}
|
||||
size_t operator()(bool drop_index);
|
||||
|
|
@ -51,7 +51,7 @@ struct gc_layer_rcu_callback_ng : public P::threadinfo_type::mrcu_callback {
|
|||
return size_;
|
||||
}
|
||||
|
||||
static void make(node_base<P>** root_ref, Str prefix, threadinfo& ti);
|
||||
static void make(node_base<P>* root, Str prefix, threadinfo& ti);
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
|
|
@ -60,8 +60,8 @@ size_t gc_layer_rcu_callback_ng<P>::operator()(bool drop_index)
|
|||
// If drop_index == true, all index's pools are going to be cleaned, so we can skip gc_layer call (which might add
|
||||
// more elements into GC)
|
||||
if (drop_index == false) {
|
||||
// GC layer remove might delete elements from tree and might create new gc layer removal requests and add them to GC.
|
||||
// Index must be provided to allow access to the memory pools.
|
||||
// GC layer remove might delete elements from tree and add them to the limbolist. Index must be provided to
|
||||
// allow access to the memory pools.
|
||||
mtSessionThreadInfo->set_working_index(index_);
|
||||
(*this)(*mtSessionThreadInfo);
|
||||
mtSessionThreadInfo->set_working_index(NULL);
|
||||
|
|
@ -73,33 +73,30 @@ size_t gc_layer_rcu_callback_ng<P>::operator()(bool drop_index)
|
|||
template <typename P>
|
||||
void gc_layer_rcu_callback_ng<P>::operator()(threadinfo& ti)
|
||||
{
|
||||
masstree_invariant(root_ref_);
|
||||
|
||||
tcursor<P> node_cursor(root_ref_, s_, len_);
|
||||
bool do_remove = node_cursor.gc_layer(ti);
|
||||
if (!do_remove || !node_cursor.finish_remove(ti)) {
|
||||
node_cursor.n_->unlock();
|
||||
// root_ node while creating gc_layer_rcu_callback_ng might not be the current root. Find updated tree's root.
|
||||
while (!root_->is_root()) {
|
||||
root_ = root_->maybe_parent();
|
||||
}
|
||||
ti.add_nodes_to_gc();
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
void gc_layer_rcu_callback_ng<P>::make(node_base<P>** root_ref, Str prefix, threadinfo& ti)
|
||||
{
|
||||
size_t sz = prefix.len + sizeof(gc_layer_rcu_callback_ng<P>);
|
||||
// As we are using slab allocator for allocation, sz is will be updated by ti.allocate with the real allocation
|
||||
// size. We need this size for GC deallocation size report
|
||||
void* data = ti.allocate(sz, memtag_masstree_gc, &sz /* IN/OUT PARAM */);
|
||||
if (!data) {
|
||||
// If allocation fails, gc layer removal command will not be added to GC and this layer wont be removed.
|
||||
// We might deal with this issue in the future by replacing the current mechanism with one of the following options:
|
||||
// 1. Use thread local GC layer removal object (per threadinfo) and keep list of key suffixes to clean (also in threadinfo)
|
||||
// 2. Move this feature to VACUUM process: Create special iterator that adds GC Layer callbacks when it finds empty layers
|
||||
ti.set_last_error(MT_MERR_GC_LAYER_REMOVAL_MAKE);
|
||||
// If root was already deleted, do nothing.
|
||||
if (root_->deleted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
gc_layer_rcu_callback_ng<P>* cb = new (data) gc_layer_rcu_callback_ng<P>(root_ref, prefix, sz);
|
||||
tcursor<P> node_cursor(root_, s_, len_);
|
||||
if (!node_cursor.gc_layer(ti) || !node_cursor.finish_remove(ti)) {
|
||||
node_cursor.n_->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
void gc_layer_rcu_callback_ng<P>::make(node_base<P>* root, Str prefix, threadinfo& ti)
|
||||
{
|
||||
size_t sz = prefix.len + sizeof(gc_layer_rcu_callback_ng<P>);
|
||||
// As we are using slab allocator to allocate the memory, sz is will updated in ti.allocate with the real allocated
|
||||
// size
|
||||
void* data = ti.allocate(sz, memtag_masstree_gc, &sz /*OUT PARAM*/);
|
||||
gc_layer_rcu_callback_ng<P>* cb = new (data) gc_layer_rcu_callback_ng<P>(root, prefix, sz);
|
||||
ti.rcu_register(cb, sz);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,17 +69,14 @@ Sentinel* MasstreePrimaryIndex::IndexInsertImpl(const Key* key, Sentinel* sentin
|
|||
mtSessionThreadInfo->set_gc_session(
|
||||
MOTEngine::GetInstance()->GetCurrentGcSession()); // set current GC session in thread-pooled envelope
|
||||
|
||||
mtSessionThreadInfo->set_last_error(MT_MERR_OK);
|
||||
|
||||
existingItem = m_index.insert(key, sentinel, inserted, pid);
|
||||
|
||||
mtSessionThreadInfo->set_gc_session(NULL);
|
||||
mtSessionThreadInfo->set_working_index(NULL);
|
||||
|
||||
if (!inserted && existingItem) { // key mapping already exists in unique index
|
||||
if (!inserted) { // key mapping already exists in unique index
|
||||
result = reinterpret_cast<Sentinel*>(existingItem);
|
||||
} // otherwise return null pointer (if !inserted && !existingItem, Key does not exist and insertation failed due to
|
||||
// memory issue)
|
||||
} // otherwise return null pointer
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -111,8 +108,6 @@ Sentinel* MasstreePrimaryIndex::IndexRemoveImpl(const Key* key, uint32_t pid)
|
|||
mtSessionThreadInfo->set_gc_session(
|
||||
MOTEngine::GetInstance()->GetCurrentGcSession()); // set current GC session in thread-pooled envelope
|
||||
|
||||
mtSessionThreadInfo->set_last_error(MT_MERR_OK);
|
||||
|
||||
output = m_index.remove(key->GetKeyBuf(), key->GetKeyLength(), result, pid);
|
||||
|
||||
mtSessionThreadInfo->set_gc_session(NULL);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue