forked from huawei/openGauss-server
Compare commits
185 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
6b9fcdb2c7 | |
|
|
9c287ac5ad | |
|
|
0ff71e4c79 | |
|
|
18d7731b5e | |
|
|
b69573e1d8 | |
|
|
9a30eec7ac | |
|
|
ed8f12a6a8 | |
|
|
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 |
|
|
@ -23,6 +23,8 @@ declare binarylib_dir='None'
|
|||
declare make_check='off'
|
||||
declare separate_symbol='on'
|
||||
|
||||
# function name: print_help
|
||||
# Note: this function is used to show the command if you forget
|
||||
function print_help()
|
||||
{
|
||||
echo "Usage: $0 [OPTION]
|
||||
|
|
@ -38,6 +40,8 @@ function print_help()
|
|||
"
|
||||
}
|
||||
|
||||
# function name: print_version
|
||||
# Note: this function is used to show the version the user is using
|
||||
function print_version()
|
||||
{
|
||||
echo $(cat ${SCRIPT_DIR}/gaussdb.ver | grep 'VERSION' | awk -F "=" '{print $2}')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* contrib/adminpack/adminpack--1.0.sql */
|
||||
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
-- check if the script runs in psql, or remind user to use "CREATE EXTENSION adminpack" to run
|
||||
\echo Use "CREATE EXTENSION adminpack" to load this file. \quit
|
||||
|
||||
/* ***********************************************
|
||||
|
|
@ -8,27 +8,31 @@
|
|||
* *********************************************** */
|
||||
|
||||
/* generic file access functions */
|
||||
|
||||
-- check the file whether is coverd
|
||||
CREATE FUNCTION pg_catalog.pg_file_write(text, text, bool)
|
||||
RETURNS bigint
|
||||
AS 'MODULE_PATHNAME', 'pg_file_write'
|
||||
LANGUAGE C VOLATILE STRICT;
|
||||
|
||||
-- change the name of file
|
||||
CREATE FUNCTION pg_catalog.pg_file_rename(text, text, text)
|
||||
RETURNS bool
|
||||
AS 'MODULE_PATHNAME', 'pg_file_rename'
|
||||
LANGUAGE C VOLATILE;
|
||||
|
||||
-- an overloaded version of the definition function
|
||||
CREATE FUNCTION pg_catalog.pg_file_rename(text, text)
|
||||
RETURNS bool
|
||||
AS 'SELECT pg_catalog.pg_file_rename($1, $2, NULL::pg_catalog.text);'
|
||||
LANGUAGE SQL VOLATILE STRICT;
|
||||
|
||||
-- delete the file
|
||||
CREATE FUNCTION pg_catalog.pg_file_unlink(text)
|
||||
RETURNS bool
|
||||
AS 'MODULE_PATHNAME', 'pg_file_unlink'
|
||||
LANGUAGE C VOLATILE STRICT;
|
||||
|
||||
-- list files in the log directory.
|
||||
CREATE FUNCTION pg_catalog.pg_logdir_ls()
|
||||
RETURNS setof record
|
||||
AS 'MODULE_PATHNAME', 'pg_logdir_ls'
|
||||
|
|
@ -37,16 +41,19 @@ LANGUAGE C VOLATILE STRICT;
|
|||
|
||||
/* Renaming of existing backend functions for pgAdmin compatibility */
|
||||
|
||||
-- read the contents of the file
|
||||
CREATE FUNCTION pg_catalog.pg_file_read(text, bigint, bigint)
|
||||
RETURNS text
|
||||
AS 'pg_read_file'
|
||||
LANGUAGE INTERNAL VOLATILE STRICT;
|
||||
|
||||
-- get the size of the file
|
||||
CREATE FUNCTION pg_catalog.pg_file_length(text)
|
||||
RETURNS bigint
|
||||
AS 'SELECT size FROM pg_catalog.pg_stat_file($1)'
|
||||
LANGUAGE SQL VOLATILE STRICT;
|
||||
|
||||
-- manually starting log file rotation.
|
||||
CREATE FUNCTION pg_catalog.pg_logfile_rotate()
|
||||
RETURNS int4
|
||||
AS 'pg_rotate_logfile'
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ PG_FUNCTION_INFO_V1(pg_file_unlink);
|
|||
PG_FUNCTION_INFO_V1(pg_logdir_ls);
|
||||
|
||||
typedef struct {
|
||||
char* location;
|
||||
DIR* dirdesc;
|
||||
char *location;
|
||||
DIR *dirdesc;
|
||||
} directory_fctx;
|
||||
|
||||
/*-----------------------
|
||||
|
|
@ -65,30 +65,30 @@ typedef struct {
|
|||
* Filename may be absolute or relative to the t_thrd.proc_cxt.DataDir, but we only allow
|
||||
* absolute paths that match t_thrd.proc_cxt.DataDir or u_sess->attr.attr_common.Log_directory.
|
||||
*/
|
||||
static char* convert_and_check_filename(text* arg, bool logAllowed)
|
||||
static char *convert_and_check_filename(text *arg, bool logAllowed)
|
||||
{
|
||||
char* filename = text_to_cstring(arg);
|
||||
char *filename = text_to_cstring(arg);
|
||||
|
||||
canonicalize_path(filename); /* filename can change length here */
|
||||
canonicalize_path(filename); /* May change the length of 'filename' */
|
||||
|
||||
if (is_absolute_path(filename)) {
|
||||
/* Disallow '/a/b/data/..' */
|
||||
if (path_contains_parent_reference(filename))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
(errmsg("reference to parent directory (\"..\") not allowed"))));
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
(errmsg("reference to parent directory (\"..\") not allowed"))));
|
||||
|
||||
/*
|
||||
* Allow absolute paths if within t_thrd.proc_cxt.DataDir or u_sess->attr.attr_common.Log_directory, even
|
||||
* though u_sess->attr.attr_common.Log_directory might be outside t_thrd.proc_cxt.DataDir.
|
||||
* Allow absolute paths if they are within t_thrd.proc_cxt.DataDir or u_sess->attr.attr_common.Log_directory.
|
||||
* However, if 'logAllowed' is false, absolute paths outside u_sess->attr.attr_common.Log_directory are
|
||||
* disallowed.
|
||||
*/
|
||||
if (!path_is_prefix_of_path(t_thrd.proc_cxt.DataDir, filename) &&
|
||||
(!logAllowed || !is_absolute_path(u_sess->attr.attr_common.Log_directory) ||
|
||||
!path_is_prefix_of_path(u_sess->attr.attr_common.Log_directory, filename)))
|
||||
!path_is_prefix_of_path(u_sess->attr.attr_common.Log_directory, filename)))
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("absolute path not allowed"))));
|
||||
} else if (!path_is_relative_and_below_cwd(filename))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("path must be in or below the current directory"))));
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("path must be in or below the current directory"))));
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
|
@ -99,8 +99,9 @@ static char* convert_and_check_filename(text* arg, bool logAllowed)
|
|||
static void requireSuperuser(void)
|
||||
{
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("only system admin may access generic file functions"))));
|
||||
// print the ERROR_REPORT
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
(errmsg("only system admin may access generic file functions"))));
|
||||
}
|
||||
|
||||
/* ------------------------------------
|
||||
|
|
@ -109,56 +110,67 @@ static void requireSuperuser(void)
|
|||
|
||||
Datum pg_file_write(PG_FUNCTION_ARGS)
|
||||
{
|
||||
FILE* f = NULL;
|
||||
char* filename = NULL;
|
||||
text* data = NULL;
|
||||
int64 count = 0;
|
||||
FILE *f = NULL; // File pointer for file operations
|
||||
char *filename = NULL; // File name
|
||||
text *data = NULL; // Data content
|
||||
int64 count = 0; // Number of bytes written
|
||||
|
||||
requireSuperuser();
|
||||
requireSuperuser(); // Check if the current user is a superuser, and raise an error if not
|
||||
|
||||
filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
|
||||
data = PG_GETARG_TEXT_P(1);
|
||||
filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false); // Get and validate the file name
|
||||
data = PG_GETARG_TEXT_P(1); // Get the data to be written
|
||||
|
||||
if (!PG_GETARG_BOOL(2)) {
|
||||
struct stat fst;
|
||||
|
||||
// Check if the file already exists, and raise an error if it does
|
||||
if (stat(filename, &fst) >= 0)
|
||||
ereport(ERROR, (ERRCODE_DUPLICATE_FILE, errmsg("file \"%s\" exists", filename)));
|
||||
|
||||
// Open the file in write mode
|
||||
f = fopen(filename, "wb");
|
||||
} else
|
||||
} else {
|
||||
// Open the file in append mode
|
||||
f = fopen(filename, "ab");
|
||||
}
|
||||
|
||||
// Check if the file opening is successful, and raise an error if not
|
||||
if (!f)
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not open file \"%s\" for writing: %m", filename)));
|
||||
|
||||
// If the data content is not empty, write the data to the file
|
||||
if (VARSIZE(data) != 0) {
|
||||
// Write the data content to the file and record the number of bytes written
|
||||
count = fwrite(VARDATA(data), 1, VARSIZE(data) - VARHDRSZ, f);
|
||||
|
||||
// Check if the write operation is successful, and raise an error if not
|
||||
if (count != VARSIZE(data) - VARHDRSZ)
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not write file \"%s\": %m", filename)));
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
fclose(f); // Close the file
|
||||
|
||||
// Return the number of bytes successfully written
|
||||
PG_RETURN_INT64(count);
|
||||
}
|
||||
|
||||
Datum pg_file_rename(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *fn1, *fn2, *fn3;
|
||||
int rc;
|
||||
char *fn1, *fn2, *fn3; // File name variables
|
||||
int rc; // Return code for access operation
|
||||
|
||||
requireSuperuser();
|
||||
requireSuperuser(); // Check if the current user is a superuser, and raise an error if not
|
||||
|
||||
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
|
||||
PG_RETURN_NULL();
|
||||
|
||||
fn1 = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
|
||||
fn2 = convert_and_check_filename(PG_GETARG_TEXT_P(1), false);
|
||||
fn1 = convert_and_check_filename(PG_GETARG_TEXT_P(0), false); // Get and validate the source file name
|
||||
fn2 = convert_and_check_filename(PG_GETARG_TEXT_P(1), false); // Get and validate the destination file name
|
||||
|
||||
if (PG_ARGISNULL(2))
|
||||
fn3 = 0;
|
||||
else
|
||||
fn3 = convert_and_check_filename(PG_GETARG_TEXT_P(2), false);
|
||||
fn3 = convert_and_check_filename(PG_GETARG_TEXT_P(2), false); // Get and validate the backup file name
|
||||
|
||||
if (access(fn1, W_OK) < 0) {
|
||||
ereport(WARNING, (errcode_for_file_access(), errmsg("file \"%s\" is not accessible: %m", fn1)));
|
||||
|
|
@ -172,7 +184,7 @@ Datum pg_file_rename(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_BOOL(false);
|
||||
}
|
||||
|
||||
rc = access(fn3 ? fn3 : fn2, 2);
|
||||
rc = access(fn3 ? fn3 : fn2, 2); // Check if the target file exists
|
||||
if (rc >= 0 || errno != ENOENT) {
|
||||
ereport(ERROR, (ERRCODE_DUPLICATE_FILE, errmsg("cannot rename to target file \"%s\"", fn3 ? fn3 : fn2)));
|
||||
}
|
||||
|
|
@ -185,8 +197,8 @@ Datum pg_file_rename(PG_FUNCTION_ARGS)
|
|||
ereport(WARNING, (errcode_for_file_access(), errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2)));
|
||||
|
||||
if (rename(fn3, fn2) != 0) {
|
||||
ereport(
|
||||
ERROR, (errcode_for_file_access(), errmsg("could not rename \"%s\" back to \"%s\": %m", fn3, fn2)));
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(), errmsg("could not rename \"%s\" back to \"%s\": %m", fn3, fn2)));
|
||||
} else {
|
||||
ereport(ERROR, (ERRCODE_UNDEFINED_FILE, errmsg("renaming \"%s\" to \"%s\" was reverted", fn2, fn3)));
|
||||
}
|
||||
|
|
@ -200,111 +212,116 @@ Datum pg_file_rename(PG_FUNCTION_ARGS)
|
|||
|
||||
Datum pg_file_unlink(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char* filename = NULL;
|
||||
char *filename = NULL; // File name variable
|
||||
|
||||
requireSuperuser();
|
||||
requireSuperuser(); // Check if the current user is a superuser, and raise an error if not
|
||||
|
||||
filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false);
|
||||
filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false); // Get and validate the file name
|
||||
|
||||
if (access(filename, W_OK) < 0) {
|
||||
if (errno == ENOENT)
|
||||
PG_RETURN_BOOL(false);
|
||||
PG_RETURN_BOOL(false); // Return false if the file does not exist
|
||||
else
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("file \"%s\" is not accessible: %m", filename)));
|
||||
// Raise an error if the file is not accessible
|
||||
}
|
||||
|
||||
if (unlink(filename) < 0) {
|
||||
ereport(WARNING, (errcode_for_file_access(), errmsg("could not unlink file \"%s\": %m", filename)));
|
||||
// Raise a warning with an error code if the file unlinking fails
|
||||
|
||||
PG_RETURN_BOOL(false);
|
||||
PG_RETURN_BOOL(false); // Return false indicating the unlinking operation failed
|
||||
}
|
||||
PG_RETURN_BOOL(true);
|
||||
|
||||
PG_RETURN_BOOL(true); // Return true to indicate a successful unlinking operation
|
||||
}
|
||||
|
||||
Datum pg_logdir_ls(PG_FUNCTION_ARGS)
|
||||
{
|
||||
FuncCallContext* funcctx = NULL;
|
||||
struct dirent* de;
|
||||
directory_fctx* fctx = NULL;
|
||||
FuncCallContext *funcctx = NULL; // Function call context
|
||||
struct dirent *de; // Directory entry
|
||||
directory_fctx *fctx = NULL; // Directory context
|
||||
|
||||
if (!superuser())
|
||||
ereport(
|
||||
ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("only system admin can list the log directory"))));
|
||||
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("only system admin can list the log directory"))));
|
||||
|
||||
if (strcmp(u_sess->attr.attr_common.Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log") != 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
(errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'"))));
|
||||
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
(errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'"))));
|
||||
|
||||
if (SRF_IS_FIRSTCALL()) {
|
||||
MemoryContext oldcontext;
|
||||
TupleDesc tupdesc;
|
||||
|
||||
|
||||
/* Initialize function call context and memory */
|
||||
funcctx = SRF_FIRSTCALL_INIT();
|
||||
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
||||
|
||||
fctx = (directory_fctx*)palloc(sizeof(directory_fctx));
|
||||
|
||||
|
||||
fctx = (directory_fctx *)palloc(sizeof(directory_fctx));
|
||||
|
||||
/* Create tuple descriptor */
|
||||
tupdesc = CreateTemplateTupleDesc(2, false);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber)1, "starttime", TIMESTAMPOID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber)2, "filename", TEXTOID, -1, 0);
|
||||
|
||||
|
||||
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
|
||||
|
||||
|
||||
fctx->location = pstrdup(u_sess->attr.attr_common.Log_directory);
|
||||
fctx->dirdesc = AllocateDir(fctx->location);
|
||||
|
||||
|
||||
if (!fctx->dirdesc)
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not read directory \"%s\": %m", fctx->location)));
|
||||
|
||||
|
||||
funcctx->user_fctx = fctx;
|
||||
(void)MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
|
||||
|
||||
/* Set up the function call context and directory context */
|
||||
funcctx = SRF_PERCALL_SETUP();
|
||||
fctx = (directory_fctx*)funcctx->user_fctx;
|
||||
|
||||
fctx = (directory_fctx *)funcctx->user_fctx;
|
||||
|
||||
/* Loop through directory entries */
|
||||
while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL) {
|
||||
char* values[2];
|
||||
char *values[2];
|
||||
HeapTuple tuple;
|
||||
char timestampbuf[32];
|
||||
char* field[MAXDATEFIELDS];
|
||||
char *field[MAXDATEFIELDS];
|
||||
char lowstr[MAXDATELEN + 1];
|
||||
int dtype;
|
||||
int nf, ftype[MAXDATEFIELDS];
|
||||
fsec_t fsec;
|
||||
int tz = 0;
|
||||
struct pg_tm date;
|
||||
|
||||
|
||||
/*
|
||||
* Default format: postgresql-YYYY-MM-DD_HHMMSS.log
|
||||
*/
|
||||
if (strlen(de->d_name) != 32 || strncmp(de->d_name, "postgresql-", 11) != 0 || de->d_name[21] != '_' ||
|
||||
strcmp(de->d_name + 28, ".log") != 0)
|
||||
if (strlen(de->d_name) != 32 || strncmp(de->d_name, "postgresql-", 11) != 0 ||
|
||||
de->d_name[21] != '_' || strcmp(de->d_name + 28, ".log") != 0)
|
||||
continue;
|
||||
|
||||
/* extract timestamp portion of filename */
|
||||
|
||||
/* Extract timestamp portion of filename */
|
||||
strcpy(timestampbuf, de->d_name + 11);
|
||||
timestampbuf[17] = '\0';
|
||||
|
||||
/* parse and decode expected timestamp to verify it's OK format */
|
||||
|
||||
/* Parse and decode expected timestamp to verify it's a valid format */
|
||||
if (ParseDateTime(timestampbuf, lowstr, MAXDATELEN, field, ftype, MAXDATEFIELDS, &nf))
|
||||
continue;
|
||||
|
||||
|
||||
if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz))
|
||||
continue;
|
||||
|
||||
/* Seems the timestamp is OK; prepare and return tuple */
|
||||
|
||||
|
||||
/* Timestamp is valid; prepare and return tuple */
|
||||
values[0] = timestampbuf;
|
||||
values[1] = (char*)palloc(strlen(fctx->location) + strlen(de->d_name) + 2);
|
||||
values[1] = (char *)palloc(strlen(fctx->location) + strlen(de->d_name) + 2);
|
||||
sprintf(values[1], "%s/%s", fctx->location, de->d_name);
|
||||
|
||||
|
||||
tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
|
||||
|
||||
|
||||
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
|
||||
}
|
||||
|
||||
|
||||
/* Clean up and return */
|
||||
FreeDir(fctx->dirdesc);
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ void _PG_init(void)
|
|||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
// Define common Boolean variables
|
||||
DefineCustomBoolVariable("auto_explain.log_analyze",
|
||||
"Use EXPLAIN ANALYZE for plan logging.",
|
||||
NULL,
|
||||
|
|
@ -84,6 +84,7 @@ void _PG_init(void)
|
|||
NULL,
|
||||
NULL);
|
||||
|
||||
// Define common Boolean variables of auto_explain.log_verbose
|
||||
DefineCustomBoolVariable("auto_explain.log_verbose",
|
||||
"Use EXPLAIN VERBOSE for plan logging.",
|
||||
NULL,
|
||||
|
|
@ -95,6 +96,7 @@ void _PG_init(void)
|
|||
NULL,
|
||||
NULL);
|
||||
|
||||
// Define common Boolean variables of auto_explain.log_buffers
|
||||
DefineCustomBoolVariable("auto_explain.log_buffers",
|
||||
"Log buffers usage.",
|
||||
NULL,
|
||||
|
|
|
|||
|
|
@ -87,10 +87,13 @@ static GBT_VARKEY* gbt_bit_l2n(GBT_VARKEY* leaf)
|
|||
static const gbtree_vinfo tinfo = {
|
||||
gbt_t_bit, 0, TRUE, gbt_bitgt, gbt_bitge, gbt_biteq, gbt_bitle, gbt_bitlt, gbt_bitcmp, gbt_bit_l2n};
|
||||
|
||||
/**************************************************
|
||||
* Bit ops
|
||||
**************************************************/
|
||||
|
||||
/*
|
||||
* This function compresses a GIST entry using variable-length bitstrings.
|
||||
* The input is a pointer to a GISTENTRY struct.
|
||||
* The output is a pointer to the compressed data, which is obtained by calling
|
||||
* the gbt_var_compress function with the GISTENTRY pointer and a pointer to
|
||||
* the tinfo struct.
|
||||
*/
|
||||
Datum gbt_bit_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
|
|
@ -98,6 +101,20 @@ Datum gbt_bit_compress(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_POINTER(gbt_var_compress(entry, &tinfo));
|
||||
}
|
||||
|
||||
/*
|
||||
* This function checks if a GIST entry is consistent with a query using
|
||||
* variable-length bitstrings. The input is a pointer to a GISTENTRY struct,
|
||||
* a pointer to the query data, and a strategy number. The output is a boolean
|
||||
* value indicating whether the entry is consistent with the query.
|
||||
*
|
||||
* The function also sets a flag indicating whether a recheck is needed, but
|
||||
* this flag is always set to false for all cases served by this function.
|
||||
*
|
||||
* If the entry is a leaf node, the function calls gbt_var_consistent with
|
||||
* the readable key of the entry and the query data. Otherwise, the function
|
||||
* transforms the query data using gbt_bit_xfrm and calls gbt_var_consistent
|
||||
* with the readable key of the entry and the transformed query data.
|
||||
*/
|
||||
Datum gbt_bit_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
|
|
@ -122,6 +139,15 @@ Datum gbt_bit_consistent(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_BOOL(retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function performs a union of a set of GIST entries using
|
||||
* variable-length bitstrings. The input is a pointer to a GistEntryVector
|
||||
* struct and a pointer to an integer that will hold the size of the resulting
|
||||
* union. The output is a pointer to the union of the entries.
|
||||
*
|
||||
* The function calls gbt_var_union with the GistEntryVector, the collation,
|
||||
* and a pointer to the tinfo struct.
|
||||
*/
|
||||
Datum gbt_bit_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
|
|
@ -130,6 +156,16 @@ Datum gbt_bit_union(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(), &tinfo));
|
||||
}
|
||||
|
||||
/*
|
||||
* This function performs a picksplit operation on a set of GIST entries using
|
||||
* variable-length bitstrings. The input is a pointer to a GistEntryVector
|
||||
* struct and a pointer to a GIST_SPLITVEC struct that will hold the results
|
||||
* of the picksplit operation. The output is a pointer to the GIST_SPLITVEC
|
||||
* struct.
|
||||
*
|
||||
* The function calls gbt_var_picksplit with the GistEntryVector, the GIST_SPLITVEC
|
||||
* struct, the collation, and a pointer to the tinfo struct.
|
||||
*/
|
||||
Datum gbt_bit_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
|
|
@ -139,6 +175,16 @@ Datum gbt_bit_picksplit(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_POINTER(v);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function checks if two variable-length bitstrings are the same. The input
|
||||
* is two Datums representing the bitstrings and a pointer to a boolean variable
|
||||
* that will hold the result of the comparison. The output is a pointer to the
|
||||
* boolean variable.
|
||||
*
|
||||
* The function calls gbt_var_same with the two Datums, the collation, and a
|
||||
* pointer to the tinfo struct. The result of the comparison is stored in the
|
||||
* boolean variable pointed to by the input argument.
|
||||
*/
|
||||
Datum gbt_bit_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum d1 = PG_GETARG_DATUM(0);
|
||||
|
|
|
|||
|
|
@ -1,43 +1,43 @@
|
|||
/*
|
||||
* contrib/btree_gist/btree_gist.c
|
||||
*/
|
||||
#include "postgres.h"
|
||||
#include "knl/knl_variable.h"
|
||||
|
||||
#include "btree_gist.h"
|
||||
|
||||
PG_MODULE_MAGIC;
|
||||
|
||||
PG_FUNCTION_INFO_V1(gbt_decompress);
|
||||
PG_FUNCTION_INFO_V1(gbtreekey_in);
|
||||
PG_FUNCTION_INFO_V1(gbtreekey_out);
|
||||
|
||||
extern "C" Datum gbt_decompress(PG_FUNCTION_ARGS);
|
||||
|
||||
/**************************************************
|
||||
* In/Out for keys
|
||||
**************************************************/
|
||||
|
||||
Datum gbtreekey_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("<datatype>key_in() not implemented")));
|
||||
|
||||
PG_RETURN_POINTER(NULL);
|
||||
}
|
||||
|
||||
#include "btree_utils_var.h"
|
||||
#include "utils/builtins.h"
|
||||
Datum gbtreekey_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("<datatype>key_out() not implemented")));
|
||||
PG_RETURN_POINTER(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
** GiST DeCompress methods
|
||||
** do not do anything.
|
||||
*/
|
||||
Datum gbt_decompress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_POINTER(PG_GETARG_POINTER(0));
|
||||
}
|
||||
/*
|
||||
* contrib/btree_gist/btree_gist.c
|
||||
*/
|
||||
#include "postgres.h" // Include the PostgreSQL database header file
|
||||
#include "knl/knl_variable.h" // Include the knl_variable.h header file, which may contain internal variable definitions
|
||||
|
||||
#include "btree_gist.h" // Include the btree_gist.h header file, which defines the data structures and function prototypes required for B-tree GiST indexing
|
||||
|
||||
PG_MODULE_MAGIC; // Macro definition to identify this as a PostgreSQL module
|
||||
|
||||
PG_FUNCTION_INFO_V1(gbt_decompress); // Macro definition to declare a PostgreSQL function, gbt_decompress, with version 1 information
|
||||
PG_FUNCTION_INFO_V1(gbtreekey_in); // Macro definition to declare a PostgreSQL function, gbtreekey_in, with version 1 information
|
||||
PG_FUNCTION_INFO_V1(gbtreekey_out); // Macro definition to declare a PostgreSQL function, gbtreekey_out, with version 1 information
|
||||
|
||||
extern "C" Datum gbt_decompress(PG_FUNCTION_ARGS); // Define a C function named gbt_decompress that returns a Datum type and accepts PG_FUNCTION_ARGS parameters
|
||||
|
||||
/**************************************************
|
||||
* In/Out for keys
|
||||
**************************************************/
|
||||
|
||||
Datum gbtreekey_in(PG_FUNCTION_ARGS) // Define a function named gbtreekey_in that returns a Datum type and accepts PG_FUNCTION_ARGS parameters
|
||||
{
|
||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("<datatype>key_in() not implemented"))); // If the function is called, output an error message indicating that the feature is not supported
|
||||
|
||||
PG_RETURN_POINTER(NULL); // Return a null pointer, indicating that there is no return value
|
||||
}
|
||||
|
||||
#include "btree_utils_var.h" // Include the btree_utils_var.h header file, which may contain auxiliary functions and variables related to B-trees
|
||||
#include "utils/builtins.h" // Include the builtins.h header file, which may contain definitions related to built-in functions
|
||||
Datum gbtreekey_out(PG_FUNCTION_ARGS) // Define the gbtreekey_out function, which converts internal keys to output form and returns a Datum type result
|
||||
{
|
||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("<datatype>key_out() not implemented"))); // If the function is called, output an error message indicating that the feature is not supported
|
||||
PG_RETURN_POINTER(NULL); // Return a null pointer, indicating that there is no return value
|
||||
}
|
||||
|
||||
/*
|
||||
** GiST DeCompress methods
|
||||
** do not do anything.
|
||||
*/
|
||||
Datum gbt_decompress(PG_FUNCTION_ARGS) // Define the gbt_decompress function, which decompresses GiST keys and returns a Datum type result
|
||||
{
|
||||
PG_RETURN_POINTER(PG_GETARG_POINTER(0)); // Directly return the pointer to the input argument as the result, without performing any decompression operations
|
||||
}
|
||||
|
|
@ -138,6 +138,17 @@ Datum gbt_inet_picksplit(PG_FUNCTION_ARGS)
|
|||
gbt_num_picksplit((GistEntryVector*)PG_GETARG_POINTER(0), (GIST_SPLITVEC*)PG_GETARG_POINTER(1), &tinfo));
|
||||
}
|
||||
|
||||
/*
|
||||
function name: gbt_inet_same
|
||||
description:
|
||||
This function checks whether two inet keys are equal. The input is two
|
||||
inetKEY pointers representing the keys to compare, and a pointer to a boolean
|
||||
to hold the result. The output is a pointer to the boolean result.
|
||||
The function calls gbt_num_same with the two keys and a pointer to the tinfo
|
||||
struct. The result of the comparison is stored in the result pointer and
|
||||
returned.
|
||||
|
||||
*/
|
||||
Datum gbt_inet_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
inetKEY* b1 = (inetKEY*)PG_GETARG_POINTER(0);
|
||||
|
|
|
|||
|
|
@ -132,6 +132,21 @@ Datum gbt_int2_consistent(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_BOOL(gbt_num_consistent(&key, (void*)&query, &strategy, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
/*
|
||||
function name: gbt_int2_distance
|
||||
description:
|
||||
This function calculates the distance between an int2 key and a query value.
|
||||
The input is a GISTENTRY pointer representing the key, and an int16 value
|
||||
representing the query. The output is a float8 value representing the
|
||||
distance between the key and the query.
|
||||
|
||||
The function extracts the lower and upper bounds of the int2 key and stores
|
||||
them in a GBT_NUMKEY_R struct. It then calls gbt_num_distance with the key,
|
||||
the query value, a boolean indicating whether the key is a leaf node, and a
|
||||
pointer to the tinfo struct. The result of the distance calculation is
|
||||
returned as a float8 value.
|
||||
|
||||
*/
|
||||
Datum gbt_int2_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
|
|
@ -172,6 +187,18 @@ Datum gbt_int2_picksplit(PG_FUNCTION_ARGS)
|
|||
gbt_num_picksplit((GistEntryVector*)PG_GETARG_POINTER(0), (GIST_SPLITVEC*)PG_GETARG_POINTER(1), &tinfo));
|
||||
}
|
||||
|
||||
/*
|
||||
function name: gbt_int2_same
|
||||
description:
|
||||
This function checks whether two int2 keys are the same.
|
||||
The input is two pointers to int16KEY structs representing the keys,
|
||||
and a pointer to a boolean variable to store the result.
|
||||
The output is a pointer to the boolean variable.
|
||||
|
||||
The function calls gbt_num_same with the two keys, and a pointer to the tinfo
|
||||
struct. The result of the comparison is stored in the boolean variable
|
||||
pointed to by the result argument, and a pointer to this variable is returned.
|
||||
*/
|
||||
Datum gbt_int2_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int16KEY* b1 = (int16KEY*)PG_GETARG_POINTER(0);
|
||||
|
|
|
|||
|
|
@ -15,169 +15,211 @@ typedef struct int32key {
|
|||
/*
|
||||
** int32 ops
|
||||
*/
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_compress);
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_union);
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_picksplit);
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_consistent);
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_distance);
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_penalty);
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_same);
|
||||
|
||||
extern "C" Datum gbt_int4_compress(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_int4_union(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_int4_picksplit(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_int4_consistent(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_int4_distance(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_int4_penalty(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_int4_same(PG_FUNCTION_ARGS);
|
||||
|
||||
static bool gbt_int4gt(const void* a, const void* b)
|
||||
{
|
||||
return (*((const int32*)a) > *((const int32*)b));
|
||||
}
|
||||
static bool gbt_int4ge(const void* a, const void* b)
|
||||
{
|
||||
return (*((const int32*)a) >= *((const int32*)b));
|
||||
}
|
||||
static bool gbt_int4eq(const void* a, const void* b)
|
||||
{
|
||||
return (*((const int32*)a) == *((const int32*)b));
|
||||
}
|
||||
static bool gbt_int4le(const void* a, const void* b)
|
||||
{
|
||||
return (*((const int32*)a) <= *((const int32*)b));
|
||||
}
|
||||
static bool gbt_int4lt(const void* a, const void* b)
|
||||
{
|
||||
return (*((const int32*)a) < *((const int32*)b));
|
||||
// Define a set of function info macros for the different GiST functions.
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_compress); // Compress function for GiST index.
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_union); // Union function for GiST index.
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_picksplit); // Picksplit function for GiST index.
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_consistent);// Consistent function for GiST index.
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_distance); // Distance function for GiST index.
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_penalty); // Penalty function for GiST index.
|
||||
PG_FUNCTION_INFO_V1(gbt_int4_same); // Same function for GiST index.
|
||||
|
||||
// Declare the different GiST functions.
|
||||
extern "C" Datum gbt_int4_compress(PG_FUNCTION_ARGS); // Compress function.
|
||||
extern "C" Datum gbt_int4_union(PG_FUNCTION_ARGS); // Union function.
|
||||
extern "C" Datum gbt_int4_picksplit(PG_FUNCTION_ARGS); // Picksplit function.
|
||||
extern "C" Datum gbt_int4_consistent(PG_FUNCTION_ARGS);// Consistent function.
|
||||
extern "C" Datum gbt_int4_distance(PG_FUNCTION_ARGS); // Distance function.
|
||||
extern "C" Datum gbt_int4_penalty(PG_FUNCTION_ARGS); // Penalty function.
|
||||
extern "C" Datum gbt_int4_same(PG_FUNCTION_ARGS); // Same function.
|
||||
|
||||
// Define a set of comparison functions for integers.
|
||||
static bool gbt_int4gt(const void* a, const void* b) // Greater than comparison.
|
||||
{
|
||||
return (*((const int32*)a) > *((const int32*)b));
|
||||
}
|
||||
static bool gbt_int4ge(const void* a, const void* b) // Greater than or equal to comparison.
|
||||
{
|
||||
return (*((const int32*)a) >= *((const int32*)b));
|
||||
}
|
||||
static bool gbt_int4eq(const void* a, const void* b) // Equal to comparison.
|
||||
{
|
||||
return (*((const int32*)a) == *((const int32*)b));
|
||||
}
|
||||
static bool gbt_int4le(const void* a, const void* b) // Less than or equal to comparison.
|
||||
{
|
||||
return (*((const int32*)a) <= *((const int32*)b));
|
||||
}
|
||||
static bool gbt_int4lt(const void* a, const void* b) // Less than comparison.
|
||||
{
|
||||
return (*((const int32*)a) < *((const int32*)b));
|
||||
}
|
||||
|
||||
static int gbt_int4key_cmp(const void* a, const void* b)
|
||||
{
|
||||
int32KEY* ia = (int32KEY*)(((const Nsrt*)a)->t);
|
||||
int32KEY* ib = (int32KEY*)(((const Nsrt*)b)->t);
|
||||
|
||||
if (ia->lower == ib->lower) {
|
||||
if (ia->upper == ib->upper)
|
||||
return 0;
|
||||
|
||||
return (ia->upper > ib->upper) ? 1 : -1;
|
||||
}
|
||||
|
||||
return (ia->lower > ib->lower) ? 1 : -1;
|
||||
// Define a function that compares two integer keys.
|
||||
static int gbt_int4key_cmp(const void* a, const void* b)
|
||||
{
|
||||
// Cast the input pointers to their actual types.
|
||||
int32KEY* ia = (int32KEY*)(((const Nsrt*)a)->t);
|
||||
int32KEY* ib = (int32KEY*)(((const Nsrt*)b)->t);
|
||||
|
||||
// Compare the lower bounds first. If they are equal...
|
||||
if (ia->lower == ib->lower) {
|
||||
// Compare the upper bounds. If they are equal...
|
||||
if (ia->upper == ib->upper)
|
||||
return 0; // Return 0 if both are equal.
|
||||
|
||||
// Return 1 or -1 depending on which upper bound is greater.
|
||||
return (ia->upper > ib->upper) ? 1 : -1;
|
||||
}
|
||||
|
||||
// Return 1 or -1 depending on which lower bound is greater.
|
||||
return (ia->lower > ib->lower) ? 1 : -1;
|
||||
}
|
||||
|
||||
// Define a function that calculates the distance between two integer keys.
|
||||
static float8 gbt_int4_dist(const void* a, const void* b)
|
||||
{
|
||||
// Use a macro to calculate the distance between the two integers.
|
||||
return GET_FLOAT_DISTANCE(int4, a, b);
|
||||
}
|
||||
|
||||
// Define a structure with information about the integer keys.
|
||||
static const gbtree_ninfo tinfo = {gbt_t_int4,
|
||||
sizeof(int32),
|
||||
gbt_int4gt,
|
||||
gbt_int4ge,
|
||||
gbt_int4eq,
|
||||
gbt_int4le,
|
||||
gbt_int4lt,
|
||||
gbt_int4key_cmp,
|
||||
gbt_int4_dist};
|
||||
|
||||
// Declare the function that will calculate the distance between two integers.
|
||||
PG_FUNCTION_INFO_V1(int4_dist);
|
||||
extern "C" Datum int4_dist(PG_FUNCTION_ARGS);
|
||||
Datum int4_dist(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the two integer arguments.
|
||||
int4 a = PG_GETARG_INT32(0);
|
||||
int4 b = PG_GETARG_INT32(1);
|
||||
int4 r;
|
||||
int4 ra;
|
||||
|
||||
// Calculate the difference between the two integers.
|
||||
r = a - b;
|
||||
ra = Abs(r); // Calculate the absolute value of the difference.
|
||||
|
||||
/* Check for overflow. */
|
||||
if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
|
||||
ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); // Report an error if there is an overflow.
|
||||
|
||||
// Return the absolute value of the difference.
|
||||
PG_RETURN_INT32(ra);
|
||||
}
|
||||
|
||||
static float8 gbt_int4_dist(const void* a, const void* b)
|
||||
{
|
||||
return GET_FLOAT_DISTANCE(int4, a, b);
|
||||
}
|
||||
|
||||
static const gbtree_ninfo tinfo = {gbt_t_int4,
|
||||
sizeof(int32),
|
||||
gbt_int4gt,
|
||||
gbt_int4ge,
|
||||
gbt_int4eq,
|
||||
gbt_int4le,
|
||||
gbt_int4lt,
|
||||
gbt_int4key_cmp,
|
||||
gbt_int4_dist};
|
||||
|
||||
PG_FUNCTION_INFO_V1(int4_dist);
|
||||
extern "C" Datum int4_dist(PG_FUNCTION_ARGS);
|
||||
Datum int4_dist(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int4 a = PG_GETARG_INT32(0);
|
||||
int4 b = PG_GETARG_INT32(1);
|
||||
int4 r;
|
||||
int4 ra;
|
||||
|
||||
r = a - b;
|
||||
ra = Abs(r);
|
||||
|
||||
/* Overflow check. */
|
||||
if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
|
||||
ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range")));
|
||||
|
||||
PG_RETURN_INT32(ra);
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* int32 ops
|
||||
**************************************************/
|
||||
|
||||
Datum gbt_int4_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* retval = NULL;
|
||||
|
||||
PG_RETURN_POINTER(gbt_num_compress(retval, entry, &tinfo));
|
||||
// Define a function that compresses a GiST entry.
|
||||
Datum gbt_int4_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the GiST entry from the function arguments.
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* retval = NULL;
|
||||
|
||||
// Compress the GiST entry and return the result.
|
||||
PG_RETURN_POINTER(gbt_num_compress(retval, entry, &tinfo));
|
||||
}
|
||||
|
||||
// Define a function that checks if a GiST entry is consistent with a query value.
|
||||
Datum gbt_int4_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the GiST entry, query value, strategy number, and output buffer from the function arguments.
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
int32 query = PG_GETARG_INT32(1);
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
int32KEY* kkk = (int32KEY*)DatumGetPointer(entry->key);
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
// Set the recheck flag to false, indicating that all cases served by this function are exact.
|
||||
/* All cases served by this function are exact */
|
||||
*recheck = false;
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
// Check if the GiST entry is consistent with the query value and return the result.
|
||||
PG_RETURN_BOOL(gbt_num_consistent(&key, (void*)&query, &strategy, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_int4_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
int32 query = PG_GETARG_INT32(1);
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
int32KEY* kkk = (int32KEY*)DatumGetPointer(entry->key);
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
/* All cases served by this function are exact */
|
||||
*recheck = false;
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
PG_RETURN_BOOL(gbt_num_consistent(&key, (void*)&query, &strategy, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_int4_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
int32 query = PG_GETARG_INT32(1);
|
||||
|
||||
int32KEY* kkk = (int32KEY*)DatumGetPointer(entry->key);
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
PG_RETURN_FLOAT8(gbt_num_distance(&key, (void*)&query, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_int4_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
void* out = palloc(sizeof(int32KEY));
|
||||
|
||||
*(int*)PG_GETARG_POINTER(1) = sizeof(int32KEY);
|
||||
PG_RETURN_POINTER(gbt_num_union((GBT_NUMKEY*)out, entryvec, &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_int4_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int32KEY* origentry = (int32KEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(0))->key);
|
||||
int32KEY* newentry = (int32KEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(1))->key);
|
||||
float* result = (float*)PG_GETARG_POINTER(2);
|
||||
|
||||
penalty_num(result, origentry->lower, origentry->upper, newentry->lower, newentry->upper);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
Datum gbt_int4_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_POINTER(
|
||||
gbt_num_picksplit((GistEntryVector*)PG_GETARG_POINTER(0), (GIST_SPLITVEC*)PG_GETARG_POINTER(1), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_int4_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int32KEY* b1 = (int32KEY*)PG_GETARG_POINTER(0);
|
||||
int32KEY* b2 = (int32KEY*)PG_GETARG_POINTER(1);
|
||||
bool* result = (bool*)PG_GETARG_POINTER(2);
|
||||
|
||||
*result = gbt_num_same((GBT_NUMKEY*)b1, (GBT_NUMKEY*)b2, &tinfo);
|
||||
PG_RETURN_POINTER(result);
|
||||
// Define a function that calculates the distance between two GiST entries.
|
||||
Datum gbt_int4_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the GiST entry and query value from the function arguments.
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
int32 query = PG_GETARG_INT32(1);
|
||||
|
||||
// Extract the lower and upper bounds from the GiST entry.
|
||||
int32KEY* kkk = (int32KEY*)DatumGetPointer(entry->key);
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
// Calculate and return the distance between the GiST entry and the query value.
|
||||
PG_RETURN_FLOAT8(gbt_num_distance(&key, (void*)&query, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
// Define a function that calculates the union of a set of GiST entries.
|
||||
Datum gbt_int4_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the vector of GiST entries and output buffer from the function arguments.
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
void* out = palloc(sizeof(int32KEY));
|
||||
|
||||
// Set the size of the output buffer.
|
||||
*(int*)PG_GETARG_POINTER(1) = sizeof(int32KEY);
|
||||
|
||||
// Calculate and return the union of the set of GiST entries.
|
||||
PG_RETURN_POINTER(gbt_num_union((GBT_NUMKEY*)out, entryvec, &tinfo));
|
||||
}
|
||||
|
||||
// Define a function that calculates the penalty for splitting a set of GiST entries.
|
||||
Datum gbt_int4_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the original and new GiST entries, as well as the output buffer, from the function arguments.
|
||||
int32KEY* origentry = (int32KEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(0))->key);
|
||||
int32KEY* newentry = (int32KEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(1))->key);
|
||||
float* result = (float*)PG_GETARG_POINTER(2);
|
||||
|
||||
// Calculate the penalty for splitting the set of GiST entries.
|
||||
penalty_num(result, origentry->lower, origentry->upper, newentry->lower, newentry->upper);
|
||||
|
||||
// Return the calculated penalty.
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
// Define a function that selects which GiST entries to split.
|
||||
Datum gbt_int4_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Select which GiST entries to split and return the result.
|
||||
PG_RETURN_POINTER(
|
||||
gbt_num_picksplit((GistEntryVector*)PG_GETARG_POINTER(0), (GIST_SPLITVEC*)PG_GETARG_POINTER(1), &tinfo));
|
||||
}
|
||||
|
||||
// Define a function that checks if two GiST entries are equal.
|
||||
Datum gbt_int4_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the two GiST entries and output buffer from the function arguments.
|
||||
int32KEY* b1 = (int32KEY*)PG_GETARG_POINTER(0);
|
||||
int32KEY* b2 = (int32KEY*)PG_GETARG_POINTER(1);
|
||||
bool* result = (bool*)PG_GETARG_POINTER(2);
|
||||
|
||||
// Check if the two GiST entries are equal and store the result in the output buffer.
|
||||
*result = gbt_num_same((GBT_NUMKEY*)b1, (GBT_NUMKEY*)b2, &tinfo);
|
||||
|
||||
// Return the output buffer.
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,110 +1,130 @@
|
|||
/*
|
||||
* contrib/btree_gist/btree_int8.c
|
||||
*/
|
||||
#include "postgres.h"
|
||||
#include "knl/knl_variable.h"
|
||||
|
||||
#include "btree_gist.h"
|
||||
#include "btree_utils_num.h"
|
||||
|
||||
typedef struct int64key {
|
||||
int64 lower;
|
||||
int64 upper;
|
||||
} int64KEY;
|
||||
|
||||
/*
|
||||
** int64 ops
|
||||
*/
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_compress);
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_union);
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_picksplit);
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_consistent);
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_distance);
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_penalty);
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_same);
|
||||
|
||||
extern "C" Datum gbt_int8_compress(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_int8_union(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_int8_picksplit(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_int8_consistent(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_int8_distance(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_int8_penalty(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_int8_same(PG_FUNCTION_ARGS);
|
||||
|
||||
static bool gbt_int8gt(const void* a, const void* b)
|
||||
{
|
||||
return (*((const int64*)a) > *((const int64*)b));
|
||||
}
|
||||
static bool gbt_int8ge(const void* a, const void* b)
|
||||
{
|
||||
return (*((const int64*)a) >= *((const int64*)b));
|
||||
}
|
||||
static bool gbt_int8eq(const void* a, const void* b)
|
||||
{
|
||||
return (*((const int64*)a) == *((const int64*)b));
|
||||
}
|
||||
static bool gbt_int8le(const void* a, const void* b)
|
||||
{
|
||||
return (*((const int64*)a) <= *((const int64*)b));
|
||||
}
|
||||
static bool gbt_int8lt(const void* a, const void* b)
|
||||
{
|
||||
return (*((const int64*)a) < *((const int64*)b));
|
||||
// Include the PostgreSQL header file, which contains all the basic PostgreSQL data types and function definitions.
|
||||
#include "postgres.h"
|
||||
// Include the knl/knl_variable.h header file, which is part of the PostgreSQL kernel and contains the definitions of some internal variables and functions.
|
||||
#include "knl/knl_variable.h"
|
||||
|
||||
// Include the btree_gist.h header file, which defines the B-tree structure used to implement the GiST index.
|
||||
#include "btree_gist.h"
|
||||
// Include the btree_utils_num.h header file, which contains some utility functions for operating on B-trees.
|
||||
#include "btree_utils_num.h"
|
||||
|
||||
// Define a structure int64key for storing the lower and upper bounds of a 64-bit integer.
|
||||
typedef struct int64key {
|
||||
// The lower bound of the 64-bit integer.
|
||||
int64 lower;
|
||||
// The upper bound of the 64-bit integer.
|
||||
int64 upper;
|
||||
} int64KEY;
|
||||
|
||||
/*
|
||||
** int64 ops
|
||||
*/
|
||||
// Define a series of function info macros, which are used to register functions in PostgreSQL.
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_compress); // Compression function.
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_union); // Merging function.
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_picksplit); // Splitting function.
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_consistent);// Consistency check function.
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_distance); // Distance function.
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_penalty); // Penalty function.
|
||||
PG_FUNCTION_INFO_V1(gbt_int8_same); // Sameness check function.
|
||||
|
||||
// Define a series of function prototypes, which are used to implement different operations on the GiST index.
|
||||
extern "C" Datum gbt_int8_compress(PG_FUNCTION_ARGS); // Compression function.
|
||||
extern "C" Datum gbt_int8_union(PG_FUNCTION_ARGS); // Merging function.
|
||||
extern "C" Datum gbt_int8_picksplit(PG_FUNCTION_ARGS); // Splitting function.
|
||||
extern "C" Datum gbt_int8_consistent(PG_FUNCTION_ARGS);// Consistency check function.
|
||||
extern "C" Datum gbt_int8_distance(PG_FUNCTION_ARGS); // Distance function.
|
||||
extern "C" Datum gbt_int8_penalty(PG_FUNCTION_ARGS); // Penalty function.
|
||||
extern "C" Datum gbt_int8_same(PG_FUNCTION_ARGS); // Sameness check function.
|
||||
|
||||
// Define a series of comparison functions, which are used to compare the sizes of two 64-bit integers.
|
||||
static bool gbt_int8gt(const void* a, const void* b) // Greater than comparison.
|
||||
{
|
||||
return (*((const int64*)a) > *((const int64*)b));
|
||||
}
|
||||
static bool gbt_int8ge(const void* a, const void* b) // Greater than or equal to comparison.
|
||||
{
|
||||
return (*((const int64*)a) >= *((const int64*)b));
|
||||
}
|
||||
static bool gbt_int8eq(const void* a, const void* b) // Equal to comparison.
|
||||
{
|
||||
return (*((const int64*)a) == *((const int64*)b));
|
||||
}
|
||||
static bool gbt_int8le(const void* a, const void* b) // Less than or equal to comparison.
|
||||
{
|
||||
return (*((const int64*)a) <= *((const int64*)b));
|
||||
}
|
||||
static bool gbt_int8lt(const void* a, const void* b) // Less than comparison.
|
||||
{
|
||||
return (*((const int64*)a) < *((const int64*)b));
|
||||
}
|
||||
|
||||
static int gbt_int8key_cmp(const void* a, const void* b)
|
||||
{
|
||||
int64KEY* ia = (int64KEY*)(((const Nsrt*)a)->t);
|
||||
int64KEY* ib = (int64KEY*)(((const Nsrt*)b)->t);
|
||||
|
||||
if (ia->lower == ib->lower) {
|
||||
if (ia->upper == ib->upper)
|
||||
return 0;
|
||||
|
||||
return (ia->upper > ib->upper) ? 1 : -1;
|
||||
}
|
||||
|
||||
return (ia->lower > ib->lower) ? 1 : -1;
|
||||
// Define a function to compare two 64-bit integers.
|
||||
static int gbt_int8key_cmp(const void* a, const void* b)
|
||||
{
|
||||
// Cast the input pointers to their original types.
|
||||
int64KEY* ia = (int64KEY*)(((const Nsrt*)a)->t);
|
||||
int64KEY* ib = (int64KEY*)(((const Nsrt*)b)->t);
|
||||
|
||||
// Compare the lower bounds first. If they are equal, compare the upper bounds.
|
||||
if (ia->lower == ib->lower) {
|
||||
if (ia->upper == ib->upper)
|
||||
return 0; // Return 0 if both are equal.
|
||||
|
||||
return (ia->upper > ib->upper) ? 1 : -1; // Return 1 if ia->upper is greater than ib->upper, else return -1.
|
||||
}
|
||||
|
||||
return (ia->lower > ib->lower) ? 1 : -1; // Return 1 if ia->lower is greater than ib->lower, else return -1.
|
||||
}
|
||||
|
||||
// Define a function to calculate the distance between two 64-bit integers.
|
||||
static float8 gbt_int8_dist(const void* a, const void* b)
|
||||
{
|
||||
// Use the macro GET_FLOAT_DISTANCE to calculate the distance.
|
||||
return GET_FLOAT_DISTANCE(int64, a, b);
|
||||
}
|
||||
|
||||
// Define a structure to store the information about the GiST index.
|
||||
static const gbtree_ninfo tinfo = {gbt_t_int8,
|
||||
sizeof(int64),
|
||||
gbt_int8gt,
|
||||
gbt_int8ge,
|
||||
gbt_int8eq,
|
||||
gbt_int8le,
|
||||
gbt_int8lt,
|
||||
gbt_int8key_cmp,
|
||||
gbt_int8_dist};
|
||||
|
||||
// Register the int8_dist function in PostgreSQL.
|
||||
PG_FUNCTION_INFO_V1(int8_dist);
|
||||
extern "C" Datum int8_dist(PG_FUNCTION_ARGS);
|
||||
Datum int8_dist(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the input arguments.
|
||||
int64 a = PG_GETARG_INT64(0);
|
||||
int64 b = PG_GETARG_INT64(1);
|
||||
int64 r;
|
||||
int64 ra;
|
||||
|
||||
// Calculate the difference between a and b.
|
||||
r = a - b;
|
||||
ra = Abs(r); // Calculate the absolute value of r.
|
||||
|
||||
/* Check for overflow. */
|
||||
if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
|
||||
ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("bigint out of range"))); // Report an error if there is an overflow.
|
||||
|
||||
// Return the absolute value of the difference.
|
||||
PG_RETURN_INT64(ra);
|
||||
}
|
||||
|
||||
static float8 gbt_int8_dist(const void* a, const void* b)
|
||||
{
|
||||
return GET_FLOAT_DISTANCE(int64, a, b);
|
||||
}
|
||||
|
||||
static const gbtree_ninfo tinfo = {gbt_t_int8,
|
||||
sizeof(int64),
|
||||
gbt_int8gt,
|
||||
gbt_int8ge,
|
||||
gbt_int8eq,
|
||||
gbt_int8le,
|
||||
gbt_int8lt,
|
||||
gbt_int8key_cmp,
|
||||
gbt_int8_dist};
|
||||
|
||||
PG_FUNCTION_INFO_V1(int8_dist);
|
||||
extern "C" Datum int8_dist(PG_FUNCTION_ARGS);
|
||||
Datum int8_dist(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int64 a = PG_GETARG_INT64(0);
|
||||
int64 b = PG_GETARG_INT64(1);
|
||||
int64 r;
|
||||
int64 ra;
|
||||
|
||||
r = a - b;
|
||||
ra = Abs(r);
|
||||
|
||||
/* Overflow check. */
|
||||
if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
|
||||
ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("bigint out of range")));
|
||||
|
||||
PG_RETURN_INT64(ra);
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* int64 ops
|
||||
**************************************************/
|
||||
|
||||
/* gbt_int8_compress - compresses a GIST entry containing an int8 key */
|
||||
Datum gbt_int8_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
|
|
@ -113,6 +133,7 @@ Datum gbt_int8_compress(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_POINTER(gbt_num_compress(retval, entry, &tinfo));
|
||||
}
|
||||
|
||||
/* gbt_int8_consistent - checks if a query is consistent with an int8 key */
|
||||
Datum gbt_int8_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
|
|
@ -132,6 +153,7 @@ Datum gbt_int8_consistent(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_BOOL(gbt_num_consistent(&key, (void*)&query, &strategy, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
/* gbt_int8_distance - calculates the distance between an int8 key and a query */
|
||||
Datum gbt_int8_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
|
|
@ -146,6 +168,7 @@ Datum gbt_int8_distance(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_FLOAT8(gbt_num_distance(&key, (void*)&query, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
/* gbt_int8_union - performs a union on a vector of int8 keys */
|
||||
Datum gbt_int8_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
|
|
@ -155,6 +178,7 @@ Datum gbt_int8_union(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_POINTER(gbt_num_union((GBT_NUMKEY*)out, entryvec, &tinfo));
|
||||
}
|
||||
|
||||
/* gbt_int8_penalty - calculates the penalty for splitting an int8 key */
|
||||
Datum gbt_int8_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int64KEY* origentry = (int64KEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(0))->key);
|
||||
|
|
@ -166,12 +190,14 @@ Datum gbt_int8_penalty(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
/* gbt_int8_picksplit - performs a picksplit on a GIST entry vector */
|
||||
Datum gbt_int8_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_POINTER(
|
||||
gbt_num_picksplit((GistEntryVector*)PG_GETARG_POINTER(0), (GIST_SPLITVEC*)PG_GETARG_POINTER(1), &tinfo));
|
||||
}
|
||||
|
||||
/* gbt_int8_same - checks if two int8 keys are the same */
|
||||
Datum gbt_int8_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int64KEY* b1 = (int64KEY*)PG_GETARG_POINTER(0);
|
||||
|
|
@ -180,4 +206,4 @@ Datum gbt_int8_same(PG_FUNCTION_ARGS)
|
|||
|
||||
*result = gbt_num_same((GBT_NUMKEY*)b1, (GBT_NUMKEY*)b2, &tinfo);
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -31,134 +31,169 @@ extern "C" Datum gbt_macad_consistent(PG_FUNCTION_ARGS);
|
|||
extern "C" Datum gbt_macad_penalty(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_macad_same(PG_FUNCTION_ARGS);
|
||||
|
||||
static bool gbt_macadgt(const void* a, const void* b)
|
||||
{
|
||||
return DatumGetBool(DirectFunctionCall2(macaddr_gt, PointerGetDatum(a), PointerGetDatum(b)));
|
||||
}
|
||||
static bool gbt_macadge(const void* a, const void* b)
|
||||
{
|
||||
return DatumGetBool(DirectFunctionCall2(macaddr_ge, PointerGetDatum(a), PointerGetDatum(b)));
|
||||
// Define a function to compare two macaddr values.
|
||||
static bool gbt_macadgt(const void* a, const void* b)
|
||||
{
|
||||
// Use the macaddr_gt function to compare the input pointers and return the result as a boolean value.
|
||||
return DatumGetBool(DirectFunctionCall2(macaddr_gt, PointerGetDatum(a), PointerGetDatum(b)));
|
||||
}
|
||||
|
||||
// Define a function to compare two macaddr values.
|
||||
static bool gbt_macadge(const void* a, const void* b)
|
||||
{
|
||||
// Use the macaddr_ge function to compare the input pointers and return the result as a boolean value.
|
||||
return DatumGetBool(DirectFunctionCall2(macaddr_ge, PointerGetDatum(a), PointerGetDatum(b)));
|
||||
}
|
||||
|
||||
// Define a function to compare two macaddr values.
|
||||
static bool gbt_macadeq(const void* a, const void* b)
|
||||
{
|
||||
// Use the macaddr_eq function to compare the input pointers and return the result as a boolean value.
|
||||
return DatumGetBool(DirectFunctionCall2(macaddr_eq, PointerGetDatum(a), PointerGetDatum(b)));
|
||||
}
|
||||
|
||||
// Define a function to compare two macaddr values.
|
||||
static bool gbt_macadle(const void* a, const void* b)
|
||||
{
|
||||
// Use the macaddr_le function to compare the input pointers and return the result as a boolean value.
|
||||
return DatumGetBool(DirectFunctionCall2(macaddr_le, PointerGetDatum(a), PointerGetDatum(b)));
|
||||
}
|
||||
|
||||
// Define a function to compare two macaddr values.
|
||||
static bool gbt_macadlt(const void* a, const void* b)
|
||||
{
|
||||
// Use the macaddr_lt function to compare the input pointers and return the result as a boolean value.
|
||||
return DatumGetBool(DirectFunctionCall2(macaddr_lt, PointerGetDatum(a), PointerGetDatum(b)));
|
||||
}
|
||||
|
||||
// Define a function to compare two macaddr keys.
|
||||
static int gbt_macadkey_cmp(const void* a, const void* b)
|
||||
{
|
||||
// Cast the input pointers to their original types.
|
||||
macKEY* ia = (macKEY*)(((const Nsrt*)a)->t);
|
||||
macKEY* ib = (macKEY*)(((const Nsrt*)b)->t);
|
||||
int res;
|
||||
|
||||
// Compare the lower bounds first. If they are equal, compare the upper bounds.
|
||||
res = DatumGetInt32(DirectFunctionCall2(macaddr_cmp, MacaddrPGetDatum(&ia->lower), MacaddrPGetDatum(&ib->lower)));
|
||||
if (res == 0)
|
||||
return DatumGetInt32(DirectFunctionCall2(macaddr_cmp, MacaddrPGetDatum(&ia->upper), MacaddrPGetDatum(&ib->upper)));
|
||||
|
||||
return res; // Return the result of the comparison.
|
||||
}
|
||||
|
||||
static bool gbt_macadeq(const void* a, const void* b)
|
||||
{
|
||||
return DatumGetBool(DirectFunctionCall2(macaddr_eq, PointerGetDatum(a), PointerGetDatum(b)));
|
||||
// Define a structure to store the information about the GiST index.
|
||||
static const gbtree_ninfo tinfo = {gbt_t_macad,
|
||||
sizeof(macaddr),
|
||||
gbt_macadgt,
|
||||
gbt_macadge,
|
||||
gbt_macadeq,
|
||||
gbt_macadle,
|
||||
gbt_macadlt,
|
||||
gbt_macadkey_cmp,
|
||||
NULL};
|
||||
|
||||
/**************************************************
|
||||
* macaddr ops
|
||||
**************************************************/
|
||||
|
||||
// Define a function to convert a macaddr to a uint64.
|
||||
static uint64 mac_2_uint64(macaddr* m)
|
||||
{
|
||||
unsigned char* mi = (unsigned char*)m;
|
||||
uint64 res = 0;
|
||||
int i;
|
||||
|
||||
// Loop through each byte of the macaddr and shift it to the correct position.
|
||||
for (i = 0; i < 6; i++)
|
||||
res += (((uint64)mi[i]) << ((uint64)((5 - i) * 8)));
|
||||
return res;
|
||||
}
|
||||
|
||||
// Define a function to compress a GiST entry.
|
||||
Datum gbt_macad_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* retval = NULL;
|
||||
|
||||
// Use the gbt_num_compress function to compress the entry and return it.
|
||||
PG_RETURN_POINTER(gbt_num_compress(retval, entry, &tinfo));
|
||||
}
|
||||
|
||||
// Define a function to check the consistency of a GiST entry.
|
||||
Datum gbt_macad_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
macaddr* query = (macaddr*)PG_GETARG_POINTER(1);
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
macKEY* kkk = (macKEY*)DatumGetPointer(entry->key);
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
// All cases served by this function are exact, so set *recheck to false.
|
||||
*recheck = false;
|
||||
|
||||
// Set the lower and upper bounds of the key.
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
// Use the gbt_num_consistent function to check the consistency of the entry and return the result.
|
||||
PG_RETURN_BOOL(gbt_num_consistent(&key, (void*)query, &strategy, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
static bool gbt_macadle(const void* a, const void* b)
|
||||
{
|
||||
return DatumGetBool(DirectFunctionCall2(macaddr_le, PointerGetDatum(a), PointerGetDatum(b)));
|
||||
}
|
||||
|
||||
static bool gbt_macadlt(const void* a, const void* b)
|
||||
{
|
||||
return DatumGetBool(DirectFunctionCall2(macaddr_lt, PointerGetDatum(a), PointerGetDatum(b)));
|
||||
}
|
||||
|
||||
static int gbt_macadkey_cmp(const void* a, const void* b)
|
||||
{
|
||||
macKEY* ia = (macKEY*)(((const Nsrt*)a)->t);
|
||||
macKEY* ib = (macKEY*)(((const Nsrt*)b)->t);
|
||||
int res;
|
||||
|
||||
res = DatumGetInt32(DirectFunctionCall2(macaddr_cmp, MacaddrPGetDatum(&ia->lower), MacaddrPGetDatum(&ib->lower)));
|
||||
if (res == 0)
|
||||
return DatumGetInt32(
|
||||
DirectFunctionCall2(macaddr_cmp, MacaddrPGetDatum(&ia->upper), MacaddrPGetDatum(&ib->upper)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static const gbtree_ninfo tinfo = {gbt_t_macad,
|
||||
sizeof(macaddr),
|
||||
gbt_macadgt,
|
||||
gbt_macadge,
|
||||
gbt_macadeq,
|
||||
gbt_macadle,
|
||||
gbt_macadlt,
|
||||
gbt_macadkey_cmp,
|
||||
NULL};
|
||||
|
||||
/**************************************************
|
||||
* macaddr ops
|
||||
**************************************************/
|
||||
|
||||
static uint64 mac_2_uint64(macaddr* m)
|
||||
{
|
||||
unsigned char* mi = (unsigned char*)m;
|
||||
uint64 res = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
res += (((uint64)mi[i]) << ((uint64)((5 - i) * 8)));
|
||||
return res;
|
||||
}
|
||||
|
||||
Datum gbt_macad_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* retval = NULL;
|
||||
|
||||
PG_RETURN_POINTER(gbt_num_compress(retval, entry, &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_macad_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
macaddr* query = (macaddr*)PG_GETARG_POINTER(1);
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
macKEY* kkk = (macKEY*)DatumGetPointer(entry->key);
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
/* All cases served by this function are exact */
|
||||
*recheck = false;
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
PG_RETURN_BOOL(gbt_num_consistent(&key, (void*)query, &strategy, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_macad_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
void* out = palloc(sizeof(macKEY));
|
||||
|
||||
*(int*)PG_GETARG_POINTER(1) = sizeof(macKEY);
|
||||
PG_RETURN_POINTER(gbt_num_union((GBT_NUMKEY*)out, entryvec, &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_macad_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
macKEY* origentry = (macKEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(0))->key);
|
||||
macKEY* newentry = (macKEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(1))->key);
|
||||
float* result = (float*)PG_GETARG_POINTER(2);
|
||||
uint64 iorg[2], inew[2];
|
||||
|
||||
iorg[0] = mac_2_uint64(&origentry->lower);
|
||||
iorg[1] = mac_2_uint64(&origentry->upper);
|
||||
inew[0] = mac_2_uint64(&newentry->lower);
|
||||
inew[1] = mac_2_uint64(&newentry->upper);
|
||||
|
||||
penalty_num(result, iorg[0], iorg[1], inew[0], inew[1]);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
Datum gbt_macad_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_POINTER(
|
||||
gbt_num_picksplit((GistEntryVector*)PG_GETARG_POINTER(0), (GIST_SPLITVEC*)PG_GETARG_POINTER(1), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_macad_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
macKEY* b1 = (macKEY*)PG_GETARG_POINTER(0);
|
||||
macKEY* b2 = (macKEY*)PG_GETARG_POINTER(1);
|
||||
bool* result = (bool*)PG_GETARG_POINTER(2);
|
||||
|
||||
*result = gbt_num_same((GBT_NUMKEY*)b1, (GBT_NUMKEY*)b2, &tinfo);
|
||||
PG_RETURN_POINTER(result);
|
||||
// Define a function to union GiST entries.
|
||||
Datum gbt_macad_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
void* out = palloc(sizeof(macKEY));
|
||||
|
||||
// Set the size of the output to the size of a macKEY.
|
||||
*(int*)PG_GETARG_POINTER(1) = sizeof(macKEY);
|
||||
|
||||
// Use the gbt_num_union function to union the entries and return the result.
|
||||
PG_RETURN_POINTER(gbt_num_union((GBT_NUMKEY*)out, entryvec, &tinfo));
|
||||
}
|
||||
|
||||
// Define a function to calculate the penalty of GiST entries.
|
||||
Datum gbt_macad_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
macKEY* origentry = (macKEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(0))->key);
|
||||
macKEY* newentry = (macKEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(1))->key);
|
||||
float* result = (float*)PG_GETARG_POINTER(2);
|
||||
uint64 iorg[2], inew[2];
|
||||
|
||||
// Convert the original and new entries to uint64.
|
||||
iorg[0] = mac_2_uint64(&origentry->lower);
|
||||
iorg[1] = mac_2_uint64(&origentry->upper);
|
||||
inew[0] = mac_2_uint64(&newentry->lower);
|
||||
inew[1] = mac_2_uint64(&newentry->upper);
|
||||
|
||||
// Calculate the penalty and store it in the result.
|
||||
penalty_num(result, iorg[0], iorg[1], inew[0], inew[1]);
|
||||
|
||||
// Return the result.
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
// Define a function to pick a split point for GiST entries.
|
||||
Datum gbt_macad_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Use the gbt_num_picksplit function to pick a split point and return the result.
|
||||
PG_RETURN_POINTER(
|
||||
gbt_num_picksplit((GistEntryVector*)PG_GETARG_POINTER(0), (GIST_SPLITVEC*)PG_GETARG_POINTER(1), &tinfo));
|
||||
}
|
||||
|
||||
// Define a function to check if two GiST entries are the same.
|
||||
Datum gbt_macad_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
macKEY* b1 = (macKEY*)PG_GETARG_POINTER(0);
|
||||
macKEY* b2 = (macKEY*)PG_GETARG_POINTER(1);
|
||||
bool* result = (bool*)PG_GETARG_POINTER(2);
|
||||
|
||||
// Use the gbt_num_same function to check if the two entries are the same and store the result.
|
||||
*result = gbt_num_same((GBT_NUMKEY*)b1, (GBT_NUMKEY*)b2, &tinfo);
|
||||
|
||||
// Return the result.
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,95 +87,159 @@ Datum gbt_numeric_compress(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_POINTER(gbt_var_compress(entry, &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_numeric_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
void* query = (void*)DatumGetNumeric(PG_GETARG_DATUM(1));
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
bool retval = false;
|
||||
GBT_VARKEY* key = (GBT_VARKEY*)DatumGetPointer(entry->key);
|
||||
GBT_VARKEY_R r = gbt_var_key_readable(key);
|
||||
|
||||
/* All cases served by this function are exact */
|
||||
*recheck = false;
|
||||
|
||||
retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(), GIST_LEAF(entry), &tinfo);
|
||||
PG_RETURN_BOOL(retval);
|
||||
// Function to check consistency of a numeric value with a GIST entry
|
||||
Datum gbt_numeric_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Pointer to the GIST entry
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
|
||||
// The numeric value to be checked
|
||||
void* query = (void*)DatumGetNumeric(PG_GETARG_DATUM(1));
|
||||
|
||||
// The strategy number representing the strategy to be used
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
// Pointer to a boolean variable that indicates whether a recheck is necessary
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
|
||||
// The return value, initialized to false
|
||||
bool retval = false;
|
||||
|
||||
// Pointer to the key of the GIST entry, converted to a GBT_VARKEY*
|
||||
GBT_VARKEY* key = (GBT_VARKEY*)DatumGetPointer(entry->key);
|
||||
|
||||
// The readable representation of the GBT_VARKEY
|
||||
GBT_VARKEY_R r = gbt_var_key_readable(key);
|
||||
|
||||
// All cases served by this function are exact, so set *recheck to false
|
||||
*recheck = false;
|
||||
|
||||
// Check consistency using the gbt_var_consistent function and store the result in retval
|
||||
retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(), GIST_LEAF(entry), &tinfo);
|
||||
|
||||
// Return the result of the consistency check as a boolean value
|
||||
PG_RETURN_BOOL(retval);
|
||||
}
|
||||
|
||||
Datum gbt_numeric_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
int32* size = (int*)PG_GETARG_POINTER(1);
|
||||
|
||||
PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(), &tinfo));
|
||||
// Function to perform a union operation on numeric values using GBT
|
||||
Datum gbt_numeric_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Pointer to the GistEntryVector containing the GIST entries
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
|
||||
// Pointer to an integer representing the size of the union
|
||||
int32* size = (int*)PG_GETARG_POINTER(1);
|
||||
|
||||
// Perform the union operation using gbt_var_union and return the result as a pointer
|
||||
PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_numeric_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum d1 = PG_GETARG_DATUM(0);
|
||||
Datum d2 = PG_GETARG_DATUM(1);
|
||||
bool* result = (bool*)PG_GETARG_POINTER(2);
|
||||
|
||||
*result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo);
|
||||
PG_RETURN_POINTER(result);
|
||||
// Function to check if two numeric values are the same using GBT
|
||||
Datum gbt_numeric_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// The first numeric value
|
||||
Datum d1 = PG_GETARG_DATUM(0);
|
||||
|
||||
// The second numeric value
|
||||
Datum d2 = PG_GETARG_DATUM(1);
|
||||
|
||||
// Pointer to a boolean variable that will store the result
|
||||
bool* result = (bool*)PG_GETARG_POINTER(2);
|
||||
|
||||
// Check if the two numeric values are the same using gbt_var_same and store the result in *result
|
||||
*result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo);
|
||||
|
||||
// Return the result as a pointer
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
Datum gbt_numeric_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* o = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* n = (GISTENTRY*)PG_GETARG_POINTER(1);
|
||||
float* result = (float*)PG_GETARG_POINTER(2);
|
||||
|
||||
Numeric us, os, ds;
|
||||
|
||||
GBT_VARKEY* org = (GBT_VARKEY*)DatumGetPointer(o->key);
|
||||
GBT_VARKEY* newe = (GBT_VARKEY*)DatumGetPointer(n->key);
|
||||
Datum uni;
|
||||
GBT_VARKEY_R rk, ok, uk;
|
||||
|
||||
rk = gbt_var_key_readable(org);
|
||||
uni = PointerGetDatum(gbt_var_key_copy(&rk, TRUE));
|
||||
gbt_var_bin_union(&uni, newe, PG_GET_COLLATION(), &tinfo);
|
||||
ok = gbt_var_key_readable(org);
|
||||
uk = gbt_var_key_readable((GBT_VARKEY*)DatumGetPointer(uni));
|
||||
|
||||
us = DatumGetNumeric(DirectFunctionCall2(numeric_sub, PointerGetDatum(uk.upper), PointerGetDatum(uk.lower)));
|
||||
|
||||
os = DatumGetNumeric(DirectFunctionCall2(numeric_sub, PointerGetDatum(ok.upper), PointerGetDatum(ok.lower)));
|
||||
|
||||
ds = DatumGetNumeric(DirectFunctionCall2(numeric_sub, NumericGetDatum(us), NumericGetDatum(os)));
|
||||
|
||||
if (numeric_is_nan(us)) {
|
||||
if (numeric_is_nan(os))
|
||||
*result = 0.0;
|
||||
else
|
||||
*result = 1.0;
|
||||
} else {
|
||||
Numeric nul = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(0)));
|
||||
|
||||
*result = 0.0;
|
||||
|
||||
if (DirectFunctionCall2(numeric_gt, NumericGetDatum(ds), NumericGetDatum(nul))) {
|
||||
*result += FLT_MIN;
|
||||
os = DatumGetNumeric(DirectFunctionCall2(numeric_div, NumericGetDatum(ds), NumericGetDatum(us)));
|
||||
*result += (float4)DatumGetFloat8(DirectFunctionCall1(numeric_float8_no_overflow, NumericGetDatum(os)));
|
||||
}
|
||||
}
|
||||
|
||||
if (*result > 0)
|
||||
*result *= (FLT_MAX / (((GISTENTRY*)PG_GETARG_POINTER(0))->rel->rd_att->natts + 1));
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
// Function to calculate the penalty for a numeric value using GBT
|
||||
Datum gbt_numeric_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Pointer to the original GIST entry
|
||||
GISTENTRY* o = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
|
||||
// Pointer to the new GIST entry
|
||||
GISTENTRY* n = (GISTENTRY*)PG_GETARG_POINTER(1);
|
||||
|
||||
// Pointer to a float variable that will store the result
|
||||
float* result = (float*)PG_GETARG_POINTER(2);
|
||||
|
||||
Numeric us, os, ds;
|
||||
|
||||
// Pointers to the keys of the original and new GIST entries
|
||||
GBT_VARKEY* org = (GBT_VARKEY*)DatumGetPointer(o->key);
|
||||
GBT_VARKEY* newe = (GBT_VARKEY*)DatumGetPointer(n->key);
|
||||
|
||||
// The union of the original and new keys
|
||||
Datum uni;
|
||||
GBT_VARKEY_R rk, ok, uk;
|
||||
|
||||
// Make the original key readable and store it in rk
|
||||
rk = gbt_var_key_readable(org);
|
||||
|
||||
// Create a copy of the original key and store it in uni
|
||||
uni = PointerGetDatum(gbt_var_key_copy(&rk, TRUE));
|
||||
|
||||
// Perform a union operation on the original and new keys and update uni
|
||||
gbt_var_bin_union(&uni, newe, PG_GET_COLLATION(), &tinfo);
|
||||
|
||||
// Make the original key readable and store it in ok
|
||||
ok = gbt_var_key_readable(org);
|
||||
|
||||
// Make the union key readable and store it in uk
|
||||
uk = gbt_var_key_readable((GBT_VARKEY*)DatumGetPointer(uni));
|
||||
|
||||
// Calculate the upper and lower bounds of the union and store them in us
|
||||
us = DatumGetNumeric(DirectFunctionCall2(numeric_sub, PointerGetDatum(uk.upper), PointerGetDatum(uk.lower)));
|
||||
|
||||
// Calculate the upper and lower bounds of the original key and store them in os
|
||||
os = DatumGetNumeric(DirectFunctionCall2(numeric_sub, PointerGetDatum(ok.upper), PointerGetDatum(ok.lower)));
|
||||
|
||||
// Calculate the difference between us and os and store it in ds
|
||||
ds = DatumGetNumeric(DirectFunctionCall2(numeric_sub, NumericGetDatum(us), NumericGetDatum(os)));
|
||||
|
||||
// Check if us is NaN (Not a Number)
|
||||
if (numeric_is_nan(us)) {
|
||||
// If os is also NaN, set the result to 0.0; otherwise, set it to 1.0
|
||||
if (numeric_is_nan(os))
|
||||
*result = 0.0;
|
||||
else
|
||||
*result = 1.0;
|
||||
} else {
|
||||
// Create a numeric value representing 0 and store it in nul
|
||||
Numeric nul = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(0)));
|
||||
|
||||
// Initialize the result to 0.0
|
||||
*result = 0.0;
|
||||
|
||||
// Check if ds is greater than nul and, if so, update the result accordingly
|
||||
if (DirectFunctionCall2(numeric_gt, NumericGetDatum(ds), NumericGetDatum(nul))) {
|
||||
*result += FLT_MIN;
|
||||
os = DatumGetNumeric(DirectFunctionCall2(numeric_div, NumericGetDatum(ds), NumericGetDatum(us)));
|
||||
*result += (float4)DatumGetFloat8(DirectFunctionCall1(numeric_float8_no_overflow, NumericGetDatum(os)));
|
||||
}
|
||||
}
|
||||
|
||||
// If the result is greater than 0, scale it by a certain factor and return it as a pointer; otherwise, return it as-is
|
||||
if (*result > 0)
|
||||
*result *= (FLT_MAX / (((GISTENTRY*)PG_GETARG_POINTER(0))->rel->rd_att->natts + 1));
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
Datum gbt_numeric_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
GIST_SPLITVEC* v = (GIST_SPLITVEC*)PG_GETARG_POINTER(1);
|
||||
|
||||
gbt_var_picksplit(entryvec, v, PG_GET_COLLATION(), &tinfo);
|
||||
PG_RETURN_POINTER(v);
|
||||
// Function to perform a picksplit operation on numeric values using GBT
|
||||
Datum gbt_numeric_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Pointer to the GistEntryVector containing the GIST entries
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
|
||||
// Pointer to the GIST_SPLITVEC that will store the result of the picksplit operation
|
||||
GIST_SPLITVEC* v = (GIST_SPLITVEC*)PG_GETARG_POINTER(1);
|
||||
|
||||
// Perform the picksplit operation using gbt_var_picksplit and store the result in v
|
||||
gbt_var_picksplit(entryvec, v, PG_GET_COLLATION(), &tinfo);
|
||||
|
||||
// Return the result as a pointer
|
||||
PG_RETURN_POINTER(v);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,164 +15,199 @@ typedef struct {
|
|||
/*
|
||||
** OID ops
|
||||
*/
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_compress);
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_union);
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_picksplit);
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_consistent);
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_distance);
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_penalty);
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_same);
|
||||
|
||||
extern "C" Datum gbt_oid_compress(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_oid_union(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_oid_picksplit(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_oid_consistent(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_oid_distance(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_oid_penalty(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_oid_same(PG_FUNCTION_ARGS);
|
||||
|
||||
static bool gbt_oidgt(const void* a, const void* b)
|
||||
{
|
||||
return (*((const Oid*)a) > *((const Oid*)b));
|
||||
}
|
||||
static bool gbt_oidge(const void* a, const void* b)
|
||||
{
|
||||
return (*((const Oid*)a) >= *((const Oid*)b));
|
||||
}
|
||||
static bool gbt_oideq(const void* a, const void* b)
|
||||
{
|
||||
return (*((const Oid*)a) == *((const Oid*)b));
|
||||
}
|
||||
static bool gbt_oidle(const void* a, const void* b)
|
||||
{
|
||||
return (*((const Oid*)a) <= *((const Oid*)b));
|
||||
}
|
||||
static bool gbt_oidlt(const void* a, const void* b)
|
||||
{
|
||||
return (*((const Oid*)a) < *((const Oid*)b));
|
||||
// These macros define the function information for each of the GiST functions.
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_compress);
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_union);
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_picksplit);
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_consistent);
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_distance);
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_penalty);
|
||||
PG_FUNCTION_INFO_V1(gbt_oid_same);
|
||||
|
||||
// These functions are defined as extern "C" because they are called from C code.
|
||||
// Each function takes PG_FUNCTION_ARGS as parameters and returns a Datum.
|
||||
extern "C" Datum gbt_oid_compress(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_oid_union(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_oid_picksplit(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_oid_consistent(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_oid_distance(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_oid_penalty(PG_FUNCTION_ARGS);
|
||||
extern "C" Datum gbt_oid_same(PG_FUNCTION_ARGS);
|
||||
|
||||
// These functions are used to compare Oids. They take two void pointers and return a bool.
|
||||
static bool gbt_oidgt(const void* a, const void* b)
|
||||
{
|
||||
// Compare the Oids and return true if the first is greater than the second.
|
||||
return (*((const Oid*)a) > *((const Oid*)b));
|
||||
}
|
||||
static bool gbt_oidge(const void* a, const void* b)
|
||||
{
|
||||
// Compare the Oids and return true if the first is greater than or equal to the second.
|
||||
return (*((const Oid*)a) >= *((const Oid*)b));
|
||||
}
|
||||
static bool gbt_oideq(const void* a, const void* b)
|
||||
{
|
||||
// Compare the Oids and return true if they are equal.
|
||||
return (*((const Oid*)a) == *((const Oid*)b));
|
||||
}
|
||||
static bool gbt_oidle(const void* a, const void* b)
|
||||
{
|
||||
// Compare the Oids and return true if the first is less than or equal to the second.
|
||||
return (*((const Oid*)a) <= *((const Oid*)b));
|
||||
}
|
||||
static bool gbt_oidlt(const void* a, const void* b)
|
||||
{
|
||||
// Compare the Oids and return true if the first is less than the second.
|
||||
return (*((const Oid*)a) < *((const Oid*)b));
|
||||
}
|
||||
|
||||
static int gbt_oidkey_cmp(const void* a, const void* b)
|
||||
{
|
||||
oidKEY* ia = (oidKEY*)(((const Nsrt*)a)->t);
|
||||
oidKEY* ib = (oidKEY*)(((const Nsrt*)b)->t);
|
||||
|
||||
if (ia->lower == ib->lower) {
|
||||
if (ia->upper == ib->upper)
|
||||
return 0;
|
||||
|
||||
return (ia->upper > ib->upper) ? 1 : -1;
|
||||
}
|
||||
|
||||
return (ia->lower > ib->lower) ? 1 : -1;
|
||||
// Define a function to compare two oidKEYs.
|
||||
static int gbt_oidkey_cmp(const void* a, const void* b)
|
||||
{
|
||||
oidKEY* ia = (oidKEY*)(((const Nsrt*)a)->t);
|
||||
oidKEY* ib = (oidKEY*)(((const Nsrt*)b)->t);
|
||||
|
||||
// Compare the lower and upper bounds of the oidKEYs.
|
||||
if (ia->lower == ib->lower) {
|
||||
if (ia->upper == ib->upper)
|
||||
return 0;
|
||||
|
||||
return (ia->upper > ib->upper) ? 1 : -1;
|
||||
}
|
||||
|
||||
return (ia->lower > ib->lower) ? 1 : -1;
|
||||
}
|
||||
|
||||
// Define a function to calculate the distance between two Oids.
|
||||
static float8 gbt_oid_dist(const void* a, const void* b)
|
||||
{
|
||||
Oid aa = *(const Oid*)a;
|
||||
Oid bb = *(const Oid*)b;
|
||||
|
||||
// Calculate the distance between the two Oids.
|
||||
if (aa < bb)
|
||||
return (float8)(bb - aa);
|
||||
else
|
||||
return (float8)(aa - bb);
|
||||
}
|
||||
|
||||
// Define the tree information for the GiST index.
|
||||
static const gbtree_ninfo tinfo = {
|
||||
gbt_t_oid, sizeof(Oid), gbt_oidgt, gbt_oidge, gbt_oideq, gbt_oidle, gbt_oidlt, gbt_oidkey_cmp, gbt_oid_dist};
|
||||
|
||||
// Define the function information for the oid_dist function.
|
||||
PG_FUNCTION_INFO_V1(oid_dist);
|
||||
extern "C" Datum oid_dist(PG_FUNCTION_ARGS);
|
||||
Datum oid_dist(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid a = PG_GETARG_OID(0);
|
||||
Oid b = PG_GETARG_OID(1);
|
||||
Oid res;
|
||||
|
||||
// Calculate the distance between the two Oids.
|
||||
if (a < b)
|
||||
res = b - a;
|
||||
else
|
||||
res = a - b;
|
||||
PG_RETURN_OID(res);
|
||||
}
|
||||
|
||||
static float8 gbt_oid_dist(const void* a, const void* b)
|
||||
{
|
||||
Oid aa = *(const Oid*)a;
|
||||
Oid bb = *(const Oid*)b;
|
||||
|
||||
if (aa < bb)
|
||||
return (float8)(bb - aa);
|
||||
else
|
||||
return (float8)(aa - bb);
|
||||
}
|
||||
|
||||
static const gbtree_ninfo tinfo = {
|
||||
gbt_t_oid, sizeof(Oid), gbt_oidgt, gbt_oidge, gbt_oideq, gbt_oidle, gbt_oidlt, gbt_oidkey_cmp, gbt_oid_dist};
|
||||
|
||||
PG_FUNCTION_INFO_V1(oid_dist);
|
||||
extern "C" Datum oid_dist(PG_FUNCTION_ARGS);
|
||||
Datum oid_dist(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid a = PG_GETARG_OID(0);
|
||||
Oid b = PG_GETARG_OID(1);
|
||||
Oid res;
|
||||
|
||||
if (a < b)
|
||||
res = b - a;
|
||||
else
|
||||
res = a - b;
|
||||
PG_RETURN_OID(res);
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* Oid ops
|
||||
**************************************************/
|
||||
|
||||
Datum gbt_oid_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* retval = NULL;
|
||||
|
||||
PG_RETURN_POINTER(gbt_num_compress(retval, entry, &tinfo));
|
||||
// Function to compress a GiST entry.
|
||||
// It takes a single argument of type GISTENTRY* and returns a pointer to the compressed entry.
|
||||
Datum gbt_oid_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* retval = NULL;
|
||||
|
||||
PG_RETURN_POINTER(gbt_num_compress(retval, entry, &tinfo));
|
||||
}
|
||||
|
||||
// Function to check if an Oid is consistent with a GiST entry.
|
||||
// It takes four arguments: a GISTENTRY*, an Oid, a StrategyNumber, and a bool*.
|
||||
// The function sets the bool* to false, indicating that all cases served by this function are exact.
|
||||
// It returns a bool indicating if the Oid is consistent with the GiST entry.
|
||||
Datum gbt_oid_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
Oid query = PG_GETARG_OID(1);
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
oidKEY* kkk = (oidKEY*)DatumGetPointer(entry->key);
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
/* All cases served by this function are exact */
|
||||
*recheck = false;
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
PG_RETURN_BOOL(gbt_num_consistent(&key, (void*)&query, &strategy, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
// Function to calculate the distance between an Oid and a GiST entry.
|
||||
// It takes two arguments: a GISTENTRY* and an Oid.
|
||||
// The function returns a float8 representing the distance between the Oid and the GiST entry.
|
||||
Datum gbt_oid_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
Oid query = PG_GETARG_OID(1);
|
||||
|
||||
oidKEY* kkk = (oidKEY*)DatumGetPointer(entry->key);
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
PG_RETURN_FLOAT8(gbt_num_distance(&key, (void*)&query, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_oid_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
Oid query = PG_GETARG_OID(1);
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
oidKEY* kkk = (oidKEY*)DatumGetPointer(entry->key);
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
/* All cases served by this function are exact */
|
||||
*recheck = false;
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
PG_RETURN_BOOL(gbt_num_consistent(&key, (void*)&query, &strategy, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_oid_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
Oid query = PG_GETARG_OID(1);
|
||||
|
||||
oidKEY* kkk = (oidKEY*)DatumGetPointer(entry->key);
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
PG_RETURN_FLOAT8(gbt_num_distance(&key, (void*)&query, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_oid_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
void* out = palloc(sizeof(oidKEY));
|
||||
|
||||
*(int*)PG_GETARG_POINTER(1) = sizeof(oidKEY);
|
||||
PG_RETURN_POINTER(gbt_num_union((GBT_NUMKEY*)out, entryvec, &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_oid_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
oidKEY* origentry = (oidKEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(0))->key);
|
||||
oidKEY* newentry = (oidKEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(1))->key);
|
||||
float* result = (float*)PG_GETARG_POINTER(2);
|
||||
|
||||
penalty_num(result, origentry->lower, origentry->upper, newentry->lower, newentry->upper);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
Datum gbt_oid_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_POINTER(
|
||||
gbt_num_picksplit((GistEntryVector*)PG_GETARG_POINTER(0), (GIST_SPLITVEC*)PG_GETARG_POINTER(1), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_oid_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
oidKEY* b1 = (oidKEY*)PG_GETARG_POINTER(0);
|
||||
oidKEY* b2 = (oidKEY*)PG_GETARG_POINTER(1);
|
||||
bool* result = (bool*)PG_GETARG_POINTER(2);
|
||||
|
||||
*result = gbt_num_same((GBT_NUMKEY*)b1, (GBT_NUMKEY*)b2, &tinfo);
|
||||
PG_RETURN_POINTER(result);
|
||||
// Function to union a GiST entry vector.
|
||||
// It takes a single argument of type GistEntryVector* and returns a pointer to the unioned entry.
|
||||
Datum gbt_oid_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
void* out = palloc(sizeof(oidKEY));
|
||||
|
||||
*(int*)PG_GETARG_POINTER(1) = sizeof(oidKEY);
|
||||
PG_RETURN_POINTER(gbt_num_union((GBT_NUMKEY*)out, entryvec, &tinfo));
|
||||
}
|
||||
|
||||
// Function to calculate the penalty of merging two GiST entries.
|
||||
// It takes two arguments of type oidKEY* and a float*.
|
||||
// The function calls penalty_num to calculate the penalty and returns a pointer to the result.
|
||||
Datum gbt_oid_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
oidKEY* origentry = (oidKEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(0))->key);
|
||||
oidKEY* newentry = (oidKEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(1))->key);
|
||||
float* result = (float*)PG_GETARG_POINTER(2);
|
||||
|
||||
penalty_num(result, origentry->lower, origentry->upper, newentry->lower, newentry->upper);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
// Function to pick a split point for a GiST entry vector.
|
||||
// It takes two arguments of type GistEntryVector* and GIST_SPLITVEC*.
|
||||
// The function returns a pointer to the split point.
|
||||
Datum gbt_oid_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_POINTER(
|
||||
gbt_num_picksplit((GistEntryVector*)PG_GETARG_POINTER(0), (GIST_SPLITVEC*)PG_GETARG_POINTER(1), &tinfo));
|
||||
}
|
||||
|
||||
// Function to check if two GiST entries are the same.
|
||||
// It takes two arguments of type oidKEY* and a bool*.
|
||||
// The function calls gbt_num_same to check if the entries are the same and returns a pointer to the result.
|
||||
Datum gbt_oid_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
oidKEY* b1 = (oidKEY*)PG_GETARG_POINTER(0);
|
||||
oidKEY* b2 = (oidKEY*)PG_GETARG_POINTER(1);
|
||||
bool* result = (bool*)PG_GETARG_POINTER(2);
|
||||
|
||||
*result = gbt_num_same((GBT_NUMKEY*)b1, (GBT_NUMKEY*)b2, &tinfo);
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,118 +68,158 @@ static gbtree_vinfo tinfo = {
|
|||
* Text ops
|
||||
**************************************************/
|
||||
|
||||
Datum gbt_text_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
|
||||
if (tinfo.eml == 0) {
|
||||
tinfo.eml = pg_database_encoding_max_length();
|
||||
}
|
||||
|
||||
PG_RETURN_POINTER(gbt_var_compress(entry, &tinfo));
|
||||
// Function to compress a GiST entry for text type.
|
||||
// It takes a single argument of type GISTENTRY* and returns a pointer to the compressed entry.
|
||||
// If the tinfo.eml is 0, it sets it to the maximum length of the database encoding.
|
||||
Datum gbt_text_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
|
||||
if (tinfo.eml == 0) {
|
||||
tinfo.eml = pg_database_encoding_max_length();
|
||||
}
|
||||
|
||||
PG_RETURN_POINTER(gbt_var_compress(entry, &tinfo));
|
||||
}
|
||||
|
||||
// Function to compress a GiST entry for bpchar type.
|
||||
// It takes a single argument of type GISTENTRY* and returns a pointer to the compressed entry.
|
||||
// If the tinfo.eml is 0, it sets it to the maximum length of the database encoding.
|
||||
// If the entry is a leafkey, it trims the key and compresses the trimmed entry. Otherwise, it returns the entry as it is.
|
||||
Datum gbt_bpchar_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* retval = NULL;
|
||||
|
||||
if (tinfo.eml == 0) {
|
||||
tinfo.eml = pg_database_encoding_max_length();
|
||||
}
|
||||
|
||||
if (entry->leafkey) {
|
||||
Datum d = DirectFunctionCall1(rtrim1, entry->key);
|
||||
GISTENTRY trim;
|
||||
|
||||
gistentryinit(trim, d, entry->rel, entry->page, entry->offset, TRUE);
|
||||
retval = gbt_var_compress(&trim, &tinfo);
|
||||
} else
|
||||
retval = entry;
|
||||
|
||||
PG_RETURN_POINTER(retval);
|
||||
}
|
||||
|
||||
// Function to check if a text is consistent with a GiST entry for text type.
|
||||
// It takes four arguments: a GISTENTRY*, a void* representing the text, a StrategyNumber, and a bool*.
|
||||
// The function sets the bool* to false, indicating that all cases served by this function are exact.
|
||||
// It returns a bool indicating if the text is consistent with the GiST entry.
|
||||
// If the tinfo.eml is 0, it sets it to the maximum length of the database encoding.
|
||||
Datum gbt_text_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
void* query = (void*)DatumGetTextP(PG_GETARG_DATUM(1));
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
bool retval = false;
|
||||
GBT_VARKEY* key = (GBT_VARKEY*)DatumGetPointer(entry->key);
|
||||
GBT_VARKEY_R r = gbt_var_key_readable(key);
|
||||
|
||||
/* All cases served by this function are exact */
|
||||
*recheck = false;
|
||||
|
||||
if (tinfo.eml == 0) {
|
||||
tinfo.eml = pg_database_encoding_max_length();
|
||||
}
|
||||
|
||||
retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(), GIST_LEAF(entry), &tinfo);
|
||||
|
||||
PG_RETURN_BOOL(retval);
|
||||
}
|
||||
|
||||
Datum gbt_bpchar_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* retval = NULL;
|
||||
|
||||
if (tinfo.eml == 0) {
|
||||
tinfo.eml = pg_database_encoding_max_length();
|
||||
}
|
||||
|
||||
if (entry->leafkey) {
|
||||
|
||||
Datum d = DirectFunctionCall1(rtrim1, entry->key);
|
||||
GISTENTRY trim;
|
||||
|
||||
gistentryinit(trim, d, entry->rel, entry->page, entry->offset, TRUE);
|
||||
retval = gbt_var_compress(&trim, &tinfo);
|
||||
} else
|
||||
retval = entry;
|
||||
|
||||
PG_RETURN_POINTER(retval);
|
||||
}
|
||||
|
||||
Datum gbt_text_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
void* query = (void*)DatumGetTextP(PG_GETARG_DATUM(1));
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
bool retval = false;
|
||||
GBT_VARKEY* key = (GBT_VARKEY*)DatumGetPointer(entry->key);
|
||||
GBT_VARKEY_R r = gbt_var_key_readable(key);
|
||||
|
||||
/* All cases served by this function are exact */
|
||||
*recheck = false;
|
||||
|
||||
if (tinfo.eml == 0) {
|
||||
tinfo.eml = pg_database_encoding_max_length();
|
||||
}
|
||||
|
||||
retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(), GIST_LEAF(entry), &tinfo);
|
||||
|
||||
PG_RETURN_BOOL(retval);
|
||||
}
|
||||
|
||||
Datum gbt_bpchar_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
void* query = (void*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(1)));
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
bool retval = false;
|
||||
GBT_VARKEY* key = (GBT_VARKEY*)DatumGetPointer(entry->key);
|
||||
GBT_VARKEY_R r = gbt_var_key_readable(key);
|
||||
void* trim = (void*)DatumGetPointer(DirectFunctionCall1(rtrim1, PointerGetDatum(query)));
|
||||
|
||||
/* All cases served by this function are exact */
|
||||
*recheck = false;
|
||||
|
||||
if (tinfo.eml == 0) {
|
||||
tinfo.eml = pg_database_encoding_max_length();
|
||||
}
|
||||
|
||||
retval = gbt_var_consistent(&r, trim, strategy, PG_GET_COLLATION(), GIST_LEAF(entry), &tinfo);
|
||||
PG_RETURN_BOOL(retval);
|
||||
}
|
||||
|
||||
Datum gbt_text_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
int32* size = (int*)PG_GETARG_POINTER(1);
|
||||
|
||||
PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_text_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
GIST_SPLITVEC* v = (GIST_SPLITVEC*)PG_GETARG_POINTER(1);
|
||||
|
||||
gbt_var_picksplit(entryvec, v, PG_GET_COLLATION(), &tinfo);
|
||||
PG_RETURN_POINTER(v);
|
||||
}
|
||||
|
||||
Datum gbt_text_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum d1 = PG_GETARG_DATUM(0);
|
||||
Datum d2 = PG_GETARG_DATUM(1);
|
||||
bool* result = (bool*)PG_GETARG_POINTER(2);
|
||||
|
||||
*result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo);
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
Datum gbt_text_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* o = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* n = (GISTENTRY*)PG_GETARG_POINTER(1);
|
||||
float* result = (float*)PG_GETARG_POINTER(2);
|
||||
|
||||
PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), &tinfo));
|
||||
// Check if a bpchar is consistent with a GiST entry for bpchar type.
|
||||
Datum gbt_bpchar_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the GiST entry.
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
// Get the query.
|
||||
void* query = (void*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(1)));
|
||||
// Get the strategy.
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
// Get the recheck boolean.
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
// Initialize the return value.
|
||||
bool retval = false;
|
||||
// Get the key.
|
||||
GBT_VARKEY* key = (GBT_VARKEY*)DatumGetPointer(entry->key);
|
||||
// Make the key readable.
|
||||
GBT_VARKEY_R r = gbt_var_key_readable(key);
|
||||
// Trim the query.
|
||||
void* trim = (void*)DatumGetPointer(DirectFunctionCall1(rtrim1, PointerGetDatum(query)));
|
||||
|
||||
// All cases served by this function are exact.
|
||||
*recheck = false;
|
||||
|
||||
// If the tinfo.eml is 0, set it to the maximum length of the database encoding.
|
||||
if (tinfo.eml == 0) {
|
||||
tinfo.eml = pg_database_encoding_max_length();
|
||||
}
|
||||
|
||||
// Check if the query is consistent with the GiST entry.
|
||||
retval = gbt_var_consistent(&r, trim, strategy, PG_GET_COLLATION(), GIST_LEAF(entry), &tinfo);
|
||||
// Return the result.
|
||||
PG_RETURN_BOOL(retval);
|
||||
}
|
||||
|
||||
// Union GiST entries for text type.
|
||||
Datum gbt_text_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the GiST entry vector.
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
// Get the size.
|
||||
int32* size = (int*)PG_GETARG_POINTER(1);
|
||||
|
||||
// Union the entries and return the result.
|
||||
PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(), &tinfo));
|
||||
}
|
||||
|
||||
// Split GiST entries for text type.
|
||||
Datum gbt_text_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the GiST entry vector.
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
// Get the split vector.
|
||||
GIST_SPLITVEC* v = (GIST_SPLITVEC*)PG_GETARG_POINTER(1);
|
||||
|
||||
// Split the entries.
|
||||
gbt_var_picksplit(entryvec, v, PG_GET_COLLATION(), &tinfo);
|
||||
// Return the split vector.
|
||||
PG_RETURN_POINTER(v);
|
||||
}
|
||||
|
||||
// Check if two text values are the same.
|
||||
Datum gbt_text_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the two text values.
|
||||
Datum d1 = PG_GETARG_DATUM(0);
|
||||
Datum d2 = PG_GETARG_DATUM(1);
|
||||
// Get the result boolean.
|
||||
bool* result = (bool*)PG_GETARG_POINTER(2);
|
||||
|
||||
// Check if the two text values are the same.
|
||||
*result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo);
|
||||
// Return the result.
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
// Calculate the penalty for GiST entries for text type.
|
||||
Datum gbt_text_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the two GiST entries.
|
||||
GISTENTRY* o = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* n = (GISTENTRY*)PG_GETARG_POINTER(1);
|
||||
// Get the result float.
|
||||
float* result = (float*)PG_GETARG_POINTER(2);
|
||||
|
||||
// Calculate the penalty
|
||||
PG_RETURN_POINTER(gbt_var_penalty(result,o,n,PG_GET_COLLATION(),&tinfo));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,133 +43,180 @@ extern "C" Datum gbt_time_same(PG_FUNCTION_ARGS);
|
|||
#define TimeADTGetDatumFast(X) PointerGetDatum(&(X))
|
||||
#endif
|
||||
|
||||
static bool gbt_timegt(const void* a, const void* b)
|
||||
{
|
||||
const TimeADT* aa = (const TimeADT*)a;
|
||||
const TimeADT* bb = (const TimeADT*)b;
|
||||
|
||||
return DatumGetBool(DirectFunctionCall2(time_gt, TimeADTGetDatumFast(*aa), TimeADTGetDatumFast(*bb)));
|
||||
// Compare if time a is greater than time b
|
||||
static bool gbt_timegt(const void* a, const void* b)
|
||||
{
|
||||
const TimeADT* aa = (const TimeADT*)a; // Cast void pointer a to TimeADT pointer
|
||||
const TimeADT* bb = (const TimeADT*)b; // Cast void pointer b to TimeADT pointer
|
||||
|
||||
// Use the function time_gt to compare the two time objects, and return the result as a boolean value
|
||||
return DatumGetBool(DirectFunctionCall2(time_gt, TimeADTGetDatumFast(*aa), TimeADTGetDatumFast(*bb)));
|
||||
}
|
||||
|
||||
// Compare if time a is greater than or equal to time b
|
||||
static bool gbt_timege(const void* a, const void* b)
|
||||
{
|
||||
const TimeADT* aa = (const TimeADT*)a; // Cast void pointer a to TimeADT pointer
|
||||
const TimeADT* bb = (const TimeADT*)b; // Cast void pointer b to TimeADT pointer
|
||||
|
||||
// Use the function time_ge to compare the two time objects, and return the result as a boolean value
|
||||
return DatumGetBool(DirectFunctionCall2(time_ge, TimeADTGetDatumFast(*aa), TimeADTGetDatumFast(*bb)));
|
||||
}
|
||||
|
||||
// Compare if time a is equal to time b
|
||||
static bool gbt_timeeq(const void* a, const void* b)
|
||||
{
|
||||
const TimeADT* aa = (const TimeADT*)a; // Cast void pointer a to TimeADT pointer
|
||||
const TimeADT* bb = (const TimeADT*)b; // Cast void pointer b to TimeADT pointer
|
||||
|
||||
// Use the function time_eq to compare the two time objects, and return the result as a boolean value
|
||||
return DatumGetBool(DirectFunctionCall2(time_eq, TimeADTGetDatumFast(*aa), TimeADTGetDatumFast(*bb)));
|
||||
}
|
||||
|
||||
// Compare if time a is less than or equal to time b
|
||||
static bool gbt_timele(const void* a, const void* b)
|
||||
{
|
||||
const TimeADT* aa = (const TimeADT*)a; // Cast void pointer a to TimeADT pointer
|
||||
const TimeADT* bb = (const TimeADT*)b; // Cast void pointer b to TimeADT pointer
|
||||
|
||||
// Use the function time_le to compare the two time objects, and return the result as a boolean value
|
||||
return DatumGetBool(DirectFunctionCall2(time_le, TimeADTGetDatumFast(*aa), TimeADTGetDatumFast(*bb)));
|
||||
}
|
||||
|
||||
// Compare if time a is less than time b
|
||||
static bool gbt_timelt(const void* a, const void* b)
|
||||
{
|
||||
const TimeADT* aa = (const TimeADT*)a; // Cast void pointer a to TimeADT pointer
|
||||
const TimeADT* bb = (const TimeADT*)b; // Cast void pointer b to TimeADT pointer
|
||||
|
||||
// Use the function time_lt to compare the two time objects, and return the result as a boolean value
|
||||
return DatumGetBool(DirectFunctionCall2(time_lt, TimeADTGetDatumFast(*aa), TimeADTGetDatumFast(*bb)));
|
||||
}
|
||||
|
||||
static bool gbt_timege(const void* a, const void* b)
|
||||
{
|
||||
const TimeADT* aa = (const TimeADT*)a;
|
||||
const TimeADT* bb = (const TimeADT*)b;
|
||||
|
||||
return DatumGetBool(DirectFunctionCall2(time_ge, TimeADTGetDatumFast(*aa), TimeADTGetDatumFast(*bb)));
|
||||
}
|
||||
|
||||
static bool gbt_timeeq(const void* a, const void* b)
|
||||
{
|
||||
const TimeADT* aa = (const TimeADT*)a;
|
||||
const TimeADT* bb = (const TimeADT*)b;
|
||||
|
||||
return DatumGetBool(DirectFunctionCall2(time_eq, TimeADTGetDatumFast(*aa), TimeADTGetDatumFast(*bb)));
|
||||
}
|
||||
|
||||
static bool gbt_timele(const void* a, const void* b)
|
||||
{
|
||||
const TimeADT* aa = (const TimeADT*)a;
|
||||
const TimeADT* bb = (const TimeADT*)b;
|
||||
|
||||
return DatumGetBool(DirectFunctionCall2(time_le, TimeADTGetDatumFast(*aa), TimeADTGetDatumFast(*bb)));
|
||||
}
|
||||
|
||||
static bool gbt_timelt(const void* a, const void* b)
|
||||
{
|
||||
const TimeADT* aa = (const TimeADT*)a;
|
||||
const TimeADT* bb = (const TimeADT*)b;
|
||||
|
||||
return DatumGetBool(DirectFunctionCall2(time_lt, TimeADTGetDatumFast(*aa), TimeADTGetDatumFast(*bb)));
|
||||
}
|
||||
|
||||
static int gbt_timekey_cmp(const void* a, const void* b)
|
||||
{
|
||||
timeKEY* ia = (timeKEY*)(((const Nsrt*)a)->t);
|
||||
timeKEY* ib = (timeKEY*)(((const Nsrt*)b)->t);
|
||||
int res;
|
||||
|
||||
res = DatumGetInt32(DirectFunctionCall2(time_cmp, TimeADTGetDatumFast(ia->lower), TimeADTGetDatumFast(ib->lower)));
|
||||
if (res == 0)
|
||||
return DatumGetInt32(
|
||||
DirectFunctionCall2(time_cmp, TimeADTGetDatumFast(ia->upper), TimeADTGetDatumFast(ib->upper)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static float8 gbt_time_dist(const void* a, const void* b)
|
||||
{
|
||||
const TimeADT* aa = (const TimeADT*)a;
|
||||
const TimeADT* bb = (const TimeADT*)b;
|
||||
Interval* i = NULL;
|
||||
|
||||
i = DatumGetIntervalP(DirectFunctionCall2(time_mi_time, TimeADTGetDatumFast(*aa), TimeADTGetDatumFast(*bb)));
|
||||
return (float8)Abs(INTERVAL_TO_SEC(i));
|
||||
}
|
||||
|
||||
static const gbtree_ninfo tinfo = {gbt_t_time,
|
||||
sizeof(TimeADT),
|
||||
gbt_timegt,
|
||||
gbt_timege,
|
||||
gbt_timeeq,
|
||||
gbt_timele,
|
||||
gbt_timelt,
|
||||
gbt_timekey_cmp,
|
||||
gbt_time_dist};
|
||||
|
||||
PG_FUNCTION_INFO_V1(time_dist);
|
||||
extern "C" Datum time_dist(PG_FUNCTION_ARGS);
|
||||
Datum time_dist(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum diff = DirectFunctionCall2(time_mi_time, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1));
|
||||
|
||||
PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
|
||||
// Compare two timeKEY objects a and b
|
||||
static int gbt_timekey_cmp(const void* a, const void* b)
|
||||
{
|
||||
// Cast void pointers a and b to timeKEY pointers
|
||||
timeKEY* ia = (timeKEY*)(((const Nsrt*)a)->t);
|
||||
timeKEY* ib = (timeKEY*)(((const Nsrt*)b)->t);
|
||||
int res;
|
||||
|
||||
// Compare the lower bounds of the timeKEY objects
|
||||
res = DatumGetInt32(DirectFunctionCall2(time_cmp, TimeADTGetDatumFast(ia->lower), TimeADTGetDatumFast(ib->lower)));
|
||||
// If the lower bounds are equal, compare the upper bounds
|
||||
if (res == 0)
|
||||
return DatumGetInt32(
|
||||
DirectFunctionCall2(time_cmp, TimeADTGetDatumFast(ia->upper), TimeADTGetDatumFast(ib->upper)));
|
||||
|
||||
// Return the result of the comparison of the lower bounds
|
||||
return res;
|
||||
}
|
||||
|
||||
// Calculate the distance between two time objects a and b
|
||||
static float8 gbt_time_dist(const void* a, const void* b)
|
||||
{
|
||||
// Cast void pointers a and b to TimeADT pointers
|
||||
const TimeADT* aa = (const TimeADT*)a;
|
||||
const TimeADT* bb = (const TimeADT*)b;
|
||||
Interval* i = NULL;
|
||||
|
||||
// Call the function time_mi_time to get the interval between the two time objects
|
||||
i = DatumGetIntervalP(DirectFunctionCall2(time_mi_time, TimeADTGetDatumFast(*aa), TimeADTGetDatumFast(*bb)));
|
||||
// Return the absolute value of the interval converted to seconds
|
||||
return (float8)Abs(INTERVAL_TO_SEC(i));
|
||||
}
|
||||
|
||||
// Define the information about the time type for the generalized binary tree (gbtree)
|
||||
static const gbtree_ninfo tinfo = {gbt_t_time,
|
||||
sizeof(TimeADT),
|
||||
gbt_timegt,
|
||||
gbt_timege,
|
||||
gbt_timeeq,
|
||||
gbt_timele,
|
||||
gbt_timelt,
|
||||
gbt_timekey_cmp,
|
||||
gbt_time_dist};
|
||||
|
||||
// Define the information about the time_dist function for PostgreSQL
|
||||
PG_FUNCTION_INFO_V1(time_dist);
|
||||
extern "C" Datum time_dist(PG_FUNCTION_ARGS);
|
||||
// Define the time_dist function for PostgreSQL
|
||||
Datum time_dist(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Call the function time_mi_time to get the interval between the two time objects passed as arguments
|
||||
Datum diff = DirectFunctionCall2(time_mi_time, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1));
|
||||
|
||||
// Return the absolute value of the interval as a PostgreSQL interval object
|
||||
PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* time ops
|
||||
**************************************************/
|
||||
|
||||
Datum gbt_time_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* retval = NULL;
|
||||
|
||||
PG_RETURN_POINTER(gbt_num_compress(retval, entry, &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_timetz_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
GISTENTRY* retval = NULL;
|
||||
|
||||
if (entry->leafkey) {
|
||||
timeKEY* r = (timeKEY*)palloc(sizeof(timeKEY));
|
||||
TimeTzADT* tz = DatumGetTimeTzADTP(entry->key);
|
||||
TimeADT tmp;
|
||||
|
||||
retval = (GISTENTRY*)palloc(sizeof(GISTENTRY));
|
||||
|
||||
/* We are using the time + zone only to compress */
|
||||
#ifdef HAVE_INT64_TIMESTAMP
|
||||
tmp = tz->time + (tz->zone * INT64CONST(1000000));
|
||||
#else
|
||||
tmp = (tz->time + tz->zone);
|
||||
#endif
|
||||
r->lower = r->upper = tmp;
|
||||
gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page, entry->offset, FALSE);
|
||||
} else
|
||||
retval = entry;
|
||||
PG_RETURN_POINTER(retval);
|
||||
}
|
||||
|
||||
Datum gbt_time_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
TimeADT query = PG_GETARG_TIMEADT(1);
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
// Compress the time data
|
||||
Datum gbt_time_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the entry from the argument
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
// Initialize the return value
|
||||
GISTENTRY* retval = NULL;
|
||||
|
||||
// Return the compressed value
|
||||
PG_RETURN_POINTER(gbt_num_compress(retval, entry, &tinfo));
|
||||
}
|
||||
|
||||
// Compress the time zone data
|
||||
Datum gbt_timetz_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the entry from the argument
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
// Initialize the return value
|
||||
GISTENTRY* retval = NULL;
|
||||
|
||||
// If the entry is a leaf key
|
||||
if (entry->leafkey) {
|
||||
// Allocate memory for the timeKEY object
|
||||
timeKEY* r = (timeKEY*)palloc(sizeof(timeKEY));
|
||||
// Get the time zone data from the entry
|
||||
TimeTzADT* tz = DatumGetTimeTzADTP(entry->key);
|
||||
// Initialize a temporary time object
|
||||
TimeADT tmp;
|
||||
|
||||
// Allocate memory for the return entry
|
||||
retval = (GISTENTRY*)palloc(sizeof(GISTENTRY));
|
||||
|
||||
// Use the time and zone to compress the data
|
||||
#ifdef HAVE_INT64_TIMESTAMP
|
||||
tmp = tz->time + (tz->zone * INT64CONST(1000000));
|
||||
#else
|
||||
tmp = (tz->time + tz->zone);
|
||||
#endif
|
||||
// Set the lower and upper bounds of the timeKEY object to the compressed time value
|
||||
r->lower = r->upper = tmp;
|
||||
// Initialize the return entry with the compressed timeKEY object and other information from the original entry
|
||||
gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page, entry->offset, FALSE);
|
||||
} else {
|
||||
// If the entry is not a leaf key, return the original entry
|
||||
retval = entry;
|
||||
}
|
||||
// Return the compressed entry or the original entry if it is not a leaf key
|
||||
PG_RETURN_POINTER(retval);
|
||||
}
|
||||
|
||||
// Check if the query is consistent with the compressed time data
|
||||
Datum gbt_time_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the entry from the argument
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
// Get the query time from the argument
|
||||
TimeADT query = PG_GETARG_TIMEADT(1);
|
||||
// Get the strategy number from the argument
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
// Get a pointer to the recheck boolean from the argument
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
|
||||
timeKEY* kkk = (timeKEY*)DatumGetPointer(entry->key);
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
|
|
@ -182,101 +229,142 @@ Datum gbt_time_consistent(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_BOOL(gbt_num_consistent(&key, (void*)&query, &strategy, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_time_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
TimeADT query = PG_GETARG_TIMEADT(1);
|
||||
|
||||
timeKEY* kkk = (timeKEY*)DatumGetPointer(entry->key);
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
PG_RETURN_FLOAT8(gbt_num_distance(&key, (void*)&query, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_timetz_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
TimeTzADT* query = PG_GETARG_TIMETZADT_P(1);
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
timeKEY* kkk = (timeKEY*)DatumGetPointer(entry->key);
|
||||
TimeADT qqq;
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
/* All cases served by this function are inexact */
|
||||
*recheck = true;
|
||||
|
||||
#ifdef HAVE_INT64_TIMESTAMP
|
||||
qqq = query->time + (query->zone * INT64CONST(1000000));
|
||||
#else
|
||||
qqq = (query->time + query->zone);
|
||||
#endif
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
PG_RETURN_BOOL(gbt_num_consistent(&key, (void*)&qqq, &strategy, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_time_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
// Calculate the distance between the time data and the query
|
||||
Datum gbt_time_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the entry from the argument
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
// Get the query time from the argument
|
||||
TimeADT query = PG_GETARG_TIMEADT(1);
|
||||
|
||||
// Get the timeKEY object from the entry's key datum object
|
||||
timeKEY* kkk = (timeKEY*)DatumGetPointer(entry->key);
|
||||
// Initialize the key structure with the lower and upper bounds of the timeKEY object
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
// Return the distance between the time data and the query
|
||||
PG_RETURN_FLOAT8(gbt_num_distance(&key, (void*)&query, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
// Check if the query is consistent with the time zone data
|
||||
Datum gbt_timetz_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the entry from the argument
|
||||
GISTENTRY* entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
// Get the query time zone from the argument
|
||||
TimeTzADT* query = PG_GETARG_TIMETZADT_P(1);
|
||||
// Get the strategy number from the argument
|
||||
StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2);
|
||||
|
||||
// Get a pointer to the recheck boolean from the argument
|
||||
bool* recheck = (bool*)PG_GETARG_POINTER(4);
|
||||
// Get the timeKEY object from the entry's key datum object
|
||||
timeKEY* kkk = (timeKEY*)DatumGetPointer(entry->key);
|
||||
// Initialize a temporary time object
|
||||
TimeADT qqq;
|
||||
// Initialize the key structure with the lower and upper bounds of the timeKEY object
|
||||
GBT_NUMKEY_R key;
|
||||
|
||||
/* All cases served by this function are inexact */
|
||||
*recheck = true;
|
||||
|
||||
#ifdef HAVE_INT64_TIMESTAMP
|
||||
qqq = query->time + (query->zone * INT64CONST(1000000));
|
||||
#else
|
||||
qqq = (query->time + query->zone);
|
||||
#endif
|
||||
|
||||
key.lower = (GBT_NUMKEY*)&kkk->lower;
|
||||
key.upper = (GBT_NUMKEY*)&kkk->upper;
|
||||
|
||||
// Return if the query is consistent with the time zone data
|
||||
PG_RETURN_BOOL(gbt_num_consistent(&key, (void*)&qqq, &strategy, GIST_LEAF(entry), &tinfo));
|
||||
}
|
||||
|
||||
// Union the time data
|
||||
Datum gbt_time_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the entry vector from the argument
|
||||
GistEntryVector* entryvec = (GistEntryVector*)PG_GETARG_POINTER(0);
|
||||
void* out = palloc(sizeof(timeKEY));
|
||||
|
||||
*(int*)PG_GETARG_POINTER(1) = sizeof(timeKEY);
|
||||
PG_RETURN_POINTER(gbt_num_union((GBT_NUMKEY*)out, entryvec, &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_time_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
timeKEY* origentry = (timeKEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(0))->key);
|
||||
timeKEY* newentry = (timeKEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(1))->key);
|
||||
float* result = (float*)PG_GETARG_POINTER(2);
|
||||
Interval* intr = NULL;
|
||||
double res;
|
||||
double res2;
|
||||
|
||||
intr = DatumGetIntervalP(
|
||||
DirectFunctionCall2(time_mi_time, TimeADTGetDatumFast(newentry->upper), TimeADTGetDatumFast(origentry->upper)));
|
||||
res = INTERVAL_TO_SEC(intr);
|
||||
res = Max(res, 0);
|
||||
|
||||
intr = DatumGetIntervalP(
|
||||
DirectFunctionCall2(time_mi_time, TimeADTGetDatumFast(origentry->lower), TimeADTGetDatumFast(newentry->lower)));
|
||||
res2 = INTERVAL_TO_SEC(intr);
|
||||
res2 = Max(res2, 0);
|
||||
|
||||
res += res2;
|
||||
|
||||
*result = 0.0;
|
||||
|
||||
if (res > 0) {
|
||||
intr = DatumGetIntervalP(DirectFunctionCall2(
|
||||
time_mi_time, TimeADTGetDatumFast(origentry->upper), TimeADTGetDatumFast(origentry->lower)));
|
||||
*result += FLT_MIN;
|
||||
*result += (float)(res / (res + INTERVAL_TO_SEC(intr)));
|
||||
*result *= (FLT_MAX / (((GISTENTRY*)PG_GETARG_POINTER(0))->rel->rd_att->natts + 1));
|
||||
}
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
Datum gbt_time_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_POINTER(
|
||||
gbt_num_picksplit((GistEntryVector*)PG_GETARG_POINTER(0), (GIST_SPLITVEC*)PG_GETARG_POINTER(1), &tinfo));
|
||||
}
|
||||
|
||||
Datum gbt_time_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
timeKEY* b1 = (timeKEY*)PG_GETARG_POINTER(0);
|
||||
timeKEY* b2 = (timeKEY*)PG_GETARG_POINTER(1);
|
||||
bool* result = (bool*)PG_GETARG_POINTER(2);
|
||||
|
||||
*result = gbt_num_same((GBT_NUMKEY*)b1, (GBT_NUMKEY*)b2, &tinfo);
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
// Calculate the penalty for the time data
|
||||
Datum gbt_time_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the original and new entries' timeKEY objects from the arguments
|
||||
timeKEY* origentry = (timeKEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(0))->key);
|
||||
timeKEY* newentry = (timeKEY*)DatumGetPointer(((GISTENTRY*)PG_GETARG_POINTER(1))->key);
|
||||
// Get a pointer to the result float from the argument
|
||||
float* result = (float*)PG_GETARG_POINTER(2);
|
||||
// Initialize an interval object
|
||||
Interval* intr = NULL;
|
||||
// Initialize result variables
|
||||
double res;
|
||||
double res2;
|
||||
|
||||
// Calculate the time interval between the new and original entries' upper bounds
|
||||
intr = DatumGetIntervalP(
|
||||
DirectFunctionCall2(time_mi_time, TimeADTGetDatumFast(newentry->upper), TimeADTGetDatumFast(origentry->upper)));
|
||||
// Convert the interval to seconds and take the maximum value as 0
|
||||
res = INTERVAL_TO_SEC(intr);
|
||||
res = Max(res, 0);
|
||||
|
||||
// Calculate the time interval between the new and original entries' lower bounds
|
||||
intr = DatumGetIntervalP(
|
||||
DirectFunctionCall2(time_mi_time, TimeADTGetDatumFast(origentry->lower), TimeADTGetDatumFast(newentry->lower)));
|
||||
// Convert the interval to seconds and take the maximum value as 0
|
||||
res2 = INTERVAL_TO_SEC(intr);
|
||||
res2 = Max(res2, 0);
|
||||
|
||||
// Add the two intervals together
|
||||
res += res2;
|
||||
|
||||
// Initialize the result to 0.0
|
||||
*result = 0.0;
|
||||
|
||||
// If the total interval is greater than 0, calculate the penalty
|
||||
if (res > 0) {
|
||||
// Calculate the time interval between the original entry's upper and lower bounds
|
||||
intr = DatumGetIntervalP(DirectFunctionCall2(
|
||||
time_mi_time, TimeADTGetDatumFast(origentry->upper), TimeADTGetDatumFast(origentry->lower)));
|
||||
// Add a small value to the result
|
||||
*result += FLT_MIN;
|
||||
// Calculate the ratio of the total interval to the original entry's upper and lower bounds' interval
|
||||
*result += (float)(res / (res + INTERVAL_TO_SEC(intr)));
|
||||
// Scale the result by a maximum value divided by the number of attributes plus 1
|
||||
*result *= (FLT_MAX / (((GISTENTRY*)PG_GETARG_POINTER(0))->rel->rd_att->natts + 1));
|
||||
}
|
||||
|
||||
// Return a pointer to the result float
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
// Pick a split point for the time data
|
||||
Datum gbt_time_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Return a pointer to the result of picking a split point for the time data
|
||||
PG_RETURN_POINTER(
|
||||
gbt_num_picksplit((GistEntryVector*)PG_GETARG_POINTER(0), (GIST_SPLITVEC*)PG_GETARG_POINTER(1), &tinfo));
|
||||
}
|
||||
|
||||
// Check if two time data are the same
|
||||
Datum gbt_time_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the two timeKEY objects from the arguments
|
||||
timeKEY* b1 = (timeKEY*)PG_GETARG_POINTER(0);
|
||||
timeKEY* b2 = (timeKEY*)PG_GETARG_POINTER(1);
|
||||
// Get a pointer to the result boolean from the argument
|
||||
bool* result = (bool*)PG_GETARG_POINTER(2);
|
||||
|
||||
// Check if the two time data are the same and store the result in the result boolean
|
||||
*result = gbt_num_same((GBT_NUMKEY*)b1, (GBT_NUMKEY*)b2, &tinfo);
|
||||
// Return a pointer to the result boolean
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
|
@ -240,26 +240,38 @@ Datum citext_ge(PG_FUNCTION_ARGS)
|
|||
* ===================
|
||||
*/
|
||||
|
||||
PG_FUNCTION_INFO_V1(citext_smaller);
|
||||
|
||||
Datum citext_smaller(PG_FUNCTION_ARGS)
|
||||
{
|
||||
text* left = PG_GETARG_TEXT_PP(0);
|
||||
text* right = PG_GETARG_TEXT_PP(1);
|
||||
text* result = NULL;
|
||||
|
||||
result = (citextcmp(left, right, PG_GET_COLLATION()) < 0) ? left : right;
|
||||
PG_RETURN_TEXT_P(result);
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(citext_larger);
|
||||
|
||||
Datum citext_larger(PG_FUNCTION_ARGS)
|
||||
{
|
||||
text* left = PG_GETARG_TEXT_PP(0);
|
||||
text* right = PG_GETARG_TEXT_PP(1);
|
||||
text* result = NULL;
|
||||
|
||||
result = (citextcmp(left, right, PG_GET_COLLATION()) > 0) ? left : right;
|
||||
PG_RETURN_TEXT_P(result);
|
||||
}
|
||||
// Declare a function named citext_smaller, which is an internal function of PostgreSQL, used to compare whether the first citext value is smaller than the second.
|
||||
PG_FUNCTION_INFO_V1(citext_smaller);
|
||||
|
||||
// Implement the citext_smaller function. This function takes two parameters (two text* pointers) and returns a text* value.
|
||||
Datum citext_smaller(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the first and second text* pointers from the function parameters and assign them to left and right respectively.
|
||||
text* left = PG_GETARG_TEXT_PP(0);
|
||||
text* right = PG_GETARG_TEXT_PP(1);
|
||||
// Initialize a text* variable result, which will store the result of the comparison.
|
||||
text* result = NULL;
|
||||
|
||||
// Use the citextcmp function to compare left and right. If left is smaller than right, assign left to result; otherwise, assign right to result.
|
||||
result = (citextcmp(left, right, PG_GET_COLLATION()) < 0) ? left : right;
|
||||
// Return the result of the comparison.
|
||||
PG_RETURN_TEXT_P(result);
|
||||
}
|
||||
|
||||
// Declare a function named citext_larger, which is an internal function of PostgreSQL, used to compare whether the first citext value is larger than the second.
|
||||
PG_FUNCTION_INFO_V1(citext_larger);
|
||||
|
||||
// Implement the citext_larger function. This function takes two parameters (two text* pointers) and returns a text* value.
|
||||
Datum citext_larger(PG_FUNCTION_ARGS)
|
||||
{
|
||||
// Get the first and second text* pointers from the function parameters and assign them to left and right respectively.
|
||||
text* left = PG_GETARG_TEXT_PP(0);
|
||||
text* right = PG_GETARG_TEXT_PP(1);
|
||||
// Initialize a text* variable result, which will store the result of the comparison.
|
||||
text* result = NULL;
|
||||
|
||||
// Use the citextcmp function to compare left and right. If left is larger than right, assign left to result; otherwise, assign right to result.
|
||||
result = (citextcmp(left, right, PG_GET_COLLATION()) > 0) ? left : right;
|
||||
// Return the result of the comparison.
|
||||
PG_RETURN_TEXT_P(result);
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -363,6 +363,7 @@ void cgptree_get_group_info(struct group_info* curr_ginfo, const struct cgroup_m
|
|||
*/
|
||||
static struct group_info* cgptree_get_group_tree(const struct cgroup_mount_point& mount_info)
|
||||
{
|
||||
/* init the variable */
|
||||
int curr_depth = -1;
|
||||
int prev_depth = -1;
|
||||
void* tree_handle = NULL;
|
||||
|
|
@ -401,15 +402,15 @@ static struct group_info* cgptree_get_group_tree(const struct cgroup_mount_point
|
|||
while (error != ECGEOF) {
|
||||
/* get the relative path */
|
||||
rel_path = (char*)(info.full_path + strlen(root_path));
|
||||
|
||||
/* specific path of the file */
|
||||
tmpstr = rel_path + sizeof(GSCGROUP_TOP_DATABASE);
|
||||
cm_tmpstr = rel_path + sizeof(GSCGROUP_CM);
|
||||
tmplen = strlen(cgutil_passwd_user->pw_name);
|
||||
|
||||
if ((CHECK_GSCGROUP_TOP_DATABASE || CHECK_GSCGROUP_CM) && info.type == CGROUP_FILE_TYPE_DIR) {
|
||||
curr_ginfo = (struct group_info*)calloc(1, sizeof(struct group_info));
|
||||
|
||||
if (curr_ginfo == NULL)
|
||||
/* when it doesn't exit , create error_report*/
|
||||
if (curr_ginfo == NULL)
|
||||
goto error;
|
||||
|
||||
curr_ginfo->depth = info.depth;
|
||||
|
|
|
|||
|
|
@ -197,11 +197,10 @@ static int check_percentage_value(int bkd, int grp, int cls, int top)
|
|||
/* fixed mode */
|
||||
if (cgutil_opt.fixed) {
|
||||
/*
|
||||
* it is not allowed if more than one group percentage is specified when updating
|
||||
* cpuset by percentage
|
||||
* Alert: Fixed mode only allows one group percentage at a time when updating cpuset by percentage.
|
||||
*/
|
||||
if (bkd + cls + top + grp > 1) {
|
||||
fprintf(stderr, "ERROR: redundant options of cpu core percentage. \n");
|
||||
fprintf(stderr, "ERROR: Redundant options for cpu core percentage. \n");
|
||||
return -1;
|
||||
} else if (bkd + cls + top + grp == 0) {
|
||||
return 0;
|
||||
|
|
@ -209,36 +208,36 @@ static int check_percentage_value(int bkd, int grp, int cls, int top)
|
|||
|
||||
check_group_name_redundant(bkd, grp, cls, top);
|
||||
|
||||
/* check backend percentage, cpuset percentage range is 1-100 */
|
||||
/* Check backend percentage. Valid range: 1-100. */
|
||||
if (cgutil_opt.uflag && bkd) {
|
||||
if (cgutil_opt.bkdpct > 100 || cgutil_opt.bkdpct < 0) {
|
||||
fprintf(stderr,
|
||||
"ERROR: invalid value for cpu core percentage. "
|
||||
"its range should be 0-100. \n");
|
||||
"ERROR: Invalid value for cpu core percentage. "
|
||||
"The range should be 0-100. \n");
|
||||
return -1;
|
||||
}
|
||||
cgutil_opt.setspct = cgutil_opt.bkdpct;
|
||||
cgutil_opt.bkdpct = 0;
|
||||
}
|
||||
|
||||
/* check group percentage */
|
||||
/* Check group percentage */
|
||||
if (cgutil_opt.uflag && grp) {
|
||||
if (cgutil_opt.grppct > 100 || cgutil_opt.grppct < 0) {
|
||||
fprintf(stderr,
|
||||
"ERROR: invalid value for cpu core percentage. "
|
||||
"its range should be 0-100. \n");
|
||||
"ERROR: Invalid value for cpu core percentage. "
|
||||
"The range should be 0-100. \n");
|
||||
return -1;
|
||||
}
|
||||
cgutil_opt.setspct = cgutil_opt.grppct;
|
||||
cgutil_opt.grppct = 0;
|
||||
}
|
||||
|
||||
/* check class percentage */
|
||||
/* Check class percentage */
|
||||
if (cgutil_opt.uflag && cls) {
|
||||
if (cgutil_opt.clspct > 100 || cgutil_opt.clspct < 0) {
|
||||
fprintf(stderr,
|
||||
"ERROR: invalid value for cpu core percentage. "
|
||||
"its range should be 0-100. \n");
|
||||
"ERROR: Invalid value for cpu core percentage. "
|
||||
"The range should be 0-100. \n");
|
||||
return -1;
|
||||
}
|
||||
cgutil_opt.setspct = cgutil_opt.clspct;
|
||||
|
|
@ -246,50 +245,50 @@ static int check_percentage_value(int bkd, int grp, int cls, int top)
|
|||
cgutil_opt.clssetpct = 1;
|
||||
}
|
||||
|
||||
/* check top group percentage */
|
||||
/* Check top group percentage */
|
||||
if (cgutil_opt.uflag && top) {
|
||||
if (cgutil_opt.toppct > 100 || cgutil_opt.toppct < 0) {
|
||||
fprintf(stderr,
|
||||
"ERROR: invalid value for cpu core percentage. "
|
||||
"its range should be 0-100. \n");
|
||||
"ERROR: Invalid value for cpu core percentage. "
|
||||
"The range should be 0-100. \n");
|
||||
return -1;
|
||||
}
|
||||
cgutil_opt.setspct = cgutil_opt.toppct;
|
||||
cgutil_opt.toppct = 0;
|
||||
}
|
||||
|
||||
// if user set core percentage is 0, set a flag to show that user set
|
||||
// Set a flag to indicate that user set core percentage is 0
|
||||
if (cgutil_opt.setspct == 0)
|
||||
cgutil_opt.setfixed = 1;
|
||||
} else {
|
||||
if ((cgutil_opt.cflag || cgutil_opt.uflag) && bkd && (cgutil_opt.bkdpct >= 100 || cgutil_opt.bkdpct < 1)) {
|
||||
fprintf(stderr,
|
||||
"ERROR: invalid value for backend group dynamic percentage. "
|
||||
"its range should be 1 ~ 99!\n");
|
||||
"ERROR: Invalid value for backend group dynamic percentage. "
|
||||
"The range should be 1 ~ 99!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check backend percentage */
|
||||
/* Check backend percentage */
|
||||
if ((cgutil_opt.cflag || cgutil_opt.uflag) && grp && (cgutil_opt.grppct >= 100 || cgutil_opt.grppct < 1)) {
|
||||
fprintf(stderr,
|
||||
"ERROR: invalid value for workload group dynamic percentage. "
|
||||
"its range should be 1 ~ 99!\n");
|
||||
"ERROR: Invalid value for workload group dynamic percentage. "
|
||||
"The range should be 1 ~ 99!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check group percentage */
|
||||
/* Check group percentage */
|
||||
if ((cgutil_opt.cflag || cgutil_opt.uflag) && cls && (cgutil_opt.clspct >= 100 || (cgutil_opt.clspct < 1))) {
|
||||
fprintf(stderr,
|
||||
"ERROR: invalid value for class group dynamic percentage. "
|
||||
"its range should be 1 ~ 99!\n");
|
||||
"ERROR: Invalid value for class group dynamic percentage. "
|
||||
"The range should be 1 ~ 99!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check class percentage */
|
||||
/* Check class percentage */
|
||||
if ((cgutil_opt.cflag || cgutil_opt.uflag) && top && (cgutil_opt.toppct >= 100 || cgutil_opt.toppct < 1)) {
|
||||
fprintf(stderr,
|
||||
"ERROR: invalid value for top group dynamic percentage. "
|
||||
"its range should be 1 ~ 99!\n");
|
||||
"ERROR: Invalid value for top group dynamic percentage. "
|
||||
"The range should be 1 ~ 99!\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -348,11 +347,13 @@ static int check_node_group_name()
|
|||
*/
|
||||
static void check_input_for_security(char* input)
|
||||
{
|
||||
char* danger_token[] = {"|", ";", "&", "$", "<", ">", "`", "\\", "!", "\n", NULL};
|
||||
// Array of dangerous tokens
|
||||
char* danger_token[] = {"|", ";", "&", "$", "<", ">", "`", "\", "!", "\n", NULL};
|
||||
|
||||
// Check if any of the dangerous tokens are present in the input string
|
||||
for (int i = 0; danger_token[i] != NULL; ++i) {
|
||||
if (strstr(input, danger_token[i]) != NULL) {
|
||||
printf("invalid token \"%s\"\n", danger_token[i]);
|
||||
printf("Invalid token \"%s\"\n", danger_token[i]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -413,20 +414,20 @@ static int check_name_valid(void)
|
|||
*/
|
||||
static int check_input_valid(void)
|
||||
{
|
||||
/* check group name with flag '--fixed' */
|
||||
// Check group name with flag '--fixed'
|
||||
if (*cgutil_opt.clsname == '\0' && *cgutil_opt.wdname == '\0' && *cgutil_opt.bkdname == '\0' &&
|
||||
*cgutil_opt.topname == '\0' && cgutil_opt.fixed) {
|
||||
fprintf(stderr, "ERROR: Please specify a group name with flag \"--fixed\"\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check flag '--fixed' and '-u' */
|
||||
// Check flag '--fixed' and '-u'
|
||||
if (cgutil_opt.fixed && 0 == cgutil_opt.uflag) {
|
||||
fprintf(stderr, "ERROR: Please specify \'--fixed\' flag together with \'-u\' flag.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check group name with flag '-f' */
|
||||
// Check group name with flag '-f'
|
||||
if ((*cgutil_opt.clsname || *cgutil_opt.wdname || *cgutil_opt.bkdname ||
|
||||
(*cgutil_opt.topname &&
|
||||
(0 != strncmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE, sizeof(GSCGROUP_TOP_DATABASE))))) &&
|
||||
|
|
@ -435,43 +436,43 @@ static int check_input_valid(void)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* users cannot use -f and --fixed at the same time */
|
||||
// Users cannot use -f and --fixed at the same time
|
||||
if (cgutil_opt.fixed && *cgutil_opt.sets) {
|
||||
fprintf(stderr, "ERROR: Please specify one option from \'-f\',\'--fixed\'.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get current mount points */
|
||||
// Get current mount points
|
||||
if (cgexec_get_mount_points() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check '-c', '-d', '-u' flag */
|
||||
// Check '-c', '-d', '-u' flag
|
||||
if ((cgutil_opt.cflag && cgutil_opt.dflag) || (cgutil_opt.cflag && cgutil_opt.uflag) ||
|
||||
(cgutil_opt.uflag && cgutil_opt.dflag)) {
|
||||
fprintf(stderr, "ERROR: please only specify one option from '-c', '-d' and '-u'.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check '-e' flag */
|
||||
// Check '-e' flag
|
||||
if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_ERROR)) {
|
||||
fprintf(stderr, "ERROR: abort and penalty cannot be specified together!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check exception data from '-e' flag */
|
||||
// Check exception data from '-e' flag
|
||||
if (cgutil_opt.clsname[0] == '\0' && IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_PENALTY)) {
|
||||
fprintf(stderr, "ERROR: you must specify a class name with penalty!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set default exception data without '--penalty', '--abort' and '-a' flag */
|
||||
// Set default exception data without '--penalty', '--abort' and '-a' flag
|
||||
if (*cgutil_opt.edata && IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_NONE)) {
|
||||
cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_PENALTY);
|
||||
fprintf(stdout, "NOTICE: if do not specify exceptional action, default is penalty!\n");
|
||||
}
|
||||
|
||||
/* check '--refresh', '--revert' and '--recover' flag */
|
||||
// Check '--refresh', '--revert' and '--recover' flag
|
||||
if ((cgutil_opt.cflag || cgutil_opt.dflag || cgutil_opt.uflag) &&
|
||||
(cgutil_opt.refresh || cgutil_opt.revert || cgutil_opt.recover)) {
|
||||
fprintf(stderr,
|
||||
|
|
@ -480,7 +481,7 @@ static int check_input_valid(void)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* check '--recover' flag */
|
||||
// Check '--recover' flag
|
||||
if ((geteuid() == 0) && cgutil_opt.recover) {
|
||||
fprintf(stderr, "ERROR: you cannpt specify option '--recover' by root user!\n");
|
||||
return -1;
|
||||
|
|
@ -488,16 +489,17 @@ static int check_input_valid(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: check user info with flags.
|
||||
* @IN void
|
||||
* @Return: -1: abnormal 0: normal
|
||||
* @See also:
|
||||
*/
|
||||
// This function checks the user and process information for the cgutil command.
|
||||
// It ensures that the command is being run with the correct permissions and that the user information is being specified correctly.
|
||||
static int check_user_process(void)
|
||||
{
|
||||
/* check root user process */
|
||||
// Check if running as root user and user info is missing for certain flags
|
||||
if ((geteuid() == 0) && ((cgutil_opt.cflag || cgutil_opt.display || cgutil_opt.uflag || cgutil_opt.dflag) &&
|
||||
cgutil_opt.user[0] == '\0')) {
|
||||
fprintf(stderr,
|
||||
|
|
@ -506,13 +508,13 @@ static int check_user_process(void)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* check non-root user process */
|
||||
// Check if running as non-root user and user info is specified
|
||||
if (geteuid() && cgutil_opt.user[0] != '\0') {
|
||||
fprintf(stderr, "ERROR: you can't specify the user name while running as non-root user.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check user info for '-P' flag */
|
||||
// Check if running as root user and user info is missing for '-P' flag
|
||||
if (0 == geteuid() && cgutil_opt.ptree && '\0' == *cgutil_opt.user) {
|
||||
fprintf(stderr,
|
||||
"ERROR: you must specify the user name when running as root user "
|
||||
|
|
@ -520,7 +522,7 @@ static int check_user_process(void)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* check non-root user info for '-M' flag */
|
||||
// Check if running as non-root user and trying to mount or unmount cgroup
|
||||
if ((cgutil_opt.mflag || cgutil_opt.umflag) && geteuid()) {
|
||||
fprintf(stderr, "ERROR: you must run mount or umount cgroup by root user!\n");
|
||||
return -1;
|
||||
|
|
@ -537,7 +539,7 @@ static int check_user_process(void)
|
|||
*/
|
||||
static int check_flag_process(void)
|
||||
{
|
||||
/* create flag process */
|
||||
// Check if creating a cgroup and validate group and class names
|
||||
if (cgutil_opt.cflag) {
|
||||
/* check top and backend group name */
|
||||
if (cgutil_opt.topname[0] != '\0' || cgutil_opt.bkdname[0] != '\0') {
|
||||
|
|
@ -561,7 +563,7 @@ static int check_flag_process(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* delete flag process */
|
||||
// Check if deleting a cgroup and validate group and class names
|
||||
if (cgutil_opt.dflag) {
|
||||
/* check top and backend group name */
|
||||
if (cgutil_opt.topname[0] != '\0' || cgutil_opt.bkdname[0] != '\0') {
|
||||
|
|
@ -572,7 +574,7 @@ static int check_flag_process(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* update flag process */
|
||||
// Check if updating a cgroup and validate group and class names
|
||||
if (cgutil_opt.uflag &&
|
||||
('\0' == cgutil_opt.topname[0] && '\0' == cgutil_opt.bkdname[0] && '\0' == cgutil_opt.clsname[0])) {
|
||||
fprintf(stderr, "ERROR: please specify the Group name when updating!\n");
|
||||
|
|
@ -825,7 +827,7 @@ static int check_cpuset_value_valid(char* cpuset)
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// check the value
|
||||
if ((a < 0) || (b < 0) || (a > b) || (b >= cgutil_cpucnt)) {
|
||||
fprintf(stderr, "ERROR: please specify the cpuset with a valid value.\n");
|
||||
return -1;
|
||||
|
|
@ -843,6 +845,7 @@ static int check_cpuset_value_valid(char* cpuset)
|
|||
* @Return: 1: OK 0: Not OK
|
||||
* @See also:
|
||||
*/
|
||||
// check configuration
|
||||
static int check_config_flag(void)
|
||||
{
|
||||
if (cgutil_opt.cflag || (cgutil_opt.dflag && (*cgutil_opt.clsname || *cgutil_opt.nodegroup)) || cgutil_opt.uflag ||
|
||||
|
|
@ -859,6 +862,7 @@ static int check_config_flag(void)
|
|||
* @Return: -1: abnormal 0: normal
|
||||
* @See also:
|
||||
*/
|
||||
// initialize the configuration
|
||||
static int initialize_cgroup_config(void)
|
||||
{
|
||||
char* hpath = NULL;
|
||||
|
|
@ -905,6 +909,7 @@ static int initialize_cgroup_config(void)
|
|||
* @Return: -1: abnormal 0: normal
|
||||
* @See also:
|
||||
*/
|
||||
// check the configuration status
|
||||
static int check_and_get_group_percent(char* percent, char* gtype)
|
||||
{
|
||||
char* bad = NULL;
|
||||
|
|
@ -941,6 +946,7 @@ static int check_and_get_group_percent(char* percent, char* gtype)
|
|||
* -1: abnormal
|
||||
* 0: normal
|
||||
*/
|
||||
// structure of long options
|
||||
static struct option long_options[] = {{"help", no_argument, NULL, 'h'},
|
||||
{"version", no_argument, NULL, 'V'},
|
||||
{"abort", no_argument, NULL, 'a'},
|
||||
|
|
@ -954,6 +960,7 @@ static struct option long_options[] = {{"help", no_argument, NULL, 'h'},
|
|||
{"rename", no_argument, NULL, 7},
|
||||
{NULL, 0, NULL, 0}};
|
||||
|
||||
// match the choice
|
||||
static int parse_options(int argc, char** argv)
|
||||
{
|
||||
int c;
|
||||
|
|
@ -1164,7 +1171,7 @@ int main(int argc, char** argv)
|
|||
" or \"/sys/devices/system\" is acceptable. \n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// set the style of string
|
||||
int rc = sprintf_s(cgutil_allset, sizeof(cgutil_allset), "%d-%d", 0, cgutil_cpucnt - 1);
|
||||
securec_check_intval(rc, , -1);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,6 +233,7 @@ char* all_data_nodename_list = NULL;
|
|||
const uint32 USTORE_UPGRADE_VERSION = 92368;
|
||||
const uint32 PACKAGE_ENHANCEMENT = 92444;
|
||||
const uint32 SUBSCRIPTION_VERSION = 92580;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
|
||||
|
||||
#ifdef DUMPSYSLOG
|
||||
char* syslogpath = NULL;
|
||||
|
|
@ -4444,23 +4445,16 @@ void getSubscriptions(Archive *fout)
|
|||
int i_subslotname;
|
||||
int i_subsynccommit;
|
||||
int i_subpublications;
|
||||
int i, ntups;
|
||||
int i_subbinary;
|
||||
int i;
|
||||
int ntups;
|
||||
|
||||
if (no_subscriptions || GetVersionNum(fout) < SUBSCRIPTION_VERSION) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -4469,14 +4463,20 @@ void getSubscriptions(Archive *fout)
|
|||
resetPQExpBuffer(query);
|
||||
|
||||
/* Get the subscriptions in current database. */
|
||||
appendPQExpBuffer(query,
|
||||
"SELECT s.tableoid, s.oid, s.subname,"
|
||||
"(%s s.subowner) AS rolname, "
|
||||
" s.subconninfo, s.subslotname, s.subsynccommit, s.subpublications "
|
||||
"FROM pg_catalog.pg_subscription s "
|
||||
appendPQExpBuffer(query, "SELECT s.tableoid, s.oid, s.subname,"
|
||||
"(%s s.subowner) AS rolname, s.subconninfo, s.subslotname, "
|
||||
"s.subsynccommit, s.subpublications, \n", username_subquery);
|
||||
|
||||
if (GetVersionNum(fout) >= SUBSCRIPTION_BINARY_VERSION_NUM) {
|
||||
appendPQExpBuffer(query, " s.subbinary\n");
|
||||
} else {
|
||||
appendPQExpBuffer(query, " false AS subbinary\n");
|
||||
}
|
||||
|
||||
appendPQExpBuffer(query, "FROM pg_catalog.pg_subscription s "
|
||||
"WHERE s.subdbid = (SELECT oid FROM pg_catalog.pg_database"
|
||||
" WHERE datname = current_database())",
|
||||
username_subquery);
|
||||
" WHERE datname = current_database())");
|
||||
|
||||
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
|
||||
|
||||
ntups = PQntuples(res);
|
||||
|
|
@ -4494,6 +4494,7 @@ void getSubscriptions(Archive *fout)
|
|||
i_subslotname = PQfnumber(res, "subslotname");
|
||||
i_subsynccommit = PQfnumber(res, "subsynccommit");
|
||||
i_subpublications = PQfnumber(res, "subpublications");
|
||||
i_subbinary = PQfnumber(res, "subbinary");
|
||||
|
||||
subinfo = (SubscriptionInfo *)pg_malloc(ntups * sizeof(SubscriptionInfo));
|
||||
|
||||
|
|
@ -4512,6 +4513,7 @@ void getSubscriptions(Archive *fout)
|
|||
}
|
||||
subinfo[i].subsynccommit = gs_strdup(PQgetvalue(res, i, i_subsynccommit));
|
||||
subinfo[i].subpublications = gs_strdup(PQgetvalue(res, i, i_subpublications));
|
||||
subinfo[i].subbinary = gs_strdup(PQgetvalue(res, i, i_subbinary));
|
||||
|
||||
if (strlen(subinfo[i].rolname) == 0) {
|
||||
write_msg(NULL, "WARNING: owner of subscription \"%s\" appears to be invalid\n", subinfo[i].dobj.name);
|
||||
|
|
@ -4578,6 +4580,10 @@ static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
|
|||
appendPQExpBufferStr(query, "NONE");
|
||||
}
|
||||
|
||||
if (strcmp(subinfo->subbinary, "t") == 0) {
|
||||
appendPQExpBuffer(query, ", binary = true");
|
||||
}
|
||||
|
||||
if (strcmp(subinfo->subsynccommit, "off") != 0) {
|
||||
appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
|
||||
}
|
||||
|
|
@ -10780,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");
|
||||
|
||||
|
|
@ -21389,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,
|
||||
|
|
|
|||
|
|
@ -498,6 +498,7 @@ typedef struct _SubscriptionInfo {
|
|||
char *subslotname;
|
||||
char *subsynccommit;
|
||||
char *subpublications;
|
||||
char *subbinary;
|
||||
} SubscriptionInfo;
|
||||
|
||||
/* global decls */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "replication/worker_internal.h"
|
||||
|
||||
static List *textarray_to_stringlist(ArrayType *textarray);
|
||||
|
||||
|
|
@ -91,6 +90,13 @@ Subscription *GetSubscription(Oid subid, bool missing_ok)
|
|||
}
|
||||
sub->publications = textarray_to_stringlist(DatumGetArrayTypeP(datum));
|
||||
|
||||
datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subbinary, &isnull);
|
||||
if (unlikely(isnull)) {
|
||||
sub->binary = false;
|
||||
} else {
|
||||
sub->binary = DatumGetBool(datum);
|
||||
}
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
return sub;
|
||||
|
|
@ -183,7 +189,7 @@ char *get_subscription_name(Oid subid, bool missing_ok)
|
|||
}
|
||||
|
||||
/* Clear the list content, only deal with DefElem and string content */
|
||||
static void ClearListContent(List *list)
|
||||
void ClearListContent(List *list)
|
||||
{
|
||||
ListCell *cell = NULL;
|
||||
foreach(cell, list) {
|
||||
|
|
@ -203,25 +209,6 @@ static void ClearListContent(List *list)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Decrypt conninfo for subscription.
|
||||
* IMPORTANT: caller should clear and free the memory after using it immediately
|
||||
*/
|
||||
char *DecryptConninfo(char *encryptConninfo)
|
||||
{
|
||||
const char* sensitiveOptionsArray[] = {"password"};
|
||||
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
|
||||
List *defList = ConninfoToDefList(encryptConninfo);
|
||||
DecryptOptions(defList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
||||
char *decryptConninfo = DefListToString(defList);
|
||||
|
||||
/* defList has plain content, clear it before free */
|
||||
ClearListContent(defList);
|
||||
list_free_ext(defList);
|
||||
/* IMPORTANT: caller should clear and free the memory after using it immediately */
|
||||
return decryptConninfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert text array to list of strings.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 = 92605;
|
||||
const uint32 GRAND_VERSION_NUM = 92606;
|
||||
|
||||
const uint32 PREDPUSH_SAME_LEVEL_VERSION_NUM = 92522;
|
||||
const uint32 UPSERT_WHERE_VERSION_NUM = 92514;
|
||||
|
|
@ -101,6 +101,7 @@ const uint32 PRIVS_DIRECTORY_VERSION_NUM = 92460;
|
|||
const uint32 COMMENT_RECORD_PARAM_VERSION_NUM = 92484;
|
||||
const uint32 SCAN_BATCH_MODE_VERSION_NUM = 92568;
|
||||
const uint32 PUBLICATION_VERSION_NUM = 92580;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
|
||||
|
||||
/* Version number of the guc parameter backend_version added in V500R001C20 */
|
||||
const uint32 V5R1C20_BACKEND_VERSION_NUM = 92305;
|
||||
|
|
|
|||
|
|
@ -2712,8 +2712,8 @@ void PostgresInitializer::InitExtensionVariable()
|
|||
}
|
||||
|
||||
/* check whether the extension has been created */
|
||||
const char* 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
#include "utils/array.h"
|
||||
#include "utils/acl.h"
|
||||
|
||||
static void ConnectPublisher(char *conninfo, char* slotname);
|
||||
static bool ConnectPublisher(char* conninfo, char* slotname);
|
||||
static void CreateSlotInPublisher(char *slotname);
|
||||
static void ValidateReplicationSlot(char *slotname, List *publications);
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ static void ValidateReplicationSlot(char *slotname, List *publications);
|
|||
* accommodate that.
|
||||
*/
|
||||
static void parse_subscription_options(const List *options, char **conninfo, List **publications, bool *enabled_given,
|
||||
bool *enabled, bool *slot_name_given, char **slot_name, char **synchronous_commit)
|
||||
bool *enabled, bool *slot_name_given, char **slot_name, char **synchronous_commit, bool *binary_given, bool *binary)
|
||||
{
|
||||
ListCell *lc;
|
||||
|
||||
|
|
@ -76,6 +76,10 @@ static void parse_subscription_options(const List *options, char **conninfo, Lis
|
|||
if (synchronous_commit) {
|
||||
*synchronous_commit = NULL;
|
||||
}
|
||||
if (binary) {
|
||||
*binary_given = false;
|
||||
*binary = false;
|
||||
}
|
||||
|
||||
/* Parse options */
|
||||
foreach (lc, options) {
|
||||
|
|
@ -124,6 +128,15 @@ static void parse_subscription_options(const List *options, char **conninfo, Lis
|
|||
/* Test if the given value is valid for synchronous_commit GUC. */
|
||||
(void)set_config_option("synchronous_commit", *synchronous_commit, PGC_BACKEND, PGC_S_TEST, GUC_ACTION_SET,
|
||||
false, 0, false);
|
||||
} else if (strcmp(defel->defname, "binary") == 0 && binary) {
|
||||
if (*binary_given) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting or redundant options")));
|
||||
}
|
||||
|
||||
*binary_given = true;
|
||||
*binary = defGetBoolean(defel);
|
||||
} else {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR), errmsg("unrecognized subscription parameter: %s", defel->defname)));
|
||||
|
|
@ -197,26 +210,82 @@ static Datum publicationListToArray(List *publist)
|
|||
}
|
||||
|
||||
/*
|
||||
* connect publisher and create slot.
|
||||
* the input conninfo should be encrypt, we will decrypt password inside
|
||||
* Parse the original connection string which is encrypted, poll all hosts and ports,
|
||||
* and try to connect to the publisher.
|
||||
* When checkRemoteMode is true, the remotemode must be normal or primary.
|
||||
* Return true to indicate successful connection.
|
||||
*/
|
||||
static void ConnectPublisher(char *conninfo, char *slotname)
|
||||
bool AttemptConnectPublisher(const char *conninfoOriginal, char* slotname, bool checkRemoteMode)
|
||||
{
|
||||
size_t conninfoLen = strlen(conninfoOriginal) + 1;
|
||||
|
||||
char* conninfo = NULL;
|
||||
StringInfoData conninfoWithoutHostport;
|
||||
initStringInfo(&conninfoWithoutHostport);
|
||||
HostPort* hostPortList[MAX_REPLNODE_NUM] = {NULL};
|
||||
ParseConninfo(conninfoOriginal, &conninfoWithoutHostport, hostPortList);
|
||||
if (hostPortList[0] == NULL) {
|
||||
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg(
|
||||
"invalid connection string syntax, missing host and port")));
|
||||
}
|
||||
bool connectSuccess = false;
|
||||
conninfo = (char*)palloc(conninfoLen * sizeof(char));
|
||||
for (int i = 0; i < MAX_REPLNODE_NUM; ++i) {
|
||||
if (hostPortList[i] == NULL) {
|
||||
break;
|
||||
}
|
||||
int ret = snprintf_s(conninfo, conninfoLen, conninfoLen - 1,
|
||||
"%s host=%s port=%s", conninfoWithoutHostport.data,
|
||||
hostPortList[i]->host, hostPortList[i]->port);
|
||||
securec_check_ss(ret, "\0", "\0");
|
||||
|
||||
connectSuccess = ConnectPublisher(conninfo, slotname);
|
||||
if (!connectSuccess) {
|
||||
/* try next host */
|
||||
continue;
|
||||
}
|
||||
if (!checkRemoteMode) {
|
||||
break;
|
||||
}
|
||||
ServerMode publisherServerMde = IdentifyRemoteMode();
|
||||
if (publisherServerMde == NORMAL_MODE || publisherServerMde == PRIMARY_MODE) {
|
||||
break;
|
||||
}
|
||||
/* it's a standby, try next host */
|
||||
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
|
||||
connectSuccess = false;
|
||||
}
|
||||
pfree_ext(conninfo);
|
||||
|
||||
/* clean up */
|
||||
FreeStringInfo(&conninfoWithoutHostport);
|
||||
for (int i = 0; i < MAX_REPLNODE_NUM; ++i) {
|
||||
if (hostPortList[i] == NULL) {
|
||||
break;
|
||||
}
|
||||
pfree_ext(hostPortList[i]->host);
|
||||
pfree_ext(hostPortList[i]->port);
|
||||
pfree_ext(hostPortList[i]);
|
||||
}
|
||||
return connectSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* connect to publisher with conninfo
|
||||
*/
|
||||
static bool ConnectPublisher(char* conninfo, char* slotname)
|
||||
{
|
||||
/* Try to connect to the publisher. */
|
||||
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
|
||||
SpinLockAcquire(&walrcv->mutex);
|
||||
walrcv->conn_target = REPCONNTARGET_PUBLICATION;
|
||||
SpinLockRelease(&walrcv->mutex);
|
||||
|
||||
char *decryptConninfo = DecryptConninfo(conninfo);
|
||||
char* decryptConninfo = EncryptOrDecryptConninfo(conninfo, 'D');
|
||||
bool connectSuccess = (WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_connect(decryptConninfo, NULL, slotname, -1);
|
||||
int rc = memset_s(decryptConninfo, strlen(decryptConninfo), 0, strlen(decryptConninfo));
|
||||
securec_check(rc, "", "");
|
||||
pfree_ext(decryptConninfo);
|
||||
|
||||
if (!connectSuccess) {
|
||||
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("could not connect to the publisher")));
|
||||
}
|
||||
return connectSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -293,9 +362,10 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
|||
bool enabled_given = false;
|
||||
bool enabled = true;
|
||||
char *synchronous_commit;
|
||||
char *conninfo;
|
||||
char *slotname;
|
||||
bool slotname_given;
|
||||
bool binary;
|
||||
bool binary_given;
|
||||
char originname[NAMEDATALEN];
|
||||
List *publications;
|
||||
int rc;
|
||||
|
|
@ -305,7 +375,7 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
|||
* Connection and publication should not be specified here.
|
||||
*/
|
||||
parse_subscription_options(stmt->options, NULL, NULL, &enabled_given, &enabled, &slotname_given, &slotname,
|
||||
&synchronous_commit);
|
||||
&synchronous_commit, &binary_given, &binary);
|
||||
|
||||
/*
|
||||
* Since creating a replication slot is not transactional, rolling back
|
||||
|
|
@ -333,11 +403,10 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
|||
synchronous_commit = "off";
|
||||
}
|
||||
|
||||
conninfo = stmt->conninfo;
|
||||
publications = stmt->publication;
|
||||
|
||||
/* Check the connection info string. */
|
||||
libpqrcv_check_conninfo(conninfo);
|
||||
libpqrcv_check_conninfo(stmt->conninfo);
|
||||
|
||||
/* Everything ok, form a new tuple. */
|
||||
rc = memset_s(values, sizeof(values), 0, sizeof(values));
|
||||
|
|
@ -349,18 +418,12 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
|||
values[Anum_pg_subscription_subname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->subname));
|
||||
values[Anum_pg_subscription_subowner - 1] = ObjectIdGetDatum(owner);
|
||||
values[Anum_pg_subscription_subenabled - 1] = BoolGetDatum(enabled);
|
||||
values[Anum_pg_subscription_subbinary - 1] = BoolGetDatum(binary);
|
||||
|
||||
/* encrypt conninfo */
|
||||
List *conninfoList = ConninfoToDefList(stmt->conninfo);
|
||||
/* Sensitive options for subscription, will be encrypted when saved to catalog. */
|
||||
const char* sensitiveOptionsArray[] = {"password"};
|
||||
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
|
||||
EncryptGenericOptions(conninfoList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
||||
char *encryptConninfo = DefListToString(conninfoList);
|
||||
|
||||
char *encryptConninfo = EncryptOrDecryptConninfo(stmt->conninfo, 'E');
|
||||
values[Anum_pg_subscription_subconninfo - 1] = CStringGetTextDatum(encryptConninfo);
|
||||
|
||||
pfree_ext(conninfoList);
|
||||
if (enabled) {
|
||||
if (!slotname_given) {
|
||||
slotname = stmt->subname;
|
||||
|
|
@ -396,11 +459,14 @@ ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel)
|
|||
*/
|
||||
if (enabled) {
|
||||
Assert(slotname);
|
||||
ConnectPublisher(encryptConninfo, slotname);
|
||||
|
||||
if (!AttemptConnectPublisher(encryptConninfo, slotname, true)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("Failed to connect to publisher.")));
|
||||
}
|
||||
|
||||
CreateSlotInPublisher(slotname);
|
||||
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
|
||||
}
|
||||
|
||||
pfree_ext(encryptConninfo);
|
||||
heap_close(rel, RowExclusiveLock);
|
||||
rc = memset_s(stmt->conninfo, strlen(stmt->conninfo), 0, strlen(stmt->conninfo));
|
||||
|
|
@ -439,6 +505,8 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
|||
Oid subid;
|
||||
bool enabled_given = false;
|
||||
bool enabled;
|
||||
bool binary_given;
|
||||
bool binary;
|
||||
char *synchronous_commit;
|
||||
char *conninfo;
|
||||
char *slot_name;
|
||||
|
|
@ -473,7 +541,7 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
|||
|
||||
/* Parse options. */
|
||||
parse_subscription_options(stmt->options, &conninfo, &publications, &enabled_given, &enabled, &slotname_given,
|
||||
&slot_name, &synchronous_commit);
|
||||
&slot_name, &synchronous_commit, &binary_given, &binary);
|
||||
|
||||
/* Form a new tuple. */
|
||||
rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls));
|
||||
|
|
@ -490,23 +558,15 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
|||
if (conninfo) {
|
||||
/* Check the connection info string. */
|
||||
libpqrcv_check_conninfo(conninfo);
|
||||
|
||||
/* encrypt conninfo */
|
||||
List *conninfoList = ConninfoToDefList(conninfo);
|
||||
/* Sensitive options for subscription, will be encrypted when saved to catalog. */
|
||||
const char* sensitiveOptionsArray[] = {"password"};
|
||||
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
|
||||
EncryptGenericOptions(conninfoList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
||||
encryptConninfo = DefListToString(conninfoList);
|
||||
needFreeConninfo = true;
|
||||
|
||||
encryptConninfo = EncryptOrDecryptConninfo(conninfo, 'E');
|
||||
rc = memset_s(conninfo, strlen(conninfo), 0, strlen(conninfo));
|
||||
securec_check(rc, "\0", "\0");
|
||||
values[Anum_pg_subscription_subconninfo - 1] = CStringGetTextDatum(encryptConninfo);
|
||||
replaces[Anum_pg_subscription_subconninfo - 1] = true;
|
||||
needFreeConninfo = true;
|
||||
|
||||
pfree_ext(conninfoList);
|
||||
|
||||
/* need to check whether new conninfo can be used to connect to new publisher */
|
||||
if (sub->enabled || (enabled_given && enabled)) {
|
||||
/* we need to check whether new conninfo can be used to connect to new publisher */
|
||||
checkConn = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -548,6 +608,10 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
|||
values[Anum_pg_subscription_subsynccommit - 1] = CStringGetTextDatum(synchronous_commit);
|
||||
replaces[Anum_pg_subscription_subsynccommit - 1] = true;
|
||||
}
|
||||
if (binary_given) {
|
||||
values[Anum_pg_subscription_subbinary - 1] = BoolGetDatum(binary);
|
||||
replaces[Anum_pg_subscription_subbinary - 1] = true;
|
||||
}
|
||||
if (publications != NIL) {
|
||||
values[Anum_pg_subscription_subpublications - 1] = publicationListToArray(publications);
|
||||
replaces[Anum_pg_subscription_subpublications - 1] = true;
|
||||
|
|
@ -570,16 +634,18 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
|||
if (sub->enabled && !enabled) {
|
||||
ereport(ERROR, (errmsg("If you want to deactivate this subscription, use DROP SUBSCRIPTION.")));
|
||||
}
|
||||
/* enable subscription */
|
||||
if (!sub->enabled && enabled) {
|
||||
/* if slot hasn't been created, then create it */
|
||||
if (!sub->slotname || !*(sub->slotname)) {
|
||||
/* enabling subscription, but slot hasn't been created,
|
||||
* then mark createSlot to true.
|
||||
*/
|
||||
if (!sub->enabled && enabled && (!sub->slotname || !*(sub->slotname))) {
|
||||
createSlot = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkConn || createSlot || validateSlot) {
|
||||
ConnectPublisher(encryptConninfo, finalSlotName);
|
||||
if (!AttemptConnectPublisher(encryptConninfo, finalSlotName, true)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg(
|
||||
checkConn ? "The new conninfo cannot connect to new publisher." : "Failed to connect to publisher.")));
|
||||
}
|
||||
|
||||
if (createSlot) {
|
||||
CreateSlotInPublisher(finalSlotName);
|
||||
|
|
@ -597,12 +663,6 @@ ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt)
|
|||
if (needFreeConninfo) {
|
||||
pfree_ext(encryptConninfo);
|
||||
}
|
||||
|
||||
if (conninfo) {
|
||||
rc = memset_s(conninfo, strlen(conninfo), 0, strlen(conninfo));
|
||||
securec_check(rc, "", "");
|
||||
}
|
||||
|
||||
return myself;
|
||||
}
|
||||
|
||||
|
|
@ -753,7 +813,11 @@ void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
|
|||
initStringInfo(&cmd);
|
||||
appendStringInfo(&cmd, "DROP_REPLICATION_SLOT %s", quote_identifier(slotname));
|
||||
|
||||
ConnectPublisher(conninfo, slotname);
|
||||
if (!AttemptConnectPublisher(conninfo, slotname, true)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg(
|
||||
"could not connect to publisher.")));
|
||||
}
|
||||
|
||||
PG_TRY();
|
||||
{
|
||||
int sqlstate = 0;
|
||||
|
|
@ -779,6 +843,7 @@ void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
|
|||
|
||||
(WalReceiverFuncTable[GET_FUNC_IDX]).walrcv_disconnect();
|
||||
|
||||
pfree_ext(conninfo);
|
||||
pfree(cmd.data);
|
||||
heap_close(rel, NoLock);
|
||||
}
|
||||
|
|
@ -908,3 +973,149 @@ void RenameSubscription(List *oldname, const char *newname)
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the host or port string into a string array,
|
||||
* where host and port are separated by ",".
|
||||
* input: conn --- host or port string separated by ","
|
||||
* output: connArray --- host or port string array
|
||||
* return: the length of connArray
|
||||
* for example:
|
||||
* (1):
|
||||
* conn = 1.1.1.1,2.2.2.2,...,9.9.9.9
|
||||
* connArray = {
|
||||
* 1,.1.1.1,
|
||||
* 2.2.2.2,
|
||||
* ...,
|
||||
* 9.9.9.9
|
||||
* }
|
||||
* return 9
|
||||
* (2):
|
||||
* conn = 1,2,...,9
|
||||
* connArray = {1,2,...,9}
|
||||
* return 9
|
||||
*/
|
||||
static int HostsPortsToArray(const char* conn, char** connArray)
|
||||
{
|
||||
if (conn == NULL) {
|
||||
return 0;
|
||||
}
|
||||
char* cp = NULL;
|
||||
char* cur = NULL;
|
||||
char *buf = pstrdup(conn);
|
||||
|
||||
cp = buf;
|
||||
int i = 0;
|
||||
while (*cp) {
|
||||
cur = cp;
|
||||
while (*cp && *cp != ',') {
|
||||
++cp;
|
||||
}
|
||||
if (*cp == ',') {
|
||||
*cp = '\0';
|
||||
++cp;
|
||||
}
|
||||
if (i >= MAX_REPLNODE_NUM) {
|
||||
ereport(ERROR, (errmsg("Currently, a maximum of %d servers are "
|
||||
"supported.", MAX_REPLNODE_NUM)));
|
||||
}
|
||||
connArray[i++] = pstrdup(cur);
|
||||
|
||||
if (*cp == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
pfree(buf);
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse host and port
|
||||
*/
|
||||
static void ParseHostPort(char* hoststr, char* portstr, HostPort** hostPortList)
|
||||
{
|
||||
char* hosts[MAX_REPLNODE_NUM] = {NULL};
|
||||
char* ports[MAX_REPLNODE_NUM] = {NULL};
|
||||
int hostNum = HostsPortsToArray(hoststr, hosts);
|
||||
int portNum = HostsPortsToArray(portstr, ports);
|
||||
if (hostNum != portNum) {
|
||||
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("The number of host and port are inconsistent.")));
|
||||
}
|
||||
|
||||
for (int i = 0; i < hostNum; ++i) {
|
||||
hostPortList[i] = (HostPort*)palloc(sizeof(HostPort));
|
||||
hostPortList[i]->host = hosts[i];
|
||||
hostPortList[i]->port = ports[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse conninfo
|
||||
* conninfo format:
|
||||
* 'dbname=abc user=username password=xxxx host=ip1,ip2,...,ip9 port=p1,p2,...,p9'
|
||||
* after parsing:
|
||||
* conninfoWithoutHostPort:
|
||||
* 'dbname=abc user=username password=xxxx'
|
||||
* hostPortList:
|
||||
* {
|
||||
* {host=ip1, port=p1},
|
||||
* {host=ip2, port=p2},
|
||||
* ...
|
||||
* {host=ip9, port=p9}
|
||||
* }
|
||||
*/
|
||||
void ParseConninfo(const char* conninfo, StringInfoData* conninfoWithoutHostPort, HostPort** hostPortList)
|
||||
{
|
||||
List* conninfoList = ConninfoToDefList(conninfo);
|
||||
ListCell* l = NULL;
|
||||
|
||||
char* hostStr = NULL;
|
||||
char* portStr = NULL;
|
||||
foreach (l, conninfoList) {
|
||||
DefElem* defel = (DefElem*)lfirst(l);
|
||||
if (pg_strcasecmp(defel->defname, "host") == 0) {
|
||||
hostStr = defGetString(defel);
|
||||
} else if (pg_strcasecmp(defel->defname, "port") == 0) {
|
||||
portStr = defGetString(defel);
|
||||
} else {
|
||||
appendStringInfo(conninfoWithoutHostPort, "%s=%s ", defel->defname, defGetString(defel));
|
||||
}
|
||||
}
|
||||
if (hostPortList != NULL) {
|
||||
ParseHostPort(hostStr, portStr, hostPortList);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* encrypt conninfo when action = 'E'
|
||||
* decrypt conninfo when action = 'D'
|
||||
* conninfoNew: encrypted or decrypted conninfo
|
||||
*/
|
||||
char* EncryptOrDecryptConninfo(const char* conninfo, const char action)
|
||||
{
|
||||
/* parse conninfo to list */
|
||||
List *conninfoList = ConninfoToDefList(conninfo);
|
||||
/* Sensitive options for subscription */
|
||||
const char* sensitiveOptionsArray[] = {"password"};
|
||||
const int sensitiveArrayLength = lengthof(sensitiveOptionsArray);
|
||||
switch (action) {
|
||||
/* Encrypt */
|
||||
case 'E':
|
||||
EncryptGenericOptions(conninfoList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
||||
break;
|
||||
|
||||
/* Decrypt */
|
||||
case 'D':
|
||||
DecryptOptions(conninfoList, sensitiveOptionsArray, sensitiveArrayLength, SUBSCRIPTION_MODE);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
char* conninfoNew = DefListToString(conninfoList);
|
||||
ClearListContent(conninfoList);
|
||||
list_free_ext(conninfoList);
|
||||
|
||||
return conninfoNew;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23175,7 +23175,7 @@ static void checkValidationForExchangeTable(Relation partTableRel, Relation ordT
|
|||
int2 bucketId = InvalidBktId;
|
||||
|
||||
// get right partition oid for the tuple
|
||||
targetPartOid = heapTupleGetPartitionId(partTableRel, (HeapTuple) tuple);
|
||||
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,10 +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;
|
||||
|
||||
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))();
|
||||
}
|
||||
|
|
@ -862,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);
|
||||
|
|
@ -6112,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,
|
||||
|
|
@ -7569,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) && u_sess->attr.attr_sql.b_sql_plugin) {
|
||||
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.dolphin) {
|
||||
InitBSqlPluginHookIfNeeded();
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -848,7 +848,7 @@ static bool InitSession(knl_session_context* session)
|
|||
t_thrd.proc_cxt.PostInit->InitSession();
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.b_sql_plugin) {
|
||||
if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT) && u_sess->attr.attr_sql.dolphin) {
|
||||
InitBSqlPluginHookIfNeeded();
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -720,7 +720,6 @@ static Datum ExecEvalWholeRowVar(
|
|||
{
|
||||
Var* variable = (Var*)wrvstate->xprstate.expr;
|
||||
TupleTableSlot* slot = NULL;
|
||||
TupleDesc slot_tupdesc;
|
||||
bool needslow = false;
|
||||
|
||||
if (isDone != NULL)
|
||||
|
|
@ -802,21 +801,13 @@ static Datum ExecEvalWholeRowVar(
|
|||
if (wrvstate->wrv_junkFilter != NULL)
|
||||
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
|
||||
|
||||
slot_tupdesc = slot->tts_tupleDescriptor;
|
||||
|
||||
/*
|
||||
* If it's a RECORD Var, we'll use the slot's type ID info. It's likely
|
||||
* that the slot's type is also RECORD; if so, make sure it's been
|
||||
* "blessed", so that the Datum can be interpreted later.
|
||||
*
|
||||
* If the Var identifies a named composite type, we must check that the
|
||||
* actual tuple type is compatible with it.
|
||||
*/
|
||||
if (variable->vartype == RECORDOID) {
|
||||
if (slot_tupdesc->tdtypeid == RECORDOID && slot_tupdesc->tdtypmod < 0)
|
||||
assign_record_type_typmod(slot_tupdesc);
|
||||
} else {
|
||||
if (variable->vartype != RECORDOID) {
|
||||
TupleDesc var_tupdesc;
|
||||
TupleDesc slot_tupdesc;
|
||||
int i;
|
||||
|
||||
/*
|
||||
|
|
@ -833,6 +824,8 @@ static Datum ExecEvalWholeRowVar(
|
|||
*/
|
||||
var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
|
||||
|
||||
slot_tupdesc = slot->tts_tupleDescriptor;
|
||||
|
||||
if (var_tupdesc->natts != slot_tupdesc->natts)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
|
|
@ -886,6 +879,7 @@ static Datum ExecEvalWholeRowFast(
|
|||
{
|
||||
Var* variable = (Var*)wrvstate->xprstate.expr;
|
||||
TupleTableSlot* slot = NULL;
|
||||
TupleDesc slot_tupdesc;
|
||||
HeapTuple tuple;
|
||||
TupleDesc tupleDesc;
|
||||
HeapTupleHeader dtuple;
|
||||
|
|
@ -915,6 +909,17 @@ static Datum ExecEvalWholeRowFast(
|
|||
if (wrvstate->wrv_junkFilter != NULL)
|
||||
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
|
||||
|
||||
/*
|
||||
* If it's a RECORD Var, we'll use the slot's type ID info. It's likely
|
||||
* that the slot's type is also RECORD; if so, make sure it's been
|
||||
* "blessed", so that the Datum can be interpreted later.
|
||||
*/
|
||||
slot_tupdesc = slot->tts_tupleDescriptor;
|
||||
if (variable->vartype == RECORDOID) {
|
||||
if (slot_tupdesc->tdtypeid == RECORDOID && slot_tupdesc->tdtypmod < 0)
|
||||
assign_record_type_typmod(slot_tupdesc);
|
||||
}
|
||||
|
||||
tuple = ExecFetchSlotTuple(slot);
|
||||
tupleDesc = slot->tts_tupleDescriptor;
|
||||
|
||||
|
|
@ -1896,6 +1901,13 @@ static void set_result_for_plpgsql_language_function_with_outparam(FuncExprState
|
|||
* @bool has_cursor_return - need store out-args cursor info.
|
||||
* @bool has_refcursor - need store in-args cursor info.
|
||||
* @bool isSetReturnFunc - indicate function returns a set.
|
||||
*The execution process of the ExecMakeFunctionResult function is as follows.
|
||||
* (1) Check whether funcResultStore exists, if so, get the result and return it
|
||||
(2) The calculated parameter values are stored in fcinfo.
|
||||
(3) Pass the parameter into the expression function to calculate the expression,
|
||||
first determine whether the parameter args exists null, and then determine the return mode of the function that returns the set,
|
||||
SFRM_ValuePerCall mode is to return a value each time the call, The SFRM_Materialize schema is the result set instantiated in Tuplestore.
|
||||
(4) Calculate and return results according to different modes.
|
||||
*/
|
||||
template <bool has_refcursor, bool has_cursor_return, bool isSetReturnFunc>
|
||||
static Datum ExecMakeFunctionResult(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
|
||||
|
|
@ -3077,6 +3089,10 @@ no_function_result:
|
|||
/* ----------------------------------------------------------------
|
||||
* ExecEvalFunc
|
||||
* ----------------------------------------------------------------
|
||||
*The execution process of the ExecEvalFunc function is as follows.
|
||||
(1) Initialize the FuncExprState node by init_fcache function, including initialization parameters, memory management, etc.
|
||||
(2) Judge whether the returned result is of set type according to the data in the FuncExprState function,
|
||||
and call the corresponding function to calculate the result.
|
||||
*/
|
||||
static Datum ExecEvalFunc(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
|
||||
{
|
||||
|
|
@ -3521,6 +3537,10 @@ static Datum ExecEvalNot(BoolExprState* notclause, ExprContext* econtext, bool*
|
|||
/* ----------------------------------------------------------------
|
||||
* ExecEvalOr
|
||||
* ----------------------------------------------------------------
|
||||
*The main execution process of ExecEvalOr function is as follows.
|
||||
(1) Traverse child expression clauses.
|
||||
(2) Use the function ExecEvalExpr to call the expression calculation function in clause and calculate the result.
|
||||
(3) To judge the results, if there is a result in the or expression that meets the conditions, it will jump out of the loop and return directly.
|
||||
*/
|
||||
static Datum ExecEvalOr(BoolExprState* orExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
|
||||
{
|
||||
|
|
@ -5159,6 +5179,11 @@ Datum ExecEvalExprSwitchContext(ExprState* expression, ExprContext* econtext, bo
|
|||
* 'parent' may be NULL if we are preparing an expression that is not
|
||||
* associated with a plan tree. (If so, it can't have aggs or subplans.)
|
||||
* This case should usually come through ExecPrepareExpr, not directly here.
|
||||
*The execution process of the ExecInitExpr function is as follows.
|
||||
(1) Determine whether the input node is empty. If it is empty,return NULL directly, indicating that there is no restriction for expression.
|
||||
(2) According to the type of node input,Initialize variable evalfunc which is the execution function corresponding to node,
|
||||
If the node has parameters or expressions, the function ExecInitExpr will be recursively called and ExprState tree will be generated.
|
||||
(3) Return ExprState tree, and execute the expression recursively according to ExprState tree.
|
||||
*/
|
||||
ExprState* ExecInitExpr(Expr* node, PlanState* parent)
|
||||
{
|
||||
|
|
@ -6118,6 +6143,10 @@ Datum fetch_lob_value_from_tuple(varatt_lob_pointer* lob_pointer, Oid update_oid
|
|||
* of *isDone = ExprMultipleResult signifies a set element, and a return
|
||||
* of *isDone = ExprEndResult signifies end of the set of tuple.
|
||||
* We assume that *isDone has been initialized to ExprSingleResult by caller.
|
||||
* The execution process of the ExecTargetList function is as follows.
|
||||
(1) Iterate over the expressions in targetlist.
|
||||
(2) Calculation of expression results.
|
||||
(3) Judge the itemIsDone[resind] parameter in the results and generate the final tuple.
|
||||
*/
|
||||
static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* values, bool* isnull,
|
||||
ExprDoneCond* itemIsDone, ExprDoneCond* isDone)
|
||||
|
|
|
|||
|
|
@ -322,7 +322,6 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
|
|||
}
|
||||
if (found) {
|
||||
/* Found tuple, try to lock it in the lockmode. */
|
||||
outslot->tts_tuple = ExecMaterializeSlot(outslot);
|
||||
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
|
||||
/*
|
||||
* If the tuple is locked, wait for locking transaction to finish
|
||||
|
|
@ -346,12 +345,16 @@ static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation
|
|||
ItemPointer tid = tableam_tops_get_t_self(targetRel, outslot->tts_tuple);
|
||||
|
||||
if (RelationIsUstoreFormat(targetRel)) {
|
||||
/* materialize the slot, so we can visit it after the scan is end */
|
||||
outslot->tts_tuple = UHeapMaterialize(outslot);
|
||||
ItemPointerCopy(tid, &UHeaplocktup.ctid);
|
||||
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
|
||||
securec_check(rc, "\0", "\0");
|
||||
UHeaplocktup.disk_tuple = &tbuf.hdr;
|
||||
locktup = &UHeaplocktup;
|
||||
} else {
|
||||
/* materialize the slot, so we can visit it after the scan is end */
|
||||
outslot->tts_tuple = ExecMaterializeSlot(outslot);
|
||||
ItemPointerCopy(tid, &heaplocktup.t_self);
|
||||
locktup = &heaplocktup;
|
||||
}
|
||||
|
|
@ -401,6 +404,10 @@ static bool tuple_equals_slot(TupleDesc desc, const Tuple tup, TupleTableSlot *s
|
|||
/* Check equality of the attributes. */
|
||||
for (attrnum = 0; attrnum < desc->natts; attrnum++) {
|
||||
TypeCacheEntry *typentry;
|
||||
/* skip generate column */
|
||||
if (GetGeneratedCol(desc, attrnum)) {
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* If one value is NULL and other is not, then they are certainly not
|
||||
* equal
|
||||
|
|
@ -478,7 +485,6 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
|
|||
|
||||
found = true;
|
||||
ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
|
||||
outslot->tts_tuple = ExecMaterializeSlot(outslot);
|
||||
|
||||
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
|
||||
/*
|
||||
|
|
@ -511,12 +517,16 @@ static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, Tuple
|
|||
ItemPointer tid = tableam_tops_get_t_self(rel, outslot->tts_tuple);
|
||||
|
||||
if (RelationIsUstoreFormat(targetRel)) {
|
||||
/* materialize the slot, so we can visit it after the scan is end */
|
||||
outslot->tts_tuple = UHeapMaterialize(outslot);
|
||||
ItemPointerCopy(tid, &UHeaplocktup.ctid);
|
||||
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
|
||||
securec_check(rc, "\0", "\0");
|
||||
UHeaplocktup.disk_tuple = &tbuf.hdr;
|
||||
locktup = &UHeaplocktup;
|
||||
} else {
|
||||
/* materialize the slot, so we can visit it after the scan is end */
|
||||
outslot->tts_tuple = ExecMaterializeSlot(outslot);
|
||||
ItemPointerCopy(tid, &heaplocktup.t_self);
|
||||
locktup = &heaplocktup;
|
||||
}
|
||||
|
|
@ -653,7 +663,6 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
|
|||
/* Compute stored generated columns */
|
||||
if (rel->rd_att->constr && rel->rd_att->constr->has_generated_stored) {
|
||||
ExecComputeStoredGenerated(resultRelInfo, estate, slot, tuple, CMD_UPDATE);
|
||||
tuple = slot->tts_tuple;
|
||||
}
|
||||
|
||||
/* Check the constraints of the tuple */
|
||||
|
|
@ -672,10 +681,11 @@ void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot
|
|||
rowMovement = true;
|
||||
}
|
||||
|
||||
tuple = slot->tts_tuple;
|
||||
CommandId cid = GetCurrentCommandId(true);
|
||||
/* OK, update the tuple and index entries for it */
|
||||
if (!rowMovement) {
|
||||
res = tableam_tuple_update(targetRelation, parentRelation, searchSlotTid, slot->tts_tuple, cid,
|
||||
res = tableam_tuple_update(targetRelation, parentRelation, searchSlotTid, tuple, cid,
|
||||
InvalidSnapshot, estate->es_snapshot, true, &oldslot, &tmfd, &updateIndexes, &modifiedIdxAttrs,
|
||||
false, allowInplaceUpdate);
|
||||
CheckTupleModifyRes(res);
|
||||
|
|
|
|||
|
|
@ -2947,9 +2947,9 @@ void SetOneOfCompressOption(DefElem* defElem, TableCreateSupport* tableCreateSup
|
|||
} else if (pg_strcasecmp(defname, "compress_level") == 0) {
|
||||
tableCreateSupport->compressLevel = true;
|
||||
} else if (pg_strcasecmp(defname, "compress_byte_convert") == 0) {
|
||||
tableCreateSupport->compressByteConvert = true;
|
||||
tableCreateSupport->compressByteConvert = defGetBoolean(defElem);
|
||||
} else if (pg_strcasecmp(defname, "compress_diff_convert") == 0) {
|
||||
tableCreateSupport->compressDiffConvert = true;
|
||||
tableCreateSupport->compressDiffConvert = defGetBoolean(defElem);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8965,6 +8965,8 @@ void StartupXLOG(void)
|
|||
g_instance.comm_cxt.predo_cxt.redoPf.redo_done_time = 0;
|
||||
pg_atomic_write_u32(&(g_instance.comm_cxt.localinfo_cxt.is_finish_redo), 0);
|
||||
|
||||
NotifyGscRecoveryStarted();
|
||||
|
||||
/*
|
||||
* Initialize WAL insert status array and the flush index - lastWalStatusEntryFlushed.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1818,3 +1818,47 @@ void UHeapSlotStoreUHeapTuple(UHeapTuple utuple, TupleTableSlot *slot, bool shou
|
|||
/* Mark extracted state invalid */
|
||||
slot->tts_nvalid = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make the contents of the uheap table's slot contents solely depend on the slot(make them a local copy),
|
||||
* and not on underlying external resources like another memory context, buffers etc.
|
||||
*
|
||||
* @pram slot: slot to be materialized.
|
||||
*/
|
||||
Tuple UHeapMaterialize(TupleTableSlot *slot)
|
||||
{
|
||||
Assert(!slot->tts_isempty);
|
||||
Assert(slot->tts_tupslotTableAm == TAM_USTORE);
|
||||
Assert(slot->tts_tupleDescriptor != NULL);
|
||||
/*
|
||||
* If we have a regular physical tuple, and it's locally palloc'd, we have
|
||||
* nothing to do.
|
||||
*/
|
||||
if (slot->tts_tuple && slot->tts_shouldFree) {
|
||||
return slot->tts_tuple;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise, copy or build a physical tuple, and store it into the slot.
|
||||
*
|
||||
* We may be called in a context that is shorter-lived than the tuple
|
||||
* slot, but we have to ensure that the materialized tuple will survive
|
||||
* anyway.
|
||||
*/
|
||||
MemoryContext old_context = MemoryContextSwitchTo(slot->tts_mcxt);
|
||||
if (slot->tts_tuple != NULL) {
|
||||
slot->tts_tuple = UHeapCopyTuple((UHeapTuple)slot->tts_tuple);
|
||||
} else {
|
||||
slot->tts_tuple = UHeapFormTuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull);
|
||||
}
|
||||
slot->tts_shouldFree = true;
|
||||
MemoryContextSwitchTo(old_context);
|
||||
|
||||
/*
|
||||
* Have to deform from scratch, otherwise tts_values[] entries could point
|
||||
* into the non-materialized tuple (which might be gone when accessed).
|
||||
*/
|
||||
slot->tts_nvalid = 0;
|
||||
return slot->tts_tuple;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -238,15 +238,19 @@ public:
|
|||
return m_purpose;
|
||||
}
|
||||
|
||||
void GcStartTxnMTtests()
|
||||
GcEpochType GcStartInnerTxn()
|
||||
{
|
||||
if (m_gcEpoch != GetGlobalEpoch())
|
||||
m_gcEpoch = GetGlobalEpoch();
|
||||
m_gcEpoch = GetGlobalEpoch();
|
||||
|
||||
return m_gcEpoch;
|
||||
}
|
||||
|
||||
void GcEndTxnMTtests()
|
||||
void GcEndInnerTxn(bool clean_gc)
|
||||
{
|
||||
RunQuicese();
|
||||
if (clean_gc) {
|
||||
RunQuicese();
|
||||
}
|
||||
m_gcEpoch = 0;
|
||||
}
|
||||
|
||||
void GcStartTxn()
|
||||
|
|
@ -272,6 +276,7 @@ public:
|
|||
RunQuicese();
|
||||
m_managerLock.unlock();
|
||||
}
|
||||
m_gcEpoch = 0;
|
||||
m_isTxnStarted = false;
|
||||
}
|
||||
|
||||
|
|
@ -313,7 +318,6 @@ public:
|
|||
{
|
||||
if (m_performGcEpoch != g_gcActiveEpoch)
|
||||
HardQuiesce(m_rcuFreeCount);
|
||||
m_gcEpoch = 0;
|
||||
}
|
||||
|
||||
/** @brief Clean all object at the end of the session */
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue