From 53e272bdae82e18a48943a206cc28f675a78d2f8 Mon Sep 17 00:00:00 2001 From: w00427717 Date: Tue, 18 Aug 2020 13:12:21 +0800 Subject: [PATCH 1/6] gs_basebackup: support mot backup --- src/bin/pg_basebackup/pg_basebackup.cpp | 148 +++-------- src/bin/pg_ctl/backup.cpp | 3 +- src/bin/pg_ctl/fetchmot.cpp | 315 +++++++++++++++++++++++- src/bin/pg_ctl/pg_ctl.cpp | 5 +- src/include/access/xlog.h | 7 +- src/include/gs_tar_const.h | 76 ++++++ 6 files changed, 431 insertions(+), 123 deletions(-) create mode 100644 src/include/gs_tar_const.h diff --git a/src/bin/pg_basebackup/pg_basebackup.cpp b/src/bin/pg_basebackup/pg_basebackup.cpp index 2bb16734d..52217cf09 100644 --- a/src/bin/pg_basebackup/pg_basebackup.cpp +++ b/src/bin/pg_basebackup/pg_basebackup.cpp @@ -19,7 +19,7 @@ /* * We have to use postgres.h not postgres_fe.h here, because there's so much * backend-only stuff in the XLOG include files we need. But we need a - * frontend-ish environment otherwise. Hence this ugly hack. + * frontend-ish environment otherwise. Hence this ugly hack. */ #include "postgres.h" #include "knl/knl_variable.h" @@ -31,13 +31,10 @@ #include #include -#ifdef HAVE_LIBZ -#include "zlib.h" -#endif - #include "getopt_long.h" #include "receivelog.h" #include "streamutil.h" +#include "gs_tar_const.h" #include "bin/elog.h" #include "lib/string.h" @@ -55,7 +52,6 @@ typedef struct TablespaceList { /* Global options */ char* basedir = NULL; static TablespaceList tablespacee_dirs = {NULL, NULL}; -static const int BLOCK_SIZE = 2560; /* gs_tar block size */ char* g_xlogOption = NULL; char format = 'p'; /* p(lain)/t(ar) */ char* label = "gs_basebackup base backup"; @@ -66,17 +62,6 @@ bool includewal = false; bool streamwal = false; bool fastcheckpoint = false; -/* fileStream result */ -static const int READ_ERROR = -2; - -/* gs_tar offset */ -static const int LEN_LEFT = 1048; -static const int FILE_PADDING = 511; /* All files are padded up to 512 bytes */ -static const int FILE_TYPE = 1080; - -/* file type */ -static const char TYPE_DICTORY = '5'; - extern char** tblspaceDirectory; extern int tblspaceCount; extern int tblspaceIndex; @@ -122,21 +107,8 @@ static int GsTar(int argc, char** argv); static int GsBaseBackup(int argc, char** argv); static const char* get_tablespace_mapping(const char* dir); -extern void FetchMotCheckpoint(const char* basedir, PGconn* fetchConn, const char* progname, bool verbose); - -#ifdef HAVE_LIBZ -static const char* get_gz_error(gzFile gzf) -{ - int errnum; - const char* errmsg = NULL; - - errmsg = gzerror(gzf, &errnum); - if (errnum == Z_ERRNO) - return strerror(errno); - else - return errmsg; -} -#endif +extern void FetchMotCheckpoint(const char* basedir, PGconn* fetchConn, const char* progname, bool verbose, + const char format = 'p', const int compresslevel = 0); /* * Split argument into old_dir and new_dir and append to tablespace mapping @@ -144,7 +116,7 @@ static const char* get_gz_error(gzFile gzf) */ static void tablespace_list_append(const char* arg) { - TablespaceListCell* cell = (TablespaceListCell*)malloc(sizeof(TablespaceListCell)); + TablespaceListCell* cell = (TablespaceListCell*)xmalloc0(sizeof(TablespaceListCell)); char* dst = NULL; char* dst_ptr = NULL; const char* arg_ptr = NULL; @@ -601,32 +573,6 @@ static void progress_report(int tablespacenum, const char* filename) fprintf(stderr, "\r"); } -#ifdef HAVE_LIBZ -static gzFile openGzFile(const char* filename) -{ - gzFile ztarfile = gzopen(filename, "wb"); - if (gzsetparams(ztarfile, compresslevel, Z_DEFAULT_STRATEGY) != Z_OK) { - fprintf( - stderr, _("%s: could not set compression level %d: %s\n"), progname, compresslevel, get_gz_error(ztarfile)); - disconnect_and_exit(1); - } - return ztarfile; -} - -static void writeGzFile(gzFile ztarfile, char* copybuf, int buf_size, char* filename) -{ - if (gzwrite(ztarfile, copybuf, buf_size) != buf_size) { - fprintf(stderr, - _("%s: could not write to compressed file \"%s\": %s\n"), - progname, - filename, - get_gz_error(ztarfile)); - disconnect_and_exit(1); - } -} - -#endif - /* * Receive a tar format file from the connection to the server, and write * the data from this file directly into a tar file. If compression is @@ -687,7 +633,15 @@ static void ReceiveTarFile(PGconn* conn, PGresult* res, int rownum) if (compresslevel != 0) { errorno = snprintf_s(filename, sizeof(filename), sizeof(filename) - 1, "%s/base.tar.gz", basedir); securec_check_ss_c(errorno, "", ""); - ztarfile = openGzFile(filename); + ztarfile = openGzFile(filename, compresslevel); + if (ztarfile == NULL) { + fprintf(stderr, + _("%s: could not set compression level %d: %s\n"), + progname, + compresslevel, + get_gz_error(ztarfile)); + disconnect_and_exit(1); + } } else #endif { @@ -706,7 +660,7 @@ static void ReceiveTarFile(PGconn* conn, PGresult* res, int rownum) errorno = snprintf_s( filename, sizeof(filename), sizeof(filename) - 1, "%s/%s.tar.gz", basedir, PQgetvalue(res, rownum, 0)); securec_check_ss_c(errorno, "", ""); - ztarfile = openGzFile(filename); + ztarfile = openGzFile(filename, compresslevel); } else #endif { @@ -760,30 +714,6 @@ static void ReceiveTarFile(PGconn* conn, PGresult* res, int rownum) int r = PQgetCopyData(conn, ©buf, 0); if (r == -1) { - /* - * End of chunk. Close file (but not stdout). - * - * Also, write two completely empty blocks at the end of the tar - * file, as required by some tar programs. - */ - char zerobuf[1024]; - - errorno = memset_s(zerobuf, sizeof(zerobuf), 0, sizeof(zerobuf)); - securec_check_c(errorno, "", ""); -#ifdef HAVE_LIBZ - if (ztarfile != NULL) { - writeGzFile(ztarfile, zerobuf, sizeof(zerobuf), filename); - } else -#endif - { - if (fwrite(zerobuf, sizeof(zerobuf), 1, tarfile) != 1) { - fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), progname, filename, strerror(errno)); - fclose(tarfile); - tarfile = NULL; - disconnect_and_exit(1); - } - } - #ifdef HAVE_LIBZ if (ztarfile != NULL) { if (gzclose(ztarfile) != 0) { @@ -807,7 +737,6 @@ static void ReceiveTarFile(PGconn* conn, PGresult* res, int rownum) tarfile = NULL; } } - break; } else if (r == -2) { fprintf(stderr, _("%s: could not read COPY data: %s"), progname, PQerrorMessage(conn)); @@ -816,7 +745,14 @@ static void ReceiveTarFile(PGconn* conn, PGresult* res, int rownum) #ifdef HAVE_LIBZ if (ztarfile != NULL) { - writeGzFile(ztarfile, copybuf, r, filename); + if (!writeGzFile(ztarfile, copybuf, r)) { + fprintf(stderr, + _("%s: could not write to compressed file \"%s\": %s\n"), + progname, + filename, + get_gz_error(ztarfile)); + disconnect_and_exit(1); + } } else #endif { @@ -960,7 +896,7 @@ static void ReceiveAndUnpackTarFile(PGconn* conn, PGresult* res, int rownum) } break; - } else if (r == READ_ERROR) { + } else if (r == TAR_READ_ERROR) { fprintf(stderr, _("%s: could not read COPY data: %s"), progname, PQerrorMessage(conn)); disconnect_and_exit(1); } @@ -972,13 +908,13 @@ static void ReceiveAndUnpackTarFile(PGconn* conn, PGresult* res, int rownum) /* * No current file, so this must be the header for a new file */ - if (r != BLOCK_SIZE) { + if (r != TAR_BLOCK_SIZE) { fprintf(stderr, _("%s: invalid tar block header size: %d\n"), progname, r); disconnect_and_exit(1); } - totaldone += BLOCK_SIZE; + totaldone += TAR_BLOCK_SIZE; - if (sscanf_s(copybuf + LEN_LEFT, "%201o", ¤t_len_left) != 1) { + if (sscanf_s(copybuf + TAR_LEN_LEFT, "%201o", ¤t_len_left) != 1) { fprintf(stderr, _("%s: could not parse file size\n"), progname); disconnect_and_exit(1); } @@ -992,7 +928,7 @@ static void ReceiveAndUnpackTarFile(PGconn* conn, PGresult* res, int rownum) /* * All files are padded up to 512 bytes */ - current_padding = ((current_len_left + FILE_PADDING) & ~FILE_PADDING) - current_len_left; + current_padding = ((current_len_left + TAR_FILE_PADDING) & ~TAR_FILE_PADDING) - current_len_left; /* * First part of header is zero terminated filename @@ -1010,7 +946,7 @@ static void ReceiveAndUnpackTarFile(PGconn* conn, PGresult* res, int rownum) /* * Ends in a slash means directory or symlink to directory */ - if (copybuf[FILE_TYPE] == TYPE_DICTORY) { + if (copybuf[TAR_FILE_TYPE] == TAR_TYPE_DICTORY) { /* * Directory */ @@ -1039,12 +975,12 @@ static void ReceiveAndUnpackTarFile(PGconn* conn, PGresult* res, int rownum) filename, strerror(errno)); #endif - } else if (copybuf[FILE_TYPE] == '2') { + } else if (copybuf[TAR_FILE_TYPE] == '2') { /* * Symbolic link */ filename[strlen(filename) - 1] = '\0'; /* Remove trailing slash */ - mapped_tblspc_path = get_tablespace_mapping(©buf[FILE_TYPE + 1]); + mapped_tblspc_path = get_tablespace_mapping(©buf[TAR_FILE_TYPE + 1]); if (symlink(mapped_tblspc_path, filename) != 0) { if (IsXlogDir(filename)) { fprintf(stderr, @@ -1072,7 +1008,7 @@ static void ReceiveAndUnpackTarFile(PGconn* conn, PGresult* res, int rownum) sizeof(absolut_path) - 1, "%s/%s", basedir, - ©buf[FILE_TYPE + 1]); + ©buf[TAR_FILE_TYPE + 1]); securec_check_ss_c(errorno, "\0", "\0"); if (symlink(absolut_path, filename) != 0) { @@ -1517,9 +1453,7 @@ static void BaseBackup(void) exit(1); } ClearAndFreePasswd(); - if (format == 'p') { - FetchMotCheckpoint(basedir, conn, progname, (bool)verbose); - } + FetchMotCheckpoint(basedir, conn, progname, (bool)verbose, format, compresslevel); PQfinish(conn); conn = NULL; @@ -1555,7 +1489,7 @@ static void remove_dw_file(const char* dw_file_name, const char* target_dir, cha /* * * * delete existing double write file if existed, recreate it and write one page of zero * * @param target_dir data base root dir - * */ + * */ static void backup_dw_file(const char* target_dir) { int rc; @@ -1708,12 +1642,12 @@ static int GsTar(int argc, char** argv) copybuf = NULL; } - current_len_left = current_len_left == 0 && current_padding == 0 ? BLOCK_SIZE : current_len_left; + current_len_left = current_len_left == 0 && current_padding == 0 ? TAR_BLOCK_SIZE : current_len_left; if (current_len_left == 0 && current_padding != 0) { - copybuf = (char*)malloc(current_padding); + copybuf = (char*)xmalloc0(current_padding); r = fread(copybuf, 1, current_padding, tarfile); } else { - copybuf = (char*)malloc(current_len_left); + copybuf = (char*)xmalloc0(current_len_left); r = fread(copybuf, 1, current_len_left, tarfile); // end of file if ((uint64)r < current_len_left) { @@ -1742,12 +1676,12 @@ static int GsTar(int argc, char** argv) int filemode; /* No current file, so this must be the header for a new file */ - if (r != BLOCK_SIZE) { + if (r != TAR_BLOCK_SIZE) { fprintf(stderr, "%s: invalid tar block header size: %d\n", progname, r); fclose(tarfile); return -1; } - totaldone += BLOCK_SIZE; + totaldone += TAR_BLOCK_SIZE; if (sscanf_s(copybuf + 1048, "%201o", ¤t_len_left) != 1) { fprintf(stderr, "%s: could not parse file size\n", progname); @@ -1858,7 +1792,7 @@ static int GsTar(int argc, char** argv) } } } else { - pg_log(PG_WARNING, "unrecognized link indicator \"%c\"\n", copybuf[FILE_TYPE]); + pg_log(PG_WARNING, "unrecognized link indicator \"%c\"\n", copybuf[TAR_FILE_TYPE]); return -1; } continue; /* directory or link handled */ @@ -1979,7 +1913,7 @@ static int GsBaseBackup(int argc, char** argv) } } - while ((c = getopt_long(argc, argv, "D:l:c:h:p:U:s:X:F:T:wWvPxz", long_options, &option_index)) != -1) { + while ((c = getopt_long(argc, argv, "D:l:c:h:p:U:s:X:F:T:Z:wWvPxz", long_options, &option_index)) != -1) { switch (c) { case 'D': { GS_FREE(basedir); diff --git a/src/bin/pg_ctl/backup.cpp b/src/bin/pg_ctl/backup.cpp index 64afc59f3..59b7b4aec 100755 --- a/src/bin/pg_ctl/backup.cpp +++ b/src/bin/pg_ctl/backup.cpp @@ -127,7 +127,8 @@ static int replace_node_name(char* sSrc, const char* sMatchStr, const char* sRep static void show_full_build_process(const char* errmg); static void backup_dw_file(const char* target_dir); static void get_xlog_location(char (&xlog_location)[MAXPGPATH]); -extern void FetchMotCheckpoint(const char* basedir, PGconn* fetchConn, const char* progname, bool verbose); +extern void FetchMotCheckpoint(const char* basedir, PGconn* fetchConn, const char* progname, bool verbose, + const char format = 'p', const int compresslevel = 0); extern char* GetOptionValueFromFile(const char* fileName, const char* option); /* diff --git a/src/bin/pg_ctl/fetchmot.cpp b/src/bin/pg_ctl/fetchmot.cpp index a518b0386..f70910fcc 100644 --- a/src/bin/pg_ctl/fetchmot.cpp +++ b/src/bin/pg_ctl/fetchmot.cpp @@ -27,6 +27,8 @@ #include #include #include "postgres_fe.h" +#include "gs_tar_const.h" +#include "streamutil.h" #include "libpq/libpq-fe.h" #include "utils/builtins.h" #include "common/fe_memutils.h" @@ -40,6 +42,290 @@ static uint64 totaldone = 0; +static void CheckConnResult(PGconn* conn, const char* progname) +{ + PGresult* res = PQgetResult(conn); + if (PQresultStatus(res) != PGRES_COPY_OUT) { + fprintf(stderr, "%s: could not get COPY data stream: %s", progname, PQerrorMessage(conn)); + disconnect_and_exit(1); + } + PQclear(res); +} + +static void MotReceiveAndAppendTarFile( + const char* basedir, const char* chkptName, PGconn* conn, const char* progname, int compresslevel) +{ + CheckConnResult(conn, progname); + + FILE* tarfile = NULL; + char filename[MAXPGPATH]; + errno_t errorno = EOK; + char* copybuf = NULL; + FILE* file = NULL; + char current_path[MAXPGPATH]; + int current_len_left = 0; + int current_padding = 0; + + errorno = strncpy_s(current_path, sizeof(current_path), basedir, sizeof(current_path) - 1); + securec_check_c(errorno, "", ""); + +#ifdef HAVE_LIBZ + gzFile ztarfile = NULL; + int duplicatedfd = -1; +#endif + if (strcmp(basedir, "-") == 0) { +#ifdef HAVE_LIBZ + if (compresslevel != 0) { + duplicatedfd = dup(fileno(stdout)); + if (duplicatedfd == -1) { + fprintf(stderr, _("%s: could not allocate dup fd by fileno(stdout): %s\n"), progname, strerror(errno)); + disconnect_and_exit(1); + } + + ztarfile = gzdopen(duplicatedfd, "ab"); + if (gzsetparams(ztarfile, compresslevel, Z_DEFAULT_STRATEGY) != Z_OK) { + fprintf(stderr, + _("%s: could not set compression level %d: %s\n"), + progname, + compresslevel, + get_gz_error(ztarfile)); + close(duplicatedfd); + duplicatedfd = -1; + disconnect_and_exit(1); + } + close(duplicatedfd); + duplicatedfd = -1; + } else +#endif + tarfile = stdout; + strcpy_s(filename, 1, "-"); + } else { +#ifdef HAVE_LIBZ + if (compresslevel != 0) { + errorno = snprintf_s(filename, sizeof(filename), sizeof(filename) - 1, "%s/base.tar.gz", basedir); + securec_check_ss_c(errorno, "", ""); + ztarfile = openGzFile(filename, compresslevel, "ab"); + } else +#endif + { + errorno = snprintf_s(filename, sizeof(filename), sizeof(filename) - 1, "%s/base.tar", basedir); + // basdir has been realpath before + securec_check_ss_c(errorno, "", ""); + tarfile = fopen(filename, "ab"); + } + } + + /* chkptName header */ + copybuf = (char*)xmalloc0(TAR_BLOCK_SIZE); + + int chkptDirLen = strlen(chkptName); + errorno = strcpy_s(copybuf + 2, chkptDirLen + 1, chkptName); + securec_check_ss_c(errorno, "", ""); + + copybuf[0] = '.'; + copybuf[1] = '/'; + copybuf[chkptDirLen + 2] = '/'; + copybuf[TAR_FILE_TYPE] = TAR_TYPE_DICTORY; + + for (int i = 0; i < 11; i++) { + copybuf[TAR_LEN_LEFT + i] = '0'; + } + errorno = sprintf_s(©buf[TAR_FILE_MODE], TAR_BLOCK_SIZE - TAR_FILE_MODE, "%07o ", FILE_PERMISSION); + securec_check_ss_c(errorno, "", ""); + +#ifdef HAVE_LIBZ + if (ztarfile != NULL) { + if (!writeGzFile(ztarfile, copybuf, TAR_BLOCK_SIZE)) { + fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), progname, filename, strerror(errno)); + disconnect_and_exit(1); + } + } else +#endif + { + if (fwrite(copybuf, TAR_BLOCK_SIZE, 1, tarfile) != 1) { + fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), progname, filename, strerror(errno)); + disconnect_and_exit(1); + } + } + + while (1) { + int r; + + if (copybuf != NULL) { + PQfreemem(copybuf); + copybuf = NULL; + } + + r = PQgetCopyData(conn, ©buf, 0); + if (r == -1) { +#ifdef HAVE_LIBZ + if (ztarfile != NULL) { + if (gzclose(ztarfile) != 0) { + fprintf(stderr, + _("%s: could not close compressed file \"%s\": %s\n"), + progname, + filename, + get_gz_error(ztarfile)); + disconnect_and_exit(1); + } + } else +#endif + { + if (strcmp(basedir, "-") != 0) { + if (fclose(tarfile) != 0) { + fprintf( + stderr, _("%s: could not close file \"%s\": %s\n"), progname, filename, strerror(errno)); + tarfile = NULL; + disconnect_and_exit(1); + } + tarfile = NULL; + } + } + break; + } else if (r == TAR_READ_ERROR) { + fprintf(stderr, "%s: could not read COPY data: %s", progname, PQerrorMessage(conn)); + disconnect_and_exit(1); + } + if (current_len_left == 0 && current_padding == 0) { + /* No current file, so this must be the header for a new file */ + if (r != TAR_BLOCK_SIZE) { + fprintf(stderr, _("%s: invalid tar block header size: %d\n"), progname, r); + disconnect_and_exit(1); + } + + /* new file */ + int filemode; + totaldone += TAR_BLOCK_SIZE; + if (sscanf_s(copybuf + TAR_LEN_LEFT, "%201o", ¤t_len_left) != 1) { + fprintf(stderr, "%s: could not parse file size\n", progname); + disconnect_and_exit(1); + } + + /* Set permissions on the file */ + if (sscanf_s(©buf[TAR_FILE_MODE], "%07o ", (unsigned int*)&filemode) != 1) { + fprintf(stderr, "%s: could not parse file mode\n", progname); + disconnect_and_exit(1); + } + + /* + * All files are padded up to 512 bytes + */ + current_padding = ((current_len_left + TAR_FILE_PADDING) & ~TAR_FILE_PADDING) - current_len_left; + /* + * First part of header is zero terminated filename. + * when getting a checkpoint, file name can be either + * the control file (written to base_dir), or a checkpoint + * file (written to base_dir/chkpt_) + */ + if (strstr(copybuf, "mot.ctrl")) { + errorno = + snprintf_s(filename, sizeof(filename), sizeof(filename) - 1, "%s/%s", current_path, "mot.ctrl"); + securec_check_ss_c(errorno, "", ""); + } else { + char* chkptOffset = strstr(copybuf, chkptName); + if (chkptOffset) { + errorno = snprintf_s( + filename, sizeof(filename), sizeof(filename) - 1, "%s/%s", current_path, chkptOffset); + securec_check_ss_c(errorno, "", ""); + } else { + errorno = + snprintf_s(filename, sizeof(filename), sizeof(filename) - 1, "%s/%s", current_path, copybuf); + securec_check_ss_c(errorno, "", ""); + } + } + + if (filename[strlen(filename) - 1] == '/') { + filename[strlen(filename) - 1] = '\0'; /* Remove trailing slash */ + strcpy_s(copybuf, strlen(filename), filename); + continue; /* directory or link handled */ + } +#ifdef HAVE_LIBZ + if (ztarfile != NULL) { + if (!writeGzFile(ztarfile, copybuf, r)) { + fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), progname, filename, strerror(errno)); + disconnect_and_exit(1); + } + } else +#endif + { + if (fwrite(copybuf, r, 1, tarfile) != 1) { + fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), progname, filename, strerror(errno)); + disconnect_and_exit(1); + } + } + if (current_len_left == 0) { + continue; + } + } else { + /* + * Continuing blocks in existing file + */ + if (current_len_left == 0 && r == current_padding) { +#ifdef HAVE_LIBZ + if (ztarfile != NULL) { + if (!writeGzFile(ztarfile, copybuf, current_padding)) { + fprintf( + stderr, _("%s: could not write to file \"%s\": %s\n"), progname, filename, strerror(errno)); + disconnect_and_exit(1); + } + } else +#endif + { + if (fwrite(copybuf, current_padding, 1, tarfile) != 1) { + fprintf( + stderr, _("%s: could not write to file \"%s\": %s\n"), progname, filename, strerror(errno)); + disconnect_and_exit(1); + } + } + totaldone += r; + current_padding -= r; + continue; + } + + totaldone += r; +#ifdef HAVE_LIBZ + if (ztarfile != NULL) { + if (!writeGzFile(ztarfile, copybuf, r)) { + fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), progname, filename, strerror(errno)); + disconnect_and_exit(1); + } + } else +#endif + { + if (fwrite(copybuf, r, 1, tarfile) != 1) { + fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), progname, filename, strerror(errno)); + disconnect_and_exit(1); + } + } + if (current_len_left == 0) { + continue; + } + current_len_left -= r; + if (current_len_left == 0 && current_padding == 0) { + /* + * Received the last block, and there is no padding to be + * expected. Close the file and move on to the next tar + * header. + */ + fclose(file); + file = NULL; + continue; + } + } /* continuing data in existing file */ + } /* loop over all data blocks */ + if (tarfile != NULL) { + fclose(tarfile); + tarfile = NULL; + } + + if (copybuf != NULL) { + PQfreemem(copybuf); + copybuf = NULL; + } + + return; +} + /* * Receive a tar format stream from the connection to the server, and unpack * the contents of it into a directory. Only files, directories and @@ -154,11 +440,8 @@ static void MotReceiveAndUnpackTarFile(const char* basedir, const char* chkptNam */ filename[strlen(filename) - 1] = '\0'; /* Remove trailing slash */ if (mkdir(filename, S_IRWXU) != 0) { - fprintf(stderr, - "%s: could not create directory \"%s\": %s\n", - progname, - filename, - strerror(errno)); + fprintf( + stderr, "%s: could not create directory \"%s\": %s\n", progname, filename, strerror(errno)); disconnect_and_exit(1); } #ifndef WIN32 @@ -272,7 +555,8 @@ static void MotReceiveAndUnpackTarFile(const char* basedir, const char* chkptNam * @param controls verbose output * @return Boolean value denoting success or failure. */ -void FetchMotCheckpoint(const char* basedir, PGconn* fetchConn, const char* progname, bool verbose) +void FetchMotCheckpoint( + const char* basedir, PGconn* fetchConn, const char* progname, bool verbose, const char format, int compresslevel) { PGresult* res = NULL; const char* fetchQuery = "FETCH_MOT_CHECKPOINT"; @@ -333,17 +617,24 @@ void FetchMotCheckpoint(const char* basedir, PGconn* fetchConn, const char* prog fprintf(stderr, "%s: mot checkpoint directory: %s\n", progname, dirName); } - if (stat(dirName, &fileStat) < 0) { - if (pg_mkdir_p(dirName, S_IRWXU) == -1) { - fprintf(stderr, "%s: could not create directory \"%s\": %s\n", progname, dirName, strerror(errno)); + if (format == 'p') { + if (stat(dirName, &fileStat) < 0) { + if (pg_mkdir_p(dirName, S_IRWXU) == -1) { + fprintf(stderr, "%s: could not create directory \"%s\": %s\n", progname, dirName, strerror(errno)); + exit(1); + } + } else { + fprintf(stderr, "%s: directory \"%s\" already exists, please remove it and try again\n", progname, + dirName); exit(1); } + MotReceiveAndUnpackTarFile(basedir, chkptName, fetchConn, progname); + } else if (format == 't') { + MotReceiveAndAppendTarFile(basedir, chkptName, fetchConn, progname, compresslevel); } else { - fprintf(stderr, "%s: directory \"%s\" already exists, please remove it and try again\n", progname, dirName); + fprintf(stderr, "%s: unsupport format type: \"%c\".\n", progname, format); exit(1); } - - MotReceiveAndUnpackTarFile(basedir, chkptName, fetchConn, progname); if (verbose) { fprintf(stderr, "%s: finished fetching mot checkpoint\n", progname); } diff --git a/src/bin/pg_ctl/pg_ctl.cpp b/src/bin/pg_ctl/pg_ctl.cpp index 388a47024..d2a3f5882 100644 --- a/src/bin/pg_ctl/pg_ctl.cpp +++ b/src/bin/pg_ctl/pg_ctl.cpp @@ -290,7 +290,8 @@ static char* get_string_by_sync_mode(bool syncmode); static void free_ctl(); extern int GetLengthAndCheckReplConn(const char* ConnInfoList); extern BuildErrorCode gs_increment_build(char* pgdata, const char* connstr, const uint32 term); -extern void FetchMotCheckpoint(const char* basedir, PGconn* fetchConn, const char* progname, bool verbose); +extern void FetchMotCheckpoint(const char* basedir, PGconn* fetchConn, const char* progname, bool verbose, + const char format = 'p', const int compresslevel = 0); extern char* GetOptionValueFromFile(const char* fileName, const char* option); void check_input_for_security(char* input_env_value) @@ -2268,7 +2269,7 @@ static void do_query(void) } static bool check_pid_exists(pgpid_t pid) -{ +{ FILE* fp = NULL; char buf[1024] = {0}; char command[128] = {0}; diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 8a9a9e0f6..d97e44aec 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -94,7 +94,12 @@ typedef enum { } RecoveryTargetType; /* WAL levels */ -typedef enum WalLevel { WAL_LEVEL_MINIMAL = 0, WAL_LEVEL_ARCHIVE, WAL_LEVEL_HOT_STANDBY, WAL_LEVEL_LOGICAL } WalLevel; +typedef enum WalLevel { + WAL_LEVEL_MINIMAL = 0, + WAL_LEVEL_ARCHIVE, + WAL_LEVEL_HOT_STANDBY, + WAL_LEVEL_LOGICAL +} WalLevel; #define XLogArchivingActive() \ (u_sess->attr.attr_common.XLogArchiveMode && g_instance.attr.attr_storage.wal_level >= WAL_LEVEL_ARCHIVE) diff --git a/src/include/gs_tar_const.h b/src/include/gs_tar_const.h new file mode 100644 index 000000000..ca610c579 --- /dev/null +++ b/src/include/gs_tar_const.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * --------------------------------------------------------------------------------------- + * + * + * + * IDENTIFICATION + * src/include/gs_tar_const.h + * + * --------------------------------------------------------------------------------------- + */ + +#ifndef GS_TAR_CONST_H_ +#define GS_TAR_CONST_H_ + +#ifdef HAVE_LIBZ +#include "zlib.h" +#endif + +/* gs_tar offset */ +static const int TAR_LEN_LEFT = 1048; +static const int TAR_FILE_PADDING = 511; /* All files are padded up to 512 bytes */ +static const int TAR_FILE_TYPE = 1080; /* file type */ +static const int TAR_BLOCK_SIZE = 2560; /* gs_tar block size */ +static const int TAR_FILE_MODE = 1024; + +/* fileStream result */ +static const int TAR_READ_ERROR = -2; + +/* file type */ +static const char TAR_TYPE_DICTORY = '5'; +static const int FILE_PERMISSION = 16832; + +#ifdef HAVE_LIBZ +static const char* get_gz_error(gzFile gzf) +{ + int errnum; + const char* errmsg = NULL; + + errmsg = gzerror(gzf, &errnum); + if (errnum == Z_ERRNO) + return strerror(errno); + else + return errmsg; +} + +static gzFile openGzFile(const char* filename, int compresslevel, const char* mode = "wb") +{ + gzFile ztarfile = gzopen(filename, mode); + if (gzsetparams(ztarfile, compresslevel, Z_DEFAULT_STRATEGY) != Z_OK) { + return NULL; + } + return ztarfile; +} + +static bool writeGzFile(gzFile ztarfile, char* copybuf, int buf_size) +{ + if (gzwrite(ztarfile, copybuf, buf_size) != buf_size) { + return false; + } + return true; +} +#endif + +#endif /* GS_TAR_CONST_H_ */ \ No newline at end of file From b39e6a4b5ca442263fd12ae204846e73a8774112 Mon Sep 17 00:00:00 2001 From: zhang_xubo <2578876417@qq.com> Date: Wed, 26 Aug 2020 11:05:00 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E5=A4=87=E6=9C=BA=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=89=A9=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package/package.sh | 1 + src/manager/om/script/gs_expansion | 245 ++++++ .../om/script/gspylib/common/Common.py | 1 + .../om/script/gspylib/common/ErrorCode.py | 18 + .../gspylib/common/ParameterParsecheck.py | 5 +- .../om/script/impl/expansion/ExpansionImpl.py | 829 ++++++++++++++++++ .../om/script/impl/expansion/__init__.py | 0 7 files changed, 1098 insertions(+), 1 deletion(-) create mode 100644 src/manager/om/script/gs_expansion create mode 100644 src/manager/om/script/impl/expansion/ExpansionImpl.py create mode 100644 src/manager/om/script/impl/expansion/__init__.py diff --git a/package/package.sh b/package/package.sh index 59fdc5d0b..6ac751843 100644 --- a/package/package.sh +++ b/package/package.sh @@ -398,6 +398,7 @@ function target_file_copy() sed -i '/gs_lcctl/d' binfile sed -i '/gs_wsr/d' binfile sed -i '/gs_gucZenith/d' binfile + sed -i '/gs_expansion/d' binfile bin_script=$(cat binfile) rm binfile script_file cd $BUILD_DIR diff --git a/src/manager/om/script/gs_expansion b/src/manager/om/script/gs_expansion new file mode 100644 index 000000000..ee2d8648b --- /dev/null +++ b/src/manager/om/script/gs_expansion @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 +# -*- coding:utf-8 -*- +############################################################################# +# Copyright (c) 2020 Huawei Technologies Co.,Ltd. +# +# openGauss is licensed under Mulan PSL v2. +# You can use this software according to the terms +# and conditions of the Mulan PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# +# http://license.coscl.org.cn/MulanPSL2 +# +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OF ANY KIND, +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. +# ---------------------------------------------------------------------------- +# Description : gs_expansion is a utility to expansion standby node databases +############################################################################# + +import os +import pwd +import sys +import threading +import uuid +import subprocess +import weakref + +sys.path.append(sys.path[0]) +from gspylib.common.DbClusterInfo import dbClusterInfo, \ + readOneClusterConfigItem, initParserXMLFile, dbNodeInfo, checkPathVaild +from gspylib.common.GaussLog import GaussLog +from gspylib.common.Common import DefaultValue +from gspylib.common.ErrorCode import ErrorCode +from gspylib.common.ParallelBaseOM import ParallelBaseOM +from gspylib.common.ParameterParsecheck import Parameter +from impl.preinstall.OLAP.PreinstallImplOLAP import PreinstallImplOLAP +from gspylib.threads.SshTool import SshTool +from impl.expansion.ExpansionImpl import ExpansionImpl + +ENV_LIST = ["MPPDB_ENV_SEPARATE_PATH", "GPHOME", "PATH", + "LD_LIBRARY_PATH", "PYTHONPATH", "GAUSS_WARNING_TYPE", + "GAUSSHOME", "PATH", "LD_LIBRARY_PATH", + "S3_CLIENT_CRT_FILE", "GAUSS_VERSION", "PGHOST", + "GS_CLUSTER_NAME", "GAUSSLOG", "GAUSS_ENV", "umask"] + +class Expansion(ParallelBaseOM): + """ + """ + + def __init__(self): + """ + """ + ParallelBaseOM.__init__(self) + # new added standby node backip list + self.newHostList = [] + self.clusterInfoDict = {} + self.backIpNameMap = {} + self.packagepath = os.path.realpath( + os.path.join(os.path.realpath(__file__), "../../")) + + self.standbyLocalMode = False + + def usage(self): + """ +gs_expansion is a utility to expansion standby node for a cluster. + +Usage: + gs_expansion -? | --help + gs_expansion -V | --version + gs_expansion -U USER -G GROUP -X XMLFILE -h nodeList [-L] +General options: + -U Cluster user. + -G Group of the cluster user. + -X Path of the XML configuration file. + -h New standby node node backip list. + Separate multiple nodes with commas (,). + such as '-h 192.168.0.1,192.168.0.2' + -L The standby database installed with + local mode. + -?, --help Show help information for this + utility, and exit the command line mode. + -V, --version Show version information. + """ + print(self.usage.__doc__) + + def parseCommandLine(self): + """ + parse parameter from command line + """ + ParaObj = Parameter() + ParaDict = ParaObj.ParameterCommandLine("expansion") + + # parameter -h or -? + if (ParaDict.__contains__("helpFlag")): + self.usage() + sys.exit(0) + # Resolves command line arguments + # parameter -U + if (ParaDict.__contains__("user")): + self.user = ParaDict.get("user") + DefaultValue.checkPathVaild(self.user) + # parameter -G + if (ParaDict.__contains__("group")): + self.group = ParaDict.get("group") + # parameter -X + if (ParaDict.__contains__("confFile")): + self.xmlFile = ParaDict.get("confFile") + # parameter -L + if (ParaDict.__contains__("localMode")): + self.localMode = ParaDict.get("localMode") + self.standbyLocalMode = ParaDict.get("localMode") + # parameter -l + if (ParaDict.__contains__("logFile")): + self.logFile = ParaDict.get("logFile") + #parameter -h + if (ParaDict.__contains__("nodename")): + self.newHostList = ParaDict.get("nodename") + + + def checkParameters(self): + """ + function: Check parameter from command line + input: NA + output: NA + """ + + # check user | group | xmlfile | node + if len(self.user) == 0: + GaussLog.exitWithError(ErrorCode.GAUSS_357["GAUSS_35701"] % "-U") + if len(self.group) == 0: + GaussLog.exitWithError(ErrorCode.GAUSS_357["GAUSS_35701"] % "-G") + if len(self.xmlFile) == 0: + GaussLog.exitWithError(ErrorCode.GAUSS_357["GAUSS_35701"] % "-X") + if len(self.newHostList) == 0: + GaussLog.exitWithError(ErrorCode.GAUSS_357["GAUSS_35701"] % "-h") + + clusterInfo = ExpansipnClusterInfo() + hostNameIpDict = clusterInfo.initFromXml(self.xmlFile) + clusterDict = clusterInfo.getClusterDirectorys() + backIpList = clusterInfo.getClusterBackIps() + nodeNameList = clusterInfo.getClusterNodeNames() + + self.nodeNameList = nodeNameList + self.backIpNameMap = {} + for backip in backIpList: + self.backIpNameMap[backip] = clusterInfo.getNodeNameByBackIp(backip) + + # check parameter node must in xml config file + for nodeid in self.newHostList: + if nodeid not in backIpList: + GaussLog.exitWithError(ErrorCode.GAUSS_357["GAUSS_35702"] % \ + nodeid) + + # get corepath and toolpath from xml file + corePath = clusterInfo.readClustercorePath(self.xmlFile) + toolPath = clusterInfo.getToolPath(self.xmlFile) + # parse xml file and cache node info + clusterInfoDict = {} + clusterInfoDict["appPath"] = clusterDict["appPath"][0] + clusterInfoDict["logPath"] = clusterDict["logPath"][0] + clusterInfoDict["corePath"] = corePath + clusterInfoDict["toolPath"] = toolPath + for nodeName in nodeNameList: + hostInfo = hostNameIpDict[nodeName] + ipList = hostInfo[0] + portList = hostInfo[1] + backIp = "" + sshIp = "" + if len(ipList) == 1: + backIp = sshIp = ipList[0] + elif len(ipList) == 2: + backIp = ipList[0] + sshIp = ipList[1] + port = portList[0] + cluster = clusterDict[nodeName] + dataNode = cluster[2] + clusterInfoDict[nodeName] = { + "backIp": backIp, + "sshIp": sshIp, + "port": port, + "localport": int(port) + 1, + "localservice": int(port) + 4, + "heartBeatPort": int(port) + 3, + "dataNode": dataNode, + "instanceType": -1 + } + + nodeIdList = clusterInfo.getClusterNodeIds() + for id in nodeIdList: + insType = clusterInfo.getdataNodeInstanceType(id) + hostName = clusterInfo.getHostNameByNodeId(id) + clusterInfoDict[hostName]["instanceType"] = insType + self.clusterInfoDict = clusterInfoDict + + + def initLogs(self): + """ + init log file + """ + # if no log file + if (self.logFile == ""): + self.logFile = DefaultValue.getOMLogPath( + DefaultValue.EXPANSION_LOG_FILE, self.user, "", + self.xmlFile) + # if not absolute path + if (not os.path.isabs(self.logFile)): + GaussLog.exitWithError(ErrorCode.GAUSS_502["GAUSS_50213"] % "log") + + self.initLogger("gs_expansion") + self.logger.ignoreErr = True + +class ExpansipnClusterInfo(dbClusterInfo): + + def __init__(self): + dbClusterInfo.__init__(self) + + def getToolPath(self, xmlFile): + """ + function : Read tool path from default xml file + input : String + output : String + """ + self.setDefaultXmlFile(xmlFile) + # read gaussdb tool path from xml file + (retStatus, retValue) = readOneClusterConfigItem( + initParserXMLFile(xmlFile), "gaussdbToolPath", "cluster") + if retStatus != 0: + raise Exception(ErrorCode.GAUSS_512["GAUSS_51200"] + % "gaussdbToolPath" + " Error: \n%s" % retValue) + toolPath = os.path.normpath(retValue) + checkPathVaild(toolPath) + return toolPath + +if __name__ == "__main__": + """ + """ + expansion = Expansion() + expansion.parseCommandLine() + expansion.checkParameters() + expansion.initLogs() + expImpl = ExpansionImpl(expansion) + expImpl.run() + diff --git a/src/manager/om/script/gspylib/common/Common.py b/src/manager/om/script/gspylib/common/Common.py index f56c6243c..7a60f822a 100644 --- a/src/manager/om/script/gspylib/common/Common.py +++ b/src/manager/om/script/gspylib/common/Common.py @@ -309,6 +309,7 @@ class DefaultValue(): LCCTL_LOG_FILE = "gs_lcctl.log" RESIZE_LOG_FILE = "gs_resize.log" HOTPATCH_LOG_FILE = "gs_hotpatch.log" + EXPANSION_LOG_FILE = "gs_expansion.log" # hotpatch action HOTPATCH_ACTION_LIST = ["load", "unload", "active", "deactive", "info", "list"] diff --git a/src/manager/om/script/gspylib/common/ErrorCode.py b/src/manager/om/script/gspylib/common/ErrorCode.py index e6725c18e..fcf19ba94 100644 --- a/src/manager/om/script/gspylib/common/ErrorCode.py +++ b/src/manager/om/script/gspylib/common/ErrorCode.py @@ -1092,6 +1092,24 @@ class ErrorCode(): 'GAUSS_53612': "[GAUSS-53612]: Can not find any catalog in database %s" } + ########################################################################## + # gs_expansion + # [GAUSS-537] : gs_expansion failed + ########################################################################## + GAUSS_357 = { + "GAUSS_35700": "[GAUSS-35700] Expansion standby node failed.", + "GAUSS_35701": "[GAUSS-35701] Empty parameter. The %s parameter is" + "missing in the command.", + "GAUSS_35702": "[GAUSS-35702] Unrecognized parameter, standby host " + "backip %s is not in the " + "XML configuration file", + "GAUSS_35703": "[GAUSS-35703] Check standby database Failed. The " + "database on node is abnormal. \n" + "node [%s], user [%s], dataNode [%s]. \n" + "You can use command \"gs_ctl query -D %s\" for more " + "detail." + } + class OmError(BaseException): """ diff --git a/src/manager/om/script/gspylib/common/ParameterParsecheck.py b/src/manager/om/script/gspylib/common/ParameterParsecheck.py index 717890d84..89397c06b 100644 --- a/src/manager/om/script/gspylib/common/ParameterParsecheck.py +++ b/src/manager/om/script/gspylib/common/ParameterParsecheck.py @@ -93,6 +93,8 @@ gs_ssh = ["-?", "--help", "-V", "--version", "-c:"] gs_checkos = ["-?", "--help", "-V", "--version", "-h:", "-f:", "-o:", "-i:", "--detail", "-l:", "-X:"] +gs_expansion = ["-?", "--help", "-V", "--version", "-U:", "-G:", "-L", + "-X:", "-h:", "--sep-env-file="] # gs_om child branch gs_om_start = ["-t:", "-?", "--help", "-V", "--version", "-h:", "-I:", @@ -153,7 +155,8 @@ ParameterDict = {"preinstall": gs_preinstall, "postuninstall": gs_postuninstall, "view": gs_om_view, "query": gs_om_query, - "refreshconf": gs_om_refreshconf + "refreshconf": gs_om_refreshconf, + "expansion": gs_expansion } # List of scripts with the -t parameter diff --git a/src/manager/om/script/impl/expansion/ExpansionImpl.py b/src/manager/om/script/impl/expansion/ExpansionImpl.py new file mode 100644 index 000000000..95e21dd77 --- /dev/null +++ b/src/manager/om/script/impl/expansion/ExpansionImpl.py @@ -0,0 +1,829 @@ +# -*- coding:utf-8 -*- +############################################################################# +# Copyright (c) 2020 Huawei Technologies Co.,Ltd. +# +# openGauss is licensed under Mulan PSL v2. +# You can use this software according to the terms +# and conditions of the Mulan PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# +# http://license.coscl.org.cn/MulanPSL2 +# +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OF ANY KIND, +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. +# ---------------------------------------------------------------------------- +# Description : ExpansionImpl.py +############################################################################# + +import subprocess +import sys +import re +import os +import getpass +import pwd +import datetime +from random import sample +import time +from multiprocessing import Process, Value + +sys.path.append(sys.path[0] + "/../../../../") +from gspylib.common.DbClusterInfo import dbClusterInfo, queryCmd +from gspylib.threads.SshTool import SshTool +from gspylib.common.DbClusterStatus import DbClusterStatus +from gspylib.common.ErrorCode import ErrorCode +from gspylib.common.Common import DefaultValue +from gspylib.common.GaussLog import GaussLog + +sys.path.append(sys.path[0] + "/../../../lib/") +DefaultValue.doConfigForParamiko() +import paramiko + + +#mode +MODE_PRIMARY = "primary" +MODE_STANDBY = "standby" +MODE_NORMAL = "normal" + +#db state +STAT_NORMAL = "normal" + +# master +MASTER_INSTANCE = 0 +# standby +STANDBY_INSTANCE = 1 + +# statu failed +STATUS_FAIL = "Failure" + +class ExpansionImpl(): + """ + class for expansion standby node. + step: + 1. preinstall database on new standby node + 2. install as single-node database + 3. establish primary-standby relationship of all node + """ + + def __init__(self, expansion): + """ + """ + self.context = expansion + + self.user = self.context.user + self.group = self.context.group + + self.logger = self.context.logger + + self.envFile = DefaultValue.getEnv("MPPDB_ENV_SEPARATE_PATH") + + currentTime = str(datetime.datetime.now()).replace(" ", "_").replace( + ".", "_") + + self.commonGsCtl = GsCtlCommon(expansion) + self.tempFileDir = "/tmp/gs_expansion_%s" % (currentTime) + self.logger.debug("tmp expansion dir is %s ." % self.tempFileDir) + + + def sendSoftToHosts(self): + """ + create software dir and send it on each nodes + """ + self.logger.debug("Start to send software to each standby nodes.\n") + hostNames = self.context.newHostList + hostList = hostNames + + sshTool = SshTool(hostNames) + + srcFile = self.context.packagepath + targetDir = os.path.realpath( + os.path.join(srcFile, "../")) + + ## mkdir package dir and send package to remote nodes. + sshTool.executeCommand("mkdir -p %s" % srcFile , "", DefaultValue.SUCCESS, + hostList) + sshTool.scpFiles(srcFile, targetDir, hostList) + + ## change mode of package dir to set privileges for users + tPathList = os.path.split(targetDir) + path2ChangeMode = targetDir + if len(tPathList) > 2: + path2ChangeMode = os.path.join(tPathList[0],tPathList[1]) + changeModCmd = "chmod -R a+x {srcFile}".format(user=self.user, + group=self.group,srcFile=path2ChangeMode) + sshTool.executeCommand(changeModCmd, "", DefaultValue.SUCCESS, + hostList) + self.logger.debug("End to send software to each standby nodes.\n") + + def generateAndSendXmlFile(self): + """ + """ + self.logger.debug("Start to generateAndSend XML file.\n") + + tempXmlFile = "%s/clusterconfig.xml" % self.tempFileDir + cmd = "mkdir -p %s; touch %s; cat /dev/null > %s" % \ + (self.tempFileDir, tempXmlFile, tempXmlFile) + (status, output) = subprocess.getstatusoutput(cmd) + + cmd = "chown -R %s:%s %s" % (self.user, self.group, self.tempFileDir) + (status, output) = subprocess.getstatusoutput(cmd) + + newHosts = self.context.newHostList + for host in newHosts: + # create single deploy xml file for each standby node + xmlContent = self.__generateXml(host) + fo = open("%s" % tempXmlFile, "w") + fo.write( xmlContent ) + fo.close() + # send single deploy xml file to each standby node + sshTool = SshTool(host) + retmap, output = sshTool.getSshStatusOutput("mkdir -p %s" % + self.tempFileDir , [host], self.envFile) + retmap, output = sshTool.getSshStatusOutput("chown %s:%s %s" % + (self.user, self.group, self.tempFileDir), [host], self.envFile) + sshTool.scpFiles("%s" % tempXmlFile, "%s" % + tempXmlFile, [host], self.envFile) + + self.logger.debug("End to generateAndSend XML file.\n") + + def __generateXml(self, backIp): + """ + """ + nodeName = self.context.backIpNameMap[backIp] + nodeInfo = self.context.clusterInfoDict[nodeName] + + backIp = nodeInfo["backIp"] + sshIp = nodeInfo["sshIp"] + port = nodeInfo["port"] + dataNode = nodeInfo["dataNode"] + + appPath = self.context.clusterInfoDict["appPath"] + logPath = self.context.clusterInfoDict["logPath"] + corePath = self.context.clusterInfoDict["corePath"] + toolPath = self.context.clusterInfoDict["toolPath"] + + xmlConfig = """\ + + + + + + + + + + + + + + + + + + + + + + + + + + + """.format(nodeName=nodeName,backIp=backIp,appPath=appPath, + logPath=logPath,toolPath=toolPath,corePath=corePath, + sshIp=sshIp,port=port,dataNode=dataNode) + return xmlConfig + + def changeUser(self): + user = self.user + try: + pw_record = pwd.getpwnam(user) + except Exception: + GaussLog.exitWithError(ErrorCode.GAUSS_503["GAUSS_50300"] % user) + + user_name = pw_record.pw_name + user_uid = pw_record.pw_uid + user_gid = pw_record.pw_gid + env = os.environ.copy() + os.setgid(user_gid) + os.setuid(user_uid) + + def initSshConnect(self, host, user='root'): + + try: + getPwdStr = "Please enter the password of user [%s] on node [%s]: " \ + % (user, host) + passwd = getpass.getpass(getPwdStr) + self.sshClient = paramiko.SSHClient() + self.sshClient.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + self.sshClient.connect(host, 22, user, passwd) + except paramiko.ssh_exception.AuthenticationException as e : + self.logger.log("Authentication failed.") + self.initSshConnect(host, user) + + def installDatabaseOnHosts(self): + """ + install database on each standby node + """ + hostList = self.context.newHostList + envfile = DefaultValue.getEnv(DefaultValue.MPPRC_FILE_ENV) + tempXmlFile = "%s/clusterconfig.xml" % self.tempFileDir + installCmd = "source {envfile} ; gs_install -X {xmlfile} \ + 2>&1".format(envfile=envfile,xmlfile=tempXmlFile) + + statusArr = [] + + for newHost in hostList: + + self.logger.log("\ninstalling database on node %s:" % newHost) + self.logger.debug(installCmd) + + hostName = self.context.backIpNameMap[newHost] + sshIp = self.context.clusterInfoDict[hostName]["sshIp"] + self.initSshConnect(sshIp, self.user) + + stdin, stdout, stderr = self.sshClient.exec_command(installCmd, + get_pty=True) + channel = stdout.channel + echannel = stderr.channel + + while not channel.exit_status_ready(): + try: + recvOut = channel.recv(1024) + outDecode = recvOut.decode("utf-8"); + outStr = outDecode.strip() + if(len(outStr) == 0): + continue + if(outDecode.endswith("\r\n")): + self.logger.log(outStr) + else: + value = "" + if re.match(r".*yes.*no.*", outStr): + value = input(outStr) + while True: + # check the input + if ( + value.upper() != "YES" + and value.upper() != "NO" + and value.upper() != "Y" + and value.upper() != "N"): + value = input("Please type 'yes' or 'no': ") + continue + break + else: + value = getpass.getpass(outStr) + stdin.channel.send("%s\r\n" %value) + stdin.flush() + stdout.flush() + except Exception as e: + sys.exit(1) + pass + if channel.exit_status_ready() and \ + not channel.recv_stderr_ready() and \ + not channel.recv_ready(): + channel.close() + break + + stdout.close() + stderr.close() + status = channel.recv_exit_status() + statusArr.append(status) + + isBothSuccess = True + for status in statusArr: + if status != 0: + isBothSuccess = False + break + if isBothSuccess: + self.logger.log("\nSuccessfully install database on node %s" % + hostList) + else: + sys.exit(1) + + def preInstallOnHosts(self): + """ + execute preinstall step + """ + self.logger.debug("Start to preinstall database step.\n") + newBackIps = self.context.newHostList + newHostNames = [] + for host in newBackIps: + newHostNames.append(self.context.backIpNameMap[host]) + envfile = self.envFile + tempXmlFile = "%s/clusterconfig.xml" % self.tempFileDir + + preinstallCmd = "{softpath}/script/gs_preinstall -U {user} -G {group} \ + -X {xmlfile} --sep-env-file={envfile} \ + --non-interactive 2>&1\ + ".format(softpath=self.context.packagepath,user=self.user, + group=self.group,xmlfile=tempXmlFile,envfile=envfile) + + sshTool = SshTool(newHostNames) + + status, output = sshTool.getSshStatusOutput(preinstallCmd , [], envfile) + statusValues = status.values() + if STATUS_FAIL in statusValues: + GaussLog.exitWithError(output) + + self.logger.debug("End to preinstall database step.\n") + + + def buildStandbyRelation(self): + """ + func: after install single database on standby nodes. + build the relation with primary and standby nodes. + step: + 1. restart primary node with Primary Mode + (only used to Single-Node instance) + 2. set guc config to primary node + 3. restart standby node with Standby Mode + 4. set guc config to standby node + 5. generate cluster static file and send to each node. + """ + self.queryPrimaryClusterDetail() + self.setPrimaryGUCConfig() + self.setStandbyGUCConfig() + self.buildStandbyHosts() + self.generateClusterStaticFile() + + def queryPrimaryClusterDetail(self): + """ + get current cluster type. + single-node or primary-standby + """ + self.logger.debug("Query primary database instance mode.\n") + self.isSingleNodeInstance = True + primaryHost = self.getPrimaryHostName() + result = self.commonGsCtl.queryOmCluster(primaryHost, self.envFile) + instance = re.findall(r"node\s+node_ip\s+instance\s+state", result) + if len(instance) > 1: + self.isSingleNodeInstance = False + self.logger.debug("Original instance mode is %s" % + self.isSingleNodeInstance) + + def setPrimaryGUCConfig(self): + """ + """ + self.logger.debug("Start to set primary node GUC config.\n") + primaryHost = self.getPrimaryHostName() + dataNode = self.context.clusterInfoDict[primaryHost]["dataNode"] + + self.setGUCOnClusterHosts([primaryHost]) + self.addStandbyIpInPrimaryConf() + insType, dbStat = self.commonGsCtl.queryInstanceStatus(primaryHost, + dataNode,self.envFile) + if insType != MODE_PRIMARY: + self.commonGsCtl.stopInstance(primaryHost, dataNode, self.envFile) + self.commonGsCtl.startInstanceWithMode(primaryHost, dataNode, + MODE_PRIMARY,self.envFile) + + # start db to primary state for three times max + start_retry_num = 1 + while start_retry_num <= 3: + insType, dbStat = self.commonGsCtl.queryInstanceStatus(primaryHost, + dataNode, self.envFile) + if insType == MODE_PRIMARY: + break + self.logger.debug("Start database as Primary mode failed, \ +retry for %s times" % start_retry_num) + self.commonGsCtl.startInstanceWithMode(primaryHost, dataNode, + MODE_PRIMARY, self.envFile) + start_retry_num = start_retry_num + 1 + + def setStandbyGUCConfig(self): + """ + """ + self.logger.debug("Start to set standby node GUC config.\n") + standbyHosts = self.context.newHostList + standbyHostNames = [] + for host in standbyHosts: + hostName = self.context.backIpNameMap[host] + standbyHostNames.append(hostName) + self.setGUCOnClusterHosts(standbyHostNames) + + def addStandbyIpInPrimaryConf(self): + """ + add standby hosts ip in primary node pg_hba.conf + """ + + standbyHosts = self.context.newHostList + primaryHost = self.getPrimaryHostName() + command = '' + for host in standbyHosts: + hostName = self.context.backIpNameMap[host] + dataNode = self.context.clusterInfoDict[hostName]["dataNode"] + command += "gs_guc set -D %s -h 'host all all %s/32 \ + trust';" % (dataNode, host) + self.logger.debug(command) + sshTool = SshTool([primaryHost]) + resultMap, outputCollect = sshTool.getSshStatusOutput(command, + [primaryHost], self.envFile) + self.logger.debug(outputCollect) + + def reloadPrimaryConf(self): + """ + """ + primaryHost = self.getPrimaryHostName() + dataNode = self.context.clusterInfoDict[primaryHost]["dataNode"] + command = "gs_ctl reload -D %s " % dataNode + sshTool = SshTool([primaryHost]) + self.logger.debug(command) + resultMap, outputCollect = sshTool.getSshStatusOutput(command, + [primaryHost], self.envFile) + self.logger.debug(outputCollect) + + def getPrimaryHostName(self): + """ + """ + primaryHost = "" + for nodeName in self.context.nodeNameList: + if self.context.clusterInfoDict[nodeName]["instanceType"] \ + == MASTER_INSTANCE: + primaryHost = nodeName + break + return primaryHost + + + def buildStandbyHosts(self): + """ + stop the new standby host`s database and build it as standby mode + """ + self.logger.debug("start to build standby node...\n") + + standbyHosts = self.context.newHostList + + for host in standbyHosts: + hostName = self.context.backIpNameMap[host] + dataNode = self.context.clusterInfoDict[hostName]["dataNode"] + self.commonGsCtl.stopInstance(hostName, dataNode, self.envFile) + self.commonGsCtl.startInstanceWithMode(hostName, dataNode, + MODE_STANDBY, self.envFile) + + # start standby as standby mode for three times max. + start_retry_num = 1 + while start_retry_num <= 3: + insType, dbStat = self.commonGsCtl.queryInstanceStatus(hostName, + dataNode, self.envFile) + if insType != MODE_STANDBY: + self.logger.debug("Start databasse as Standby mode failed, \ +retry for %s times" % start_retry_num) + self.setGUCOnClusterHosts([]) + self.addStandbyIpInPrimaryConf() + self.reloadPrimaryConf() + self.commonGsCtl.startInstanceWithMode(hostName, dataNode, + MODE_STANDBY, self.envFile) + start_retry_num = start_retry_num + 1 + else: + break + + # build standby node + self.addStandbyIpInPrimaryConf() + self.reloadPrimaryConf() + self.commonGsCtl.buildInstance(hostName, dataNode, MODE_STANDBY, + self.envFile) + + # if build failed first time. retry for three times. + start_retry_num = 1 + while start_retry_num <= 3: + insType, dbStat = self.commonGsCtl.queryInstanceStatus(hostName, + dataNode, self.envFile) + if dbStat != STAT_NORMAL: + self.logger.debug("Build standby instance failed, \ +retry for %s times" % start_retry_num) + self.addStandbyIpInPrimaryConf() + self.reloadPrimaryConf() + self.commonGsCtl.buildInstance(hostName, dataNode, + MODE_STANDBY, self.envFile) + start_retry_num = start_retry_num + 1 + else: + break + + + def generateClusterStaticFile(self): + """ + generate static_config_files and send to all hosts + """ + self.logger.debug("Start to generate and send cluster static file.\n") + + primaryHosts = self.getPrimaryHostName() + command = "gs_om -t generateconf -X %s" % self.context.xmlFile + sshTool = SshTool([primaryHosts]) + resultMap, outputCollect = sshTool.getSshStatusOutput(command, + [primaryHosts], self.envFile) + self.logger.debug(outputCollect) + + nodeNameList = self.context.nodeNameList + + for hostName in nodeNameList: + hostSsh = SshTool([hostName]) + toolPath = self.context.clusterInfoDict["toolPath"] + appPath = self.context.clusterInfoDict["appPath"] + srcFile = "%s/script/static_config_files/cluster_static_config_%s" \ + % (toolPath, hostName) + targetFile = "%s/bin/cluster_static_config" % appPath + hostSsh.scpFiles(srcFile, targetFile, [hostName], self.envFile) + + self.logger.debug("End to generate and send cluster static file.\n") + time.sleep(10) + + # Single-node database need start cluster after expansion + if self.isSingleNodeInstance: + self.logger.debug("Single-Node instance need restart.\n") + self.commonGsCtl.queryOmCluster(primaryHosts, self.envFile) + + # if primary database not normal, restart it + primaryHost = self.getPrimaryHostName() + dataNode = self.context.clusterInfoDict[primaryHost]["dataNode"] + insType, dbStat = self.commonGsCtl.queryInstanceStatus(primaryHost, + dataNode, self.envFile) + if insType != MODE_PRIMARY: + self.commonGsCtl.startInstanceWithMode(primaryHost, dataNode, + MODE_PRIMARY, self.envFile) + # if stat if not normal,rebuild standby database + standbyHosts = self.context.newHostList + for host in standbyHosts: + hostName = self.context.backIpNameMap[host] + dataNode = self.context.clusterInfoDict[hostName]["dataNode"] + insType, dbStat = self.commonGsCtl.queryInstanceStatus(hostName, + dataNode, self.envFile) + if dbStat != STAT_NORMAL: + self.commonGsCtl.buildInstance(hostName, dataNode, + MODE_STANDBY, self.envFile) + + self.commonGsCtl.startOmCluster(primaryHosts, self.envFile) + + def setGUCOnClusterHosts(self, hostNames=[]): + """ + guc config on all hosts + """ + + gucDict = self.getGUCConfig() + + tempShFile = "%s/guc.sh" % self.tempFileDir + + if len(hostNames) == 0: + hostNames = self.context.nodeNameList + + for host in hostNames: + + command = "source %s ; " % self.envFile + gucDict[host] + + self.logger.debug(command) + + sshTool = SshTool([host]) + + # create temporary dir to save guc command bashfile. + mkdirCmd = "mkdir -m a+x -p %s; chown %s:%s %s" % \ + (self.tempFileDir,self.user,self.group,self.tempFileDir) + retmap, output = sshTool.getSshStatusOutput(mkdirCmd, [host], self.envFile) + + subprocess.getstatusoutput("mkdir -m a+x -p %s; touch %s; \ + cat /dev/null > %s" % \ + (self.tempFileDir, tempShFile, tempShFile)) + fo = open("%s" % tempShFile, "w") + fo.write("#bash\n") + fo.write( command ) + fo.close() + + # send guc command bashfile to each host and execute it. + sshTool.scpFiles("%s" % tempShFile, "%s" % tempShFile, [host], + self.envFile) + + resultMap, outputCollect = sshTool.getSshStatusOutput("sh %s" % \ + tempShFile, [host], self.envFile) + + self.logger.debug(outputCollect) + + def getGUCConfig(self): + """ + get guc config of each node: + replconninfo[index] + remote_read_mode + replication_type + """ + nodeDict = self.context.clusterInfoDict + hostNames = self.context.nodeNameList + + gucDict = {} + + for hostName in hostNames: + + localeHostInfo = nodeDict[hostName] + index = 1 + guc_tempate_str = "" + for remoteHost in hostNames: + if(remoteHost == hostName): + continue + remoteHostInfo = nodeDict[remoteHost] + + guc_repl_template = """\ + gs_guc set -D {dn} -c "replconninfo{index}=\ + 'localhost={localhost} localport={localport} \ + localheartbeatport={localeHeartPort} \ + localservice={localservice} \ + remotehost={remoteNode} \ + remoteport={remotePort} \ + remoteheartbeatport={remoteHeartPort} \ + remoteservice={remoteservice}'" + """.format(dn=localeHostInfo["dataNode"], + index=index, + localhost=localeHostInfo["sshIp"], + localport=localeHostInfo["localport"], + localeHeartPort=localeHostInfo["heartBeatPort"], + localservice=localeHostInfo["localservice"], + remoteNode=remoteHostInfo["sshIp"], + remotePort=remoteHostInfo["localport"], + remoteHeartPort=remoteHostInfo["heartBeatPort"], + remoteservice=remoteHostInfo["localservice"]) + + guc_tempate_str += guc_repl_template + + index += 1 + + guc_mode_type = """ + gs_guc set -D {dn} -c 'remote_read_mode=off'; + gs_guc set -D {dn} -c 'replication_type=1'; + """.format(dn=localeHostInfo["dataNode"]) + guc_tempate_str += guc_mode_type + + gucDict[hostName] = guc_tempate_str + return gucDict + + def checkLocalModeOnStandbyHosts(self): + """ + """ + standbyHosts = self.context.newHostList + self.logger.log("Checking the database with locale mode.") + for host in standbyHosts: + hostName = self.context.backIpNameMap[host] + dataNode = self.context.clusterInfoDict[hostName]["dataNode"] + insType, dbStat = self.commonGsCtl.queryInstanceStatus(hostName, + dataNode, self.envFile) + if insType not in (MODE_PRIMARY, MODE_STANDBY, MODE_NORMAL): + GaussLog.exitWithError(ErrorCode.GAUSS_357["GAUSS_35703"] % + (hostName, self.user, dataNode, dataNode)) + + self.logger.log("Successfully checked the database with locale mode.") + + def preInstall(self): + """ + preinstall on new hosts. + """ + self.logger.log("Start to preinstall database on the new \ +standby nodes.") + self.sendSoftToHosts() + self.generateAndSendXmlFile() + self.preInstallOnHosts() + self.logger.log("Successfully preinstall database on the new \ +standby nodes.") + + + def clearTmpFile(self): + """ + clear temporary file after expansion success + """ + self.logger.debug("start to delete temporary file") + hostNames = self.context.nodeNameList + sshTool = SshTool(hostNames) + clearCmd = "source %s ; rm -rf %s" % (self.envFile, self.tempFileDir) + result, output = sshTool.getSshStatusOutput(clearCmd, + hostNames, self.envFile) + self.logger.debug(output) + + def installAndExpansion(self): + """ + install database and expansion standby node with db om user + """ + pvalue = Value('i', 0) + proc = Process(target=self.installProcess, args=(pvalue,)) + proc.start() + proc.join() + if not pvalue.value: + sys.exit(1) + else: + proc.terminate() + + def installProcess(self, pvalue): + # change to db manager user. the below steps run with db manager user. + self.changeUser() + + if not self.context.standbyLocalMode: + self.logger.log("\nStart to install database on the new \ +standby nodes.") + self.installDatabaseOnHosts() + else: + self.logger.log("\nStandby nodes is installed with locale mode.") + self.checkLocalModeOnStandbyHosts() + + self.logger.log("\nDatabase on standby nodes installed finished. \ +Start to establish the primary-standby relationship.") + self.buildStandbyRelation() + # process success + pvalue.value = 1 + + def run(self): + """ + start expansion + """ + # preinstall on standby nodes with root user. + if not self.context.standbyLocalMode: + self.preInstall() + + self.installAndExpansion() + + self.clearTmpFile() + + self.logger.log("\nSuccess to expansion standby nodes.") + + +class GsCtlCommon: + + def __init__(self, expansion): + """ + """ + self.logger = expansion.logger + + def queryInstanceStatus(self, host, datanode, env): + """ + """ + command = "source %s ; gs_ctl query -D %s" % (env, datanode) + sshTool = SshTool([datanode]) + resultMap, outputCollect = sshTool.getSshStatusOutput(command, + [host], env) + self.logger.debug(outputCollect) + localRole = re.findall(r"local_role.*: (.*?)\n", outputCollect) + db_state = re.findall(r"db_state.*: (.*?)\n", outputCollect) + + insType = "" + + if(len(localRole)) == 0: + insType = "" + else: + insType = localRole[0] + + dbStatus = "" + if(len(db_state)) == 0: + dbStatus = "" + else: + dbStatus = db_state[0] + + return insType.strip().lower(), dbStatus.strip().lower() + + def stopInstance(self, host, datanode, env): + """ + """ + command = "source %s ; gs_ctl stop -D %s" % (env, datanode) + sshTool = SshTool([host]) + resultMap, outputCollect = sshTool.getSshStatusOutput(command, + [host], env) + self.logger.debug(host) + self.logger.debug(outputCollect) + + def startInstanceWithMode(self, host, datanode, mode, env): + """ + """ + command = "source %s ; gs_ctl start -D %s -M %s" % (env, datanode, mode) + self.logger.debug(command) + sshTool = SshTool([host]) + resultMap, outputCollect = sshTool.getSshStatusOutput(command, + [host], env) + self.logger.debug(host) + self.logger.debug(outputCollect) + + def buildInstance(self, host, datanode, mode, env): + command = "source %s ; gs_ctl build -D %s -M %s" % (env, datanode, mode) + self.logger.debug(command) + sshTool = SshTool([host]) + resultMap, outputCollect = sshTool.getSshStatusOutput(command, + [host], env) + self.logger.debug(host) + self.logger.debug(outputCollect) + + def startOmCluster(self, host, env): + """ + om tool start cluster + """ + command = "source %s ; gs_om -t start" % env + self.logger.debug(command) + sshTool = SshTool([host]) + resultMap, outputCollect = sshTool.getSshStatusOutput(command, + [host], env) + self.logger.debug(host) + self.logger.debug(outputCollect) + + def queryOmCluster(self, host, env): + """ + query om cluster detail with command: + gs_om -t status --detail + """ + command = "source %s ; gs_om -t status --detail" % env + sshTool = SshTool([host]) + resultMap, outputCollect = sshTool.getSshStatusOutput(command, + [host], env) + self.logger.debug(host) + self.logger.debug(outputCollect) + return outputCollect + + diff --git a/src/manager/om/script/impl/expansion/__init__.py b/src/manager/om/script/impl/expansion/__init__.py new file mode 100644 index 000000000..e69de29bb From e226023e610fa0a901d0114173cc037653258c3e Mon Sep 17 00:00:00 2001 From: sqyyeah <948885883@qq.com> Date: Thu, 27 Aug 2020 10:21:30 +0800 Subject: [PATCH 3/6] postgres_fdw supports column serial --- .../optimizer/commands/sequence.cpp | 3 +- src/test/regress/output/postgres_fdw.source | 303 +++++++----------- 2 files changed, 124 insertions(+), 182 deletions(-) diff --git a/src/gausskernel/optimizer/commands/sequence.cpp b/src/gausskernel/optimizer/commands/sequence.cpp index eeeb1ffb5..bf4950867 100755 --- a/src/gausskernel/optimizer/commands/sequence.cpp +++ b/src/gausskernel/optimizer/commands/sequence.cpp @@ -2090,7 +2090,8 @@ static void process_owned_by(const Relation seqrel, List* owned_by) /* Must be a regular table */ if (tablerel->rd_rel->relkind != RELKIND_RELATION && - !(tablerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE && isMOTFromTblOid(RelationGetRelid(tablerel)))) + !(tablerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE && (isMOTFromTblOid(RelationGetRelid(tablerel)) || + isPostgresFDWFromTblOid(RelationGetRelid(tablerel))))) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("referenced relation \"%s\" is not a table", RelationGetRelationName(tablerel)))); diff --git a/src/test/regress/output/postgres_fdw.source b/src/test/regress/output/postgres_fdw.source index 53d7154fe..8e6b8be7a 100644 --- a/src/test/regress/output/postgres_fdw.source +++ b/src/test/regress/output/postgres_fdw.source @@ -2868,33 +2868,34 @@ NOTICE: CREATE TABLE will create implicit sequence "loc1_f1_seq" for serial col create foreign table rem1 (f1 serial, f2 text) server loopback options(table_name 'loc1'); NOTICE: CREATE FOREIGN TABLE will create implicit sequence "rem1_f1_seq" for serial column "rem1.f1" -ERROR: referenced relation "rem1" is not a table select pg_catalog.setval('rem1_f1_seq', 10, false); -ERROR: relation "rem1_f1_seq" does not exist -LINE 1: select pg_catalog.setval('rem1_f1_seq', 10, false); - ^ -CONTEXT: referenced column: setval + setval +-------- + 10 +(1 row) + insert into loc1(f2) values('hi'); insert into rem1(f2) values('hi remote'); -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: insert into rem1(f2) values('hi remote'); - ^ insert into loc1(f2) values('bye'); insert into rem1(f2) values('bye remote'); -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: insert into rem1(f2) values('bye remote'); - ^ select * from loc1; - f1 | f2 -----+----- + f1 | f2 +----+------------ 1 | hi + 10 | hi remote 2 | bye -(2 rows) + 11 | bye remote +(4 rows) select * from rem1; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: select * from rem1; - ^ + f1 | f2 +----+------------ + 1 | hi + 10 | hi remote + 2 | bye + 11 | bye remote +(4 rows) + -- =================================================================== -- test local triggers -- =================================================================== @@ -2907,10 +2908,10 @@ BEGIN END;$$; CREATE TRIGGER trig_stmt_before BEFORE DELETE OR INSERT OR UPDATE ON rem1 FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func(); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view CREATE TRIGGER trig_stmt_after AFTER DELETE OR INSERT OR UPDATE ON rem1 FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func(); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view CREATE OR REPLACE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpgsql AS $$ @@ -2953,97 +2954,67 @@ $$; CREATE TRIGGER trig_row_before BEFORE INSERT OR UPDATE OR DELETE ON rem1 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view CREATE TRIGGER trig_row_after AFTER INSERT OR UPDATE OR DELETE ON rem1 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view delete from rem1; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: delete from rem1; - ^ insert into rem1 values(1,'insert'); -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: insert into rem1 values(1,'insert'); - ^ update rem1 set f2 = 'update' where f1 = 1; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: update rem1 set f2 = 'update' where f1 = 1; - ^ update rem1 set f2 = f2 || f2; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: update rem1 set f2 = f2 || f2; - ^ -- cleanup DROP TRIGGER trig_row_before ON rem1; -ERROR: relation "rem1" does not exist +ERROR: trigger "trig_row_before" for table "rem1" does not exist DROP TRIGGER trig_row_after ON rem1; -ERROR: relation "rem1" does not exist +ERROR: trigger "trig_row_after" for table "rem1" does not exist DROP TRIGGER trig_stmt_before ON rem1; -ERROR: relation "rem1" does not exist +ERROR: trigger "trig_stmt_before" for table "rem1" does not exist DROP TRIGGER trig_stmt_after ON rem1; -ERROR: relation "rem1" does not exist +ERROR: trigger "trig_stmt_after" for table "rem1" does not exist DELETE from rem1; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: DELETE from rem1; - ^ -- Test WHEN conditions CREATE TRIGGER trig_row_before_insupd BEFORE INSERT OR UPDATE ON rem1 FOR EACH ROW WHEN (NEW.f2 like '%update%') EXECUTE PROCEDURE trigger_data(23,'skidoo'); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view CREATE TRIGGER trig_row_after_insupd AFTER INSERT OR UPDATE ON rem1 FOR EACH ROW WHEN (NEW.f2 like '%update%') EXECUTE PROCEDURE trigger_data(23,'skidoo'); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view -- Insert or update not matching: nothing happens INSERT INTO rem1 values(1, 'insert'); -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: INSERT INTO rem1 values(1, 'insert'); - ^ UPDATE rem1 set f2 = 'test'; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: UPDATE rem1 set f2 = 'test'; - ^ -- Insert or update matching: triggers are fired INSERT INTO rem1 values(2, 'update'); -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: INSERT INTO rem1 values(2, 'update'); - ^ UPDATE rem1 set f2 = 'update update' where f1 = '2'; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: UPDATE rem1 set f2 = 'update update' where f1 = '2'; - ^ CREATE TRIGGER trig_row_before_delete BEFORE DELETE ON rem1 FOR EACH ROW WHEN (OLD.f2 like '%update%') EXECUTE PROCEDURE trigger_data(23,'skidoo'); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view CREATE TRIGGER trig_row_after_delete AFTER DELETE ON rem1 FOR EACH ROW WHEN (OLD.f2 like '%update%') EXECUTE PROCEDURE trigger_data(23,'skidoo'); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view -- Trigger is fired for f1=2, not for f1=1 DELETE FROM rem1; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: DELETE FROM rem1; - ^ -- cleanup DROP TRIGGER trig_row_before_insupd ON rem1; -ERROR: relation "rem1" does not exist +ERROR: trigger "trig_row_before_insupd" for table "rem1" does not exist DROP TRIGGER trig_row_after_insupd ON rem1; -ERROR: relation "rem1" does not exist +ERROR: trigger "trig_row_after_insupd" for table "rem1" does not exist DROP TRIGGER trig_row_before_delete ON rem1; -ERROR: relation "rem1" does not exist +ERROR: trigger "trig_row_before_delete" for table "rem1" does not exist DROP TRIGGER trig_row_after_delete ON rem1; -ERROR: relation "rem1" does not exist +ERROR: trigger "trig_row_after_delete" for table "rem1" does not exist -- Test various RETURN statements in BEFORE triggers. CREATE FUNCTION trig_row_before_insupdate() RETURNS TRIGGER AS $$ BEGIN @@ -3054,134 +3025,123 @@ $$ language plpgsql; CREATE TRIGGER trig_row_before_insupd BEFORE INSERT OR UPDATE ON rem1 FOR EACH ROW EXECUTE PROCEDURE trig_row_before_insupdate(); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view -- The new values should have 'triggered' appended INSERT INTO rem1 values(1, 'insert'); -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: INSERT INTO rem1 values(1, 'insert'); - ^ SELECT * from loc1; - f1 | f2 -----+----- - 1 | hi - 2 | bye -(2 rows) + f1 | f2 +----+-------- + 1 | insert +(1 row) INSERT INTO rem1 values(2, 'insert') RETURNING f2; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: INSERT INTO rem1 values(2, 'insert') RETURNING f2; - ^ + f2 +-------- + insert +(1 row) + SELECT * from loc1; - f1 | f2 -----+----- - 1 | hi - 2 | bye + f1 | f2 +----+-------- + 1 | insert + 2 | insert (2 rows) UPDATE rem1 set f2 = ''; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: UPDATE rem1 set f2 = ''; - ^ SELECT * from loc1; - f1 | f2 -----+----- - 1 | hi - 2 | bye + f1 | f2 +----+---- + 1 | + 2 | (2 rows) UPDATE rem1 set f2 = 'skidoo' RETURNING f2; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: UPDATE rem1 set f2 = 'skidoo' RETURNING f2; - ^ + f2 +-------- + skidoo + skidoo +(2 rows) + SELECT * from loc1; - f1 | f2 -----+----- - 1 | hi - 2 | bye + f1 | f2 +----+-------- + 1 | skidoo + 2 | skidoo (2 rows) EXPLAIN (verbose, costs off) UPDATE rem1 set f1 = 10; -- all columns should be transmitted -ERROR: relation "rem1" does not exist on datanode1 -LINE 2: UPDATE rem1 set f1 = 10; - ^ + QUERY PLAN +----------------------------------------------------------------- + Update on public.rem1 + -> Foreign Scan on public.rem1 + Output: 10, f2, ctid + Remote SQL: SELECT f2, ctid FROM public.loc1 FOR UPDATE +(4 rows) + UPDATE rem1 set f1 = 10; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: UPDATE rem1 set f1 = 10; - ^ SELECT * from loc1; - f1 | f2 -----+----- - 1 | hi - 2 | bye + f1 | f2 +----+-------- + 10 | skidoo + 10 | skidoo (2 rows) DELETE FROM rem1; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: DELETE FROM rem1; - ^ -- Add a second trigger, to check that the changes are propagated correctly -- from trigger to trigger CREATE TRIGGER trig_row_before_insupd2 BEFORE INSERT OR UPDATE ON rem1 FOR EACH ROW EXECUTE PROCEDURE trig_row_before_insupdate(); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view INSERT INTO rem1 values(1, 'insert'); -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: INSERT INTO rem1 values(1, 'insert'); - ^ SELECT * from loc1; - f1 | f2 -----+----- - 1 | hi - 2 | bye -(2 rows) + f1 | f2 +----+-------- + 1 | insert +(1 row) INSERT INTO rem1 values(2, 'insert') RETURNING f2; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: INSERT INTO rem1 values(2, 'insert') RETURNING f2; - ^ + f2 +-------- + insert +(1 row) + SELECT * from loc1; - f1 | f2 -----+----- - 1 | hi - 2 | bye + f1 | f2 +----+-------- + 1 | insert + 2 | insert (2 rows) UPDATE rem1 set f2 = ''; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: UPDATE rem1 set f2 = ''; - ^ SELECT * from loc1; - f1 | f2 -----+----- - 1 | hi - 2 | bye + f1 | f2 +----+---- + 1 | + 2 | (2 rows) UPDATE rem1 set f2 = 'skidoo' RETURNING f2; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: UPDATE rem1 set f2 = 'skidoo' RETURNING f2; - ^ + f2 +-------- + skidoo + skidoo +(2 rows) + SELECT * from loc1; - f1 | f2 -----+----- - 1 | hi - 2 | bye + f1 | f2 +----+-------- + 1 | skidoo + 2 | skidoo (2 rows) DROP TRIGGER trig_row_before_insupd ON rem1; -ERROR: relation "rem1" does not exist +ERROR: trigger "trig_row_before_insupd" for table "rem1" does not exist DROP TRIGGER trig_row_before_insupd2 ON rem1; -ERROR: relation "rem1" does not exist +ERROR: trigger "trig_row_before_insupd2" for table "rem1" does not exist DELETE from rem1; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: DELETE from rem1; - ^ INSERT INTO rem1 VALUES (1, 'test'); -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: INSERT INTO rem1 VALUES (1, 'test'); - ^ -- Test with a trigger returning NULL CREATE FUNCTION trig_null() RETURNS TRIGGER AS $$ BEGIN @@ -3191,68 +3151,49 @@ $$ language plpgsql; CREATE TRIGGER trig_null BEFORE INSERT OR UPDATE OR DELETE ON rem1 FOR EACH ROW EXECUTE PROCEDURE trig_null(); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view -- Nothing should have changed. INSERT INTO rem1 VALUES (2, 'test2'); -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: INSERT INTO rem1 VALUES (2, 'test2'); - ^ SELECT * from loc1; - f1 | f2 -----+----- - 1 | hi - 2 | bye + f1 | f2 +----+------- + 1 | test + 2 | test2 (2 rows) UPDATE rem1 SET f2 = 'test2'; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: UPDATE rem1 SET f2 = 'test2'; - ^ SELECT * from loc1; - f1 | f2 -----+----- - 1 | hi - 2 | bye + f1 | f2 +----+------- + 1 | test2 + 2 | test2 (2 rows) DELETE from rem1; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: DELETE from rem1; - ^ SELECT * from loc1; - f1 | f2 -----+----- - 1 | hi - 2 | bye -(2 rows) + f1 | f2 +----+---- +(0 rows) DROP TRIGGER trig_null ON rem1; -ERROR: relation "rem1" does not exist +ERROR: trigger "trig_null" for table "rem1" does not exist DELETE from rem1; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: DELETE from rem1; - ^ -- Test a combination of local and remote triggers CREATE TRIGGER trig_row_before BEFORE INSERT OR UPDATE OR DELETE ON rem1 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view CREATE TRIGGER trig_row_after AFTER INSERT OR UPDATE OR DELETE ON rem1 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); -ERROR: relation "rem1" does not exist +ERROR: "rem1" is not a table or view CREATE TRIGGER trig_local_before BEFORE INSERT OR UPDATE ON loc1 FOR EACH ROW EXECUTE PROCEDURE trig_row_before_insupdate(); INSERT INTO rem1(f2) VALUES ('test'); -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: INSERT INTO rem1(f2) VALUES ('test'); - ^ UPDATE rem1 SET f2 = 'testo'; -ERROR: relation "rem1" does not exist on datanode1 -LINE 1: UPDATE rem1 SET f2 = 'testo'; - ^ -- Test returning a system attribute INSERT INTO rem1(f2) VALUES ('test') RETURNING ctid; -ERROR: relation "rem1" does not exist on datanode1 +ERROR: column "ctid" does not exist LINE 1: INSERT INTO rem1(f2) VALUES ('test') RETURNING ctid; - ^ + ^ +CONTEXT: referenced column: ctid \ No newline at end of file From 6c5141b20bf65f0b480a4727fcc6b2ba4a6ce843 Mon Sep 17 00:00:00 2001 From: luozihao Date: Mon, 24 Aug 2020 21:43:03 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E5=88=86=E5=8C=BA=E8=A1=A8=E5=A4=96?= =?UTF-8?q?=E9=94=AE=E5=BC=80=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 代码格式修改 函数逻辑修改 --- .../optimizer/commands/tablecmds.cpp | 4 +- .../optimizer/commands/trigger.cpp | 32 +- .../expected/partition_foreign_key.out | 751 ++++++++++++++++++ .../regress/expected/partition_trigger.out | 295 +++++++ src/test/regress/parallel_schedule10 | 2 + .../regress/sql/partition_foreign_key.sql | 312 ++++++++ src/test/regress/sql/partition_trigger.sql | 143 ++++ 7 files changed, 1519 insertions(+), 20 deletions(-) create mode 100644 src/test/regress/expected/partition_foreign_key.out create mode 100644 src/test/regress/expected/partition_trigger.out create mode 100644 src/test/regress/sql/partition_foreign_key.sql create mode 100644 src/test/regress/sql/partition_trigger.sql diff --git a/src/gausskernel/optimizer/commands/tablecmds.cpp b/src/gausskernel/optimizer/commands/tablecmds.cpp index 636166ffa..3ed000464 100644 --- a/src/gausskernel/optimizer/commands/tablecmds.cpp +++ b/src/gausskernel/optimizer/commands/tablecmds.cpp @@ -9224,13 +9224,13 @@ static void ATAddForeignKeyConstraint(AlteredTableInfo* tab, Relation rel, Const ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(pkrel)))); - +#ifdef ENABLE_MULTIPLE_NODES if (RELATION_IS_PARTITIONED(pkrel)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("Invalid FOREIGN KEY constraints"), errdetail("Partitioned table cannot be referenced table"))); - +#endif /* * References from permanent or unlogged tables to temp tables, and from * permanent tables to unlogged tables, are disallowed because the diff --git a/src/gausskernel/optimizer/commands/trigger.cpp b/src/gausskernel/optimizer/commands/trigger.cpp index 255c71be4..c34e47a1e 100644 --- a/src/gausskernel/optimizer/commands/trigger.cpp +++ b/src/gausskernel/optimizer/commands/trigger.cpp @@ -83,6 +83,8 @@ static void ConvertTriggerToFK(CreateTrigStmt* stmt, Oid funcoid); static void SetTriggerFlags(TriggerDesc* trigdesc, const Trigger* trigger); static HeapTuple GetTupleForTrigger(EState* estate, EPQState* epqstate, ResultRelInfo* relinfo, Oid targetPartitionOid, int2 bucketid, ItemPointer tid, TupleTableSlot** newSlot); + +static void ReleaseFakeRelation(Relation relation, Partition part, Relation* fakeRelation); static bool TriggerEnabled(EState* estate, ResultRelInfo* relinfo, Trigger* trigger, TriggerEvent event, const Bitmapset* modifiedCols, HeapTuple oldtup, HeapTuple newtup); static HeapTuple ExecCallTriggerFunc( @@ -2624,12 +2626,7 @@ static HeapTuple GetTupleForTrigger(EState* estate, EPQState* epqstate, ResultRe case HeapTupleSelfUpdated: /* treat it as deleted; do not process */ ReleaseBuffer(buffer); - if (RELATION_IS_PARTITIONED(relation)) { - partitionClose(relation, part, NoLock); - } - if (RELATION_OWN_BUCKET(relation)) { - releaseDummyRelation(&fakeRelation); - } + ReleaseFakeRelation(relation, part, &fakeRelation); return NULL; case HeapTupleMayBeUpdated: @@ -2665,12 +2662,7 @@ static HeapTuple GetTupleForTrigger(EState* estate, EPQState* epqstate, ResultRe * if tuple was deleted or PlanQual failed for updated tuple - * we must not process this tuple! */ - if (RELATION_IS_PARTITIONED(relation)) { - partitionClose(relation, part, NoLock); - } - if (RELATION_OWN_BUCKET(relation)) { - releaseDummyRelation(&fakeRelation); - } + ReleaseFakeRelation(relation, part, &fakeRelation); return NULL; default: @@ -2716,16 +2708,20 @@ static HeapTuple GetTupleForTrigger(EState* estate, EPQState* epqstate, ResultRe result = heapCopyTuple(&tuple, RelationGetDescr(relation), BufferGetPage(buffer)); ReleaseBuffer(buffer); - if (RELATION_IS_PARTITIONED(relation)) { - partitionClose(relation, part, NoLock); - } - if (RELATION_OWN_BUCKET(relation)) { - releaseDummyRelation(&fakeRelation); - } + ReleaseFakeRelation(relation, part, &fakeRelation); return result; } +static void ReleaseFakeRelation(Relation relation, Partition part, Relation* fakeRelation){ + if (RELATION_IS_PARTITIONED(relation)) { + partitionClose(relation, part, NoLock); + releaseDummyRelation(fakeRelation); + } else if (RELATION_OWN_BUCKET(relation)) { + releaseDummyRelation(fakeRelation); + } +} + /* * Is trigger enabled to fire? */ diff --git a/src/test/regress/expected/partition_foreign_key.out b/src/test/regress/expected/partition_foreign_key.out new file mode 100644 index 000000000..fd91efa4e --- /dev/null +++ b/src/test/regress/expected/partition_foreign_key.out @@ -0,0 +1,751 @@ +DROP TABLE IF EXISTS pt2; +NOTICE: table "pt2" does not exist, skipping +DROP TABLE IF EXISTS pt1; +NOTICE: table "pt1" does not exist, skipping +CREATE TABLE pt1( + d_date date UNIQUE +)partition BY RANGE (d_date) INTERVAL('1 day') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); +NOTICE: CREATE TABLE / UNIQUE will create implicit index "pt1_d_date_key" for table "pt1" + +CREATE TABLE pt2( + d_date date +)partition BY RANGE (d_date) INTERVAL('1 day') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + +ALTER TABLE pt2 ADD CONSTRAINT test_foreign FOREIGN KEY(d_date) +REFERENCES pt1(d_date) on UPDATE CASCADE ON DELETE CASCADE; + +INSERT into pt1 VALUES('1999-01-01'),('1999-01-02'),('1999-01-03'),('1999-01-04'); + +INSERT into pt2 VALUES('1999-01-01'); + +INSERT into pt2 VALUES('2000-01-01'); +ERROR: insert or update on table "pt2" violates foreign key constraint "test_foreign" +DETAIL: Key (d_date)=(Sat Jan 01 00:00:00 2000) is not present in table "pt1". + +update pt2 set pt2.d_date = '2000-01-01' where pt2.d_date = '1999-01-01'; +ERROR: insert or update on table "pt2" violates foreign key constraint "test_foreign" +DETAIL: Key (d_date)=(Sat Jan 01 00:00:00 2000) is not present in table "pt1". + +update pt2 set pt2.d_date = '1999-01-02' where pt2.d_date = '1999-01-01'; + +SELECT * FROM pt2; + d_date +-------------------------- + Sat Jan 02 00:00:00 1999 +(1 row) + + +SELECT * FROM pt2 PARTITION(sys_p2); + d_date +-------------------------- + Sat Jan 02 00:00:00 1999 +(1 row) + + +delete from pt2; +INSERT into pt2 VALUES('1999-01-01'), ('1999-01-03'),('1999-01-04'); + +INSERT into pt1 VALUES('1999-01-08'); + +INSERT into pt2 VALUES('1999-01-08'); + +update pt1 set pt1.d_date = '2000-01-01' where pt1.d_date = '1999-01-08'; + +delete from pt1 where pt1.d_date = '2000-01-01'; + +DROP TABLE pt2; +DROP TABLE pt1; + +CREATE TABLE pt1( + d_date date UNIQUE +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); +NOTICE: CREATE TABLE / UNIQUE will create implicit index "pt1_d_date_key" for table "pt1" + +CREATE TABLE pt2( + d_date date +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + +ALTER TABLE pt2 ADD CONSTRAINT test_foreign FOREIGN KEY(d_date) +REFERENCES pt1(d_date) on UPDATE CASCADE ON DELETE CASCADE; + +INSERT INTO pt1 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + +INSERT INTO pt2 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + +delete from pt2 where pt2.d_date<'1999-03-01'; + +select * from pt2 where pt2.d_date<'1999-06-01'; + d_date +-------------------------- + Mon Mar 01 00:00:00 1999 + Tue Mar 02 00:00:00 1999 + Fri Mar 19 00:00:00 1999 + Sat Mar 20 00:00:00 1999 + Thu Apr 01 00:00:00 1999 + Fri Apr 02 00:00:00 1999 + Mon Apr 19 00:00:00 1999 + Tue Apr 20 00:00:00 1999 + Sat May 01 00:00:00 1999 + Sun May 02 00:00:00 1999 + Wed May 19 00:00:00 1999 + Thu May 20 00:00:00 1999 +(12 rows) + + +delete from pt1 where pt1.d_date<'1999-05-01'; + +delete from pt2 where pt2.d_date>'1999-11-15' and pt2.d_date<'1999-12-15'; +SELECT * FROM pt2 WHERE pt2.d_date>='1999-10-01'; + d_date +-------------------------- + Fri Oct 01 00:00:00 1999 + Sat Oct 02 00:00:00 1999 + Tue Oct 19 00:00:00 1999 + Wed Oct 20 00:00:00 1999 + Mon Nov 01 00:00:00 1999 + Tue Nov 02 00:00:00 1999 + Sun Dec 19 00:00:00 1999 + Mon Dec 20 00:00:00 1999 +(8 rows) + + +delete from pt1 where pt1.d_date>'1999-10-15' and pt1.d_date<'1999-11-15'; +SELECT * FROM pt1 WHERE pt1.d_date>='1999-10-01'; + d_date +-------------------------- + Fri Oct 01 00:00:00 1999 + Sat Oct 02 00:00:00 1999 + Fri Nov 19 00:00:00 1999 + Sat Nov 20 00:00:00 1999 + Wed Dec 01 00:00:00 1999 + Thu Dec 02 00:00:00 1999 + Sun Dec 19 00:00:00 1999 + Mon Dec 20 00:00:00 1999 +(8 rows) + + +SELECT * FROM pt2 WHERE pt2.d_date>='1999-10-01'; + d_date +-------------------------- + Fri Oct 01 00:00:00 1999 + Sat Oct 02 00:00:00 1999 + Sun Dec 19 00:00:00 1999 + Mon Dec 20 00:00:00 1999 +(4 rows) + + +UPDATE pt2 set d_date = d_date - INTERVAL '4' month where d_date>='1999-05-01' and d_date<'1999-06-01'; +ERROR: insert or update on table "pt2" violates foreign key constraint "test_foreign" +DETAIL: Key (d_date)=(Fri Jan 01 00:00:00 1999) is not present in table "pt1". + +UPDATE pt1 set d_date = d_date - INTERVAL '4' month where d_date>='1999-05-01' and d_date<'1999-06-01'; +SELECT * FROM pt1 WHERE d_date<'1999-07-01'; + d_date +-------------------------- + Fri Jan 01 00:00:00 1999 + Sat Jan 02 00:00:00 1999 + Tue Jan 19 00:00:00 1999 + Wed Jan 20 00:00:00 1999 + Tue Jun 01 00:00:00 1999 + Wed Jun 02 00:00:00 1999 + Sat Jun 19 00:00:00 1999 + Sun Jun 20 00:00:00 1999 +(8 rows) + + +SELECT * FROM pt1 WHERE d_date<'1999-07-01'; + d_date +-------------------------- + Fri Jan 01 00:00:00 1999 + Sat Jan 02 00:00:00 1999 + Tue Jan 19 00:00:00 1999 + Wed Jan 20 00:00:00 1999 + Tue Jun 01 00:00:00 1999 + Wed Jun 02 00:00:00 1999 + Sat Jun 19 00:00:00 1999 + Sun Jun 20 00:00:00 1999 +(8 rows) + + +UPDATE pt2 set d_date = d_date - INTERVAL '1' month where d_date>='1999-06-01' and d_date<='1999-07-15'; +ERROR: insert or update on table "pt2" violates foreign key constraint "test_foreign" +DETAIL: Key (d_date)=(Sat May 01 00:00:00 1999) is not present in table "pt1". + +UPDATE pt1 set d_date = d_date - INTERVAL '1' month where d_date>='1999-06-01' and d_date<='1999-07-15'; +SELECT * FROM pt1 WHERE d_date<'1999-08-01'; + d_date +-------------------------- + Fri Jan 01 00:00:00 1999 + Sat Jan 02 00:00:00 1999 + Tue Jan 19 00:00:00 1999 + Wed Jan 20 00:00:00 1999 + Sat May 01 00:00:00 1999 + Sun May 02 00:00:00 1999 + Wed May 19 00:00:00 1999 + Thu May 20 00:00:00 1999 + Tue Jun 01 00:00:00 1999 + Wed Jun 02 00:00:00 1999 + Mon Jul 19 00:00:00 1999 + Tue Jul 20 00:00:00 1999 +(12 rows) + + +SELECT * FROM pt2 WHERE d_date<'1999-08-01'; + d_date +-------------------------- + Fri Jan 01 00:00:00 1999 + Sat Jan 02 00:00:00 1999 + Tue Jan 19 00:00:00 1999 + Wed Jan 20 00:00:00 1999 + Sat May 01 00:00:00 1999 + Sun May 02 00:00:00 1999 + Wed May 19 00:00:00 1999 + Thu May 20 00:00:00 1999 + Tue Jun 01 00:00:00 1999 + Wed Jun 02 00:00:00 1999 + Mon Jul 19 00:00:00 1999 + Tue Jul 20 00:00:00 1999 +(12 rows) + + +DROP TABLE IF EXISTS pt2; +DROP TABLE IF EXISTS pt1; + +CREATE TABLE pt1( +d_date date UNIQUE +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')) +; +NOTICE: CREATE TABLE / UNIQUE will create implicit index "pt1_d_date_key" for table "pt1" + +CREATE TABLE pt2( +d_date date, +d_date_backup date +)partition BY RANGE (d_date_backup) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + + +ALTER TABLE pt2 ADD CONSTRAINT test_foreign FOREIGN KEY(d_date) +REFERENCES pt1(d_date) on UPDATE SET NULL ON DELETE SET NULL; + +INSERT INTO pt1 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + +INSERT INTO pt2 VALUES +('1999-01-01','1999-01-01'),('1999-01-02','1999-01-02'), +('1999-01-19','1999-01-19'),('1999-01-20','1999-01-20'), +('1999-02-01','1999-02-01'),('1999-02-02','1999-02-02'), +('1999-02-19','1999-02-19'),('1999-02-20','1999-02-20'), +('1999-03-01','1999-03-01'),('1999-03-02','1999-03-02'), +('1999-03-19','1999-03-19'),('1999-03-20','1999-03-20'), +('1999-04-01','1999-04-01'),('1999-04-02','1999-04-02'), +('1999-04-19','1999-04-19'),('1999-04-20','1999-04-20'), +('1999-05-01','1999-05-01'),('1999-05-02','1999-05-02'), +('1999-05-19','1999-05-19'),('1999-05-20','1999-05-20'), +('1999-06-01','1999-06-01'),('1999-06-02','1999-06-02'), +('1999-06-19','1999-06-19'),('1999-06-20','1999-06-20'), +('1999-07-01','1999-07-01'),('1999-07-02','1999-07-02'), +('1999-07-19','1999-07-19'),('1999-07-20','1999-07-20'), +('1999-08-01','1999-08-01'),('1999-08-02','1999-08-02'), +('1999-08-19','1999-08-19'),('1999-08-20','1999-08-20'), +('1999-09-01','1999-09-01'),('1999-09-02','1999-09-02'), +('1999-09-19','1999-09-19'),('1999-09-20','1999-09-20'), +('1999-10-01','1999-10-01'),('1999-10-02','1999-10-02'), +('1999-10-19','1999-10-19'),('1999-10-20','1999-10-20'), +('1999-11-01','1999-11-01'),('1999-11-02','1999-11-02'), +('1999-11-19','1999-11-19'),('1999-11-20','1999-11-20'), +('1999-12-01','1999-12-01'),('1999-12-02','1999-12-02'), +('1999-12-19','1999-12-19'),('1999-12-20','1999-12-20'); + +insert into pt1 VALUES('2000-01-01'),('2000-01-02'), +('2000-01-19'),('2000-01-20'), +('2000-02-01'),('2000-02-02'), +('2000-02-19'),('2000-02-20'); +SELECT * FROM pt1 WHERE d_date>='2000-01-01'; + d_date +-------------------------- + Sat Jan 01 00:00:00 2000 + Sun Jan 02 00:00:00 2000 + Wed Jan 19 00:00:00 2000 + Thu Jan 20 00:00:00 2000 + Tue Feb 01 00:00:00 2000 + Wed Feb 02 00:00:00 2000 + Sat Feb 19 00:00:00 2000 + Sun Feb 20 00:00:00 2000 +(8 rows) + + +update pt1 set d_date = '2000-02-21' WHERE d_date = '2000-02-20'; +SELECT * FROM pt1 WHERE d_date>='2000-02-01'; + d_date +-------------------------- + Tue Feb 01 00:00:00 2000 + Wed Feb 02 00:00:00 2000 + Sat Feb 19 00:00:00 2000 + Mon Feb 21 00:00:00 2000 +(4 rows) + + +DELETE FROM pt1 WHERE d_date = '2000-02-21'; +SELECT * FROM pt1 WHERE d_date>='2000-02-01'; + d_date +-------------------------- + Tue Feb 01 00:00:00 2000 + Wed Feb 02 00:00:00 2000 + Sat Feb 19 00:00:00 2000 +(3 rows) + + +SELECT * FROM pt1 PARTITION FOR('1999-06-16'); + d_date +-------------------------- + Tue Jun 01 00:00:00 1999 + Wed Jun 02 00:00:00 1999 + Sat Jun 19 00:00:00 1999 + Sun Jun 20 00:00:00 1999 +(4 rows) + + +INSERT into pt2 VALUES('2020-08-19','2020-08-19'); +ERROR: insert or update on table "pt2" violates foreign key constraint "test_foreign" +DETAIL: Key (d_date)=(Wed Aug 19 00:00:00 2020) is not present in table "pt1". + +INSERT into pt2 VALUES('2000-01-01','2000-01-01'); + +update pt2 set pt2.d_date = '2020-08-13' +where pt2.d_date = '1999-01-01'; +ERROR: insert or update on table "pt2" violates foreign key constraint "test_foreign" +DETAIL: Key (d_date)=(Thu Aug 13 00:00:00 2020) is not present in table "pt1". + +update pt2 set pt2.d_date = '2000-02-01',pt2.d_date_backup = '2000-02-01' +where pt2.d_date = '2000-01-01'; + +SELECT * FROM pt2; + d_date | d_date_backup +--------------------------+-------------------------- + Fri Jan 01 00:00:00 1999 | Fri Jan 01 00:00:00 1999 + Sat Jan 02 00:00:00 1999 | Sat Jan 02 00:00:00 1999 + Tue Jan 19 00:00:00 1999 | Tue Jan 19 00:00:00 1999 + Wed Jan 20 00:00:00 1999 | Wed Jan 20 00:00:00 1999 + Mon Feb 01 00:00:00 1999 | Mon Feb 01 00:00:00 1999 + Tue Feb 02 00:00:00 1999 | Tue Feb 02 00:00:00 1999 + Fri Feb 19 00:00:00 1999 | Fri Feb 19 00:00:00 1999 + Sat Feb 20 00:00:00 1999 | Sat Feb 20 00:00:00 1999 + Mon Mar 01 00:00:00 1999 | Mon Mar 01 00:00:00 1999 + Tue Mar 02 00:00:00 1999 | Tue Mar 02 00:00:00 1999 + Fri Mar 19 00:00:00 1999 | Fri Mar 19 00:00:00 1999 + Sat Mar 20 00:00:00 1999 | Sat Mar 20 00:00:00 1999 + Thu Apr 01 00:00:00 1999 | Thu Apr 01 00:00:00 1999 + Fri Apr 02 00:00:00 1999 | Fri Apr 02 00:00:00 1999 + Mon Apr 19 00:00:00 1999 | Mon Apr 19 00:00:00 1999 + Tue Apr 20 00:00:00 1999 | Tue Apr 20 00:00:00 1999 + Sat May 01 00:00:00 1999 | Sat May 01 00:00:00 1999 + Sun May 02 00:00:00 1999 | Sun May 02 00:00:00 1999 + Wed May 19 00:00:00 1999 | Wed May 19 00:00:00 1999 + Thu May 20 00:00:00 1999 | Thu May 20 00:00:00 1999 + Tue Jun 01 00:00:00 1999 | Tue Jun 01 00:00:00 1999 + Wed Jun 02 00:00:00 1999 | Wed Jun 02 00:00:00 1999 + Sat Jun 19 00:00:00 1999 | Sat Jun 19 00:00:00 1999 + Sun Jun 20 00:00:00 1999 | Sun Jun 20 00:00:00 1999 + Thu Jul 01 00:00:00 1999 | Thu Jul 01 00:00:00 1999 + Fri Jul 02 00:00:00 1999 | Fri Jul 02 00:00:00 1999 + Mon Jul 19 00:00:00 1999 | Mon Jul 19 00:00:00 1999 + Tue Jul 20 00:00:00 1999 | Tue Jul 20 00:00:00 1999 + Sun Aug 01 00:00:00 1999 | Sun Aug 01 00:00:00 1999 + Mon Aug 02 00:00:00 1999 | Mon Aug 02 00:00:00 1999 + Thu Aug 19 00:00:00 1999 | Thu Aug 19 00:00:00 1999 + Fri Aug 20 00:00:00 1999 | Fri Aug 20 00:00:00 1999 + Wed Sep 01 00:00:00 1999 | Wed Sep 01 00:00:00 1999 + Thu Sep 02 00:00:00 1999 | Thu Sep 02 00:00:00 1999 + Sun Sep 19 00:00:00 1999 | Sun Sep 19 00:00:00 1999 + Mon Sep 20 00:00:00 1999 | Mon Sep 20 00:00:00 1999 + Fri Oct 01 00:00:00 1999 | Fri Oct 01 00:00:00 1999 + Sat Oct 02 00:00:00 1999 | Sat Oct 02 00:00:00 1999 + Tue Oct 19 00:00:00 1999 | Tue Oct 19 00:00:00 1999 + Wed Oct 20 00:00:00 1999 | Wed Oct 20 00:00:00 1999 + Mon Nov 01 00:00:00 1999 | Mon Nov 01 00:00:00 1999 + Tue Nov 02 00:00:00 1999 | Tue Nov 02 00:00:00 1999 + Fri Nov 19 00:00:00 1999 | Fri Nov 19 00:00:00 1999 + Sat Nov 20 00:00:00 1999 | Sat Nov 20 00:00:00 1999 + Wed Dec 01 00:00:00 1999 | Wed Dec 01 00:00:00 1999 + Thu Dec 02 00:00:00 1999 | Thu Dec 02 00:00:00 1999 + Sun Dec 19 00:00:00 1999 | Sun Dec 19 00:00:00 1999 + Mon Dec 20 00:00:00 1999 | Mon Dec 20 00:00:00 1999 + Tue Feb 01 00:00:00 2000 | Tue Feb 01 00:00:00 2000 +(49 rows) + + +SELECT * FROM pt2 PARTITION(sys_p2); + d_date | d_date_backup +--------------------------+-------------------------- + Mon Feb 01 00:00:00 1999 | Mon Feb 01 00:00:00 1999 + Tue Feb 02 00:00:00 1999 | Tue Feb 02 00:00:00 1999 + Fri Feb 19 00:00:00 1999 | Fri Feb 19 00:00:00 1999 + Sat Feb 20 00:00:00 1999 | Sat Feb 20 00:00:00 1999 +(4 rows) + + +delete from pt2 where pt2.d_date<'1999-03-01'; +select * from pt2 where pt2.d_date<'1999-06-01'; + d_date | d_date_backup +--------------------------+-------------------------- + Mon Mar 01 00:00:00 1999 | Mon Mar 01 00:00:00 1999 + Tue Mar 02 00:00:00 1999 | Tue Mar 02 00:00:00 1999 + Fri Mar 19 00:00:00 1999 | Fri Mar 19 00:00:00 1999 + Sat Mar 20 00:00:00 1999 | Sat Mar 20 00:00:00 1999 + Thu Apr 01 00:00:00 1999 | Thu Apr 01 00:00:00 1999 + Fri Apr 02 00:00:00 1999 | Fri Apr 02 00:00:00 1999 + Mon Apr 19 00:00:00 1999 | Mon Apr 19 00:00:00 1999 + Tue Apr 20 00:00:00 1999 | Tue Apr 20 00:00:00 1999 + Sat May 01 00:00:00 1999 | Sat May 01 00:00:00 1999 + Sun May 02 00:00:00 1999 | Sun May 02 00:00:00 1999 + Wed May 19 00:00:00 1999 | Wed May 19 00:00:00 1999 + Thu May 20 00:00:00 1999 | Thu May 20 00:00:00 1999 +(12 rows) + + +delete from pt1 where d_date<'1999-05-01'; +select * from pt1 where d_date<'1999-06-01'; + d_date +-------------------------- + Sat May 01 00:00:00 1999 + Sun May 02 00:00:00 1999 + Wed May 19 00:00:00 1999 + Thu May 20 00:00:00 1999 +(4 rows) + + +select * from pt2 where d_date_backup<'1999-06-01'; + d_date | d_date_backup +--------------------------+-------------------------- + | Mon Mar 01 00:00:00 1999 + | Tue Mar 02 00:00:00 1999 + | Fri Mar 19 00:00:00 1999 + | Sat Mar 20 00:00:00 1999 + | Thu Apr 01 00:00:00 1999 + | Fri Apr 02 00:00:00 1999 + | Mon Apr 19 00:00:00 1999 + | Tue Apr 20 00:00:00 1999 + Sat May 01 00:00:00 1999 | Sat May 01 00:00:00 1999 + Sun May 02 00:00:00 1999 | Sun May 02 00:00:00 1999 + Wed May 19 00:00:00 1999 | Wed May 19 00:00:00 1999 + Thu May 20 00:00:00 1999 | Thu May 20 00:00:00 1999 +(12 rows) + + +delete from pt2 where pt2.d_date>'1999-11-15' and pt2.d_date<'1999-12-15'; +SELECT * FROM pt2 WHERE pt2.d_date>='1999-10-01'; + d_date | d_date_backup +--------------------------+-------------------------- + Fri Oct 01 00:00:00 1999 | Fri Oct 01 00:00:00 1999 + Sat Oct 02 00:00:00 1999 | Sat Oct 02 00:00:00 1999 + Tue Oct 19 00:00:00 1999 | Tue Oct 19 00:00:00 1999 + Wed Oct 20 00:00:00 1999 | Wed Oct 20 00:00:00 1999 + Mon Nov 01 00:00:00 1999 | Mon Nov 01 00:00:00 1999 + Tue Nov 02 00:00:00 1999 | Tue Nov 02 00:00:00 1999 + Sun Dec 19 00:00:00 1999 | Sun Dec 19 00:00:00 1999 + Mon Dec 20 00:00:00 1999 | Mon Dec 20 00:00:00 1999 + Tue Feb 01 00:00:00 2000 | Tue Feb 01 00:00:00 2000 +(9 rows) + + +delete from pt1 where pt1.d_date>'1999-10-15' and pt1.d_date<'1999-11-15'; +SELECT * FROM pt1 WHERE pt1.d_date>='1999-10-01'; + d_date +-------------------------- + Fri Oct 01 00:00:00 1999 + Sat Oct 02 00:00:00 1999 + Fri Nov 19 00:00:00 1999 + Sat Nov 20 00:00:00 1999 + Wed Dec 01 00:00:00 1999 + Thu Dec 02 00:00:00 1999 + Sun Dec 19 00:00:00 1999 + Mon Dec 20 00:00:00 1999 + Sat Jan 01 00:00:00 2000 + Sun Jan 02 00:00:00 2000 + Wed Jan 19 00:00:00 2000 + Thu Jan 20 00:00:00 2000 + Tue Feb 01 00:00:00 2000 + Wed Feb 02 00:00:00 2000 + Sat Feb 19 00:00:00 2000 +(15 rows) + + +SELECT * FROM pt2 WHERE pt2.d_date_backup>='1999-10-01'; + d_date | d_date_backup +--------------------------+-------------------------- + Fri Oct 01 00:00:00 1999 | Fri Oct 01 00:00:00 1999 + Sat Oct 02 00:00:00 1999 | Sat Oct 02 00:00:00 1999 + | Tue Oct 19 00:00:00 1999 + | Wed Oct 20 00:00:00 1999 + | Mon Nov 01 00:00:00 1999 + | Tue Nov 02 00:00:00 1999 + Sun Dec 19 00:00:00 1999 | Sun Dec 19 00:00:00 1999 + Mon Dec 20 00:00:00 1999 | Mon Dec 20 00:00:00 1999 + Tue Feb 01 00:00:00 2000 | Tue Feb 01 00:00:00 2000 +(9 rows) + + +UPDATE pt2 set d_date = d_date - INTERVAL '4' month where d_date>='1999-05-01' and d_date<'1999-06-01'; +ERROR: insert or update on table "pt2" violates foreign key constraint "test_foreign" +DETAIL: Key (d_date)=(Fri Jan 01 00:00:00 1999) is not present in table "pt1". + +UPDATE pt1 set d_date = d_date - INTERVAL '4' month where d_date>='1999-05-01' and d_date<'1999-06-01'; +SELECT * FROM pt1 WHERE d_date<'1999-07-01'; + d_date +-------------------------- + Fri Jan 01 00:00:00 1999 + Sat Jan 02 00:00:00 1999 + Tue Jan 19 00:00:00 1999 + Wed Jan 20 00:00:00 1999 + Tue Jun 01 00:00:00 1999 + Wed Jun 02 00:00:00 1999 + Sat Jun 19 00:00:00 1999 + Sun Jun 20 00:00:00 1999 +(8 rows) + + +SELECT * FROM pt2 WHERE d_date_backup<'1999-07-01'; + d_date | d_date_backup +--------------------------+-------------------------- + | Mon Mar 01 00:00:00 1999 + | Tue Mar 02 00:00:00 1999 + | Fri Mar 19 00:00:00 1999 + | Sat Mar 20 00:00:00 1999 + | Thu Apr 01 00:00:00 1999 + | Fri Apr 02 00:00:00 1999 + | Mon Apr 19 00:00:00 1999 + | Tue Apr 20 00:00:00 1999 + | Sat May 01 00:00:00 1999 + | Sun May 02 00:00:00 1999 + | Wed May 19 00:00:00 1999 + | Thu May 20 00:00:00 1999 + Tue Jun 01 00:00:00 1999 | Tue Jun 01 00:00:00 1999 + Wed Jun 02 00:00:00 1999 | Wed Jun 02 00:00:00 1999 + Sat Jun 19 00:00:00 1999 | Sat Jun 19 00:00:00 1999 + Sun Jun 20 00:00:00 1999 | Sun Jun 20 00:00:00 1999 +(16 rows) + + +UPDATE pt2 set d_date = d_date - INTERVAL '1' month where d_date>='1999-06-01' and d_date<='1999-07-15'; +ERROR: insert or update on table "pt2" violates foreign key constraint "test_foreign" +DETAIL: Key (d_date)=(Sat May 01 00:00:00 1999) is not present in table "pt1". + +UPDATE pt1 set d_date = d_date - INTERVAL '1' month where d_date>='1999-06-01' and d_date<='1999-07-15'; +SELECT * FROM pt1 WHERE d_date<'1999-08-01'; + d_date +-------------------------- + Fri Jan 01 00:00:00 1999 + Sat Jan 02 00:00:00 1999 + Tue Jan 19 00:00:00 1999 + Wed Jan 20 00:00:00 1999 + Sat May 01 00:00:00 1999 + Sun May 02 00:00:00 1999 + Wed May 19 00:00:00 1999 + Thu May 20 00:00:00 1999 + Tue Jun 01 00:00:00 1999 + Wed Jun 02 00:00:00 1999 + Mon Jul 19 00:00:00 1999 + Tue Jul 20 00:00:00 1999 +(12 rows) + + +SELECT * FROM pt2 WHERE d_date_backup<'1999-08-01'; + d_date | d_date_backup +--------------------------+-------------------------- + | Mon Mar 01 00:00:00 1999 + | Tue Mar 02 00:00:00 1999 + | Fri Mar 19 00:00:00 1999 + | Sat Mar 20 00:00:00 1999 + | Thu Apr 01 00:00:00 1999 + | Fri Apr 02 00:00:00 1999 + | Mon Apr 19 00:00:00 1999 + | Tue Apr 20 00:00:00 1999 + | Sat May 01 00:00:00 1999 + | Sun May 02 00:00:00 1999 + | Wed May 19 00:00:00 1999 + | Thu May 20 00:00:00 1999 + | Tue Jun 01 00:00:00 1999 + | Wed Jun 02 00:00:00 1999 + | Sat Jun 19 00:00:00 1999 + | Sun Jun 20 00:00:00 1999 + Mon Jul 19 00:00:00 1999 | Mon Jul 19 00:00:00 1999 + Tue Jul 20 00:00:00 1999 | Tue Jul 20 00:00:00 1999 + | Thu Jul 01 00:00:00 1999 + | Fri Jul 02 00:00:00 1999 +(20 rows) + + +DROP TABLE IF EXISTS pt2; +DROP TABLE IF EXISTS pt1; + +CREATE TABLE pt1( +d_date date UNIQUE +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); +NOTICE: CREATE TABLE / UNIQUE will create implicit index "pt1_d_date_key" for table "pt1" + +CREATE TABLE pt2( +d_date date, +d_date_backup date +)partition BY RANGE (d_date_backup) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + + +ALTER TABLE pt2 ADD CONSTRAINT test_foreign FOREIGN KEY(d_date) +REFERENCES pt1(d_date) on UPDATE RESTRICT ON DELETE RESTRICT; + +INSERT INTO pt1 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + +INSERT INTO pt2 VALUES +('1999-01-01','1999-01-01'),('1999-01-02','1999-01-02'), +('1999-01-19','1999-01-19'),('1999-01-20','1999-01-20'), +('1999-02-01','1999-02-01'),('1999-02-02','1999-02-02'), +('1999-02-19','1999-02-19'),('1999-02-20','1999-02-20'), +('1999-03-01','1999-03-01'),('1999-03-02','1999-03-02'), +('1999-03-19','1999-03-19'),('1999-03-20','1999-03-20'), +('1999-04-01','1999-04-01'),('1999-04-02','1999-04-02'), +('1999-04-19','1999-04-19'),('1999-04-20','1999-04-20'), +('1999-05-01','1999-05-01'),('1999-05-02','1999-05-02'), +('1999-05-19','1999-05-19'),('1999-05-20','1999-05-20'), +('1999-06-01','1999-06-01'),('1999-06-02','1999-06-02'), +('1999-06-19','1999-06-19'),('1999-06-20','1999-06-20'), +('1999-07-01','1999-07-01'),('1999-07-02','1999-07-02'), +('1999-07-19','1999-07-19'),('1999-07-20','1999-07-20'), +('1999-08-01','1999-08-01'),('1999-08-02','1999-08-02'), +('1999-08-19','1999-08-19'),('1999-08-20','1999-08-20'), +('1999-09-01','1999-09-01'),('1999-09-02','1999-09-02'), +('1999-09-19','1999-09-19'),('1999-09-20','1999-09-20'), +('1999-10-01','1999-10-01'),('1999-10-02','1999-10-02'), +('1999-10-19','1999-10-19'),('1999-10-20','1999-10-20'), +('1999-11-01','1999-11-01'),('1999-11-02','1999-11-02'), +('1999-11-19','1999-11-19'),('1999-11-20','1999-11-20'), +('1999-12-01','1999-12-01'),('1999-12-02','1999-12-02'), +('1999-12-19','1999-12-19'),('1999-12-20','1999-12-20'); + +delete from pt2 where pt2.d_date<'1999-03-01'; +select * from pt2 where pt2.d_date<'1999-06-01'; + d_date | d_date_backup +--------------------------+-------------------------- + Mon Mar 01 00:00:00 1999 | Mon Mar 01 00:00:00 1999 + Tue Mar 02 00:00:00 1999 | Tue Mar 02 00:00:00 1999 + Fri Mar 19 00:00:00 1999 | Fri Mar 19 00:00:00 1999 + Sat Mar 20 00:00:00 1999 | Sat Mar 20 00:00:00 1999 + Thu Apr 01 00:00:00 1999 | Thu Apr 01 00:00:00 1999 + Fri Apr 02 00:00:00 1999 | Fri Apr 02 00:00:00 1999 + Mon Apr 19 00:00:00 1999 | Mon Apr 19 00:00:00 1999 + Tue Apr 20 00:00:00 1999 | Tue Apr 20 00:00:00 1999 + Sat May 01 00:00:00 1999 | Sat May 01 00:00:00 1999 + Sun May 02 00:00:00 1999 | Sun May 02 00:00:00 1999 + Wed May 19 00:00:00 1999 | Wed May 19 00:00:00 1999 + Thu May 20 00:00:00 1999 | Thu May 20 00:00:00 1999 +(12 rows) + + +delete from pt1 where d_date<'1999-05-01'; +ERROR: update or delete on table "pt1" violates foreign key constraint "test_foreign" on table "pt2" +DETAIL: Key (d_date)=(Mon Mar 01 00:00:00 1999) is still referenced from table "pt2". + +delete from pt2 where pt2.d_date>'1999-11-15' and pt2.d_date<'1999-12-15'; +SELECT * FROM pt2 WHERE pt2.d_date>='1999-10-01'; + d_date | d_date_backup +--------------------------+-------------------------- + Fri Oct 01 00:00:00 1999 | Fri Oct 01 00:00:00 1999 + Sat Oct 02 00:00:00 1999 | Sat Oct 02 00:00:00 1999 + Tue Oct 19 00:00:00 1999 | Tue Oct 19 00:00:00 1999 + Wed Oct 20 00:00:00 1999 | Wed Oct 20 00:00:00 1999 + Mon Nov 01 00:00:00 1999 | Mon Nov 01 00:00:00 1999 + Tue Nov 02 00:00:00 1999 | Tue Nov 02 00:00:00 1999 + Sun Dec 19 00:00:00 1999 | Sun Dec 19 00:00:00 1999 + Mon Dec 20 00:00:00 1999 | Mon Dec 20 00:00:00 1999 +(8 rows) + + +delete from pt1 where pt1.d_date>'1999-10-15' and pt1.d_date<'1999-11-15'; +ERROR: update or delete on table "pt1" violates foreign key constraint "test_foreign" on table "pt2" +DETAIL: Key (d_date)=(Tue Oct 19 00:00:00 1999) is still referenced from table "pt2". + +delete from pt2 where pt2.d_date>='1999-01-01' and pt2.d_date<'1999-05-01'; +delete from pt1 where pt1.d_date>='1999-01-01' and pt1.d_date<'1999-05-01'; + +UPDATE pt2 set d_date = d_date - INTERVAL '4' month where d_date>='1999-05-01' and d_date<'1999-06-01'; +ERROR: insert or update on table "pt2" violates foreign key constraint "test_foreign" +DETAIL: Key (d_date)=(Fri Jan 01 00:00:00 1999) is not present in table "pt1". + +UPDATE pt1 set d_date = d_date - INTERVAL '4' month where d_date>='1999-05-01' and d_date<'1999-06-01'; +ERROR: update or delete on table "pt1" violates foreign key constraint "test_foreign" on table "pt2" +DETAIL: Key (d_date)=(Sat May 01 00:00:00 1999) is still referenced from table "pt2". +SELECT * FROM pt1 WHERE d_date<'1999-07-01'; + d_date +-------------------------- + Sat May 01 00:00:00 1999 + Sun May 02 00:00:00 1999 + Wed May 19 00:00:00 1999 + Thu May 20 00:00:00 1999 + Tue Jun 01 00:00:00 1999 + Wed Jun 02 00:00:00 1999 + Sat Jun 19 00:00:00 1999 + Sun Jun 20 00:00:00 1999 +(8 rows) + + +UPDATE pt2 set d_date = d_date - INTERVAL '1' month where d_date>='1999-06-01' and d_date<='1999-07-15'; + +UPDATE pt1 set d_date = d_date - INTERVAL '5' month where d_date>='1999-06-01' and d_date<='1999-07-15'; +ERROR: update or delete on table "pt1" violates foreign key constraint "test_foreign" on table "pt2" +DETAIL: Key (d_date)=(Tue Jun 01 00:00:00 1999) is still referenced from table "pt2". + +delete from pt2 where d_date>='1999-06-01' and d_date<='1999-07-15'; +UPDATE pt1 set d_date = d_date - INTERVAL '5' month where d_date>='1999-06-01' and d_date<='1999-07-15'; + diff --git a/src/test/regress/expected/partition_trigger.out b/src/test/regress/expected/partition_trigger.out new file mode 100644 index 000000000..9de8c0a1a --- /dev/null +++ b/src/test/regress/expected/partition_trigger.out @@ -0,0 +1,295 @@ +DROP TABLE IF EXISTS pt2; +DROP TABLE IF EXISTS pt1; +DROP FUNCTION IF EXISTS test_function(); +NOTICE: function test_function() does not exist, skipping + +CREATE TABLE pt1( + d_date date UNIQUE +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); +NOTICE: CREATE TABLE / UNIQUE will create implicit index "pt1_d_date_key" for table "pt1" + +CREATE TABLE pt2( + d_date date +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + + +INSERT INTO pt1 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + +INSERT INTO pt2 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + + +CREATE FUNCTION test_function() +RETURNS TRIGGER AS $$ +BEGIN + IF(TG_OP = 'DELETE') THEN + DELETE FROM pt2 WHERE d_date=OLD.d_date; + RETURN OLD; + ELSEIF(TG_OP = 'UPDATE') THEN + UPDATE pt2 set d_date=NEW.d_date where d_date=OLD.d_date; + RETURN NEW; + ELSEIF(TG_OP = 'INSERT') THEN + INSERT INTO pt2 VALUES(NEW.d_date); + RETURN NEW; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER test_trigger +AFTER INSERT OR UPDATE OR DELETE on pt1 +FOR EACH ROW EXECUTE PROCEDURE test_function(); + +INSERT INTO pt1 VALUES('2016-01-01'); +UPDATE pt2 set d_date = d_date + INTERVAL '4' month where d_date>='1999-05-01'; +DELETE FROM pt1 WHERE d_date<'1999-05-01'; +SELECT * FROM pt1; + d_date +-------------------------- + Sat May 01 00:00:00 1999 + Sun May 02 00:00:00 1999 + Wed May 19 00:00:00 1999 + Thu May 20 00:00:00 1999 + Tue Jun 01 00:00:00 1999 + Wed Jun 02 00:00:00 1999 + Sat Jun 19 00:00:00 1999 + Sun Jun 20 00:00:00 1999 + Thu Jul 01 00:00:00 1999 + Fri Jul 02 00:00:00 1999 + Mon Jul 19 00:00:00 1999 + Tue Jul 20 00:00:00 1999 + Sun Aug 01 00:00:00 1999 + Mon Aug 02 00:00:00 1999 + Thu Aug 19 00:00:00 1999 + Fri Aug 20 00:00:00 1999 + Wed Sep 01 00:00:00 1999 + Thu Sep 02 00:00:00 1999 + Sun Sep 19 00:00:00 1999 + Mon Sep 20 00:00:00 1999 + Fri Oct 01 00:00:00 1999 + Sat Oct 02 00:00:00 1999 + Tue Oct 19 00:00:00 1999 + Wed Oct 20 00:00:00 1999 + Mon Nov 01 00:00:00 1999 + Tue Nov 02 00:00:00 1999 + Fri Nov 19 00:00:00 1999 + Sat Nov 20 00:00:00 1999 + Wed Dec 01 00:00:00 1999 + Thu Dec 02 00:00:00 1999 + Sun Dec 19 00:00:00 1999 + Mon Dec 20 00:00:00 1999 + Fri Jan 01 00:00:00 2016 +(33 rows) + + +SELECT * FROM pt2; + d_date +-------------------------- + Wed Sep 01 00:00:00 1999 + Thu Sep 02 00:00:00 1999 + Sun Sep 19 00:00:00 1999 + Mon Sep 20 00:00:00 1999 + Fri Oct 01 00:00:00 1999 + Sat Oct 02 00:00:00 1999 + Tue Oct 19 00:00:00 1999 + Wed Oct 20 00:00:00 1999 + Mon Nov 01 00:00:00 1999 + Tue Nov 02 00:00:00 1999 + Fri Nov 19 00:00:00 1999 + Sat Nov 20 00:00:00 1999 + Wed Dec 01 00:00:00 1999 + Thu Dec 02 00:00:00 1999 + Sun Dec 19 00:00:00 1999 + Mon Dec 20 00:00:00 1999 + Sat Jan 01 00:00:00 2000 + Sun Jan 02 00:00:00 2000 + Wed Jan 19 00:00:00 2000 + Thu Jan 20 00:00:00 2000 + Tue Feb 01 00:00:00 2000 + Wed Feb 02 00:00:00 2000 + Sat Feb 19 00:00:00 2000 + Sun Feb 20 00:00:00 2000 + Wed Mar 01 00:00:00 2000 + Thu Mar 02 00:00:00 2000 + Sun Mar 19 00:00:00 2000 + Mon Mar 20 00:00:00 2000 + Sat Apr 01 00:00:00 2000 + Sun Apr 02 00:00:00 2000 + Wed Apr 19 00:00:00 2000 + Thu Apr 20 00:00:00 2000 + Sun May 01 00:00:00 2016 +(33 rows) + + +DROP TABLE IF EXISTS pt2; +DROP TABLE IF EXISTS pt1; +DROP FUNCTION IF EXISTS test_function(); + +CREATE TABLE pt1( + d_date date UNIQUE +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); +NOTICE: CREATE TABLE / UNIQUE will create implicit index "pt1_d_date_key" for table "pt1" + +CREATE TABLE pt2( + d_date date +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + + +INSERT INTO pt1 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + +INSERT INTO pt2 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + + +CREATE FUNCTION test_function() +RETURNS TRIGGER AS $$ +BEGIN + IF(TG_OP = 'DELETE') THEN + DELETE FROM pt2 WHERE d_date=OLD.d_date; + RETURN OLD; + ELSEIF(TG_OP = 'UPDATE') THEN + UPDATE pt2 set d_date=NEW.d_date where d_date=OLD.d_date; + RETURN NEW; + ELSEIF(TG_OP = 'INSERT') THEN + INSERT INTO pt2 VALUES(NEW.d_date); + RETURN NEW; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER test_trigger +BEFORE INSERT OR UPDATE OR DELETE on pt1 +FOR EACH ROW EXECUTE PROCEDURE test_function(); + +INSERT INTO pt1 VALUES('2016-01-01'); +UPDATE pt2 set d_date = d_date + INTERVAL '4' month where d_date>='1999-05-01'; +DELETE FROM pt1 WHERE d_date<'1999-05-01'; +SELECT * FROM pt1; + d_date +-------------------------- + Sat May 01 00:00:00 1999 + Sun May 02 00:00:00 1999 + Wed May 19 00:00:00 1999 + Thu May 20 00:00:00 1999 + Tue Jun 01 00:00:00 1999 + Wed Jun 02 00:00:00 1999 + Sat Jun 19 00:00:00 1999 + Sun Jun 20 00:00:00 1999 + Thu Jul 01 00:00:00 1999 + Fri Jul 02 00:00:00 1999 + Mon Jul 19 00:00:00 1999 + Tue Jul 20 00:00:00 1999 + Sun Aug 01 00:00:00 1999 + Mon Aug 02 00:00:00 1999 + Thu Aug 19 00:00:00 1999 + Fri Aug 20 00:00:00 1999 + Wed Sep 01 00:00:00 1999 + Thu Sep 02 00:00:00 1999 + Sun Sep 19 00:00:00 1999 + Mon Sep 20 00:00:00 1999 + Fri Oct 01 00:00:00 1999 + Sat Oct 02 00:00:00 1999 + Tue Oct 19 00:00:00 1999 + Wed Oct 20 00:00:00 1999 + Mon Nov 01 00:00:00 1999 + Tue Nov 02 00:00:00 1999 + Fri Nov 19 00:00:00 1999 + Sat Nov 20 00:00:00 1999 + Wed Dec 01 00:00:00 1999 + Thu Dec 02 00:00:00 1999 + Sun Dec 19 00:00:00 1999 + Mon Dec 20 00:00:00 1999 + Fri Jan 01 00:00:00 2016 +(33 rows) + +SELECT * FROM pt2; + d_date +-------------------------- + Wed Sep 01 00:00:00 1999 + Thu Sep 02 00:00:00 1999 + Sun Sep 19 00:00:00 1999 + Mon Sep 20 00:00:00 1999 + Fri Oct 01 00:00:00 1999 + Sat Oct 02 00:00:00 1999 + Tue Oct 19 00:00:00 1999 + Wed Oct 20 00:00:00 1999 + Mon Nov 01 00:00:00 1999 + Tue Nov 02 00:00:00 1999 + Fri Nov 19 00:00:00 1999 + Sat Nov 20 00:00:00 1999 + Wed Dec 01 00:00:00 1999 + Thu Dec 02 00:00:00 1999 + Sun Dec 19 00:00:00 1999 + Mon Dec 20 00:00:00 1999 + Sat Jan 01 00:00:00 2000 + Sun Jan 02 00:00:00 2000 + Wed Jan 19 00:00:00 2000 + Thu Jan 20 00:00:00 2000 + Tue Feb 01 00:00:00 2000 + Wed Feb 02 00:00:00 2000 + Sat Feb 19 00:00:00 2000 + Sun Feb 20 00:00:00 2000 + Wed Mar 01 00:00:00 2000 + Thu Mar 02 00:00:00 2000 + Sun Mar 19 00:00:00 2000 + Mon Mar 20 00:00:00 2000 + Sat Apr 01 00:00:00 2000 + Sun Apr 02 00:00:00 2000 + Wed Apr 19 00:00:00 2000 + Thu Apr 20 00:00:00 2000 + Sun May 01 00:00:00 2016 +(33 rows) + diff --git a/src/test/regress/parallel_schedule10 b/src/test/regress/parallel_schedule10 index 10100e4d0..18186d1ef 100644 --- a/src/test/regress/parallel_schedule10 +++ b/src/test/regress/parallel_schedule10 @@ -74,3 +74,5 @@ test: with # run alter object to test pg_object #test: pg_object_test +test: partition_foreign_key +test: partition_trigger \ No newline at end of file diff --git a/src/test/regress/sql/partition_foreign_key.sql b/src/test/regress/sql/partition_foreign_key.sql new file mode 100644 index 000000000..3158d286f --- /dev/null +++ b/src/test/regress/sql/partition_foreign_key.sql @@ -0,0 +1,312 @@ +DROP TABLE IF EXISTS pt2; +DROP TABLE IF EXISTS pt1; +CREATE TABLE pt1( + d_date date UNIQUE +)partition BY RANGE (d_date) INTERVAL('1 day') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + +CREATE TABLE pt2( + d_date date +)partition BY RANGE (d_date) INTERVAL('1 day') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + +ALTER TABLE pt2 ADD CONSTRAINT test_foreign FOREIGN KEY(d_date) +REFERENCES pt1(d_date) on UPDATE CASCADE ON DELETE CASCADE; + +INSERT into pt1 VALUES('1999-01-01'),('1999-01-02'),('1999-01-03'),('1999-01-04'); + +INSERT into pt2 VALUES('1999-01-01'); + +INSERT into pt2 VALUES('2000-01-01'); + +update pt2 set pt2.d_date = '2000-01-01' where pt2.d_date = '1999-01-01'; + +update pt2 set pt2.d_date = '1999-01-02' where pt2.d_date = '1999-01-01'; + +SELECT * FROM pt2; + +SELECT * FROM pt2 PARTITION(sys_p2); + +delete from pt2; +INSERT into pt2 VALUES('1999-01-01'), ('1999-01-03'),('1999-01-04'); + +INSERT into pt1 VALUES('1999-01-08'); + +INSERT into pt2 VALUES('1999-01-08'); + +update pt1 set pt1.d_date = '2000-01-01' where pt1.d_date = '1999-01-08'; + +delete from pt1 where pt1.d_date = '2000-01-01'; + +DROP TABLE pt2; +DROP TABLE pt1; + +CREATE TABLE pt1( + d_date date UNIQUE +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + +CREATE TABLE pt2( + d_date date +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + +ALTER TABLE pt2 ADD CONSTRAINT test_foreign FOREIGN KEY(d_date) +REFERENCES pt1(d_date) on UPDATE CASCADE ON DELETE CASCADE; + +INSERT INTO pt1 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + +INSERT INTO pt2 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + +delete from pt2 where pt2.d_date<'1999-03-01'; + +select * from pt2 where pt2.d_date<'1999-06-01'; + +delete from pt1 where pt1.d_date<'1999-05-01'; + +delete from pt2 where pt2.d_date>'1999-11-15' and pt2.d_date<'1999-12-15'; +SELECT * FROM pt2 WHERE pt2.d_date>='1999-10-01'; + +delete from pt1 where pt1.d_date>'1999-10-15' and pt1.d_date<'1999-11-15'; +SELECT * FROM pt1 WHERE pt1.d_date>='1999-10-01'; + +SELECT * FROM pt2 WHERE pt2.d_date>='1999-10-01'; + +UPDATE pt2 set d_date = d_date - INTERVAL '4' month where d_date>='1999-05-01' and d_date<'1999-06-01'; + +UPDATE pt1 set d_date = d_date - INTERVAL '4' month where d_date>='1999-05-01' and d_date<'1999-06-01'; +SELECT * FROM pt1 WHERE d_date<'1999-07-01'; + +SELECT * FROM pt1 WHERE d_date<'1999-07-01'; + +UPDATE pt2 set d_date = d_date - INTERVAL '1' month where d_date>='1999-06-01' and d_date<='1999-07-15'; + +UPDATE pt1 set d_date = d_date - INTERVAL '1' month where d_date>='1999-06-01' and d_date<='1999-07-15'; +SELECT * FROM pt1 WHERE d_date<'1999-08-01'; + +SELECT * FROM pt2 WHERE d_date<'1999-08-01'; + +DROP TABLE IF EXISTS pt2; +DROP TABLE IF EXISTS pt1; + +CREATE TABLE pt1( +d_date date UNIQUE +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')) +; + +CREATE TABLE pt2( +d_date date, +d_date_backup date +)partition BY RANGE (d_date_backup) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + + +ALTER TABLE pt2 ADD CONSTRAINT test_foreign FOREIGN KEY(d_date) +REFERENCES pt1(d_date) on UPDATE SET NULL ON DELETE SET NULL; + +INSERT INTO pt1 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + +INSERT INTO pt2 VALUES +('1999-01-01','1999-01-01'),('1999-01-02','1999-01-02'), +('1999-01-19','1999-01-19'),('1999-01-20','1999-01-20'), +('1999-02-01','1999-02-01'),('1999-02-02','1999-02-02'), +('1999-02-19','1999-02-19'),('1999-02-20','1999-02-20'), +('1999-03-01','1999-03-01'),('1999-03-02','1999-03-02'), +('1999-03-19','1999-03-19'),('1999-03-20','1999-03-20'), +('1999-04-01','1999-04-01'),('1999-04-02','1999-04-02'), +('1999-04-19','1999-04-19'),('1999-04-20','1999-04-20'), +('1999-05-01','1999-05-01'),('1999-05-02','1999-05-02'), +('1999-05-19','1999-05-19'),('1999-05-20','1999-05-20'), +('1999-06-01','1999-06-01'),('1999-06-02','1999-06-02'), +('1999-06-19','1999-06-19'),('1999-06-20','1999-06-20'), +('1999-07-01','1999-07-01'),('1999-07-02','1999-07-02'), +('1999-07-19','1999-07-19'),('1999-07-20','1999-07-20'), +('1999-08-01','1999-08-01'),('1999-08-02','1999-08-02'), +('1999-08-19','1999-08-19'),('1999-08-20','1999-08-20'), +('1999-09-01','1999-09-01'),('1999-09-02','1999-09-02'), +('1999-09-19','1999-09-19'),('1999-09-20','1999-09-20'), +('1999-10-01','1999-10-01'),('1999-10-02','1999-10-02'), +('1999-10-19','1999-10-19'),('1999-10-20','1999-10-20'), +('1999-11-01','1999-11-01'),('1999-11-02','1999-11-02'), +('1999-11-19','1999-11-19'),('1999-11-20','1999-11-20'), +('1999-12-01','1999-12-01'),('1999-12-02','1999-12-02'), +('1999-12-19','1999-12-19'),('1999-12-20','1999-12-20'); + +insert into pt1 VALUES('2000-01-01'),('2000-01-02'), +('2000-01-19'),('2000-01-20'), +('2000-02-01'),('2000-02-02'), +('2000-02-19'),('2000-02-20'); +SELECT * FROM pt1 WHERE d_date>='2000-01-01'; + +update pt1 set d_date = '2000-02-21' WHERE d_date = '2000-02-20'; +SELECT * FROM pt1 WHERE d_date>='2000-02-01'; + +DELETE FROM pt1 WHERE d_date = '2000-02-21'; +SELECT * FROM pt1 WHERE d_date>='2000-02-01'; + +SELECT * FROM pt1 PARTITION FOR('1999-06-16'); + +INSERT into pt2 VALUES('2020-08-19','2020-08-19'); + +INSERT into pt2 VALUES('2000-01-01','2000-01-01'); + +update pt2 set pt2.d_date = '2020-08-13' +where pt2.d_date = '1999-01-01'; + +update pt2 set pt2.d_date = '2000-02-01',pt2.d_date_backup = '2000-02-01' +where pt2.d_date = '2000-01-01'; + +SELECT * FROM pt2; + +SELECT * FROM pt2 PARTITION(sys_p2); + +delete from pt2 where pt2.d_date<'1999-03-01'; +select * from pt2 where pt2.d_date<'1999-06-01'; + +delete from pt1 where d_date<'1999-05-01'; +select * from pt1 where d_date<'1999-06-01'; + +select * from pt2 where d_date_backup<'1999-06-01'; + +delete from pt2 where pt2.d_date>'1999-11-15' and pt2.d_date<'1999-12-15'; +SELECT * FROM pt2 WHERE pt2.d_date>='1999-10-01'; + +delete from pt1 where pt1.d_date>'1999-10-15' and pt1.d_date<'1999-11-15'; +SELECT * FROM pt1 WHERE pt1.d_date>='1999-10-01'; + +SELECT * FROM pt2 WHERE pt2.d_date_backup>='1999-10-01'; + +UPDATE pt2 set d_date = d_date - INTERVAL '4' month where d_date>='1999-05-01' and d_date<'1999-06-01'; + +UPDATE pt1 set d_date = d_date - INTERVAL '4' month where d_date>='1999-05-01' and d_date<'1999-06-01'; +SELECT * FROM pt1 WHERE d_date<'1999-07-01'; + +SELECT * FROM pt2 WHERE d_date_backup<'1999-07-01'; + +UPDATE pt2 set d_date = d_date - INTERVAL '1' month where d_date>='1999-06-01' and d_date<='1999-07-15'; + +UPDATE pt1 set d_date = d_date - INTERVAL '1' month where d_date>='1999-06-01' and d_date<='1999-07-15'; +SELECT * FROM pt1 WHERE d_date<'1999-08-01'; + +SELECT * FROM pt2 WHERE d_date_backup<'1999-08-01'; + +DROP TABLE IF EXISTS pt2; +DROP TABLE IF EXISTS pt1; + +CREATE TABLE pt1( +d_date date UNIQUE +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + +CREATE TABLE pt2( +d_date date, +d_date_backup date +)partition BY RANGE (d_date_backup) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + + +ALTER TABLE pt2 ADD CONSTRAINT test_foreign FOREIGN KEY(d_date) +REFERENCES pt1(d_date) on UPDATE RESTRICT ON DELETE RESTRICT; + +INSERT INTO pt1 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + +INSERT INTO pt2 VALUES +('1999-01-01','1999-01-01'),('1999-01-02','1999-01-02'), +('1999-01-19','1999-01-19'),('1999-01-20','1999-01-20'), +('1999-02-01','1999-02-01'),('1999-02-02','1999-02-02'), +('1999-02-19','1999-02-19'),('1999-02-20','1999-02-20'), +('1999-03-01','1999-03-01'),('1999-03-02','1999-03-02'), +('1999-03-19','1999-03-19'),('1999-03-20','1999-03-20'), +('1999-04-01','1999-04-01'),('1999-04-02','1999-04-02'), +('1999-04-19','1999-04-19'),('1999-04-20','1999-04-20'), +('1999-05-01','1999-05-01'),('1999-05-02','1999-05-02'), +('1999-05-19','1999-05-19'),('1999-05-20','1999-05-20'), +('1999-06-01','1999-06-01'),('1999-06-02','1999-06-02'), +('1999-06-19','1999-06-19'),('1999-06-20','1999-06-20'), +('1999-07-01','1999-07-01'),('1999-07-02','1999-07-02'), +('1999-07-19','1999-07-19'),('1999-07-20','1999-07-20'), +('1999-08-01','1999-08-01'),('1999-08-02','1999-08-02'), +('1999-08-19','1999-08-19'),('1999-08-20','1999-08-20'), +('1999-09-01','1999-09-01'),('1999-09-02','1999-09-02'), +('1999-09-19','1999-09-19'),('1999-09-20','1999-09-20'), +('1999-10-01','1999-10-01'),('1999-10-02','1999-10-02'), +('1999-10-19','1999-10-19'),('1999-10-20','1999-10-20'), +('1999-11-01','1999-11-01'),('1999-11-02','1999-11-02'), +('1999-11-19','1999-11-19'),('1999-11-20','1999-11-20'), +('1999-12-01','1999-12-01'),('1999-12-02','1999-12-02'), +('1999-12-19','1999-12-19'),('1999-12-20','1999-12-20'); + +delete from pt2 where pt2.d_date<'1999-03-01'; +select * from pt2 where pt2.d_date<'1999-06-01'; + +delete from pt1 where d_date<'1999-05-01'; + +delete from pt2 where pt2.d_date>'1999-11-15' and pt2.d_date<'1999-12-15'; +SELECT * FROM pt2 WHERE pt2.d_date>='1999-10-01'; + +delete from pt1 where pt1.d_date>'1999-10-15' and pt1.d_date<'1999-11-15'; + +delete from pt2 where pt2.d_date>='1999-01-01' and pt2.d_date<'1999-05-01'; +delete from pt1 where pt1.d_date>='1999-01-01' and pt1.d_date<'1999-05-01'; + +UPDATE pt2 set d_date = d_date - INTERVAL '4' month where d_date>='1999-05-01' and d_date<'1999-06-01'; + +UPDATE pt1 set d_date = d_date - INTERVAL '4' month where d_date>='1999-05-01' and d_date<'1999-06-01'; +SELECT * FROM pt1 WHERE d_date<'1999-07-01'; + +UPDATE pt2 set d_date = d_date - INTERVAL '1' month where d_date>='1999-06-01' and d_date<='1999-07-15'; + +UPDATE pt1 set d_date = d_date - INTERVAL '5' month where d_date>='1999-06-01' and d_date<='1999-07-15'; + +delete from pt2 where d_date>='1999-06-01' and d_date<='1999-07-15'; +UPDATE pt1 set d_date = d_date - INTERVAL '5' month where d_date>='1999-06-01' and d_date<='1999-07-15'; + diff --git a/src/test/regress/sql/partition_trigger.sql b/src/test/regress/sql/partition_trigger.sql new file mode 100644 index 000000000..38c50e17e --- /dev/null +++ b/src/test/regress/sql/partition_trigger.sql @@ -0,0 +1,143 @@ +DROP TABLE IF EXISTS pt2; +DROP TABLE IF EXISTS pt1; +DROP FUNCTION IF EXISTS test_function(); + +CREATE TABLE pt1( + d_date date UNIQUE +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + +CREATE TABLE pt2( + d_date date +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + + +INSERT INTO pt1 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + +INSERT INTO pt2 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + + +CREATE FUNCTION test_function() +RETURNS TRIGGER AS $$ +BEGIN + IF(TG_OP = 'DELETE') THEN + DELETE FROM pt2 WHERE d_date=OLD.d_date; + RETURN OLD; + ELSEIF(TG_OP = 'UPDATE') THEN + UPDATE pt2 set d_date=NEW.d_date where d_date=OLD.d_date; + RETURN NEW; + ELSEIF(TG_OP = 'INSERT') THEN + INSERT INTO pt2 VALUES(NEW.d_date); + RETURN NEW; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER test_trigger +AFTER INSERT OR UPDATE OR DELETE on pt1 +FOR EACH ROW EXECUTE PROCEDURE test_function(); + +INSERT INTO pt1 VALUES('2016-01-01'); +UPDATE pt2 set d_date = d_date + INTERVAL '4' month where d_date>='1999-05-01'; +DELETE FROM pt1 WHERE d_date<'1999-05-01'; +SELECT * FROM pt1; +SELECT * FROM pt2; + +DROP TABLE IF EXISTS pt2; +DROP TABLE IF EXISTS pt1; +DROP FUNCTION IF EXISTS test_function(); + +CREATE TABLE pt1( + d_date date UNIQUE +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + +CREATE TABLE pt2( + d_date date +)partition BY RANGE (d_date) INTERVAL('1 month') +(PARTITION part1 VALUES LESS THAN ('1900-01-01')); + + +INSERT INTO pt1 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + +INSERT INTO pt2 VALUES +('1999-01-01'),('1999-01-02'),('1999-01-019'),('1999-01-20'), +('1999-02-01'),('1999-02-02'),('1999-02-19'),('1999-02-20'), +('1999-03-01'),('1999-03-02'),('1999-03-19'),('1999-03-20'), +('1999-04-01'),('1999-04-02'),('1999-04-19'),('1999-04-20'), +('1999-05-01'),('1999-05-02'),('1999-05-19'),('1999-05-20'), +('1999-06-01'),('1999-06-02'),('1999-06-19'),('1999-06-20'), +('1999-07-01'),('1999-07-02'),('1999-07-19'),('1999-07-20'), +('1999-08-01'),('1999-08-02'),('1999-08-19'),('1999-08-20'), +('1999-09-01'),('1999-09-02'),('1999-09-19'),('1999-09-20'), +('1999-10-01'),('1999-10-02'),('1999-10-19'),('1999-10-20'), +('1999-11-01'),('1999-11-02'),('1999-11-19'),('1999-11-20'), +('1999-12-01'),('1999-12-02'),('1999-12-19'),('1999-12-20'); + + +CREATE FUNCTION test_function() +RETURNS TRIGGER AS $$ +BEGIN + IF(TG_OP = 'DELETE') THEN + DELETE FROM pt2 WHERE d_date=OLD.d_date; + RETURN OLD; + ELSEIF(TG_OP = 'UPDATE') THEN + UPDATE pt2 set d_date=NEW.d_date where d_date=OLD.d_date; + RETURN NEW; + ELSEIF(TG_OP = 'INSERT') THEN + INSERT INTO pt2 VALUES(NEW.d_date); + RETURN NEW; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER test_trigger +BEFORE INSERT OR UPDATE OR DELETE on pt1 +FOR EACH ROW EXECUTE PROCEDURE test_function(); + +INSERT INTO pt1 VALUES('2016-01-01'); +UPDATE pt2 set d_date = d_date + INTERVAL '4' month where d_date>='1999-05-01'; +DELETE FROM pt1 WHERE d_date<'1999-05-01'; +SELECT * FROM pt1; +SELECT * FROM pt2; \ No newline at end of file From 96ee6911d5b89ba8e819205b10a5a67fd6ce6ff7 Mon Sep 17 00:00:00 2001 From: luozihao Date: Tue, 25 Aug 2020 15:56:11 +0800 Subject: [PATCH 5/6] --- src/test/regress/expected/partition_trigger.out | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/regress/expected/partition_trigger.out b/src/test/regress/expected/partition_trigger.out index 9de8c0a1a..5758af803 100644 --- a/src/test/regress/expected/partition_trigger.out +++ b/src/test/regress/expected/partition_trigger.out @@ -107,7 +107,6 @@ SELECT * FROM pt1; Fri Jan 01 00:00:00 2016 (33 rows) - SELECT * FROM pt2; d_date -------------------------- From 085fbf3304feb9a5c17da28a815d067883b61a41 Mon Sep 17 00:00:00 2001 From: p00421579 Date: Wed, 26 Aug 2020 21:27:29 +0800 Subject: [PATCH 6/6] add the builtin function pg_terminate_session to terminate a user session Signed-off-by: pufuan --- src/common/backend/catalog/builtin_funcs.ini | 4 ++ src/common/backend/utils/adt/misc.cpp | 45 ++++++++++++++----- .../cbb/utils/gssignal/gs_signal.cpp | 12 +++++ .../process/threadpool/threadpool_sessctl.cpp | 38 +++++++++++++--- .../runtime/executor/opfusion_scan.cpp | 2 +- src/include/gssignal/gs_signal.h | 4 +- src/include/knl/knl_thread.h | 1 + src/include/threadpool/threadpool_sessctl.h | 1 + src/include/utils/builtins.h | 1 + src/test/regress/expected/opr_sanity.out | 3 +- .../expected/single_node_opr_sanity.out | 5 ++- 11 files changed, 95 insertions(+), 21 deletions(-) diff --git a/src/common/backend/catalog/builtin_funcs.ini b/src/common/backend/catalog/builtin_funcs.ini index 4073bb3af..3c0e492ba 100644 --- a/src/common/backend/catalog/builtin_funcs.ini +++ b/src/common/backend/catalog/builtin_funcs.ini @@ -7272,6 +7272,10 @@ "pg_terminate_backend", 1, AddBuiltinFunc(_0(2096), _1("pg_terminate_backend"), _2(1), _3(true), _4(false), _5(pg_terminate_backend), _6(16), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('v'), _18(0), _19(1, 20), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("pg_terminate_backend"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)) ), + AddFuncGroup( + "pg_terminate_session", 1, + AddBuiltinFunc(_0(2099), _1("pg_terminate_session"), _2(2), _3(true), _4(false), _5(pg_terminate_session), _6(16), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('v'), _18(0), _19(2, 20, 20), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("pg_terminate_session"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)) + ), AddFuncGroup( "pg_test_err_contain_err", 1, AddBuiltinFunc(_0(9999), _1("pg_test_err_contain_err"), _2(1), _3(true), _4(false), _5(pg_test_err_contain_err), _6(2278), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14('f'), _15(false), _16(false), _17('v'), _18(0), _19(1, 23), _20(NULL), _21(NULL), _22(NULL), _23(NULL), _24("pg_test_err_contain_err"), _25(NULL), _26(NULL), _27(NULL), _28(0), _29(false), _30(NULL), _31(false)) diff --git a/src/common/backend/utils/adt/misc.cpp b/src/common/backend/utils/adt/misc.cpp index 97d18641a..f6d559c76 100755 --- a/src/common/backend/utils/adt/misc.cpp +++ b/src/common/backend/utils/adt/misc.cpp @@ -238,15 +238,9 @@ Datum signal_backend(PG_FUNCTION_ARGS) PG_RETURN_BOOL(true); } -/* - * Signal to terminate a backend process. This is allowed if you are superuser - * or have the same role as the process being terminated. - */ -Datum pg_terminate_backend(PG_FUNCTION_ARGS) -{ - ThreadId tid = PG_GETARG_INT64(0); - int r = 0; +static int kill_backend(ThreadId tid) +{ /* * It is forbidden to kill backend in the online expansion to protect * the lock session from being interrupted by external applications. @@ -256,20 +250,49 @@ Datum pg_terminate_backend(PG_FUNCTION_ARGS) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("kill backend is prohibited during online expansion.")))); } - r = pg_signal_backend(tid, SIGTERM); + int r = pg_signal_backend(tid, SIGTERM); if (r == SIGNAL_BACKEND_NOPERMISSION) { ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be system admin or have the same role to terminate other backend")))); } - - if (t_thrd.proc && t_thrd.proc->workingVersionNum >= 92060) { + + if (t_thrd.proc != NULL) { uint64 query_id = get_query_id_beentry(tid); (void)gs_close_all_stream_by_debug_id(query_id); } + + return r; +} + +/* + * Signal to terminate a backend process. This is allowed if you are superuser + * or have the same role as the process being terminated. + */ +Datum pg_terminate_backend(PG_FUNCTION_ARGS) +{ + ThreadId tid = PG_GETARG_INT64(0); + int r = kill_backend(tid); PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS); } +Datum pg_terminate_session(PG_FUNCTION_ARGS) +{ + ThreadId tid = PG_GETARG_INT64(0); + uint64 sid = PG_GETARG_INT64(1); + int r = 0; + + if (tid == sid) { + r = kill_backend(tid); + } else { + ThreadPoolSessControl *sess_ctrl = g_threadPoolControler->GetSessionCtrl(); + int ctrl_idx = sess_ctrl->FindCtrlIdxBySessId(sid); + r = sess_ctrl->SendSignal((int)ctrl_idx, SIGTERM); + } + + PG_RETURN_BOOL(r == 0); +} + /* * function name: pg_wlm_jump_queue * description : wlm jump the queue with thread id. diff --git a/src/gausskernel/cbb/utils/gssignal/gs_signal.cpp b/src/gausskernel/cbb/utils/gssignal/gs_signal.cpp index d7b1a48ea..1d6b4edc4 100644 --- a/src/gausskernel/cbb/utils/gssignal/gs_signal.cpp +++ b/src/gausskernel/cbb/utils/gssignal/gs_signal.cpp @@ -215,6 +215,10 @@ static void gs_signal_send_mark(GsNode* local_node, GsSignalCheckType check_type } break; } + case SIGNAL_CHECK_SESS_KEY: { + local_node->sig_data.check.session_id = t_thrd.sig_cxt.session_id; + break; + } default: { break; } @@ -670,6 +674,14 @@ static bool gs_signal_handle_check(const GsSndSignal* local_node) } break; } + case SIGNAL_CHECK_SESS_KEY: { + if (u_sess != NULL && local_node->check.session_id == u_sess->session_id) { + return true; + } else { + return false; + } + break; + } default: { break; } diff --git a/src/gausskernel/process/threadpool/threadpool_sessctl.cpp b/src/gausskernel/process/threadpool/threadpool_sessctl.cpp index 09d4154f7..115ae2c94 100644 --- a/src/gausskernel/process/threadpool/threadpool_sessctl.cpp +++ b/src/gausskernel/process/threadpool/threadpool_sessctl.cpp @@ -216,6 +216,17 @@ int ThreadPoolSessControl::SendSignal(int ctrl_index, int signal) } knl_sess_control* ctrl = &m_base[ctrl_index - m_maxReserveSessionCount]; + knl_session_context* sess = ctrl->sess; + + /* check permission */ + if (!superuser()) { + if (sess->proc_cxt.MyRoleId != GetUserId()) { + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be system admin or have the same role to terminate other backend")))); + } + } + volatile sig_atomic_t* plock = &ctrl->lock; sig_atomic_t val; do { @@ -223,13 +234,18 @@ int ThreadPoolSessControl::SendSignal(int ctrl_index, int signal) /* perform an atomic compare and swap. */ val = __sync_val_compare_and_swap(plock, 0, 1); if (val == 0) { - if (ctrl->sess != NULL) { - if (ctrl->sess->status == KNL_SESS_ATTACH) { - status = gs_signal_send(ctrl->sess->attachPid, signal); - } else if (ctrl->sess->status == KNL_SESS_DETACH) { + if (sess != NULL) { + if (sess->status == KNL_SESS_ATTACH) { + t_thrd.sig_cxt.gs_sigale_check_type = SIGNAL_CHECK_SESS_KEY; + t_thrd.sig_cxt.session_id = sess->session_id; + status = gs_signal_send(sess->attachPid, signal); + t_thrd.sig_cxt.gs_sigale_check_type = SIGNAL_CHECK_NONE; + t_thrd.sig_cxt.session_id = 0; + } else if (sess->status == KNL_SESS_DETACH) { switch (signal) { case SIGTERM: - ctrl->sess->status = KNL_SESS_CLOSE; + sess->status = KNL_SESS_CLOSE; + CloseClientSocket(sess, false); status = 0; break; default: @@ -504,3 +520,15 @@ knl_session_context* ThreadPoolSessControl::GetSessionByIdx(int idx) return NULL; } } + +int ThreadPoolSessControl::FindCtrlIdxBySessId(uint64 id) +{ + int cidx = 0; + for (cidx = 0; cidx < m_maxActiveSessionCount; cidx++) { + if (m_base[cidx].sess != NULL && m_base[cidx].sess->session_id == id) { + break; + } + } + + return cidx + m_maxReserveSessionCount; +} diff --git a/src/gausskernel/runtime/executor/opfusion_scan.cpp b/src/gausskernel/runtime/executor/opfusion_scan.cpp index 12f19808b..1fc8cc03e 100644 --- a/src/gausskernel/runtime/executor/opfusion_scan.cpp +++ b/src/gausskernel/runtime/executor/opfusion_scan.cpp @@ -485,7 +485,7 @@ IndexOnlyScanFusion::IndexOnlyScanFusion(IndexOnlyScan* node, PlannedStmt* plans m_keyInit = false; m_keyNum = list_length(node->indexqual); - ; + m_scanKeys = (ScanKey)palloc0(m_keyNum * sizeof(ScanKeyData)); /* init params */ diff --git a/src/include/gssignal/gs_signal.h b/src/include/gssignal/gs_signal.h index 58306cf77..c3cd9777f 100755 --- a/src/include/gssignal/gs_signal.h +++ b/src/include/gssignal/gs_signal.h @@ -38,13 +38,15 @@ typedef void (*gs_sigfunc)(int); typedef enum GsSignalCheckType { SIGNAL_CHECK_NONE, SIGNAL_CHECK_EXECUTOR_STOP, - SIGNAL_CHECK_STREAM_STOP + SIGNAL_CHECK_STREAM_STOP, + SIGNAL_CHECK_SESS_KEY } GsSignalCheckType; /* the struct of signal check */ typedef struct GsSignalCheck { GsSignalCheckType check_type; uint64 debug_query_id; + uint64 session_id; } GsSignalCheck; /* the struct of signal to be handled and the signal sender's thread id */ diff --git a/src/include/knl/knl_thread.h b/src/include/knl/knl_thread.h index 4a2939477..9dd03559b 100644 --- a/src/include/knl/knl_thread.h +++ b/src/include/knl/knl_thread.h @@ -1929,6 +1929,7 @@ typedef struct knl_t_libwalreceiver_context { typedef struct knl_t_sig_context { unsigned long signal_handle_cnt; GsSignalCheckType gs_sigale_check_type; + uint64 session_id; } knl_t_sig_context; typedef struct knl_t_slot_context { diff --git a/src/include/threadpool/threadpool_sessctl.h b/src/include/threadpool/threadpool_sessctl.h index 996ead02b..d035e0437 100644 --- a/src/include/threadpool/threadpool_sessctl.h +++ b/src/include/threadpool/threadpool_sessctl.h @@ -76,6 +76,7 @@ public: void HandlePoolerReload(); SessionMemoryDetail* getSessionMemoryDetail(uint32* num); knl_session_context* GetSessionByIdx(int idx); + int FindCtrlIdxBySessId(uint64 id); inline int GetActiveSessionCount() { diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 058b46292..b4e9545c7 100755 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -580,6 +580,7 @@ extern Datum pg_cancel_invalid_query(PG_FUNCTION_ARGS); extern Datum report_fatal(PG_FUNCTION_ARGS); extern Datum signal_backend(PG_FUNCTION_ARGS); extern Datum pg_terminate_backend(PG_FUNCTION_ARGS); +extern Datum pg_terminate_session(PG_FUNCTION_ARGS); extern Datum pg_reload_conf(PG_FUNCTION_ARGS); extern Datum pg_tablespace_databases(PG_FUNCTION_ARGS); extern Datum pg_tablespace_location(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 94ffb71ba..ca1a3f9f4 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1146,6 +1146,7 @@ WHERE d.classoid IS NULL AND p1.oid <= 9999 order by 1; 2096 | pg_terminate_backend 2097 | pg_get_variable_info 2098 | pg_get_functiondef + 2099 | pg_terminate_session 2100 | avg 2101 | avg 2102 | avg @@ -2637,7 +2638,7 @@ WHERE d.classoid IS NULL AND p1.oid <= 9999 order by 1; 9016 | pg_advisory_lock 9017 | pgxc_unlock_for_sp_database 9999 | pg_test_err_contain_err -(2275 rows) +(2276 rows) -- **************** pg_cast **************** -- Catch bogus values in pg_cast columns (other than cases detected by diff --git a/src/test/regress/expected/single_node_opr_sanity.out b/src/test/regress/expected/single_node_opr_sanity.out index 4178e37e2..5c059615d 100755 --- a/src/test/regress/expected/single_node_opr_sanity.out +++ b/src/test/regress/expected/single_node_opr_sanity.out @@ -1185,6 +1185,7 @@ WHERE d.classoid IS NULL AND p1.oid <= 9999 order by 1; 2096 | pg_terminate_backend 2097 | pg_get_variable_info 2098 | pg_get_functiondef + 2099 | pg_terminate_session 2100 | avg 2101 | avg 2102 | avg @@ -2676,7 +2677,7 @@ WHERE d.classoid IS NULL AND p1.oid <= 9999 order by 1; 9016 | pg_advisory_lock 9017 | pgxc_unlock_for_sp_database 9999 | pg_test_err_contain_err -(2275 rows) +(2276 rows) -- Check prokind select count(*) from pg_proc where prokind = 'a'; @@ -2694,7 +2695,7 @@ select count(*) from pg_proc where prokind = 'w'; select count(*) from pg_proc where prokind = 'f'; count ------- - 3149 + 3150 (1 row) select count(*) from pg_proc where prokind = 'p';