forked from huawei/openGauss-server
Compare commits
309 Commits
undorecycl
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
dac13f7edd | |
|
|
d060ec8971 | |
|
|
f2b23b7cf8 | |
|
|
4df7df69fc | |
|
|
ef49079f35 | |
|
|
19742c8434 | |
|
|
25d0e29ac0 | |
|
|
2735deae05 | |
|
|
eff5132bd3 | |
|
|
08cba03d86 | |
|
|
d46d3998e1 | |
|
|
e88d95da79 | |
|
|
788616ffb8 | |
|
|
2114ca86a2 | |
|
|
75cf0b4134 | |
|
|
d0cb929c6f | |
|
|
e77287962e | |
|
|
35ee6b9745 | |
|
|
5bb60ad543 | |
|
|
5bbbdf26d9 | |
|
|
ce7b86e7fb | |
|
|
3775f70b07 | |
|
|
5d86659bd6 | |
|
|
8ea182c5b0 | |
|
|
70288f1850 | |
|
|
56b2c6cd08 | |
|
|
9e65244713 | |
|
|
29e5630b68 | |
|
|
9b0745f77d | |
|
|
c45c127df5 | |
|
|
184182b0be | |
|
|
d936c0a403 | |
|
|
a5a6741636 | |
|
|
b496a59691 | |
|
|
74d1600b86 | |
|
|
0c5025f5dd | |
|
|
81e118d020 | |
|
|
ff3124a53f | |
|
|
77461eb859 | |
|
|
1b8563ebda | |
|
|
01a88a9c7b | |
|
|
f4fe449ba4 | |
|
|
38315c3047 | |
|
|
c2567e99dc | |
|
|
7e451a02ed | |
|
|
d95bce6550 | |
|
|
c7c2e1d859 | |
|
|
0e43d78b51 | |
|
|
17e4f7a03e | |
|
|
bc77c287a2 | |
|
|
d3efcd4564 | |
|
|
8761932145 | |
|
|
53c537a0ae | |
|
|
981e4a8f2e | |
|
|
59d9dfe333 | |
|
|
bd8af1fe46 | |
|
|
cee58fd2c2 | |
|
|
255240899c | |
|
|
cbc2e94653 | |
|
|
819accf25a | |
|
|
dfb3849780 | |
|
|
74fa63df6e | |
|
|
f0a0f5fcc5 | |
|
|
76815dc009 | |
|
|
1a882a9ee6 | |
|
|
7758348b68 | |
|
|
b074601139 | |
|
|
19ece33f53 | |
|
|
5d826c856b | |
|
|
e807068a8f | |
|
|
23f16eba7c | |
|
|
5bc56fc02f | |
|
|
5a196e8096 | |
|
|
6b0ccf6f62 | |
|
|
3a54c74459 | |
|
|
7576ca6e07 | |
|
|
09c55212e6 | |
|
|
f455fe3ac1 | |
|
|
04af05a859 | |
|
|
93ace67b06 | |
|
|
2a46b8bdd8 | |
|
|
dfc44d4a42 | |
|
|
3900e88c53 | |
|
|
41d9fcfd26 | |
|
|
8f4aea4f0b | |
|
|
e970fb92dc | |
|
|
1fbc036907 | |
|
|
4387e8852a | |
|
|
42b6cbb501 | |
|
|
c5547abf8e | |
|
|
8269dc065f | |
|
|
705c19c73d | |
|
|
95d9400d77 | |
|
|
0f06c8238c | |
|
|
49c1d030a9 | |
|
|
7a55bdb7cc | |
|
|
4c7cc4c269 | |
|
|
800ed82ca3 | |
|
|
81f221828d | |
|
|
541ddd61a6 | |
|
|
70773c8f53 | |
|
|
928b0c8fbb | |
|
|
22d9b9fe98 | |
|
|
5514a55bfa | |
|
|
94ef8aa0e3 | |
|
|
73189d9112 | |
|
|
ccb26b2152 | |
|
|
c1cda703b1 | |
|
|
01dd50c007 | |
|
|
4f76c8b27a | |
|
|
f0c4025a52 | |
|
|
e91972e82f | |
|
|
379ed252f3 | |
|
|
605b5b00f0 | |
|
|
915b2800cd | |
|
|
a8e2cf0915 | |
|
|
f12abf70f9 | |
|
|
c70b28b19f | |
|
|
8de0f268bd | |
|
|
70a1854ad9 | |
|
|
defd901624 | |
|
|
7b49a450dc | |
|
|
01163f2e40 | |
|
|
5eb65aa4ca | |
|
|
95b7221f15 | |
|
|
200c9b14a7 | |
|
|
95cd0ad4c7 | |
|
|
8790761995 | |
|
|
ae7a0bc797 | |
|
|
a6ce672068 | |
|
|
9e915d9fbf | |
|
|
76e3f4dad6 | |
|
|
8e9089b6fe | |
|
|
4b7af41aff | |
|
|
313e3c4e00 | |
|
|
43c3001415 | |
|
|
d9cde8c89b | |
|
|
91a7306a88 | |
|
|
eb4c1bd953 | |
|
|
991925bfe1 | |
|
|
6021c26abe | |
|
|
817264937d | |
|
|
62e2170f65 | |
|
|
19adb21866 | |
|
|
3446cb0aba | |
|
|
3c0a229006 | |
|
|
5f8ca172c7 | |
|
|
22b3c75a36 | |
|
|
8f463f2f77 | |
|
|
95fafe1ce0 | |
|
|
59ad28eed7 | |
|
|
4c345b1d31 | |
|
|
62e6f3a028 | |
|
|
c7eae459f2 | |
|
|
f9b4d9a702 | |
|
|
11e6760776 | |
|
|
272ebd125f | |
|
|
3a2a690ee8 | |
|
|
d0a5914e3f | |
|
|
dc65ea0040 | |
|
|
4728c88126 | |
|
|
cd677d1243 | |
|
|
3fde3abb91 | |
|
|
7679dfe93b | |
|
|
78d7d8e54f | |
|
|
8cd2925f93 | |
|
|
a4e1b350cb | |
|
|
32cdbbabff | |
|
|
cdc8d9748f | |
|
|
9017a1a64e | |
|
|
751e1c4267 | |
|
|
ac1d40fca2 | |
|
|
a7af1741d2 | |
|
|
8d84b37543 | |
|
|
bab8f82a0e | |
|
|
3c1a1c2bef | |
|
|
8902cfd59a | |
|
|
e7dd9fa2a1 | |
|
|
2812c3315c | |
|
|
5c988a09f4 | |
|
|
bcfff28219 | |
|
|
72a82e2182 | |
|
|
c7e4a74544 | |
|
|
cf86dd46f5 | |
|
|
edf79014bf | |
|
|
fb3e4435e0 | |
|
|
88840f46d2 | |
|
|
54de7afd7d | |
|
|
907997d5a6 | |
|
|
55ae2df08c | |
|
|
340b61c47f | |
|
|
4761d514e6 | |
|
|
0ac4f9bd4b | |
|
|
1a3fad4d43 | |
|
|
730e66cdf0 | |
|
|
00dd058431 | |
|
|
36efc4cbb7 | |
|
|
38ca00c31c | |
|
|
2165635b9a | |
|
|
e06c03a579 | |
|
|
4cd25670b5 | |
|
|
f70898ecf5 | |
|
|
95466298fa | |
|
|
119d5d39da | |
|
|
537cba6247 | |
|
|
14c4068daa | |
|
|
cf8e1541b1 | |
|
|
58d5c900c9 | |
|
|
25332da4aa | |
|
|
fd0098411a | |
|
|
b4f33b07cf | |
|
|
caccfc026d | |
|
|
30621924cf | |
|
|
abb0cee0fc | |
|
|
0c2af1d742 | |
|
|
09e299ac8a | |
|
|
7dc9903be8 | |
|
|
bc0dc304c8 | |
|
|
5f334a5656 | |
|
|
4ec1513e05 | |
|
|
fb0026b93b | |
|
|
ce56fb62b6 | |
|
|
2b198387ab | |
|
|
72f4d68de6 | |
|
|
62e3bed09b | |
|
|
7d4d95a5ad | |
|
|
240f61c595 | |
|
|
a0d1a547f8 | |
|
|
df804a8a91 | |
|
|
91917dcfaf | |
|
|
e2163187ca | |
|
|
1d7eadd778 | |
|
|
b335ef9af7 | |
|
|
572d99d03c | |
|
|
0b0959fc69 | |
|
|
238ee19a59 | |
|
|
d199d90a71 | |
|
|
2a77a3cebc | |
|
|
214d01ddb6 | |
|
|
4f024161f0 | |
|
|
82d5ac65b0 | |
|
|
7edda5e2a4 | |
|
|
e10c482649 | |
|
|
0a29277070 | |
|
|
96dad83a1d | |
|
|
d433ee097a | |
|
|
a590e0b47f | |
|
|
7f8b900e69 | |
|
|
f14255f040 | |
|
|
5af581f12a | |
|
|
26793e3bf2 | |
|
|
dbcf479205 | |
|
|
ae11cad2c3 | |
|
|
57bc5c4a37 | |
|
|
fb449157d0 | |
|
|
6c5898646e | |
|
|
c1335bebc9 | |
|
|
b2c6e04eab | |
|
|
16d32d3978 | |
|
|
3e14e85fdf | |
|
|
438bba32ff | |
|
|
d82eba4790 | |
|
|
4b7080c8ef | |
|
|
5e1d9b79f6 | |
|
|
51c355c7b6 | |
|
|
4c0a495a18 | |
|
|
cbb8fd609b | |
|
|
475170ca0c | |
|
|
7f9a1f5428 | |
|
|
c04e377cdc | |
|
|
6380ca0f4d | |
|
|
4898f04a3c | |
|
|
11a1a5c9ad | |
|
|
a30ae9d5ad | |
|
|
0b70c7ab70 | |
|
|
f719422916 | |
|
|
88dcc7f99e | |
|
|
dbd9958443 | |
|
|
38b5a0ac95 | |
|
|
a548f5c3c6 | |
|
|
eb4e54d4eb | |
|
|
3f7a909887 | |
|
|
d8b89ceea5 | |
|
|
2250adfd4b | |
|
|
fd34b5da2a | |
|
|
7f97d633f1 | |
|
|
6ba4c95f5f | |
|
|
5755feee5d | |
|
|
cd5d44d66c | |
|
|
2d657cddbe | |
|
|
ccd674c159 | |
|
|
8292238381 | |
|
|
5054ddd002 | |
|
|
48a5033c18 | |
|
|
811a9afcd9 | |
|
|
bb0aa02eb0 | |
|
|
02c14696bf | |
|
|
877f0b69e3 | |
|
|
120ddcc6d8 | |
|
|
2cc80a5727 | |
|
|
6d8bec171c | |
|
|
9f7da01725 | |
|
|
ea938c947e | |
|
|
498d7ac08d | |
|
|
40b717d9dd | |
|
|
94aeb766e5 | |
|
|
8577a61c7b | |
|
|
59db2a14cc | |
|
|
e7795cd01c |
|
|
@ -1,6 +1,7 @@
|
|||
#!/bin/bash
|
||||
#######################################################################
|
||||
# Copyright (c): 2020-2021, Huawei Tech. Co., Ltd.
|
||||
|
||||
# descript: Compile and pack MPPDB
|
||||
# Return 0 means OK.
|
||||
# Return 1 means failed.
|
||||
|
|
|
|||
|
|
@ -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) platform."
|
||||
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64), Asianux platform."
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
|
|
@ -96,16 +96,21 @@ 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) platform."
|
||||
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64), Asianux 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" ] ; then
|
||||
echo "We only support NUMA on openEuler(aarch64), EulerOS(aarch64), Kylin(aarch64) platform."
|
||||
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."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ 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">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
<refentry id="sql-alterprocedure">
|
||||
<indexterm zone="sql-alterprocedure">
|
||||
<primary>ALTER PROCEDURE</primary>
|
||||
</indexterm>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>ALTER PROCEDURE</refentrytitle>
|
||||
<manvolnum>7</manvolnum>
|
||||
<refmiscinfo>SQL - Language Statements</refmiscinfo>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>ALTER PROCEDURE</refname>
|
||||
<refpurpose>change the definition of a procedure</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
|
||||
<replaceable class="parameter">action</replaceable> [ ... ] [ RESTRICT ]
|
||||
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
|
||||
RENAME TO <replaceable>new_name</replaceable>
|
||||
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
|
||||
OWNER TO { <replaceable>new_owner</replaceable> | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
|
||||
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
|
||||
SET SCHEMA <replaceable>new_schema</replaceable>
|
||||
|
||||
<phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
|
||||
|
||||
[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
||||
SET <replaceable class="parameter">configuration_parameter</replaceable> { TO | = } { <replaceable class="parameter">value</replaceable> | DEFAULT }
|
||||
SET <replaceable class="parameter">configuration_parameter</replaceable> FROM CURRENT
|
||||
RESET <replaceable class="parameter">configuration_parameter</replaceable>
|
||||
RESET ALL
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
</refentry>
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
DROP TABLE [ IF EXISTS ]
|
||||
{[schema.]table_name} [, ...] [ CASCADE | RESTRICT ] [ PURGE ]};
|
||||
{[schema.]table_name} [, ...] [ CASCADE | RESTRICT ] [ PURGE ];
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
</refentry>
|
||||
|
|
@ -27,7 +27,14 @@
|
|||
#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,6 +34,14 @@
|
|||
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[] = {"|",
|
||||
|
|
@ -69,6 +77,14 @@ void check_path(const char *path_name)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function name: check_key_num
|
||||
description: Check if the password is a null string, if so, then the password is invalid.
|
||||
At the same time, the function check if the length of password exceeds MAX_CRYPT_LEN, if so, print the error.
|
||||
arguments: A pointer to string that its type is const char
|
||||
return value: An integer that its type is static int
|
||||
Note:The length of password should not be zero, and never exceeds MAX_CRYPT_LEN
|
||||
*/
|
||||
static int check_key_num(const char* password)
|
||||
{
|
||||
int key_len = 0;
|
||||
|
|
|
|||
|
|
@ -1229,6 +1229,12 @@ parse_next_sync_groups(char **pgroup, char *result)
|
|||
static int
|
||||
transform_az_name(char *config_value, char *allAZString, int allAZStringBufLen, const char *data_dir)
|
||||
{
|
||||
if (strcmp(config_value, "''") == 0) {
|
||||
errno_t rc = strncpy_s(allAZString, allAZStringBufLen, config_value, strlen(config_value));
|
||||
securec_check_c(rc, "\0", "\0");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
char *azString = NULL;
|
||||
char *buf = allAZString;
|
||||
int buflen = allAZStringBufLen;
|
||||
|
|
|
|||
|
|
@ -5923,7 +5923,7 @@ int main(int argc, char** argv)
|
|||
&option_index)) != -1)
|
||||
#endif
|
||||
#else
|
||||
while ((c = getopt_long(argc, argv, "b:cD:e:fi:G:l:m:M:N:o:O:p:P:r:R:v:x:sS:t:u:U:wWZ:dqL:T:Q:", long_options,
|
||||
while ((c = getopt_long(argc, argv, "b:cD:e:fi:G:l:m:M:N:o:O:p:P:r:R:v:x:sS:t:u:U:wWZ:C:dqL:T:Q:", long_options,
|
||||
&option_index)) != -1)
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ char* all_data_nodename_list = NULL;
|
|||
const uint32 USTORE_UPGRADE_VERSION = 92368;
|
||||
const uint32 PACKAGE_ENHANCEMENT = 92444;
|
||||
const uint32 SUBSCRIPTION_VERSION = 92580;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92607;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
|
||||
|
||||
#ifdef DUMPSYSLOG
|
||||
char* syslogpath = NULL;
|
||||
|
|
@ -4454,16 +4454,7 @@ void getSubscriptions(Archive *fout)
|
|||
}
|
||||
|
||||
if (!isExecUserSuperRole(fout)) {
|
||||
res = ExecuteSqlQuery(fout,
|
||||
"SELECT count(*) FROM pg_subscription "
|
||||
"WHERE subdbid = (SELECT oid FROM pg_catalog.pg_database"
|
||||
" WHERE datname = current_database())",
|
||||
PGRES_TUPLES_OK);
|
||||
uint64 n = (res != NULL) ? strtoul(PQgetvalue(res, 0, 0), NULL, 10) : 0;
|
||||
if (n > 0) {
|
||||
write_msg(NULL, "WARNING: subscriptions not dumped because current user is not a superuser\n");
|
||||
}
|
||||
PQclear(res);
|
||||
write_msg(NULL, "WARNING: subscriptions not dumped because current user is not a superuser\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -10795,6 +10786,11 @@ 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");
|
||||
|
||||
|
|
@ -21404,6 +21400,11 @@ static void dumpSynonym(Archive* fout)
|
|||
PQExpBuffer q;
|
||||
PQExpBuffer delq;
|
||||
|
||||
if (!isExecUserSuperRole(fout)) {
|
||||
write_msg(NULL, "WARNING: synonym not dumped because current user is not a superuser\n");
|
||||
return;
|
||||
}
|
||||
|
||||
selectSourceSchema(fout, "pg_catalog");
|
||||
query = createPQExpBuffer();
|
||||
printfPQExpBuffer(query,
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@
|
|||
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;
|
||||
|
|
@ -89,10 +92,11 @@ 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);
|
||||
static void do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs,
|
||||
bool backup_replslots);
|
||||
|
||||
static void pg_start_backup(const char *label, bool smooth, pgBackup *backup,
|
||||
PGNodeInfo *nodeInfo, PGconn *conn);
|
||||
PGNodeInfo *nodeInfo, PGconn *conn, bool backup_replslots);
|
||||
static void pg_stop_backup(pgBackup *backup, PGconn *pg_startbackup_conn, PGNodeInfo *nodeInfo);
|
||||
static int checkpoint_timeout(PGconn *backup_conn);
|
||||
|
||||
|
|
@ -558,7 +562,7 @@ static void sync_files(parray *database_map, const char *database_path, parray *
|
|||
* Move files from 'pgdata' to a subdirectory in 'backup_path'.
|
||||
*/
|
||||
static void
|
||||
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs)
|
||||
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs, bool backup_replslots)
|
||||
{
|
||||
int i;
|
||||
char database_path[MAXPGPATH];
|
||||
|
|
@ -591,7 +595,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
|
|||
securec_check_c(rc, "\0", "\0");
|
||||
|
||||
/* Call pg_start_backup function in openGauss connect */
|
||||
pg_start_backup(label, smooth_checkpoint, ¤t, nodeInfo, backup_conn);
|
||||
pg_start_backup(label, smooth_checkpoint, ¤t, nodeInfo, backup_conn, backup_replslots);
|
||||
|
||||
/* Obtain current timeline */
|
||||
#if PG_VERSION_NUM >= 90600
|
||||
|
|
@ -624,10 +628,10 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
|
|||
/* list files with the logical path. omit $PGDATA */
|
||||
if (fio_is_remote(FIO_DB_HOST))
|
||||
fio_list_dir(backup_files_list, instance_config.pgdata,
|
||||
true, true, false, backup_logs, true, 0);
|
||||
true, true, false, backup_logs, true, 0, backup_replslots);
|
||||
else
|
||||
dir_list_file(backup_files_list, instance_config.pgdata,
|
||||
true, true, false, backup_logs, true, 0, FIO_LOCAL_HOST);
|
||||
true, true, false, backup_logs, true, 0, FIO_LOCAL_HOST, backup_replslots);
|
||||
|
||||
/*
|
||||
* Get database_map (name to oid) for use in partial restore feature.
|
||||
|
|
@ -749,6 +753,11 @@ 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)
|
||||
{
|
||||
|
|
@ -849,7 +858,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 no_validate, bool no_sync, bool backup_logs, bool backup_replslots)
|
||||
{
|
||||
PGconn *backup_conn = NULL;
|
||||
PGNodeInfo nodeInfo;
|
||||
|
|
@ -925,7 +934,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);
|
||||
do_backup_instance(backup_conn, &nodeInfo, no_sync, backup_logs, backup_replslots);
|
||||
pgut_atexit_pop(backup_cleanup, NULL);
|
||||
|
||||
/* compute size of wal files of this backup stored in the archive */
|
||||
|
|
@ -1034,13 +1043,15 @@ confirm_block_size(PGconn *conn, const char *name, int blcksz)
|
|||
*/
|
||||
static void
|
||||
pg_start_backup(const char *label, bool smooth, pgBackup *backup,
|
||||
PGNodeInfo *nodeInfo, PGconn *conn)
|
||||
PGNodeInfo *nodeInfo, PGconn *conn, bool backup_replslots)
|
||||
{
|
||||
PGresult *res;
|
||||
const char *params[2];
|
||||
uint32 lsn_hi;
|
||||
uint32 lsn_lo;
|
||||
int ret;
|
||||
int i;
|
||||
XLogRecPtr startLsn;
|
||||
|
||||
params[0] = label;
|
||||
|
||||
|
|
@ -1068,7 +1079,33 @@ pg_start_backup(const char *label, bool smooth, pgBackup *backup,
|
|||
XLogDataFromLSN(ret, PQgetvalue(res, 0, 0), &lsn_hi, &lsn_lo);
|
||||
securec_check_for_sscanf_s(ret, 2, "\0", "\0");
|
||||
/* Calculate LSN */
|
||||
backup->start_lsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
|
||||
startLsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
|
||||
|
||||
if (backup_replslots) {
|
||||
logical_replslot = parray_new();
|
||||
/* query for logical replication slots of subscriptions */
|
||||
res = pgut_execute(conn,
|
||||
"SELECT slot_name, restart_lsn FROM pg_catalog.pg_get_replication_slots()"
|
||||
"WHERE slot_type = 'logical' AND plugin = 'pgoutput'", 0, NULL);
|
||||
if (PQntuples(res) == 0) {
|
||||
elog(LOG, "logical replication slots for subscriptions not found");
|
||||
} else {
|
||||
XLogRecPtr repslotLsn;
|
||||
|
||||
for (i = 0; i < PQntuples(res); i++) {
|
||||
XLogDataFromLSN(ret, PQgetvalue(res, i, 1), &lsn_hi, &lsn_lo);
|
||||
securec_check_for_sscanf_s(ret, 2, "\0", "\0");
|
||||
repslotLsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
|
||||
startLsn = Min(startLsn, repslotLsn);
|
||||
|
||||
char* slotname = pg_strdup(PQgetvalue(res, i, 0));
|
||||
parray_append(logical_replslot, slotname);
|
||||
}
|
||||
elog(WARNING, "logical replication slots for subscriptions will be backed up. "
|
||||
"If don't use them after restoring, please drop them to avoid affecting xlog recycling.");
|
||||
}
|
||||
}
|
||||
backup->start_lsn = startLsn;
|
||||
|
||||
PQclear(res);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,13 +42,6 @@ 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",
|
||||
|
||||
|
|
@ -68,7 +61,7 @@ const char *pgdata_exclude_dir[] =
|
|||
(const char *)"pg_subtrans",
|
||||
|
||||
/* end of list */
|
||||
NULL, /* pg_log will be set later */
|
||||
NULL, /* pg_log and pg_replslot will be set later */
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -128,17 +121,20 @@ 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);
|
||||
static char dir_check_file(pgFile *file, bool backup_logs, bool backup_replslots);
|
||||
static char check_in_tablespace(pgFile *file, bool in_tablespace);
|
||||
static char check_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 skip_hidden, int external_dir_num, fio_location location,
|
||||
bool backup_replslots);
|
||||
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 */
|
||||
|
|
@ -538,7 +534,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)
|
||||
fio_location location, bool backup_replslots)
|
||||
{
|
||||
pgFile *file;
|
||||
|
||||
|
|
@ -565,7 +561,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_logs, skip_hidden, external_dir_num, location, backup_replslots);
|
||||
|
||||
if (!add_root)
|
||||
pgFileFree(file);
|
||||
|
|
@ -589,7 +585,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)
|
||||
dir_check_file(pgFile *file, bool backup_logs, bool backup_replslots)
|
||||
{
|
||||
int i;
|
||||
int sscanf_res;
|
||||
|
|
@ -652,6 +648,29 @@ dir_check_file(pgFile *file, bool backup_logs)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Backup pg_replslot if it is specified.
|
||||
* It is generally not useful to backup the contents of this directory even
|
||||
* if the intention is to restore to another master. See backup.sgml for a
|
||||
* more detailed description.
|
||||
*/
|
||||
if (!backup_replslots) {
|
||||
if (strcmp(file->rel_path, PG_REPLSLOT_DIR) == 0) {
|
||||
/* Skip */
|
||||
elog(VERBOSE, "Excluding directory content: %s", file->rel_path);
|
||||
return CHECK_EXCLUDE_FALSE;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Check file that under pg_replslot and judge whether it
|
||||
* belonged to logical replication slots for subscriptions.
|
||||
*/
|
||||
if (strcmp(file->rel_path, PG_REPLSLOT_DIR) != 0 &&
|
||||
path_is_prefix_of_path(PG_REPLSLOT_DIR, file->rel_path)) {
|
||||
return check_logical_replslot_dir(file->rel_path);
|
||||
}
|
||||
}
|
||||
|
||||
ret = check_nobackup_dir(file);
|
||||
if (ret != -1) { /* -1 means need backup */
|
||||
return ret;
|
||||
|
|
@ -749,6 +768,35 @@ 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;
|
||||
|
|
@ -889,7 +937,8 @@ 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 skip_hidden, int external_dir_num, fio_location location,
|
||||
bool backup_replslots)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *dent;
|
||||
|
|
@ -937,7 +986,7 @@ dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
|
|||
|
||||
if (exclude)
|
||||
{
|
||||
check_res = dir_check_file(file, backup_logs);
|
||||
check_res = dir_check_file(file, backup_logs, backup_replslots);
|
||||
if (check_res == CHECK_FALSE)
|
||||
{
|
||||
/* Skip */
|
||||
|
|
@ -963,7 +1012,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_logs, skip_hidden, external_dir_num, location, backup_replslots);
|
||||
}
|
||||
|
||||
if (errno && errno != ENOENT)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ typedef struct
|
|||
bool exclusive_backup;
|
||||
bool skip_hidden;
|
||||
int external_dir_num;
|
||||
bool backup_replslots;
|
||||
} fio_list_dir_request;
|
||||
|
||||
typedef struct
|
||||
|
|
@ -1794,7 +1795,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 skip_hidden, int external_dir_num, bool backup_replslots)
|
||||
{
|
||||
fio_header hdr;
|
||||
fio_list_dir_request req;
|
||||
|
|
@ -1811,6 +1812,7 @@ void fio_list_dir(parray *files, const char *root, bool exclude,
|
|||
req.exclusive_backup = exclusive_backup;
|
||||
req.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);
|
||||
|
|
@ -1870,7 +1872,14 @@ 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);
|
||||
}
|
||||
|
|
@ -1914,7 +1923,7 @@ static void fio_list_dir_impl(int out, char* buf)
|
|||
|
||||
dir_list_file(file_files, req->path, req->exclude, req->follow_symlink,
|
||||
req->add_root, req->backup_logs, req->skip_hidden,
|
||||
req->external_dir_num, FIO_LOCAL_HOST);
|
||||
req->external_dir_num, FIO_LOCAL_HOST, req->backup_replslots);
|
||||
|
||||
/* send information about files to the main process */
|
||||
for (i = 0; i < (int)parray_num(file_files); i++)
|
||||
|
|
|
|||
|
|
@ -163,5 +163,7 @@ extern z_off_t fio_gzseek(gzFile f, z_off_t offset, int whence);
|
|||
extern const char* fio_gzerror(gzFile file, int *errnum);
|
||||
#endif
|
||||
|
||||
extern char check_logical_replslot_dir(const char *rel_path);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ 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);
|
||||
|
|
@ -420,6 +421,7 @@ 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"));
|
||||
|
|
@ -441,6 +443,7 @@ 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,6 +77,7 @@ 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;
|
||||
|
|
@ -186,6 +187,7 @@ 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 },
|
||||
|
|
@ -550,7 +552,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);
|
||||
return do_backup(start_time, set_backup_params, no_validate, no_sync, backup_logs, backup_replslots);
|
||||
}
|
||||
case RESTORE_CMD:
|
||||
return do_restore_or_validate(current.backup_id,
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ extern const char *PROGRAM_FULL_PATH;
|
|||
#define HEADER_MAP "page_header_map"
|
||||
#define HEADER_MAP_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,6 +54,9 @@ 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;
|
||||
|
||||
|
|
@ -89,7 +92,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 no_validate, bool no_sync, bool backup_logs, bool backup_replslots);
|
||||
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,
|
||||
|
|
@ -239,7 +242,8 @@ 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 skip_hidden, int external_dir_num, fio_location location,
|
||||
bool backup_replslots = false);
|
||||
|
||||
extern void create_data_directories(parray *dest_files,
|
||||
const char *data_dir,
|
||||
|
|
@ -432,7 +436,8 @@ extern int fio_send_file(const char *from_fullpath, const char *to_fullpath, FIL
|
|||
pgFile *file, char **errormsg);
|
||||
|
||||
extern void fio_list_dir(parray *files, const char *root, bool exclude, bool follow_symlink,
|
||||
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num);
|
||||
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num,
|
||||
bool backup_replslots = false);
|
||||
|
||||
extern bool pgut_rmtree(const char *path, bool rmtopdir, bool strict);
|
||||
|
||||
|
|
|
|||
|
|
@ -8641,7 +8641,7 @@
|
|||
),
|
||||
AddFuncGroup(
|
||||
"pg_stat_get_wal_senders", 1,
|
||||
AddBuiltinFunc(_0(3099), _1("pg_stat_get_wal_senders"), _2(0), _3(false), _4(true), _5(pg_stat_get_wal_senders), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(0), _21(22, 20, 23, 25, 25, 25, 25, 1184, 1184, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 23, 23, 25, 25), _22(22, 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o'), _23(22, "pid", "sender_pid", "local_role", "peer_role", "peer_state", "state", "catchup_start", "catchup_end", "sender_sent_location", "sender_write_location", "sender_flush_location", "sender_replay_location", "receiver_received_location", "receiver_write_location", "receiver_flush_location", "receiver_replay_location", "sync_percent", "sync_state", "sync_group", "sync_priority", "sync_most_available", "channel"), _24(NULL), _25("pg_stat_get_wal_senders"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("statistics: information about currently active replication"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0))
|
||||
AddBuiltinFunc(_0(3099), _1("pg_stat_get_wal_senders"), _2(0), _3(false), _4(true), _5(pg_stat_get_wal_senders), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(0), _21(21, 20, 23, 25, 25, 25, 25, 1184, 1184, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 23, 25, 25), _22(21, 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o'), _23(21, "pid", "sender_pid", "local_role", "peer_role", "peer_state", "state", "catchup_start", "catchup_end", "sender_sent_location", "sender_write_location", "sender_flush_location", "sender_replay_location", "receiver_received_location", "receiver_write_location", "receiver_flush_location", "receiver_replay_location", "sync_percent", "sync_state", "sync_priority", "sync_most_available", "channel"), _24(NULL), _25("pg_stat_get_wal_senders"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("statistics: information about currently active replication"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0))
|
||||
),
|
||||
AddFuncGroup(
|
||||
"pg_stat_get_wlm_ec_operator_info", 1,
|
||||
|
|
|
|||
|
|
@ -6230,7 +6230,7 @@ Datum GetPartBoundaryByTuple(Relation rel, HeapTuple tuple)
|
|||
return Timestamp2Boundarys(rel, Align2UpBoundary(value, partMap->intervalValue, boundaryTs));
|
||||
}
|
||||
|
||||
Oid AddNewIntervalPartition(Relation rel, void* insertTuple)
|
||||
Oid AddNewIntervalPartition(Relation rel, void* insertTuple, bool isDDL)
|
||||
{
|
||||
Relation pgPartRel = NULL;
|
||||
Oid newPartOid = InvalidOid;
|
||||
|
|
@ -6327,7 +6327,13 @@ Oid AddNewIntervalPartition(Relation rel, void* insertTuple)
|
|||
*/
|
||||
CommandCounterIncrement();
|
||||
|
||||
UpdatePgObjectChangecsn(RelationGetRelid(rel), rel->rd_rel->relkind);
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
return newPartOid;
|
||||
}
|
||||
|
|
@ -7113,7 +7119,7 @@ int lookupHBucketid(oidvector *buckets, int low, int2 bktId)
|
|||
* Description :
|
||||
* Notes :
|
||||
*/
|
||||
Oid heapTupleGetPartitionId(Relation rel, void *tuple)
|
||||
Oid heapTupleGetPartitionId(Relation rel, void *tuple, bool isDDL)
|
||||
{
|
||||
Oid partitionid = InvalidOid;
|
||||
|
||||
|
|
@ -7140,7 +7146,7 @@ Oid heapTupleGetPartitionId(Relation rel, void *tuple)
|
|||
(errcode(ERRCODE_NO_DATA_FOUND), errmsg("inserted partition key does not map to any table partition")));
|
||||
} break;
|
||||
case PART_AREA_INTERVAL: {
|
||||
return AddNewIntervalPartition(rel, tuple);
|
||||
return AddNewIntervalPartition(rel, tuple, isDDL);
|
||||
} break;
|
||||
case PART_AREA_LIST: {
|
||||
ereport(ERROR,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ static void InternalAggIsSupported(const char *aggName)
|
|||
"json_agg",
|
||||
"json_object_agg",
|
||||
"st_summarystatsagg",
|
||||
"st_union"
|
||||
"st_union",
|
||||
"wm_concat"
|
||||
};
|
||||
|
||||
uint len = lengthof(supportList);
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ static_assert(sizeof(false) == sizeof(char), "illegal bool size");
|
|||
static struct HTAB* nameHash = NULL;
|
||||
static struct HTAB* oidHash = NULL;
|
||||
|
||||
/* for b_sql_plugin */
|
||||
/* for dolphin */
|
||||
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.b_sql_plugin) {
|
||||
if (DB_IS_CMPT(B_FORMAT) && b_nameHash != NULL && u_sess->attr.attr_sql.dolphin) {
|
||||
result = (HashEntryNameToFuncGroup *)hash_search(b_nameHash, &temp_name, action, &found);
|
||||
} 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.b_sql_plugin) {
|
||||
if (DB_IS_CMPT(B_FORMAT) && b_oidHash != NULL && u_sess->attr.attr_sql.dolphin) {
|
||||
result = (HashEntryOidToBuiltinFunc *)hash_search(b_oidHash, &oid, action, &found);
|
||||
} else {
|
||||
result = (HashEntryOidToBuiltinFunc *)hash_search(oidHash, &oid, action, &found);
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ Subscription *GetSubscription(Oid subid, bool missing_ok)
|
|||
|
||||
/* Get slotname */
|
||||
datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subslotname, &isnull);
|
||||
if (unlikely(isnull)) {
|
||||
if (!isnull) {
|
||||
sub->slotname = pstrdup(NameStr(*DatumGetName(datum)));
|
||||
} else {
|
||||
sub->slotname = NULL;
|
||||
|
|
@ -92,10 +92,10 @@ Subscription *GetSubscription(Oid subid, bool missing_ok)
|
|||
|
||||
datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subbinary, &isnull);
|
||||
if (unlikely(isnull)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
||||
errmsg("null binary for subscription %u", subid)));
|
||||
sub->binary = false;
|
||||
} else {
|
||||
sub->binary = DatumGetBool(datum);
|
||||
}
|
||||
sub->binary = DatumGetBool(datum);
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
/***
|
||||
* @Author: 张鹏春
|
||||
* @Team: 兰心开源
|
||||
* @Date: 2023-09-11 20:25:05
|
||||
*/
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* dllist.cpp
|
||||
|
|
@ -13,317 +20,292 @@
|
|||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
#include "postgres.h"
|
||||
#include "knl/knl_variable.h"
|
||||
|
||||
#include "lib/dllist.h"
|
||||
#include "miscadmin.h"
|
||||
|
||||
Dllist* DLNewList(void)
|
||||
{
|
||||
Dllist* l = NULL;
|
||||
Dllist* DLNewList(void) {
|
||||
Dllist* l = NULL; // list pointer
|
||||
|
||||
l = (Dllist*)palloc(sizeof(Dllist));
|
||||
l = (Dllist*)palloc(sizeof(Dllist)); // allocate memory
|
||||
|
||||
l->dll_head = NULL;
|
||||
l->dll_tail = NULL;
|
||||
l->dll_len = 0;
|
||||
l->dll_head = NULL; // init head pointer
|
||||
l->dll_tail = NULL; // init tail pointer
|
||||
l->dll_len = 0; // init length
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
void DLInitList(Dllist* list)
|
||||
{
|
||||
list->dll_head = NULL;
|
||||
list->dll_tail = NULL;
|
||||
list->dll_len = 0;
|
||||
void DLInitList(Dllist* list) {
|
||||
list->dll_head = NULL; // init head pointer
|
||||
list->dll_tail = NULL; // init tail pointer
|
||||
list->dll_len = 0; // init length
|
||||
}
|
||||
|
||||
/*
|
||||
* free up a list and all the nodes in it --- but *not* whatever the nodes
|
||||
* might point to!
|
||||
*/
|
||||
void DLFreeList(Dllist* list)
|
||||
{
|
||||
Dlelem* curr = NULL;
|
||||
void DLFreeList(Dllist* list) {
|
||||
Dlelem* curr = NULL; // current pointer
|
||||
|
||||
while ((curr = DLRemHead(list)) != NULL)
|
||||
while ((curr = DLRemHead(list)) != NULL) // remove head from list
|
||||
pfree(curr);
|
||||
|
||||
pfree(list);
|
||||
pfree(list); // free list
|
||||
}
|
||||
|
||||
Dlelem* DLNewElem(void* val)
|
||||
{
|
||||
Dlelem* e = NULL;
|
||||
Dlelem* DLNewElem(void* val) {
|
||||
Dlelem* e = NULL; // element pointer
|
||||
|
||||
e = (Dlelem*)palloc(sizeof(Dlelem));
|
||||
e = (Dlelem*)palloc(sizeof(Dlelem)); // allocate memory
|
||||
|
||||
e->dle_next = NULL;
|
||||
e->dle_prev = NULL;
|
||||
e->dle_val = val;
|
||||
e->dle_list = NULL;
|
||||
e->dle_next = NULL; // init next pointer
|
||||
e->dle_prev = NULL; // init prev pointer
|
||||
e->dle_val = val; // init value
|
||||
e->dle_list = NULL; // init list
|
||||
return e;
|
||||
}
|
||||
|
||||
void DLInitElem(Dlelem* e, void* val)
|
||||
{
|
||||
e->dle_next = NULL;
|
||||
e->dle_prev = NULL;
|
||||
e->dle_val = val;
|
||||
e->dle_list = NULL;
|
||||
void DLInitElem(Dlelem* e, void* val) {
|
||||
e->dle_next = NULL; // init next pointer
|
||||
e->dle_prev = NULL; // init prev pointer
|
||||
e->dle_val = val; // init value
|
||||
e->dle_list = NULL; // init list
|
||||
}
|
||||
|
||||
void DLFreeElem(Dlelem* e)
|
||||
{
|
||||
pfree(e);
|
||||
void DLFreeElem(Dlelem* e) {
|
||||
pfree(e); // free element
|
||||
}
|
||||
|
||||
void DLRemove(Dlelem* e)
|
||||
{
|
||||
Dllist* l = e->dle_list;
|
||||
void DLRemove(Dlelem* e) {
|
||||
Dllist* l = e->dle_list; // list pointer
|
||||
|
||||
if (e->dle_prev)
|
||||
if (e->dle_prev) // if e has prev element
|
||||
e->dle_prev->dle_next = e->dle_next;
|
||||
else {
|
||||
/* must be the head element */
|
||||
Assert(e == l->dll_head);
|
||||
l->dll_head = e->dle_next;
|
||||
l->dll_head = e->dle_next; // set head pointer
|
||||
}
|
||||
if (e->dle_next)
|
||||
e->dle_next->dle_prev = e->dle_prev;
|
||||
else {
|
||||
/* must be the tail element */
|
||||
Assert(e == l->dll_tail);
|
||||
l->dll_tail = e->dle_prev;
|
||||
l->dll_tail = e->dle_prev; // set tail pointer
|
||||
}
|
||||
if (l != NULL) {
|
||||
l->dll_len--;
|
||||
l->dll_len--; // decrease length
|
||||
}
|
||||
|
||||
e->dle_next = NULL;
|
||||
e->dle_next = NULL; // reset pointer
|
||||
e->dle_prev = NULL;
|
||||
e->dle_list = NULL;
|
||||
}
|
||||
|
||||
void DLAddHead(Dllist* l, Dlelem* e)
|
||||
{
|
||||
e->dle_list = l;
|
||||
void DLAddHead(Dllist* l, Dlelem* e) {
|
||||
e->dle_list = l; // set list pointer
|
||||
|
||||
if (l->dll_head)
|
||||
l->dll_head->dle_prev = e;
|
||||
e->dle_next = l->dll_head;
|
||||
e->dle_prev = NULL;
|
||||
l->dll_head = e;
|
||||
if (l->dll_head) // if list is not empty
|
||||
l->dll_head->dle_prev = e; // set prev pointer
|
||||
e->dle_next = l->dll_head; // set next pointer
|
||||
e->dle_prev = NULL; // set prev pointer
|
||||
l->dll_head = e; // set head pointer
|
||||
|
||||
if (l->dll_tail == NULL) /* if this is first element added */
|
||||
l->dll_tail = e;
|
||||
l->dll_len++;
|
||||
l->dll_tail = e; // set tail pointer
|
||||
l->dll_len++; // increase length
|
||||
}
|
||||
|
||||
void DLAddTail(Dllist* l, Dlelem* e)
|
||||
{
|
||||
e->dle_list = l;
|
||||
void DLAddTail(Dllist* l, Dlelem* e) {
|
||||
e->dle_list = l; // set list pointer
|
||||
|
||||
if (l->dll_tail)
|
||||
l->dll_tail->dle_next = e;
|
||||
e->dle_prev = l->dll_tail;
|
||||
e->dle_next = NULL;
|
||||
l->dll_tail = e;
|
||||
if (l->dll_tail) // if list is not empty
|
||||
l->dll_tail->dle_next = e; // set next pointer
|
||||
e->dle_prev = l->dll_tail; // set prev pointer
|
||||
e->dle_next = NULL; // set next pointer
|
||||
l->dll_tail = e; // set tail pointer
|
||||
|
||||
if (l->dll_head == NULL) /* if this is first element added */
|
||||
l->dll_head = e;
|
||||
l->dll_len++;
|
||||
l->dll_len++; // increase length
|
||||
}
|
||||
|
||||
Dlelem* DLRemHead(Dllist* l)
|
||||
{
|
||||
Dlelem* DLRemHead(Dllist* l) {
|
||||
/* remove and return the head */
|
||||
Dlelem* result = l->dll_head;
|
||||
|
||||
if (result == NULL)
|
||||
if (result == NULL) /* if list is empty */
|
||||
return result;
|
||||
|
||||
if (result->dle_next)
|
||||
result->dle_next->dle_prev = NULL;
|
||||
if (result->dle_next) // if head has next element
|
||||
result->dle_next->dle_prev = NULL; // set prev pointer
|
||||
|
||||
l->dll_head = result->dle_next;
|
||||
l->dll_head = result->dle_next; // set head pointer
|
||||
|
||||
if (result == l->dll_tail) /* if the head is also the tail */
|
||||
l->dll_tail = NULL;
|
||||
|
||||
l->dll_len--;
|
||||
result->dle_next = NULL;
|
||||
l->dll_len--; // decrease length
|
||||
result->dle_next = NULL; // reset pointer
|
||||
result->dle_list = NULL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Dlelem* DLRemTail(Dllist* l)
|
||||
{
|
||||
Dlelem* DLRemTail(Dllist* l) {
|
||||
/* remove and return the tail */
|
||||
Dlelem* result = l->dll_tail;
|
||||
|
||||
if (result == NULL)
|
||||
if (result == NULL) /* if list is empty */
|
||||
return result;
|
||||
|
||||
if (result->dle_prev)
|
||||
result->dle_prev->dle_next = NULL;
|
||||
if (result->dle_prev) // if tail has prev element
|
||||
result->dle_prev->dle_next = NULL; // set the previous poninter's next pointer
|
||||
|
||||
l->dll_tail = result->dle_prev;
|
||||
l->dll_tail = result->dle_prev; // set tail pointer
|
||||
|
||||
if (result == l->dll_head) /* if the tail is also the head */
|
||||
l->dll_head = NULL;
|
||||
l->dll_head = NULL; // set head pointer
|
||||
|
||||
l->dll_len--;
|
||||
result->dle_prev = NULL;
|
||||
l->dll_len--; // decrease length
|
||||
result->dle_prev = NULL; // reset pointer
|
||||
result->dle_list = NULL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Same as DLRemove followed by DLAddHead, but faster */
|
||||
void DLMoveToFront(Dlelem* e)
|
||||
{
|
||||
Dllist* l = e->dle_list;
|
||||
void DLMoveToFront(Dlelem* e) {
|
||||
Dllist* l = e->dle_list; // list pointer
|
||||
|
||||
if (l->dll_head == e)
|
||||
return; /* Fast path if already at front */
|
||||
|
||||
Assert(e->dle_prev != NULL); /* since it's not the head */
|
||||
e->dle_prev->dle_next = e->dle_next;
|
||||
e->dle_prev->dle_next = e->dle_next; // set next pointer
|
||||
|
||||
if (e->dle_next)
|
||||
e->dle_next->dle_prev = e->dle_prev;
|
||||
if (e->dle_next) // if e has next element
|
||||
e->dle_next->dle_prev = e->dle_prev; // set prev pointer
|
||||
else {
|
||||
/* must be the tail element */
|
||||
Assert(e == l->dll_tail);
|
||||
l->dll_tail = e->dle_prev;
|
||||
l->dll_tail = e->dle_prev; // set tail pointer
|
||||
}
|
||||
|
||||
l->dll_head->dle_prev = e;
|
||||
e->dle_next = l->dll_head;
|
||||
e->dle_prev = NULL;
|
||||
l->dll_head = e;
|
||||
l->dll_head->dle_prev = e; // set prev pointer
|
||||
e->dle_next = l->dll_head; // set next pointer
|
||||
e->dle_prev = NULL; // set prev pointer
|
||||
l->dll_head = e; // set head pointer
|
||||
/* We need not check dll_tail, since there must have been > 1 entry */
|
||||
}
|
||||
|
||||
/*
|
||||
* double-linked list length
|
||||
*/
|
||||
uint64 DLListLength(Dllist* list)
|
||||
{
|
||||
Dlelem* cur = list->dll_head;
|
||||
uint64 length = 0;
|
||||
uint64 DLListLength(Dllist* list) {
|
||||
Dlelem* cur = list->dll_head; // current pointer
|
||||
uint64 length = 0; // init length
|
||||
|
||||
while (cur != NULL) {
|
||||
length++;
|
||||
cur = cur->dle_next;
|
||||
while (cur != NULL) { // traverse list
|
||||
length++; // increase length
|
||||
cur = cur->dle_next; // get next pointer
|
||||
}
|
||||
Assert(length == list->dll_len);
|
||||
return length;
|
||||
Assert(length == list->dll_len); // check length
|
||||
return length; // return length
|
||||
}
|
||||
|
||||
DllistWithLock::DllistWithLock()
|
||||
{
|
||||
DLInitList(&m_list);
|
||||
SpinLockInit(&m_lock);
|
||||
DllistWithLock::DllistWithLock() {
|
||||
DLInitList(&m_list); // init list
|
||||
SpinLockInit(&m_lock); // init lock
|
||||
}
|
||||
|
||||
DllistWithLock::~DllistWithLock()
|
||||
{
|
||||
SpinLockFree(&m_lock);
|
||||
DllistWithLock::~DllistWithLock() {
|
||||
SpinLockFree(&m_lock); // free lock
|
||||
}
|
||||
|
||||
bool DllistWithLock::RemoveConfirm(Dlelem* e)
|
||||
{
|
||||
bool found = false;
|
||||
START_CRIT_SECTION();
|
||||
SpinLockAcquire(&(m_lock));
|
||||
if (e->dle_list == &m_list) {
|
||||
bool DllistWithLock::RemoveConfirm(Dlelem* e) {
|
||||
bool found = false; // found flag
|
||||
START_CRIT_SECTION(); // start critical section,avoid interrupt
|
||||
SpinLockAcquire(&(m_lock)); // get lock
|
||||
if (e->dle_list == &m_list) { // if e is in list
|
||||
found = true;
|
||||
DLRemove(e);
|
||||
DLRemove(e); // remove e from list
|
||||
}
|
||||
SpinLockRelease(&(m_lock));
|
||||
END_CRIT_SECTION();
|
||||
return found;
|
||||
SpinLockRelease(&(m_lock)); // release lock
|
||||
END_CRIT_SECTION(); // end critical section
|
||||
return found; // return found flag
|
||||
}
|
||||
|
||||
void DllistWithLock::AddHead(Dlelem* e)
|
||||
{
|
||||
START_CRIT_SECTION();
|
||||
SpinLockAcquire(&(m_lock));
|
||||
if (e->dle_list == NULL) {
|
||||
DLAddHead(&m_list, e);
|
||||
void DllistWithLock::AddHead(Dlelem* e) {
|
||||
START_CRIT_SECTION(); // start critical section
|
||||
SpinLockAcquire(&(m_lock)); // get lock
|
||||
if (e->dle_list == NULL) { // if e is not in list
|
||||
DLAddHead(&m_list, e); // add e to list
|
||||
}
|
||||
SpinLockRelease(&(m_lock));
|
||||
END_CRIT_SECTION();
|
||||
SpinLockRelease(&(m_lock)); // release lock
|
||||
END_CRIT_SECTION(); // end critical section
|
||||
}
|
||||
|
||||
void DllistWithLock::AddTail(Dlelem* e)
|
||||
{
|
||||
START_CRIT_SECTION();
|
||||
SpinLockAcquire(&(m_lock));
|
||||
if (e->dle_list == NULL) {
|
||||
DLAddTail(&m_list, e);
|
||||
void DllistWithLock::AddTail(Dlelem* e) {
|
||||
START_CRIT_SECTION(); // start critical section
|
||||
SpinLockAcquire(&(m_lock)); // get lock
|
||||
if (e->dle_list == NULL) { // if e is not in list
|
||||
DLAddTail(&m_list, e); // add e to list
|
||||
}
|
||||
SpinLockRelease(&(m_lock));
|
||||
END_CRIT_SECTION();
|
||||
SpinLockRelease(&(m_lock)); // release lock
|
||||
END_CRIT_SECTION(); // end critical section
|
||||
}
|
||||
|
||||
Dlelem* DllistWithLock::RemoveHead()
|
||||
{
|
||||
Dlelem* head = NULL;
|
||||
START_CRIT_SECTION();
|
||||
SpinLockAcquire(&(m_lock));
|
||||
head = DLRemHead(&m_list);
|
||||
SpinLockRelease(&(m_lock));
|
||||
END_CRIT_SECTION();
|
||||
return head;
|
||||
Dlelem* DllistWithLock::RemoveHead() {
|
||||
Dlelem* head = NULL; // head pointer
|
||||
START_CRIT_SECTION(); // start critical section
|
||||
SpinLockAcquire(&(m_lock)); // get lock
|
||||
head = DLRemHead(&m_list); // remove head from list
|
||||
SpinLockRelease(&(m_lock)); // release lock
|
||||
END_CRIT_SECTION(); // end critical section
|
||||
return head; // return head pointer
|
||||
}
|
||||
|
||||
Dlelem* DllistWithLock::RemoveTail()
|
||||
{
|
||||
Dlelem* head = NULL;
|
||||
START_CRIT_SECTION();
|
||||
SpinLockAcquire(&(m_lock));
|
||||
head = DLRemTail(&m_list);
|
||||
SpinLockRelease(&(m_lock));
|
||||
END_CRIT_SECTION();
|
||||
return head;
|
||||
Dlelem* DllistWithLock::RemoveTail() {
|
||||
Dlelem* head = NULL; // head pointer
|
||||
START_CRIT_SECTION(); // start critical section
|
||||
SpinLockAcquire(&(m_lock)); // get lock
|
||||
head = DLRemTail(&m_list); // remove tail from list
|
||||
SpinLockRelease(&(m_lock)); // release lock
|
||||
END_CRIT_SECTION(); // end critical section
|
||||
return head; // return head pointer
|
||||
}
|
||||
|
||||
bool DllistWithLock::IsEmpty()
|
||||
{
|
||||
START_CRIT_SECTION();
|
||||
SpinLockAcquire(&(m_lock));
|
||||
bool ret = DLIsNIL(&m_list);
|
||||
SpinLockRelease(&(m_lock));
|
||||
END_CRIT_SECTION();
|
||||
return ret;
|
||||
bool DllistWithLock::IsEmpty() {
|
||||
START_CRIT_SECTION(); // start critical section
|
||||
SpinLockAcquire(&(m_lock)); // get lock
|
||||
bool ret = DLIsNIL(&m_list); // judge whether list is empty
|
||||
SpinLockRelease(&(m_lock)); // release lock
|
||||
END_CRIT_SECTION(); // end critical section
|
||||
return ret; // return result
|
||||
}
|
||||
|
||||
Dlelem* DllistWithLock::GetHead()
|
||||
{
|
||||
Dlelem* head = NULL;
|
||||
head = m_list.dll_head;
|
||||
return head;
|
||||
Dlelem* DllistWithLock::GetHead() {
|
||||
Dlelem* head = NULL; // head pointer
|
||||
head = m_list.dll_head; // get head pointer
|
||||
return head; // return head pointer
|
||||
}
|
||||
|
||||
void DllistWithLock::GetLock()
|
||||
{
|
||||
START_CRIT_SECTION();
|
||||
SpinLockAcquire(&(m_lock));
|
||||
void DllistWithLock::GetLock() {
|
||||
START_CRIT_SECTION(); // start critical section
|
||||
SpinLockAcquire(&(m_lock)); // get lock
|
||||
}
|
||||
|
||||
Dlelem* DllistWithLock::RemoveHeadNoLock()
|
||||
{
|
||||
Dlelem* head = DLRemHead(&m_list);
|
||||
return head;
|
||||
Dlelem* DllistWithLock::RemoveHeadNoLock() {
|
||||
Dlelem* head = DLRemHead(&m_list); // remove head from list
|
||||
return head; // return head pointer
|
||||
}
|
||||
|
||||
void DllistWithLock::ReleaseLock()
|
||||
{
|
||||
SpinLockRelease(&(m_lock));
|
||||
END_CRIT_SECTION();
|
||||
void DllistWithLock::ReleaseLock() {
|
||||
SpinLockRelease(&(m_lock)); // release lock
|
||||
END_CRIT_SECTION(); // end critical section
|
||||
}
|
||||
|
|
|
|||
|
|
@ -309,6 +309,7 @@ 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;
|
||||
|
|
@ -322,6 +323,7 @@ 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,6 +772,15 @@ 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)
|
||||
{
|
||||
|
|
@ -799,6 +808,7 @@ 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,6 +70,7 @@
|
|||
|
||||
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
|
||||
|
|
@ -401,24 +402,27 @@ 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); \
|
||||
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); \
|
||||
/* 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); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define READ_TYPEINFO(typePtr) \
|
||||
|
|
@ -493,9 +497,30 @@ THR_LOCAL bool skip_read_extern_fields = false;
|
|||
token = pg_strtok(&length); \
|
||||
token = pg_strtok(&length); \
|
||||
funcnamespace = nullable_string(token, length); \
|
||||
if (IS_PGXC_DATANODE && !skip_read_extern_fields) { \
|
||||
local_node->fldname = \
|
||||
get_func_oid(funcname, get_namespace_oid(funcnamespace, false), (Expr*)local_node); \
|
||||
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; \
|
||||
} \
|
||||
pfree_ext(funcname); \
|
||||
pfree_ext(funcnamespace); \
|
||||
|
|
@ -525,7 +550,7 @@ THR_LOCAL bool skip_read_extern_fields = false;
|
|||
token = pg_strtok(&length); \
|
||||
token = pg_strtok(&length); \
|
||||
oprrightname = nullable_string(token, length); \
|
||||
if (IS_PGXC_DATANODE) { \
|
||||
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
|
||||
namespaceId = get_namespace_oid(opnamespace, false); \
|
||||
oprleft = get_typeoid(namespaceId, oprleftname); \
|
||||
oprright = oprleft; \
|
||||
|
|
@ -568,7 +593,7 @@ THR_LOCAL bool skip_read_extern_fields = false;
|
|||
token = pg_strtok(&length); \
|
||||
token = pg_strtok(&length); \
|
||||
oprrightname = nullable_string(token, length); \
|
||||
if (IS_PGXC_DATANODE) { \
|
||||
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
|
||||
namespaceId = get_namespace_oid(opnamespace, false); \
|
||||
oprleft = get_typeoid(namespaceId, oprleftname); \
|
||||
oprright = oprleft; \
|
||||
|
|
@ -2126,14 +2151,21 @@ static FuncExpr* _readFuncExpr(void)
|
|||
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("NULL seqNamespace for nextval()")));
|
||||
}
|
||||
|
||||
if (!IS_PGXC_COORDINATOR && !skip_read_extern_fields) {
|
||||
Oid seqid = get_valid_relname_relid(seqNamespace, seqName);
|
||||
if (IS_DATANODE_BUT_NOT_SINGLENODE && !skip_read_extern_fields) {
|
||||
|
||||
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,7 +1454,14 @@ 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, rel_size);
|
||||
pg_atomic_fetch_sub_u64(&m_base_space, AllocSetContextUsedSpace(((AllocSet)entry->rel_mem_manager)));
|
||||
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, rel_size);
|
||||
pg_atomic_fetch_add_u64(&m_base_space, AllocSetContextUsedSpace(((AllocSet)entry->rel_mem_manager)));
|
||||
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,7 +659,27 @@ 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)
|
||||
|
|
@ -1272,9 +1292,18 @@ 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(!g_instance.global_sysdbcache.recovery_finished)) {
|
||||
if (unlikely(!IsPrimaryRecoveryFinished())) {
|
||||
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(!g_instance.global_sysdbcache.recovery_finished)) {
|
||||
if (unlikely(!IsPrimaryRecoveryFinished())) {
|
||||
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(!g_instance.global_sysdbcache.recovery_finished);
|
||||
unlikely(!IsPrimaryRecoveryFinished());
|
||||
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(!g_instance.global_sysdbcache.recovery_finished);
|
||||
unlikely(!IsPrimaryRecoveryFinished());
|
||||
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(!g_instance.global_sysdbcache.recovery_finished);
|
||||
unlikely(!IsPrimaryRecoveryFinished());
|
||||
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(!g_instance.global_sysdbcache.recovery_finished)) {
|
||||
if (unlikely(!IsPrimaryRecoveryFinished())) {
|
||||
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(!g_instance.global_sysdbcache.recovery_finished)) {
|
||||
if (unlikely(!IsPrimaryRecoveryFinished())) {
|
||||
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)
|
||||
Oid get_valid_relname_relid(const char* relnamespace, const char* relname, bool nsp_missing_ok)
|
||||
{
|
||||
Oid nspid = InvalidOid;
|
||||
Oid oldnspid = InvalidOid;
|
||||
|
|
@ -1747,7 +1747,10 @@ Oid get_valid_relname_relid(const char* relnamespace, const char* relname)
|
|||
if (EnableLocalSysCache()) {
|
||||
thrd_inval_count = t_thrd.lsc_cxt.lsc->inval_cxt.SIMCounter;
|
||||
}
|
||||
nspid = get_namespace_oid(relnamespace, false);
|
||||
nspid = get_namespace_oid(relnamespace, nsp_missing_ok);
|
||||
if (!OidIsValid(nspid)) {
|
||||
return InvalidOid;
|
||||
}
|
||||
relid = get_relname_relid(relname, nspid);
|
||||
/*
|
||||
* 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 = 92607;
|
||||
const uint32 GRAND_VERSION_NUM = 92606;
|
||||
|
||||
const uint32 PREDPUSH_SAME_LEVEL_VERSION_NUM = 92522;
|
||||
const uint32 UPSERT_WHERE_VERSION_NUM = 92514;
|
||||
|
|
@ -101,7 +101,7 @@ const uint32 PRIVS_DIRECTORY_VERSION_NUM = 92460;
|
|||
const uint32 COMMENT_RECORD_PARAM_VERSION_NUM = 92484;
|
||||
const uint32 SCAN_BATCH_MODE_VERSION_NUM = 92568;
|
||||
const uint32 PUBLICATION_VERSION_NUM = 92580;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92607;
|
||||
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* b_sql_plugin = "b_sql_plugin";
|
||||
u_sess->attr.attr_sql.b_sql_plugin = CheckIfExtensionExists(b_sql_plugin);
|
||||
const char* dolphin = "dolphin";
|
||||
u_sess->attr.attr_sql.dolphin = CheckIfExtensionExists(dolphin);
|
||||
}
|
||||
|
||||
void PostgresInitializer::FinishInit()
|
||||
|
|
|
|||
|
|
@ -10340,7 +10340,16 @@ check_sql_expr(const char *stmt, int location, int leaderlen)
|
|||
|
||||
oldCxt = MemoryContextSwitchTo(u_sess->plsql_cxt.curr_compile_context->compile_tmp_cxt);
|
||||
u_sess->plsql_cxt.plpgsql_yylloc = plpgsql_yylloc;
|
||||
(void) raw_parser(stmt);
|
||||
RawParserHook parser_hook= raw_parser;
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (u_sess->attr.attr_sql.dolphin) {
|
||||
int id = GetCustomParserId();
|
||||
if (id >= 0 && g_instance.raw_parser_hook[id] != NULL) {
|
||||
parser_hook = (RawParserHook)g_instance.raw_parser_hook[id];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
(void)parser_hook(stmt, NULL);
|
||||
MemoryContextSwitchTo(oldCxt);
|
||||
|
||||
/* Restore former ereport callback */
|
||||
|
|
|
|||
|
|
@ -44,11 +44,22 @@ static int g_iPosBlackList = 0;
|
|||
/* array store for black list */
|
||||
static BBOX_BLACKLIST_STRU g_stBlackList[BBOX_BLACK_LIST_COUNT_MAX];
|
||||
|
||||
/*
|
||||
* Determines whether the byte order of the local machine is large or small
|
||||
* return : ELFDATA2LSB - large
|
||||
* : ELFDATA2MSB - small
|
||||
*/
|
||||
/*
|
||||
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
|
||||
*/
|
||||
int BBOX_DetermineMsb(void)
|
||||
{
|
||||
union INT_PROBE {
|
||||
|
|
|
|||
|
|
@ -51,8 +51,19 @@ struct PIPE_IDS {
|
|||
static struct PIPE_IDS astPipeIds[BBOX_MAX_PIDS];
|
||||
|
||||
/*
|
||||
* compare string pszSrc and pszTarget
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 bbox_strncmp(const char* pszSrc, const char* pszTarget, s32 count)
|
||||
{
|
||||
signed char cRes = 0;
|
||||
|
|
@ -68,8 +79,20 @@ s32 bbox_strncmp(const char* pszSrc, const char* pszTarget, s32 count)
|
|||
}
|
||||
|
||||
/*
|
||||
* compare string pszSrc and pszTarget
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 bbox_strcmp(const char* pszSrc, const char* pszTarget)
|
||||
{
|
||||
unsigned char c1, c2;
|
||||
|
|
@ -90,8 +113,15 @@ s32 bbox_strcmp(const char* pszSrc, const char* pszTarget)
|
|||
}
|
||||
|
||||
/*
|
||||
* get the length of string pszString
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 bbox_strlen(const char* pszString)
|
||||
{
|
||||
const char* pszTemp = NULL;
|
||||
|
|
@ -105,8 +135,16 @@ s32 bbox_strlen(const char* pszString)
|
|||
}
|
||||
|
||||
/*
|
||||
* get the length of string 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
|
||||
*/
|
||||
s32 bbox_strnlen(const char* pszString, s32 count)
|
||||
{
|
||||
const char* pszTemp = NULL;
|
||||
|
|
@ -119,8 +157,16 @@ s32 bbox_strnlen(const char* pszString, s32 count)
|
|||
}
|
||||
|
||||
/*
|
||||
* convert a string to interger
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 bbox_atoi(const char* pszString)
|
||||
{
|
||||
s32 n = 0;
|
||||
|
|
@ -140,10 +186,18 @@ s32 bbox_atoi(const char* pszString)
|
|||
|
||||
return iNeg ? -n : n;
|
||||
}
|
||||
|
||||
/*
|
||||
* compare memory
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 bbox_memcmp(const void* cs, const void* ct, s32 count)
|
||||
{
|
||||
const unsigned char *su1 = NULL;
|
||||
|
|
@ -159,8 +213,18 @@ s32 bbox_memcmp(const void* cs, const void* ct, s32 count)
|
|||
}
|
||||
|
||||
/*
|
||||
* search string l2 in l1
|
||||
*/
|
||||
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
|
||||
*/
|
||||
char* bbox_strstr(const char* s1, const char* s2)
|
||||
{
|
||||
int l1, l2;
|
||||
|
|
@ -182,8 +246,17 @@ char* bbox_strstr(const char* s1, const char* s2)
|
|||
}
|
||||
|
||||
/*
|
||||
* make a directory
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 bbox_mkdir(const char* pszDir)
|
||||
{
|
||||
char szDirName[BBOX_TMP_LEN_32 * 16];
|
||||
|
|
@ -228,8 +301,16 @@ s32 bbox_mkdir(const char* pszDir)
|
|||
}
|
||||
|
||||
/*
|
||||
* search free pipe id
|
||||
*/
|
||||
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
|
||||
*/
|
||||
struct PIPE_ID* bbox_GetFreePid(void)
|
||||
{
|
||||
u32 i;
|
||||
|
|
@ -245,8 +326,14 @@ struct PIPE_ID* bbox_GetFreePid(void)
|
|||
}
|
||||
|
||||
/*
|
||||
* Release the occupied pipeid
|
||||
*/
|
||||
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
|
||||
*/
|
||||
void bbox_PutPid(struct PIPE_ID* pstPid)
|
||||
{
|
||||
struct PIPE_IDS* pstPids = NULL;
|
||||
|
|
@ -261,8 +348,16 @@ void bbox_PutPid(struct PIPE_ID* pstPid)
|
|||
}
|
||||
|
||||
/*
|
||||
* find available pipe id by file handle
|
||||
*/
|
||||
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
|
||||
*/
|
||||
struct PIPE_ID* bbox_FindPid(int iFd)
|
||||
{
|
||||
u32 i;
|
||||
|
|
@ -281,8 +376,17 @@ struct PIPE_ID* bbox_FindPid(int iFd)
|
|||
}
|
||||
|
||||
/*
|
||||
* run popen
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 sys_popen(char* pszCmd, const char* pszMode)
|
||||
{
|
||||
struct PIPE_ID* volatile stCurPid = NULL;
|
||||
|
|
@ -387,8 +491,15 @@ s32 sys_popen(char* pszCmd, const char* pszMode)
|
|||
}
|
||||
|
||||
/*
|
||||
* close file handle
|
||||
*/
|
||||
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
|
||||
*/
|
||||
int sys_pclose(s32 iFd)
|
||||
{
|
||||
struct PIPE_ID* pstCur = NULL;
|
||||
|
|
@ -411,8 +522,17 @@ int sys_pclose(s32 iFd)
|
|||
}
|
||||
|
||||
/*
|
||||
* list file in directory
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 bbox_listdir(const char* pstPath, BBOX_LIST_DIR_CALLBACK callback, void* pArgs)
|
||||
{
|
||||
struct linux_dirent* pstEntry = NULL;
|
||||
|
|
|
|||
|
|
@ -57,23 +57,37 @@ void bbox_initlog(int iLogScreen)
|
|||
}
|
||||
|
||||
/*
|
||||
* convert int to string
|
||||
*/
|
||||
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
|
||||
*/
|
||||
inline char bbox_itoc(u8 sNum)
|
||||
{
|
||||
return (char)((sNum < 10) ? (sNum + 48) : (sNum + 87));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 bbox_put_dox(BBOX_vnprintCallBack pCallback, void* ptr, s32* piCount, u32 iSize, u64 uNum, s32 sSys, s32 isNeg)
|
||||
{
|
||||
s64 i = 0;
|
||||
|
|
@ -108,15 +122,21 @@ s32 bbox_put_dox(BBOX_vnprintCallBack pCallback, void* ptr, s32* piCount, u32 iS
|
|||
|
||||
return iRet;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const char* pFmt, va_list ap)
|
||||
{
|
||||
|
||||
|
|
@ -235,13 +255,20 @@ s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const c
|
|||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 bbox_SnprintCallback(char c, void* pPtr, s32* piCount, s32 iSize)
|
||||
{
|
||||
char** pszBuff = (char**)pPtr;
|
||||
|
|
|
|||
|
|
@ -64,8 +64,14 @@ u8 g_szAltStackMem[BBOX_ALT_STACKSIZE]; /* independent thread stack memory */
|
|||
BBOX_ATOMIC_STRU g_isBusy = BBOX_ATOMIC_INIT(0); /* whether deal with core file. */
|
||||
|
||||
/*
|
||||
* reserved count bytes on current stack, and set 0
|
||||
*/
|
||||
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
|
||||
*/
|
||||
void BBOX_ReserveZeroStack(s32 count)
|
||||
{
|
||||
char buff[count];
|
||||
|
|
@ -95,8 +101,14 @@ s32 BBOX_CloneRun(u32 uFlags, s32 (*pFn)(void*), void* pArg, ...)
|
|||
}
|
||||
|
||||
/*
|
||||
* get count of thread
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 BBOX_GetTaskNumber(char* szTaskPath)
|
||||
{
|
||||
struct kernel_stat stProcSB = {0};
|
||||
|
|
@ -130,8 +142,17 @@ s32 BBOX_GetTaskNumber(char* szTaskPath)
|
|||
}
|
||||
|
||||
/*
|
||||
* get thread pid
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 BBOX_GetTaskId(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iSize, char* szTaskPath)
|
||||
{
|
||||
s32 iProc = -1;
|
||||
|
|
@ -214,13 +235,19 @@ errout:
|
|||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s32 iDoPtraceCheck)
|
||||
{
|
||||
u32 i;
|
||||
|
|
@ -272,13 +299,18 @@ s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s3
|
|||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
void BBOX_DetachAllThread(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount)
|
||||
{
|
||||
u32 i;
|
||||
|
|
@ -323,12 +355,18 @@ void BBOX_CheckResumeThread(void* pArgs)
|
|||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 BBOX_PtraceAndRun(struct BBOX_ListParams* pstArgs, s32 iMaxThreadCount, char* pszProcSelfTask)
|
||||
{
|
||||
struct TASK_ATTACH_INFO stTaskInfo[iMaxThreadCount];
|
||||
|
|
@ -407,8 +445,15 @@ errout:
|
|||
}
|
||||
|
||||
/*
|
||||
* print log information if export failed.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
void BBOX_PrintFailedLog(const char* pFileName)
|
||||
{
|
||||
ssize_t iRet = 0;
|
||||
|
|
@ -437,8 +482,15 @@ void BBOX_PrintFailedLog(const char* pFileName)
|
|||
}
|
||||
|
||||
/*
|
||||
* export thread information.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
void BBOX_ListThread(struct BBOX_ListParams* pstArgs)
|
||||
{
|
||||
pid_t ppid = 0;
|
||||
|
|
@ -545,12 +597,18 @@ errout:
|
|||
}
|
||||
|
||||
/*
|
||||
* get return value of child process
|
||||
* in : iClonePid - PID of child process
|
||||
* pstArgs - parameter
|
||||
* iCloneErrno - err code
|
||||
* return 0 if success else failed.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
s32 BBOX_GetClonePidResult(pid_t iClonePid, struct BBOX_ListParams* pstArgs, s32 iCloneErrno)
|
||||
{
|
||||
s32 iStatus = 0;
|
||||
|
|
|
|||
|
|
@ -57,6 +57,22 @@ 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;
|
||||
|
|
@ -84,8 +100,19 @@ static void coredump_handler(int sig, siginfo_t *si, void *uc)
|
|||
}
|
||||
|
||||
/*
|
||||
* bbox_handler - handle signal conditions for bbox
|
||||
*/
|
||||
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
|
||||
*/
|
||||
static void bbox_handler(int sig, siginfo_t *si, void *uc)
|
||||
{
|
||||
static volatile int64 first_tid = INVALID_TID;
|
||||
|
|
@ -125,8 +152,16 @@ static void bbox_handler(int sig, siginfo_t *si, void *uc)
|
|||
}
|
||||
|
||||
/*
|
||||
* get_bbox_coredump_pattern_path - get the core dump path from the file "/proc/sys/kernel/core_pattern"
|
||||
*/
|
||||
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
|
||||
*/
|
||||
static void get_bbox_coredump_pattern_path(char* path, Size len)
|
||||
{
|
||||
FILE* fp = NULL;
|
||||
|
|
@ -156,7 +191,17 @@ static void get_bbox_coredump_pattern_path(char* path, Size len)
|
|||
}
|
||||
}
|
||||
|
||||
/* compute directory into which bbox dump core files are saved. */
|
||||
/*
|
||||
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
|
||||
*/
|
||||
static void build_bbox_corepath(char *bbox_core_path, Size path_size, char *config_path)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
|
|
@ -232,6 +277,15 @@ 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;
|
||||
|
|
@ -239,6 +293,15 @@ 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;
|
||||
|
|
@ -264,7 +327,6 @@ 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)
|
||||
|
|
@ -402,10 +464,15 @@ void bbox_blacklist_remove(BlacklistIndex item, void* addr)
|
|||
}
|
||||
|
||||
/*
|
||||
* @Description: check the value from environment variablethe to prevent command injection.
|
||||
* @in input_env_value : the input value need be checked.
|
||||
*
|
||||
*/
|
||||
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
|
||||
*/
|
||||
int CheckFilenameValid(const char* inputEnvValue)
|
||||
{
|
||||
const int maxLen = 1024;
|
||||
|
|
|
|||
|
|
@ -45,6 +45,15 @@
|
|||
|
||||
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;
|
||||
|
|
@ -270,6 +279,15 @@ 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);
|
||||
|
|
@ -387,6 +405,21 @@ 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)
|
||||
{
|
||||
|
|
@ -487,6 +520,22 @@ 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,6 +175,24 @@ 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;
|
||||
|
|
@ -205,6 +223,25 @@ 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,6 +53,24 @@ 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;
|
||||
|
|
@ -154,6 +172,16 @@ 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;
|
||||
|
|
@ -205,6 +233,18 @@ 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;
|
||||
|
|
@ -259,6 +299,21 @@ 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;
|
||||
|
|
@ -343,6 +398,17 @@ 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;
|
||||
|
|
@ -394,6 +460,17 @@ 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;
|
||||
|
|
@ -421,6 +498,16 @@ 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;
|
||||
|
|
@ -447,6 +534,19 @@ 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;
|
||||
|
|
@ -476,6 +576,19 @@ 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;
|
||||
|
|
@ -504,6 +617,18 @@ 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;
|
||||
|
|
@ -530,6 +655,17 @@ 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;
|
||||
|
|
@ -556,6 +692,19 @@ 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;
|
||||
|
|
@ -622,6 +771,17 @@ 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;
|
||||
|
|
@ -658,6 +818,15 @@ 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)
|
||||
{
|
||||
/*
|
||||
|
|
@ -687,6 +856,21 @@ 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;
|
||||
|
|
@ -926,6 +1110,23 @@ 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,6 +81,27 @@ 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};
|
||||
|
|
@ -121,6 +142,17 @@ 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;
|
||||
|
|
@ -147,6 +179,17 @@ 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;
|
||||
|
|
@ -191,6 +234,20 @@ 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
|
||||
|
|
@ -261,6 +318,20 @@ 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
|
||||
|
|
@ -306,6 +377,17 @@ 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};
|
||||
|
|
@ -368,6 +450,19 @@ 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
|
||||
|
|
@ -426,6 +521,21 @@ 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
|
||||
|
|
@ -519,6 +629,17 @@ 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;
|
||||
|
|
@ -566,6 +687,18 @@ 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
|
||||
|
|
@ -656,6 +789,18 @@ 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,6 +232,21 @@ 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;
|
||||
|
|
@ -322,6 +337,19 @@ 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;
|
||||
|
|
@ -371,6 +399,23 @@ 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,6 +137,16 @@ 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;
|
||||
|
|
@ -227,7 +237,20 @@ 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};
|
||||
|
|
@ -251,8 +274,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 mayown certification and private key*/
|
||||
if not,decrypt the default cipher and rand files begins with client.
|
||||
Because,for every client user may own certification and private key*/
|
||||
if (NULL == userName) {
|
||||
retval = LibCommClientCheckPermissionCipherFile(CertFilesDir, conn, NULL);
|
||||
if (retval != 1)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,16 @@ 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) {
|
||||
|
|
@ -60,6 +70,17 @@ 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) {
|
||||
|
|
@ -89,6 +110,18 @@ 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,6 +222,7 @@ NON_EXEC_STATIC void PercentileMain()
|
|||
g_instance.stat_cxt.force_process = false;
|
||||
sleep(SLEEP_INTERVAL);
|
||||
}
|
||||
elog(LOG, "instrumention percentile ended");
|
||||
gs_thread_exit(0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
|
|
@ -21,8 +25,33 @@
|
|||
* ---------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*In artificial intelligence, it is not so easy to accurately and easily identify and output the image/voice
|
||||
we expect to output in the face of a large number of data/materials input by users. Therefore, the algorithm
|
||||
is particularly important. The algorithm is what we call a model.
|
||||
|
||||
Of course, in addition to the core recognition engine, the content of the algorithm also includes various
|
||||
configuration parameters, such as bit rate, sampling rate, timbre, tone, pitch, audio, cadence, dialect, noise
|
||||
and other messy parameters. In a mature recognition engine, the core content generally does not change
|
||||
frequently. In order to achieve the goal of "successful recognition", we can only adjust the configuration
|
||||
parameters. For different inputs, we will configure different parameter values, and finally take a group of
|
||||
parameter values with balanced parties and high recognition rate in the result statistics. This group of
|
||||
parameter values is the result we get after training. This is the training process, also called model training.
|
||||
|
||||
So:
|
||||
Model = algorithm
|
||||
Training = the process of finding out the optimal configuration parameters by using big data to
|
||||
achieve the goal of high recognition rate.
|
||||
Results = Determine the parameter configuration and achieve high recognition rate.
|
||||
*/
|
||||
|
||||
#include "db4ai/db4ai_api.h"
|
||||
|
||||
/*Function: model_ Fit
|
||||
Formal parameters: (const char * name, AlgorithmML algorithm, const Hyperparameter * hyperparameters, int nhyperp,
|
||||
Oid * typid, bool * typbyval, int16 * typlen, int ncolumns, callback_ Ml_ Fetch fetch,
|
||||
Callback_ Ml_ Rescan rescan, void * callback_ Data)
|
||||
Return value: Model*
|
||||
model training*/
|
||||
Model *model_fit(const char *name, AlgorithmML algorithm, const Hyperparameter *hyperparameters, int nhyperp,
|
||||
Oid *typid, bool *typbyval, int16 *typlen, int ncolumns, callback_ml_fetch fetch,
|
||||
callback_ml_rescan rescan, void *callback_data)
|
||||
|
|
@ -100,6 +129,10 @@ ModelPredictor model_prepare_predict(const Model* model)
|
|||
pred->predictor = pred->palgo->prepare_predict(pred->palgo, &model->data, model->return_type);
|
||||
return (ModelPredictor)pred;
|
||||
}
|
||||
/*Function: model_ Predict
|
||||
Formal parameters: (ModelPredictor predictor, Datum * values, bool * isnull, Oid * typid, int num_columns)
|
||||
Return value: Datum
|
||||
model prediction*/
|
||||
|
||||
Datum model_predict(ModelPredictor predictor, Datum *values, bool *isnull, Oid *typid, int num_columns)
|
||||
{
|
||||
|
|
@ -107,11 +140,18 @@ Datum model_predict(ModelPredictor predictor, Datum *values, bool *isnull, Oid *
|
|||
return pred->palgo->predict(pred->palgo, pred->predictor, values, isnull, typid, num_columns);
|
||||
}
|
||||
|
||||
/*Function: model_ Store
|
||||
Parameter: (const Model * model)
|
||||
Return value: None
|
||||
Model Storage*/
|
||||
void model_store(const Model *model)
|
||||
{
|
||||
store_model(model);
|
||||
}
|
||||
|
||||
/*Function: model_ Load
|
||||
Formal parameter: (const char * modelname)
|
||||
Return value: Model*
|
||||
Model loading*/
|
||||
const Model *model_load(const char *model_name)
|
||||
{
|
||||
return get_model(model_name, false);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/* *
|
||||
Copyright (c) 2021 Huawei Technologies Co.,Ltd.
|
||||
|
||||
|
|
@ -37,6 +41,13 @@ IDENTIFICATION
|
|||
#endif
|
||||
|
||||
|
||||
/*Euclidean distance: the distance between two points,
|
||||
that is, the distance we usually calculate.
|
||||
Manhattan distance: the sum of absolute
|
||||
wheelbase of two points in the standard coordinate system.
|
||||
Chebyshev distance: the maximum value of the
|
||||
numerical difference of each coordinate.*/
|
||||
|
||||
/*
|
||||
* L1 distance (Manhattan)
|
||||
* We sum using cascaded summation
|
||||
|
|
@ -44,6 +55,14 @@ IDENTIFICATION
|
|||
* are not available or for the the case that the dimension is not a multiple
|
||||
* of the width of the registers
|
||||
*/
|
||||
|
||||
/*Vectorization refers to using an array instead of a scalar
|
||||
to manipulate each element in the array.*/
|
||||
|
||||
/*Function: l1_ Non_ Vectorized
|
||||
Formal parameters: (double const * p, double const * q, uint32_t const dimension)
|
||||
Return value: double
|
||||
Calculate Manhattan distance without vectorization processing*/
|
||||
static force_inline double l1_non_vectorized(double const * p, double const * q, uint32_t const dimension)
|
||||
{
|
||||
double term = 0.;
|
||||
|
|
@ -73,6 +92,11 @@ static force_inline double l1_non_vectorized(double const * p, double const * q,
|
|||
* This version is vectorized using SSE or NEON and is used in case only 128-bit
|
||||
* vectorized instructions are available
|
||||
*/
|
||||
|
||||
/*Function: l1_ 128
|
||||
Formal parameters: (double const * p, double const * q, uint32_t const dimension)
|
||||
Return value: double
|
||||
Calculate Manhattan distance without vectorization processing*/
|
||||
static double l1_128(double const * p, double const * q, uint32_t const dimension)
|
||||
{
|
||||
if (unlikely(dimension == 0))
|
||||
|
|
@ -179,6 +203,11 @@ static double l1_128(double const * p, double const * q, uint32_t const dimensio
|
|||
* are not available or for the the case that the dimension is not a multiple
|
||||
* of the width of the registers
|
||||
*/
|
||||
|
||||
/*Function: l2_ Squared_ Non_ Vectorized
|
||||
Formal parameters: (double const * p, double const * q, uint32_t const dimension)
|
||||
Return value: double
|
||||
Calculating Euclidean Distance Without Vectorization*/
|
||||
static force_inline double l2_squared_non_vectorized(double const * p, double const * q, uint32_t const dimension)
|
||||
{
|
||||
double subtraction = 0.;
|
||||
|
|
@ -212,6 +241,11 @@ static force_inline double l2_squared_non_vectorized(double const * p, double co
|
|||
* This version is vectorized using SSE or NEON and is used in case only 128-bit
|
||||
* vectorized instructions are available
|
||||
*/
|
||||
|
||||
/*Function: l2_ Squared_128
|
||||
Formal parameters: (double const * p, double const * q, uint32_t const dimension)
|
||||
Return value: double
|
||||
Vectorization processing for calculating Euclidean distance*/
|
||||
static double l2_squared_128(double const * p, double const * q, uint32_t const dimension)
|
||||
{
|
||||
if (unlikely(dimension == 0))
|
||||
|
|
@ -307,6 +341,12 @@ static double l2_squared_128(double const * p, double const * q, uint32_t const
|
|||
|
||||
#endif
|
||||
|
||||
/*
|
||||
Function: linf_ Non_ Vectorized
|
||||
Formal parameters: (double const * p, double const * q, uint32_t const dimension)
|
||||
Return value: double
|
||||
Calculating Chebyshev Distance through Non Vectorization Processing
|
||||
*/
|
||||
/*
|
||||
* L infinity distance (Chebyshev)
|
||||
* This version is unvectorized and is used in case vectorized instructions
|
||||
|
|
@ -336,6 +376,13 @@ static force_inline double linf_non_vectorized(double const * p, double const *
|
|||
}
|
||||
|
||||
#if (defined(__x86_64__) && defined(__SSE3__)) || (defined(__aarch64__) && defined(__ARM_NEON))
|
||||
/*
|
||||
Function: linf_ one hundred and twenty-eight
|
||||
Formal parameters: (double const * p, double const * q, uint32_t const dimension)
|
||||
Return value: double
|
||||
Vectorization processing for calculating Chebyshev distance
|
||||
*/
|
||||
|
||||
/*
|
||||
* L infinity distance (Chebyshev)
|
||||
* This version is vectorized using SSE or NEON and is used in case only 128-bit
|
||||
|
|
@ -420,6 +467,11 @@ static double linf_128(double const * p, double const * q, uint32_t const dimens
|
|||
|
||||
#endif
|
||||
|
||||
/*Function: l1
|
||||
Formal parameters: (double const * p, double const * q, uint32_t const dimension)
|
||||
Return value: double
|
||||
Automatic vectorization processing for calculating Manhattan distance*/
|
||||
|
||||
/*
|
||||
* L1 distance (Manhattan)
|
||||
* This is the main function. It will be automatically vectorized
|
||||
|
|
@ -446,6 +498,11 @@ double l1(double const * p, double const * q, uint32_t const dimension)
|
|||
* This is the main function. It will be automatically vectorized
|
||||
* if possible
|
||||
*/
|
||||
|
||||
/*Function: l2_ Squared
|
||||
Formal parameters: (double const * p, double const * q, uint32_t const dimension)
|
||||
Return value: double
|
||||
Automatic vectorization processing for calculating Euclidean distance*/
|
||||
double l2_squared(double const * p, double const * q, uint32_t const dimension)
|
||||
{
|
||||
if (unlikely(dimension == 0))
|
||||
|
|
@ -467,6 +524,11 @@ double l2_squared(double const * p, double const * q, uint32_t const dimension)
|
|||
* This is the main function. It will be automatically vectorized
|
||||
* if possible
|
||||
*/
|
||||
|
||||
/*Function: l2_ Squared
|
||||
Formal parameters: (double const * p, double const * q, uint32_t const dimension)
|
||||
Return value: double
|
||||
Automatic vectorization processing for calculating Euclidean distance*/
|
||||
double l2(double const * p, double const * q, uint32_t const dimension)
|
||||
{
|
||||
if (unlikely(dimension == 0))
|
||||
|
|
@ -488,6 +550,12 @@ double l2(double const * p, double const * q, uint32_t const dimension)
|
|||
* This is the main function. It will be automatically vectorized
|
||||
* if possible
|
||||
*/
|
||||
|
||||
/*Function: linf
|
||||
Parameter: (double const * p, double const * q, uint32 _ t const dimension)
|
||||
Return value: double
|
||||
|
||||
Calculation of Chebyshev distance by automatic vectorization processing*/
|
||||
double linf(double const * p, double const * q, uint32_t const dimension)
|
||||
{
|
||||
if (unlikely(dimension == 0))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
|
|
@ -22,6 +26,12 @@
|
|||
* ---------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*Gradient Descent is a commonly used optimization algorithm, which is used to solve
|
||||
the minimum value of the objective function. It is an iterative algorithm. In each iteration,
|
||||
the gradient (or approximate gradient) of the objective function is calculated, and then the
|
||||
parameters are updated along the negative gradient direction until the minimum value that
|
||||
meets the conditions is reached.*/
|
||||
|
||||
#include "postgres.h"
|
||||
#include "executor/executor.h"
|
||||
#include "utils/builtins.h"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
|
|
@ -21,6 +25,15 @@
|
|||
* ---------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*Linear regression is a regression analysis that uses the least square function called linear
|
||||
regression equation to model the relationship between one or more independent variables and dependent
|
||||
variables. Its expression form is y = w'x+e, where e is the normal distribution with the average value of 0.
|
||||
In regression analysis, only one independent variable and one dependent variable are included, and the
|
||||
relationship between them can be approximately expressed by a straight line. This regression analysis is
|
||||
called unary linear regression analysis. If regression analysis includes two or more independent variables,
|
||||
and there is a linear relationship between dependent variables and independent variables, it is called
|
||||
multivariate linear regression analysis.*/
|
||||
|
||||
#include "db4ai/gd.h"
|
||||
|
||||
static void linear_reg_gradients(GradientsConfig *cfg)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
|
|
@ -21,6 +25,10 @@
|
|||
* ---------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*Logistic regression, also known as logistic regression analysis, is mainly used in epidemiology.
|
||||
The common situation is to explore the risk factors of a disease and predict the probability
|
||||
of a disease according to the risk factors.*/
|
||||
|
||||
#include "db4ai/gd.h"
|
||||
|
||||
static void logreg_gradients(GradientsConfig *cfg)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
|
|
@ -22,6 +26,14 @@
|
|||
* ---------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*Principal component analysis is a mathematical transformation method,
|
||||
which transforms a given set of related variables into another set of unrelated
|
||||
variables through linear transformation, and these new variables are arranged
|
||||
in the order of decreasing variance. In mathematical transformation, the total
|
||||
variance of variables is kept constant, so that the first variable has the largest
|
||||
variance, which is called first principal component, and the second variable has
|
||||
the second largest variance and is not related to the first variable, which is called
|
||||
the second principal component. By analogy, I variables have I principal components.*/
|
||||
#include "db4ai/gd.h"
|
||||
#include "db4ai/db4ai_cpu.h"
|
||||
#include "db4ai/fp_ops.h"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
|
|
@ -23,6 +27,10 @@
|
|||
|
||||
#include "db4ai/gd.h"
|
||||
#include "db4ai/kernel.h"
|
||||
/*Support vector machine (SVM), because of its English name, is generally referred to as SVM. Generally speaking,
|
||||
it is a two-class classification model. Its basic model is defined as a linear classifier with the largest interval in the
|
||||
feature space, and its learning strategy is to maximize the interval, which can eventually be transformed into the
|
||||
solution of a convex quadratic programming problem.*/
|
||||
|
||||
static void svmc_gradients(GradientsConfig *cfg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
|
|
@ -21,6 +25,16 @@
|
|||
* ---------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*In the direction of DB4AI, the database can avoid the problem of data handling
|
||||
when users perform AI calculation by integrating AI capabilities. Different from other
|
||||
DB4AI frameworks, the native framework of openGauss open source is to complete
|
||||
the AI calculation in the database by adding AI operators.*/
|
||||
|
||||
/*In the context of machine learning, superparameters are parameters whose values are
|
||||
set before the learning process begins, rather than parameter data obtained through training.
|
||||
Usually, it is necessary to optimize the hyperparameters and choose a set of optimal hyperparameters
|
||||
for the learning machine to improve the learning performance and effect.*/
|
||||
|
||||
#include "db4ai/hyperparameter_validation.h"
|
||||
|
||||
#include "db4ai/aifuncs.h"
|
||||
|
|
@ -28,6 +42,8 @@
|
|||
#include "nodes/plannodes.h"
|
||||
#include "db4ai/db4ai_api.h"
|
||||
|
||||
//Used to add, delete, check and modify the value of the superparameter.
|
||||
//
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
@ -37,13 +53,22 @@
|
|||
is_supervised \
|
||||
}
|
||||
|
||||
|
||||
/*Function: get_ Hyperparameter_ Definitions
|
||||
Parameter: (AlgorithmML algorithm, int32_t * result_size)
|
||||
Return: HyperparameterDefinition*
|
||||
Enter the algorithm and number of result digits to return the definition of hyperparameters for this model.*/
|
||||
|
||||
const HyperparameterDefinition* get_hyperparameter_definitions(AlgorithmML algorithm, int32_t *result_size)
|
||||
{
|
||||
AlgorithmAPI* api = get_algorithm_api(algorithm);
|
||||
return api->get_hyperparameters_definitions(api, result_size);
|
||||
}
|
||||
|
||||
/*Function: get_ Algorithm_ Configuration
|
||||
Parameter: AlgorithmML algorithm
|
||||
Return: AlgorithmConfiguration*
|
||||
Determine if the algorithm exists.*/
|
||||
|
||||
AlgorithmConfiguration *get_algorithm_configuration(AlgorithmML algorithm)
|
||||
{
|
||||
switch (algorithm) {
|
||||
|
|
@ -55,6 +80,16 @@ AlgorithmConfiguration *get_algorithm_configuration(AlgorithmML algorithm)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
Function: find_hyperparameter_definition
|
||||
|
||||
Parameter:(const HyperparameterDefinition definitions[],
|
||||
int32_t definitions_size,
|
||||
const char *hyperparameter_name)
|
||||
|
||||
Return:HyperparameterDefinition *
|
||||
Enter the model name and return the model definition.
|
||||
*/
|
||||
const HyperparameterDefinition *find_hyperparameter_definition(const HyperparameterDefinition definitions[],
|
||||
int32_t definitions_size,
|
||||
const char *hyperparameter_name)
|
||||
|
|
@ -68,6 +103,14 @@ const HyperparameterDefinition *find_hyperparameter_definition(const Hyperparame
|
|||
}
|
||||
|
||||
// Set the value of a hyperparameter structure
|
||||
/*
|
||||
Function: set_ Hyperparameter_ Datum
|
||||
Parameter: (Hyperparameter * hyperp, Oid type, Datum value)
|
||||
Return: None
|
||||
Set the hyperparameter type and value, which is called by the system.
|
||||
An exception is thrown if there is no super parameter.
|
||||
*/
|
||||
|
||||
static void set_hyperparameter_datum(Hyperparameter *hyperp, Oid type, Datum value)
|
||||
{
|
||||
if (type == ANYENUMOID) { // Outside of hyperparameter module, treat them as strings
|
||||
|
|
@ -80,6 +123,14 @@ static void set_hyperparameter_datum(Hyperparameter *hyperp, Oid type, Datum val
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function: add_model_hyperparameter
|
||||
Parameter: (List *hyperparameters, MemoryContext memcxt, const char *name, Oid type,
|
||||
Datum value)
|
||||
Return: List *
|
||||
Adding hyperparameters to the model
|
||||
*/
|
||||
|
||||
static List *add_model_hyperparameter(List *hyperparameters, MemoryContext memcxt, const char *name, Oid type,
|
||||
Datum value)
|
||||
{
|
||||
|
|
@ -93,7 +144,14 @@ static List *add_model_hyperparameter(List *hyperparameters, MemoryContext memcx
|
|||
|
||||
return hyperparameters;
|
||||
}
|
||||
|
||||
/*
|
||||
Function: update_model_hyperparameter
|
||||
Parameter: (MemoryContext memcxt, List *hyperparameters, const char *name, Oid type, Datum value)
|
||||
Return:None
|
||||
update hyperparameters to the model
|
||||
*/
|
||||
|
||||
|
||||
void update_model_hyperparameter(MemoryContext memcxt, List *hyperparameters, const char *name, Oid type, Datum value)
|
||||
{
|
||||
MemoryContext old_context = MemoryContextSwitchTo(memcxt);
|
||||
|
|
@ -108,12 +166,20 @@ void update_model_hyperparameter(MemoryContext memcxt, List *hyperparameters, co
|
|||
MemoryContextSwitchTo(old_context);
|
||||
}
|
||||
|
||||
|
||||
/* inline change bool to str*/
|
||||
inline const char *bool_to_str(bool value)
|
||||
{
|
||||
return value ? "TRUE" : "FALSE";
|
||||
}
|
||||
|
||||
/*Function: ereport_ Hyperparameter
|
||||
|
||||
Formal parameters: (int level, const char * name, Datum value, Oid type)
|
||||
|
||||
Return value: None
|
||||
|
||||
Display model hyperparameters*/
|
||||
|
||||
static void ereport_hyperparameter(int level, const char *name, Datum value, Oid type)
|
||||
{
|
||||
switch (type) {
|
||||
|
|
@ -214,6 +280,15 @@ static Datum get_hyperparameter(const HyperparameterDefinition *definition, void
|
|||
|
||||
|
||||
// Set hyperparameter in hyperparameter struct to the givne value in the datum. Definition is used for metadata
|
||||
/*Function: set_ Hyperparameter
|
||||
|
||||
Formal parameters: (const HyperparameterDefinition * definition, Datum value, void * hyperparameter_struct)
|
||||
|
||||
Return value: None
|
||||
|
||||
Modify model hyperparameter values*/
|
||||
|
||||
|
||||
static void set_hyperparameter(const HyperparameterDefinition *definition, Datum value, void *hyperparameter_struct)
|
||||
{
|
||||
switch (definition->type) {
|
||||
|
|
@ -259,6 +334,17 @@ static void set_hyperparameter(const HyperparameterDefinition *definition, Datum
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Function: validate_ Hyperparameter_ String
|
||||
|
||||
Formal parameters: (const char * name, const char * value, const char * valid_values [],
|
||||
|
||||
Int32_ T valid_ Values_ Size)
|
||||
|
||||
Return value: None
|
||||
|
||||
Given the hyperparameter name, modify the model hyperparameter value.*/
|
||||
static void validate_hyperparameter_string(const char *name, const char *value, const char *valid_values[],
|
||||
int32_t valid_values_size)
|
||||
{
|
||||
|
|
@ -284,6 +370,11 @@ static void validate_hyperparameter_string(const char *name, const char *value,
|
|||
}
|
||||
}
|
||||
|
||||
/*Function: validate_ Hyperparameter
|
||||
Formal parameters: (Datum value, Oid type, const HyperparameterValidation * validation, const char * name)
|
||||
Return value: None
|
||||
Make the modified hyperparameter values effective.*/
|
||||
|
||||
static void validate_hyperparameter(Datum value, Oid type, const HyperparameterValidation *validation, const char *name)
|
||||
{
|
||||
switch (type) {
|
||||
|
|
@ -354,6 +445,16 @@ static void validate_hyperparameter(Datum value, Oid type, const HyperparameterV
|
|||
}
|
||||
}
|
||||
|
||||
/*Function: extract_ Value_ From_ Variable_ Set_ Stmt
|
||||
Parameter: (VariableSetStmt * stmt)
|
||||
Return value: Value
|
||||
Obtain modified values using preprocessing.*/
|
||||
/*STMT is a C API provided by MySQL,
|
||||
which is used to execute Prepared statements.
|
||||
Compared with the direct execution of SQL, the
|
||||
preprocessing statement has higher running
|
||||
efficiency and better security.*/
|
||||
|
||||
static Value *extract_value_from_variable_set_stmt(VariableSetStmt *stmt)
|
||||
{
|
||||
if (list_length(stmt->args) > 1) {
|
||||
|
|
@ -369,7 +470,16 @@ static Value *extract_value_from_variable_set_stmt(VariableSetStmt *stmt)
|
|||
}
|
||||
return value;
|
||||
}
|
||||
/*Datum' is one of the data types used in C language functions
|
||||
in PostgreSQL, which can represent any value in valid SQL types.
|
||||
*/
|
||||
/*Function: value_ To_ Datum
|
||||
|
||||
Formal parameters: (Value * value, Oid expected_type, const char * name)
|
||||
|
||||
Return value: Datum
|
||||
|
||||
Modify the hyperparameter value to Datum type.*/
|
||||
static Datum value_to_datum(Value *value, Oid expected_type, const char *name)
|
||||
{
|
||||
Datum result = (Datum)0;
|
||||
|
|
@ -457,6 +567,10 @@ static Datum value_to_datum(Value *value, Oid expected_type, const char *name)
|
|||
return result;
|
||||
}
|
||||
|
||||
/*Function: extract_ Datum_ From_ Variable_ Set_ Stmt
|
||||
Formal parameters: (VariableSetStmt * stmt, const HyperparameterDefinition * definition)
|
||||
Return value: Datum
|
||||
Use preprocessing to obtain and modify Datum.*/
|
||||
Datum extract_datum_from_variable_set_stmt(VariableSetStmt *stmt, const HyperparameterDefinition *definition)
|
||||
{
|
||||
Datum selected_value = (Datum)0;
|
||||
|
|
@ -470,6 +584,16 @@ Datum extract_datum_from_variable_set_stmt(VariableSetStmt *stmt, const Hyperpar
|
|||
return selected_value;
|
||||
}
|
||||
|
||||
|
||||
/*Function: configure_ Hyperparameters_ VSET
|
||||
|
||||
Formal parameters: (const HyperparameterDefinition definitions [], int32_t definitions_size,
|
||||
|
||||
List * hyperparameters, void * configuration)
|
||||
|
||||
Return value: Datum
|
||||
|
||||
Initialize hyperparameter configuration using set.*/
|
||||
void configure_hyperparameters_vset(const HyperparameterDefinition definitions[], int32_t definitions_size,
|
||||
List *hyperparameters, void *configuration)
|
||||
{
|
||||
|
|
@ -543,6 +667,16 @@ void configure_hyperparameters(const HyperparameterDefinition definitions[], int
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*Function: prepare_ Model_ Hyperparameters
|
||||
|
||||
Formal parameters: (const HyperparameterDefinition * definitions, int32_t definitions_size,
|
||||
|
||||
Void * hyperparameter_ Struct, MemoryContext memcxt)
|
||||
|
||||
Return value: List*
|
||||
|
||||
Prepare model hyperparameters.*/
|
||||
List *prepare_model_hyperparameters(const HyperparameterDefinition *definitions, int32_t definitions_size,
|
||||
void *hyperparameter_struct, MemoryContext memcxt)
|
||||
{
|
||||
|
|
@ -555,6 +689,17 @@ List *prepare_model_hyperparameters(const HyperparameterDefinition *definitions,
|
|||
return hyperparameters;
|
||||
}
|
||||
|
||||
/*Function: init_ Hyperparameters_ With_ Defaults
|
||||
|
||||
Formal parameters: (const HyperparameterDefinition definitions [], int32_t definitions_size,
|
||||
|
||||
Void * hyperparameter_ Struct
|
||||
|
||||
Return value: None
|
||||
|
||||
Initialize hyperparameters
|
||||
|
||||
*/
|
||||
void init_hyperparameters_with_defaults(const HyperparameterDefinition definitions[], int32_t definitions_size,
|
||||
void *hyperparameter_struct)
|
||||
{
|
||||
|
|
@ -563,6 +708,10 @@ void init_hyperparameters_with_defaults(const HyperparameterDefinition definitio
|
|||
}
|
||||
}
|
||||
|
||||
/*Function: print_ Hyperparameters
|
||||
Formal parameters: (int level, List * hyperparameters)
|
||||
Return value: None
|
||||
Output all hyperparameter attributes*/
|
||||
void print_hyperparameters(int level, List *hyperparameters)
|
||||
{
|
||||
foreach_cell(it, hyperparameters) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/**
|
||||
Copyright (c) 2021 Huawei Technologies Co.,Ltd.
|
||||
|
||||
|
|
@ -42,6 +46,18 @@ IDENTIFICATION
|
|||
/*
|
||||
* parameters that affect k-means (hyper-parameters)
|
||||
*/
|
||||
|
||||
/*KMeans is one of the top ten algorithms in data mining.
|
||||
In data mining practice, we often apply KMeans to various
|
||||
scenarios, because it is simple in principle, easy to implement
|
||||
and suitable for various data mining scenarios.*/
|
||||
|
||||
/*The basic steps are as follows:
|
||||
1. Select k objects from the data as the initial clustering centers.
|
||||
2. Calculate the distance from each cluster object to the cluster center.
|
||||
3. Calculate each cluster center again.
|
||||
4. Calculate termination conditions*/
|
||||
|
||||
typedef struct HyperparametersKMeans {
|
||||
ModelHyperparameters mhp; // place-holder
|
||||
SeedingFunction seeding = KMEANS_RANDOM_SEED;
|
||||
|
|
@ -225,6 +241,11 @@ static bool copy_slot_coordinates_to_array(GSPoint *coordinates, ModelTuple cons
|
|||
* given a set of centroids (as a PG list) and a point, this function compute the distance to the closest
|
||||
* centroid
|
||||
*/
|
||||
|
||||
/*Function: closest_ Centroid
|
||||
Formal parameters: (List const * centroids, GSPoint const * point, uint32_t const dimension, double * distance)
|
||||
Return value: bool
|
||||
Given a set of centroids (as PG list) and a point, this function calculates the distance to the nearest point*/
|
||||
static bool closest_centroid(List const *centroids, GSPoint const *point, uint32_t const dimension, double *distance)
|
||||
{
|
||||
ListCell const *current_centroid_cell = centroids ? centroids->head : nullptr;
|
||||
|
|
@ -314,6 +335,14 @@ static bool deal_sample(bool const sample, std::mt19937_64 *prng, GSPoint *batch
|
|||
return false;
|
||||
}
|
||||
|
||||
/*Function: compute_cost_and_weights
|
||||
Parameters: (list const * centroids, GS point const * points, uint32 _ tdimension,
|
||||
uint32_t const num_slots, double *cost)
|
||||
Return value: bool
|
||||
Given a set of centroids (as a PG list) and a set of points, this function
|
||||
calculates the cost of the centroid set and their weights
|
||||
(the number of points assigned to each centroid).*/
|
||||
|
||||
/*
|
||||
* given a set of centroids (as a PG list) and a set of points, this function computes
|
||||
* the cost of the set of centroids as well as their weights (number of points assigned
|
||||
|
|
@ -366,6 +395,12 @@ force_inline static void release_batch(GSPoint *batch, uint32_t const num_slots)
|
|||
* using a sum that provides higher precision (we could provide much higher precision at the cost
|
||||
* of allocating yet another array to keep correction terms for every dimension
|
||||
*/
|
||||
/*Function: aggregate_ Point
|
||||
Formal parameters: (double * centroid_aggregation, double const * new_point,
|
||||
Uint32_ T const dimension)
|
||||
Return value: None
|
||||
Given the moving average of the centroid and new points, this will add new points to the set*/
|
||||
|
||||
force_inline static void aggregate_point(double *centroid_aggregation, double const *new_point,
|
||||
uint32_t const dimension)
|
||||
{
|
||||
|
|
@ -380,6 +415,12 @@ force_inline static void aggregate_point(double *centroid_aggregation, double co
|
|||
* we assume that all slots in the batch are non-null (guaranteed by the upper call)
|
||||
* also, that the next set of centroids has been reset previous to the very first call
|
||||
*/
|
||||
|
||||
/*Function: update_ Centroids
|
||||
Formal parameters: (KMeansStateDescription * description, GSPoint * slots, uint32_t const num_slots,
|
||||
Uint32_ T const idx_ Current_ Centroids, uint32_ T const idx_ Next_ Centroids)
|
||||
Return value: None
|
||||
Update centroid*/
|
||||
static void update_centroids(KMeansStateDescription *description, GSPoint *slots, uint32_t const num_slots,
|
||||
uint32_t const idx_current_centroids, uint32_t const idx_next_centroids)
|
||||
{
|
||||
|
|
@ -450,6 +491,13 @@ static void update_centroids(KMeansStateDescription *description, GSPoint *slots
|
|||
/*
|
||||
* updates the minimum bounding box to contain the new given point
|
||||
*/
|
||||
/*Function: update_ Centroids
|
||||
Formal parameters: (double * const bbox_min, double * const bbox_max, double const * point,
|
||||
Uint32_ T const dimension)
|
||||
Return value: None
|
||||
Update the minimum bounding box to include the new given point*/
|
||||
|
||||
|
||||
force_inline static void update_bbox(double *const bbox_min, double *const bbox_max, double const *point,
|
||||
uint32_t const dimension)
|
||||
{
|
||||
|
|
@ -743,6 +791,11 @@ static List *one_data_pass(TrainModelState *pstate, KMeansStateDescription *stat
|
|||
/*
|
||||
* this sets the weights of a set of candidates to 1 (every point is the centroid of itself)
|
||||
*/
|
||||
/*Function: reset_ Weights
|
||||
Formal parameters: (List const * centroids)
|
||||
Return value: None
|
||||
Initialize weights (each point has a centroid of 1)*/
|
||||
|
||||
void reset_weights(List const *centroids)
|
||||
{
|
||||
ListCell const *current_centroid_cell = centroids ? centroids->head : nullptr;
|
||||
|
|
@ -983,6 +1036,12 @@ void reset_centroids(KMeansStateDescription *description, uint32_t const idx_cen
|
|||
* this produces the centroid by dividing the aggregate by the amount of points it got assigned
|
||||
* we assumed that population > 0
|
||||
*/
|
||||
|
||||
/*Function: finish_ Centroid
|
||||
Formal parameters: (double * centroid_aggregation,
|
||||
uint32_t const dimension, double const population)
|
||||
Return value: None
|
||||
Generate centroid*/
|
||||
force_inline void finish_centroid(double *centroid_aggregation, uint32_t const dimension, double const population)
|
||||
{
|
||||
double local_correction = 0.;
|
||||
|
|
@ -992,6 +1051,11 @@ force_inline void finish_centroid(double *centroid_aggregation, uint32_t const d
|
|||
}
|
||||
}
|
||||
|
||||
/*Function: merge_ Centroids
|
||||
Parameter: (KMeansStateDescription * description, uint32_t const idx_current_centroids,
|
||||
Uint32_ T const idx_ Next_ Centroids)
|
||||
Return value: None
|
||||
Merge centroids*/
|
||||
void merge_centroids(KMeansStateDescription *description, uint32_t const idx_current_centroids,
|
||||
uint32_t const idx_next_centroids)
|
||||
{
|
||||
|
|
@ -1416,6 +1480,11 @@ void kmeans_create_model(KMeansState *kmeans_state, Model *model)
|
|||
* 2) execute a seeding method (random++ or kmeans||) (at least one data pass but not more than 10),
|
||||
* 3) run Lloyd's algorithm (at least one data pass)
|
||||
*/
|
||||
/*Function: kmeans_ Run
|
||||
Formal parameters: (AlgorithmAPI * self, TrainModelState * pstate, Model * * models)
|
||||
Return value: None
|
||||
Run kmeans until convergence.*/
|
||||
|
||||
static void kmeans_run(AlgorithmAPI *self, TrainModelState *pstate, Model **models)
|
||||
{
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
|
|
@ -21,10 +25,23 @@
|
|||
* ---------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*Matrix refers to a set of complex numbers or real numbers arranged in a rectangular array in mathematics.
|
||||
It originated from the square matrix composed of coefficients and constants of equations. It was first proposed
|
||||
by the 19th century British mathematician Kelly. It is a common tool in advanced algebra, and its operation is
|
||||
an important problem in the field of numerical analysis. Decomposition of a matrix into a combination of simple
|
||||
matrices can simplify the operation of the matrix in theory and practical application.
|
||||
|
||||
For a matrix, at least the following operations should be included: addition, multiplication, transposition, eigenvalue
|
||||
calculation, and for a square matrix, determinant calculation is also required.*/
|
||||
#include "db4ai/matrix.h"
|
||||
|
||||
#define MATRIX_LIMITED_OUTPUT 30
|
||||
|
||||
|
||||
/*Function: void matrix_ Init_ Random_ Gaussian
|
||||
Formal parameters: (Matrix * matrix, int rows, int columns, float8 mu, float8 sigma, int seed)
|
||||
Return: None
|
||||
Production random Gaussian matrix*/
|
||||
// using Box-Muller implementation
|
||||
void matrix_init_random_gaussian(Matrix *matrix, int rows, int columns, float8 mu, float8 sigma, int seed)
|
||||
{
|
||||
|
|
@ -52,12 +69,20 @@ void matrix_init_random_gaussian(Matrix *matrix, int rows, int columns, float8 m
|
|||
}
|
||||
}
|
||||
|
||||
/*Function: matrix_ Init_ Kernel_ Gaussian
|
||||
Formal parameters: (int features, int components, float8 gamma, int seed, Matrix * weights, Matrix * offsets)
|
||||
Return: None
|
||||
Initialize a matrix using a specified number*/
|
||||
void matrix_init_kernel_gaussian(int features, int components, float8 gamma, int seed, Matrix *weights, Matrix *offsets)
|
||||
{
|
||||
matrix_init_random_gaussian(weights, features, components, 0.0, sqrt(2.0 * gamma), seed);
|
||||
matrix_init_random_uniform(offsets, components, 1, 0.0, 2.0 * M_PI, seed+1);
|
||||
}
|
||||
|
||||
/*Function: matrix_ Transform_ Kernel_ Gaussian
|
||||
Formal parameters: (const Matrix * input, const Matrix * weights, const Matrix * offsets, Matrix * output)
|
||||
Return: None
|
||||
Matrix transpose*/
|
||||
void matrix_transform_kernel_gaussian(const Matrix *input, const Matrix *weights, const Matrix *offsets, Matrix *output)
|
||||
{
|
||||
int components = weights->columns;
|
||||
|
|
@ -83,6 +108,10 @@ void matrix_transform_kernel_gaussian(const Matrix *input, const Matrix *weights
|
|||
matrix_mult_scalar(output, sqrt(2.0 / components));
|
||||
}
|
||||
|
||||
/*Function: matrix_ Init_ Random_ Uniform
|
||||
Formal parameters: (Matrix * matrix, int rows, int columns, float8 min, float8 max, int seed)
|
||||
Return: None
|
||||
Initializing a matrix using random floating-point numbers*/
|
||||
void matrix_init_random_uniform(Matrix *matrix, int rows, int columns, float8 min, float8 max, int seed)
|
||||
{
|
||||
Assert(min < max);
|
||||
|
|
@ -101,7 +130,10 @@ void matrix_init_random_uniform(Matrix *matrix, int rows, int columns, float8 mi
|
|||
*pd++ = min + range * u;
|
||||
}
|
||||
}
|
||||
|
||||
/*Function: matrix_ Init_ Random_ Bernoulli
|
||||
Formal parameters: (Matrix * matrix, int rows, int columns, float8 p, float8 min, float8 max, int seed)
|
||||
Return: None
|
||||
Generate Random Bernoulli Matrix*/
|
||||
void matrix_init_random_bernoulli(Matrix *matrix, int rows, int columns, float8 p, float8 min, float8 max, int seed)
|
||||
{
|
||||
matrix_init(matrix, rows, columns);
|
||||
|
|
@ -118,6 +150,11 @@ void matrix_init_random_bernoulli(Matrix *matrix, int rows, int columns, float8
|
|||
}
|
||||
}
|
||||
|
||||
/*Function: matrix_ Init_ Kernel_ Polynomial
|
||||
Formal parameters: (int features, int components, int degree, float8 coef0, int seed, Matrix * weights,
|
||||
Matrix * coefs)
|
||||
Return: int*
|
||||
Initialize a polynomial matrix using a specified number*/
|
||||
int *matrix_init_kernel_polynomial(int features, int components, int degree, float8 coef0, int seed, Matrix *weights,
|
||||
Matrix *coefs)
|
||||
{
|
||||
|
|
@ -152,6 +189,11 @@ int *matrix_init_kernel_polynomial(int features, int components, int degree, flo
|
|||
return pcomponents;
|
||||
}
|
||||
|
||||
/*Function: matrix_ Transform_ Kernel_ Polynomial
|
||||
Formal parameters: (const Matrix * input, int ncomponents, int * components, const Matrix * weights,
|
||||
Const Matrix * coefficients, Matrix * output)
|
||||
Return: None
|
||||
Polynomial matrix transpose*/
|
||||
void matrix_transform_kernel_polynomial(const Matrix *input, int ncomponents, int *components, const Matrix *weights,
|
||||
const Matrix *coefficients, Matrix *output)
|
||||
{
|
||||
|
|
@ -177,6 +219,10 @@ void matrix_transform_kernel_polynomial(const Matrix *input, int ncomponents, in
|
|||
matrix_mult_scalar(output, sqrt(1.0 / output->rows));
|
||||
}
|
||||
|
||||
/*Function: matrix_ Mult
|
||||
Formal parameters: (const Matrix * matrix1, const Matrix * matrix2, Matrix * result)
|
||||
Return value: None
|
||||
matrix multiplication*/
|
||||
void matrix_mult(const Matrix *matrix1, const Matrix *matrix2, Matrix *result)
|
||||
{
|
||||
Assert(matrix1 != nullptr);
|
||||
|
|
@ -205,6 +251,10 @@ void matrix_mult(const Matrix *matrix1, const Matrix *matrix2, Matrix *result)
|
|||
}
|
||||
}
|
||||
|
||||
/*Function: matrix_ Print
|
||||
Formal parameters: (const Matrix * matrix, StringInfo buf, bool full)
|
||||
Return value: None
|
||||
Print Matrix*/
|
||||
void matrix_print(const Matrix *matrix, StringInfo buf, bool full)
|
||||
{
|
||||
Assert(matrix != nullptr);
|
||||
|
|
@ -245,6 +295,24 @@ void matrix_print(const Matrix *matrix, StringInfo buf, bool full)
|
|||
appendStringInfoChar(buf, ']');
|
||||
}
|
||||
|
||||
/*Function: elog_ Matrix
|
||||
Formal parameters: (int level, const char * msg, const matrix * matrix)
|
||||
Return value: None
|
||||
Matrix error*/
|
||||
/*elog is an old mode that can be equivalent to the ereport mode.
|
||||
You can see that it provides level and the error level is the same,
|
||||
but it does not provide errcode. As mentioned earlier, the default
|
||||
errcode is provided based on the severity level. Then the message
|
||||
is passed through an auxiliary function errmsg_ Internal() goes to
|
||||
show it, and the process is different from the errmsg in ereport mentioned
|
||||
earlier. errmsg() is set according to regional settings, such as it can be
|
||||
translated into the language of the corresponding country, such as Chinese.
|
||||
In fact, errmsg_ Internal() is a language that is not limited by translation and
|
||||
can automatically print out the original language.
|
||||
Why should we keep this old pattern? Because it is concise enough, when
|
||||
there are some internal errors, such as internal errors in the PG kernel, these
|
||||
errors are not actually displayed to the user and are not of interest to the user.
|
||||
This concise mode can be used for printing, which is very convenient and has been preserved.*/
|
||||
void elog_matrix(int elevel, const char *msg, const Matrix *matrix)
|
||||
{
|
||||
if (is_errmodule_enable(elevel, MOD_DB4AI)) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
|
|
@ -40,6 +44,20 @@
|
|||
double total_exec_time = 0.0;
|
||||
struct timespec exec_start_time, exec_end_time;
|
||||
|
||||
/*XGBoost provides gradient lifting tree (also called GBDT, GBM), which
|
||||
can solve many data science problems quickly and accurately. The same
|
||||
code can run in major distributed environments (Apache Hadoop, Apache
|
||||
Spark, Apache Flink). System optimization: parallel computing: supporting
|
||||
parallel computing. Tree pruning: use greedy algorithm to choose the best
|
||||
splitting point and then start pruning. Hardware optimization: effective use
|
||||
of hardware resources. Algorithm addition: regularization: preventing over-fitting.
|
||||
Sparse consciousness: automatically "learn" the best missing value according to
|
||||
the training loss and deal with different types of sparse patterns in the data more
|
||||
effectively. Weighted quantile sketch: Using the distributed weighted quantile sketch
|
||||
algorithm, the optimal split point in the weighted data set can be found effectively.
|
||||
Cross-validation: Each iteration has a built-in cross-validation method.*/
|
||||
|
||||
|
||||
#define XGBOOST_LIB_NAME "libxgboost.so"
|
||||
|
||||
typedef const int (*XGBoosterSetParam_Sym)(BoosterHandle handle, const char *name, const char *value);
|
||||
|
|
@ -430,6 +448,10 @@ void setup_xg_chunk(xg_data_t &xg_data)
|
|||
/*
|
||||
* this function initializes the algorithm
|
||||
*/
|
||||
/*Function: xgboost_ Create
|
||||
Parameter: (AlgorithmAPI * self, const TrainModel * pnode)
|
||||
Return value: TrainModelState*
|
||||
Create xgboost*/
|
||||
static TrainModelState *xgboost_create(AlgorithmAPI *self, const TrainModel *pnode)
|
||||
{
|
||||
if (pnode->configurations != 1)
|
||||
|
|
@ -449,6 +471,11 @@ static TrainModelState *xgboost_create(AlgorithmAPI *self, const TrainModel *pno
|
|||
* chunk.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/*Function: trainXG
|
||||
Formal parameters: (AlgorithmAPI * alg, const HyperparamsXGBoost * xg_hyp, xg_data_t * chunk, const int n_tuples,
|
||||
Bool first_ Call=true)
|
||||
Return value: None
|
||||
Training xgboost*/
|
||||
void trainXG(AlgorithmAPI *alg, const HyperparamsXGBoost *xg_hyp, xg_data_t *chunk, const int n_tuples,
|
||||
bool first_call = true)
|
||||
{
|
||||
|
|
@ -714,6 +741,10 @@ ModelPredictor xgboost_predict_prepare(AlgorithmAPI *, SerializedModel const *mo
|
|||
|
||||
return reinterpret_cast<ModelPredictor>(xgboostm);
|
||||
}
|
||||
/*Function: xgboost_ Predict
|
||||
Formal parameters: (AlgorithmAPI * self, TrainModelState * pstate, Model * * models)
|
||||
Return value: Datum
|
||||
Using xgboost for prediction*/
|
||||
|
||||
Datum xgboost_predict(AlgorithmAPI *, ModelPredictor model, Datum *values, bool *isnull, Oid *types, int ncolumns)
|
||||
{
|
||||
|
|
@ -752,6 +783,12 @@ Datum xgboost_predict(AlgorithmAPI *, ModelPredictor model, Datum *values, bool
|
|||
/*
|
||||
* used in EXPLAIN MODEL
|
||||
*/
|
||||
|
||||
/*Function: xgboost_ Explain
|
||||
Parameter: (AlgorithmAPI * self, SerializedModel const * model, Oid return_type)
|
||||
Return value: List
|
||||
Explain xgboost*/
|
||||
|
||||
List *xgboost_explain(AlgorithmAPI *self, SerializedModel const *model, Oid return_type)
|
||||
{
|
||||
if (unlikely(!model))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* hypopg_index.cpp: Implementation of hypothetical indexes for openGauss
|
||||
|
|
@ -17,13 +21,30 @@
|
|||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
In PG database, if you check whether an index contributes to one or more queries,
|
||||
HypoPG can play a key role. It is an extension of postgresql, which allows you to create
|
||||
virtual indexes and observe whether the optimizer is used. Therefore, you can provide methods
|
||||
for which queries need to be optimized and which indexes you want to try. So for the average user,
|
||||
how to better judge whether indexing is effective or not? Virtual index is a very useful thing, with no
|
||||
side effects. It is just a virtual index. After establishing a virtual index, you can check the COST estimate
|
||||
after adding the index through EXPLAIN to judge whether the cost will be reduced. In addition, the hypothetica
|
||||
l index HypoPG will create is not stored in any directory, but in the connection private memory. Therefore, it
|
||||
will not inflate any tables, nor will it affect any concurrent connections. Because it is assumed that indexes
|
||||
don't really exist, HypoPG ensures that they will only be used with a simple EXPLAIN statement (no ANALYZE option).
|
||||
|
||||
Virtual index does not occupy space, and can be used to evaluate the performance of sql query conveniently,
|
||||
which helps us to understand the effect of query optimization.
|
||||
*/
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include "postgres.h"
|
||||
#include "fmgr.h"
|
||||
#include "funcapi.h"
|
||||
#include "miscadmin.h"
|
||||
#include "access/gist.h"
|
||||
#include "access/gist.h" /*Storage index*/
|
||||
#include "access/nbtree.h"
|
||||
#include "access/reloptions.h"
|
||||
#include "access/spgist.h"
|
||||
|
|
@ -94,6 +115,13 @@ static void hypo_injectHypotheticalIndex(PlannerInfo *root, Oid relationObjectId
|
|||
static List *get_table_indexes(Oid oid);
|
||||
static List *get_index_attrnum(Oid oid);
|
||||
|
||||
/*
|
||||
Parameter: None,
|
||||
return value: None.
|
||||
Judge whether the virtual index of the instance exists.
|
||||
If it does not exist, initialize the virtual index with
|
||||
AllocSetContextCreate () function and mark it as unexplained.
|
||||
*/
|
||||
void InitHypopg()
|
||||
{
|
||||
// init memory context
|
||||
|
|
@ -103,16 +131,30 @@ void InitHypopg()
|
|||
}
|
||||
isExplain = false;
|
||||
}
|
||||
/*
|
||||
SQLAllocConnect() allocates a connection handle and associated resources within the
|
||||
environment that is identified by the input environment handle. Call SQLGetInfo() with
|
||||
fInfoType set to SQL_ACTIVE_CONNECTIONS to query the number of connections that
|
||||
can be allocated at any one time. SQLAllocEnv() must be called before calling this function.*/
|
||||
|
||||
|
||||
/*
|
||||
* This function is used for setting prev_utility_hook to rewrite
|
||||
* standard_ProcessUtility by extension.
|
||||
Set_hypopg_prehook function:
|
||||
Parameter: ProcessUtility_hook_type func,
|
||||
return value: none.
|
||||
Function: Set prev_utility_hook, rewrite standard_ProcessUtility,
|
||||
and control the execution of specific activities in the database
|
||||
by using the hook mechanism of the database.
|
||||
|
||||
*/
|
||||
void set_hypopg_prehook(ProcessUtility_hook_type func)
|
||||
{
|
||||
prev_utility_hook = func;
|
||||
}
|
||||
|
||||
/*
|
||||
Full hook mechanism to control all kinds of activities of the database.*/
|
||||
|
||||
void hypopg_register_hook()
|
||||
{
|
||||
// register hooks
|
||||
|
|
@ -133,6 +175,16 @@ void hypopg_register_hook()
|
|||
* Wrapper around GetNewRelFileNode
|
||||
* Return a new OID for an hypothetical index.
|
||||
*/
|
||||
|
||||
/*
|
||||
Hypo_getNewOid function:
|
||||
Parameter: oid
|
||||
Return value: oid
|
||||
Open the relationship that we want a new OID,
|
||||
now close the relationship and release the lock,
|
||||
open pg_class to get a new OID, request a new relfilenode,
|
||||
close pg_class and unlock it immediately.*/
|
||||
|
||||
static Oid hypo_getNewOid(Oid relid)
|
||||
{
|
||||
Relation pg_class;
|
||||
|
|
@ -143,7 +195,18 @@ static Oid hypo_getNewOid(Oid relid)
|
|||
|
||||
/* Open the relation on which we want a new OID */
|
||||
relation = heap_open(relid, AccessShareLock);
|
||||
|
||||
|
||||
/*In PostgreSQL, AccessShareLock is a lock type used to
|
||||
control concurrent access to database objects. It is a read
|
||||
lock that allows multiple transactions to read from the same
|
||||
object at the same time, but it prevents concurrent transactions
|
||||
from acquiring conflicting locks, such as write locks or exclusive locks.
|
||||
When a transaction obtains AccessShareLock on an object, other
|
||||
transactions can also obtain AccessShareLock on the same object.
|
||||
This means that multiple transactions can read objects at the same
|
||||
time without interfering with each other.*/
|
||||
|
||||
|
||||
reltablespace = relation->rd_rel->reltablespace;
|
||||
relpersistence = relation->rd_rel->relpersistence;
|
||||
|
||||
|
|
@ -177,6 +240,15 @@ void hypo_utility_hook(Node *parsetree, const char *queryString, ParamListInfo p
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Hypo_index_match_table function:
|
||||
|
||||
Formal parameter: hypoIndex *entry Oid relid
|
||||
|
||||
Return value: bool
|
||||
|
||||
Judge whether the virtual index and the object identifier (OID) match.
|
||||
*/
|
||||
static bool hypo_index_match_table(hypoIndex *entry, Oid relid)
|
||||
{
|
||||
/* Hypothetical index on the exact same relation, use it. */
|
||||
|
|
@ -210,6 +282,15 @@ static bool hypo_query_walker(Node *parsetree)
|
|||
}
|
||||
|
||||
/* Reset the isExplain flag after each query */
|
||||
|
||||
/*Hypo_executorEnd_hook function
|
||||
|
||||
Parameter: QueryDesc *queryDesc
|
||||
|
||||
Return value: None
|
||||
|
||||
Reset the isExplain flag after each query.*/
|
||||
|
||||
static void hypo_executorEnd_hook(QueryDesc *queryDesc)
|
||||
{
|
||||
isExplain = false;
|
||||
|
|
@ -220,6 +301,19 @@ static void hypo_executorEnd_hook(QueryDesc *queryDesc)
|
|||
standard_ExecutorEnd(queryDesc);
|
||||
}
|
||||
}
|
||||
|
||||
/*Get_table_indexes function:
|
||||
|
||||
Parameter: oid
|
||||
|
||||
Back to: list
|
||||
|
||||
Query the list corresponding to the object identifier
|
||||
|
||||
First open the specified heap to get the list of
|
||||
|
||||
the specified heap, then close the heap to return to the list.*/
|
||||
|
||||
List *get_table_indexes(Oid oid)
|
||||
{
|
||||
Relation rel = heap_open(oid, NoLock);
|
||||
|
|
@ -227,8 +321,18 @@ List *get_table_indexes(Oid oid)
|
|||
heap_close(rel, NoLock);
|
||||
return indexes;
|
||||
}
|
||||
/*Read-only here will not cause deadlock, so use NoLock lock.*/
|
||||
|
||||
/* Return the names of all the columns involved in the index. */
|
||||
|
||||
/*Get_index_attrnum function:
|
||||
|
||||
Parameter: index_oid
|
||||
|
||||
Back to: list
|
||||
|
||||
Returns the names of all columns.*/
|
||||
|
||||
List *get_index_attrnum(Oid index_oid)
|
||||
{
|
||||
HeapTuple index_tup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
|
||||
|
|
@ -427,7 +531,16 @@ static hypoIndex *hypo_newIndex(Oid relid, char *accessMethod, int nkeycolumns,
|
|||
return entry;
|
||||
}
|
||||
|
||||
/* Add an hypoIndex to hypo_index_list */
|
||||
/*
|
||||
|
||||
Hypo_addIndex function:
|
||||
|
||||
Formal parameter: *entry
|
||||
|
||||
Return: None
|
||||
|
||||
Add a virtual index to the virtual index table.*/
|
||||
|
||||
static void hypo_addIndex(hypoIndex *entry)
|
||||
{
|
||||
MemoryContext oldcontext;
|
||||
|
|
@ -444,6 +557,12 @@ static void hypo_addIndex(hypoIndex *entry)
|
|||
}
|
||||
|
||||
/*
|
||||
Hypo_index_reset function:
|
||||
|
||||
Formal parameter: none
|
||||
|
||||
Return: None
|
||||
|
||||
* Remove cleanly all hypothetical indexes by calling hypo_index_remove() on
|
||||
* each entry. hypo_index_remove() function pfree all allocated memory
|
||||
*/
|
||||
|
|
@ -495,7 +614,7 @@ static void hypo_process_attr(IndexStmt *node, hypoIndex *volatile entry, String
|
|||
int attn;
|
||||
|
||||
attn = 0;
|
||||
foreach (lc, node->indexParams) {
|
||||
foreach (lc, node->indexParams) { /*Traverse all nodes*/
|
||||
IndexElem *attribute = (IndexElem *)lfirst(lc);
|
||||
Oid atttype = InvalidOid;
|
||||
Oid opclass;
|
||||
|
|
@ -690,7 +809,7 @@ static const hypoIndex *hypo_index_store_parsetree(IndexStmt *node, const char *
|
|||
if (nkeycolumns > INDEX_MAX_KEYS) {
|
||||
elog(ERROR, "hypopg: cannot use more thant %d columns in an index", INDEX_MAX_KEYS);
|
||||
}
|
||||
|
||||
//Show basic attributes
|
||||
initStringInfo(&indexRelationName);
|
||||
appendStringInfoString(&indexRelationName, node->accessMethod);
|
||||
appendStringInfoString(&indexRelationName, "_");
|
||||
|
|
@ -761,7 +880,7 @@ static const hypoIndex *hypo_index_store_parsetree(IndexStmt *node, const char *
|
|||
pull_varattnos((Node *)entry->indexprs, 1, &indexattrs);
|
||||
pull_varattnos((Node *)entry->indpred, 1, &indexattrs);
|
||||
|
||||
for (i = FirstLowInvalidHeapAttributeNumber + 1; i < 0; i++) {
|
||||
for (i = FirstLowInvalidHeapAttributeNumber + 1; i < 0; i++) { //I is a negative number
|
||||
if (i != ObjectIdAttributeNumber && bms_is_member(i - FirstLowInvalidHeapAttributeNumber, indexattrs)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("hypopg: index creation on system columns is not supported")));
|
||||
|
|
@ -882,7 +1001,13 @@ static bool hypo_index_remove(Oid indexid)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* pfree all allocated memory for within an hypoIndex and the entry itself. */
|
||||
/*
|
||||
Hypo_index_pfree function:
|
||||
|
||||
Formal parameter: entry
|
||||
|
||||
Return: None
|
||||
pfree all allocated memory for within an hypoIndex and the entry itself. */
|
||||
static void hypo_index_pfree(hypoIndex *entry)
|
||||
{
|
||||
/* pfree all memory that has been allocated */
|
||||
|
|
@ -1166,6 +1291,13 @@ Datum hypopg_display_index(PG_FUNCTION_ARGS)
|
|||
}
|
||||
|
||||
/*
|
||||
Hypopg_create_index function:
|
||||
|
||||
Parameter: (PG_FUNCTION_ARGS)
|
||||
|
||||
Return: (Datum)
|
||||
|
||||
|
||||
* SQL wrapper to create an hypothetical index with his parsetree
|
||||
*/
|
||||
Datum hypopg_create_index(PG_FUNCTION_ARGS)
|
||||
|
|
@ -1244,6 +1376,14 @@ Datum hypopg_create_index(PG_FUNCTION_ARGS)
|
|||
}
|
||||
|
||||
/*
|
||||
|
||||
Hypopg_drop_index function:
|
||||
|
||||
Parameter: (PG_FUNCTION_ARGS)
|
||||
|
||||
Return: (Datum)
|
||||
|
||||
Delete the specified index
|
||||
* SQL wrapper to drop an hypothetical index.
|
||||
*/
|
||||
Datum hypopg_drop_index(PG_FUNCTION_ARGS)
|
||||
|
|
@ -1253,6 +1393,8 @@ Datum hypopg_drop_index(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_BOOL(hypo_index_remove(indexid));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* SQL Wrapper around the hypothetical index size estimation
|
||||
*/
|
||||
|
|
@ -1472,7 +1614,14 @@ static void hypo_estimate_index(hypoIndex *entry, RelOptInfo *rel)
|
|||
}
|
||||
|
||||
/*
|
||||
* Estimate a single index's column of an hypothetical index.
|
||||
*
|
||||
Hypo_estimate_index_colsize function:
|
||||
|
||||
Parameter: (hypoIndex *entry, int col)
|
||||
|
||||
Return: (int)
|
||||
|
||||
Estimate the index column size of a virtual index.
|
||||
*/
|
||||
static int hypo_estimate_index_colsize(hypoIndex *entry, int col)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
|
|
@ -24,6 +28,25 @@
|
|||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*Single index recommendation: it is suitable for the case of large data in the table,
|
||||
and it will not be recommended if the data is too small.
|
||||
|
||||
When there is only one query condition in where, a single index is recommended;
|
||||
if only id is in where, only id is recommended as the index; When there are multiple query conditions in where,
|
||||
multiple indexes are recommended. For example, if id and name are used as conditions in where, they are recommended
|
||||
as joint indexes together. However, if id and person_id exist
|
||||
at the same time, id is recommended by default(the reason is unknown at present).
|
||||
|
||||
When there are conditions such as order by and group by besides the where statement in the query,
|
||||
all the attributes in where, order by and group by are recommended as joint indexes.
|
||||
|
||||
When using like fuzzy query or precise query, the attribute is not indexed, and = attribute must be recommended for indexing.
|
||||
|
||||
When there are too many conditions in the query and there are more than three attributes, it is still recommended that the joint
|
||||
attributes are more than three, which will lead to too many recommended indexes, which is debatable. It is best to recommend
|
||||
the attributes in the index to be less than three. However, this may need to be learned through DRL to determine which attributes
|
||||
in a query to choose to build an index.*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/tableam.h"
|
||||
|
|
@ -58,6 +81,7 @@
|
|||
#define MAX_SAMPLE_ROWS 10000 /* sampling range for executing a query */
|
||||
#define CARDINALITY_THRESHOLD 30 /* the threshold of index selection */
|
||||
|
||||
/*Some structures used for queries*/
|
||||
#define RelAttrName(__tupdesc, __attridx) (NameStr((__tupdesc)->attrs[(__attridx)]->attname))
|
||||
#define IsSameRel(_schema1, _table1, _schema2, _table2) \
|
||||
((!_schema1 || !_schema2 || strcasecmp(_schema1, _schema2) == 0) && strcasecmp(_table1, _table2) == 0)
|
||||
|
|
@ -241,17 +265,26 @@ Datum gs_index_advise(PG_FUNCTION_ARGS)
|
|||
}
|
||||
|
||||
/*
|
||||
* suggest_index
|
||||
* Parse the given query and return the suggested indexes. The suggested
|
||||
* index consists of table names and column names.
|
||||
*
|
||||
* The main steps are summarized as follows:
|
||||
* 1. Get parse tree;
|
||||
* 2. Find and parse SelectStmt structures;
|
||||
* 3. Parse 'from' and 'where' clause, and add candidate indexes for tables;
|
||||
* 4. Determine the driver table;
|
||||
* 5. Parse 'group' and 'order' clause and add candidate indexes for tables;
|
||||
* 6. Add candidate indexes for drived tables according to the 'join' conditions.
|
||||
Suggest_index function:
|
||||
|
||||
Parameter: (constchar * query _ string, _ out _ int * len)
|
||||
|
||||
Return value: SuggestedIndex
|
||||
|
||||
Function: Parse the given query and return the suggested index. This proposed index consists of table names and column names.
|
||||
The main steps are summarized as follows:
|
||||
|
||||
1. Obtain a parse tree;
|
||||
|
||||
2. Find and parse the structure of SelectStmt;
|
||||
|
||||
3. Parse the "from" and "where" clauses and add candidate indexes to the table.
|
||||
|
||||
4. Determine the driver table;
|
||||
|
||||
5. Analyze the "group" and "order" clauses and add candidate indexes to the table;
|
||||
|
||||
6. Add a candidate index for the driver table according to the "Join" condition.
|
||||
*/
|
||||
SuggestedIndex *suggest_index(const char *query_string, _out_ int *len)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,11 +14,23 @@ 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,22 +11,27 @@
|
|||
# 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)):
|
||||
|
|
@ -56,6 +61,16 @@ 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,6 +11,13 @@
|
|||
# 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,17 +12,16 @@
|
|||
# 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,8 +13,14 @@
|
|||
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,7 +55,12 @@ 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:
|
||||
|
|
@ -87,6 +92,16 @@ 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
|
||||
|
|
@ -96,6 +111,8 @@ 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:
|
||||
|
|
@ -122,7 +139,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)
|
||||
|
|
@ -170,7 +187,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,6 +43,7 @@ except ImportError:
|
|||
|
||||
SKIP_LIST = ('COMMENT', 'LOG')
|
||||
|
||||
# The global variable acts as a switch that controls whether the program runs
|
||||
dbmind_master_should_exit = False
|
||||
|
||||
|
||||
|
|
@ -57,8 +58,16 @@ 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:
|
||||
|
|
@ -148,10 +157,12 @@ 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,6 +27,17 @@ 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,7 +15,11 @@ 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,7 +12,11 @@
|
|||
# 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,13 +18,19 @@ 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,
|
||||
|
|
@ -33,6 +39,7 @@ 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)
|
||||
|
|
@ -41,11 +48,13 @@ 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,6 +13,11 @@
|
|||
|
||||
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):
|
||||
|
|
@ -24,7 +29,9 @@ 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
|
||||
|
|
@ -36,11 +43,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:]:
|
||||
|
|
@ -52,6 +59,7 @@ 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():
|
||||
|
|
@ -66,6 +74,7 @@ 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(' ')
|
||||
|
|
@ -88,6 +97,7 @@ 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
|
||||
|
|
@ -125,6 +135,7 @@ 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()):
|
||||
|
|
@ -142,6 +153,7 @@ 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,12 +23,16 @@ 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:
|
||||
|
|
@ -38,6 +42,7 @@ 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:
|
||||
|
|
@ -47,6 +52,7 @@ 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:
|
||||
|
|
@ -74,6 +80,7 @@ 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] == ' (':
|
||||
|
|
@ -183,6 +190,7 @@ 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
|
||||
|
|
@ -222,6 +230,7 @@ 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
|
||||
|
|
@ -264,6 +273,7 @@ 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,9 +26,11 @@ 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
|
||||
|
|
@ -192,9 +194,12 @@ class IndexAdvisor:
|
|||
self.workload_used_index))
|
||||
if DRIVER:
|
||||
self.db.close_conn()
|
||||
|
||||
opt_config = greedy_determine_opt_config(self.workload_info[0], atomic_config_total,
|
||||
candidate_indexes, self.index_cost_total[0])
|
||||
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])
|
||||
self.retain_lower_cost_index(candidate_indexes)
|
||||
if len(opt_config) == 0:
|
||||
print("No optimal indexes generated!")
|
||||
|
|
@ -943,7 +948,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 int value" %
|
||||
raise argparse.ArgumentTypeError("%s is an invalid positive float value" %
|
||||
args.max_index_storage)
|
||||
JSON_TYPE = args.json
|
||||
MAX_INDEX_NUM = args.max_index_num
|
||||
|
|
@ -971,7 +976,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=int)
|
||||
help="Maximum storage of suggested indexes/MB", type=float)
|
||||
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',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,397 @@
|
|||
import sys
|
||||
import math
|
||||
import random
|
||||
import copy
|
||||
|
||||
STORAGE_THRESHOLD = 0
|
||||
AVAILABLE_CHOICES = None
|
||||
ATOMIC_CHOICES = None
|
||||
WORKLOAD_INFO = None
|
||||
MAX_INDEX_NUM = 0
|
||||
|
||||
|
||||
def is_same_index(index, compared_index):
|
||||
return index.table == compared_index.table and \
|
||||
index.columns == compared_index.columns and \
|
||||
index.index_type == compared_index.index_type
|
||||
|
||||
|
||||
def atomic_config_is_valid(atomic_config, config):
|
||||
# if candidate indexes contains all atomic index of current config1, then record it
|
||||
for atomic_index in atomic_config:
|
||||
is_exist = False
|
||||
for index in config:
|
||||
if is_same_index(index, atomic_index):
|
||||
index.storage = atomic_index.storage
|
||||
is_exist = True
|
||||
break
|
||||
if not is_exist:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def find_subsets_num(choice):
|
||||
atomic_subsets_num = []
|
||||
for pos, atomic in enumerate(ATOMIC_CHOICES):
|
||||
if not atomic or len(atomic) > len(choice):
|
||||
continue
|
||||
# find valid atomic index
|
||||
if atomic_config_is_valid(atomic, choice):
|
||||
atomic_subsets_num.append(pos)
|
||||
# find the same atomic index as the candidate index
|
||||
if len(atomic) == 1 and (is_same_index(choice[-1], atomic[0])):
|
||||
choice[-1].atomic_pos = pos
|
||||
return atomic_subsets_num
|
||||
|
||||
|
||||
def find_best_benefit(choice):
|
||||
atomic_subsets_num = find_subsets_num(choice)
|
||||
total_benefit = 0
|
||||
for ind, obj in enumerate(WORKLOAD_INFO):
|
||||
# calculate the best benefit for the current sql
|
||||
max_benefit = 0
|
||||
for pos in atomic_subsets_num:
|
||||
if (obj.cost_list[0] - obj.cost_list[pos]) > max_benefit:
|
||||
max_benefit = obj.cost_list[0] - obj.cost_list[pos]
|
||||
total_benefit += max_benefit
|
||||
return total_benefit
|
||||
|
||||
|
||||
def get_diff(available_choices, choices):
|
||||
except_choices = copy.copy(available_choices)
|
||||
for i in available_choices:
|
||||
for j in choices:
|
||||
if is_same_index(i, j):
|
||||
except_choices.remove(i)
|
||||
return except_choices
|
||||
|
||||
|
||||
class State(object):
|
||||
"""
|
||||
The game state of the Monte Carlo tree search,
|
||||
the state data recorded under a certain Node node,
|
||||
including the current game score, the current number of game rounds,
|
||||
and the execution record from the beginning to the current.
|
||||
|
||||
It is necessary to realize whether the current state has reached the end of the game state,
|
||||
and support the operation of randomly fetching from the Action collection.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.current_storage = 0.0
|
||||
self.current_benefit = 0.0
|
||||
# record the sum of choices up to the current state
|
||||
self.accumulation_choices = []
|
||||
# record available choices of current state
|
||||
self.available_choices = []
|
||||
self.displayable_choices = []
|
||||
|
||||
def get_available_choices(self):
|
||||
return self.available_choices
|
||||
|
||||
def set_available_choices(self, choices):
|
||||
self.available_choices = choices
|
||||
|
||||
def get_current_storage(self):
|
||||
return self.current_storage
|
||||
|
||||
def set_current_storage(self, value):
|
||||
self.current_storage = value
|
||||
|
||||
def get_current_benefit(self):
|
||||
return self.current_benefit
|
||||
|
||||
def set_current_benefit(self, value):
|
||||
self.current_benefit = value
|
||||
|
||||
def get_accumulation_choices(self):
|
||||
return self.accumulation_choices
|
||||
|
||||
def set_accumulation_choices(self, choices):
|
||||
self.accumulation_choices = choices
|
||||
|
||||
def is_terminal(self):
|
||||
# the current node is a leaf node
|
||||
return len(self.accumulation_choices) == MAX_INDEX_NUM
|
||||
|
||||
def compute_benefit(self):
|
||||
return self.current_benefit
|
||||
|
||||
def get_next_state_with_random_choice(self):
|
||||
# ensure that the choices taken are not repeated
|
||||
if not self.available_choices:
|
||||
return None
|
||||
random_choice = random.choice([choice for choice in self.available_choices])
|
||||
self.available_choices.remove(random_choice)
|
||||
choice = copy.copy(self.accumulation_choices)
|
||||
choice.append(random_choice)
|
||||
benefit = find_best_benefit(choice)
|
||||
# if current choice not satisfy restrictions, then continue get next choice
|
||||
if benefit <= self.current_benefit or \
|
||||
self.current_storage + random_choice.storage > STORAGE_THRESHOLD:
|
||||
return self.get_next_state_with_random_choice()
|
||||
|
||||
next_state = State()
|
||||
# initialize the properties of the new state
|
||||
next_state.set_accumulation_choices(choice)
|
||||
next_state.set_current_benefit(benefit)
|
||||
next_state.set_current_storage(self.current_storage + random_choice.storage)
|
||||
next_state.set_available_choices(get_diff(AVAILABLE_CHOICES, choice))
|
||||
return next_state
|
||||
|
||||
def __repr__(self):
|
||||
self.displayable_choices = ['{}: {}'.format(choice.table, choice.columns)
|
||||
for choice in self.accumulation_choices]
|
||||
return "reward: {}, storage :{}, choices: {}".format(
|
||||
self.current_benefit, self.current_storage, self.displayable_choices)
|
||||
|
||||
|
||||
class Node(object):
|
||||
"""
|
||||
The Node of the Monte Carlo tree search tree contains the parent node and
|
||||
current point information,
|
||||
which is used to calculate the traversal times and quality value of the UCB,
|
||||
and the State of the Node selected by the game.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.visit_number = 0
|
||||
self.quality = 0.0
|
||||
|
||||
self.parent = None
|
||||
self.children = []
|
||||
self.state = None
|
||||
|
||||
def get_parent(self):
|
||||
return self.parent
|
||||
|
||||
def set_parent(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
def get_children(self):
|
||||
return self.children
|
||||
|
||||
def expand_child(self, node):
|
||||
node.set_parent(self)
|
||||
self.children.append(node)
|
||||
|
||||
def set_state(self, state):
|
||||
self.state = state
|
||||
|
||||
def get_state(self):
|
||||
return self.state
|
||||
|
||||
def get_visit_number(self):
|
||||
return self.visit_number
|
||||
|
||||
def set_visit_number(self, number):
|
||||
self.visit_number = number
|
||||
|
||||
def update_visit_number(self):
|
||||
self.visit_number += 1
|
||||
|
||||
def get_quality_value(self):
|
||||
return self.quality
|
||||
|
||||
def set_quality_value(self, value):
|
||||
self.quality = value
|
||||
|
||||
def update_quality_value(self, reward):
|
||||
self.quality += reward
|
||||
|
||||
def is_all_expand(self):
|
||||
return len(self.children) == \
|
||||
len(AVAILABLE_CHOICES) - len(self.get_state().get_accumulation_choices())
|
||||
|
||||
def __repr__(self):
|
||||
return "Node: {}, Q/N: {}/{}, State: {}".format(
|
||||
hash(self), self.quality, self.visit_number, self.state)
|
||||
|
||||
|
||||
def tree_policy(node):
|
||||
"""
|
||||
In the Selection and Expansion stages of Monte Carlo tree search,
|
||||
the node that needs to be searched (such as the root node) is passed in,
|
||||
and the best node that needs to be expanded is returned
|
||||
according to the exploration/exploitation algorithm.
|
||||
Note that if the node is a leaf node, it will be returned directly.
|
||||
|
||||
The basic strategy is to first find the child nodes that have not been selected at present,
|
||||
and select them randomly if there are more than one. If both are selected,
|
||||
find the one with the largest UCB value that has weighed exploration/exploitation,
|
||||
and randomly select if the UCB values are equal.
|
||||
"""
|
||||
|
||||
# check if the current node is leaf node
|
||||
while node and not node.get_state().is_terminal():
|
||||
|
||||
if node.is_all_expand():
|
||||
node = best_child(node, True)
|
||||
else:
|
||||
# return the new sub node
|
||||
sub_node = expand(node)
|
||||
# when there is no node that satisfies the condition in the remaining nodes,
|
||||
# this state is empty
|
||||
if sub_node.get_state():
|
||||
return sub_node
|
||||
|
||||
# return the leaf node
|
||||
return node
|
||||
|
||||
|
||||
def default_policy(node):
|
||||
"""
|
||||
In the Simulation stage of Monte Carlo tree search, input a node that needs to be expanded,
|
||||
create a new node after random operation, and return the reward of the new node.
|
||||
Note that the input node should not be a child node,
|
||||
and there are unexecuted Actions that can be expendable.
|
||||
|
||||
The basic strategy is to choose the Action at random.
|
||||
"""
|
||||
|
||||
# get the state of the game
|
||||
current_state = copy.deepcopy(node.get_state())
|
||||
|
||||
# run until the game over
|
||||
while not current_state.is_terminal():
|
||||
# pick one random action to play and get next state
|
||||
next_state = current_state.get_next_state_with_random_choice()
|
||||
if not next_state:
|
||||
break
|
||||
current_state = next_state
|
||||
|
||||
final_state_reward = current_state.compute_benefit()
|
||||
return final_state_reward
|
||||
|
||||
|
||||
def expand(node):
|
||||
"""
|
||||
Enter a node, expand a new node on the node, use the random method to execute the Action,
|
||||
and return the new node. Note that it is necessary to ensure that the newly
|
||||
added nodes are different from other node Action
|
||||
"""
|
||||
|
||||
new_state = node.get_state().get_next_state_with_random_choice()
|
||||
sub_node = Node()
|
||||
sub_node.set_state(new_state)
|
||||
node.expand_child(sub_node)
|
||||
|
||||
return sub_node
|
||||
|
||||
|
||||
def best_child(node, is_exploration):
|
||||
"""
|
||||
Using the UCB algorithm,
|
||||
select the child node with the highest score after weighing the exploration and exploitation.
|
||||
Note that if it is the prediction stage,
|
||||
the current Q-value score with the highest score is directly selected.
|
||||
"""
|
||||
|
||||
best_score = -sys.maxsize
|
||||
best_sub_node = None
|
||||
|
||||
# travel all sub nodes to find the best one
|
||||
for sub_node in node.get_children():
|
||||
# The children nodes of the node contains the children node whose state is empty,
|
||||
# this kind of node comes from the node that does not meet the conditions.
|
||||
if not sub_node.get_state():
|
||||
continue
|
||||
# ignore exploration for inference
|
||||
if is_exploration:
|
||||
C = 1 / math.sqrt(2.0)
|
||||
else:
|
||||
C = 0.0
|
||||
|
||||
# UCB = quality / times + C * sqrt(2 * ln(total_times) / times)
|
||||
left = sub_node.get_quality_value() / sub_node.get_visit_number()
|
||||
right = 2.0 * math.log(node.get_visit_number()) / sub_node.get_visit_number()
|
||||
score = left + C * math.sqrt(right)
|
||||
# get the maximum score, while filtering nodes that do not meet the space constraints and
|
||||
# nodes that have no revenue
|
||||
if score > best_score \
|
||||
and sub_node.get_state().get_current_storage() <= STORAGE_THRESHOLD \
|
||||
and sub_node.get_state().get_current_benefit() > 0:
|
||||
best_sub_node = sub_node
|
||||
best_score = score
|
||||
|
||||
return best_sub_node
|
||||
|
||||
|
||||
def backpropagate(node, reward):
|
||||
"""
|
||||
In the Backpropagation stage of Monte Carlo tree search,
|
||||
input the node that needs to be expended and the reward of the newly executed Action,
|
||||
feed it back to the expend node and all upstream nodes,
|
||||
and update the corresponding data.
|
||||
"""
|
||||
|
||||
# update util the root node
|
||||
while node is not None:
|
||||
# update the visit number
|
||||
node.update_visit_number()
|
||||
|
||||
# update the quality value
|
||||
node.update_quality_value(reward)
|
||||
|
||||
# change the node to the parent node
|
||||
node = node.parent
|
||||
|
||||
|
||||
def monte_carlo_tree_search(node):
|
||||
"""
|
||||
Implement the Monte Carlo tree search algorithm, pass in a root node,
|
||||
expand new nodes and update data according to the
|
||||
tree structure that has been explored before in a limited time,
|
||||
and then return as long as the child node with the highest exploitation.
|
||||
|
||||
When making predictions,
|
||||
you only need to select the node with the largest exploitation according to the Q value,
|
||||
and find the next optimal node.
|
||||
"""
|
||||
|
||||
computation_budget = len(AVAILABLE_CHOICES) * 3
|
||||
|
||||
# run as much as possible under the computation budget
|
||||
for i in range(computation_budget):
|
||||
# 1. find the best node to expand
|
||||
expand_node = tree_policy(node)
|
||||
if not expand_node:
|
||||
# when it is None, it means that all nodes are added but no nodes meet the space limit
|
||||
break
|
||||
# 2. random get next action and get reward
|
||||
reward = default_policy(expand_node)
|
||||
|
||||
# 3. update all passing nodes with reward
|
||||
backpropagate(expand_node, reward)
|
||||
|
||||
# get the best next node
|
||||
best_next_node = best_child(node, False)
|
||||
|
||||
return best_next_node
|
||||
|
||||
|
||||
def MCTS(workload_info, atomic_choices, available_choices, storage_threshold, max_index_num):
|
||||
global ATOMIC_CHOICES, STORAGE_THRESHOLD, WORKLOAD_INFO, AVAILABLE_CHOICES, MAX_INDEX_NUM
|
||||
WORKLOAD_INFO = workload_info
|
||||
AVAILABLE_CHOICES = available_choices
|
||||
ATOMIC_CHOICES = atomic_choices
|
||||
STORAGE_THRESHOLD = storage_threshold
|
||||
MAX_INDEX_NUM = max_index_num if max_index_num else len(available_choices)
|
||||
|
||||
# create the initialized state and initialized node
|
||||
init_state = State()
|
||||
choices = copy.copy(available_choices)
|
||||
init_state.set_available_choices(choices)
|
||||
init_node = Node()
|
||||
init_node.set_state(init_state)
|
||||
current_node = init_node
|
||||
|
||||
opt_config = []
|
||||
# set the rounds to play
|
||||
for i in range(len(AVAILABLE_CHOICES)):
|
||||
if current_node:
|
||||
current_node = monte_carlo_tree_search(current_node)
|
||||
if current_node:
|
||||
opt_config = current_node.state.accumulation_choices
|
||||
else:
|
||||
break
|
||||
return opt_config
|
||||
|
|
@ -539,13 +539,14 @@ 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:
|
||||
if need_init:# Cold start training
|
||||
epoch_start = 0
|
||||
self.model = self._build_model(epsilon)
|
||||
else:
|
||||
else:# Incremental training
|
||||
epoch_start = int(self.model_info.last_epoch)
|
||||
ratio_error = ratio_error_loss_wrapper(epsilon)
|
||||
ratio_acc_2 = ratio_error_acc_wrapper(epsilon, 2)
|
||||
|
|
@ -556,12 +557,16 @@ 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,6 +27,7 @@ from . import AbstractModel
|
|||
|
||||
|
||||
class TemplateModel(AbstractModel):
|
||||
# Initialize algorithm parameters
|
||||
def __init__(self, params):
|
||||
super().__init__(params)
|
||||
self.bias = 1e-5
|
||||
|
|
|
|||
|
|
@ -173,11 +173,16 @@ 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'])
|
||||
|
|
@ -200,6 +205,7 @@ 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
|
||||
|
||||
|
|
@ -207,6 +213,13 @@ 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.')
|
||||
|
||||
|
|
@ -222,12 +235,21 @@ 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.
|
||||
|
||||
|
|
@ -237,6 +259,7 @@ 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,6 +27,7 @@ from collections.abc import Iterable
|
|||
from collections import defaultdict
|
||||
|
||||
import index_advisor_workload as iaw
|
||||
import mcts
|
||||
|
||||
|
||||
def hash_any(obj):
|
||||
|
|
@ -227,6 +228,32 @@ 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,7 +90,10 @@ static void DropExtensionInListIsSupported(List* objname)
|
|||
}
|
||||
}
|
||||
|
||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXTENSION is not yet supported.")));
|
||||
/* 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.")));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1175,7 +1175,7 @@ void CreateExtension(CreateExtensionStmt* stmt)
|
|||
FEATURE_NOT_PUBLIC_ERROR("EXTENSION is not yet supported.");
|
||||
}
|
||||
|
||||
if (pg_strcasecmp(stmt->extname, "b_sql_plugin") == 0 && !DB_IS_CMPT(B_FORMAT)) {
|
||||
if (pg_strcasecmp(stmt->extname, "dolphin") == 0 && !DB_IS_CMPT(B_FORMAT)) {
|
||||
ereport(ERROR,
|
||||
(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, "b_sql_plugin") == 0) {
|
||||
u_sess->attr.attr_sql.b_sql_plugin = true;
|
||||
if (pg_strcasecmp(stmt->extname, "dolphin") == 0) {
|
||||
u_sess->attr.attr_sql.dolphin = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -23175,7 +23175,7 @@ static void checkValidationForExchangeTable(Relation partTableRel, Relation ordT
|
|||
int2 bucketId = InvalidBktId;
|
||||
|
||||
// get right partition oid for the tuple
|
||||
targetPartOid = heapTupleGetPartitionId(partTableRel, (HeapTuple) tuple);
|
||||
targetPartOid = heapTupleGetPartitionId(partTableRel, (HeapTuple)tuple, true);
|
||||
|
||||
searchFakeReationForPartitionOid(
|
||||
partRelHTAB, CurrentMemoryContext, partTableRel, targetPartOid, partRel, part, RowExclusiveLock);
|
||||
|
|
@ -24797,7 +24797,8 @@ static Oid AddTemporaryPartitionForAlterPartitions(const AlterTableCmd* cmd, Rel
|
|||
destPartOid = AddTemporaryHashPartitionForAlterPartitions(cmd, partTableRel, partSeq, renameTargetPart);
|
||||
break;
|
||||
}
|
||||
case PART_TYPE_RANGE: {
|
||||
case PART_TYPE_RANGE:
|
||||
case PART_TYPE_INTERVAL: {
|
||||
destPartOid = AddTemporaryRangePartitionForAlterPartitions(cmd, partTableRel, partSeq, renameTargetPart);
|
||||
break;
|
||||
}
|
||||
|
|
@ -25098,11 +25099,11 @@ static void readTuplesAndInsertInternal(Relation tempTableRel, Relation partTabl
|
|||
|
||||
/* tableam_tops_copy_tuple is not ready so we add UStore hack path */
|
||||
copyTuple = tableam_tops_copy_tuple(tuple);
|
||||
targetPartOid = heapTupleGetPartitionId(partTableRel, (void *)tuple);
|
||||
targetPartOid = heapTupleGetPartitionId(partTableRel, (void *)tuple, true);
|
||||
searchFakeReationForPartitionOid(
|
||||
partRelHTAB, CurrentMemoryContext, partTableRel, targetPartOid, partRel, part, RowExclusiveLock);
|
||||
if (RelationIsSubPartitioned(partTableRel)) {
|
||||
targetPartOid = heapTupleGetPartitionId(partRel, (void *)tuple);
|
||||
targetPartOid = heapTupleGetPartitionId(partRel, (void *)tuple, true);
|
||||
searchFakeReationForPartitionOid(partRelHTAB, CurrentMemoryContext, partRel, targetPartOid, subPartRel,
|
||||
subPart, RowExclusiveLock);
|
||||
partRel = subPartRel;
|
||||
|
|
|
|||
|
|
@ -5911,6 +5911,7 @@ 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),
|
||||
|
|
@ -5921,7 +5922,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");
|
||||
|
|
@ -6052,6 +6053,7 @@ 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.")));
|
||||
}
|
||||
|
|
@ -6059,6 +6061,7 @@ 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);
|
||||
}
|
||||
|
|
@ -6068,6 +6071,7 @@ 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)) {
|
||||
|
|
|
|||
|
|
@ -1181,6 +1181,17 @@ 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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
if (candidate_num == 0 && !t_thrd.pagewriter_cxt.shutdown_requested) {
|
||||
/* wakeup sub thread scan the buffer pool, init the candidate list */
|
||||
wakeup_sub_thread();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -825,13 +825,10 @@ void client_read_ended(void)
|
|||
#define INIT_PLUGIN_OBJECT "init_plugin_object"
|
||||
void InitBSqlPluginHookIfNeeded()
|
||||
{
|
||||
const char* b_sql_plugin = "b_sql_plugin";
|
||||
const char* dolphin = "dolphin";
|
||||
CFunInfo tmpCF;
|
||||
if (!CheckIfExtensionExists(b_sql_plugin)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tmpCF = load_external_function(b_sql_plugin, INIT_PLUGIN_OBJECT, false, false);
|
||||
tmpCF = load_external_function(dolphin, INIT_PLUGIN_OBJECT, false, false);
|
||||
if (tmpCF.user_fn != NULL) {
|
||||
((void* (*)(void))(tmpCF.user_fn))();
|
||||
}
|
||||
|
|
@ -865,9 +862,11 @@ List* pg_parse_query(const char* query_string, List** query_string_locationlist)
|
|||
|
||||
List* (*parser_hook)(const char*, List**) = raw_parser;
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
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];
|
||||
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];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
raw_parsetree_list = parser_hook(query_string, query_string_locationlist);
|
||||
|
|
@ -6115,6 +6114,9 @@ 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,
|
||||
|
|
@ -7572,7 +7574,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)) {
|
||||
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.dolphin) {
|
||||
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)) {
|
||||
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.dolphin) {
|
||||
InitBSqlPluginHookIfNeeded();
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
*
|
||||
|
|
@ -21,6 +25,22 @@
|
|||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*/
|
||||
/*This code is a part of a C language function library, including
|
||||
some functions for dealing with time and data type conversion.
|
||||
The time_diff function is used to calculate the difference between
|
||||
the times represented by two timespec structures.
|
||||
The interval_to_sec and interval_to_msec functions convert a
|
||||
numerical value representing a time interval into seconds and milliseconds.
|
||||
The float8_get_Datum function converts a value of float8 type into
|
||||
a corresponding datum value according to the input data type.
|
||||
The Datum_get_float8 function converts a datum value into a
|
||||
corresponding float8 value according to the input data type.
|
||||
The Datum_get_int function converts a datum value into a
|
||||
corresponding value of type int32 according to the input data type.
|
||||
The string_to_Datum function converts a string into a datum value of the specified data type.
|
||||
The check_hyper_bounds function is used to check the boundary condition of the superparameter.
|
||||
These functions can be used in data type conversion,
|
||||
time difference calculation and boundary check of superparameters in database systems.*/
|
||||
|
||||
#include "db4ai/db4ai_common.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* execAmi.cpp
|
||||
|
|
@ -73,6 +77,39 @@ static bool index_supports_backward_scan(Oid indexid);
|
|||
* Note that if the plan node has parameters that have changed value,
|
||||
* the output might be different from last time.
|
||||
*/
|
||||
/*The ExecReScanByType' function performs
|
||||
different rescan operations according to the type of plan node, such as:
|
||||
-For the ResultState, perform the result rescan operation.
|
||||
-For the modified table node (ModifyTableState) and the distributed
|
||||
DistInsertSelectState, perform the modified table rescan operation.
|
||||
-For the merge AppendState, perform the merge append rescan operation.
|
||||
-For a RecursiveUnionState, perform a recursive union rescan operation.
|
||||
-For the node with the initial operation, perform the rescan operation with the initial operation.
|
||||
-For BitmapAndState and BitmapOrState, perform bitoperation rescan operation.
|
||||
-For a sequential scan node (SeqScanState), perform a sequential scan rescan operation.
|
||||
-For index scan node (IndexScanState), index scan only node (indexscan state), bit index scan node
|
||||
(BitmapIndexScanState) and bit heap scan node (BitmapHeapScanState), perform corresponding scan rescan operations.
|
||||
-For tid scan nodes, SubqueryScanState, FunctionScanState, ValuesScanState, CteScanState, Worksheet
|
||||
scanning node (workbench scanning state), external scanning node (foreign scanning state), extensible
|
||||
planning node (extensible planning state), etc., and perform corresponding rescan operations according to specific node types.
|
||||
-Rescan the partition PartIteratorState.
|
||||
-In PGXC environment, rescan the remote query for the RemoteQueryState.
|
||||
-Rescan the nested loop connection node.
|
||||
-Rescan the MergeJoinState.
|
||||
-rescan the hash connection node (HashJoinState).
|
||||
-Rescan the materialized state.
|
||||
-Rescan the SortState.
|
||||
-Rescan the grouped nodes.
|
||||
-rescan the aggregation node (AggState) and the window aggregation node (WindowAggState).
|
||||
-rescan the UniqueState.
|
||||
-rescan the HashState.
|
||||
-Rescan the set operation node (SetOpState).
|
||||
-Rescan the lock result of the lock node that locks the row node.
|
||||
-Rescan the restriction result of the restriction node.
|
||||
Rescan the vectorization conversion result of the vectorization conversion node.
|
||||
|
||||
The implementation of these rescan operations varies according to the type of specific planning nodes,
|
||||
and they will re-read the data and generate new output results for use in the next execution.*/
|
||||
void ExecReScanByType(PlanState* node)
|
||||
{
|
||||
/* If collecting timing stats, update them */
|
||||
|
|
@ -260,6 +297,18 @@ void ExecReScanByType(PlanState* node)
|
|||
}
|
||||
}
|
||||
|
||||
/*This code is the source code of an executor access method, which is used to perform rescan operation in the executor.
|
||||
|
||||
Rescan refers to rescan the executed plan node in order to regenerate the output results. In the executor,
|
||||
the plan node refers to each step in the query plan, such as scanning tables, filtering data and aggregating data.
|
||||
The function ExecReScan in the code is the main function to
|
||||
perform rescan in the actuator. It takes a plan node as a parameter and does the following:
|
||||
|
||||
1. If performance statistics are being collected, update the statistics.
|
||||
2. If the parameters of the plan node change, update the parameter information.
|
||||
3. Close any SRF(Server-Side Function) in the plan node.
|
||||
4. Stop rescanning if Stub execution is needed.
|
||||
5. Call the ExecReScanByType' function to perform the corresponding rescan operation according to the type of the plan node.*/
|
||||
/*
|
||||
* ExecReScan
|
||||
* Reset a plan node so that its output can be re-scanned.
|
||||
|
|
@ -343,6 +392,40 @@ void ExecReScan(PlanState* node)
|
|||
node->chgParam = NULL;
|
||||
}
|
||||
}
|
||||
/*This code defines the functions and auxiliary functions related to the execution plan.
|
||||
|
||||
The function ExecMarkPos' is used to save the scanning position and mark the status of the execution
|
||||
plan as saved. According to the passed-in PlanState object, the function will call the corresponding
|
||||
function to save the scanning position according to its type.
|
||||
|
||||
The function ExecRestrPos' is used to restore the
|
||||
scanning position and perform the corresponding
|
||||
restoration operation. According to the passed-in PlanState object, this function will call the
|
||||
corresponding function to restore the scanning position according to its type.
|
||||
|
||||
The function ExecSupportsMarkRestore' is used
|
||||
to check whether the execution plan supports marking
|
||||
and recovery operations. According to the passed-in Path object,
|
||||
the function will return the corresponding result according to its type.
|
||||
|
||||
The function ExecSupportsBackwardScan is used to check
|
||||
whether the execution plan supports reverse scanning.
|
||||
According to the passed-in Plan object, the function will return
|
||||
the corresponding result according to its type.
|
||||
|
||||
The function `target _ list _ supports _ backward _ scan` is used to check whether
|
||||
the target list supports reverse scanning.
|
||||
It determines whether to support reverse scanning by checking
|
||||
whether each expression in the target list returns a collection type.
|
||||
|
||||
The function `index _ supports _ backward _ scan` is used to check
|
||||
whether the index supports reverse scanning.
|
||||
It determines whether to support reverse scanning by checking
|
||||
whether the access method of the index supports reverse scanning.
|
||||
|
||||
The function ExecMaterializesOutput' is used to check whether the execution plan automatically
|
||||
materialization the output. Depending on the type of execution
|
||||
plan passed in, this function will return the corresponding results.*/
|
||||
|
||||
/*
|
||||
* ExecMarkPos
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* execClusterResize.cpp
|
||||
|
|
@ -69,6 +73,31 @@ static inline bool redis_offset_retrive_function(const char* funcname, Oid retty
|
|||
((nargs) == 4 && (rettype) == TIDOID && (argstype)[0] == TEXTOID && (argstype)[1] == NAMEOID && \
|
||||
(argstype)[2] == INT4OID && (argstype)[3] == INT4OID))
|
||||
|
||||
/*The function of this code is to implement some functions related to Redis. Specifically includes the following aspects:
|
||||
1. Some macros are defined to specify some constants.
|
||||
2. Some inline functions are declared to judge whether a function is a corresponding Redis function.
|
||||
3. Some functions are implemented, including recording deleted tuples, judging whether the relationship is in cluster
|
||||
redistribution, checking whether the table is a deletion operation table, and judging whether the process is in the process of cluster redistribution.
|
||||
The function RecordDeletedTuple is used to record the tupleid of a given tuple into the `pg _ delete _ delta` table. As follows:
|
||||
|
||||
-parameters:
|
||||
-`Relid`: OID of the target relationship of the update/delete operation.
|
||||
-`bucket id`: ID of the bucket where the target tuple is located.
|
||||
-`tupleid': the tupleid to be recorded.
|
||||
-`deldelta_rel: the corresponding `pg _ delete _ delta` relationship.
|
||||
|
||||
The function RelationInClusterResizing' is used to determine whether the relationship is in the operation of cluster resizing.
|
||||
|
||||
The function `relationinclusteresinggreadonly` is used to determine whether the relationship is in a read-only cluster resizing operation.
|
||||
|
||||
The function `relationinclusteresizingendachup' is used to determine whether the relationship is in an operation (write error) before the end of cluster resizing.
|
||||
|
||||
The function CheckRangeVarInRedistribution' is used to check whether the relationship is in redistribution through the relationship variable.
|
||||
|
||||
The function RelationIsDeleteDeltaTable is used to determine whether the given table name is a delete_delta table.
|
||||
|
||||
The function `clusterSizingProgress' is used to determine whether the cluster resizing process is in progress.*/
|
||||
|
||||
static inline bool redis_tupleid_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs)
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* execCurrent.c
|
||||
|
|
@ -47,6 +51,63 @@ static ScanState* search_plan_tree(PlanState *node, Oid table_oid);
|
|||
* legal situation in inheritance cases). Raises error if cursor is not a
|
||||
* valid updatable scan of the specified table.
|
||||
*/
|
||||
/*The segment code defines a function named execCurrentOf, which is used to execute the CURRENT OF expression in SQL query.
|
||||
The CURRENT OF expression is a part of SQL/PSM (Persistent Storage Module),
|
||||
which is used to implement sensitive operations on a cursor.
|
||||
The function execCurrentOf receives five parameters:
|
||||
|
||||
1. cexpr: a pointer to the CurrentOfExpr structure, which contains the information of the CURRENT OF expression.
|
||||
2. econtext: a pointer to ExprContext structure, which contains the context information of expression execution.
|
||||
3. Relationship: A pointer to the relationship structure, which represents a relationship (i.e. a table) in the database.
|
||||
4. current_tid: a pointer to the ItemPointer structure, which represents the current transaction ID.
|
||||
5. partitionOfCursor_tid: A pointer to the RelationPtr structure, which represents the partition of the cursor.
|
||||
|
||||
The function first obtains the name of the cursor according to cexpr, and
|
||||
then finds the corresponding Portal according to the name.
|
||||
If a valid Portal cannot be found, an error is reported. Then, the function checks
|
||||
the query description (query_desc) corresponding to the Portal.
|
||||
An error is also reported if the query description does not exist
|
||||
or the status of the query description is invalid.
|
||||
|
||||
Then, the function decides which strategy to execute according to the row marks in the query description.
|
||||
If there is a line mark, use FOR UPDATE/SHARE; Otherwise, use a FOR-UPDATE method.
|
||||
|
||||
It defines a variable named `erm` with an initial value of NULL. Then it traverses ` query _ desc-> estate-> es _ row marks`,
|
||||
which is a list of all the rowmarks in the cursor query. During traversal, it
|
||||
checks whether each row tag needs a row share lock, and if not, it ignores the row tag.
|
||||
|
||||
For the row tag that needs a row sharing lock, the code checks whether the table associated with the row tag is
|
||||
the target table (that is, the OID returned by the RelationGetRelid' of `thiserm-> relation' is equal to table_oid').
|
||||
If it is, and there is already a row tag associated with the target table, it will report an error because
|
||||
the cursor cannot have more than one FOR UPDATE/SHARE reference to the same table.
|
||||
|
||||
After the traversal is completed, if the row tag associated with the target table is not found, it will report an error,
|
||||
because the cursor must have a FOR UPDATE/SHARE reference to the target table.
|
||||
|
||||
Next, the code checks whether the cursor currently has a result row.
|
||||
If not, it will report an error, because in the SQL specification, this is wrong.
|
||||
|
||||
Finally, if there is a valid TID (transaction ID) of the current scan, it will set' current_tid' and check whether
|
||||
the relationship is partitioned. If the relationship is partitioned, it will set `partition of cursor _ tid' to NULL.
|
||||
Then return true, indicating that the related TID has been found. If a valid TID is not found, it will return false,
|
||||
indicating that this table has not generated the current row of the cursor, and other inherited
|
||||
sub-tables may have generated the current row of the cursor.
|
||||
|
||||
Some variables are defined, including a pointer named scanstate', a boolean variable` lisnull',
|
||||
an Oid variable` tuple_tableoid' and an ItemPointer variable` tuple_tid'.
|
||||
|
||||
Then, it searches the search_plan_tree by calling the `search _ plan _ tree` function to find the scan node
|
||||
associated with the given table OID. If the scan node is not found,
|
||||
or the scan node is overwritten by the aggregation operation, it will report an error.
|
||||
|
||||
Next, the code checks whether the cursor currently has a result row. If not,
|
||||
it will report an error, because in the SQL specification, this is wrong.
|
||||
|
||||
Then, if the current scan tuple in the scan state is NULL, it will return false.
|
||||
|
||||
Finally, the code uses the slot_getattr function to get the table OID and transaction ID of
|
||||
the tuple and check whether they are valid. If the relationship is partitioned, it will also check
|
||||
whether the table OID is the same as the parent table OID of the partition.*/
|
||||
bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relation, ItemPointer current_tid,
|
||||
RelationPtr partitionOfCursor_tid)
|
||||
{
|
||||
|
|
@ -208,6 +269,22 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
|
|||
*
|
||||
* Fetch the string value of a param, verifying it is of type REFCURSOR.
|
||||
*/
|
||||
/*This code defines a function `fetch _ cursor _ param _ value`, which is used to get the specified
|
||||
parameter value, especially when the parameter is of the reference cursor type.
|
||||
|
||||
The input parameters of the function include a pointer to an ExprContext structure and an integer paramId.
|
||||
The ExprContext' structure contains the execution context of the expression, which may contain
|
||||
some parameter information. ParamId' is the ID of the parameter to get.
|
||||
|
||||
The function first checks whether there is parameter information and whether the parameter ID is within the valid range.
|
||||
Then, it locates the specific parameter and checks its type. If the parameter type is dynamic (that is, its type identifier is invalid)
|
||||
and there is a parameter obtaining function, it will call this function to obtain the value of the parameter.
|
||||
|
||||
If the parameter type is valid and not null, the function will check further. If the parameter type is not a reference refcursor,
|
||||
it will report an error because the function only deals with this type. If the parameter type is a reference cursor,
|
||||
the function will convert its value to a C string and return this string.
|
||||
|
||||
If the value of the parameter is not found during the execution of the function, it will report an error and return NULL.*/
|
||||
static char *fetch_cursor_param_value(ExprContext *econtext, int paramId)
|
||||
{
|
||||
ParamListInfo paramInfo = econtext->ecxt_param_list_info;
|
||||
|
|
@ -243,6 +320,40 @@ static char *fetch_cursor_param_value(ExprContext *econtext, int paramId)
|
|||
* Search through a PlanState tree for a scan node on the specified table.
|
||||
* Return NULL if not found or multiple candidates.
|
||||
*/
|
||||
/*t searches the PlanState tree for scan nodes on the specified table.
|
||||
In PostgreSQL, the PlanState tree is a data structure representing the query execution plan.
|
||||
The function `search _ plan _ tree` receives two parameters: a pointer` node of PlanState and
|
||||
a ` table _ Oid` of oid type. The function starts searching from the given node
|
||||
and finds the scanning node that matches the OID of the specified table.
|
||||
In the code, use the switch statement to judge the type of the node. For each scan node type that can be processed
|
||||
(for example, sequential scan, index scan, index only scan, bitmap heap scan and TID scan), the code checks whether the ID
|
||||
of the current relationship (that is, the scanned table) matches the given table OID.
|
||||
If there is a match, the function returns a pointer to the scan node.
|
||||
|
||||
For the `t _ remotequerystate` node, the code will return the scanning status of the node.
|
||||
For the `t _ extensibleplanstate' node, the code will check whether the ID of the current relationship matches
|
||||
the given table OID, and return the scanning status at the time of matching.
|
||||
|
||||
For the `t _ appendstate` node, the code will iterate through all the attached plans and recursively call the `search _ plan _ tree` function.
|
||||
If multiple matching scan nodes are found in the attached schedule, the function will return NULL.
|
||||
|
||||
-`T_AppendState' and `t _ mergeappendState': Both node types represent a method of combining multiple subquery results into one result.
|
||||
The code will traverse each subquery and recursively call the `search _ plan _ tree` function for each subquery.
|
||||
If a matching scanning node is found, and no matching node has been found before, the matching node is assigned to result.
|
||||
If multiple matching nodes are found, the function will return NULL.
|
||||
-`t _ resultstate`, `t _ limitstate`, `t _ partiteratorstate`, and `t _ materialstate` (only exists in PGXC): These node types can be
|
||||
traversed directly because they always return the current line of their input.
|
||||
-`T_SubqueryScanState: This node type represents the scanning of the subquery,
|
||||
and the code will return the scanning node in the subquery.
|
||||
-Default: If the node is not of any of the above types,
|
||||
the code will assume that it cannot traverse through the node, so it will return NULL.
|
||||
|
||||
The main purpose of this function is to find the scanning node corresponding to a specific table in the query execution plan.
|
||||
This is very useful for understanding and tracking query execution, especially
|
||||
when it is necessary to understand and debug query performance problems.
|
||||
|
||||
Generally speaking, this function is used to find the scan node corresponding to the specified table in the query execution plan.*/
|
||||
|
||||
#ifdef PGXC
|
||||
ScanState* search_plan_tree(PlanState* node, Oid table_oid)
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* execGrouping.cpp
|
||||
|
|
@ -48,6 +52,25 @@ static int TupleHashTableMatch(const void* key1, const void* key2, Size keysize)
|
|||
*
|
||||
* NB: evalContext is reset each time!
|
||||
*/
|
||||
/*The main purpose of this code is to compare whether two Tuple are equal.
|
||||
In database, tuple is a basic data structure, which is used to store a series of related values.
|
||||
The function execTuplesMatch receives two TupleTableSlot pointers (slot1 and slot2),
|
||||
which point to the tuple to be compared, as well as the number of columns (numCols),
|
||||
the matching column index (matchColIdx), the equation functions (eqfunctions) and an evalContext.
|
||||
|
||||
It first switches to a temporary memory context (evalContext), and then loops through each column,
|
||||
starting with the last column (the least important sort key). This is because the last column
|
||||
is most likely to be different when processing sorted input.
|
||||
|
||||
For each column, it gets the property values in two tuples and checks whether they are empty.
|
||||
If one is empty and the other is not, they are not equal, and the function sets the result to false
|
||||
and jumps out of the loop. If both of them are empty, they are regarded as equal and continue the next cycle.
|
||||
|
||||
If both attributes are not empty, then a specific type of equality function will be used to compare whether
|
||||
they are equal. If not, the function sets the result to false and jumps out of the loop.
|
||||
|
||||
Finally, the function switches back to the old memory context and returns the result. If all columns match,
|
||||
the function will return true, otherwise it will return false.*/
|
||||
bool execTuplesMatch(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols, AttrNumber* matchColIdx,
|
||||
FmgrInfo* eqfunctions, MemoryContext evalContext)
|
||||
{
|
||||
|
|
@ -166,6 +189,26 @@ bool execTuplesUnequal(TupleTableSlot* slot1, TupleTableSlot* slot2, int numCols
|
|||
*
|
||||
* The result is a palloc'd array.
|
||||
*/
|
||||
/*The main purpose of this code is to generate an array of function information for
|
||||
each pair of equality operators for subsequent tuple comparison.
|
||||
|
||||
The function execTuplesMatchPrepare takes the number of columns (numCols) and
|
||||
the array of equality eqOperators (`eq operators`) as parameters.
|
||||
|
||||
First, it uses `p palloc to allocate memory for the function information array,
|
||||
and the length of the array is the number of columns.
|
||||
|
||||
Then, it enters a loop, and each iteration in the loop corresponds to a column.
|
||||
For each column, it gets the equality operator (` eq _ opr`) and the corresponding
|
||||
function (` eq _ function`). This is done by calling the get_opcode function.
|
||||
|
||||
Next, it uses the fmgr_info function to fill the corresponding position
|
||||
of the function information array.
|
||||
|
||||
Finally, the function returns the generated function information array.
|
||||
|
||||
This function is usually called before performing tuple matching to
|
||||
prepare the required function information.*/
|
||||
FmgrInfo* execTuplesMatchPrepare(int numCols, Oid* eqOperators)
|
||||
{
|
||||
FmgrInfo* eqFunctions = (FmgrInfo*)palloc(numCols * sizeof(FmgrInfo));
|
||||
|
|
@ -192,6 +235,26 @@ FmgrInfo* execTuplesMatchPrepare(int numCols, Oid* eqOperators)
|
|||
*
|
||||
* Note: we expect that the given operators are not cross-type comparisons.
|
||||
*/
|
||||
/*The purpose of this code is to prepare equality function and hash function for tuple hash table.
|
||||
|
||||
The' executtupleshahprep' function receives four parameters: number of columns ('numCols'),
|
||||
equality operator array ('eqOperators'), equality function array ('eqFunctions') and hash function array ('hashFunctions').
|
||||
|
||||
First, the function allocates memory for the array of equality functions and hash functions.
|
||||
|
||||
Then, it enters a loop, and each iteration in the loop corresponds to a column. For each column,
|
||||
it gets the equality operator (` eq _ opr`) and the corresponding function (` eq _ function`). This is done by calling the get_opcode function.
|
||||
|
||||
Next, it tries to get the hash function of the equality operator. If the hash function cannot be found,
|
||||
it will report an error and call the `ereport' function, which will send the error information to the error handling system of PostgreSQL.
|
||||
|
||||
Then, it asserts that the left and right hash functions are the same, which
|
||||
means that it does not support cross-type cases.
|
||||
|
||||
Finally, it uses the fmgr_info function to fill the corresponding positions
|
||||
of the array of equality functions and hash functions.
|
||||
|
||||
This function is usually called before performing tuple hashing to prepare the required function information.*/
|
||||
void execTuplesHashPrepare(int numCols, Oid* eqOperators, FmgrInfo** eqFunctions, FmgrInfo** hashFunctions)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -248,6 +311,29 @@ void execTuplesHashPrepare(int numCols, Oid* eqOperators, FmgrInfo** eqFunctions
|
|||
* Note that keyColIdx, eqfunctions, and hashfunctions must be allocated in
|
||||
* storage that will live as long as the hashtable does.
|
||||
*/
|
||||
/*The main purpose of this code is to create a TupleHashTable, which is a data structure for storing tuples,
|
||||
which is the basic data structure for storing a series of related values in the database.
|
||||
|
||||
The function' BuildTupleHashTable' receives a series of parameters, including the number of columns ('numCols'),
|
||||
key column index ('keyColIdx'), equation function ('eqfunctions'), hash function ('hashfunctions'), number of buckets ('nbuckets'), entrysize ('entrysize').
|
||||
|
||||
First, the function checks whether the number of buckets and the entry size are valid. Then,
|
||||
it limits the request for the initial table size according to the working memory.
|
||||
|
||||
Then, it allocates memory in the table context to store TupleHashTableData.
|
||||
|
||||
Then, it sets various fields, including column number, key column index, hash function,
|
||||
equality function, table context, temporary context, entry size, etc.
|
||||
|
||||
Then, it clears the memory of the hash_ctl structure and sets its various fields, including key size,
|
||||
entry size, hash function, matching function and hash context.
|
||||
|
||||
Finally, it creates a hashtable using the hash_create function and stores it in the hashtab' field of `hashtable'.
|
||||
|
||||
Function returns the created ` hashtable'.
|
||||
|
||||
This function is usually called when creating a tuple hash when executing a database query,
|
||||
and is used to prepare the required data structure.*/
|
||||
TupleHashTable BuildTupleHashTable(int numCols, AttrNumber* keyColIdx, FmgrInfo* eqfunctions, FmgrInfo* hashfunctions,
|
||||
long nbuckets, Size entrysize, MemoryContext tablecxt, MemoryContext tempcxt, int workMem)
|
||||
{
|
||||
|
|
@ -309,6 +395,21 @@ TupleHashTable BuildTupleHashTable(int numCols, AttrNumber* keyColIdx, FmgrInfo*
|
|||
* hash table if it is new
|
||||
*
|
||||
*/
|
||||
/*The main purpose of this code is to find the TupleTableSlot in the TupleHashTable and insert it as needed.
|
||||
TupleHashTable is a data structure used to store tuples, which are the basic data structures used to store a series of related values in the database.
|
||||
|
||||
The function LookupTupleHashEntry receives four parameters: a TupleHashTable(`hashtable), a TupleTableSlot pointer (`slot`),
|
||||
a Boolean pointer (isnew) and a Boolean value (isinserthashtbl).
|
||||
|
||||
The function first checks whether it is the first time to pass, and if it is, it will clone
|
||||
the input time slot to make the table time slot.
|
||||
|
||||
Then, the function switches to the temporary context, sets the hash and the data needed
|
||||
by the matching function, and saves the current tuple hash table.
|
||||
|
||||
Next, it searches the hash table. If' isinserthashtbl' is true, it will search the hash table and return the found entry
|
||||
if it is found; If it is not found and' isnew' is not NULL, set' isnew' to true, indicating a new entry. If' isinserthashtbl' is false,
|
||||
it will only search the hash table, and if it is found, it will return the found entry; If it is not found, it will create a new table.*/
|
||||
TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot* slot, bool* isnew, bool isinserthashtbl)
|
||||
{
|
||||
TupleHashEntry entry;
|
||||
|
|
@ -510,6 +611,21 @@ static uint32 TupleHashTableHash(const void* key, Size keysize)
|
|||
* Also, the caller must select an appropriate memory context for running
|
||||
* the compare functions. (dynahash.c doesn't change CurrentMemoryContext.)
|
||||
*/
|
||||
/*This code is used to process a part of tuple hash table, which is a data
|
||||
structure used to store and retrieve tuple data in PostgreSQL.
|
||||
|
||||
The function TupleHashTableMatch' is a comparison function, which is used to compare
|
||||
whether two tuples are equal. This function is designed to be used with dynahash.c library,
|
||||
which is a general hash table library and can be used to store and retrieve data.
|
||||
|
||||
The function receives three parameters: key1, key2 and keysize. Key1' and' key2' are pointers
|
||||
to two tuples to be compared, and' keysize' is the size of tuples.
|
||||
|
||||
Within the function, firstly, ` key1' and ` key2' are converted into tuples, and then the tuples are stored
|
||||
in the table slots and input slots of the hash table by using the ` ExecStoreMinimalTuple' function.
|
||||
|
||||
Finally, compare whether two tuples are equal by using the execTuplesMatch function.
|
||||
If two tuples are equal, the function returns 0, otherwise it returns 1.*/
|
||||
static int TupleHashTableMatch(const void* key1, const void* key2, Size keysize)
|
||||
{
|
||||
MinimalTuple tuple1 = ((const TupleHashEntryData*)key1)->firstTuple;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* execJunk.cpp
|
||||
|
|
@ -60,6 +64,30 @@
|
|||
* of whether to include room for an OID or not.
|
||||
* An optional resultSlot can be passed as well.
|
||||
*/
|
||||
/*This function is used to initialize a JunkFilter structure,
|
||||
which is mainly used to deal with' junk' data in SQL query results. ,
|
||||
Function definition: JunkFilter * executinitjunkfilter
|
||||
(list * targetlist, boolhasoid, tupletableslot * slot, tablemtype tam)
|
||||
is a function whose return value is a pointer of junkfilter type. ",
|
||||
Variable initialization: some variables are initialized inside the
|
||||
function, including a JunkFilter pointer, a TupleDesc cleaning
|
||||
tuple type, an int cleaning length, an AttrNumber pointer array,
|
||||
and a ListCell pointer.
|
||||
Calculate the clean tuple type: use the ExecCleanTypeFromTL
|
||||
function to calculate the clean tuple type based on the target list,
|
||||
whether it has OID, and the type of table access method.
|
||||
Set or create a tuple table slot: if the passed slot is not empty,
|
||||
then use the passed slot; Otherwise, create a new slot.
|
||||
Calculating the mapping between the original tuple and the clean tuple:
|
||||
calculating the mapping between the attributes of the original tuple and
|
||||
the attributes of the clean tuple. This mapping is an array whose length is
|
||||
equal to the number of attributes of the clean tuple. For each attribute of
|
||||
the clean tuple, if the corresponding original tuple attribute is not' garbage',
|
||||
the attribute number is stored in the mapping array.
|
||||
Create and initialize JunkFilter structure: Finally, create a new JunkFilter structure,
|
||||
and store the data (target list, clean tuple type, mapping, result slot) obtained
|
||||
by the above calculation in this structure. Then take the pointer of this
|
||||
structure as the return value of the function.*/
|
||||
JunkFilter* ExecInitJunkFilter(List* targetList, bool hasoid, TupleTableSlot* slot, TableAmType tam)
|
||||
{
|
||||
JunkFilter* junkfilter = NULL;
|
||||
|
|
@ -131,6 +159,26 @@ JunkFilter* ExecInitJunkFilter(List* targetList, bool hasoid, TupleTableSlot* sl
|
|||
* deleted columns. It is assumed that the caller has checked that the
|
||||
* non-deleted columns match up with the non-junk columns of the targetlist.
|
||||
*/
|
||||
/*The segment code defines a function named ExecInitJunkFilterConversion,
|
||||
which is used to initialize a JunkFilter structure, mainly used to deal with' junk' data in SQL query results.
|
||||
|
||||
The function receives three parameters: a targetList, a clean tuple type and a tuple table slot.
|
||||
|
||||
Inside the function, some variables are initialized, including a JunkFilter pointer,
|
||||
a clean length, a clean mapping array, a ListCell pointer and an integer variable.
|
||||
|
||||
Next, the function checks whether the incoming slot is empty.
|
||||
If it is not empty, the incoming slot is used, otherwise, a new slot is created.
|
||||
|
||||
Then, the function calculates the mapping between the original tuple and the clean tuple.
|
||||
This mapping is an array whose length is equal to the number of attributes of the clean tuple.
|
||||
For each attribute of the clean tuple, if the corresponding original tuple attribute is not' garbage',
|
||||
the attribute number is stored in the mapping array. If an attribute of the clean tuple is deleted, a
|
||||
0 will be stored in the corresponding position in the mapping array, indicating that a NULL value is needed in the output tuple.
|
||||
|
||||
Finally, the function creates a new JunkFilter structure, and stores the data (target list, clean tuple type,
|
||||
mapping, result slot) obtained from the above calculation into this structure. Then take the
|
||||
pointer of this structure as the return value of the function.*/
|
||||
JunkFilter* ExecInitJunkFilterConversion(List* targetList, TupleDesc cleanTupType, TupleTableSlot* slot)
|
||||
{
|
||||
JunkFilter* junkfilter = NULL;
|
||||
|
|
@ -208,6 +256,17 @@ AttrNumber ExecFindJunkAttribute(JunkFilter* junkfilter, const char* attrName)
|
|||
* Locate the specified junk attribute in the junk filter's targetlist.
|
||||
* Returns NIL if not found.
|
||||
*/
|
||||
/*This function is called' ExecFindJunkPrimaryKeys', and it receives a parameter named' targetList',
|
||||
which is a pointer to the list type. The main goal of the function is to traverse the' targetlist'
|
||||
and find and return all the' garbage' attributes named' xc_primary_key'.
|
||||
|
||||
A list named' jk_primary_keys' is initialized inside the function to store the found properties that meet the conditions.
|
||||
Then, use the foreach loop to traverse' targetlist'. In each loop, it first gets a pointer to the current element and casts
|
||||
the element to the' TargetEntry' type. Then, check whether the element is a' garbage' attribute and its name is' xc_primary_key'.
|
||||
If the condition is met, then add the expression of this attribute to the' jk_primary_keys' list.
|
||||
|
||||
Finally, the function returns the' jk_primary_keys' list.
|
||||
This list contains all the' junk' attributes named' xc_primary_key' found in' targetlist'.*/
|
||||
List* ExecFindJunkPrimaryKeys(List* targetlist)
|
||||
{
|
||||
List* jk_primary_keys = NIL;
|
||||
|
|
@ -231,6 +290,29 @@ List* ExecFindJunkPrimaryKeys(List* targetlist)
|
|||
* Find a junk attribute given a subplan's targetlist (not necessarily
|
||||
* part of a JunkFilter).
|
||||
*/
|
||||
/*This code has three functions, namely' ExecFindJunkAttributeInTlist','
|
||||
ExecGetJunkAttribute' and' ExecFilterJunk'. The following is an explanation of each function:
|
||||
|
||||
1. `ExecFindUnkattributeinlist': This function receives a targetlist and an attribute name as parameters,
|
||||
and then looks for the matching attribute name in the target list. If a matching attribute is found,
|
||||
and the attribute is marked as' junk' (that is,' resjunk' is true), the number of the attribute ('resno') is returned.
|
||||
If no matching attribute is found, or the attribute is not marked as' junk', an invalid attribute number ('InvalidAttrNumber') is returned.
|
||||
2. `ExecGetJunkatAttribute`: This function receives a tuple table slot, an attribute number (attno)
|
||||
and a pointer to a Boolean value (isNull) as parameters. It uses the `tableam _ tslot _ getattr` function
|
||||
to get the value of the specified attribute number and the isNull flag from the slot. The function also does
|
||||
some assertion checking to ensure that the attribute number passed in is greater than 0 and the slot is not empty.
|
||||
3.'ExecFilterJunk': It is used to filter the "junk" attribute in OpenGauss database.
|
||||
It receives two parameters: a JunkFilter structure pointer and a TupleTableSlot structure pointer.
|
||||
The main work of this function can be roughly divided into the following steps:
|
||||
1. Extract all the values of the old tuple (that is, the input TupleTableSlot) and store them in old_values and old_isnull.
|
||||
2. Get the required information from JunkFilter structure, including clean tuple type, cleanLength and cleanMap.
|
||||
3. Prepare to build a new virtual tuple (namely resultSlot).
|
||||
4. Traverse every element in the clean map, and if the value of the map is 0, set it to NULL;
|
||||
in the corresponding position in the new tuple; Otherwise, the corresponding value is obtained from the old tuple and copied to the new tuple.
|
||||
5. Finally, return the virtual TupleTableSlot that stores the new tuple.
|
||||
In this way, this function realizes the function of transforming from tuple containing "garbage" attribute to a tuple without "garbage" attribute.
|
||||
These functions may be related to database query optimization, especially when dealing with a large number of data,
|
||||
by identifying and filtering out' junk' attributes (that is, unnecessary attributes), the efficiency and performance of the query can be improved.*/
|
||||
AttrNumber ExecFindJunkAttributeInTlist(List* targetlist, const char* attrName)
|
||||
{
|
||||
ListCell* t = NULL;
|
||||
|
|
@ -364,7 +446,25 @@ VectorBatch* BatchExecFilterJunk(_in_ JunkFilter* junkfilter, __inout VectorBatc
|
|||
//
|
||||
return batch;
|
||||
}
|
||||
|
||||
/*"ExecSetjunkFilteDescriptor function":
|
||||
"Function": "This function is mainly used to set the tupleDescriptor of the resultSlot
|
||||
of JunkFilter. It receives two parameters: a JunkFilter structure pointer and a TupleDesc structure.
|
||||
It copies the attribute type ID in the input tuple descriptor to the corresponding attribute in
|
||||
the tuple descriptor of the result slot by traversing the cleanMap. " ,
|
||||
"parameters":
|
||||
"junkfilter": "A pointer to JunkFilter structure, which contains information needed for filtering operation,
|
||||
such as cleaning tuple type and cleaning mapping." ,
|
||||
"tupdesc": "A pointer to a TupleDesc structure that describes the properties of a tuple."
|
||||
"BatchCheckNodeIdentifier function":
|
||||
"Function": "This function is mainly used to check whether the value of the' xc_node_id'
|
||||
column in a VectorBatch is the same as the identifier of the current node. If not, the function
|
||||
will report an error. The function first checks whether' xc_node_id' is a valid attribute number,
|
||||
and then obtains the values of the' xc_node_id' column, and checks whether they are the same
|
||||
as the identifier of the current node one by one. " ,
|
||||
"parameters":
|
||||
"junkfilter": "A pointer to JunkFilter structure, which contains information needed
|
||||
for filtering operation, such as cleaning tuple type and cleaning mapping." ,
|
||||
"batch": "A pointer to the VectorBatch structure, which contains the data to be filtered."*/
|
||||
void ExecSetjunkFilteDescriptor(JunkFilter* junkfilter, TupleDesc tupdesc)
|
||||
{
|
||||
TupleDesc resultslotTupType;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* execMain.cpp
|
||||
|
|
@ -435,6 +439,39 @@ void standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
|
|||
*
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/*This code is written in C++ and seems to be extracted from a
|
||||
database management system (maybe PostgreSQL).
|
||||
|
||||
The ExecutorRun function is the main routine of the executor module.
|
||||
It receives the query descriptor from the traffic police and executes the query plan.
|
||||
|
||||
This function first performs some initialization operations, and then
|
||||
calls the exec_explain_plan function, probably to explain the query plan.
|
||||
|
||||
Then, if the workload manager is enabled, and the resource tracking
|
||||
level is set to RESOURCE_TRACK_OPERATOR,
|
||||
and the query descriptor is not empty, and the plan
|
||||
statement in the query descriptor is a flow plan,
|
||||
and resources need to be tracked, then some additional variables are set.
|
||||
|
||||
Then, it checks whether operation history statistics
|
||||
can be performed, and if so, it calls the ExplainNodeFinish function.
|
||||
|
||||
Next, it checks whether there is an ExecutorRun_hook, and calls it if there is;
|
||||
Otherwise, call the standard_ExecutorRun function.
|
||||
|
||||
Then, if it is a PGXC coordinator or a single node, and the query operation is insert,
|
||||
delete, update or merge, the report_iud_time function is called.
|
||||
|
||||
Next, if resources need to be tracked, and there are query descriptors and tracking operations,
|
||||
the PlanAnalyzerOperator function is called to analyze the query plan problem.
|
||||
If a problem is found, it is stored in the system view gs_wlm_session_history.
|
||||
|
||||
Finally, print the query duration and call the instr_stmt_report_query_plan function.
|
||||
If operation history statistics can be performed, set can_record_to_table to true,
|
||||
and call the ExplainNodeFinish function again. If it is a PGXC coordinator and the
|
||||
global instrument is not empty, delete the global instrument and set the thread instrument to NULL.
|
||||
Finally, the old statement name is restored and the execution level is reduced by 1.*/
|
||||
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count)
|
||||
{
|
||||
/* sql active feature, opeartor history statistics */
|
||||
|
|
@ -655,6 +692,19 @@ void standard_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long co
|
|||
*
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/*"ExecutorFinish function": "This function is a calling hook. It first checks whether there is an ExecutorFinish_hook'
|
||||
and calls it if there is one; Otherwise, call the standard_ExecutorFinish function. " ,
|
||||
"standard_ExecutorFinish function":
|
||||
"Function": "This is the standard end routine of the actuator module." ,
|
||||
"step":
|
||||
"Perform a health check to make sure that the query descriptor and status exist and are not in an interpretation mode." ,
|
||||
"Switch to the memory context of each query." ,
|
||||
"If the total time is set, start the instrument node." ,
|
||||
"Run the ModifyTable node to finish." ,
|
||||
"Executes a queued AFTER trigger unless told to skip the trigger." ,
|
||||
"If the total time is set, stop the instrument node." ,
|
||||
"Switch back to the old memory context." ,
|
||||
"Mark the status as completed."*/
|
||||
void ExecutorFinish(QueryDesc *queryDesc)
|
||||
{
|
||||
if (ExecutorFinish_hook) {
|
||||
|
|
@ -733,7 +783,28 @@ int ExecGetPlanNodeid(void)
|
|||
}
|
||||
return key;
|
||||
}
|
||||
/*This is a function called standard_ExecutorEnd',
|
||||
which releases the resources used by the executor during the query.
|
||||
|
||||
The following is the function explanation of the function:
|
||||
|
||||
1. Define some variables, including an execution state pointer ` estate', a memory context
|
||||
` old_context', an instrument time ` starttime' and a totaltime ` totaltime'.
|
||||
2. Set the start time by calling `instr _ time _ set _ current (start time)'.
|
||||
3. Do some health checks to ensure that' queryDesc' and' estate' are not empty.
|
||||
4. If `memory _ context _ checking` is defined, all memory contexts are checked at the start of the executor.
|
||||
5. Check whether ExecutorFinish has been called, unless it is in interpretation-only mode.
|
||||
This is because before version 9.1, the caller may forget to call it.
|
||||
6. Switch to the memory context of each query to run' ExecEndPlan'.
|
||||
7. Release our snapshot.
|
||||
8. If LLVM compilation is enabled and it is not currently running in the function manager,
|
||||
code generation thread disassembly is performed.
|
||||
9. Switch to the old context before destroying it.
|
||||
10. If `memory _ context _ checking` is defined, the memory context of each query is checked before FreeExecutorState'.
|
||||
11. Release the execution state and the memory context of each query, which should release all the contents allocated by the executor.
|
||||
12. Reset the fields in the query descriptor that no longer point to anything.
|
||||
|
||||
The main purpose of this code is to clean up and release resources after the query execution.*/
|
||||
void standard_ExecutorEnd(QueryDesc *queryDesc)
|
||||
{
|
||||
EState *estate = NULL;
|
||||
|
|
@ -826,6 +897,20 @@ void standard_ExecutorEnd(QueryDesc *queryDesc)
|
|||
* to the start.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/*This is a function called ExecutorRewind', which is used to rescan the query plan without executing it.
|
||||
|
||||
The following is the functional explanation of the code:
|
||||
|
||||
1. Define two variables, an execution estate pointer `establishment` and a memory context `old _ context`.
|
||||
2. Do some health checks to ensure that' queryDesc' and' estate' are not empty.
|
||||
3. Check whether the query operation is CMD_SELECT by assertion,
|
||||
because it may be meaningless to rescan and update the query.
|
||||
4. Switch to the memory context of each query to run ExecReScan.
|
||||
5. Rescan the query plan without executing it.
|
||||
6. Switch back to the old memory context.
|
||||
|
||||
This function may be used to reload or rescan the query plan under certain circumstances without actually executing it.
|
||||
This may be useful when you need to refresh the query plan or reload the data.*/
|
||||
void ExecutorRewind(QueryDesc *queryDesc)
|
||||
{
|
||||
EState *estate = NULL;
|
||||
|
|
@ -910,6 +995,22 @@ bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation)
|
|||
* ExecCheckRTEPerms
|
||||
* Check access permissions for a single RTE.
|
||||
*/
|
||||
/*The function ` ExecCheckRTPerms' is used to check whether each table in a query
|
||||
(listed in ` rangeTable') meets certain permission requirements.
|
||||
|
||||
The main logic of the code is as follows:
|
||||
|
||||
1. Define a' foreach' loop to traverse each element in' rangeTable'.
|
||||
2. For each element, it first checks whether this element is a time series table (RTE_RELATION),
|
||||
if so, it skips the check, if not, it continues to check the permissions.
|
||||
3. The function `ExecCheckrtePerms (RTE) ` is called to check the permissions of the current element (table).
|
||||
4. If the permission check fails, the function will report an error (if `ereport _ on _ violation` is `true`) and then return `false`.
|
||||
5. If ExecutorCheckPerms_hook is defined, call this function and assign the result to `result`.
|
||||
6. After all the operations are completed, the function returns result.
|
||||
|
||||
It should be noted that some parts of this function may be compiled according to whether
|
||||
`enable _ multiple _ nodes` is defined, which is a common technique of preprocessor to include
|
||||
or exclude specific code segments in different compilation environments.*/
|
||||
static bool ExecCheckRTEPerms(RangeTblEntry *rte)
|
||||
{
|
||||
AclMode requiredPerms;
|
||||
|
|
@ -1067,6 +1168,29 @@ static bool ExecCheckRTEPerms(RangeTblEntry *rte)
|
|||
* Check INSERT or UPDATE access permissions for a single RTE (these
|
||||
* are processed uniformly).
|
||||
*/
|
||||
/*The function ExecCheckRTEPermsModified' in this code is the process of performing permission check.
|
||||
|
||||
Function parameters:
|
||||
|
||||
-`relOid': the object identifier representing the relationship to be operated on.
|
||||
-`userid: the user ID of the operation.
|
||||
-`modifiedCols: Represents the bitmap of the modified column.
|
||||
-`requiredPerms: required permission type.
|
||||
|
||||
Code logic:
|
||||
|
||||
-If' modifiedCols' is empty, it means that the query has not explicitly updated any columns,
|
||||
so if the user has permission on any column of the relationship, the query is allowed.
|
||||
This is to deal with possible marginal situations in' SELECT FOR UPDATE' and' UPDATE'.
|
||||
-If' modifiedCols' is not empty, traverse each modified column. In the process of traversal,
|
||||
firstly, the index of the next modified column is obtained by the function of `bms _ next _ member',
|
||||
and then the attribute number attno' is obtained by adding the offset `firstlowinvalidheapattributenumber' to the index.
|
||||
-If' attno' equals' InvalidAttrNumber', it means that the whole line is referenced,
|
||||
which is not allowed here, so an error is reported and an exception is thrown.
|
||||
-If' attno' is not equal to' InvalidAttrNumber', use the' pg_attribute_aclcheck' function
|
||||
to check the user's permissions on the current attribute. If the permission check fails, it returns `false`.
|
||||
|
||||
If the function can successfully handle the permission check of all columns, then it finally returns ` true'.*/
|
||||
static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms)
|
||||
{
|
||||
int col = -1;
|
||||
|
|
@ -1097,6 +1221,20 @@ static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifie
|
|||
return true;
|
||||
}
|
||||
|
||||
/*The main function of this code is to check whether a transaction is read-only. Specifically,
|
||||
this function traverses all table references in the SQL query, and then rejects those queries that attempt to write on non-temporary tables.
|
||||
|
||||
Every part of the code has specific checks:
|
||||
|
||||
1. Traverse all table references in the query (` foreach (l, plannedstmt->rtable) `).
|
||||
2. for each table reference, check its type (rte->rtekind! = RTE_RELATION`)。 If it is not a relationship (that is, it is not a table), then skip it.
|
||||
3. check the required permissions (`rte-> requiredperms & (~ ACL _ select) `). If you only need to select the permission, then skip it.
|
||||
4. Check whether this table is in the temporary namespace (`istempnamespace (get _ rel _ namespace (rte-> Relid)) `). If so, then skip it.
|
||||
5. Check the persistence of this table (` get _ rel _ persistence (rte-> Relid) = = rel persistence _ global _ temp`). If it is a global temporary table, then skip it.
|
||||
6. For a specific Greenplum database, if it is a roach standby cluster in maintenance mode and is accessing the node relationship, then skip it.
|
||||
7. If all the above checks pass, then call the PreventCommandIfReadOnly' function to stop the execution of this query.
|
||||
|
||||
Generally speaking, the purpose of this function is to protect the consistency of the database by preventing write operations in read-only transactions.*/
|
||||
/*
|
||||
* Check that the query does not imply any writes to non-temp tables.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/***
|
||||
* @Author: 王语翀
|
||||
* @Team: 兰心开源
|
||||
*/
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* execProcnode.cpp
|
||||
|
|
@ -175,6 +179,23 @@
|
|||
* initilaization work like open scanrel, instead allow NodeInit work to continue on its
|
||||
* lefttree/righttree
|
||||
*/
|
||||
/*The function is called ` NeedStubExecution', and its input parameter is a pointer to type ` Plan'.
|
||||
The main function of this function is to judge whether a plan node needs pile execution.
|
||||
The following is a detailed explanation of each part of the function:
|
||||
|
||||
-` # ifndef enable _ multiple _ nodes`: This is a preprocessor instruction to check whether the
|
||||
macro definition `enable _ multiple _ nodes` exists. If it does not exist, the function directly returns false.
|
||||
-`if (exec _ in _ recursive _ mode (plan) )`: This judgment statement checks whether the plan node is
|
||||
in recursive mode. If so, the function returns false.
|
||||
-`if (NeedExecute(plan) )`: This judgment statement calls the `NeedExecute' function to judge
|
||||
whether this plan step needs to be executed on the current data node. If necessary, the function returns false.
|
||||
-`switch (nodeTag(plan) )`: This judgment statement is processed differently according to the type
|
||||
of plan node. For most types of planning nodes, it returns ` false', but for certain node types
|
||||
(such as T_ModifyTable, T_VecModifyTable, T_Scan, etc.), it returns ` true'. If `enable _ multiple _ nodes` is defined, it will also handle the T_TsStoreScan type.
|
||||
|
||||
Therefore, in a word, this function mainly judges whether a given plan node needs to be executed,
|
||||
and it is influenced by many conditions, including whether some macros are defined,
|
||||
the state of the plan node, and the type of the plan node.*/
|
||||
bool NeedStubExecution(Plan* plan)
|
||||
{
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
|
|
@ -219,6 +240,21 @@ bool NeedStubExecution(Plan* plan)
|
|||
/*
|
||||
* not need execute active sql if the datanode don't run in multi-nodegroup.
|
||||
*/
|
||||
/*1. `Needexecutivesql (plan * plan)' function: judge whether the current plan node needs to be executed.
|
||||
If the current node is neither a PGXC coordinator nor a single node and does not need to be executed, then return false;; Otherwise return true.
|
||||
2. `seqscannodestub (seqscanstate * seq _ scan) ` function: judge whether the sequential scanning node is a pile.
|
||||
If the scan description is NULL, then it is a pile and returns true;; Otherwise return false.
|
||||
3. `idxscannodestub (indexscanstate * index _ scan) ` function: judge whether the index scanning node is a stub.
|
||||
If the scan description is NULL, then it is a pile and returns true;; Otherwise return false.
|
||||
4. `idxonlyscannodestub (indexonlyscanstate * index _ only _ scan) ` function: judge whether the index-only
|
||||
scanning node is a pile. If the scan description is NULL, then it is a pile and returns true;; Otherwise return false.
|
||||
5. `bmidxonlyscannodestub (bitmapindexscanstate * BM _ index _ scan) ` function: judge whether the
|
||||
bitmap index scanning node is a pile. If the scan description is NULL, then it is a pile and returns true;; Otherwise return false.
|
||||
6. `bmheapscannodestub (bitmapheapstate * BM _ heap _ scan) ` function: judge whether the bitmap heap
|
||||
scanning node is a pile. If the scan description is NULL, then it is a pile and returns true;; Otherwise return false.
|
||||
|
||||
Each of these functions checks whether certain types of database operations (such as sequential scanning,
|
||||
index scanning, etc.) need to be performed on the current data node. If not, then the operation is a "stub", that is, it is a placeholder and does not actually perform any work.*/
|
||||
static bool NeedExecuteActiveSql(Plan* plan)
|
||||
{
|
||||
if ((!IS_PGXC_COORDINATOR) && (!IS_SINGLE_NODE) && false == NeedExecute(plan)) {
|
||||
|
|
@ -252,7 +288,16 @@ static inline bool BmHeapScanNodeIsStub(BitmapHeapScanState* bm_heap_scan)
|
|||
{
|
||||
return bm_heap_scan->ss.ss_currentScanDesc == NULL;
|
||||
}
|
||||
/*The function is called ExecInitNodeByType, and it has three parameters: plan * node, estate * estate,
|
||||
int eflags. This function calls the corresponding initialization function by judging the type of the incoming Plan node.
|
||||
|
||||
This code is a part of a database management system (such as PostgreSQL) to handle the execution of the query plan
|
||||
. Each query will be parsed and transformed into a plan, and then the plan will guide the execution of the query.
|
||||
|
||||
In the code, each case corresponds to a plan node type, such as T_SeqScan corresponding to sequential scanning
|
||||
and T_IndexScan corresponding to index scanning. Each case will pass the node, estate and eflags to the corresponding
|
||||
initialization function, and return the initialized PlanState. This is a kind of polymorphism,
|
||||
which enables us to call the corresponding function according to the node type.*/
|
||||
PlanState* ExecInitNodeByType(Plan* node, EState* estate, int eflags)
|
||||
{
|
||||
switch (nodeTag(node)) {
|
||||
|
|
@ -405,7 +450,24 @@ PlanState* ExecInitNodeByType(Plan* node, EState* estate, int eflags)
|
|||
return NULL; /* keep compiler quiet */
|
||||
}
|
||||
}
|
||||
/*
|
||||
The function ExecInitNodeSubPlan accepts three parameters: a Plan node, an execution state and a result PlanState.
|
||||
Its main purpose is to initialize sub-plans and execute them if certain conditions are met.
|
||||
|
||||
The following is a detailed explanation of the code:
|
||||
A sub_ps variable is defined, which is a list used to store the initialized sub-plan status.
|
||||
Traverse each element in the node->initPlan list. Node->initPlan is a list containing subplans.
|
||||
In each iteration, take the current sub-plan out of the list and check whether it is empty. If empty, the current iteration is skipped.
|
||||
Make sure that the sub-plan taken out is indeed of SubPlan type.
|
||||
This part of the code performs different processing according to whether the macro ENABLE_MULTIPLE_NODES is defined.
|
||||
If this macro is defined, then if the current node is PGXC coordinator, or estate->es_subplan_ids is empty, or the ID of the
|
||||
current node is equal to the ID of the subplan, the subplan will be executed. If the macro is not defined, the sub-plan
|
||||
will be executed if the current node is the top consumer of the stream, or if the estate->es_subplan_ids is empty,
|
||||
or if the ID of the current node is equal to the ID of the sub-plan.
|
||||
If the above conditions are met, the ExecInitSubPlan function is called to initialize the subplan,
|
||||
and the returned subplan status is stored in the sub_ps list.
|
||||
Finally, assign the sub_ps list to the result->initPlan, that is, the status of the result plan.
|
||||
Generally speaking, the main task of this function is to initialize and execute the subplans in the query plan.*/
|
||||
void ExecInitNodeSubPlan(Plan* node, EState* estate, PlanState* result)
|
||||
{
|
||||
List* sub_ps = NIL;
|
||||
|
|
@ -1002,6 +1064,27 @@ ExecProcFuncType g_execProcFuncTable[] = {
|
|||
* Execute the given node to return a(nother) tuple.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/*The function ExecProcNode accepts a pointer node of type PlanState and returns a pointer of type TupleTableSlot'.
|
||||
|
||||
The following is a detailed explanation of the code:
|
||||
|
||||
1. A pointer' result' to the type' TupleTableSlot' is defined and initialized to NULL. This pointer will be used to store the return value of the function.
|
||||
2. `CHECK_FOR_INTERRUPTS () ` is a macro used to check whether there is an interrupt signal. If so, it will stop the current operation and handle the interrupt.
|
||||
3. `MemoryContext old_context; Defines a variable' old_context' of type' MemoryContext', which will be used to save the current memory context.
|
||||
4.' # ifdef ENABLE_MULTIPLE_NODES' is a preprocessor instruction.
|
||||
If' enable _ multiple _ nodes' is defined, the next code will be compiled and executed.
|
||||
This code checks whether there is an early stop signal, and if there is, the function returns NULL.
|
||||
5. `MemoryContextSwitchTo(node->nodeContext); Switch the memory context to the memory context of the node.
|
||||
6. If the parameters of the node have changed, call ExecReScan(node)' for rescan.
|
||||
7. If the node has an instrument (for performance analysis), call `instr start node (node-> instrument)' to start the timing of the instrument.
|
||||
8. In the case of multi-nodes, if the nodes need stubs, call ExecProcNodeStub(node)' to execute stub nodes.
|
||||
Otherwise, the node is processed by looking up the ` g _ execprocfunctional` function table and executing the corresponding function.
|
||||
9. If the node has instruments, call ExecProcNodeInstr(node, result)' to record the implementation of the node.
|
||||
10. Switch back to the old memory context.
|
||||
11. Increment the row counter of the node.
|
||||
12. Return the result pointer.
|
||||
|
||||
The purpose of this code is to perform the corresponding operation according to the type of node and return the result. It is one of the core parts of database query execution.*/
|
||||
TupleTableSlot* ExecProcNode(PlanState* node)
|
||||
{
|
||||
TupleTableSlot* result = NULL;
|
||||
|
|
@ -1062,6 +1145,38 @@ TupleTableSlot* ExecProcNode(PlanState* node)
|
|||
* function must provide its own instrumentation support.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/*This code is a part of a database management system (such as PostgreSQL) and is used to handle the execution of the query plan.
|
||||
Each query will be parsed and transformed into a plan, and then the plan will guide the execution of the query.
|
||||
|
||||
The function MultiExecProcNode accepts a pointer `node` of type
|
||||
PlanState and returns a pointer to type `node`.
|
||||
|
||||
The following is a detailed explanation of the code:
|
||||
|
||||
1. A pointer result to the type Node is defined and initialized to NULL.
|
||||
This pointer will be used to store the return value of the function.
|
||||
2. `MemoryContext old_context; Defines a variable' old_context' of type' MemoryContext',
|
||||
which will be used to save the current memory context.
|
||||
3. `CHECK_FOR_INTERRUPTS(); ` is a macro used to check whether there is an interrupt signal.
|
||||
If so, it will stop the current operation and handle the interrupt.
|
||||
4. `MemoryContextSwitchTo(node->nodeContext);
|
||||
Switch the memory context to the memory context of the node.
|
||||
5. If the parameters of the node have changed, call ExecReScan(node)' for rescan.
|
||||
6. The `switch (node tag (node)) ` statement performs
|
||||
corresponding operations according to the type of node:
|
||||
-If the node type is `t _ hashstate`, call `multiexecshash ((hashstate *) node) `.
|
||||
-If the node type is `t _ bitmapindexscanState', call `multiexecbitmapindexscan ((bitmapindexscanState *) node) `.
|
||||
-If the node type is `t _ bitmapandstate`, call `multiexecbitmapand ((bitmapandstate *) node) `.
|
||||
-If the node type is `t _ bitmaporstate`, call `multiexecbitmapor ((bitmaporstate *) node) `.
|
||||
-If the node type is not any of the above, an error is reported with the error code `errcode _ unrecognized _ node _ type`
|
||||
and ERRCODE_UNRECOGNIZED_NODE_TYPE is displayed.
|
||||
7. If the node has an instrument (used for performance analysis),
|
||||
set the memory information of the node as the memory information of the instrument.
|
||||
8. Switch back to the old memory context.
|
||||
9. Return the result pointer.
|
||||
|
||||
The purpose of this code is to perform the corresponding operation according to the type of node and return the result.
|
||||
It is one of the core parts of database query execution.*/
|
||||
Node* MultiExecProcNode(PlanState* node)
|
||||
{
|
||||
Node* result = NULL;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue